diff --git a/BSDmakefile b/BSDmakefile new file mode 100644 index 0000000..f0efc9d --- /dev/null +++ b/BSDmakefile @@ -0,0 +1,4 @@ +.DONE: + @echo "Please use GNU Make (gmake) to build UnrealIRCd" +.DEFAULT: + @echo "Please use GNU Make (gmake) to build UnrealIRCd" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c1a2873 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,5 @@ +Help out and make UnrealIRCd a better product! + +You can do so by reporting issues, testing, programming, documenting, +translating, helping others, and more. +See https://www.unrealircd.org/docs/Contributing diff --git a/Config b/Config index 68a5906..8625041 100755 --- a/Config +++ b/Config @@ -1,7 +1,7 @@ #!/bin/sh # # Config script for UnrealIRCd -# (C) 2001-2019 The UnrealIRCd Team +# (C) 2001-2021 The UnrealIRCd Team # # This configure script is free software; the UnrealIRCd Team gives # unlimited permission to copy, distribute and modify as long as the @@ -37,9 +37,6 @@ mkdir -p $TMPDIR mkdir -p $PRIVATELIBDIR # Do this even if we're not in advanced mode -if [ "$SHOWLISTMODES" = "1" ] ; then - ARG="$ARG--with-showlistmodes " -fi if [ "$ADVANCED" = "1" ] ; then if [ "$NOOPEROVERRIDE" = "1" ] ; then ARG="$ARG--with-no-operoverride " @@ -53,12 +50,21 @@ ARG="$ARG--enable-ssl " else ARG="$ARG--enable-ssl=$SSLDIR " fi +# Ensure we install curl even if someone does ./Config -quick... +if [ "x$CURLDIR" = "x$UNREALCWD/extras/curl" ]; then + INSTALLCURL=1 +else + # And that the path does not refer to eg an old unrealircd-1.2.3 either ;) + if echo "$CURLDIR"|egrep -qi extras.*curl; then + echo "WARNING: Potentially old cURL directory encountered ($CURLDIR)." + echo "I am changing the cURL directory to $UNREALCWD/extras/curl and forcing a rebuild of cURL." + CURLDIR="$UNREALCWD/extras/curl" + INSTALLCURL=1 + fi +fi if [ "$REMOTEINC" = "1" ] ; then ARG="$ARG--enable-libcurl=$CURLDIR " fi -if [ "$PREFIXAQ" != "1" ]; then -ARG="$ARG--disable-prefixaq " -fi if [ "$MAXCONNECTIONS_REQUEST" != "auto" ]; then ARG="$ARG--with-maxconnections=$MAXCONNECTIONS_REQUEST " fi @@ -77,14 +83,19 @@ ARG="$ARG--with-scriptdir=$BASEPATH " ARG="$ARG--with-nick-history=$NICKNAMEHISTORYLENGTH " ARG="$ARG--with-permissions=$DEFPERM " ARG="$ARG--enable-dynamic-linking " +if [ "$GEOIP" = "classic" ]; then + ARG="$ARG--enable-geoip-classic " +fi +if [ "$GEOIP" = "libmaxminddb" ]; then + ARG="$ARG--enable-libmaxminddb " +fi +if [ "$SANITIZER" = "asan" ]; then + ARG="$ARG--enable-asan " +fi ARG="$ARG $EXTRAPARA " CONF="./configure $ARG" # remove possibly old instances of curl in ~/unrealircd/lib/ rm -f $PRIVATELIBDIR/*curl* 1>/dev/null 2>&1 -# Ensure we install curl even if someone does ./Config -quick... -if [ "x$CURLDIR" = "x$UNREALCWD/extras/curl" ]; then - INSTALLCURL=1 -fi if [ "x$INSTALLCURL" = "x1" ]; then extras/curlinstall "$PRIVATELIBDIR" || exit 1 fi @@ -127,8 +138,8 @@ if [ "$QUICK" != "1" ] ; then TEST="No" fi echo "" - echo "UnrealIRCd requires an SSL certificate in order to work." - echo "Do you want to generate an SSL certificate for the IRCd?" + echo "UnrealIRCd requires a TLS certificate in order to work." + echo "Do you want to generate a TLS certificate for the IRCd?" echo "Only answer No if you already have one." echo $n "[$TEST] -> $c" read cc @@ -152,60 +163,31 @@ if [ "$QUICK" != "1" ] ; then if [ "$GENCERTIFICATE" = 1 ]; then echo echo "*******************************************************************************" - echo "Next you will be asked some questions in order to generate the SSL certificate." + echo "Next you will be asked some questions in order to generate the TLS certificate." echo "IMPORTANT: If you don't own a domain or don't know what to answer, then you can" echo " simply press ENTER or use fictional names for each question!" echo "*******************************************************************************" echo "Press ENTER to continue" read cc - make pem + $MAKE pem echo "Certificate created successfully." sleep 1 else - echo "Ok, not generating SSL certificate. Make sure that the certificate and key" + echo "Ok, not generating TLS certificate. Make sure that the certificate and key" echo "are installed in conf/tls/server.cert.pem and conf/tls/server.key.pem prior to starting the IRCd." fi else - echo "SSL certificate already exists in configuration directory, no need to regenerate." + echo "TLS certificate already exists in configuration directory, no need to regenerate." fi fi # Silently force a 'make clean' as otherwise part (or whole) of the # compiled source could be using different settings than the user # just requested when re-running ./Config. -make clean 1>/dev/null 2>&1 +$MAKE clean 1>/dev/null 2>&1 } RUN_ADVANCED () { -TEST="" -while [ -z "$TEST" ] ; do - if [ "$SHOWLISTMODES" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to show the modes a channel has set in the /list output?" - echo $n "[$TEST] -> $c" - read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in - [Yy]*) - SHOWLISTMODES="1" - ;; - [Nn]*) - SHOWLISTMODES="" - ;; - *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac -done - TEST="" while [ -z "$TEST" ] ; do if [ "$NOOPEROVERRIDE" = "1" ] ; then @@ -271,16 +253,16 @@ UNREALCWD="`pwd`" BASEPATH="$HOME/unrealircd" DEFPERM="0600" SSLDIR="" -NICKNAMEHISTORYLENGTH="100" +NICKNAMEHISTORYLENGTH="2000" MAXCONNECTIONS_REQUEST="auto" REMOTEINC="1" CURLDIR="" -PREFIXAQ="0" -SHOWLISTMODES="0" NOOPEROVERRIDE="" OPEROVERRIDEVERIFY="" GENCERTIFICATE="1" EXTRAPARA="" +SANITIZER="" +GEOIP="classic" if [ "`eval echo -n 'a'`" = "-n a" ] ; then c="\c" else @@ -333,6 +315,33 @@ if [ "`id -u`" = "0" ]; then exit fi +# Check for gmake early... +if [ "$MAKE" = "" ]; then + MAKE="make" +fi + +if ! $MAKE --version 2>&1|grep -q "GNU Make"; then + # So $MAKE is not GNU make, but do we have gmake? + if gmake --version 2>&1|grep -q "GNU Make"; then + # Great, use that one! + MAKE="gmake" + else + # Both $MAKE and gmake are not GNU make, do we have a working $MAKE at all? + if $MAKE --version 1>/dev/null 2>&1; then + echo "Your system has 'make' but UnrealIRCd requires GNU Make ('gmake')" + echo "Please install 'gmake' (eg 'pkg install gmake' on BSD)." + exit 1 + else + echo "Your system does not have the required tools installed to build UnrealIRCd." + echo "Please check https://www.unrealircd.org/docs/Installing_from_source" + echo "and install the required tools listed under 'Prerequisites'." + echo "After that, you can run ./Config again" + exit 1 + fi + fi +fi + + clear if [ -f "doc/Config.header" -a -z "$NOINTRO" ] ; then @@ -347,7 +356,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-5.2.0 unrealircd-5.2.0-rc1 unrealircd-5.0.9.1 unrealircd-5.0.9 unrealircd-5.0.9-rc1 unrealircd-5.0.8 unrealircd-5.0.8-rc1 unrealircd-5.0.7 unrealircd-5.0.7-rc1 unrealircd-5.0.6 unrealircd-5.0.5.1 unrealircd-5.0.5 unrealircd-5.0.4 unrealircd-5.0.3.1 unrealircd-5.0.3 unrealircd-5.0.2 unrealircd-5.0.1 unrealircd-5.0.0" +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" if [ -f "config.settings" ]; then . ./config.settings else @@ -421,7 +430,7 @@ fi TEST="$BASEPATH" echo "" echo "In what directory do you want to install UnrealIRCd?" -echo "(Note: UnrealIRCd 5 will need to be installed somewhere." +echo "(Note: UnrealIRCd 6 will need to be installed somewhere." echo " If this directory does not exist it will be created.)" echo $n "[$TEST] -> $c" read cc @@ -507,9 +516,12 @@ while [ -z "$TEST" ] ; do TEST="No" fi echo "" - echo "Do you want to enable remote includes?" - echo "This allows stuff like this in your configuration file:" - echo "include \"https://www.somesite.org/files/opers.conf\";" + echo "UnrealIRCd comes with support for 'remote includes', this allows things like:" + echo "include \"https://www.example.org/files/opers.conf\";" + echo "Do you want to compile with the libcurl library to enable additional protocols?" + echo "If you answer 'No' then only https:// links will work for remote includes." + echo "Answer 'Yes' if you need other protocols, such as plaintext http, ftp, tftp or smb." + echo "Most people answer 'No' here because they don't use remote includes or only need https." echo $n "[$TEST] -> $c" read cc if [ -z "$cc" ] ; then @@ -661,39 +673,6 @@ if [ "$REMOTEINC" = "1" ] ; then fi -TEST="" -while [ -z "$TEST" ] ; do - if [ "$PREFIXAQ" = "1" ] ; then - TEST="Yes" - else - TEST="No" - fi - echo "" - echo "Do you want to enable prefixes for chanadmin and chanowner?" - echo "This will give +a the & prefix and ~ for +q (just like +o is @)" - echo "Supported by the major clients (mIRC, xchat, epic, eggdrop, Klient," - echo "PJIRC, irssi, CGI:IRC, etc.)" - echo "This feature should be enabled/disabled network-wide." - echo $n "[$TEST] -> $c" - read cc - if [ -z "$cc" ] ; then - cc=$TEST - fi - case "$cc" in - [Yy]*) - PREFIXAQ="1" - ;; - [Nn]*) - PREFIXAQ="" - ;; - *) - echo "" - echo "You must enter either Yes or No" - TEST="" - ;; - esac -done - TEST="" while [ -z "$TEST" ] ; do TEST="$NICKNAMEHISTORYLENGTH" @@ -717,6 +696,41 @@ while [ -z "$TEST" ] ; do esac done +TEST="" +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 " you need to install the libmaxminddb library on your system first" + echo " none: Don't built with any geoip feature" + echo "Choose one of: classic, libmaxminddb, none" + echo $n "[$TEST] -> $c" + read cc + if [ -z "$cc" ] ; then + GEOIP=$TEST + break + fi + case "$cc" in + classic) + GEOIP="$cc" + ;; + libmaxminddb) + GEOIP="$cc" + ;; + none) + GEOIP="$cc" + ;; + *) + echo "" + echo "Invalid choice: $cc" + TEST="" + ;; + esac +done + echo "" TEST="" while [ -z "$TEST" ] ; do @@ -753,6 +767,42 @@ if [ -n "$ADVANCED" ] ; then RUN_ADVANCED fi +TEST="" +while [ -z "$TEST" ] ; do + if [ "$SANITIZER" = "asan" ] ; then + TEST="Yes" + else + TEST="No" + fi + echo "" + echo "Are you running UnrealIRCd as a test, debugging a problem or developing a module?" + echo "Then it is possible to run with AddressSanitizer enabled. This will make it" + echo "catch bugs such as out-of-bounds and other memory corruption issues, which can" + echo "be really helpful in some cases. The downside is that it will consume a lot" + echo "more memory and run slower too. So, only answer 'Yes' if you are OK with this." + echo "Also, on some systems (notably FreeBSD), when you enable AddressSanitizer," + echo "UnrealIRCd may fail to start. So when in doubt, answer 'No'." + echo "Do you want to enable AddressSanitizer?" + echo $n "[$TEST] -> $c" + read cc + if [ -z "$cc" ] ; then + cc=$TEST + fi + case "$cc" in + [Yy]*) + SANITIZER="asan" + ;; + [Nn]*) + SANITIZER="" + ;; + *) + echo "" + echo "You must enter either Yes or No" + TEST="" + ;; + esac +done + TEST="$EXTRAPARA" echo "" echo "Would you like to pass any custom parameters to configure?" @@ -784,17 +834,17 @@ CACHEDIR="$CACHEDIR" DOCDIR="$DOCDIR" TMPDIR="$TMPDIR" PRIVATELIBDIR="$PRIVATELIBDIR" -PREFIXAQ="$PREFIXAQ" MAXCONNECTIONS_REQUEST="$MAXCONNECTIONS_REQUEST" NICKNAMEHISTORYLENGTH="$NICKNAMEHISTORYLENGTH" +GEOIP="$GEOIP" DEFPERM="$DEFPERM" SSLDIR="$SSLDIR" REMOTEINC="$REMOTEINC" CURLDIR="$CURLDIR" -SHOWLISTMODES="$SHOWLISTMODES" NOOPEROVERRIDE="$NOOPEROVERRIDE" OPEROVERRIDEVERIFY="$OPEROVERRIDEVERIFY" GENCERTIFICATE="$GENCERTIFICATE" +SANITIZER="$SANITIZER" EXTRAPARA="$EXTRAPARA" ADVANCED="$ADVANCED" __EOF__ @@ -808,16 +858,17 @@ cat << __EOF__ |_______________________________________________________________________| |_______________________________________________________________________| | | -| Now all you have to do is type 'make' and let it compile. When that's | -| done, you will receive other instructions on what to do next. | -| | -|_______________________________________________________________________| -|_______________________________________________________________________| | - The UnrealIRCd Team - | | | -| * Bram Matthys (Syzop) syzop@unrealircd.org | -| * Gottem gottem@unrealircd.org | -| * i i@unrealircd.org | +| Bram Matthys (Syzop) - syzop@unrealircd.org | +| Krzysztof Beresztant (k4be) - k4be@unrealircd.org | +| Gottem - gottem@unrealircd.org | +| i - i@unrealircd.org | +|_______________________________________________________________________| +|_______________________________________________________________________| +| | +| Now all you have to do is type '$MAKE' and let it compile. When that's | +| done, you will receive other instructions on what to do next. | |_______________________________________________________________________| __EOF__ diff --git a/Makefile.in b/Makefile.in index 1857495..7ce1dd2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,11 +34,11 @@ FROMDOS=/home/cmunk/bin/4dos # #XCFLAGS=-O -g -export-dynamic -IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @SODIUM_LIBS@ @PTHREAD_LIBS@ +IRCDLIBS=@IRCDLIBS@ @PCRE2_LIBS@ @ARGON2_LIBS@ @CARES_LIBS@ @SODIUM_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ CRYPTOLIB=@CRYPTOLIB@ OPENSSLINCLUDES= -XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @SODIUM_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@ +XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @SODIUM_CFLAGS@ @JANSSON_CFLAGS@ @CFLAGS@ @HARDEN_CFLAGS@ @CPPFLAGS@ # # use the following on MIPS: #CFLAGS= -systype bsd43 -DSYSTYPE_BSD43 -I$(INCLUDEDIR) @@ -89,7 +89,14 @@ XCFLAGS=@PTHREAD_CFLAGS@ @PCRE2_CFLAGS@ @ARGON2_CFLAGS@ @CARES_CFLAGS@ @SODIUM_C # you are not defining CMDLINE_CONFIG IRCDMODE = 711 +# Objects that are optional due to optional libraries: URL=@URL@ +GEOIP_CLASSIC_OBJECTS=@GEOIP_CLASSIC_OBJECTS@ +GEOIP_CLASSIC_LIBS=@GEOIP_CLASSIC_LIBS@ +GEOIP_CLASSIC_CFLAGS=@GEOIP_CLASSIC_CFLAGS@ +GEOIP_MAXMIND_OBJECTS=@GEOIP_MAXMIND_OBJECTS@ +LIBMAXMINDDB_CFLAGS=@LIBMAXMINDDB_CFLAGS@ +LIBMAXMINDDB_LIBS=@LIBMAXMINDDB_LIBS@ # Where is your openssl binary OPENSSLPATH=@OPENSSLPATH@ @@ -116,7 +123,13 @@ MAKEARGS = 'CFLAGS=${CFLAGS}' 'CC=${CC}' 'IRCDLIBS=${IRCDLIBS}' \ 'SHELL=${SHELL}' \ 'CRYPTOLIB=${CRYPTOLIB}' \ 'CRYPTOINCLUDES=${CRYPTOINCLUDES}' \ - 'URL=${URL}' + 'URL=${URL}' \ + 'GEOIP_CLASSIC_OBJECTS=${GEOIP_CLASSIC_OBJECTS}' \ + 'GEOIP_CLASSIC_LIBS=${GEOIP_CLASSIC_LIBS}' \ + 'GEOIP_CLASSIC_CFLAGS=${GEOIP_CLASSIC_CFLAGS}' \ + 'GEOIP_MAXMIND_OBJECTS=${GEOIP_MAXMIND_OBJECTS}' \ + 'LIBMAXMINDDB_CFLAGS=${LIBMAXMINDDB_CFLAGS}' \ + 'LIBMAXMINDDB_LIBS=${LIBMAXMINDDB_LIBS}' custommodule: @if test -z "${MODULEFILE}"; then echo "Please set MODULEFILE when calling \`\`make custommodule''. For example, \`\`make custommodule MODULEFILE=callerid''." >&2; exit 1; fi @@ -135,7 +148,7 @@ build: Makefile done @echo '' @echo '* UnrealIRCd compiled successfully' - @echo '* YOU ARE NOT DONE YET! Run "make install" to install UnrealIRCd !' + @echo '* YOU ARE NOT DONE YET! Run "${MAKE} install" to install UnrealIRCd !' @echo '' clean: @@ -183,9 +196,6 @@ install: all $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/chanmodes @rm -f $(DESTDIR)@MODULESDIR@/chanmodes/*.so 1>/dev/null 2>&1 $(INSTALL) -m 0700 src/modules/chanmodes/*.so $(DESTDIR)@MODULESDIR@/chanmodes - $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/snomasks - @rm -f $(DESTDIR)@MODULESDIR@/snomasks/*.so 1>/dev/null 2>&1 - $(INSTALL) -m 0700 src/modules/snomasks/*.so $(DESTDIR)@MODULESDIR@/snomasks $(INSTALL) -m 0700 -d $(DESTDIR)@MODULESDIR@/extbans @rm -f $(DESTDIR)@MODULESDIR@/extbans/*.so 1>/dev/null 2>&1 $(INSTALL) -m 0700 src/modules/extbans/*.so $(DESTDIR)@MODULESDIR@/extbans @@ -248,7 +258,7 @@ pem: extras/tls.cnf -config extras/tls.cnf -sha256 -out server.req.pem \ -key server.key.pem -nodes @echo "Generating self-signed certificate..." - $(OPENSSLPATH) req -x509 -days 3650 -sha256 -in server.req.pem \ + $(OPENSSLPATH) req -x509 -days 3650 -sha256 -nodes -in server.req.pem \ -key server.key.pem -out server.cert.pem @echo "Setting permissions on server.*.pem files..." chmod o-rwx server.req.pem server.key.pem server.cert.pem diff --git a/Makefile.windows b/Makefile.windows index f63f1ec..39cb1c9 100644 --- a/Makefile.windows +++ b/Makefile.windows @@ -25,15 +25,25 @@ MT=mt #ARGON2LIB="Argon2RefDll.lib" ### SODIUM ### -#SODIUM_LIB_DIR="C:\dev\unrealircd-5-libs\libsodium\......." -#SODIUM_INC_DIR="C:\dev\unrealircd-5-libs\libsodium\......." +#SODIUM_LIB_DIR="C:\dev\unrealircd-6-libs\libsodium\......." +#SODIUM_INC_DIR="C:\dev\unrealircd-6-libs\libsodium\......." #SODIUMLIB="libsodium.lib" +### JANSSON ### +#JANSSON_LIB_DIR="C:\dev\unrealircd-6-libs\jansson\lib" +#JANSSON_INC_DIR="C:\dev\unrealircd-6-libs\jansson\include" +#JANSSONLIB="jansson.lib" + ### C-ARES #### #CARES_LIB_DIR="C:\dev\c-ares\vc\cares\dll-release" #CARES_INC_DIR="C:\dev\c-ares" #CARESLIB="cares.lib" +### GEOIP CLASSIC ### +#GEOIPCLASSIC_LIB_DIR="c:\dev\unrealircd-6-libs\GeoIP\libGeoIP" ^ +#GEOIPCLASSIC_INC_DIR="c:\dev\unrealircd-6-libs\GeoIP\libGeoIP" ^ +#GEOIPCLASSICLIB="GeoIP.lib" + ##### REMOTE INCLUDES #### #To enable remote include support you must have libcurl installed on your #system and it must have ares support enabled. @@ -106,9 +116,16 @@ SODIUM_INC=/I "$(SODIUM_INC_DIR)" SODIUM_LIB=/LIBPATH:"$(SODIUM_LIB_DIR)" !ENDIF +!IFDEF JANSSON_INC_DIR +JANSSON_INC=/I "$(JANSSON_INC_DIR)" +!ENDIF +!IFDEF JANSSON_LIB_DIR +JANSSON_LIB=/LIBPATH:"$(JANSSON_LIB_DIR)" +!ENDIF + !IFDEF USE_REMOTEINC CURLCFLAGS=/D USE_LIBCURL -CURLOBJ=SRC/URL.OBJ +CURLOBJ=SRC/URL_CURL.OBJ CURLLIB=libcurl.lib !IFDEF LIBCURL_INC_DIR LIBCURL_INC=/I "$(LIBCURL_INC_DIR)" @@ -137,15 +154,15 @@ DBGLFLAG=/debug MODDBGCFLAG=/LDd /MD /Zi !ENDIF -STDOPTIONS=$(PCRE2_INC) $(ARGON2_INC) $(SODIUM_INC) $(CARES_INC) $(LIBCURL_INC) $(LIBRESSL_INC) /J /I ./INCLUDE /nologo \ +STDOPTIONS=$(PCRE2_INC) $(ARGON2_INC) $(SODIUM_INC) $(JANSSON_INC) $(CARES_INC) $(LIBCURL_INC) $(LIBRESSL_INC) \ + /J /I ./INCLUDE /nologo \ $(CURLCFLAGS) /D FD_SETSIZE=16384 $(SSLCFLAGS) /D _CRT_SECURE_NO_DEPRECATE /D _CRT_NONSTDC_NO_DEPRECATE \ /D FAKELAG_CONFIGURABLE=1 \ - /W3 /wd4267 /wd4101 /wd4018 /wd4244 /wd4996 /WX \ - /analyze:ruleset extras\VStudioAnalyze.ruleset + /W3 /wd4267 /wd4101 /wd4018 /wd4244 /wd4996 /WX /analyze:ruleset extras\VStudioAnalyze.ruleset STDLIBS=$(CARES_LIB) $(CARESLIB) $(PCRE2_LIB) $(PCRE2LIB) $(ARGON2_LIB) $(ARGON2LIB) \ - $(SODIUM_LIB) $(SODIUMLIB) $(LIBRESSL_LIB) $(SSLLIB) $(LIBCURL_LIB) $(CURLLIB) -CFLAGS=$(DBGCFLAG) $(STDOPTIONS) /c /Fosrc/ -CFLAGSST=$(DBGCFLAGST) $(STDOPTIONS) /c /Fosrc/ + $(SODIUM_LIB) $(SODIUMLIB) $(JANSSON_LIB) $(JANSSONLIB) $(LIBRESSL_LIB) $(SSLLIB) $(LIBCURL_LIB) $(CURLLIB) +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 \ @@ -157,203 +174,249 @@ 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 \ - SRC/FDLIST.OBJ SRC/DBUF.OBJ \ - SRC/HASH.OBJ SRC/PARSE.OBJ SRC/IRCD.OBJ \ - SRC/WHOWAS.OBJ \ - SRC/MISC.OBJ SRC/MATCH.OBJ SRC/CRULE.OBJ \ - SRC/DEBUG.OBJ SRC/SUPPORT.OBJ SRC/LIST.OBJ \ - SRC/NUMERIC.OBJ \ - SRC/SERV.OBJ SRC/USER.OBJ \ - SRC/VERSION.OBJ SRC/IRCSPRINTF.OBJ \ - SRC/SCACHE.OBJ SRC/DNS.OBJ SRC/MODULES.OBJ \ - SRC/ALIASES.OBJ SRC/API-EVENT.OBJ SRC/API-USERMODE.OBJ SRC/AUTH.OBJ SRC/TLS.OBJ \ - SRC/RANDOM.OBJ SRC/API-CHANNELMODE.OBJ SRC/API-MODDATA.OBJ SRC/MEMPOOL.OBJ \ - SRC/DISPATCH.OBJ SRC/API-ISUPPORT.OBJ SRC/API-COMMAND.OBJ \ - SRC/API-CLICAP.OBJ SRC/API-MESSAGETAG.OBJ SRC/API-HISTORY-BACKEND.OBJ \ - SRC/API-EXTBAN.OBJ SRC/API-EFUNCTIONS.OBJ SRC/CRYPT_BLOWFISH.OBJ \ - SRC/OPERCLASS.OBJ SRC/UPDCONF.OBJ SRC/CRASHREPORT.OBJ SRC/UNREALDB.OBJ \ - SRC/OPENSSL_HOSTNAME_VALIDATION.OBJ \ - SRC/UTF8.OBJ $(CURLOBJ) +EXP_OBJ_FILES=src/channel.obj src/send.obj src/socket.obj \ + src/conf.obj src/conf_preprocessor.obj \ + src/fdlist.obj src/dbuf.obj \ + src/hash.obj src/parse.obj src/ircd.obj \ + src/whowas.obj \ + src/misc.obj src/match.obj src/crule.obj \ + src/debug.obj src/support.obj src/list.obj \ + src/serv.obj src/user.obj \ + src/version.obj src/ircsprintf.obj \ + src/scache.obj src/dns.obj src/modules.obj \ + src/aliases.obj src/api-event.obj src/api-usermode.obj src/auth.obj src/tls.obj \ + src/random.obj src/api-channelmode.obj src/api-moddata.obj src/mempool.obj \ + src/dispatch.obj src/api-isupport.obj src/api-command.obj \ + src/api-clicap.obj src/api-messagetag.obj src/api-history-backend.obj \ + src/api-extban.obj src/api-efunctions.obj src/crypt_blowfish.obj \ + src/operclass.obj src/crashreport.obj src/unrealdb.obj \ + src/openssl_hostname_validation.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 +OBJ_FILES=$(EXP_OBJ_FILES) src/gui.obj src/service.obj src/windebug.obj src/rtf.obj \ + src/editor.obj src/win.obj -DLL_FILES=SRC/MODULES/CLOAK.DLL \ - SRC/MODULES/CHGHOST.DLL SRC/MODULES/SDESC.DLL SRC/MODULES/SETIDENT.DLL \ - SRC/MODULES/SETNAME.DLL SRC/MODULES/SETHOST.DLL SRC/MODULES/CHGIDENT.DLL \ - SRC/MODULES/SVSMOTD.DLL SRC/MODULES/SVSNLINE.DLL SRC/MODULES/WHO_OLD.DLL \ - SRC/MODULES/WHOX.DLL \ - SRC/MODULES/SWHOIS.DLL SRC/MODULES/SVSMODE.DLL SRC/MODULES/AWAY.DLL \ - SRC/MODULES/SVSNOOP.DLL SRC/MODULES/MKPASSWD.DLL \ - SRC/MODULES/SVSNICK.DLL \ - SRC/MODULES/CHGNAME.DLL \ - SRC/MODULES/LAG.DLL SRC/MODULES/MESSAGE.DLL \ - SRC/MODULES/OPER.DLL SRC/MODULES/PINGPONG.DLL SRC/MODULES/QUIT.DLL \ - SRC/MODULES/SENDUMODE.DLL \ - SRC/MODULES/SQLINE.DLL SRC/MODULES/KILL.DLL SRC/MODULES/TSCTL.DLL \ - SRC/MODULES/UNSQLINE.DLL \ - SRC/MODULES/WHOIS.DLL SRC/MODULES/TKL.DLL SRC/MODULES/VHOST.DLL \ - SRC/MODULES/CYCLE.DLL SRC/MODULES/SVSJOIN.DLL SRC/MODULES/SVSPART.DLL \ - SRC/MODULES/SVSLUSERS.DLL SRC/MODULES/SVSWATCH.DLL SRC/MODULES/SVSSILENCE.DLL \ - SRC/MODULES/SENDSNO.DLL SRC/MODULES/SVSSNO.DLL SRC/MODULES/SAJOIN.DLL \ - SRC/MODULES/SAPART.DLL SRC/MODULES/SAMODE.DLL SRC/MODULES/KICK.DLL \ - SRC/MODULES/TOPIC.DLL SRC/MODULES/INVITE.DLL SRC/MODULES/LIST.DLL \ - SRC/MODULES/TIME.DLL SRC/MODULES/SVSKILL.DLL SRC/MODULES/KNOCK.DLL \ - SRC/MODULES/UMODE2.DLL SRC/MODULES/SQUIT.DLL SRC/MODULES/PROTOCTL.DLL \ - SRC/MODULES/SJOIN.DLL SRC/MODULES/PASS.DLL SRC/MODULES/USERHOST.DLL \ - SRC/MODULES/ISON.DLL SRC/MODULES/SILENCE.DLL \ - SRC/MODULES/ADDMOTD.DLL SRC/MODULES/ADDOMOTD.DLL SRC/MODULES/WALLOPS.DLL \ - SRC/MODULES/GLOBOPS.DLL SRC/MODULES/LOCOPS.DLL \ - SRC/MODULES/ADMIN.DLL SRC/MODULES/TRACE.DLL SRC/MODULES/NETINFO.DLL \ - SRC/MODULES/LINKS.DLL SRC/MODULES/HELP.DLL SRC/MODULES/RULES.DLL \ - SRC/MODULES/CLOSE.DLL SRC/MODULES/MAP.DLL SRC/MODULES/EOS.DLL \ - SRC/MODULES/SERVER.DLL SRC/MODULES/STATS.DLL \ - SRC/MODULES/DCCDENY.DLL SRC/MODULES/WHOWAS.DLL \ - SRC/MODULES/CONNECT.DLL SRC/MODULES/DCCALLOW.DLL SRC/MODULES/USERIP.DLL \ - SRC/MODULES/NICK.DLL SRC/MODULES/USER.DLL SRC/MODULES/MODE.DLL \ - SRC/MODULES/WATCH.DLL SRC/MODULES/PART.DLL SRC/MODULES/JOIN.DLL \ - SRC/MODULES/MOTD.DLL SRC/MODULES/OPERMOTD.DLL SRC/MODULES/BOTMOTD.DLL \ - SRC/MODULES/LUSERS.DLL SRC/MODULES/NAMES.DLL SRC/MODULES/SVSNOLAG.DLL \ - SRC/MODULES/STARTTLS.DLL \ - SRC/MODULES/WEBREDIR.DLL \ - SRC/MODULES/CAP.DLL \ - SRC/MODULES/SASL.DLL \ - SRC/MODULES/TLS_ANTIDOS.DLL \ - SRC/MODULES/MD.DLL \ - SRC/MODULES/CERTFP.DLL \ - SRC/MODULES/WEBIRC.DLL \ - SRC/MODULES/WEBSOCKET.DLL \ - SRC/MODULES/BLACKLIST.DLL \ - SRC/MODULES/JOINTHROTTLE.DLL \ - SRC/MODULES/ANTIRANDOM.DLL \ - SRC/MODULES/HIDESERVER.DLL \ - SRC/MODULES/JUMPSERVER.DLL \ - SRC/MODULES/IRCOPS.DLL \ - SRC/MODULES/STAFF.DLL \ - SRC/MODULES/NOCODES.DLL \ - SRC/MODULES/CHARSYS.DLL \ - SRC/MODULES/ANTIMIXEDUTF8.DLL \ - SRC/MODULES/AUTHPROMPT.DLL \ - SRC/MODULES/SINFO.DLL \ - SRC/MODULES/REPUTATION.DLL \ - SRC/MODULES/CONNTHROTTLE.DLL \ - SRC/MODULES/CHANMODES/CENSOR.DLL \ - SRC/MODULES/CHANMODES/DELAYJOIN.DLL \ - SRC/MODULES/CHANMODES/FLOODPROT.DLL \ - SRC/MODULES/CHANMODES/ISSECURE.DLL \ - SRC/MODULES/CHANMODES/LINK.DLL \ - SRC/MODULES/CHANMODES/NOCOLOR.DLL \ - SRC/MODULES/CHANMODES/NOCTCP.DLL \ - SRC/MODULES/CHANMODES/NOINVITE.DLL \ - SRC/MODULES/CHANMODES/NOKICK.DLL \ - SRC/MODULES/CHANMODES/NOKNOCK.DLL \ - SRC/MODULES/CHANMODES/NONICKCHANGE.DLL \ - SRC/MODULES/CHANMODES/NONOTICE.DLL \ - SRC/MODULES/CHANMODES/OPERONLY.DLL \ - SRC/MODULES/CHANMODES/PERMANENT.DLL \ - SRC/MODULES/CHANMODES/REGONLY.DLL \ - SRC/MODULES/CHANMODES/REGONLYSPEAK.DLL \ - SRC/MODULES/CHANMODES/SECUREONLY.DLL \ - SRC/MODULES/CHANMODES/STRIPCOLOR.DLL \ - SRC/MODULES/CHANMODES/HISTORY.DLL \ - SRC/MODULES/USERMODES/CENSOR.DLL \ - SRC/MODULES/USERMODES/NOCTCP.DLL \ - SRC/MODULES/USERMODES/BOT.DLL \ - SRC/MODULES/USERMODES/SERVICEBOT.DLL \ - SRC/MODULES/USERMODES/SHOWWHOIS.DLL \ - SRC/MODULES/USERMODES/PRIVACY.DLL \ - SRC/MODULES/USERMODES/NOKICK.DLL \ - SRC/MODULES/USERMODES/REGONLYMSG.DLL \ - SRC/MODULES/USERMODES/PRIVDEAF.DLL \ - SRC/MODULES/USERMODES/SECUREONLYMSG.DLL \ - SRC/MODULES/SNOMASKS/DCCREJECT.DLL \ - SRC/MODULES/EXTBANS/ACCOUNT.DLL \ - SRC/MODULES/EXTBANS/INCHANNEL.DLL \ - SRC/MODULES/EXTBANS/JOIN.DLL \ - SRC/MODULES/EXTBANS/NICKCHANGE.DLL \ - SRC/MODULES/EXTBANS/QUIET.DLL \ - SRC/MODULES/EXTBANS/REALNAME.DLL \ - SRC/MODULES/EXTBANS/OPERCLASS.DLL \ - SRC/MODULES/EXTBANS/CERTFP.DLL \ - SRC/MODULES/EXTBANS/TEXTBAN.DLL \ - SRC/MODULES/EXTBANS/MSGBYPASS.DLL \ - SRC/MODULES/EXTBANS/TIMEDBAN.DLL \ - SRC/MODULES/EXTBANS/PARTMSG.DLL \ - SRC/MODULES/EXTBANS/SECURITYGROUP.DLL \ - SRC/MODULES/ACCOUNT-NOTIFY.DLL \ - SRC/MODULES/MESSAGE-TAGS.DLL \ - SRC/MODULES/BATCH.DLL \ - SRC/MODULES/ACCOUNT-TAG.DLL \ - SRC/MODULES/LABELED-RESPONSE.DLL \ - SRC/MODULES/LINK-SECURITY.DLL \ - SRC/MODULES/MESSAGE-IDS.DLL \ - SRC/MODULES/PLAINTEXT-POLICY.DLL \ - SRC/MODULES/SERVER-TIME.DLL \ - SRC/MODULES/STS.DLL \ - SRC/MODULES/TKLDB.DLL \ - SRC/MODULES/CHANNELDB.DLL \ - SRC/MODULES/HISTORY_BACKEND_MEM.DLL \ - SRC/MODULES/HISTORY_BACKEND_NULL.DLL \ - SRC/MODULES/RESTRICT-COMMANDS.DLL \ - SRC/MODULES/RMTKL.DLL \ - SRC/MODULES/ECHO-MESSAGE.DLL \ - SRC/MODULES/USERIP-TAG.DLL \ - SRC/MODULES/USERHOST-TAG.DLL \ - SRC/MODULES/BOT-TAG.DLL \ - SRC/MODULES/REPLY-TAG.DLL \ - SRC/MODULES/REQUIRE-MODULE.DLL \ - SRC/MODULES/IDENT_LOOKUP.DLL \ - SRC/MODULES/HISTORY.DLL \ - SRC/MODULES/CHATHISTORY.DLL \ - SRC/MODULES/TARGETFLOODPROT.DLL \ - SRC/MODULES/TYPING-INDICATOR.DLL \ - SRC/MODULES/CLIENTTAGDENY.DLL +DLL_FILES=\ + src/modules/account-notify.dll \ + src/modules/account-tag.dll \ + src/modules/addmotd.dll \ + src/modules/addomotd.dll \ + src/modules/admin.dll \ + src/modules/antimixedutf8.dll \ + src/modules/antirandom.dll \ + src/modules/authprompt.dll \ + src/modules/away.dll \ + src/modules/batch.dll \ + src/modules/blacklist.dll \ + src/modules/botmotd.dll \ + src/modules/bot-tag.dll \ + src/modules/cap.dll \ + src/modules/certfp.dll \ + src/modules/chanmodes/chanowner.dll \ + src/modules/chanmodes/chanadmin.dll \ + src/modules/chanmodes/chanop.dll \ + src/modules/chanmodes/halfop.dll \ + src/modules/chanmodes/voice.dll \ + src/modules/chanmodes/censor.dll \ + src/modules/chanmodes/delayjoin.dll \ + src/modules/chanmodes/floodprot.dll \ + src/modules/chanmodes/history.dll \ + src/modules/chanmodes/inviteonly.dll \ + src/modules/chanmodes/isregistered.dll \ + src/modules/chanmodes/issecure.dll \ + src/modules/chanmodes/key.dll \ + src/modules/chanmodes/limit.dll \ + src/modules/chanmodes/link.dll \ + src/modules/chanmodes/moderated.dll \ + src/modules/chanmodes/nocolor.dll \ + src/modules/chanmodes/noctcp.dll \ + src/modules/chanmodes/noexternalmsgs.dll \ + src/modules/chanmodes/noinvite.dll \ + src/modules/chanmodes/nokick.dll \ + src/modules/chanmodes/noknock.dll \ + src/modules/chanmodes/nonickchange.dll \ + src/modules/chanmodes/nonotice.dll \ + src/modules/chanmodes/operonly.dll \ + src/modules/chanmodes/permanent.dll \ + src/modules/chanmodes/private.dll \ + src/modules/chanmodes/regonly.dll \ + src/modules/chanmodes/regonlyspeak.dll \ + src/modules/chanmodes/secret.dll \ + src/modules/chanmodes/secureonly.dll \ + src/modules/chanmodes/stripcolor.dll \ + src/modules/chanmodes/topiclimit.dll \ + src/modules/channeldb.dll \ + src/modules/charsys.dll \ + src/modules/chathistory.dll \ + src/modules/chghost.dll \ + src/modules/chgident.dll \ + src/modules/chgname.dll \ + src/modules/clienttagdeny.dll \ + src/modules/close.dll \ + src/modules/connect.dll \ + src/modules/connthrottle.dll \ + src/modules/cycle.dll \ + src/modules/dccallow.dll \ + src/modules/dccdeny.dll \ + src/modules/echo-message.dll \ + src/modules/eos.dll \ + src/modules/extbans/account.dll \ + src/modules/extbans/certfp.dll \ + src/modules/extbans/country.dll \ + src/modules/extbans/inchannel.dll \ + src/modules/extbans/join.dll \ + src/modules/extbans/msgbypass.dll \ + src/modules/extbans/nickchange.dll \ + src/modules/extbans/operclass.dll \ + src/modules/extbans/partmsg.dll \ + src/modules/extbans/quiet.dll \ + src/modules/extbans/realname.dll \ + src/modules/extbans/securitygroup.dll \ + src/modules/extbans/textban.dll \ + src/modules/extbans/timedban.dll \ + src/modules/extended-monitor.dll \ + src/modules/extjwt.dll \ + src/modules/geoip_base.dll \ + src/modules/geoip_classic.dll \ + src/modules/geoip_csv.dll \ + src/modules/globops.dll \ + src/modules/help.dll \ + src/modules/hideserver.dll \ + src/modules/history_backend_mem.dll \ + src/modules/history_backend_null.dll \ + src/modules/history.dll \ + src/modules/ident_lookup.dll \ + src/modules/invite.dll \ + src/modules/ircops.dll \ + src/modules/ison.dll \ + src/modules/join.dll \ + src/modules/jointhrottle.dll \ + src/modules/json-log-tag.dll \ + src/modules/jumpserver.dll \ + src/modules/kick.dll \ + src/modules/kill.dll \ + src/modules/knock.dll \ + src/modules/labeled-response.dll \ + src/modules/lag.dll \ + src/modules/links.dll \ + src/modules/link-security.dll \ + src/modules/list.dll \ + src/modules/locops.dll \ + src/modules/lusers.dll \ + src/modules/map.dll \ + src/modules/md.dll \ + src/modules/message.dll \ + src/modules/message-ids.dll \ + src/modules/message-tags.dll \ + src/modules/mkpasswd.dll \ + src/modules/mode.dll \ + src/modules/monitor.dll \ + src/modules/motd.dll \ + src/modules/names.dll \ + src/modules/netinfo.dll \ + src/modules/nick.dll \ + src/modules/nocodes.dll \ + src/modules/cloak_md5.dll \ + src/modules/cloak_none.dll \ + src/modules/cloak_sha256.dll \ + src/modules/oper.dll \ + src/modules/operinfo.dll \ + src/modules/opermotd.dll \ + src/modules/part.dll \ + src/modules/pass.dll \ + src/modules/pingpong.dll \ + src/modules/plaintext-policy.dll \ + src/modules/protoctl.dll \ + src/modules/quit.dll \ + src/modules/reply-tag.dll \ + src/modules/reputation.dll \ + src/modules/require-module.dll \ + src/modules/restrict-commands.dll \ + src/modules/rmtkl.dll \ + src/modules/rules.dll \ + src/modules/sajoin.dll \ + src/modules/samode.dll \ + src/modules/sapart.dll \ + src/modules/sasl.dll \ + src/modules/sdesc.dll \ + src/modules/sendsno.dll \ + src/modules/sendumode.dll \ + src/modules/server.dll \ + src/modules/server-time.dll \ + src/modules/sethost.dll \ + src/modules/setident.dll \ + src/modules/setname.dll \ + src/modules/silence.dll \ + src/modules/sinfo.dll \ + src/modules/sjoin.dll \ + src/modules/slog.dll \ + src/modules/sqline.dll \ + src/modules/squit.dll \ + src/modules/staff.dll \ + src/modules/starttls.dll \ + src/modules/stats.dll \ + src/modules/sts.dll \ + src/modules/svsjoin.dll \ + src/modules/svskill.dll \ + src/modules/svslusers.dll \ + src/modules/svsmode.dll \ + src/modules/svsmotd.dll \ + src/modules/svsnick.dll \ + src/modules/svsnline.dll \ + src/modules/svsnolag.dll \ + src/modules/svsnoop.dll \ + src/modules/svspart.dll \ + src/modules/svssilence.dll \ + src/modules/svssno.dll \ + src/modules/svswatch.dll \ + src/modules/swhois.dll \ + src/modules/targetfloodprot.dll \ + src/modules/time.dll \ + src/modules/tkl.dll \ + src/modules/tkldb.dll \ + src/modules/tls_antidos.dll \ + src/modules/tls_cipher.dll \ + src/modules/topic.dll \ + src/modules/trace.dll \ + src/modules/tsctl.dll \ + src/modules/typing-indicator.dll \ + src/modules/umode2.dll \ + src/modules/unreal_server_compat.dll \ + src/modules/unsqline.dll \ + src/modules/user.dll \ + src/modules/userhost.dll \ + src/modules/userhost-tag.dll \ + src/modules/userip.dll \ + src/modules/userip-tag.dll \ + src/modules/usermodes/bot.dll \ + src/modules/usermodes/censor.dll \ + src/modules/usermodes/noctcp.dll \ + src/modules/usermodes/nokick.dll \ + src/modules/usermodes/privacy.dll \ + src/modules/usermodes/privdeaf.dll \ + src/modules/usermodes/regonlymsg.dll \ + src/modules/usermodes/secureonlymsg.dll \ + src/modules/usermodes/servicebot.dll \ + src/modules/usermodes/showwhois.dll \ + src/modules/usermodes/wallops.dll \ + src/modules/vhost.dll \ + src/modules/watch-backend.dll \ + src/modules/watch.dll \ + src/modules/webirc.dll \ + src/modules/webredir.dll \ + src/modules/websocket.dll \ + src/modules/whois.dll \ + src/modules/who_old.dll \ + src/modules/whowas.dll \ + src/modules/whox.dll ALL: CONF UNREALSVC.EXE UnrealIRCd.exe MODULES CLEAN: - -@erase *.obj >NUL - -@erase src\*.obj >NUL - -@erase src\win.res >NUL - -@erase src\version.c >NUL - -@erase src\windows\*.obj >NUL - -@erase src\modules\*.obj >NUL - -@erase src\modules\*.lib >NUL - -@erase src\modules\*.pdb >NUL - -@erase src\modules\*.dll >NUL - -@erase src\modules\*.exp >NUL - -@erase src\modules\*.ilk >NUL - -@erase src\modules\chanmodes\*.obj >NUL - -@erase src\modules\chanmodes\*.lib >NUL - -@erase src\modules\chanmodes\*.pdb >NUL - -@erase src\modules\chanmodes\*.dll >NUL - -@erase src\modules\chanmodes\*.exp >NUL - -@erase src\modules\chanmodes\*.ilk >NUL - -@erase src\modules\usermodes\*.obj >NUL - -@erase src\modules\usermodes\*.lib >NUL - -@erase src\modules\usermodes\*.pdb >NUL - -@erase src\modules\usermodes\*.dll >NUL - -@erase src\modules\usermodes\*.exp >NUL - -@erase src\modules\usermodes\*.ilk >NUL - -@erase src\modules\snomasks\*.obj >NUL - -@erase src\modules\snomasks\*.lib >NUL - -@erase src\modules\snomasks\*.pdb >NUL - -@erase src\modules\snomasks\*.dll >NUL - -@erase src\modules\snomasks\*.exp >NUL - -@erase src\modules\snomasks\*.ilk >NUL - -@erase src\modules\extbans\*.obj >NUL - -@erase src\modules\extbans\*.lib >NUL - -@erase src\modules\extbans\*.pdb >NUL - -@erase src\modules\extbans\*.dll >NUL - -@erase src\modules\extbans\*.exp >NUL - -@erase src\modules\extbans\*.ilk >NUL - -@erase .\*.exe >NUL - -@erase UnrealIRCd.lib >NUL + -@del /Q /S *.dll *.exe *.obj *.pdb *.res *.lib *.exp *.ilk src\version.c >NUL -./UNREALSVC.EXE: SRC/UNREALSVC.OBJ SRC/WINDOWS/UNREALSVC.RES +UNREALSVC.EXE: SRC/UNREALSVC.OBJ SRC/WINDOWS/UNREALSVC.RES $(LINK) $(DBGLFLAGST) advapi32.lib src/unrealsvc.obj src/windows/unrealsvc.res CONF: @@ -361,9 +424,7 @@ CONF: $(CC) src/windows/config.c -@config.exe - - -./UnrealIRCd.exe: $(OBJ_FILES) src/windows/win.res +UnrealIRCd.exe: $(OBJ_FILES) src/windows/win.res $(LINK) $(LFLAGS) $(OBJ_FILES) src/windows/win.res /MAP -@erase src\windows\win.res $(MT) -manifest src\windows\UnrealIRCd.exe.manifest -outputresource:UnrealIRCd.exe;1 @@ -430,9 +491,6 @@ src/conf_preprocessor.obj: src/conf_preprocessor.c $(INCLUDES) src/debug.obj: src/debug.c $(INCLUDES) $(CC) $(CFLAGS) src/debug.c -src/numeric.obj: src/numeric.c $(INCLUDES) - $(CC) $(CFLAGS) src/numeric.c - src/misc.obj: src/misc.c $(INCLUDES) ./include/dbuf.h $(CC) $(CFLAGS) src/misc.c @@ -509,8 +567,8 @@ src/mempool.obj: src/mempool.c $(INCLUDES) src/dispatch.obj: src/dispatch.c $(INCLUDES) $(CC) $(CFLAGS) src/dispatch.c -src/url.obj: src/url.c $(INCLUDES) ./include/url.h - $(CC) $(CFLAGS) src/url.c +src/url_curl.obj: src/url_curl.c $(INCLUDES) + $(CC) $(CFLAGS) src/url_curl.c src/api-extban.obj: src/api-extban.c $(INCLUDES) $(CC) $(CFLAGS) src/api-extban.c @@ -542,9 +600,6 @@ src/crypt_blowfish.obj: src/crypt_blowfish.c $(INCLUDES) src/operclass.obj: src/operclass.c $(INCLUDES) ./include/dbuf.h $(CC) $(CFLAGS) src/operclass.c -src/updconf.obj: src/updconf.c $(INCLUDES) ./include/dbuf.h - $(CC) $(CFLAGS) src/updconf.c - src/crashreport.obj: src/crashreport.c $(INCLUDES) ./include/dbuf.h $(CC) $(CFLAGS) src/crashreport.c @@ -554,6 +609,12 @@ src/unrealdb.obj: src/unrealdb.c $(INCLUDES) ./include/dbuf.h src/utf8.obj: src/utf8.c $(INCLUDES) ./include/dbuf.h $(CC) $(CFLAGS) src/utf8.c +src/openssl_hostname_validation.obj: src/openssl_hostname_validation.c $(INCLUDES) ./include/dbuf.h + $(CC) $(CFLAGS) src/openssl_hostname_validation.c + +src/log.obj: src/log.c $(INCLUDES) ./include/dbuf.h + $(CC) $(CFLAGS) src/log.c + src/windows/win.res: src/windows/wingui.rc $(RC) /l 0x409 /fosrc/windows/win.res /i ./include /i ./src \ /d NDEBUG src/windows/wingui.rc @@ -575,556 +636,642 @@ SYMBOLFILE: MODULES: $(DLL_FILES) -src/modules/cloak.dll: src/modules/cloak.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/cloak.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/chghost.dll: src/modules/chghost.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/chghost.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/chgident.dll: src/modules/chgident.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/chgident.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sdesc.dll: src/modules/sdesc.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sdesc.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sethost.dll: src/modules/sethost.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sethost.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/setident.dll: src/modules/setident.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/setident.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/setname.dll: src/modules/setname.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/setname.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svsmotd.dll: src/modules/svsmotd.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svsmotd.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svsmode.dll: src/modules/svsmode.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svsmode.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/tkl.dll: src/modules/tkl.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/tkl.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/swhois.dll: src/modules/swhois.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/swhois.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svsnline.dll: src/modules/svsnline.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svsnline.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/who_old.dll: src/modules/who_old.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/who_old.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/whox.dll: src/modules/whox.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/whox.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/away.dll: src/modules/away.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/away.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/mkpasswd.dll: src/modules/mkpasswd.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/mkpasswd.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svsnoop.dll: src/modules/svsnoop.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svsnoop.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svsnick.dll: src/modules/svsnick.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svsnick.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/chgname.dll: src/modules/chgname.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/chgname.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/kill.dll: src/modules/kill.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/kill.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/lag.dll: src/modules/lag.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/lag.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/message.dll: src/modules/message.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/message.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/oper.dll: src/modules/oper.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/oper.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/pingpong.dll: src/modules/pingpong.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/pingpong.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/quit.dll: src/modules/quit.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/quit.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sendumode.dll: src/modules/sendumode.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sendumode.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sqline.dll: src/modules/sqline.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sqline.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/tsctl.dll: src/modules/tsctl.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/tsctl.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/unsqline.dll: src/modules/unsqline.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/unsqline.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/whois.dll: src/modules/whois.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/whois.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/vhost.dll: src/modules/vhost.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/vhost.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/cycle.dll: src/modules/cycle.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/cycle.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svsjoin.dll: src/modules/svsjoin.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svsjoin.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svspart.dll: src/modules/svspart.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svspart.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svslusers.dll: src/modules/svslusers.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svslusers.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svswatch.dll: src/modules/svswatch.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svswatch.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svssilence.dll: src/modules/svssilence.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svssilence.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sendsno.dll: src/modules/sendsno.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sendsno.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svssno.dll: src/modules/svssno.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svssno.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sajoin.dll: src/modules/sajoin.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sajoin.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sapart.dll: src/modules/sapart.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sapart.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/samode.dll: src/modules/samode.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/samode.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/kick.dll: src/modules/kick.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/kick.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/topic.dll: src/modules/topic.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/topic.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/invite.dll: src/modules/invite.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/invite.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/list.dll: src/modules/list.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/list.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/time.dll: src/modules/time.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/time.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svskill.dll: src/modules/svskill.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svskill.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sjoin.dll: src/modules/sjoin.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sjoin.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/pass.dll: src/modules/pass.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/pass.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/userhost.dll: src/modules/userhost.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/userhost.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/ison.dll: src/modules/ison.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/ison.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/silence.dll: src/modules/silence.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/silence.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/knock.dll: src/modules/knock.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/knock.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/umode2.dll: src/modules/umode2.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/umode2.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/squit.dll: src/modules/squit.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/squit.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/protoctl.dll: src/modules/protoctl.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/protoctl.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/addmotd.dll: src/modules/addmotd.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/addmotd.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/addomotd.dll: src/modules/addomotd.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/addomotd.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/wallops.dll: src/modules/wallops.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/wallops.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/admin.dll: src/modules/admin.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/admin.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/globops.dll: src/modules/globops.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/globops.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/locops.dll: src/modules/locops.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/locops.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/trace.dll: src/modules/trace.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/trace.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/netinfo.dll: src/modules/netinfo.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/netinfo.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/links.dll: src/modules/links.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/links.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/help.dll: src/modules/help.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/help.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/rules.dll: src/modules/rules.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/rules.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/close.dll: src/modules/close.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/close.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/map.dll: src/modules/map.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/map.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/eos.dll: src/modules/eos.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/eos.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/server.dll: src/modules/server.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/server.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/stats.dll: src/modules/stats.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/stats.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/dccdeny.dll: src/modules/dccdeny.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/dccdeny.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/whowas.dll: src/modules/whowas.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/whowas.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/connect.dll: src/modules/connect.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/connect.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/dccallow.dll: src/modules/dccallow.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/dccallow.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/userip.dll: src/modules/userip.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/userip.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/nick.dll: src/modules/nick.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/nick.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/user.dll: src/modules/user.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/user.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/mode.dll: src/modules/mode.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/mode.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/watch.dll: src/modules/watch.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/watch.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/part.dll: src/modules/part.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/part.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/join.dll: src/modules/join.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/join.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/motd.dll: src/modules/motd.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/motd.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/opermotd.dll: src/modules/opermotd.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/opermotd.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/botmotd.dll: src/modules/botmotd.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/botmotd.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/lusers.dll: src/modules/lusers.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/lusers.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/names.dll: src/modules/names.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/names.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/md.dll: src/modules/md.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/md.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/certfp.dll: src/modules/certfp.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/certfp.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/webirc.dll: src/modules/webirc.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/webirc.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/websocket.dll: src/modules/websocket.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/websocket.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/blacklist.dll: src/modules/blacklist.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/blacklist.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/jointhrottle.dll: src/modules/jointhrottle.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/jointhrottle.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/svsnolag.dll: src/modules/svsnolag.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/svsnolag.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/starttls.dll: src/modules/starttls.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/starttls.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/webredir.dll: src/modules/webredir.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/webredir.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/cap.dll: src/modules/cap.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/cap.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sasl.dll: src/modules/sasl.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sasl.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/tls_antidos.dll: src/modules/tls_antidos.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/tls_antidos.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/antirandom.dll: src/modules/antirandom.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/antirandom.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/hideserver.dll: src/modules/hideserver.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/hideserver.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/jumpserver.dll: src/modules/jumpserver.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/jumpserver.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/ircops.dll: src/modules/ircops.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/ircops.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/staff.dll: src/modules/staff.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/staff.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/nocodes.dll: src/modules/nocodes.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/nocodes.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/charsys.dll: src/modules/charsys.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/charsys.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/antimixedutf8.dll: src/modules/antimixedutf8.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/antimixedutf8.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/authprompt.dll: src/modules/authprompt.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/authprompt.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/sinfo.dll: src/modules/sinfo.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/sinfo.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/reputation.dll: src/modules/reputation.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/reputation.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/connthrottle.dll: src/modules/connthrottle.c $(INCLUDES) - $(CC) $(MODCFLAGS) src/modules/connthrottle.c /Fesrc/modules/ /Fosrc/modules/ $(MODLFLAGS) - -src/modules/chanmodes/censor.dll: src/modules/chanmodes/censor.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/censor.c $(MODLFLAGS) - -src/modules/chanmodes/delayjoin.dll: src/modules/chanmodes/delayjoin.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/delayjoin.c $(MODLFLAGS) - -src/modules/chanmodes/floodprot.dll: src/modules/chanmodes/floodprot.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/floodprot.c $(MODLFLAGS) - -src/modules/chanmodes/issecure.dll: src/modules/chanmodes/issecure.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/issecure.c $(MODLFLAGS) - -src/modules/chanmodes/link.dll: src/modules/chanmodes/link.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/link.c $(MODLFLAGS) - -src/modules/chanmodes/nocolor.dll: src/modules/chanmodes/nocolor.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/nocolor.c $(MODLFLAGS) - -src/modules/chanmodes/noctcp.dll: src/modules/chanmodes/noctcp.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/noctcp.c $(MODLFLAGS) - -src/modules/chanmodes/noinvite.dll: src/modules/chanmodes/noinvite.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/noinvite.c $(MODLFLAGS) - -src/modules/chanmodes/nokick.dll: src/modules/chanmodes/nokick.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/nokick.c $(MODLFLAGS) - -src/modules/chanmodes/noknock.dll: src/modules/chanmodes/noknock.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/noknock.c $(MODLFLAGS) - -src/modules/chanmodes/nonickchange.dll: src/modules/chanmodes/nonickchange.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/nonickchange.c $(MODLFLAGS) - -src/modules/chanmodes/nonotice.dll: src/modules/chanmodes/nonotice.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/nonotice.c $(MODLFLAGS) - -src/modules/chanmodes/operonly.dll: src/modules/chanmodes/operonly.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/operonly.c $(MODLFLAGS) - -src/modules/chanmodes/permanent.dll: src/modules/chanmodes/permanent.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/permanent.c $(MODLFLAGS) - -src/modules/chanmodes/regonly.dll: src/modules/chanmodes/regonly.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/regonly.c $(MODLFLAGS) - -src/modules/chanmodes/regonlyspeak.dll: src/modules/chanmodes/regonlyspeak.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/regonlyspeak.c $(MODLFLAGS) - -src/modules/chanmodes/secureonly.dll: src/modules/chanmodes/secureonly.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/secureonly.c $(MODLFLAGS) - -src/modules/chanmodes/stripcolor.dll: src/modules/chanmodes/stripcolor.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/stripcolor.c $(MODLFLAGS) - -src/modules/chanmodes/history.dll: src/modules/chanmodes/history.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/chanmodes/ /Fesrc/modules/chanmodes/ src/modules/chanmodes/history.c $(MODLFLAGS) - -src/modules/usermodes/censor.dll: src/modules/usermodes/censor.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/censor.c $(MODLFLAGS) - -src/modules/usermodes/noctcp.dll: src/modules/usermodes/noctcp.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/noctcp.c $(MODLFLAGS) - -src/modules/usermodes/bot.dll: src/modules/usermodes/bot.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/bot.c $(MODLFLAGS) - -src/modules/usermodes/servicebot.dll: src/modules/usermodes/servicebot.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/servicebot.c $(MODLFLAGS) - -src/modules/usermodes/showwhois.dll: src/modules/usermodes/showwhois.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/showwhois.c $(MODLFLAGS) - -src/modules/usermodes/privacy.dll: src/modules/usermodes/privacy.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/privacy.c $(MODLFLAGS) - -src/modules/usermodes/nokick.dll: src/modules/usermodes/nokick.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/nokick.c $(MODLFLAGS) - -src/modules/usermodes/regonlymsg.dll: src/modules/usermodes/regonlymsg.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/regonlymsg.c $(MODLFLAGS) - -src/modules/usermodes/privdeaf.dll: src/modules/usermodes/privdeaf.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/privdeaf.c $(MODLFLAGS) - -src/modules/usermodes/secureonlymsg.dll: src/modules/usermodes/secureonlymsg.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/usermodes/ /Fesrc/modules/usermodes/ src/modules/usermodes/secureonlymsg.c $(MODLFLAGS) - -src/modules/snomasks/dccreject.dll: src/modules/snomasks/dccreject.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/snomasks/ /Fesrc/modules/snomasks/ src/modules/snomasks/dccreject.c $(MODLFLAGS) - -src/modules/extbans/account.dll: src/modules/extbans/account.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/account.c $(MODLFLAGS) - -src/modules/extbans/inchannel.dll: src/modules/extbans/inchannel.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/inchannel.c $(MODLFLAGS) - -src/modules/extbans/join.dll: src/modules/extbans/join.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/join.c $(MODLFLAGS) - -src/modules/extbans/nickchange.dll: src/modules/extbans/nickchange.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/nickchange.c $(MODLFLAGS) - -src/modules/extbans/quiet.dll: src/modules/extbans/quiet.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/quiet.c $(MODLFLAGS) - -src/modules/extbans/realname.dll: src/modules/extbans/realname.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/realname.c $(MODLFLAGS) - -src/modules/extbans/operclass.dll: src/modules/extbans/operclass.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/operclass.c $(MODLFLAGS) - -src/modules/extbans/certfp.dll: src/modules/extbans/certfp.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/certfp.c $(MODLFLAGS) - -src/modules/extbans/textban.dll: src/modules/extbans/textban.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/textban.c $(MODLFLAGS) - -src/modules/extbans/msgbypass.dll: src/modules/extbans/msgbypass.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/msgbypass.c $(MODLFLAGS) - -src/modules/extbans/timedban.dll: src/modules/extbans/timedban.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/timedban.c $(MODLFLAGS) - -src/modules/extbans/partmsg.dll: src/modules/extbans/partmsg.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/partmsg.c $(MODLFLAGS) - -src/modules/extbans/securitygroup.dll: src/modules/extbans/securitygroup.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/extbans/ /Fesrc/modules/extbans/ src/modules/extbans/securitygroup.c $(MODLFLAGS) - src/modules/account-notify.dll: src/modules/account-notify.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/account-notify.c $(MODLFLAGS) - -src/modules/message-tags.dll: src/modules/message-tags.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/message-tags.c $(MODLFLAGS) - -src/modules/batch.dll: src/modules/batch.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/batch.c $(MODLFLAGS) + $(CC) $(MODCFLAGS) src/modules/account-notify.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/account-notify.pdb $(MODLFLAGS) src/modules/account-tag.dll: src/modules/account-tag.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/account-tag.c $(MODLFLAGS) + $(CC) $(MODCFLAGS) src/modules/account-tag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/account-tag.pdb $(MODLFLAGS) -src/modules/labeled-response.dll: src/modules/labeled-response.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/labeled-response.c $(MODLFLAGS) +src/modules/addmotd.dll: src/modules/addmotd.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/addmotd.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/addmotd.pdb $(MODLFLAGS) -src/modules/link-security.dll: src/modules/link-security.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/link-security.c $(MODLFLAGS) +src/modules/addomotd.dll: src/modules/addomotd.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/addomotd.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/addomotd.pdb $(MODLFLAGS) -src/modules/message-ids.dll: src/modules/message-ids.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/message-ids.c $(MODLFLAGS) +src/modules/admin.dll: src/modules/admin.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/admin.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/admin.pdb $(MODLFLAGS) -src/modules/plaintext-policy.dll: src/modules/plaintext-policy.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/plaintext-policy.c $(MODLFLAGS) +src/modules/antimixedutf8.dll: src/modules/antimixedutf8.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/antimixedutf8.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/antimixedutf8.pdb $(MODLFLAGS) -src/modules/server-time.dll: src/modules/server-time.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/server-time.c $(MODLFLAGS) +src/modules/antirandom.dll: src/modules/antirandom.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/antirandom.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/antirandom.pdb $(MODLFLAGS) -src/modules/sts.dll: src/modules/sts.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/sts.c $(MODLFLAGS) +src/modules/authprompt.dll: src/modules/authprompt.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/authprompt.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/authprompt.pdb $(MODLFLAGS) -src/modules/tkldb.dll: src/modules/tkldb.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/tkldb.c $(MODLFLAGS) +src/modules/away.dll: src/modules/away.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/away.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/away.pdb $(MODLFLAGS) -src/modules/channeldb.dll: src/modules/channeldb.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/channeldb.c $(MODLFLAGS) +src/modules/batch.dll: src/modules/batch.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/batch.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/batch.pdb $(MODLFLAGS) -src/modules/history_backend_mem.dll: src/modules/history_backend_mem.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/history_backend_mem.c $(MODLFLAGS) +src/modules/blacklist.dll: src/modules/blacklist.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/blacklist.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/blacklist.pdb $(MODLFLAGS) -src/modules/history_backend_null.dll: src/modules/history_backend_null.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/history_backend_null.c $(MODLFLAGS) - -src/modules/restrict-commands.dll: src/modules/restrict-commands.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/restrict-commands.c $(MODLFLAGS) - -src/modules/rmtkl.dll: src/modules/rmtkl.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/rmtkl.c $(MODLFLAGS) - -src/modules/echo-message.dll: src/modules/echo-message.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/echo-message.c $(MODLFLAGS) - -src/modules/userip-tag.dll: src/modules/userip-tag.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/userip-tag.c $(MODLFLAGS) - -src/modules/userhost-tag.dll: src/modules/userhost-tag.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/userhost-tag.c $(MODLFLAGS) +src/modules/botmotd.dll: src/modules/botmotd.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/botmotd.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/botmotd.pdb $(MODLFLAGS) src/modules/bot-tag.dll: src/modules/bot-tag.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/bot-tag.c $(MODLFLAGS) + $(CC) $(MODCFLAGS) src/modules/bot-tag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/bot-tag.pdb $(MODLFLAGS) -src/modules/reply-tag.dll: src/modules/reply-tag.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/reply-tag.c $(MODLFLAGS) +src/modules/cap.dll: src/modules/cap.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/cap.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/cap.pdb $(MODLFLAGS) -src/modules/require-module.dll: src/modules/require-module.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/require-module.c $(MODLFLAGS) +src/modules/certfp.dll: src/modules/certfp.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/certfp.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/certfp.pdb $(MODLFLAGS) -src/modules/ident_lookup.dll: src/modules/ident_lookup.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/ident_lookup.c $(MODLFLAGS) +src/modules/chanmodes/chanowner.dll: src/modules/chanmodes/chanowner.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/chanowner.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/chanowner.pdb $(MODLFLAGS) -src/modules/history.dll: src/modules/history.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/history.c $(MODLFLAGS) +src/modules/chanmodes/chanadmin.dll: src/modules/chanmodes/chanadmin.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/chanadmin.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/chanadmin.pdb $(MODLFLAGS) + +src/modules/chanmodes/chanop.dll: src/modules/chanmodes/chanop.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/chanop.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/chanop.pdb $(MODLFLAGS) + +src/modules/chanmodes/halfop.dll: src/modules/chanmodes/halfop.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/halfop.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/halfop.pdb $(MODLFLAGS) + +src/modules/chanmodes/voice.dll: src/modules/chanmodes/voice.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/voice.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/voice.pdb $(MODLFLAGS) + +src/modules/chanmodes/censor.dll: src/modules/chanmodes/censor.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/censor.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/censor.pdb $(MODLFLAGS) + +src/modules/chanmodes/delayjoin.dll: src/modules/chanmodes/delayjoin.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/delayjoin.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/delayjoin.pdb $(MODLFLAGS) + +src/modules/chanmodes/floodprot.dll: src/modules/chanmodes/floodprot.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/floodprot.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/floodprot.pdb $(MODLFLAGS) + +src/modules/chanmodes/history.dll: src/modules/chanmodes/history.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/history.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/history.pdb $(MODLFLAGS) + +src/modules/chanmodes/inviteonly.dll: src/modules/chanmodes/inviteonly.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/inviteonly.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/inviteonly.pdb $(MODLFLAGS) + +src/modules/chanmodes/isregistered.dll: src/modules/chanmodes/isregistered.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/isregistered.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/isregistered.pdb $(MODLFLAGS) + +src/modules/chanmodes/issecure.dll: src/modules/chanmodes/issecure.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/issecure.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/issecure.pdb $(MODLFLAGS) + +src/modules/chanmodes/key.dll: src/modules/chanmodes/key.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/key.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/key.pdb $(MODLFLAGS) + +src/modules/chanmodes/limit.dll: src/modules/chanmodes/limit.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/limit.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/limit.pdb $(MODLFLAGS) + +src/modules/chanmodes/link.dll: src/modules/chanmodes/link.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/link.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/link.pdb $(MODLFLAGS) + +src/modules/chanmodes/moderated.dll: src/modules/chanmodes/moderated.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/moderated.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/moderated.pdb $(MODLFLAGS) + +src/modules/chanmodes/nocolor.dll: src/modules/chanmodes/nocolor.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/nocolor.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/nocolor.pdb $(MODLFLAGS) + +src/modules/chanmodes/noctcp.dll: src/modules/chanmodes/noctcp.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/noctcp.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/noctcp.pdb $(MODLFLAGS) + +src/modules/chanmodes/noexternalmsgs.dll: src/modules/chanmodes/noexternalmsgs.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/noexternalmsgs.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/noexternalmsgs.pdb $(MODLFLAGS) + +src/modules/chanmodes/noinvite.dll: src/modules/chanmodes/noinvite.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/noinvite.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/noinvite.pdb $(MODLFLAGS) + +src/modules/chanmodes/nokick.dll: src/modules/chanmodes/nokick.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/nokick.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/nokick.pdb $(MODLFLAGS) + +src/modules/chanmodes/noknock.dll: src/modules/chanmodes/noknock.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/noknock.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/noknock.pdb $(MODLFLAGS) + +src/modules/chanmodes/nonickchange.dll: src/modules/chanmodes/nonickchange.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/nonickchange.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/nonickchange.pdb $(MODLFLAGS) + +src/modules/chanmodes/nonotice.dll: src/modules/chanmodes/nonotice.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/nonotice.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/nonotice.pdb $(MODLFLAGS) + +src/modules/chanmodes/operonly.dll: src/modules/chanmodes/operonly.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/operonly.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/operonly.pdb $(MODLFLAGS) + +src/modules/chanmodes/permanent.dll: src/modules/chanmodes/permanent.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/permanent.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/permanent.pdb $(MODLFLAGS) + +src/modules/chanmodes/private.dll: src/modules/chanmodes/private.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/private.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/private.pdb $(MODLFLAGS) + +src/modules/chanmodes/regonly.dll: src/modules/chanmodes/regonly.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/regonly.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/regonly.pdb $(MODLFLAGS) + +src/modules/chanmodes/regonlyspeak.dll: src/modules/chanmodes/regonlyspeak.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/regonlyspeak.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/regonlyspeak.pdb $(MODLFLAGS) + +src/modules/chanmodes/secret.dll: src/modules/chanmodes/secret.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/secret.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/secret.pdb $(MODLFLAGS) + +src/modules/chanmodes/secureonly.dll: src/modules/chanmodes/secureonly.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/secureonly.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/secureonly.pdb $(MODLFLAGS) + +src/modules/chanmodes/stripcolor.dll: src/modules/chanmodes/stripcolor.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/stripcolor.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/stripcolor.pdb $(MODLFLAGS) + +src/modules/chanmodes/topiclimit.dll: src/modules/chanmodes/topiclimit.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chanmodes/topiclimit.c /Fesrc/modules/chanmodes/ /Fosrc/modules/chanmodes/ /Fdsrc/modules/chanmodes/topiclimit.pdb $(MODLFLAGS) + +src/modules/channeldb.dll: src/modules/channeldb.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/channeldb.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/channeldb.pdb $(MODLFLAGS) + +src/modules/charsys.dll: src/modules/charsys.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/charsys.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/charsys.pdb $(MODLFLAGS) src/modules/chathistory.dll: src/modules/chathistory.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/chathistory.c $(MODLFLAGS) + $(CC) $(MODCFLAGS) src/modules/chathistory.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/chathistory.pdb $(MODLFLAGS) -src/modules/targetfloodprot.dll: src/modules/targetfloodprot.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/targetfloodprot.c $(MODLFLAGS) +src/modules/chghost.dll: src/modules/chghost.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chghost.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/chghost.pdb $(MODLFLAGS) -src/modules/typing-indicator.dll: src/modules/typing-indicator.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/typing-indicator.c $(MODLFLAGS) +src/modules/chgident.dll: src/modules/chgident.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chgident.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/chgident.pdb $(MODLFLAGS) + +src/modules/chgname.dll: src/modules/chgname.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/chgname.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/chgname.pdb $(MODLFLAGS) src/modules/clienttagdeny.dll: src/modules/clienttagdeny.c $(INCLUDES) - $(CC) $(MODCFLAGS) /Fosrc/modules/ /Fesrc/modules/ src/modules/clienttagdeny.c $(MODLFLAGS) + $(CC) $(MODCFLAGS) src/modules/clienttagdeny.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/clienttagdeny.pdb $(MODLFLAGS) + +src/modules/close.dll: src/modules/close.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/close.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/close.pdb $(MODLFLAGS) + +src/modules/connect.dll: src/modules/connect.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/connect.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/connect.pdb $(MODLFLAGS) + +src/modules/connthrottle.dll: src/modules/connthrottle.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/connthrottle.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/connthrottle.pdb $(MODLFLAGS) + +src/modules/cycle.dll: src/modules/cycle.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/cycle.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/cycle.pdb $(MODLFLAGS) + +src/modules/dccallow.dll: src/modules/dccallow.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/dccallow.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/dccallow.pdb $(MODLFLAGS) + +src/modules/dccdeny.dll: src/modules/dccdeny.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/dccdeny.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/dccdeny.pdb $(MODLFLAGS) + +src/modules/echo-message.dll: src/modules/echo-message.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/echo-message.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/echo-message.pdb $(MODLFLAGS) + +src/modules/eos.dll: src/modules/eos.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/eos.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/eos.pdb $(MODLFLAGS) + +src/modules/extbans/account.dll: src/modules/extbans/account.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/account.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/account.pdb $(MODLFLAGS) + +src/modules/extbans/certfp.dll: src/modules/extbans/certfp.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/certfp.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/certfp.pdb $(MODLFLAGS) + +src/modules/extbans/country.dll: src/modules/extbans/country.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/country.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/country.pdb $(MODLFLAGS) + +src/modules/extbans/inchannel.dll: src/modules/extbans/inchannel.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/inchannel.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/inchannel.pdb $(MODLFLAGS) + +src/modules/extbans/join.dll: src/modules/extbans/join.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/join.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/join.pdb $(MODLFLAGS) + +src/modules/extbans/msgbypass.dll: src/modules/extbans/msgbypass.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/msgbypass.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/msgbypass.pdb $(MODLFLAGS) + +src/modules/extbans/nickchange.dll: src/modules/extbans/nickchange.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/nickchange.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/nickchange.pdb $(MODLFLAGS) + +src/modules/extbans/operclass.dll: src/modules/extbans/operclass.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/operclass.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/operclass.pdb $(MODLFLAGS) + +src/modules/extbans/partmsg.dll: src/modules/extbans/partmsg.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/partmsg.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/partmsg.pdb $(MODLFLAGS) + +src/modules/extbans/quiet.dll: src/modules/extbans/quiet.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/quiet.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/quiet.pdb $(MODLFLAGS) + +src/modules/extbans/realname.dll: src/modules/extbans/realname.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/realname.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/realname.pdb $(MODLFLAGS) + +src/modules/extbans/securitygroup.dll: src/modules/extbans/securitygroup.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/securitygroup.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/securitygroup.pdb $(MODLFLAGS) + +src/modules/extbans/textban.dll: src/modules/extbans/textban.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/textban.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/textban.pdb $(MODLFLAGS) + +src/modules/extbans/timedban.dll: src/modules/extbans/timedban.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extbans/timedban.c /Fesrc/modules/extbans/ /Fosrc/modules/extbans/ /Fdsrc/modules/extbans/timedban.pdb $(MODLFLAGS) + +src/modules/extended-monitor.dll: src/modules/extended-monitor.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extended-monitor.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/extended-monitor.pdb $(MODLFLAGS) + +src/modules/geoip_base.dll: src/modules/geoip_base.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/geoip_base.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/geoip_base.pdb $(MODLFLAGS) + +src/modules/extjwt.dll: src/modules/extjwt.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/extjwt.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/extjwt.pdb $(MODLFLAGS) + +src/modules/geoip_classic.dll: src/modules/geoip_classic.c $(INCLUDES) + $(CC) $(MODCFLAGS) /I "$(GEOIPCLASSIC_INC_DIR)" src/modules/geoip_classic.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/geoip_classic.pdb $(MODLFLAGS) /LIBPATH:"$(GEOIPCLASSIC_LIB_DIR)" $(GEOIPCLASSICLIB) + +src/modules/geoip_csv.dll: src/modules/geoip_csv.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/geoip_csv.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/geoip_csv.pdb $(MODLFLAGS) + +src/modules/geoip_maxmind.dll: src/modules/geoip_maxmind.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/geoip_maxmind.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/geoip_maxmind.pdb $(MODLFLAGS) + +src/modules/globops.dll: src/modules/globops.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/globops.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/globops.pdb $(MODLFLAGS) + +src/modules/help.dll: src/modules/help.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/help.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/help.pdb $(MODLFLAGS) + +src/modules/hideserver.dll: src/modules/hideserver.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/hideserver.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/hideserver.pdb $(MODLFLAGS) + +src/modules/history_backend_mem.dll: src/modules/history_backend_mem.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/history_backend_mem.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/history_backend_mem.pdb $(MODLFLAGS) + +src/modules/history_backend_null.dll: src/modules/history_backend_null.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/history_backend_null.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/history_backend_null.pdb $(MODLFLAGS) + +src/modules/history.dll: src/modules/history.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/history.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/history.pdb $(MODLFLAGS) + +src/modules/ident_lookup.dll: src/modules/ident_lookup.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/ident_lookup.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/ident_lookup.pdb $(MODLFLAGS) + +src/modules/invite.dll: src/modules/invite.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/invite.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/invite.pdb $(MODLFLAGS) + +src/modules/ircops.dll: src/modules/ircops.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/ircops.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/ircops.pdb $(MODLFLAGS) + +src/modules/ison.dll: src/modules/ison.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/ison.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/ison.pdb $(MODLFLAGS) + +src/modules/join.dll: src/modules/join.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/join.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/join.pdb $(MODLFLAGS) + +src/modules/jointhrottle.dll: src/modules/jointhrottle.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/jointhrottle.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/jointhrottle.pdb $(MODLFLAGS) + +src/modules/json-log-tag.dll: src/modules/json-log-tag.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/json-log-tag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/json-log-tag.pdb $(MODLFLAGS) + +src/modules/jumpserver.dll: src/modules/jumpserver.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/jumpserver.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/jumpserver.pdb $(MODLFLAGS) + +src/modules/kick.dll: src/modules/kick.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/kick.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/kick.pdb $(MODLFLAGS) + +src/modules/kill.dll: src/modules/kill.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/kill.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/kill.pdb $(MODLFLAGS) + +src/modules/knock.dll: src/modules/knock.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/knock.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/knock.pdb $(MODLFLAGS) + +src/modules/labeled-response.dll: src/modules/labeled-response.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/labeled-response.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/labeled-response.pdb $(MODLFLAGS) + +src/modules/lag.dll: src/modules/lag.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/lag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/lag.pdb $(MODLFLAGS) + +src/modules/links.dll: src/modules/links.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/links.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/links.pdb $(MODLFLAGS) + +src/modules/link-security.dll: src/modules/link-security.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/link-security.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/link-security.pdb $(MODLFLAGS) + +src/modules/list.dll: src/modules/list.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/list.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/list.pdb $(MODLFLAGS) + +src/modules/locops.dll: src/modules/locops.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/locops.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/locops.pdb $(MODLFLAGS) + +src/modules/lusers.dll: src/modules/lusers.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/lusers.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/lusers.pdb $(MODLFLAGS) + +src/modules/map.dll: src/modules/map.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/map.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/map.pdb $(MODLFLAGS) + +src/modules/md.dll: src/modules/md.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/md.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/md.pdb $(MODLFLAGS) + +src/modules/message.dll: src/modules/message.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/message.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/message.pdb $(MODLFLAGS) + +src/modules/message-ids.dll: src/modules/message-ids.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/message-ids.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/message-ids.pdb $(MODLFLAGS) + +src/modules/message-tags.dll: src/modules/message-tags.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/message-tags.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/message-tags.pdb $(MODLFLAGS) + +src/modules/mkpasswd.dll: src/modules/mkpasswd.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/mkpasswd.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/mkpasswd.pdb $(MODLFLAGS) + +src/modules/mode.dll: src/modules/mode.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/mode.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/mode.pdb $(MODLFLAGS) + +src/modules/monitor.dll: src/modules/monitor.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/monitor.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/monitor.pdb $(MODLFLAGS) + +src/modules/motd.dll: src/modules/motd.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/motd.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/motd.pdb $(MODLFLAGS) + +src/modules/names.dll: src/modules/names.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/names.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/names.pdb $(MODLFLAGS) + +src/modules/netinfo.dll: src/modules/netinfo.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/netinfo.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/netinfo.pdb $(MODLFLAGS) + +src/modules/nick.dll: src/modules/nick.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/nick.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/nick.pdb $(MODLFLAGS) + +src/modules/nocodes.dll: src/modules/nocodes.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/nocodes.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/nocodes.pdb $(MODLFLAGS) + +src/modules/cloak_md5.dll: src/modules/cloak_md5.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/cloak_md5.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/cloak_md5.pdb $(MODLFLAGS) + +src/modules/cloak_none.dll: src/modules/cloak_none.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/cloak_none.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/cloak_none.pdb $(MODLFLAGS) + +src/modules/cloak_sha256.dll: src/modules/cloak_sha256.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/cloak_sha256.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/cloak_sha256.pdb $(MODLFLAGS) + +src/modules/oper.dll: src/modules/oper.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/oper.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/oper.pdb $(MODLFLAGS) + +src/modules/operinfo.dll: src/modules/operinfo.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/operinfo.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/operinfo.pdb $(MODLFLAGS) + +src/modules/opermotd.dll: src/modules/opermotd.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/opermotd.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/opermotd.pdb $(MODLFLAGS) + +src/modules/part.dll: src/modules/part.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/part.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/part.pdb $(MODLFLAGS) + +src/modules/pass.dll: src/modules/pass.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/pass.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/pass.pdb $(MODLFLAGS) + +src/modules/pingpong.dll: src/modules/pingpong.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/pingpong.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/pingpong.pdb $(MODLFLAGS) + +src/modules/plaintext-policy.dll: src/modules/plaintext-policy.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/plaintext-policy.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/plaintext-policy.pdb $(MODLFLAGS) + +src/modules/protoctl.dll: src/modules/protoctl.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/protoctl.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/protoctl.pdb $(MODLFLAGS) + +src/modules/quit.dll: src/modules/quit.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/quit.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/quit.pdb $(MODLFLAGS) + +src/modules/reply-tag.dll: src/modules/reply-tag.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/reply-tag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/reply-tag.pdb $(MODLFLAGS) + +src/modules/reputation.dll: src/modules/reputation.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/reputation.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/reputation.pdb $(MODLFLAGS) + +src/modules/require-module.dll: src/modules/require-module.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/require-module.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/require-module.pdb $(MODLFLAGS) + +src/modules/restrict-commands.dll: src/modules/restrict-commands.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/restrict-commands.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/restrict-commands.pdb $(MODLFLAGS) + +src/modules/rmtkl.dll: src/modules/rmtkl.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/rmtkl.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/rmtkl.pdb $(MODLFLAGS) + +src/modules/rules.dll: src/modules/rules.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/rules.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/rules.pdb $(MODLFLAGS) + +src/modules/sajoin.dll: src/modules/sajoin.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sajoin.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sajoin.pdb $(MODLFLAGS) + +src/modules/samode.dll: src/modules/samode.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/samode.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/samode.pdb $(MODLFLAGS) + +src/modules/sapart.dll: src/modules/sapart.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sapart.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sapart.pdb $(MODLFLAGS) + +src/modules/sasl.dll: src/modules/sasl.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sasl.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sasl.pdb $(MODLFLAGS) + +src/modules/sdesc.dll: src/modules/sdesc.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sdesc.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sdesc.pdb $(MODLFLAGS) + +src/modules/sendsno.dll: src/modules/sendsno.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sendsno.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sendsno.pdb $(MODLFLAGS) + +src/modules/sendumode.dll: src/modules/sendumode.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sendumode.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sendumode.pdb $(MODLFLAGS) + +src/modules/server.dll: src/modules/server.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/server.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/server.pdb $(MODLFLAGS) + +src/modules/server-time.dll: src/modules/server-time.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/server-time.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/server-time.pdb $(MODLFLAGS) + +src/modules/sethost.dll: src/modules/sethost.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sethost.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sethost.pdb $(MODLFLAGS) + +src/modules/setident.dll: src/modules/setident.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/setident.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/setident.pdb $(MODLFLAGS) + +src/modules/setname.dll: src/modules/setname.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/setname.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/setname.pdb $(MODLFLAGS) + +src/modules/silence.dll: src/modules/silence.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/silence.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/silence.pdb $(MODLFLAGS) + +src/modules/sinfo.dll: src/modules/sinfo.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sinfo.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sinfo.pdb $(MODLFLAGS) + +src/modules/sjoin.dll: src/modules/sjoin.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sjoin.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sjoin.pdb $(MODLFLAGS) + +src/modules/slog.dll: src/modules/slog.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/slog.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/slog.pdb $(MODLFLAGS) + +src/modules/sqline.dll: src/modules/sqline.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sqline.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sqline.pdb $(MODLFLAGS) + +src/modules/squit.dll: src/modules/squit.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/squit.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/squit.pdb $(MODLFLAGS) + +src/modules/staff.dll: src/modules/staff.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/staff.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/staff.pdb $(MODLFLAGS) + +src/modules/starttls.dll: src/modules/starttls.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/starttls.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/starttls.pdb $(MODLFLAGS) + +src/modules/stats.dll: src/modules/stats.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/stats.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/stats.pdb $(MODLFLAGS) + +src/modules/sts.dll: src/modules/sts.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/sts.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/sts.pdb $(MODLFLAGS) + +src/modules/svsjoin.dll: src/modules/svsjoin.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svsjoin.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsjoin.pdb $(MODLFLAGS) + +src/modules/svskill.dll: src/modules/svskill.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svskill.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svskill.pdb $(MODLFLAGS) + +src/modules/svslusers.dll: src/modules/svslusers.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svslusers.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svslusers.pdb $(MODLFLAGS) + +src/modules/svsmode.dll: src/modules/svsmode.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svsmode.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsmode.pdb $(MODLFLAGS) + +src/modules/svsmotd.dll: src/modules/svsmotd.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svsmotd.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsmotd.pdb $(MODLFLAGS) + +src/modules/svsnick.dll: src/modules/svsnick.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svsnick.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsnick.pdb $(MODLFLAGS) + +src/modules/svsnline.dll: src/modules/svsnline.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svsnline.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsnline.pdb $(MODLFLAGS) + +src/modules/svsnolag.dll: src/modules/svsnolag.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svsnolag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsnolag.pdb $(MODLFLAGS) + +src/modules/svsnoop.dll: src/modules/svsnoop.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svsnoop.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svsnoop.pdb $(MODLFLAGS) + +src/modules/svspart.dll: src/modules/svspart.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svspart.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svspart.pdb $(MODLFLAGS) + +src/modules/svssilence.dll: src/modules/svssilence.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svssilence.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svssilence.pdb $(MODLFLAGS) + +src/modules/svssno.dll: src/modules/svssno.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svssno.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svssno.pdb $(MODLFLAGS) + +src/modules/svswatch.dll: src/modules/svswatch.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/svswatch.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/svswatch.pdb $(MODLFLAGS) + +src/modules/swhois.dll: src/modules/swhois.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/swhois.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/swhois.pdb $(MODLFLAGS) + +src/modules/targetfloodprot.dll: src/modules/targetfloodprot.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/targetfloodprot.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/targetfloodprot.pdb $(MODLFLAGS) + +src/modules/time.dll: src/modules/time.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/time.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/time.pdb $(MODLFLAGS) + +src/modules/tkl.dll: src/modules/tkl.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/tkl.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/tkl.pdb $(MODLFLAGS) + +src/modules/tkldb.dll: src/modules/tkldb.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/tkldb.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/tkldb.pdb $(MODLFLAGS) + +src/modules/tls_antidos.dll: src/modules/tls_antidos.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/tls_antidos.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/tls_antidos.pdb $(MODLFLAGS) + +src/modules/tls_cipher.dll: src/modules/tls_cipher.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/tls_cipher.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/tls_cipher.pdb $(MODLFLAGS) + +src/modules/topic.dll: src/modules/topic.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/topic.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/topic.pdb $(MODLFLAGS) + +src/modules/trace.dll: src/modules/trace.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/trace.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/trace.pdb $(MODLFLAGS) + +src/modules/tsctl.dll: src/modules/tsctl.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/tsctl.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/tsctl.pdb $(MODLFLAGS) + +src/modules/typing-indicator.dll: src/modules/typing-indicator.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/typing-indicator.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/typing-indicator.pdb $(MODLFLAGS) + +src/modules/umode2.dll: src/modules/umode2.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/umode2.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/umode2.pdb $(MODLFLAGS) + +src/modules/unreal_server_compat.dll: src/modules/unreal_server_compat.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/unreal_server_compat.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/unreal_server_compat.pdb $(MODLFLAGS) + +src/modules/unsqline.dll: src/modules/unsqline.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/unsqline.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/unsqline.pdb $(MODLFLAGS) + +src/modules/user.dll: src/modules/user.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/user.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/user.pdb $(MODLFLAGS) + +src/modules/userhost.dll: src/modules/userhost.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/userhost.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/userhost.pdb $(MODLFLAGS) + +src/modules/userhost-tag.dll: src/modules/userhost-tag.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/userhost-tag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/userhost-tag.pdb $(MODLFLAGS) + +src/modules/userip.dll: src/modules/userip.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/userip.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/userip.pdb $(MODLFLAGS) + +src/modules/userip-tag.dll: src/modules/userip-tag.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/userip-tag.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/userip-tag.pdb $(MODLFLAGS) + +src/modules/usermodes/bot.dll: src/modules/usermodes/bot.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/bot.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/bot.pdb $(MODLFLAGS) + +src/modules/usermodes/censor.dll: src/modules/usermodes/censor.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/censor.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/censor.pdb $(MODLFLAGS) + +src/modules/usermodes/noctcp.dll: src/modules/usermodes/noctcp.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/noctcp.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/noctcp.pdb $(MODLFLAGS) + +src/modules/usermodes/nokick.dll: src/modules/usermodes/nokick.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/nokick.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/nokick.pdb $(MODLFLAGS) + +src/modules/usermodes/privacy.dll: src/modules/usermodes/privacy.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/privacy.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/privacy.pdb $(MODLFLAGS) + +src/modules/usermodes/privdeaf.dll: src/modules/usermodes/privdeaf.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/privdeaf.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/privdeaf.pdb $(MODLFLAGS) + +src/modules/usermodes/regonlymsg.dll: src/modules/usermodes/regonlymsg.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/regonlymsg.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/regonlymsg.pdb $(MODLFLAGS) + +src/modules/usermodes/secureonlymsg.dll: src/modules/usermodes/secureonlymsg.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/secureonlymsg.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/secureonlymsg.pdb $(MODLFLAGS) + +src/modules/usermodes/servicebot.dll: src/modules/usermodes/servicebot.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/servicebot.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/servicebot.pdb $(MODLFLAGS) + +src/modules/usermodes/showwhois.dll: src/modules/usermodes/showwhois.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/showwhois.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/showwhois.pdb $(MODLFLAGS) + +src/modules/usermodes/wallops.dll: src/modules/usermodes/wallops.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/usermodes/wallops.c /Fesrc/modules/usermodes/ /Fosrc/modules/usermodes/ /Fdsrc/modules/usermodes/wallops.pdb $(MODLFLAGS) + +src/modules/vhost.dll: src/modules/vhost.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/vhost.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/vhost.pdb $(MODLFLAGS) + +src/modules/watch-backend.dll: src/modules/watch-backend.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/watch-backend.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/watch-backend.pdb $(MODLFLAGS) + +src/modules/watch.dll: src/modules/watch.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/watch.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/watch.pdb $(MODLFLAGS) + +src/modules/webirc.dll: src/modules/webirc.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/webirc.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/webirc.pdb $(MODLFLAGS) + +src/modules/webredir.dll: src/modules/webredir.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/webredir.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/webredir.pdb $(MODLFLAGS) + +src/modules/websocket.dll: src/modules/websocket.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/websocket.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/websocket.pdb $(MODLFLAGS) + +src/modules/whois.dll: src/modules/whois.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/whois.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/whois.pdb $(MODLFLAGS) + +src/modules/who_old.dll: src/modules/who_old.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/who_old.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/who_old.pdb $(MODLFLAGS) + +src/modules/whowas.dll: src/modules/whowas.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/whowas.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/whowas.pdb $(MODLFLAGS) + +src/modules/whox.dll: src/modules/whox.c $(INCLUDES) + $(CC) $(MODCFLAGS) src/modules/whox.c /Fesrc/modules/ /Fosrc/modules/ /Fdsrc/modules/whox.pdb $(MODLFLAGS) -dummy: diff --git a/README.md b/README.md index cf4a267..86b1427 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,11 @@ Key features include SSL/TLS, cloaking, its advanced anti-flood and anti-spam sy swear filtering and module support. We are also particularly proud on our extensive online documentation. +## Versions +* UnrealIRCd 6 is the *stable* series since December 2021. All new features go in there. +* UnrealIRCd 5 is the *oldstable* series. It will receive bug fixes until + July 1, 2022 plus another 12 months of security fixes. + ## How to get started Please consult our excellent online documentation at https://www.unrealircd.org/docs/ when setting up the IRCd! diff --git a/SECURITY.md b/SECURITY.md index 7708d00..0b6e8db 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,7 +1,8 @@ # Security Policy ## Supported Versions -* The latest *stable* release of the 5.x branch +* The latest *stable* release of UnrealIRCd 5 (until 2023-07-01) +* The latest *stable* release of UnrealIRCd 6 See [UnrealIRCd releases](https://www.unrealircd.org/docs/UnrealIRCd_releases) for information on older versions and End Of Life dates. diff --git a/autoconf/config.guess b/autoconf/config.guess index f7eb141..e81d3ae 100644 --- a/autoconf/config.guess +++ b/autoconf/config.guess @@ -1,8 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2015 Free Software Foundation, Inc. +# Copyright 1992-2021 Free Software Foundation, Inc. -timestamp='2015-03-04' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2021-06-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -15,7 +17,7 @@ timestamp='2015-03-04' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -27,11 +29,19 @@ timestamp='2015-03-04' # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ @@ -39,7 +49,7 @@ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -50,7 +60,7 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2015 Free Software Foundation, Inc. +Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -84,7 +94,8 @@ if test $# != 0; then exit 1 fi -trap 'exit 1' 1 2 15 +# Just in case it came from the environment. +GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires @@ -96,66 +107,90 @@ trap 'exit 1' 1 2 15 # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then +if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown -case "${UNAME_SYSTEM}" in +case $UNAME_SYSTEM in Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu + LIBC=unknown - eval $set_cc_for_build - cat <<-EOF > $dummy.c + set_cc_for_build + cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc - #else + #elif defined(__GLIBC__) LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi ;; esac # Note: order is significant - the case branches are not exclusive. -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, @@ -167,29 +202,32 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - /sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` - case "${UNAME_MACHINE_ARCH}" in + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) - arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` - endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -205,10 +243,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in ;; esac # Determine ABI tags. - case "${UNAME_MACHINE_ARCH}" in + case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release @@ -216,40 +254,68 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in + case $UNAME_VERSION in Debian*) release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}${abi}" - exit ;; + GUESS=$machine-${os}${release}${abi-} + ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` @@ -263,163 +329,158 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in + case $ALPHA_CPU_TYPE in "EV4 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; + GUESS=m68k-unknown-sysv4 + ;; *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; + GUESS=$UNAME_MACHINE-unknown-morphos + ;; *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; + GUESS=i370-ibm-openedition + ;; *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; + GUESS=s390-ibm-zvmoe + ;; *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; + GUESS=powerpc-ibm-os400 + ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; + GUESS=arm-unknown-riscos + ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; + GUESS=hppa1.1-hitachi-hiuxmpp + ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; + GUESS=pyramid-pyramid-svr4 + ;; DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; + GUESS=sparc-icl-nx6 + ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH="i386" + set_cc_for_build + SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in + case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in sun3) - echo m68k-sun-sunos${UNAME_RELEASE} + GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) - echo sparc-sun-sunos${UNAME_RELEASE} + GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac - exit ;; + ;; aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor @@ -429,44 +490,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; + GUESS=mips-dec-mach_bsd4.3 + ;; RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { @@ -475,95 +536,96 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; + GUESS=powerpc-motorola-powermax + ;; Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; + GUESS=powerpc-harris-powermax + ;; Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; + GUESS=powerpc-harris-powerunix + ;; m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; + GUESS=m88k-harris-cxux7 + ;; m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; + GUESS=m88k-motorola-sysv4 + ;; m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x then - echo m88k-dg-dgux${UNAME_RELEASE} + GUESS=m88k-dg-dgux$UNAME_RELEASE else - echo m88k-dg-dguxbcs${UNAME_RELEASE} + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else - echo i586-dg-dgux${UNAME_RELEASE} + GUESS=i586-dg-dgux$UNAME_RELEASE fi - exit ;; + ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; + GUESS=m88k-dolphin-sysv3 + ;; M88*:*:R3*:*) # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; + GUESS=m88k-motorola-sysv3 + ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; + GUESS=m88k-tektronix-sysv3 + ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; + GUESS=m68k-tektronix-bsd + ;; *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; + GUESS=i386-ibm-aix + ;; ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then + if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include main() @@ -574,77 +636,77 @@ EOF exit(0); } EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then - echo "$SYSTEM_NAME" + GUESS=$SYSTEM_NAME else - echo rs6000-ibm-aix3.2.5 + GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 + GUESS=rs6000-ibm-aix3.2.4 else - echo rs6000-ibm-aix3.2 + GUESS=rs6000-ibm-aix3.2 fi - exit ;; + ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi - if [ -x /usr/bin/lslpp ] ; then - IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; + GUESS=rs6000-bull-bosx + ;; DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; + GUESS=m68k-bull-sysv3 + ;; 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; + GUESS=m68k-hp-bsd + ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; + GUESS=m68k-hp-bsd4.4 + ;; 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then + if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include @@ -677,13 +739,13 @@ EOF exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if test "$HP_ARCH" = hppa2.0w then - eval $set_cc_for_build + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -694,23 +756,23 @@ EOF # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" #include int main () @@ -735,38 +797,38 @@ EOF exit (0); } EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; + GUESS=hppa1.0-hp-bsd + ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; + GUESS=hppa1.0-hp-osf + ;; i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk else - echo ${UNAME_MACHINE}-unknown-osf1 + GUESS=$UNAME_MACHINE-unknown-osf1 fi - exit ;; + ;; parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; + GUESS=hppa1.1-hp-lites + ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; + GUESS=c1-convex-bsd + ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd @@ -774,139 +836,145 @@ EOF fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; + GUESS=c34-convex-bsd + ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; + GUESS=c38-convex-bsd + ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; + GUESS=c4-convex-bsd + ;; CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in + case $UNAME_PROCESSOR in amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; esac - exit ;; + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; + GUESS=$UNAME_MACHINE-pc-cygwin + ;; *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; *:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-msys + ;; i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; + GUESS=$UNAME_MACHINE-pc-pw32 + ;; *:Interix*:*) - case ${UNAME_MACHINE} in + case $UNAME_MACHINE in x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; + GUESS=i586-pc-interix$UNAME_RELEASE + ;; authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; IA64) - echo ia64-unknown-interix${UNAME_RELEASE} - exit ;; + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; + GUESS=$UNAME_MACHINE-pc-uwin + ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; + GUESS=x86_64-pc-cygwin + ;; prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; @@ -916,175 +984,226 @@ EOF EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; arm*:Linux:*:*) - eval $set_cc_for_build + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi - exit ;; + ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; e2k:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el + MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} + MIPS_ENDIAN= #else - CPU= + MIPS_ENDIAN= #endif #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) - echo or1k-unknown-linux-${LIBC} - exit ;; + GUESS=or1k-unknown-linux-$LIBC + ;; or32:Linux:*:* | or1k*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; padre:Linux:*:*) - echo sparc-unknown-linux-${LIBC} - exit ;; + GUESS=sparc-unknown-linux-$LIBC + ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-${LIBC} - exit ;; + GUESS=hppa64-unknown-linux-$LIBC + ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; - PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; - *) echo hppa-unknown-linux-${LIBC} ;; + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; esac - exit ;; + ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-${LIBC} - exit ;; + GUESS=powerpc64-unknown-linux-$LIBC + ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-${LIBC} - exit ;; + GUESS=powerpc-unknown-linux-$LIBC + ;; ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-${LIBC} - exit ;; + GUESS=powerpc64le-unknown-linux-$LIBC + ;; ppcle:Linux:*:*) - echo powerpcle-unknown-linux-${LIBC} - exit ;; + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + set_cc_for_build + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_X32 >/dev/null + then + LIBCABI=${LIBC}x32 + fi + fi + GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI + ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; + GUESS=i386-sequent-sysv4 + ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; + GUESS=$UNAME_MACHINE-unknown-stop + ;; i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; + GUESS=$UNAME_MACHINE-unknown-atheos + ;; i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; + GUESS=$UNAME_MACHINE-pc-syllable + ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi - exit ;; + ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in @@ -1092,12 +1211,12 @@ EOF *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 @@ -1107,43 +1226,43 @@ EOF && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else - echo ${UNAME_MACHINE}-pc-sysv32 + GUESS=$UNAME_MACHINE-pc-sysv32 fi - exit ;; + ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; + GUESS=i586-pc-msdosdjgpp + ;; Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; + GUESS=i386-pc-mach3 + ;; paragon:*:*:*) - echo i860-intel-osf1 - exit ;; + GUESS=i860-intel-osf1 + ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi - exit ;; + ;; mini*:CTIX:SYS*5:*) # "miniframe" - echo m68010-convergent-sysv - exit ;; + GUESS=m68010-convergent-sysv + ;; mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; + GUESS=m68k-convergent-sysv + ;; M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; + GUESS=m68k-diab-dnix + ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) @@ -1151,9 +1270,9 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; @@ -1162,248 +1281,438 @@ EOF test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; + GUESS=m68k-atari-sysv4 + ;; TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; + GUESS=mips-sni-sysv4 + ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 + GUESS=$UNAME_MACHINE-sni-sysv4 else - echo ns32k-sni-sysv + GUESS=ns32k-sni-sysv fi - exit ;; + ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says - echo i586-unisys-sysv4 - exit ;; + GUESS=i586-unisys-sysv4 + ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; + GUESS=hppa1.1-stratus-sysv4 + ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; + GUESS=i860-stratus-sysv4 + ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; + GUESS=$UNAME_MACHINE-stratus-vos + ;; *:VOS:*:*) # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; + GUESS=hppa1.1-stratus-vos + ;; mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; + GUESS=mips-sony-newsos6 + ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE else - echo mips-unknown-sysv${UNAME_RELEASE} + GUESS=mips-unknown-sysv$UNAME_RELEASE fi - exit ;; + ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; + GUESS=powerpc-be-beos + ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; + GUESS=powerpc-apple-beos + ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; + GUESS=i586-pc-beos + ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; + GUESS=i586-pc-haiku + ;; x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; + GUESS=x86_64-unknown-haiku + ;; SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build fi - if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE fi - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-?:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} - exit ;; + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; + GUESS=mips-compaq-nonstopux + ;; BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; + GUESS=bs2000-siemens-sysv + ;; DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. - if test "$cputype" = "386"; then + if test "${cputype-}" = 386; then UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; + GUESS=pdp10-unknown-tops10 + ;; *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; + GUESS=pdp10-unknown-tenex + ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; + GUESS=pdp10-dec-tops20 + ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; + GUESS=pdp10-xkl-tops20 + ;; *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; + GUESS=pdp10-unknown-tops20 + ;; *:ITS:*:*) - echo pdp10-unknown-its - exit ;; + GUESS=pdp10-unknown-its + ;; SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; + GUESS=i386-pc-xenix + ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; + GUESS=$UNAME_MACHINE-pc-rdos + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx - exit ;; + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; +esac + +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 < in order to provide the needed -information to handle your system. +our_year=`echo $timestamp | sed 's,-.*,,'` +thisyear=`date +%Y` +# shellcheck disable=SC2003 +script_age=`expr "$thisyear" - "$our_year"` +if test "$script_age" -lt 3 ; then + cat >&2 </dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" EOF +fi exit 1 # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/autoconf/config.sub b/autoconf/config.sub index 8f1229c..d74fb6d 100644 --- a/autoconf/config.sub +++ b/autoconf/config.sub @@ -1,8 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. -# Copyright 1992-2015 Free Software Foundation, Inc. +# Copyright 1992-2021 Free Software Foundation, Inc. -timestamp='2015-03-08' +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2021-08-14' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -15,7 +17,7 @@ timestamp='2015-03-08' # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, see . +# along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -33,7 +35,7 @@ timestamp='2015-03-08' # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -50,15 +52,21 @@ timestamp='2015-03-08' # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. -Operation modes: +Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit @@ -68,7 +76,7 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright 1992-2015 Free Software Foundation, Inc. +Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." @@ -90,12 +98,12 @@ while test $# -gt 0 ; do - ) # Use stdin as input. break ;; -* ) - echo "$me: invalid option $1$help" + echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. - echo $1 + echo "$1" exit ;; * ) @@ -111,1231 +119,1181 @@ case $# in exit 1;; esac -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ - kopensolaris*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac +# Split fields of configuration type +# shellcheck disable=SC2162 +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) - os= - basic_machine=$1 + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 ;; - -bluegene*) - os=-cnk + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac ;; esac -# Decode aliases for certain CPU-COMPANY combinations. +# Decode 1-component or ad-hoc basic machines case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | be32 | be64 \ - | bfin \ - | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | e2k | epiphany \ - | fido | fr30 | frv | ft32 \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | k1om \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pyramid \ - | riscv32 | riscv64 \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | visium \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond ;; - c54x) - basic_machine=tic54x-unknown + op50n) + cpu=hppa1.1 + vendor=oki ;; - c55x) - basic_machine=tic55x-unknown + op60c) + cpu=hppa1.1 + vendor=oki ;; - c6x) - basic_machine=tic6x-unknown + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + basic_os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + basic_os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + basic_os=tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $basic_os in + irix*) + ;; + *) + basic_os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $basic_os in + openstep*) + ;; + nextstep*) + ;; + ns2*) + basic_os=nextstep2 + ;; + *) + basic_os=nextstep3 + ;; + esac + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + basic_os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf + ;; + none) + cpu=none + vendor=none ;; leon|leon[3-9]) - basic_machine=sparc-$basic_machine + cpu=sparc + vendor=$basic_machine ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; - strongarm | thumb | xscale) - basic_machine=arm-unknown + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | e2k-* | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | k1om-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | or1k*-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pyramid-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | visium-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - asmjs) - basic_machine=asmjs-unknown - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - leon-*|leon[3-9]-*) - basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i686-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i686-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 + cpu=$basic_machine + vendor=pc ;; + # These rules are duplicated from below for sake of the special case above; + # i.e. things that normalized to x86 arches should also default to "pc" pc98) - basic_machine=i386-pc + cpu=i386 + vendor=pc ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + x64 | amd64) + cpu=x86_64 + vendor=pc ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc + # Recognize the basic CPU types without company name. + *) + cpu=$basic_machine + vendor=unknown ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc +esac + +unset -v basic_machine + +# Decode basic machines in the full and proper CPU-Company form. +case $cpu-$vendor in + # Here we handle the default manufacturer of certain CPU types in canonical form. It is in + # some cases the only manufacturer, in others, it is the most popular. + craynv-unknown) + vendor=cray + basic_os=${basic_os:-unicosmp} ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc + c90-unknown | c90-cray) + vendor=cray + basic_os=${Basic_os:-unicos} ;; - pentium4) - basic_machine=i786-pc + fx80-unknown) + vendor=alliant ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + romp-unknown) + vendor=ibm ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + mmix-unknown) + vendor=knuth ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + microblaze-unknown | microblazeel-unknown) + vendor=xilinx ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + rs6000-unknown) + vendor=ibm ;; - pn) - basic_machine=pn-gould + vax-unknown) + vendor=dec ;; - power) basic_machine=power-ibm + pdp11-unknown) + vendor=dec ;; - ppc | ppcbe) basic_machine=powerpc-unknown + we32k-unknown) + vendor=att ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + cydra-unknown) + vendor=cydrome ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown + i370-ibm*) + vendor=ibm ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + orion-unknown) + vendor=highlevel ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sde) - basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh5el) - basic_machine=sh5le-unknown - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - strongarm-* | thumb-*) - basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tile*) - basic_machine=$basic_machine-unknown - os=-linux-gnu - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - xscale-* | xscalee[bl]-*) - basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - z80-*-coff) - basic_machine=z80-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none + xps-unknown | xps100-unknown) + cpu=xps100 + vendor=honeywell ;; -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond + # Here we normalize CPU types with a missing or matching vendor + dpx20-unknown | dpx20-bull) + cpu=rs6000 + vendor=bull + basic_os=${basic_os:-bosx} ;; - op50n) - basic_machine=hppa1.1-oki + + # Here we normalize CPU types irrespective of the vendor + amd64-*) + cpu=x86_64 ;; - op60c) - basic_machine=hppa1.1-oki + blackfin-*) + cpu=bfin + basic_os=linux ;; - romp) - basic_machine=romp-ibm + c54x-*) + cpu=tic54x ;; - mmix) - basic_machine=mmix-knuth + c55x-*) + cpu=tic55x ;; - rs6000) - basic_machine=rs6000-ibm + c6x-*) + cpu=tic6x ;; - vax) - basic_machine=vax-dec + e500v[12]-*) + cpu=powerpc + basic_os=${basic_os}"spe" ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown + mips3*-*) + cpu=mips64 ;; - pdp11) - basic_machine=pdp11-dec + ms1-*) + cpu=mt ;; - we32k) - basic_machine=we32k-att + m68knommu-*) + cpu=m68k + basic_os=linux ;; - sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown + m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*) + cpu=s12z ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun + openrisc-*) + cpu=or32 ;; - cydra) - basic_machine=cydra-cydrome + parisc-*) + cpu=hppa + basic_os=linux ;; - orion) - basic_machine=orion-highlevel + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + cpu=i586 ;; - orion105) - basic_machine=clipper-highlevel + pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*) + cpu=i686 ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + cpu=i686 ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple + pentium4-*) + cpu=i786 ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. + pc98-*) + cpu=i386 ;; + ppc-* | ppcbe-*) + cpu=powerpc + ;; + ppcle-* | powerpclittle-*) + cpu=powerpcle + ;; + ppc64-*) + cpu=powerpc64 + ;; + ppc64le-* | powerpc64little-*) + cpu=powerpc64le + ;; + sb1-*) + cpu=mipsisa64sb1 + ;; + sb1el-*) + cpu=mipsisa64sb1el + ;; + sh5e[lb]-*) + cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'` + ;; + spur-*) + cpu=spur + ;; + strongarm-* | thumb-*) + cpu=arm + ;; + tx39-*) + cpu=mipstx39 + ;; + tx39el-*) + cpu=mipstx39el + ;; + x64-*) + cpu=x86_64 + ;; + xscale-* | xscalee[bl]-*) + cpu=`echo "$cpu" | sed 's/^xscale/arm/'` + ;; + arm64-*) + cpu=aarch64 + ;; + + # Recognize the canonical CPU Types that limit and/or modify the + # company names they are paired with. + cr16-*) + basic_os=${basic_os:-elf} + ;; + crisv32-* | etraxfs*-*) + cpu=crisv32 + vendor=axis + ;; + cris-* | etrax*-*) + cpu=cris + vendor=axis + ;; + crx-*) + basic_os=${basic_os:-elf} + ;; + neo-tandem) + cpu=neo + vendor=tandem + ;; + nse-tandem) + cpu=nse + vendor=tandem + ;; + nsr-tandem) + cpu=nsr + vendor=tandem + ;; + nsv-tandem) + cpu=nsv + vendor=tandem + ;; + nsx-tandem) + cpu=nsx + vendor=tandem + ;; + mipsallegrexel-sony) + cpu=mipsallegrexel + vendor=sony + ;; + tile*-*) + basic_os=${basic_os:-linux-gnu} + ;; + *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 + # Recognize the canonical CPU types that are allowed with any + # company name. + case $cpu in + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | abacus \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ + | alphapca5[67] | alpha64pca5[67] \ + | am33_2.0 \ + | amdgcn \ + | arc | arceb | arc32 | arc64 \ + | arm | arm[lb]e | arme[lb] | armv* \ + | avr | avr32 \ + | asmjs \ + | ba \ + | be32 | be64 \ + | bfin | bpf | bs2000 \ + | c[123]* | c30 | [cjt]90 | c4x \ + | c8051 | clipper | craynv | csky | cydra \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | elxsi | epiphany \ + | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ + | h8300 | h8500 \ + | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i*86 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | loongarch32 | loongarch64 | loongarchx32 \ + | m32c | m32r | m32rle \ + | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ + | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ + | m88110 | m88k | maxq | mb | mcore | mep | metag \ + | microblaze | microblazeel \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64eb | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r3 | mipsisa32r3el \ + | mipsisa32r5 | mipsisa32r5el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r3 | mipsisa64r3el \ + | mipsisa64r5 | mipsisa64r5el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mmix \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | none | np1 | ns16k | ns32k | nvptx \ + | open8 \ + | or1k* \ + | or32 \ + | orion \ + | picochip \ + | pdp10 | pdp11 | pj | pjl | pn | power \ + | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ + | pru \ + | pyramid \ + | riscv | riscv32 | riscv32be | riscv64 | riscv64be \ + | rl78 | romp | rs6000 | rx \ + | s390 | s390x \ + | score \ + | sh | shl \ + | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ + | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ + | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ + | spu \ + | tahoe \ + | thumbv7* \ + | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ + | tron \ + | ubicom32 \ + | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ + | vax \ + | visium \ + | w65 \ + | wasm32 | wasm64 \ + | we32k \ + | x86 | x86_64 | xc16x | xgate | xps100 \ + | xstormy16 | xtensa* \ + | ymp \ + | z8k | z80) + ;; + + *) + echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 + exit 1 + ;; + esac ;; esac # Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` +case $vendor in + digital*) + vendor=dec ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + commodore*) + vendor=cbm ;; *) ;; @@ -1343,200 +1301,215 @@ esac # Decode manufacturer-specific aliases for certain operating systems. -if [ x"$os" != x"" ] +if test x$basic_os != x then + +# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just +# set os. +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read kernel os <&2 - exit 1 + # No normalization, but not necessarily accepted, that comes below. ;; esac + else # Here we handle the default operating systems that come with various machines. @@ -1549,261 +1522,362 @@ else # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -case $basic_machine in +kernel= +case $cpu-$vendor in score-*) - os=-elf + os=elf ;; spu-*) - os=-elf + os=elf ;; *-acorn) - os=-riscix1.2 + os=riscix1.2 ;; arm*-rebel) - os=-linux + kernel=linux + os=gnu ;; arm*-semi) - os=-aout + os=aout ;; c4x-* | tic4x-*) - os=-coff + os=coff ;; c8051-*) - os=-elf + os=elf + ;; + clipper-intergraph) + os=clix ;; hexagon-*) - os=-elf + os=elf ;; tic54x-*) - os=-coff + os=coff ;; tic55x-*) - os=-coff + os=coff ;; tic6x-*) - os=-coff + os=coff ;; # This must come before the *-dec entry. pdp10-*) - os=-tops20 + os=tops20 ;; pdp11-*) - os=-none + os=none ;; *-dec | vax-*) - os=-ultrix4.2 + os=ultrix4.2 ;; m68*-apollo) - os=-domain + os=domain ;; i386-sun) - os=-sunos4.0.2 + os=sunos4.0.2 ;; m68000-sun) - os=-sunos3 + os=sunos3 ;; m68*-cisco) - os=-aout + os=aout ;; mep-*) - os=-elf + os=elf ;; mips*-cisco) - os=-elf + os=elf ;; mips*-*) - os=-elf + os=elf ;; or32-*) - os=-coff + os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 + os=sysv3 ;; sparc-* | *-sun) - os=-sunos4.1.1 + os=sunos4.1.1 + ;; + pru-*) + os=elf ;; *-be) - os=-beos - ;; - *-haiku) - os=-haiku + os=beos ;; *-ibm) - os=-aix + os=aix ;; *-knuth) - os=-mmixware + os=mmixware ;; *-wec) - os=-proelf + os=proelf ;; *-winbond) - os=-proelf + os=proelf ;; *-oki) - os=-proelf + os=proelf ;; *-hp) - os=-hpux + os=hpux ;; *-hitachi) - os=-hiux + os=hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv + os=sysv ;; *-cbm) - os=-amigaos + os=amigaos ;; *-dg) - os=-dgux + os=dgux ;; *-dolphin) - os=-sysv3 + os=sysv3 ;; m68k-ccur) - os=-rtu + os=rtu ;; m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs + os=luna ;; *-next) - os=-nextstep3 + os=nextstep + ;; + *-sequent) + os=ptx + ;; + *-crds) + os=unos + ;; + *-ns) + os=genix + ;; + i370-*) + os=mvs ;; *-gould) - os=-sysv + os=sysv ;; *-highlevel) - os=-bsd + os=bsd ;; *-encore) - os=-bsd + os=bsd ;; *-sgi) - os=-irix + os=irix ;; *-siemens) - os=-sysv4 + os=sysv4 ;; *-masscomp) - os=-rtu + os=rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=-uxpv + os=uxpv ;; *-rom68k) - os=-coff + os=coff ;; *-*bug) - os=-coff + os=coff ;; *-apple) - os=-macos + os=macos ;; *-atari*) - os=-mint + os=mint + ;; + *-wrs) + os=vxworks ;; *) - os=-none + os=none ;; esac + fi +# Now, validate our (potentially fixed-up) OS. +case $os in + # Sometimes we do "kernel-libc", so those need to count as OSes. + musl* | newlib* | relibc* | uclibc*) + ;; + # Likewise for "kernel-abi" + eabi* | gnueabi*) + ;; + # VxWorks passes extra cpu info in the 4th filed. + simlinux | simwindows | spe) + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ + | os9* | macos* | osx* | ios* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | mirbsd* | netbsd* | dicos* | openedition* | ose* \ + | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* | serenity* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | mint* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ + | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr*) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + none) + ;; + *) + echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ + | linux-musl* | linux-relibc* | linux-uclibc* ) + ;; + uclinux-uclibc* ) + ;; + -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + kfreebsd*-gnu* | kopensolaris*-gnu*) + ;; + vxworks-simlinux | vxworks-simwindows | vxworks-spe) + ;; + nto-qnx*) + ;; + os2-emx) + ;; + *-eabi* | *-gnueabi*) + ;; + -*) + # Blank kernel with real OS is always fine. + ;; + *-*) + echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 + exit 1 + ;; +esac + # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) vendor=acorn ;; - -sunos*) + *-sunos*) vendor=sun ;; - -cnk*|-aix*) + *-cnk* | *-aix*) vendor=ibm ;; - -beos*) + *-beos*) vendor=be ;; - -hpux*) + *-hpux*) vendor=hp ;; - -mpeix*) + *-mpeix*) vendor=hp ;; - -hiux*) + *-hiux*) vendor=hitachi ;; - -unos*) + *-unos*) vendor=crds ;; - -dgux*) + *-dgux*) vendor=dg ;; - -luna*) + *-luna*) vendor=omron ;; - -genix*) + *-genix*) vendor=ns ;; - -mvs* | -opened*) + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) vendor=ibm ;; - -os400*) + *-os400*) vendor=ibm ;; - -ptx*) + s390-* | s390x-*) + vendor=ibm + ;; + *-ptx*) vendor=sequent ;; - -tpf*) + *-tpf*) vendor=ibm ;; - -vxsim* | -vxworks* | -windiss*) + *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; - -aux*) + *-aux*) vendor=apple ;; - -hms*) + *-hms*) vendor=hitachi ;; - -mpw* | -macos*) + *-mpw* | *-macos*) vendor=apple ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; - -vos*) + *-vos*) vendor=stratus ;; esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac -echo $basic_machine$os +echo "$cpu-$vendor-${kernel:+$kernel-}$os" exit # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" diff --git a/autoconf/m4/unreal.m4 b/autoconf/m4/unreal.m4 index 345fdf7..73d37a9 100644 --- a/autoconf/m4/unreal.m4 +++ b/autoconf/m4/unreal.m4 @@ -130,9 +130,13 @@ AC_DEFUN([CHECK_LIBCURL], LIBS="$LIBS_SAVEDA" CFLAGS="$CFLAGS_SAVEDA" - URL="url.o" - AC_SUBST(URL) + dnl Finally, choose the cURL implementation of url.c + URL="url_curl.o" + ],[ + dnl Choose UnrealIRCds internal implementation of url.c + URL="url_unreal.o" ]) dnl AS_IF(enable_curl) + AC_SUBST(URL) ]) dnl the following 2 macros are based on CHECK_SSL by Mark Ethan Trostler @@ -178,7 +182,11 @@ AS_IF([test $enable_ssl != "no"], else CRYPTOLIB="-lssl -lcrypto"; if test ! "$ssldir" = "/usr" ; then - LDFLAGS="$LDFLAGS -L$ssldir/lib"; + if test -d "$ssldir/lib64" ; then + LDFLAGS="$LDFLAGS -L$ssldir/lib64"; + else + LDFLAGS="$LDFLAGS -L$ssldir/lib"; + fi dnl check if binary path exists if test -f "$ssldir/bin/openssl"; then OPENSSLPATH="$ssldir/bin/openssl"; @@ -312,3 +320,94 @@ else AC_MSG_RESULT([no]) fi ]) + +dnl For geoip-api-c +AC_DEFUN([CHECK_GEOIP_CLASSIC], +[ + AC_ARG_ENABLE(geoip_classic, + [AC_HELP_STRING([--enable-geoip-classic=no/yes],[enable GeoIP Classic support])], + [enable_geoip_classic=$enableval], + [enable_geoip_classic=no]) + + AS_IF([test "x$enable_geoip_classic" = "xyes"], + [ + dnl First see if the system provides it + has_system_geoip_classic="no" + PKG_CHECK_MODULES([GEOIP_CLASSIC], [geoip >= 1.6.0], + [has_system_geoip_classic=yes + AS_IF([test "x$PRIVATELIBDIR" != "x"], [rm -f "$PRIVATELIBDIR/"libGeoIP.*])], + [has_system_geoip_classic=no]) + + dnl Otherwise fallback to our own.. + AS_IF([test "$has_system_geoip_classic" = "no"],[ + dnl REMEMBER TO CHANGE WITH A NEW GEOIP LIBRARY RELEASE! + geoip_classic_version="1.6.12" + AC_MSG_RESULT(extracting GeoIP Classic library) + cur_dir=`pwd` + cd extras + dnl remove old directory to force a recompile... + dnl and remove its installation prefix just to clean things up. + rm -rf GeoIP-$geoip_classic_version geoip-classic + if test "x$ac_cv_path_GUNZIP" = "x" ; then + tar xfz geoip-classic.tar.gz + else + cp geoip-classic.tar.gz geoip-classic.tar.gz.bak + gunzip -f geoip-classic.tar.gz + cp geoip-classic.tar.gz.bak geoip-classic.tar.gz + tar xf geoip-classic.tar + fi + AC_MSG_RESULT(configuring GeoIP Classic library) + cd GeoIP-$geoip_classic_version + save_cflags="$CFLAGS" + CFLAGS="$orig_cflags" + export CFLAGS + ./configure --prefix=$cur_dir/extras/geoip-classic --libdir=$PRIVATELIBDIR --enable-shared --disable-static || exit 1 + CFLAGS="$save_cflags" + AC_MSG_RESULT(compiling GeoIP Classic library) + $ac_cv_prog_MAKER || exit 1 + AC_MSG_RESULT(installing GeoIP Classic library) + $ac_cv_prog_MAKER install || exit 1 + dnl Try pkg-config first... + AS_IF([test -n "$ac_cv_path_PKGCONFIG"], + [GEOIP_CLASSIC_LIBS="`$ac_cv_path_PKGCONFIG --libs geoip.pc`" + GEOIP_CLASSIC_CFLAGS="`$ac_cv_path_PKGCONFIG --cflags geoip.pc`"]) + dnl In case the system does not have pkg-config, fallback to hardcoded settings... + AS_IF([test -z "$GEOIP_CLASSIC_LIBS"], + [GEOIP_CLASSIC_LIBS="-L$PRIVATELIBDIR -lGeoIP" + GEOIP_CLASSIC_CFLAGS="-I$cur_dir/extras/geoip-classic/include"]) + cd $cur_dir + ]) + + AC_SUBST(GEOIP_CLASSIC_LIBS) + AC_SUBST(GEOIP_CLASSIC_CFLAGS) + + GEOIP_CLASSIC_OBJECTS="geoip_classic.so" + AC_SUBST(GEOIP_CLASSIC_OBJECTS) + ]) dnl AS_IF(enable_geoip_classic) +]) + +AC_DEFUN([CHECK_LIBMAXMINDDB], +[ + AC_ARG_ENABLE(libmaxminddb, + [AC_HELP_STRING([--enable-libmaxminddb=no/yes],[enable GeoIP libmaxminddb support])], + [enable_libmaxminddb=$enableval], + [enable_libmaxminddb=no]) + + AS_IF([test "x$enable_libmaxminddb" = "xyes"], + [ + dnl see if the system provides it + has_system_libmaxminddb="no" + PKG_CHECK_MODULES([LIBMAXMINDDB], [libmaxminddb >= 1.4.3], + [has_system_libmaxminddb=yes]) + AS_IF([test "x$has_system_libmaxminddb" = "xyes"], + [ + + AC_SUBST(LIBMAXMINDDB_LIBS) + AC_SUBST(LIBMAXMINDDB_CFLAGS) + + GEOIP_MAXMIND_OBJECTS="geoip_maxmind.so" + AC_SUBST(GEOIP_MAXMIND_OBJECTS) + ]) + ]) +]) + diff --git a/configure b/configure index 11fec73..3b7068c 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 5.2.0.1. +# Generated by GNU Autoconf 2.69 for unrealircd 6.0.1.1. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='unrealircd' PACKAGE_TARNAME='unrealircd' -PACKAGE_VERSION='5.2.0.1' -PACKAGE_STRING='unrealircd 5.2.0.1' +PACKAGE_VERSION='6.0.1.1' +PACKAGE_STRING='unrealircd 6.0.1.1' PACKAGE_BUGREPORT='https://bugs.unrealircd.org/' PACKAGE_URL='https://unrealircd.org/' @@ -626,6 +626,12 @@ ac_subst_vars='LTLIBOBJS LIBOBJS UNRLINCDIR IRCDLIBS +GEOIP_MAXMIND_OBJECTS +LIBMAXMINDDB_LIBS +LIBMAXMINDDB_CFLAGS +GEOIP_CLASSIC_OBJECTS +GEOIP_CLASSIC_LIBS +GEOIP_CLASSIC_CFLAGS URL PTHREAD_CFLAGS PTHREAD_LIBS @@ -639,6 +645,8 @@ build_os build_vendor build_cpu build +JANSSON_LIBS +JANSSON_CFLAGS CARES_LIBS CARES_CFLAGS SODIUM_LIBS @@ -746,19 +754,20 @@ with_docdir with_pidfile with_privatelibdir with_maxconnections -enable_prefixaq -with_showlistmodes with_no_operoverride with_operoverride_verify with_system_pcre2 with_system_argon2 with_system_sodium with_system_cares +with_system_jansson enable_ssl enable_dynamic_linking enable_werror enable_asan enable_libcurl +enable_geoip_classic +enable_libmaxminddb ' ac_precious_vars='build_alias host_alias @@ -779,7 +788,13 @@ ARGON2_LIBS SODIUM_CFLAGS SODIUM_LIBS CARES_CFLAGS -CARES_LIBS' +CARES_LIBS +JANSSON_CFLAGS +JANSSON_LIBS +GEOIP_CLASSIC_CFLAGS +GEOIP_CLASSIC_LIBS +LIBMAXMINDDB_CFLAGS +LIBMAXMINDDB_LIBS' # Initialize some variables set by options. @@ -1330,7 +1345,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 5.2.0.1 to adapt to many kinds of systems. +\`configure' configures unrealircd 6.0.1.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1396,7 +1411,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unrealircd 5.2.0.1:";; + short | recursive ) echo "Configuration of unrealircd 6.0.1.1:";; esac cat <<\_ACEOF @@ -1406,7 +1421,6 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-hardening Enable compiler and linker options to frustrate memory corruption exploits [yes] - --disable-prefixaq Disable chanadmin (+a) and chanowner (+q) prefixes --enable-ssl= enable ssl will check /usr/local/opt/openssl /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/sfw /usr/local /usr @@ -1418,6 +1432,10 @@ Optional Features: --enable-asan Enable address sanitizer and other debugging options, not recommended for production servers! --enable-libcurl=DIR enable libcurl (remote include) support + --enable-geoip-classic=no/yes + enable GeoIP Classic support + --enable-libmaxminddb=no/yes + enable GeoIP libmaxminddb support Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1447,7 +1465,6 @@ Optional Packages: stored. Disable when building a package for a distro --with-maxconnections=size Specify the max file descriptors to use - --with-showlistmodes Specify whether modes are shown in /list --with-no-operoverride Disable OperOverride --with-operoverride-verify Require opers to invite themselves to +s/+p channels @@ -1459,6 +1476,9 @@ Optional Packages: library. Normally autodetected via pkg-config --without-system-cares Use bundled version instead of system c-ares. Normally autodetected via pkg-config. + --without-system-jansson + Use bundled version instead of system jansson. + Normally autodetected via pkg-config. Some influential environment variables: CC C compiler command @@ -1486,6 +1506,18 @@ Some influential environment variables: CARES_CFLAGS C compiler flags for CARES, overriding pkg-config CARES_LIBS linker flags for CARES, overriding pkg-config + JANSSON_CFLAGS + C compiler flags for JANSSON, overriding pkg-config + JANSSON_LIBS + linker flags for JANSSON, overriding pkg-config + GEOIP_CLASSIC_CFLAGS + C compiler flags for GEOIP_CLASSIC, overriding pkg-config + GEOIP_CLASSIC_LIBS + linker flags for GEOIP_CLASSIC, overriding pkg-config + LIBMAXMINDDB_CFLAGS + C compiler flags for LIBMAXMINDDB, overriding pkg-config + LIBMAXMINDDB_LIBS + linker flags for LIBMAXMINDDB, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1554,7 +1586,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unrealircd configure 5.2.0.1 +unrealircd configure 6.0.1.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1923,7 +1955,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 5.2.0.1, which was +It was created by unrealircd $as_me 6.0.1.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2315,7 +2347,7 @@ orig_cflags="$CFLAGS" BUILDDIR_NOW="`pwd`" # Generation version number (e.g.: X in X.Y.Z) -UNREAL_VERSION_GENERATION="5" +UNREAL_VERSION_GENERATION="6" cat >>confdefs.h <<_ACEOF #define UNREAL_VERSION_GENERATION $UNREAL_VERSION_GENERATION @@ -2323,7 +2355,7 @@ _ACEOF # Major version number (e.g.: Y in X.Y.Z) -UNREAL_VERSION_MAJOR="2" +UNREAL_VERSION_MAJOR="0" cat >>confdefs.h <<_ACEOF #define UNREAL_VERSION_MAJOR $UNREAL_VERSION_MAJOR @@ -2331,7 +2363,7 @@ _ACEOF # Minor version number (e.g.: Z in X.Y.Z) -UNREAL_VERSION_MINOR="0" +UNREAL_VERSION_MINOR="1" cat >>confdefs.h <<_ACEOF #define UNREAL_VERSION_MINOR $UNREAL_VERSION_MINOR @@ -5121,6 +5153,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu CFLAGS="$CFLAGS -funsigned-char" + CFLAGS="$CFLAGS -Wall" ac_ext=c @@ -5217,6 +5250,54 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat-nonliteral" >&5 +$as_echo_n "checking whether C compiler accepts -Wformat-nonliteral... " >&6; } +if ${ax_cv_check_cflags__Werror___Wformat_nonliteral+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -Wformat-nonliteral" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags__Werror___Wformat_nonliteral=yes +else + ax_cv_check_cflags__Werror___Wformat_nonliteral=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror___Wformat_nonliteral" >&5 +$as_echo "$ax_cv_check_cflags__Werror___Wformat_nonliteral" >&6; } +if test x"$ax_cv_check_cflags__Werror___Wformat_nonliteral" = xyes; then : + CFLAGS="$CFLAGS -Wformat-nonliteral" +else + : +fi + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -5312,6 +5393,55 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wparentheses" >&5 +$as_echo_n "checking whether C compiler accepts -Wparentheses... " >&6; } +if ${ax_cv_check_cflags__Werror___Wparentheses+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CFLAGS + CFLAGS="$CFLAGS -Werror -Wparentheses" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ax_cv_check_cflags__Werror___Wparentheses=yes +else + ax_cv_check_cflags__Werror___Wparentheses=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror___Wparentheses" >&5 +$as_echo "$ax_cv_check_cflags__Werror___Wparentheses" >&6; } +if test x"$ax_cv_check_cflags__Werror___Wparentheses" = xyes; then : + CFLAGS="$CFLAGS -Wparentheses" +else + : +fi + + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -5791,20 +5921,24 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu +if $CC --version | grep -q "clang version 3."; then : + CFLAGS="$CFLAGS -Wno-tautological-compare -Wno-pragmas" +fi + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Waddress" >&5 -$as_echo_n "checking whether C compiler accepts -Waddress... " >&6; } -if ${ax_cv_check_cflags__Werror___Waddress+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wpragmas" >&5 +$as_echo_n "checking whether C compiler accepts -Wpragmas... " >&6; } +if ${ax_cv_check_cflags__Werror___Wpragmas+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Werror -Waddress" + CFLAGS="$CFLAGS -Werror -Wpragmas" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5817,19 +5951,19 @@ main () } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags__Werror___Waddress=yes + ax_cv_check_cflags__Werror___Wpragmas=yes else - ax_cv_check_cflags__Werror___Waddress=no + ax_cv_check_cflags__Werror___Wpragmas=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror___Waddress" >&5 -$as_echo "$ax_cv_check_cflags__Werror___Waddress" >&6; } -if test x"$ax_cv_check_cflags__Werror___Waddress" = xyes; then : - CFLAGS="$CFLAGS -Wno-address" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror___Wpragmas" >&5 +$as_echo "$ax_cv_check_cflags__Werror___Wpragmas" >&6; } +if test x"$ax_cv_check_cflags__Werror___Wpragmas" = xyes; then : + no_pragmas=1 else - : + no_pragmas=0 fi ac_ext=c @@ -5838,21 +5972,20 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu - ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wcast-function-type" >&5 -$as_echo_n "checking whether C compiler accepts -Wcast-function-type... " >&6; } -if ${ax_cv_check_cflags__Werror___Wcast_function_type+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunknown-warning-option" >&5 +$as_echo_n "checking whether C compiler accepts -Wunknown-warning-option... " >&6; } +if ${ax_cv_check_cflags__Werror___Wunknown_warning_option+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS - CFLAGS="$CFLAGS -Werror -Wcast-function-type" + CFLAGS="$CFLAGS -Werror -Wunknown-warning-option" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -5865,19 +5998,19 @@ main () } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : - ax_cv_check_cflags__Werror___Wcast_function_type=yes + ax_cv_check_cflags__Werror___Wunknown_warning_option=yes else - ax_cv_check_cflags__Werror___Wcast_function_type=no + ax_cv_check_cflags__Werror___Wunknown_warning_option=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror___Wcast_function_type" >&5 -$as_echo "$ax_cv_check_cflags__Werror___Wcast_function_type" >&6; } -if test x"$ax_cv_check_cflags__Werror___Wcast_function_type" = xyes; then : - CFLAGS="$CFLAGS -Wno-cast-function-type" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror___Wunknown_warning_option" >&5 +$as_echo "$ax_cv_check_cflags__Werror___Wunknown_warning_option" >&6; } +if test x"$ax_cv_check_cflags__Werror___Wunknown_warning_option" = xyes; then : + unknown_warning_option=1 else - : + unknown_warning_option=0 fi ac_ext=c @@ -5887,6 +6020,14 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test "$unknown_warning_option" = "1"; then + CFLAGS="$CFLAGS -Wno-unknown-warning-option" +else + if test "$no_pragmas" = "1"; then + CFLAGS="$CFLAGS -Wno-pragmas" + fi +fi + @@ -6008,6 +6149,19 @@ $as_echo "#define HAVE_STRLNCAT /**/" >>confdefs.h fi done +for ac_func in strlncpy +do : + ac_fn_c_check_func "$LINENO" "strlncpy" "ac_cv_func_strlncpy" +if test "x$ac_cv_func_strlncpy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_STRLNCPY 1 +_ACEOF + +$as_echo "#define HAVE_STRLNCPY /**/" >>confdefs.h + +fi +done + for ac_func in getrusage do : @@ -6448,29 +6602,6 @@ cat >>confdefs.h <<_ACEOF _ACEOF -# Check whether --enable-prefixaq was given. -if test "${enable_prefixaq+set}" = set; then : - enableval=$enable_prefixaq; -else - enable_prefixaq=yes -fi - -if test $enable_prefixaq = "yes"; then : - -$as_echo "#define PREFIX_AQ /**/" >>confdefs.h - -fi - - -# Check whether --with-showlistmodes was given. -if test "${with_showlistmodes+set}" = set; then : - withval=$with_showlistmodes; if test $withval = "yes"; then : - -$as_echo "#define LIST_SHOW_MODES /**/" >>confdefs.h - -fi -fi - # Check whether --with-no-operoverride was given. if test "${with_no_operoverride+set}" = set; then : @@ -6524,6 +6655,14 @@ else fi +# Check whether --with-system-jansson was given. +if test "${with_system_jansson+set}" = set; then : + withval=$with_system_jansson; +else + with_system_jansson=yes +fi + + # Check whether --enable-ssl was given. if test "${enable_ssl+set}" = set; then : enableval=$enable_ssl; @@ -6570,7 +6709,11 @@ $as_echo "not found" >&6; } else CRYPTOLIB="-lssl -lcrypto"; if test ! "$ssldir" = "/usr" ; then - LDFLAGS="$LDFLAGS -L$ssldir/lib"; + if test -d "$ssldir/lib64" ; then + LDFLAGS="$LDFLAGS -L$ssldir/lib64"; + else + LDFLAGS="$LDFLAGS -L$ssldir/lib"; + fi if test -f "$ssldir/bin/openssl"; then OPENSSLPATH="$ssldir/bin/openssl"; fi @@ -7655,7 +7798,7 @@ fi if test "$has_system_cares" = "no"; then : -cares_version="1.17.1" +cares_version="1.17.2" { $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` @@ -7716,6 +7859,130 @@ cd $cur_dir fi +has_system_jansson="no" +if test "x$with_system_jansson" = "xyes"; then : + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for JANSSON" >&5 +$as_echo_n "checking for JANSSON... " >&6; } + +if test -n "$JANSSON_CFLAGS"; then + pkg_cv_JANSSON_CFLAGS="$JANSSON_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.0.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_CFLAGS=`$PKG_CONFIG --cflags "jansson >= 2.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$JANSSON_LIBS"; then + pkg_cv_JANSSON_LIBS="$JANSSON_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"jansson >= 2.0.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "jansson >= 2.0.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_JANSSON_LIBS=`$PKG_CONFIG --libs "jansson >= 2.0.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + JANSSON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "jansson >= 2.0.0" 2>&1` + else + JANSSON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "jansson >= 2.0.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$JANSSON_PKG_ERRORS" >&5 + + has_system_jansson=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + has_system_jansson=no +else + JANSSON_CFLAGS=$pkg_cv_JANSSON_CFLAGS + JANSSON_LIBS=$pkg_cv_JANSSON_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + has_system_jansson=yes +if test "x$PRIVATELIBDIR" != "x"; then : + rm -f "$PRIVATELIBDIR/"libjansson* +fi +fi +fi + +if test "$has_system_jansson" = "no"; then : + +jansson_version="2.13.1" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: extracting jansson library" >&5 +$as_echo "extracting jansson library" >&6; } +cur_dir=`pwd` +cd extras +rm -rf jansson-$jansson_version jansson +if test "x$ac_cv_path_GUNZIP" = "x" ; then + tar xfz jansson.tar.gz +else + cp jansson.tar.gz jansson.tar.gz.bak + gunzip -f jansson.tar.gz + cp jansson.tar.gz.bak jansson.tar.gz + tar xf jansson.tar +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: compiling jansson library" >&5 +$as_echo "compiling jansson library" >&6; } +cd jansson-$jansson_version +save_cflags="$CFLAGS" +CFLAGS="$orig_cflags" +export CFLAGS +./configure --prefix=$cur_dir/extras/jansson --libdir=$PRIVATELIBDIR --enable-shared --disable-static --enable-opt || exit 1 +CFLAGS="$save_cflags" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: compiling jansson resolver library" >&5 +$as_echo "compiling jansson resolver library" >&6; } +$ac_cv_prog_MAKER || exit 1 +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: installing jansson resolver library" >&5 +$as_echo "installing jansson resolver library" >&6; } +$ac_cv_prog_MAKER install || exit 1 +JANSSON_CFLAGS="-I$cur_dir/extras/jansson/include" + +JANSSON_LIBS= +if test -n "$ac_cv_path_PKGCONFIG"; then : + JANSSON_LIBS="`$ac_cv_path_PKGCONFIG --libs jansson.pc`" +fi +if test -z "$JANSSON_LIBS"; then : + JANSSON_LIBS="-L$PRIVATELIBDIR -ljansson" +fi + +cd $cur_dir + +fi + + # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 @@ -8338,11 +8605,269 @@ rm -f core conftest.err conftest.$ac_objext \ LIBS="$LIBS_SAVEDA" CFLAGS="$CFLAGS_SAVEDA" - URL="url.o" + URL="url_curl.o" + +else + + URL="url_unreal.o" + +fi + + + + # Check whether --enable-geoip_classic was given. +if test "${enable_geoip_classic+set}" = set; then : + enableval=$enable_geoip_classic; enable_geoip_classic=$enableval +else + enable_geoip_classic=no +fi + + + if test "x$enable_geoip_classic" = "xyes"; then : + + has_system_geoip_classic="no" + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GEOIP_CLASSIC" >&5 +$as_echo_n "checking for GEOIP_CLASSIC... " >&6; } + +if test -n "$GEOIP_CLASSIC_CFLAGS"; then + pkg_cv_GEOIP_CLASSIC_CFLAGS="$GEOIP_CLASSIC_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"geoip >= 1.6.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "geoip >= 1.6.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_GEOIP_CLASSIC_CFLAGS=`$PKG_CONFIG --cflags "geoip >= 1.6.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$GEOIP_CLASSIC_LIBS"; then + pkg_cv_GEOIP_CLASSIC_LIBS="$GEOIP_CLASSIC_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"geoip >= 1.6.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "geoip >= 1.6.0") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_GEOIP_CLASSIC_LIBS=`$PKG_CONFIG --libs "geoip >= 1.6.0" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + GEOIP_CLASSIC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "geoip >= 1.6.0" 2>&1` + else + GEOIP_CLASSIC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "geoip >= 1.6.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$GEOIP_CLASSIC_PKG_ERRORS" >&5 + + has_system_geoip_classic=no +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + has_system_geoip_classic=no +else + GEOIP_CLASSIC_CFLAGS=$pkg_cv_GEOIP_CLASSIC_CFLAGS + GEOIP_CLASSIC_LIBS=$pkg_cv_GEOIP_CLASSIC_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + has_system_geoip_classic=yes + if test "x$PRIVATELIBDIR" != "x"; then : + rm -f "$PRIVATELIBDIR/"libGeoIP.* +fi +fi + + if test "$has_system_geoip_classic" = "no"; then : + + geoip_classic_version="1.6.12" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: extracting GeoIP Classic library" >&5 +$as_echo "extracting GeoIP Classic library" >&6; } + cur_dir=`pwd` + cd extras + rm -rf GeoIP-$geoip_classic_version geoip-classic + if test "x$ac_cv_path_GUNZIP" = "x" ; then + tar xfz geoip-classic.tar.gz + else + cp geoip-classic.tar.gz geoip-classic.tar.gz.bak + gunzip -f geoip-classic.tar.gz + cp geoip-classic.tar.gz.bak geoip-classic.tar.gz + tar xf geoip-classic.tar + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: configuring GeoIP Classic library" >&5 +$as_echo "configuring GeoIP Classic library" >&6; } + cd GeoIP-$geoip_classic_version + save_cflags="$CFLAGS" + CFLAGS="$orig_cflags" + export CFLAGS + ./configure --prefix=$cur_dir/extras/geoip-classic --libdir=$PRIVATELIBDIR --enable-shared --disable-static || exit 1 + CFLAGS="$save_cflags" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: compiling GeoIP Classic library" >&5 +$as_echo "compiling GeoIP Classic library" >&6; } + $ac_cv_prog_MAKER || exit 1 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: installing GeoIP Classic library" >&5 +$as_echo "installing GeoIP Classic library" >&6; } + $ac_cv_prog_MAKER install || exit 1 + if test -n "$ac_cv_path_PKGCONFIG"; then : + GEOIP_CLASSIC_LIBS="`$ac_cv_path_PKGCONFIG --libs geoip.pc`" + GEOIP_CLASSIC_CFLAGS="`$ac_cv_path_PKGCONFIG --cflags geoip.pc`" +fi + if test -z "$GEOIP_CLASSIC_LIBS"; then : + GEOIP_CLASSIC_LIBS="-L$PRIVATELIBDIR -lGeoIP" + GEOIP_CLASSIC_CFLAGS="-I$cur_dir/extras/geoip-classic/include" +fi + cd $cur_dir + +fi + + + + + GEOIP_CLASSIC_OBJECTS="geoip_classic.so" fi + + # Check whether --enable-libmaxminddb was given. +if test "${enable_libmaxminddb+set}" = set; then : + enableval=$enable_libmaxminddb; enable_libmaxminddb=$enableval +else + enable_libmaxminddb=no +fi + + + if test "x$enable_libmaxminddb" = "xyes"; then : + + has_system_libmaxminddb="no" + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBMAXMINDDB" >&5 +$as_echo_n "checking for LIBMAXMINDDB... " >&6; } + +if test -n "$LIBMAXMINDDB_CFLAGS"; then + pkg_cv_LIBMAXMINDDB_CFLAGS="$LIBMAXMINDDB_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmaxminddb >= 1.4.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libmaxminddb >= 1.4.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBMAXMINDDB_CFLAGS=`$PKG_CONFIG --cflags "libmaxminddb >= 1.4.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$LIBMAXMINDDB_LIBS"; then + pkg_cv_LIBMAXMINDDB_LIBS="$LIBMAXMINDDB_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmaxminddb >= 1.4.3\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libmaxminddb >= 1.4.3") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_LIBMAXMINDDB_LIBS=`$PKG_CONFIG --libs "libmaxminddb >= 1.4.3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + LIBMAXMINDDB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmaxminddb >= 1.4.3" 2>&1` + else + LIBMAXMINDDB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmaxminddb >= 1.4.3" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$LIBMAXMINDDB_PKG_ERRORS" >&5 + + as_fn_error $? "Package requirements (libmaxminddb >= 1.4.3) were not met: + +$LIBMAXMINDDB_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +Alternatively, you may set the environment variables LIBMAXMINDDB_CFLAGS +and LIBMAXMINDDB_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details." "$LINENO" 5 +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +Alternatively, you may set the environment variables LIBMAXMINDDB_CFLAGS +and LIBMAXMINDDB_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details. + +To get pkg-config, see . +See \`config.log' for more details" "$LINENO" 5; } +else + LIBMAXMINDDB_CFLAGS=$pkg_cv_LIBMAXMINDDB_CFLAGS + LIBMAXMINDDB_LIBS=$pkg_cv_LIBMAXMINDDB_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + has_system_libmaxminddb=yes +fi + if test "x$has_system_libmaxminddb" = "xyes"; then : + + + + + + GEOIP_MAXMIND_OBJECTS="geoip_maxmind.so" + + +fi + +fi + + UNRLINCDIR="`pwd`/include" if test "$ac_cv_werror" = "yes" ; then @@ -8358,7 +8883,7 @@ fi -ac_config_files="$ac_config_files Makefile src/Makefile src/modules/Makefile src/modules/chanmodes/Makefile src/modules/usermodes/Makefile src/modules/snomasks/Makefile src/modules/extbans/Makefile src/modules/third/Makefile extras/unrealircd-upgrade-script unrealircd" +ac_config_files="$ac_config_files Makefile src/Makefile src/modules/Makefile src/modules/chanmodes/Makefile src/modules/usermodes/Makefile src/modules/extbans/Makefile src/modules/third/Makefile extras/unrealircd-upgrade-script unrealircd" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -8866,7 +9391,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 5.2.0.1, which was +This file was extended by unrealircd $as_me 6.0.1.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -8929,7 +9454,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 5.2.0.1 +unrealircd config.status 6.0.1.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -9056,7 +9581,6 @@ do "src/modules/Makefile") CONFIG_FILES="$CONFIG_FILES src/modules/Makefile" ;; "src/modules/chanmodes/Makefile") CONFIG_FILES="$CONFIG_FILES src/modules/chanmodes/Makefile" ;; "src/modules/usermodes/Makefile") CONFIG_FILES="$CONFIG_FILES src/modules/usermodes/Makefile" ;; - "src/modules/snomasks/Makefile") CONFIG_FILES="$CONFIG_FILES src/modules/snomasks/Makefile" ;; "src/modules/extbans/Makefile") CONFIG_FILES="$CONFIG_FILES src/modules/extbans/Makefile" ;; "src/modules/third/Makefile") CONFIG_FILES="$CONFIG_FILES src/modules/third/Makefile" ;; "extras/unrealircd-upgrade-script") CONFIG_FILES="$CONFIG_FILES extras/unrealircd-upgrade-script" ;; diff --git a/configure.ac b/configure.ac index 4246558..9dff663 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], [5.2.0.1], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/]) +AC_INIT([unrealircd], [6.0.1.1], [https://bugs.unrealircd.org/], [], [https://unrealircd.org/]) AC_CONFIG_SRCDIR([src/ircd.c]) AC_CONFIG_HEADER([include/setup.h]) AC_CONFIG_AUX_DIR([autoconf]) @@ -26,15 +26,15 @@ BUILDDIR_NOW="`pwd`" dnl Calculate the versions. Perhaps the use of expr is a little too extravagant # Generation version number (e.g.: X in X.Y.Z) -UNREAL_VERSION_GENERATION=["5"] +UNREAL_VERSION_GENERATION=["6"] AC_DEFINE_UNQUOTED([UNREAL_VERSION_GENERATION], [$UNREAL_VERSION_GENERATION], [Generation version number (e.g.: X for X.Y.Z)]) # Major version number (e.g.: Y in X.Y.Z) -UNREAL_VERSION_MAJOR=["2"] +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=["0"] +UNREAL_VERSION_MINOR=["1"] 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 @@ -189,18 +189,26 @@ CFLAGS="$CFLAGS -funsigned-char" dnl Compiler -W checks... +dnl == ADD THESE WARNINGS == + dnl We should be able to turn this on unconditionally: CFLAGS="$CFLAGS -Wall" dnl More warnings (if the compiler supports it): check_cc_flag([-Wextra], [CFLAGS="$CFLAGS -Wextra"]) check_cc_flag([-Waggregate-return], [CFLAGS="$CFLAGS -Waggregate-return"]) +check_cc_flag([-Wformat-nonliteral], [CFLAGS="$CFLAGS -Wformat-nonliteral"]) + dnl The following few are more experimental, if they have false positives we'll have dnl to disable them: dnl Can't use this, too bad: check_cc_flag([-Wlogical-op], [CFLAGS="$CFLAGS -Wlogical-op"]) check_cc_flag([-Wduplicated-cond], [CFLAGS="$CFLAGS -Wduplicated-cond"]) check_cc_flag([-Wduplicated-branches], [CFLAGS="$CFLAGS -Wduplicated-branches"]) +check_cc_flag([-Wparentheses], [CFLAGS="$CFLAGS -Wparentheses"]) + +dnl == REMOVE THESE WARNINGS == + dnl And now to filter out certain warnings: dnl [!] NOTE REGARDING THE check_cc_flag used by these: dnl We check for the -Woption even though we are going to use -Wno-option. @@ -247,12 +255,31 @@ check_cc_flag([-Wsign-compare], [CFLAGS="$CFLAGS -Wno-sign-compare"]) dnl Don't warn about empty body, we use this, eg via Debug(()) or in if's. check_cc_flag([-Wempty-body], [CFLAGS="$CFLAGS -Wno-empty-body"]) -dnl This one fails with ircstrdup(var, staticstring) -dnl Shame we have to turn it off completely... -check_cc_flag([-Waddress], [CFLAGS="$CFLAGS -Wno-address"]) +dnl Yeah this old clang version is a bit problematic +dnl (ships in Ubuntu 16.04 for example) +dnl -Wtautological-compare has false positives +dnl -Wno-pragmas is needed, despite -Wno-unknown-warning-option +AS_IF([$CC --version | grep -q "clang version 3."], + [CFLAGS="$CFLAGS -Wno-tautological-compare -Wno-pragmas"]) -dnl This one breaks our TO_INTFUNC() that is used in m_tkl for tkl_typetochar -check_cc_flag([-Wcast-function-type], [CFLAGS="$CFLAGS -Wno-cast-function-type"]) +dnl This one MUST be LAST!! +dnl It disables -Wsomeunknownoption being an error. Which is needed for +dnl the pragma's in individual files to selectively disable some warnings +dnl on clang/gcc (that may exist in eg gcc but not in clang or vice versa). +check_cc_flag([-Wpragmas], [no_pragmas=1],[no_pragmas=0]) +check_cc_flag([-Wunknown-warning-option], [unknown_warning_option=1], [unknown_warning_option=0]) + +if test "$unknown_warning_option" = "1"; then + dnl This is the best option + CFLAGS="$CFLAGS -Wno-unknown-warning-option" +else + if test "$no_pragmas" = "1"; then + dnl This is a fallback needed for older gcc/clang, it also + dnl disables several other useful warnings/errors related + dnl to pragma's unfortunately. + CFLAGS="$CFLAGS -Wno-pragmas" + fi +fi dnl End of -W... compiler checks. @@ -352,6 +379,8 @@ AC_CHECK_FUNCS(strlcat, AC_DEFINE([HAVE_STRLCAT], [], [Define if you have strlcat])) AC_CHECK_FUNCS(strlncat, AC_DEFINE([HAVE_STRLNCAT], [], [Define if you have strlncat])) +AC_CHECK_FUNCS(strlncpy, + AC_DEFINE([HAVE_STRLNCPY], [], [Define if you have strlncpy])) AC_CHECK_FUNCS([getrusage], [AC_DEFINE([GETRUSAGE_2], [], [Define if you have getrusage])], @@ -492,17 +521,6 @@ AC_ARG_WITH(maxconnections, [AS_HELP_STRING([--with-maxconnections=size], [Speci [ac_fd=0]) AC_DEFINE_UNQUOTED([MAXCONNECTIONS_REQUEST], [$ac_fd], [Set to the maximum number of connections you want]) -AC_ARG_ENABLE([prefixaq], - [AS_HELP_STRING([--disable-prefixaq],[Disable chanadmin (+a) and chanowner (+q) prefixes])], - [], - [enable_prefixaq=yes]) -AS_IF([test $enable_prefixaq = "yes"], - [AC_DEFINE([PREFIX_AQ], [], [Define if you want +a/+q prefixes])]) - -AC_ARG_WITH(showlistmodes, - [AS_HELP_STRING([--with-showlistmodes], [Specify whether modes are shown in /list])], - [AS_IF([test $withval = "yes"], - [AC_DEFINE([LIST_SHOW_MODES], [], [Define if you want modes shown in /list])])]) AC_ARG_WITH(no-operoverride, [AS_HELP_STRING([--with-no-operoverride], [Disable OperOverride])], [AS_IF([test $withval = "yes"], [AC_DEFINE([NO_OPEROVERRIDE], [], [Define if you want OperOverride disabled])])]) @@ -513,6 +531,7 @@ AC_ARG_WITH(system-pcre2, [AS_HELP_STRING([--without-system-pcre2], [Use the sys AC_ARG_WITH(system-argon2, [AS_HELP_STRING([--without-system-argon2], [Use bundled version instead of system argon2 library. Normally autodetected via pkg-config])], [], [with_system_argon2=yes]) AC_ARG_WITH(system-sodium, [AS_HELP_STRING([--without-system-sodium], [Use bundled version instead of system sodium library. Normally autodetected via pkg-config])], [], [with_system_sodium=yes]) AC_ARG_WITH(system-cares, [AS_HELP_STRING([--without-system-cares], [Use bundled version instead of system c-ares. Normally autodetected via pkg-config.])], [], [with_system_cares=yes]) +AC_ARG_WITH(system-jansson, [AS_HELP_STRING([--without-system-jansson], [Use bundled version instead of system jansson. Normally autodetected via pkg-config.])], [], [with_system_jansson=yes]) CHECK_SSL CHECK_SSL_CTX_SET1_CURVES_LIST CHECK_SSL_CTX_SET_MIN_PROTO_VERSION @@ -697,7 +716,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.1" +cares_version="1.17.2" AC_MSG_RESULT(extracting c-ares resolver library) cur_dir=`pwd` cd extras @@ -763,10 +782,65 @@ AC_SUBST(CARES_LIBS) cd $cur_dir ]) +dnl Use system jansson when available, unless --without-system-jansson +has_system_jansson="no" +AS_IF([test "x$with_system_jansson" = "xyes"],[ +PKG_CHECK_MODULES([JANSSON], [jansson >= 2.0.0],[has_system_jansson=yes +AS_IF([test "x$PRIVATELIBDIR" != "x"], [rm -f "$PRIVATELIBDIR/"libjansson*])],[has_system_jansson=no])]) + +AS_IF([test "$has_system_jansson" = "no"],[ +dnl REMEMBER TO CHANGE WITH A NEW JANSSON RELEASE! +jansson_version="2.13.1" +AC_MSG_RESULT(extracting jansson library) +cur_dir=`pwd` +cd extras +dnl remove old jansson directory to force a recompile... +dnl and remove its installation prefix just to clean things up. +rm -rf jansson-$jansson_version jansson +if test "x$ac_cv_path_GUNZIP" = "x" ; then + tar xfz jansson.tar.gz +else + cp jansson.tar.gz jansson.tar.gz.bak + gunzip -f jansson.tar.gz + cp jansson.tar.gz.bak jansson.tar.gz + tar xf jansson.tar +fi +AC_MSG_RESULT(compiling jansson library) +cd jansson-$jansson_version +save_cflags="$CFLAGS" +CFLAGS="$orig_cflags" +export CFLAGS +./configure --prefix=$cur_dir/extras/jansson --libdir=$PRIVATELIBDIR --enable-shared --disable-static --enable-opt || exit 1 +CFLAGS="$save_cflags" +AC_MSG_RESULT(compiling jansson resolver library) +$ac_cv_prog_MAKER || exit 1 +AC_MSG_RESULT(installing jansson resolver library) +$ac_cv_prog_MAKER install || exit 1 +JANSSON_CFLAGS="-I$cur_dir/extras/jansson/include" +AC_SUBST(JANSSON_CFLAGS) +JANSSON_LIBS= +dnl See c-ares's compilation section for more info on this hack. +dnl ensure that we're linking against the bundled version +dnl (we only reach this code if linking against the bundled version is desired). +AS_IF([test -n "$ac_cv_path_PKGCONFIG"], + [JANSSON_LIBS="`$ac_cv_path_PKGCONFIG --libs jansson.pc`"]) +dnl ^^^ FIXME FIXME this is likely incorrect the .pc etc +dnl For when pkg-config isn't available +AS_IF([test -z "$JANSSON_LIBS"], + [JANSSON_LIBS="-L$PRIVATELIBDIR -ljansson"]) +AC_SUBST(JANSSON_LIBS) +cd $cur_dir +]) + + AX_PTHREAD() CHECK_LIBCURL +CHECK_GEOIP_CLASSIC + +CHECK_LIBMAXMINDDB + UNRLINCDIR="`pwd`/include" dnl Moved to the very end to ensure it doesn't affect any libs or tests. @@ -789,7 +863,6 @@ AC_CONFIG_FILES([Makefile src/modules/Makefile src/modules/chanmodes/Makefile src/modules/usermodes/Makefile - src/modules/snomasks/Makefile src/modules/extbans/Makefile src/modules/third/Makefile extras/unrealircd-upgrade-script diff --git a/doc/Config.header b/doc/Config.header index 849ed60..98ec32f 100644 --- a/doc/Config.header +++ b/doc/Config.header @@ -7,7 +7,7 @@ \___/|_| |_|_| \___|\__,_|_|\___/\_| \_| \____/\__,_| Configuration Program - for UnrealIRCd 5.2.0.1 + for UnrealIRCd 6.0.1.1 This program will help you to compile your IRC server, and ask you questions regarding the compile-time settings of it during the process. @@ -16,15 +16,15 @@ A short installation guide is available online at: https://www.unrealircd.org/docs/Installing_from_source Full documentation is available at: -https://www.unrealircd.org/docs/UnrealIRCd_5_documentation +https://www.unrealircd.org/docs/UnrealIRCd_6_documentation -------------------------------------------------------------------------------------- The full release notes are available in doc/RELEASE-NOTES.md For easier viewing, check out the latest online release notes at: -https://github.com/unrealircd/unrealircd/blob/unreal52/doc/RELEASE-NOTES.md +https://github.com/unrealircd/unrealircd/blob/unreal60_dev/doc/RELEASE-NOTES.md -UnrealIRCd 5 is compatible with the following services: +UnrealIRCd 6 is compatible with the following services: * anope with the "unreal4" protocol module - version 2.0.7 or higher required! * atheme with the "unreal4" protocol module - tested with version 7.2.9 diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index 5379a77..e0ac30d 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -1,1000 +1,254 @@ -UnrealIRCd 5.2.0.1 Release Notes -================================= - -About 5.2.0.1 --------------- -5.2.0.1 fixes an issue with spamfilter that was present in 5.2.0. -In channels spamfilters were processed for type ```p``` instead of ```c```. -Existing 5.2.0 users on *NIX can upgrade without restart by running -```./unrealircd hot-patch wrongspamfilter520``` - -UnrealIRCd 5.2.0 is out! -------------------------- - -This is UnrealIRCd 5.2.0, a release with lots of new features. -The two main new features are: an improved and more flexible anti-flood block -and channel history which can now be stored encrypted on disk and allows -clients to fetch hundreds/thousands of lines. - -Upgrading and the 5.0.x series -------------------------------- -UnrealIRCd 5.2.0 is the direct successor to 5.0.9/5.0.9.1. -There will be [no further 5.0.x releases](https://www.unrealircd.org/docs/FAQ#About_the_new_5.2.x_series), -in particular there will be no 5.0.10. - -Only four bugs that affect a limited number of people/networks were fixed. -UnrealIRCd 5.2.0 is mostly a feature release. -Admins wishing to take a conservative approach don't need to rush an -upgrade from 5.0.x to 5.2.0, they can wait for a 5.2.1 or 5.2.2 release. - -If you are upgrading from 5.0.9(.1) to 5.2.0 then feel free to try the new -```./unrealircd upgrade``` command. - -The only configuration change is in the set::anti-flood block (as explained -further down under *Enhancements*). When starting UnrealIRCd will give you -clear instructions if anything needs to be changed (and what). -This process is really minor, the server will usually tell you to just -delete a few old lines from the configuration file. - -Enhancements -------------- -* The set::anti-flood block has been redone so you can have different limits - for *unknown-users* and *known-users*. - * As a reminder, by default, *known-users* are users who are identified - to services OR are on an IP that has been connected for over 2 hours - in the past X days. The exact definition of "known-users" is in the - [security-group block](https://www.unrealircd.org/docs/Security-group_block). - * See [here](https://www.unrealircd.org/docs/Anti-flood_settings) - for more information on the layout of the new set::anti-flood block. - * All violations of target-flood, nick-flood, join-flood, away-flood, - invite-flood, knock-flood, max-concurrent-conversations are now - reported to opers with the snomask ```f``` (flood). -* Add support for database encryption. The way this works - is that you define an encryption password in a - [secret { } block](https://www.unrealircd.org/docs/Secret_block). - Then from the various modules you can refer to this secret - block, from - [set::reputation::db-secret](https://www.unrealircd.org/docs/Set_block#set::reputation), - [set::tkldb::db-secret](https://www.unrealircd.org/docs/Set_block#set::tkldb) - and [set::channeldb::db-secret](https://www.unrealircd.org/docs/Set_block#set::channeldb). - This way you can encrypt the reputation, TKL and channel - database for increased privacy. -* Add optional support for - [persistent channel history](https://www.unrealircd.org/docs/Set_block#Persistent_channel_history): - * This stores channel history on disk for channels that have - both ```+H``` and ```+P``` set. - * If you enable this then we ALWAYS require you to set an - encryption password, as we do not allow storing of - channel history in plain text. - * If you enable the option, then the history is stored in - ```data/history/``` in individual .db files. No channel - names are visible in the filenames for optimal privacy. - * See [Persistent channel history](https://www.unrealircd.org/docs/Set_block#Persistent_channel_history) - on how to enable this. By default it is off. -* Add support for IRCv3 - [draft/chathistory](https://ircv3.net/specs/extensions/chathistory). -* The maximums for channel mode ```+H``` have been raised and are now - different for ```+r``` (registered) and ```-r``` channels. For unregistered - channels the limit is now 200 lines / 31 days. For registered channels - the limit is 5000 lines / 31 days. The old limit for both was 200 lines / 7 days. - These maximums can be changed in the now slightly different - [set::history::channel::max-storage-per-channel](https://www.unrealircd.org/docs/Set_block#set::history) - block. -* Add c-ares and libsodium version output to boot screen and /VERSION. -* WHOX now supports displaying the - [reputation score](https://www.unrealircd.org/docs/Reputation_score). - If you are an IRCOp then you can use e.g. ```WHO * %cuhsnfmdaRr```. -* Add ability to [spamfilter](https://www.unrealircd.org/docs/Spamfilter) - message tags via the new ```T``` target. Right now it would be unusual - to use this, but some day when we have more - [message tags](https://www.unrealircd.org/docs/Message_tags) it - may come in handy. -* Support [```+draft/reply```](https://ircv3.net/specs/client-tags/reply) IRCv3 - client tag. Can be used by bots (and others) to indicate to what message - people are replying to. This module, reply-tag, is loaded by default. -* Send [```draft/bot```](https://ircv3.net/specs/extensions/bot-mode) IRCv3 - message tag if the user has mode ```+B``` set. -* [Websockets](https://www.unrealircd.org/docs/WebSocket_support): - add support for clients to negotiate an explicit type via - ```Sec-WebSocket-Protocol```, instead of only the default type from - [listen::websocket::type](https://www.unrealircd.org/docs/WebSocket_support#2._Enable_websocket_on_the_port). - This is based on an IRCv3 websocket draft specification. - Note that UnrealIRCd refuses type text if your configuration allows - non-UTF8 characters in channel or nick names because it would lead - to security and compatibility issues. -* [set::restrict-commands](https://www.unrealircd.org/docs/Set_block#set::restrict-commands): - new option *exempt-tls* which allows SSL/TLS users to bypass a restriction. - -Fixes ------- -* Server squiting the wrong side. Often harmless, but when (re)connecting - rapidly to multiple servers with autoconnect this could cause the - network to fall apart. -* Forbid using [extended server bans](https://www.unrealircd.org/docs/Extended_server_bans) - in ZLINE/GZLINE since they won't work there. -* Extended server ban ```~a:accname``` was not working for shun, and only - partially working for kline/gline. -* More accurate /ELINE error message. - -Changed --------- -* Channel mode ```+H``` always showed time in minutes (```m```) until now. - From now on it will show it in minutes (```m```), hours (```h```) or - days (```d```) depending on the actual value. Eg ```+H 50:7d```. -* If you ran ```./unrealircd stop``` we used to wait only 1 second. - From now on we will wait up to 10 seconds max. This gives UnrealIRCd - plenty of time to write database files. -* If you have zero [log blocks](https://www.unrealircd.org/docs/Log_block) - then we already automatically logged errors to ```ircd.log```. - From now on we will log everything (not only errors) to that file. - -Removed --------- -* Version check for curl and openssl as nowadays they have ABI guarantees. - -Module coders / Developers ---------------------------- -* New UnrealDB API and disk format, see - https://www.unrealircd.org/docs/Dev:UnrealDB -* We now use libsodium for file encryption routines as well - as some helpers to lock/clear passwords in memory. -* Updated ```HOOKTYPE_LOCAL_NICKCHANGE``` and - ```HOOKTYPE_REMOTE_NICKCHANGE``` to include an - ```MessageTag *mtags``` argument in the middle. - You can use ```#if UNREAL_VERSION_TIME>=202115``` to detect this. -* Updated channel mode ```conv_param``` function to - include a ```Channel *channel``` argument at the end. - You can use ```#if UNREAL_VERSION_TIME>=202120``` to detect this. -* New: ```ModuleSetOptions(modinfo->handle, MOD_OPT_UNLOAD_PRIORITY, priority);```. - This can be used for modules to indicate they wish to be unloaded - before or after others. It is used by for example the channel - and history modules so they can save their databases before - channel mode modules or other modules get unloaded. -* New CAP [```draft/chathistory```](https://ircv3.net/specs/extensions/chathistory). - If a client REQ's this CAP then UnrealIRCd won't send history on-join as - it assumes the client will fetch it when they feel the need for it. -* New informative CAP: - [```unrealircd.org/history-backend```](https://www.unrealircd.org/history-backend) - -Reminder: UnrealIRCd 4 is no longer supported ----------------------------------------------- - -UnrealIRCd 4.x is [no longer supported](https://www.unrealircd.org/docs/UnrealIRCd_4_EOL). -Admins must [upgrade to UnrealIRCd 5](https://www.unrealircd.org/docs/Upgrading_from_4.x). - -UnrealIRCd 5.0.9.1 -------------------- -The only change between 5.0.9 and 5.0.9.1 is: -* Build improvements on *NIX (faster compiling and lower memory requirements) -* Windows version is unchanged and still 5.0.9 - -UnrealIRCd 5.0.9 ------------------ -The 5.0.9 release comes with several nice feature enhancements. There are no major bug fixes. - -Enhancements: -* Changes to the "Client connecting" notice on IRC (for IRCOps): - * The format changed slightly, instead of ```{clients}``` it - now shows ```[class: clients]``` - * SSL/TLS information is still shown via ```[secure]``` - * New: ```[reputation: NNN]``` to show the current - [reputation score](https://www.unrealircd.org/docs/Reputation_score) - * New: ```[account: abcdef]``` to show the services account, - but only if [SASL](https://www.unrealircd.org/docs/SASL) was used. -* In the log file the format also changed slightly: - * IP information is now added as ```[127.0.0.1]``` in both the - connect and disconnect log messages. - * The vhost is logged as ```[vhost: xyz]``` instead of ```[VHOST xyz]``` - * All the other values are now logged as well on-connect, - similar to the "Client connecting" notice, so: secure, reputation, - account (if applicable). -* New option [allow::global-maxperip](https://www.unrealircd.org/docs/Allow_block): - this imposes a global (network-wide) restriction on the number of - connections per IP address. - If you don't have a global-maxperip setting in the allow block then it - will default to maxperip plus one. So, if you currently have an - allow::maxperip of 3 then global-maxperip will be 4. -* [Handshake delay](https://www.unrealircd.org/docs/Set_block#set::handshake-delay) - is automatically disabled for users that are exempt from blacklist checking. -* Always exempt 127.* from gline, kline, etc. -* You can now have dated logfiles thanks to strftime formatting. - For example ```log "ircd.%Y-%m-%d.log" { }``` will create a log - file like called ircd.2020-01-31.log, a new one every day. -* The Windows build now supports TLSv1.3 too. +UnrealIRCd 6.0.1.1 +=================== +If you are already running UnrealIRCd 6 then read below on the +changes between 6.0.0 and 6.0.1(.1). Otherwise, jump straight to the +[summary about UnrealIRCd 6](#Summary) to learn more about UnrealIRCd 6. Fixes: -* Windows: some warnings and error messages on boot were previously - missing. +* In 6.0.1.1: extended bans were not properly synced between U5 and U6. + This caused missing extended bans on the U5 side (MODE was working OK, + this only happened when linking servers) +* Text extbans did not have any effect (`+b ~text:censor:*badword*`) +* Timed bans were not expiring if all servers on the network were on U6 +* Channel mode `+f` could place a timed extban with `~t` instead of `~time` +* Crash when unloading any of the vhoaq modules at runtime +* `./unrealircd upgrade` not working on FreeBSD and not with self-compiled cURL +* Some log messages being wrong (`CHGIDENT`, `CHGNAME`) +* Remove confusing high cpu load warning -Changes: -* Add ```doc/KEYS``` which contains the public key(s) used to sign UnrealIRCd releases -* The options set::anti-flood::unknown-flood-* have been renamed and -integrated in a new block called -[set::anti-flood::handshake-data-flood](https://www.unrealircd.org/docs/Set_block#set::anti-flood::handshake-data-flood). -The ban-action can now also be changed. Note that almost nobody will have to -change this setting since it has a good default. -* On *NIX bump the default maximum connections from 8192 to 16384. -That is, when in "auto" mode, which is like for 99% of the users. -Note that the system may still limit the actual number of connections -to a lower value, epending on the value of ```ulimit -n -H```. +Enhancements: +* Error on unknown snomask in set::snomask-on-oper and oper::snomask. +* TKL add/remove/expire messages now show `[duration: 60m]` instead of + the `[expires: ZZZ GMT]` string since that is what people are more + interested in and is not affected by time zones. The format in all the + 3 notices is also consistent now. -UnrealIRCd 5.0.8 +UnrealIRCd 6.0.0 ----------------- -The main purpose of this release is to enhance the -[reputation](https://www.unrealircd.org/docs/Reputation_score) -functionality. There have also been some other changes and minor -bug fixes. For more information, see below. - -Enhancements: -* Support for [security groups](https://www.unrealircd.org/docs/Security-group_block), - of which four groups always exist by default: known-users, unknown-users, - tls-users and tls-and-known-users. -* New extended ban ```~G:securitygroupname```. Typical usage would be - ```MODE #chan +b ~G:unknown-users``` which will ban all users from the - channel that are not identified to services and have a reputation - score below 25 (by default). The exact settings can be tweaked in the - [security group block](https://www.unrealircd.org/docs/Security-group_block). -* The reputation command (IRCOp-only) has been extended to make it - easier to look for potential troublemakers: - * ```REPUTATION Nick``` shows reputation about the nick name - * ```REPUTATION IP``` shows reputation about the IP address - * ```REPUTATION #channel``` lists users in channel with their reputation score - * ```REPUTATION chname` is `channel->name` now. +* get_channel() is now make_channel() and creates if needed, otherwise use find_channel() +* The Extended Ban API has been changed a lot. We use a `BanContext` struct now + that we pass around a lot. You also don't need to do `+3` magic anymore on the + string as it is handled in another layer. When registering the extended ban, + `.flag` is now `.letter`, and you also need to set a `.name` to a string due + to named extended bans. Have a look at the built-in extban modules to see + how to handle the changes. +* ModData now has an option `MODDATA_SYNC_EARLY`. See under *Server protocol*. +* If you want to lag someone up, don't touch `client->since`, but instead use: + `add_fake_lag(client, msec)` +* Some client/user struct changes, with `client->user->account` (instead of svid) + and `client->uplink->name` being the most important ones. +* Possibly more, but above is like 90%+ of the changes that you will encounter. Server protocol ---------------- -* UnrealIRCd 5 now assumes you support the following PROTOCTL options: - ```NOQUIT EAUTH SID NICKv2 SJOIN SJ3 NICKIP TKLEXT2```. - If you fail to use ```SID``` or ```EAUTH``` then you will receive an - error. For the other options, support is *assumed*, no warning or - error is shown when you lack support. These are options that most, - if not all, services support since UnrealIRCd 4.x so it shouldn't be - a problem. More information [here](https://www.unrealircd.org/docs/FAQ#old-server-protocol) -* ```PROTOCTL MTAGS``` indicates that the server is capable of handling - message tags and that the server can cope with 4K lines. (Note that - the ordinary non-message-tag part is still limited to 512 bytes). -* Pseudo ID support in SASL was removed. We now use real UID's. - This breaks services who rely on the old pseudo ID format. +* When multiple related `SJOIN` messages are generated for the same channel + then we now only send the current channel modes (eg `+sntk key`) in the + first SJOIN and not in the other ones as they are unneeded for the + immediate followup SJOINs, they waste unnecessary bytes and CPU. + Such messages may be generated when syncing a channel that has dozens + of users and/or bans/exempts/invexes. Ideally this should not need any + changes in other software, since we already supported such messages in the + past and code for handling it exists way back to 3.2.x, but you better + check to be sure! +* If you send `PROTOCTL NEXTBANS` then you will receive extended bans + with Named EXTended BANs instead of letters (eg: `+b ~account:xyz`), + otherwise you receive them with letters (eg: `+b ~a:xyz`). +* Some ModData of users is (also) communicated in the `UID` message while + syncing using a message tag that only appears in server-to-server traffic, + `s2s-md/moddataname=value`. Thus, data such as operinfo, tls cipher, + geoip, certfp, sasl and webirc is communicated at the same time as when + a remote connection is added. + This makes it that a "connecting from" server notice can include all this + information and also so code can make an immediate decission on what to do + with the user in hooks. ModData modules need to set + `mreq.sync = MODDATA_SYNC_EARLY;` if they want this. + Servers of course need to enable `MTAGS` in PROTOCTL to see this. +* The `SLOG` command is used to broadcast logging messages. This is done + for log::destination remote, as used in doc/conf/snomasks.default.conf, + for example for link errors, oper ups, flood messages, etc. + It also includes all JSON data in a message tag when `PROTOCTL MTAGS` is used. +* Bounced modes are gone: these were MODEs that started with a `&` which + servers were to act on with reversed logic (add becoming remove and + vice versa) and never to send something back to that server. + In practice this was almost never used and complicated the code (way) + too much. Client protocol ---------------- -* Support for message tags and other IRCv3 features. See the IRCv3 - specifications for more details. -* When a message is blocked, for whatever reason, we now use a generic - numeric response: ```:server 531 yourname targetname :reason``` for the block - This replaces all the various NOTICEs, ```ERR_NOCTCP```, ```ERR_NONONREG```, etc. - with just one single numeric. - The only other numerics that you may still encounter when PM'ing are - ```ERR_NOSUCHNICK```, ```ERR_TOOMANYTARGETS``` and ```ERR_TARGETTOOFAST```, which are - generic errors to any command involving targets. And ```ERR_SERVICESDOWN```. - Note that channel messages already had a generic numeric for signaling - blocked messages for a very long time, ```ERR_CANNOTSENDTOCHAN```. -* The 271 response to the SILENCE command is now: - ```:server 271 yournick listentry!*@*``` - Previously the nick name appeared twice, which was a mistake. -* The 470 numeric, which is sent on /JOIN #channel redirect to #redirect - now uses the following format: - ```:server 470 yournick #channel #redirect :[Link] Cannot join channel...etc..``` -* Clients are recommended to implement and enable the - [server-time](https://ircv3.net/specs/extensions/server-time-3.2) - extension by default. When enabled, channel history is played back - on-join (if any) when the channel has channel mode +H. - Otherwise your users will not see channel history. +* Extended bans now have names instead of letters. If a client sends the + old format with letters (eg `+b ~a:XYZ`) then the server will + convert it to the new format with names (eg: `+b ~account:XYZ`) +* Support for `MONITOR` and the other IRCv3 features (see *Enhancements*) diff --git a/doc/conf/badwords.conf b/doc/conf/badwords.conf index e11623e..56b5b7b 100644 --- a/doc/conf/badwords.conf +++ b/doc/conf/badwords.conf @@ -1,27 +1,44 @@ -badword channel { word "bitch"; replace "wombat"; } -badword channel { word "bro"; replace "bo"; } -badword channel { word "(brother)"; replace "bredda"; } -badword channel { word "car"; replace "taxi"; } -badword channel { word "discord"; replace "dicsord"; } -badword channel { word "efnet"; replace "efrael"; } -badword channel { word "hate"; replace "04 hate "; } -badword channel { word "hello"; replace "smello"; } -badword channel { word "house"; replace "flat"; } -badword channel { word "gaming"; replace "gaymen"; } -badword channel { word "im"; replace "m"; } -badword channel { word "i'm"; replace "m"; } -badword channel { word "i am"; replace "m"; } -badword channel { word "ima"; replace "m"; } -badword channel { word "my"; replace "me"; } -badword channel { word "(nigger)"; replace "angel"; } -badword channel { word "np"; replace "mp"; } -badword channel { word "on"; replace "pon"; } -badword channel { word "same"; replace "salami"; } -badword channel { word "(skyp)"; replace "skik"; } -badword channel { word "(ss)"; replace "ϟϟ"; } -badword channel { word "(troll)"; replace "papillion"; } -badword channel { word "uber"; replace "HELLS ANGELS"; } -badword channel { word "(year)"; replace "yonk"; } +badword channel { word "apartment"; replace "flat"; } +badword channel { word "banana"; replace "bogoya"; } +badword channel { word "bitch"; replace "wombat"; } +badword channel { word "bro"; replace "bo"; } +badword channel { word "(brother)"; replace "bredda"; } +badword channel { word "bruh"; replace "bredda"; } +badword channel { word "car"; replace "taxi"; } +badword channel { word "cool"; replace "safe"; } +badword channel { word "drunk"; replace "buck"; } +badword channel { word "dude"; replace "blud"; } +badword channel { word "discord"; replace "dicksword"; } +badword channel { word "elaborate"; replace "ebloggerate"; } +badword channel { word "efnet"; replace "efrael"; } +badword channel { word "fuckin"; replace "blood clot"; } +badword channel { word "fucking"; replace "blood clot"; } +badword channel { word "fuckn"; replace "blood clot"; } +badword channel { word "hate"; replace "04 hate "; } +badword channel { word "hello"; replace "smello"; } +badword channel { word "high"; replace "HIE"; } +badword channel { word "home"; replace "flat"; } +badword channel { word "house"; replace "flat"; } +badword channel { word "gaming"; replace "gaymen"; } +badword channel { word "im"; replace "m"; } +badword channel { word "i'm"; replace "m"; } +badword channel { word "i am"; replace "m"; } +badword channel { word "ima"; replace "m"; } +badword channel { word "jfc"; replace "bloody hell"; } +badword channel { word "my"; replace "me"; } +badword channel { word "nice"; replace "safe"; } +badword channel { word "(nigger)"; replace "angel"; } +badword channel { word "np"; replace "mp"; } +badword channel { word "on"; replace "pon"; } +badword channel { word "same"; replace "salami"; } +badword channel { word "shitfaced"; replace "buck"; } +badword channel { word "shit faced"; replace "buck"; } +badword channel { word "shoes"; replace "kicks"; } +badword channel { word "(skyp)"; replace "skik"; } +badword channel { word "(ss)"; replace "ϟϟ"; } +badword channel { word "(troll)"; replace "papillion"; } +badword channel { word "uber"; replace "HELLS ANGELS"; } +badword channel { word "(year)"; replace "yonk"; } badword channel { word "sup"; replace "wah gwaan"; } badword channel { word "wussup"; replace "wah gwaan"; } @@ -33,15 +50,18 @@ badword channel { word "what's up"; replace "wah gwaan"; } badword channel { word "wuddup"; replace "wah gwaan"; } badword channel { word "gal"; replace "bint"; } +badword channel { word "gf"; replace "bint"; } badword channel { word "(girl)"; replace "bint"; } badword channel { word "lady"; replace "bint"; } badword channel { word "ladies"; replace "bints"; } badword channel { word "(woman)"; replace "bint"; } +badword channel { word "wife"; replace "bint"; } badword channel { word "women"; replace "bints"; } badword channel { word "ganja"; replace "bobby brown"; } badword channel { word "marijuana"; replace "bobby brown"; } badword channel { word "pot"; replace "bobby brown"; } +badword channel { word "reefer"; replace "bobby brown"; } badword channel { word "weed"; replace "bobby brown"; } badword channel { word "kek"; replace "%%"; } diff --git a/doc/conf/except.conf b/doc/conf/except.conf index 5f6c0f6..18fa552 100644 --- a/doc/conf/except.conf +++ b/doc/conf/except.conf @@ -1,28 +1,34 @@ # IRCCloud -except ban { mask *@5.254.36.56/29; } -except ban { mask *@192.184.9.108/32; } -except ban { mask *@192.184.9.112/32; } -except ban { mask *@192.184.10.118/32; } -except ban { mask *@192.184.10.9/32; } -except ban { mask *@192.184.8.103/32; } -except ban { mask *@2001:67c:2f08::/48; } -except ban { mask *@2a03:5180:f::/62; } -except ban { mask *@2a03:5180:f:4::/63; } -except ban { mask *@2a03:5180:f:6::/64; } +except ban { + mask *@5.254.36.56/29; + mask *@192.184.9.108/32; + mask *@192.184.9.112/32; + mask *@192.184.10.118/32; + mask *@192.184.10.9/32; + mask *@192.184.8.103/32; + mask *@2001:67c:2f08::/48; + mask *@2a03:5180:f::/62; + mask *@2a03:5180:f:4::/63; + mask *@2a03:5180:f:6::/64; +} # KiwiIRC -except ban { mask *@107.161.19.53; } -except ban { mask *@107.161.19.109; } -except ban { mask *@109.169.31.4; } -except ban { mask *@109.169.31.13; } # KiwiIRC Verify Bot (out.kiwiirc.com) +except ban { + mask *@107.161.19.53; + mask *@107.161.19.109; + mask *@109.169.31.4; + mask *@109.169.31.13; # KiwiIRC Verify Bot (out.kiwiirc.com) +} # Mibbit -except ban { mask *@207.192.75.252; } # ircip1.mibbit.com -except ban { mask *@64.62.228.82; } # ircip2.mibbit.com -except ban { mask *@78.129.202.38; } # ircip3.mibbit.com -except ban { mask *@109.169.29.95; } # ircip4.mibbit.com -except ban { mask *@97.107.138.109; } # bot.search.mibbit.com -except ban { mask *@2600:3c03::f03c:91ff:fe96:c1fa; } # bot.search.mibbit.com +except ban { + mask *@207.192.75.252; # ircip1.mibbit.com + mask *@64.62.228.82; # ircip2.mibbit.com + mask *@78.129.202.38; # ircip3.mibbit.com + mask *@109.169.29.95; # ircip4.mibbit.com + mask *@97.107.138.109; # bot.search.mibbit.com + mask *@2600:3c03::f03c:91ff:fe96:c1fa; # bot.search.mibbit.com +} # Netsplit -except ban { mask *@85.25.10.40; } # anaconda.netsplit.de \ No newline at end of file +except ban { mask *@85.25.10.40; } # anaconda.netsplit.de \ No newline at end of file diff --git a/doc/conf/modules.conf b/doc/conf/modules.conf index f1831d2..9cbe21b 100644 --- a/doc/conf/modules.conf +++ b/doc/conf/modules.conf @@ -1,5 +1,5 @@ // Cloaking (+x) -loadmodule "cloak"; +loadmodule "cloak_sha256"; // User Commands (Minimal) #loadmodule "admin"; @@ -63,6 +63,7 @@ loadmodule "kill"; #loadmodule "locops"; loadmodule "mkpasswd"; loadmodule "oper"; +loadmodule "operinfo"; #loadmodule "opermotd"; loadmodule "sajoin"; loadmodule "samode"; @@ -76,7 +77,6 @@ loadmodule "tkl"; loadmodule "trace"; loadmodule "tsctl"; loadmodule "unsqline"; -#loadmodule "wallops"; // Server-2-Server Commands loadmodule "eos"; @@ -86,9 +86,11 @@ loadmodule "netinfo"; loadmodule "server"; loadmodule "sinfo"; loadmodule "sjoin"; +loadmodule "slog"; loadmodule "sqline"; loadmodule "swhois"; loadmodule "umode2"; +loadmodule "unreal_server_compat"; // Services Commands loadmodule "sendsno"; @@ -108,25 +110,39 @@ loadmodule "svssno"; loadmodule "svswatch"; // Channel Modes -loadmodule "chanmodes/censor"; /* +G */ -loadmodule "chanmodes/delayjoin"; /* +D */ -loadmodule "chanmodes/floodprot"; /* +f */ -loadmodule "chanmodes/history"; /* +H */ -loadmodule "chanmodes/issecure"; /* +Z */ -loadmodule "chanmodes/link"; /* +L */ -loadmodule "chanmodes/nocolor"; /* +c */ -loadmodule "chanmodes/noctcp"; /* +C */ -loadmodule "chanmodes/noinvite"; /* +V */ -loadmodule "chanmodes/nokick"; /* +Q */ -loadmodule "chanmodes/noknock"; /* +K */ -loadmodule "chanmodes/nonickchange"; /* +N */ -loadmodule "chanmodes/nonotice"; /* +T */ -loadmodule "chanmodes/operonly"; /* +O */ -loadmodule "chanmodes/permanent"; /* +P */ -loadmodule "chanmodes/regonly"; /* +R */ -loadmodule "chanmodes/regonlyspeak"; /* +M */ -loadmodule "chanmodes/secureonly"; /* +z */ -loadmodule "chanmodes/stripcolor"; /* +S */ +loadmodule "chanmodes/chanowner"; /* +q */ +loadmodule "chanmodes/chanadmin"; /* +a */ +loadmodule "chanmodes/chanop"; /* +o */ +loadmodule "chanmodes/halfop"; /* +h */ +loadmodule "chanmodes/voice"; /* +v */ +loadmodule "chanmodes/censor"; /* +G */ +loadmodule "chanmodes/delayjoin"; /* +D */ +loadmodule "chanmodes/floodprot"; /* +f */ +loadmodule "chanmodes/history"; /* +H */ +loadmodule "chanmodes/inviteonly"; /* +i */ +loadmodule "chanmodes/isregistered"; /* +r */ +loadmodule "chanmodes/issecure"; /* +Z */ +loadmodule "chanmodes/key"; /* +k */ +loadmodule "chanmodes/limit"; /* +l */ +loadmodule "chanmodes/link"; /* +L */ +loadmodule "chanmodes/moderated"; /* +m */ +loadmodule "chanmodes/nocolor"; /* +c */ +loadmodule "chanmodes/noctcp"; /* +C */ +loadmodule "chanmodes/noexternalmsgs"; /* +n */ +loadmodule "chanmodes/noinvite"; /* +V */ +loadmodule "chanmodes/nokick"; /* +Q */ +loadmodule "chanmodes/noknock"; /* +K */ +loadmodule "chanmodes/nonickchange"; /* +N */ +loadmodule "chanmodes/nonotice"; /* +T */ +loadmodule "chanmodes/operonly"; /* +O */ +loadmodule "chanmodes/permanent"; /* +P */ +loadmodule "chanmodes/private"; /* +p */ +loadmodule "chanmodes/regonly"; /* +R */ +loadmodule "chanmodes/regonlyspeak"; /* +M */ +loadmodule "chanmodes/secret"; /* +s */ +loadmodule "chanmodes/secureonly"; /* +z */ +loadmodule "chanmodes/stripcolor"; /* +S */ +loadmodule "chanmodes/topiclimit"; /* +t */ // User Modes loadmodule "usermodes/bot"; /* +B */ @@ -139,24 +155,23 @@ loadmodule "usermodes/regonlymsg"; /* +R */ loadmodule "usermodes/secureonlymsg"; /* +Z */ loadmodule "usermodes/servicebot"; /* +S */ #loadmodule "usermodes/showwhois"; /* +W */ - -// Snomasks -#loadmodule "snomasks/dccreject"; /* +D */ +#loadmodule "usermodes/wallops"; /* +w */ // Extended Bans -loadmodule "extbans/account"; /* +b ~a */ -#loadmodule "extbans/certfp"; /* +b ~S */ -#loadmodule "extbans/inchannel"; /* +b ~c */ -loadmodule "extbans/join"; /* +b ~j */ -loadmodule "extbans/msgbypass"; /* +e ~m */ -#loadmodule "extbans/nickchange"; /* +b ~n */ -#loadmodule "extbans/operclass"; /* +b ~O */ -#loadmodule "extbans/partmsg"; /* +b ~p */ -loadmodule "extbans/quiet"; /* +b ~q */ -#loadmodule "extbans/realname"; /* +b ~r */ -#loadmodule "extbans/textban"; /* +b ~T */ -loadmodule "extbans/timedban"; /* +b ~t */ -loadmodule "extbans/securitygroup"; /* +b ~G */ +loadmodule "extbans/account"; /* +b ~account */ +loadmodule "extbans/certfp"; /* +b ~certfp */ +#loadmodule "extbans/country"; /* +b ~country */ +loadmodule "extbans/inchannel"; /* +b ~channel */ +loadmodule "extbans/join"; /* +b ~join */ +loadmodule "extbans/msgbypass"; /* +e ~msgbypass */ +#loadmodule "extbans/nickchange"; /* +b ~nickchange */ +#loadmodule "extbans/operclass"; /* +b ~operclass */ +#loadmodule "extbans/partmsg"; /* +b ~partmsg */ +loadmodule "extbans/quiet"; /* +b ~quiet */ +#loadmodule "extbans/realname"; /* +b ~realname */ +loadmodule "extbans/textban"; /* +b ~text */ +loadmodule "extbans/timedban"; /* +b ~time */ +loadmodule "extbans/securitygroup"; /* +b ~security-group */ // IRCv3 Extensions loadmodule "account-notify"; @@ -166,10 +181,12 @@ loadmodule "bot-tag"; loadmodule "chathistory"; loadmodule "clienttagdeny"; loadmodule "echo-message"; +loadmodule "extended-monitor"; loadmodule "labeled-response"; loadmodule "link-security"; loadmodule "message-ids"; loadmodule "message-tags"; +loadmodule "monitor"; loadmodule "plaintext-policy"; loadmodule "reply-tag"; loadmodule "server-time"; @@ -178,24 +195,29 @@ loadmodule "typing-indicator"; // Other loadmodule "antimixedutf8"; -loadmodule "authprompt"; +#loadmodule "authprompt"; loadmodule "blacklist"; loadmodule "certfp"; loadmodule "channeldb"; loadmodule "charsys"; loadmodule "connthrottle"; +#loadmodule "geoip_base"; +#loadmodule "geoip_classic"; loadmodule "hideserver"; loadmodule "history_backend_mem"; #loadmodule "history_backend_null"; loadmodule "ident_lookup"; loadmodule "jointhrottle"; +loadmodule "json-log-tag"; loadmodule "targetfloodprot"; loadmodule "tkldb"; loadmodule "tls_antidos"; +loadmodule "tls_cipher"; loadmodule "userhost-tag"; loadmodule "userip-tag"; loadmodule "reputation"; loadmodule "restrict-commands"; loadmodule "rmtkl"; +loadmodule "watch-backend"; #loadmodule "webirc"; #loadmodule "websocket"; \ No newline at end of file diff --git a/doc/conf/snomasks.conf b/doc/conf/snomasks.conf new file mode 100644 index 0000000..5872ccc --- /dev/null +++ b/doc/conf/snomasks.conf @@ -0,0 +1,228 @@ +/* Server bans snomask - 'b' */ +log { + source { + tkl.BAN_REALNAME; + tkl.TKL_ADD; + tkl.TKL_DEL; + tkl.TKL_ADD_TEMPSHUN; + tkl.TKL_DEL_TEMPSHUN; + tkl.TKL_EXPIRE; + tkl.RMTKL_COMMAND; + } + destination { + snomask b; + } +} + +/* Blacklist snomask: 'B' */ +log { + source { + blacklist; + } + destination { + snomask B; + } +} + +/* Local client connects snomask - 'c' */ +log { + source { + connect.LOCAL_CLIENT_CONNECT; + connect.LOCAL_CLIENT_DISCONNECT; + } + destination { + snomask c; + } +} + +/* Remote client connects snomask - 'C' */ +log { + source { + connect.REMOTE_CLIENT_CONNECT; + connect.REMOTE_CLIENT_DISCONNECT; + } + destination { + snomask C; + } +} + +/* DCC rejections snomask - 'd' */ +log { + source { + dcc; + } + destination { + snomask d; + } +} + +/* Debug snomask (not recommended) - 'D' */ +log { + source { + debug; + } + destination { + snomask D; + } +} + +/* Floods snomask - 'f' */ +log { + source { + flood; + } + destination { + snomask f; + } +} + +/* Join, parts, kicks - 'j' */ +log { + source { + // TODO: these don't exist yet.. + join.LOCAL_CLIENT_JOIN; + join.REMOTE_CLIENT_JOIN; + part.LOCAL_CLIENT_PART; + part.REMOTE_CLIENT_PART; + kick.LOCAL_CLIENT_KICK; + kick.REMOTE_CLIENT_KICK; + } + destination { + snomask j; + } +} + +/* Kill snomask */ +log { + source { + kill; + } + destination { + snomask k; + } +} + +/* Local nick changes snomask - 'n' */ +log { + source { + nick.LOCAL_NICK_CHANGE; + } + destination { + snomask n; + } +} + +/* Remote nick changes snomask - 'N' */ +log { + source { + nick.REMOTE_NICK_CHANGE; + } + destination { + snomask N; + } +} + +/* Deny nick (QLINE) rejections snomask - 'q' */ +log { + source { + nick.QLINE_NICK_LOCAL_ATTEMPT; + nick.QLINE_NICK_REMOTE; + } + destination { + snomask q; + } +} + +/* Spamfilter hits snomask - 'S' */ +log { + source { + tkl.SPAMFILTER_MATCH; + } + destination { + snomask S; + } +} + +/* IRCOp overriding in channels (OperOverride) - 'o' */ +log { + source { + operoverride; + } + destination { + snomask o; + } +} + +/* IRCOp changing user properties or forcing users to do things - 'O' */ +log { + source { + chgcmds; + sacmds; + } + destination { + snomask O; + } +} + +/* VHOST usage - 'v' */ +log { + source { + vhost; + } + destination { + snomask v; + } +} + +/* Snomask s (server notices) - the "catch all" snomask for all other things */ +log { + source { + link; + oper; + !debug; + nomatch; + } + destination { + snomask s; + } +} + +/* These log sources are sent to all servers (globally). + * These are generally two categories: + * 1) Things that affect the network as a whole, eg linking + * 2) Things that otherwise cannot be logged by a remote server + * that may interest ircops. Eg: a spamfilter match, + * since that would otherwise not be propagated. + */ +log { + source { + /* All link messages affect the network so + * these should be global. Except for the + * link connecting... and timeout while + * connecting.. messages, which can be noisy. + */ + link; + !link.LINK_CONNECTING; + !link.LINK_CONNECT_TIMEOUT; + !link.SERVER_LINKED_REMOTE; + !link.SERVER_LINKED; + /* All oper up/downs */ + oper; + /* Flood messages, important to keep an eye on, network-wide */ + flood; + /* TEMPSHUN: these are otherwise missing for snomask 'b' */ + tkl.TKL_ADD_TEMPSHUN; + tkl.TKL_DEL_TEMPSHUN; + /* Spamfilter matches: needed for snomask 'S' */ + tkl.SPAMFILTER_MATCH; + /* Critical issue: */ + tls.TLS_CERT_EXPIRING; + /* SAMODE: needed for snomask 'o' */ + samode.SAMODE_COMMAND; + /* Never any debug messages */ + !debug; + } + destination { + remote; + } +} diff --git a/doc/conf/tls/curl-ca-bundle.crt b/doc/conf/tls/curl-ca-bundle.crt index 3c79c59..0bf312f 100644 --- a/doc/conf/tls/curl-ca-bundle.crt +++ b/doc/conf/tls/curl-ca-bundle.crt @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Tue Jan 19 04:12:04 2021 GMT +## Certificate data from Mozilla as of: Tue Oct 26 03:12:05 2021 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.28. -## SHA256: 3bdc63d1de27058fec943a999a2a8a01fcc6806a611b19221a7727d3d9bbbdfd +## SHA256: bb36818a81feaa4cca61101e6d6276cd09e972efcb08112dfed846918ca41d7f ## @@ -156,38 +156,6 @@ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z 12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- -QuoVadis Root CA -================ ------BEGIN CERTIFICATE----- -MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE -ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz -MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp -cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD -EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk -J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL -F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL -YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen -AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w -PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y -ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 -MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj -YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs -ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh -Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW -Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu -BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw -FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 -tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo -fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul -LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x -gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi -5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi -5nrQNiOKSnQ2+Q== ------END CERTIFICATE----- - QuoVadis Root CA 2 ================== -----BEGIN CERTIFICATE----- @@ -275,26 +243,6 @@ s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ FL39vmwLAw== -----END CERTIFICATE----- -Sonera Class 2 Root CA -====================== ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG -U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw -NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh -IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 -/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT -dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG -f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P -tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH -nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT -XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt -0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI -cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph -Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx -EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH -llpwrN9M ------END CERTIFICATE----- - XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- @@ -433,26 +381,6 @@ mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K -----END CERTIFICATE----- -DST Root CA X3 -============== ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK -ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X -DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 -cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT -rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 -UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy -xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d -utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ -MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug -dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE -GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw -RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS -fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- - SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- @@ -718,51 +646,6 @@ vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD -----END CERTIFICATE----- -GeoTrust Primary Certification Authority - G2 -============================================= ------BEGIN CERTIFICATE----- -MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu -Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 -OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg -MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl -b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG -BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc -KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ -EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m -ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 -npaqBA+K ------END CERTIFICATE----- - -VeriSign Universal Root Certification Authority -=============================================== ------BEGIN CERTIFICATE----- -MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE -BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO -ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk -IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u -IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv -cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj -1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP -MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 -9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I -AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR -tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G -CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O -a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud -DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 -Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx -Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx -P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P -wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 -mJO37M2CYfE45k+XmCpajQ== ------END CERTIFICATE----- - NetLock Arany (Class Gold) Főtanúsítvány ======================================== -----BEGIN CERTIFICATE----- @@ -938,82 +821,6 @@ Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE----- -Chambers of Commerce Root - 2008 -================================ ------BEGIN CERTIFICATE----- -MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD -MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv -bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu -QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy -Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl -ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF -EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl -cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA -XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj -h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ -ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk -NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g -D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 -lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ -0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj -ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 -EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI -G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ -BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh -bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh -bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC -CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH -AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 -wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH -3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU -RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 -M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 -YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF -9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK -zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG -nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg -OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ ------END CERTIFICATE----- - -Global Chambersign Root - 2008 -============================== ------BEGIN CERTIFICATE----- -MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD -MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv -bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu -QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx -NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg -Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ -QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD -aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf -VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf -XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 -ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB -/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA -TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M -H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe -Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF -HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh -wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB -AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT -BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE -BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm -aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm -aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp -1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 -dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG -/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 -ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s -dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg -9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH -foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du -qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr -P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq -c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z -09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B ------END CERTIFICATE----- - Go Daddy Root Certificate Authority - G2 ======================================== -----BEGIN CERTIFICATE----- @@ -1315,27 +1122,6 @@ OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== -----END CERTIFICATE----- -Trustis FPS Root CA -=================== ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG -EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 -IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV -BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ -RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk -H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa -cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt -o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA -AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd -BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c -GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC -yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P -8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV -l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl -iB6XzCGcKQENZetX2fNXlrtIzYE= ------END CERTIFICATE----- - Buypass Class 2 Root CA ======================= -----BEGIN CERTIFICATE----- @@ -1980,36 +1766,6 @@ uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 -----END CERTIFICATE----- -Staat der Nederlanden Root CA - G3 -================================== ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE -CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g -Um9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloXDTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMC -TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l -ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4y -olQPcPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WWIkYFsO2t -x1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqXxz8ecAgwoNzFs21v0IJy -EavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFyKJLZWyNtZrVtB0LrpjPOktvA9mxjeM3K -Tj215VKb8b475lRgsGYeCasH/lSJEULR9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUur -mkVLoR9BvUhTFXFkC4az5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU5 -1nus6+N86U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7Ngzp -07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHPbMk7ccHViLVlvMDo -FxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXtBznaqB16nzaeErAMZRKQFWDZJkBE -41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTtXUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMB -AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleu -yjWcLhL75LpdINyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD -U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwpLiniyMMB8jPq -KqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8Ipf3YF3qKS9Ysr1YvY2WTxB1 -v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixpgZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA -8KCWAg8zxXHzniN9lLf9OtMJgwYh/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b -8KKaa8MFSu1BYBQw0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0r -mj1AfsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq4BZ+Extq -1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR1VmiiXTTn74eS9fGbbeI -JG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/QFH1T/U67cjF68IeHRaVesd+QnGTbksV -tzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM94B7IWcnMFk= ------END CERTIFICATE----- - Staat der Nederlanden EV Root CA ================================ -----BEGIN CERTIFICATE----- @@ -3226,3 +2982,251 @@ qqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oG I/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmg kpzNNIaRkPpkUZ3+/uul9XXeifdy -----END CERTIFICATE----- + +AC RAIZ FNMT-RCM SERVIDORES SEGUROS +=================================== +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQGEwJF +UzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRFUy1RMjgy +NjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4 +MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQt +UkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNB +QyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LHsbI6GA60XYyzZl2hNPk2 +LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oKUm8BA06Oi6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqG +SM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoD +zBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c= +-----END CERTIFICATE----- + +GlobalSign Root R46 +=================== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJv +b3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAX +BgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08Es +CVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQGvGIFAha/ +r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud316HCkD7rRlr+/fKYIje +2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEt +bWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvj +K8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD4 +12lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/on +ccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdls +eVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9 +vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEM +BQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWy +gxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92 +CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZm +OUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qq +JZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwye +qiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboz +nxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7 +DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3 +QEUxeCp6 +-----END CERTIFICATE----- + +GlobalSign Root E46 +=================== +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNVBAYT +AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJvb3Qg +RTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNV +BAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkB +jtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGddyXqBPCCj +QjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRL +gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk +vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ +CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +GLOBALTRUST 2020 +================ +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx +IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT +VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh +BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy +MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi +D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO +VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM +CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm +fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA +A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR +JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG +DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU +clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ +mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud +IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw +4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9 +iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS +8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2 +HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS +vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918 +oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF +YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl +gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +ANF Secure Server Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2MzI4 +NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lv +bjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3Qg +Q0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEw +MQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQw +EgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9vdCBDQTCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCjcqQZAZ2cC4Ffc0m6p6zz +BE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9qyGFOtibBTI3/TO80sh9l2Ll49a2pcbnv +T1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcv +B2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXse +zx76W0OLzc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RM +VwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j +7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5z +JTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe +8TZBAQIvfXOn3kLMTOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVO +Hj1tyRRM4y5Bu8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ +UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzx +j6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDt +dD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM +5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjqOknkJjCb +5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ/zo1PqVUSlJZS2Db7v54 +EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis6ELhDtjTO0wugumDLmsx2d1H +hk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGy +g77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3 +r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +Certum EC-384 CA +================ +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQGEwJQ +TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2 +MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERh +dGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +GTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEKI6rGFtq +vm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7TmFy8as10CW4kjPMIRBSqn +iBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68KjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNo +ADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0 +QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +Certum Trusted Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYDVQQG +EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0Ew +HhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMY +QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZn0EGze2jusDbCSzBfN8p +fktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/qp1x4EaTByIVcJdPTsuclzxFUl6s1wB52 +HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2 +fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGt +g/BKEiJ3HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4 +NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQk +fVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJ +P/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSY +njYJdmZm/Bo/6khUHL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHK +HRzQ+8S1h9E6Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QAL +LtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s +ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiGPp2K +h2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8 +CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA +4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq0Uc9Nneo +WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj +6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmT +OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck +bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +TunTrust Root CA +================ +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTELMAkG +A1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlvbiBFbGVj +dHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQw +NDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBD +ZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZn56eY+hz +2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd2JQDoOw05TDENX37Jk0b +bjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgFVwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7 +NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAd +gjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViW +VSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8f +Tpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZ +juXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYas +DXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwS +VXAkPcvCFDVDXSdOvsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI +04Y+oXNZtPdEITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl +0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd +Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRY +YdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJp +adbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65x +xBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5Xc0yGYuP +jCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7bnV2UqL1g52KAdoGDDIzM +MEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9z +ZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3r +AZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +HARICA TLS RSA Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz +OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl +bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB +IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN +JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu +a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y +Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K +5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv +dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR +0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH +GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm +haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ +CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU +EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq +QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD +QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR +j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5 +vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0 +qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6 +Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/ +PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn +kf3/W9b3raYvAwtt41dU63ZTGI0RmLo= +-----END CERTIFICATE----- + +HARICA TLS ECC Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH +UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD +QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX +DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj +IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv +b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l +AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b +ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW +0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi +rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw +CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- diff --git a/doc/conf/unrealircd.hub.conf b/doc/conf/unrealircd.hub.conf index b55d85e..1e11620 100644 --- a/doc/conf/unrealircd.hub.conf +++ b/doc/conf/unrealircd.hub.conf @@ -16,9 +16,10 @@ link services.supernets.org { class servers; } -log "ircd.log" { flags { errors; } maxsize 1K; } - -except ban { mask *@127.0.0.1; } +log { + source { error; fatal; warn; } + destination { file "ircd.log" { maxsize 10M; } } +} ulines { services.supernets.org; } diff --git a/doc/conf/unrealircd.remote.conf b/doc/conf/unrealircd.remote.conf index 3091611..80b1a05 100644 --- a/doc/conf/unrealircd.remote.conf +++ b/doc/conf/unrealircd.remote.conf @@ -14,8 +14,8 @@ alias os { target operserv; type services; } class clients { pingfreq 120; maxclients 100; sendq 1M; options { nofakelag; } } class servers { pingfreq 120; maxclients 10; sendq 1M; connfreq 30; } -allow { ip *; class clients; maxperip 2; global-maxperip 2; } -allow { ip 127.0.0.1; class clients; maxperip 10; global-maxperip 10; } +allow { mask *; class clients; maxperip 2; global-maxperip 2; } +allow { mask 127.0.0.1; class clients; maxperip 10; global-maxperip 10; } #require authentication { # mask *@*; @@ -26,9 +26,10 @@ listen { ip *; port 6667; options { clientsonly; } } listen { ip *; port 6697; options { clientsonly; tls; } } listen { ip *; port REDACTED; options { serversonly; tls; } } -deny channel { channel "#pumpcoin"; reason "This channel has moved to #exchange"; redirect "#exchange"; } deny channel { channel "#dev"; reason "This channel has moved to #superbowl"; redirect "#superbowl"; } deny channel { channel "#help"; reason "This channel has moved to #superbowl"; redirect "#superbowl"; } +deny channel { channel "#mensa"; reason "This channel has been closed"; redirect "#superbowl"; } +deny channel { channel "#pumpcoin"; reason "This channel has moved to #exchange"; redirect "#exchange"; } link irc.supernets.org { incoming { mask REDACTED; } @@ -42,7 +43,15 @@ link irc.supernets.org { class servers; } -log "errors.log" { flags { errors; } maxsize 10K; } +log { + source { error; fatal; warn; } + destination { file "ircd.log" { maxsize 10M; } } +} + +log { + source { all; } + destination { channel "#REDACTED" } +} tld { mask *@*; motd remote.motd; rules remote.motd; options { remote; } } @@ -81,39 +90,28 @@ blacklist torbl { reason "8,4 E N T E R T H E V O I D "; } -webirc { mask 107.161.19.53; password "REDACTED"; } # KiwiIRC -webirc { mask 107.161.19.109; password "REDACTED"; } -webirc { mask 107.161.31.4; password "REDACTED"; } - -webirc { mask 207.192.75.252; password "REDACTED"; } # Mibbit -webirc { mask 64.62.228.82; password "REDACTED"; } -webirc { mask 78.129.202.38; password "REDACTED"; } -webirc { mask 109.169.29.95 ; password "REDACTED"; } - set { kline-address "enterthevoid@supernets.org"; gline-address "enterthevoid@supernets.org"; modes-on-connect "+iIpTx"; modes-on-oper "+Hq"; - snomask-on-oper "+bcFfkGsSo"; + snomask-on-oper "+bBcCfksSoO"; modes-on-join "+ns"; level-on-join "op"; restrict-usermodes "ips"; restrict-channelmodes "nLpPs"; restrict-commands { - channel-message { connect-delay 60; exempt-identified yes; exempt-reputation-score 100; } - channel-notice { connect-delay 60; exempt-identified yes; exempt-reputation-score 100; } - invite { connect-delay 3600; exempt-identified yes; exempt-reputation-score 100; } - join { connect-delay 15; exempt-identified yes; exempt-reputation-score 100; } - list { connect-delay 30; exempt-identified yes; exempt-reputation-score 100; } - private-message { connect-delay 300; exempt-identified yes; exempt-reputation-score 100; } - private-notice { connect-delay 3600; exempt-identified yes; exempt-reputation-score 100; } + channel-message { connect-delay 60; exempt-identified yes; exempt-reputation-score 100; } + channel-notice { connect-delay 60; exempt-identified yes; exempt-reputation-score 100; } + invite { connect-delay 300; exempt-identified yes; exempt-reputation-score 100; } + join { connect-delay 15; exempt-identified yes; exempt-reputation-score 100; } + list { connect-delay 30; exempt-identified yes; exempt-reputation-score 100; } + private-message { connect-delay 300; exempt-identified yes; exempt-reputation-score 100; } + private-notice { connect-delay 300; exempt-identified yes; exempt-reputation-score 100; } } - #auto-join "#superbowl"; - oper-auto-join "#superbowl"; + auto-join "#superbowl"; static-quit "EMO-QUIT"; static-part "EMO-PART"; - who-limit 100; nick-length 20; maxchannelsperuser 10; channel-command-prefix "`!@$."; @@ -134,15 +132,15 @@ set { user warn; oper deny; server deny; - user-message "4WARNING: You are not using a secure (SSL/TLS) connection"; - oper-message "Network operators must connect using SSL/TLS"; + user-message "4WARNING: You are not on a secure TLS connection"; + oper-message "Network operators must be on a secure TLS connection"; } outdated-tls-policy { user warn; oper deny; server deny; user-message "4WARNING: You are using an outdated SSL/TLS protocol or cipher"; - oper-message "Network operators must connect using an up-to-date SSL/TLS protocol or cipher"; + oper-message "Network operators must be using an up-to-date SSL/TLS protocol & cipher"; } anti-flood { everyone { @@ -152,14 +150,14 @@ set { ban-action gzline; ban-time 1h; } - target-flood { - channel-notice 15:5; - channel-privmsg 45:5; - channel-tagmsg 15:5; - private-notice 10:5; - private-privmsg 30:5; - private-tagmsg 10:5; - } + #target-flood { + # channel-notice 15:5; + # channel-privmsg 45:5; + # channel-tagmsg 15:5; + # private-notice 10:5; + # private-privmsg 30:5; + # private-tagmsg 10:5; + #} } known-users { away-flood 3:300; @@ -171,6 +169,8 @@ set { users 5; new-user-every 60s; } + lag-penalty 10; # update? + lag-penalty-bytes 0; } unknown-users { away-flood 3:300; @@ -182,6 +182,8 @@ set { users 3; new-user-every 60s; } + lag-penalty 1000; + lag-penalty-bytes 90; } } default-bantime 30d; @@ -207,32 +209,50 @@ set { ban-reason "8,4 E N T E R T H E V O I D "; } connthrottle { - known-users { minimum-reputation-score 100; sasl-bypass yes; } - new-users { local-throttle 20:60; global-throttle 30:60; } - disabled-when { reputation-gathering 1w; start-delay 3m; } + known-users { minimum-reputation-score 25; sasl-bypass yes; } + new-users { local-throttle 20:60; global-throttle 30:60; } + disabled-when { reputation-gathering 1w; start-delay 3m; } } history { channel { - playback-on-join { lines 100; time 1d; } + playback-on-join { lines 1000; time 1d; } max-storage-per-channel { - registered { lines 100; time 1d; } - unregistered { lines 50; time 1h; } + registered { lines 1000; time 1d; } + unregistered { lines 100; time 1h; } } } } - hide-idle-time { policy usermode; } + hide-idle-time { policy always; } + whois-details { + basic { everyone full; } + modes { everyone none; self full; oper full; } + realhost { everyone none; self full; oper full; } + registered-nick { everyone full; } + channels { everyone limited; self full; oper full; } + server { everyone full; } + away { everyone full; } + oper { everyone limited; self full; oper full; } + secure { everyone limited; self full; oper full; } + bot { everyone full; } + services { everyone full; } + reputation { everyone none; self none; oper full; } + geo { everyone none; self none; oper full; } + certfp { everyone full; } + shunned { everyone none; self none; oper full; } + account { everyone full; } + swhois { everyone full; } + idle { everyone limited; self full; oper full; } + } } hideserver { disable-map yes; disable-links yes; - map-deny-message "Denied"; - links-deny-message "Denied"; + map-deny-message "8,4 E N T E R T H E V O I D "; + links-deny-message "8,4 E N T E R T H E V O I D "; } security-group known-users { identified yes; - webirc no; - tls no; - reputation-score 100; + reputation-score 25; } \ No newline at end of file diff --git a/doc/translations.txt b/doc/translations.txt index 800c00a..a89c6d8 100644 --- a/doc/translations.txt +++ b/doc/translations.txt @@ -1,6 +1,6 @@ ==[ Translations ]=========================================================== -In UnrealIRCd 5 we support the following translations: +In UnrealIRCd we support the following translations: * on-line documentation at https://www.unrealircd.org/docs/ (wiki!) * help.conf * example.conf diff --git a/extras/build-tests/nix/build b/extras/build-tests/nix/build index c589458..caab490 100755 --- a/extras/build-tests/nix/build +++ b/extras/build-tests/nix/build @@ -16,7 +16,7 @@ else export MAKE="make -j4" fi -export CPPFLAGS="-DFAKELAG_CONFIGURABLE -DNOREMOVETMP" +export CPPFLAGS="-DFAKELAG_CONFIGURABLE -DNOREMOVETMP -DRAWCMDLOGGING" # !! skipped for now: extras/build-tests/nix/select-config $BUILDCONFIG !! # !! temporary use this: @@ -27,6 +27,16 @@ if lsb_release -av 2>&1|egrep 'Debian.*jessie'; then echo "Disabling ASan due to false positives on deb8" echo 'EXTRAPARA="--enable-werror --disable-asan"' >>config.settings fi +if uname -s|grep -i freebsd; then + echo "Disabling ASan on FreeBSD due to 100% CPU loop in OpenSSL initialization routine" + echo 'EXTRAPARA="--enable-werror --disable-asan"' >>config.settings +fi + +# If SSLDIR is set the environment, this overrides config.settings +# Used for example in the openssl3 build tests. +if [ "$SSLDIR" != "" ]; then + echo 'SSLDIR="'"$SSLDIR"'"' >>config.settings +fi # Read config.settings, this makes a couple of variables available to us. . ./config.settings @@ -36,10 +46,10 @@ if [ "$SSLDIR" != "" ]; then fi ./Config -quick || (tail -n 5000 config.log; exit 1) $MAKE -yes ''|make pem -make +yes ''|$MAKE pem +$MAKE || exit 1 ./unrealircd module install third/dumpcmds -make install +$MAKE install || exit 1 set +x echo "" diff --git a/extras/build-tests/nix/configs/default b/extras/build-tests/nix/configs/default index 3a7c28a..648a3ea 100644 --- a/extras/build-tests/nix/configs/default +++ b/extras/build-tests/nix/configs/default @@ -1,3 +1,9 @@ +# These are the settings saved from running './Config'. +# Note that it is not recommended to edit config.settings by hand! +# Chances are you misunderstand what a variable does or what the +# supported values are. You better just re-run the ./Config script +# and answer appropriately there, to get a correct config.settings +# file. # BASEPATH=$HOME/unrealircd BINDIR=$HOME/unrealircd/bin @@ -9,16 +15,16 @@ CACHEDIR=$HOME/unrealircd/cache DOCDIR=$HOME/unrealircd/doc TMPDIR=$HOME/unrealircd/tmp PRIVATELIBDIR=$HOME/unrealircd/lib -PREFIXAQ="1" -MAXCONNECTIONS="1024" +MAXCONNECTIONS_REQUEST="auto" NICKNAMEHISTORYLENGTH="2000" +GEOIP="classic" DEFPERM="0600" SSLDIR="" REMOTEINC="" CURLDIR="" -SHOWLISTMODES="1" NOOPEROVERRIDE="" OPEROVERRIDEVERIFY="" GENCERTIFICATE="0" -EXTRAPARA="--enable-werror --enable-asan" +SANITIZER="asan" +EXTRAPARA="--enable-werror" ADVANCED="" diff --git a/extras/build-tests/nix/run-tests b/extras/build-tests/nix/run-tests index a25c101..02df361 100755 --- a/extras/build-tests/nix/run-tests +++ b/extras/build-tests/nix/run-tests @@ -19,8 +19,13 @@ if [ ! -d ~/cipherscan ]; then git clone -q https://github.com/mozilla/cipherscan fi +if [ "$HOSTNAME" = "deb8" ]; then + echo "Not running tests on Debian 8. It's LTS is EOL and trouble with running tests." + exit 0 +fi + # Install 'unrealircd-tests' -git clone -q https://github.com/unrealircd/unrealircd-tests.git +git clone -q --branch unreal60 https://github.com/unrealircd/unrealircd-tests.git unrealircd-tests cd unrealircd-tests # FreeBSD has various issues with the tests from us and others, diff --git a/extras/build-tests/windows/build.bat b/extras/build-tests/windows/build.bat index 9eee847..6f8ed3c 100644 --- a/extras/build-tests/windows/build.bat +++ b/extras/build-tests/windows/build.bat @@ -16,35 +16,44 @@ rem cinst innosetup -y rem Installing UnrealIRCd dependencies cd \projects -mkdir unrealircd-5-libs -cd unrealircd-5-libs -curl -fsS -o unrealircd-libraries-5-devel.zip https://www.unrealircd.org/files/dev/win/libs/unrealircd-libraries-5-devel.zip -unzip unrealircd-libraries-5-devel.zip -copy dlltool.exe \users\user\worker\unreal5-w10\build /y +mkdir unrealircd-6-libs +cd unrealircd-6-libs +curl -fsS -o unrealircd-libraries-6-devel.zip https://www.unrealircd.org/files/dev/win/libs/unrealircd-libraries-6-devel.zip +unzip unrealircd-libraries-6-devel.zip +copy dlltool.exe \users\user\worker\unreal6-w10\build /y -rem for appveyor: cd \projects\unrealircd -cd \users\user\worker\unreal5-w10\build +rem for appveyor, use: cd \projects\unrealircd +cd \users\user\worker\unreal6-w10\build + +rem Install 'unrealircd-tests' +cd .. +rd /q/s unrealircd-tests +git clone -q --branch unreal60 https://github.com/unrealircd/unrealircd-tests.git unrealircd-tests +if %ERRORLEVEL% NEQ 0 EXIT /B 1 +cd build rem Now the actual build -call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat - -rem The above command will fail, due to missing symbol file -rem However the symbol file can only be generated after the above command -rem So... we create the symbolfile... +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 +rem - It will fail due to missing symbolfile, which we create here.. nmake -f makefile.windows SYMBOLFILE - -rem And we re-run the exact same command: -call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat +rem - Then we finalize building UnrealIRCd.exe: should be no error +call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat UNREALSVC.EXE UnrealIRCd.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 if %ERRORLEVEL% NEQ 0 EXIT /B 1 rem Compile dependencies for unrealircd-tests -- this doesn't belong here though.. -curl -fsS -o src\modules\third\fakereputation.c https://raw.githubusercontent.com/unrealircd/unrealircd-tests/master/serverconfig/unrealircd/modules/fakereputation.c +copy ..\unrealircd-tests\serverconfig\unrealircd\modules\fakereputation.c src\modules\third /Y call extras\build-tests\windows\compilecmd\%SHORTNAME%.bat CUSTOMMODULE MODULEFILE=fakereputation if %ERRORLEVEL% NEQ 0 EXIT /B 1 -rem Convert c:\dev to c:\projects\unrealircd-5-libs +rem Convert c:\dev to c:\projects\unrealircd-6-libs rem TODO: should use environment variable in innosetup script? -sed -i "s/c:\\dev\\unrealircd-5-libs/c:\\projects\\unrealircd-5-libs/gi" src\windows\unrealinst.iss +sed -i "s/c:\\dev\\unrealircd-6-libs/c:\\projects\\unrealircd-6-libs/gi" src\windows\unrealinst.iss rem Build installer file "c:\Program Files (x86)\Inno Setup 5\iscc.exe" /Q- src\windows\unrealinst.iss @@ -60,7 +69,7 @@ taskkill -im unrealircd.exe -f sleep 2 rem Just a safety measure so we don't end up testing rem some old version... -del "C:\Program Files\UnrealIRCd 5\bin\unrealircd.exe" +del "C:\Program Files\UnrealIRCd 6\bin\unrealircd.exe" echo Running installer... start /WAIT unrealircd-dev-build.exe /VERYSILENT /LOG=setup.log @@ -70,12 +79,7 @@ rem Upload artifact rem appveyor PushArtifact unrealircd-dev-build.exe rem if %ERRORLEVEL% NEQ 0 EXIT /B 1 -rem Install 'unrealircd-tests' -cd .. -rd /q/s unrealircd-tests -git clone https://github.com/unrealircd/unrealircd-tests.git -if %ERRORLEVEL% NEQ 0 EXIT /B 1 -cd unrealircd-tests +cd ..\unrealircd-tests dir rem All tests except db: diff --git a/extras/build-tests/windows/compilecmd/vs2019.bat b/extras/build-tests/windows/compilecmd/vs2019.bat index 690194c..238b808 100644 --- a/extras/build-tests/windows/compilecmd/vs2019.bat +++ b/extras/build-tests/windows/compilecmd/vs2019.bat @@ -1,21 +1,30 @@ rem Build command for Visual Studio 2019 -nmake -f makefile.windows ^ -LIBRESSL_INC_DIR="c:\projects\unrealircd-5-libs\libressl\include" ^ -LIBRESSL_LIB_DIR="c:\projects\unrealircd-5-libs\libressl\lib" ^ -SSLLIB="crypto-46.lib ssl-48.lib" ^ +rem This used to start with: +rem nmake -f makefile.windows ^ +rem But nowadays we use JOM for parallel builds: +jom /j32 -f makefile.windows ^ +LIBRESSL_INC_DIR="c:\projects\unrealircd-6-libs\libressl\include" ^ +LIBRESSL_LIB_DIR="c:\projects\unrealircd-6-libs\libressl\lib" ^ +SSLLIB="crypto-47.lib ssl-50.lib" ^ USE_REMOTEINC=1 ^ -LIBCURL_INC_DIR="c:\projects\unrealircd-5-libs\curl\include" ^ -LIBCURL_LIB_DIR="c:\projects\unrealircd-5-libs\curl\builds\libcurl-vc-x64-release-dll-ssl-dll-cares-dll-ipv6-obj-lib" ^ -CARES_LIB_DIR="c:\projects\unrealircd-5-libs\c-ares\msvc\cares\dll-release" ^ -CARES_INC_DIR="c:\projects\unrealircd-5-libs\c-ares\include" ^ +LIBCURL_INC_DIR="c:\projects\unrealircd-6-libs\curl\include" ^ +LIBCURL_LIB_DIR="c:\projects\unrealircd-6-libs\curl\builds\libcurl-vc-x64-release-dll-ssl-dll-cares-dll-ipv6-obj-lib" ^ +CARES_LIB_DIR="c:\projects\unrealircd-6-libs\c-ares\msvc\cares\dll-release" ^ +CARES_INC_DIR="c:\projects\unrealircd-6-libs\c-ares\include" ^ CARESLIB="cares.lib" ^ -PCRE2_INC_DIR="c:\projects\unrealircd-5-libs\pcre2\include" ^ -PCRE2_LIB_DIR="c:\projects\unrealircd-5-libs\pcre2\lib" ^ +PCRE2_INC_DIR="c:\projects\unrealircd-6-libs\pcre2\include" ^ +PCRE2_LIB_DIR="c:\projects\unrealircd-6-libs\pcre2\lib" ^ PCRE2LIB="pcre2-8.lib" ^ -ARGON2_LIB_DIR="c:\projects\unrealircd-5-libs\argon2\vs2015\build" ^ -ARGON2_INC_DIR="c:\projects\unrealircd-5-libs\argon2\include" ^ +ARGON2_LIB_DIR="c:\projects\unrealircd-6-libs\argon2\vs2015\build" ^ +ARGON2_INC_DIR="c:\projects\unrealircd-6-libs\argon2\include" ^ ARGON2LIB="Argon2RefDll.lib" ^ -SODIUM_LIB_DIR="c:\projects\unrealircd-5-libs\libsodium\bin\x64\Release\v142\dynamic" ^ -SODIUM_INC_DIR="c:\projects\unrealircd-5-libs\libsodium\src\libsodium\include" ^ -SODIUMLIB="libsodium.lib" %* +SODIUM_LIB_DIR="c:\projects\unrealircd-6-libs\libsodium\bin\x64\Release\v142\dynamic" ^ +SODIUM_INC_DIR="c:\projects\unrealircd-6-libs\libsodium\src\libsodium\include" ^ +SODIUMLIB="libsodium.lib" ^ +JANSSON_LIB_DIR="c:\projects\unrealircd-6-libs\jansson\lib" ^ +JANSSON_INC_DIR="c:\projects\unrealircd-6-libs\jansson\include" ^ +JANSSONLIB="jansson.lib" ^ +GEOIPCLASSIC_LIB_DIR="c:\projects\unrealircd-6-libs\GeoIP\libGeoIP" ^ +GEOIPCLASSIC_INC_DIR="c:\projects\unrealircd-6-libs\GeoIP\libGeoIP" ^ +GEOIPCLASSICLIB="GeoIP.lib" %* diff --git a/extras/c-ares.tar.gz b/extras/c-ares.tar.gz index b59d17a..bb16c09 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 6715ed9..85bce4e 100755 --- a/extras/curlinstall +++ b/extras/curlinstall @@ -4,7 +4,7 @@ OUTF="curl-latest.tar.gz" OUTD="curl-latest" ARESPATH="`pwd`/extras/c-ares" UNREALDIR="`pwd`" -CARESVERSION="1.17.1" +CARESVERSION="1.17.2" LIBDIR="$1" if [ "x$1" = "x" ]; then diff --git a/extras/doxygen/Developers.md b/extras/doxygen/Developers.md index 85be6d1..2344f7d 100644 --- a/extras/doxygen/Developers.md +++ b/extras/doxygen/Developers.md @@ -1,4 +1,4 @@ -Welcome to the doxygen-generated documentation for the UnrealIRCd 5.x API. +Welcome to the doxygen-generated documentation for the UnrealIRCd 6.x API. This is intended **for developers only!** If you are creating a 3rd party module for UnrealIRCd or are interested diff --git a/extras/doxygen/Doxyfile b/extras/doxygen/Doxyfile index 579e7ca..f9e71a9 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 = 5.2.0.1 +PROJECT_NUMBER = 6.0.1.1 # 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/geoip-classic.tar.gz b/extras/geoip-classic.tar.gz new file mode 100644 index 0000000..ded9ebd Binary files /dev/null and b/extras/geoip-classic.tar.gz differ diff --git a/extras/jansson.tar.gz b/extras/jansson.tar.gz new file mode 100644 index 0000000..bd1d16c Binary files /dev/null and b/extras/jansson.tar.gz differ diff --git a/extras/security/apparmor/unrealircd b/extras/security/apparmor/unrealircd index c9eb837..e6c9a0a 100644 --- a/extras/security/apparmor/unrealircd +++ b/extras/security/apparmor/unrealircd @@ -1,9 +1,9 @@ -# AppArmor profile for UnrealIRCd 5 +# AppArmor profile for UnrealIRCd 6 # # Note that you may still see some DENIED warnings in logs with # operation="chmod". These are harmless and can be safely ignored. # -# Tested on Ubuntu 16.04 LTS and Ubuntu 18.04 LTS +# Tested on Ubuntu 16.04 LTS, Ubuntu 18.04 LTS, Ubuntu 20.04 LTS # # IMPORTANT: you will have to modify the path to executable below # if it's not /home/ircd/unrealircd/bin/unrealircd ! diff --git a/extras/tests/tls/cipherscan_profiles/openssl-300.txt b/extras/tests/tls/cipherscan_profiles/openssl-300.txt new file mode 100644 index 0000000..76c368f --- /dev/null +++ b/extras/tests/tls/cipherscan_profiles/openssl-300.txt @@ -0,0 +1,27 @@ +Target: 127.0.0.1:5901 + +prio ciphersuite protocols pfs curves +1 ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 ECDH,P-521,521bits secp521r1,secp384r1 +2 ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 ECDH,P-521,521bits secp521r1,secp384r1 +3 ECDHE-ECDSA-AES256-SHA384 TLSv1.2 ECDH,P-521,521bits secp521r1,secp384r1 +4 ECDHE-ECDSA-AES128-SHA256 TLSv1.2 ECDH,P-521,521bits secp521r1,secp384r1 +5 ECDHE-ECDSA-AES256-SHA TLSv1.2 ECDH,P-521,521bits secp521r1,secp384r1 +6 ECDHE-ECDSA-AES128-SHA TLSv1.2 ECDH,P-521,521bits secp521r1,secp384r1 + +Certificate: untrusted, 384 bits, ecdsa-with-SHA256 signature +TLS ticket lifetime hint: None +NPN protocols: None +OCSP stapling: not supported +Cipher ordering: server +Curves ordering: server - fallback: no +Server supports secure renegotiation +Server supported compression methods: NONE +TLS Tolerance: yes + +Intolerance to: + SSL 3.254 : absent + TLS 1.0 : PRESENT + TLS 1.1 : PRESENT + TLS 1.2 : absent + TLS 1.3 : absent + TLS 1.4 : absent diff --git a/extras/tls.cnf b/extras/tls.cnf index 7a04a4a..46a9611 100644 --- a/extras/tls.cnf +++ b/extras/tls.cnf @@ -18,7 +18,7 @@ stateOrProvinceName_default = New York localityName = Locality Name (eg, city) 0.organizationName = Organization Name (eg, company) -0.organizationName_default = SuperNETs +0.organizationName_default = IRC geeks organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = IRCd diff --git a/extras/unrealircd-upgrade-script.in b/extras/unrealircd-upgrade-script.in index 6ce5743..6c3005b 100644 --- a/extras/unrealircd-upgrade-script.in +++ b/extras/unrealircd-upgrade-script.in @@ -1,11 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash # # This is stage 1 of the UnrealIRCd upgrade script # It downloads stage 2 online, verifies the integrity, and then # passes control to it to proceed with the rest of the upgrade. # -# This is a bash script, so it is less cross-platform than -# the rest of UnrealIRCd. We also mostly assume Linux here. +# This is a bash script, so it is less cross-platform than the +# rest of UnrealIRCd. We also mostly assume Linux/FreeBSD here. # BUILDDIR="@BUILDDIR@" @@ -55,13 +55,19 @@ if [ ! -d "$BUILDDIR" ]; then exit 1 fi +FETCHER="wget" if ! wget --help 1>/dev/null 2>&1; then - echo "The tool 'wget' is missing, which is used by this script." - echo "On Linux consider running 'sudo apt install wget' or 'sudo yum install wget'" - echo "and run this script again." - echo "Or, don't use this script and follow the manual upgrade procedure from" - echo "https://www.unrealircd.org/docs/Upgrading" - exit 1 + # fetch is a pain: it always returns 1 (false) even for usage info and has no --version + fetch 1>/dev/null 2>&1 + if [ "$?" -ne 1 ]; then + echo "The tool 'wget' is missing, which is used by this script." + echo "On Linux consider running 'sudo apt install wget' or 'sudo yum install wget'" + echo "and run this script again." + echo "Or, don't use this script and follow the manual upgrade procedure from" + echo "https://www.unrealircd.org/docs/Upgrading" + exit 1 + fi + FETCHER="fetch" fi # Weird way to get version, but ok. @@ -70,11 +76,16 @@ UNREALVER="`./configure --version|head -n1|awk '{ print $3 }'`" cd .. || fail "Could not cd back" # Set and export all variables with settings -export UNREALVER BUILDDIR SCRIPTDIR DOCDIR TMPDIR +export UNREALVER BUILDDIR SCRIPTDIR DOCDIR TMPDIR FETCHER # Download the install script -wget -O unrealircd-upgrade-script.stage2 "https://www.unrealircd.org/downloads/unrealircd-upgrade-script.stage2?from=$UNREALVER" || fail "Could not download online installer" -wget -O unrealircd-upgrade-script.stage2.asc "https://www.unrealircd.org/downloads/unrealircd-upgrade-script.stage2.asc" || fail "Could not download online installer signature" +if [ "$FETCHER" = "wget" ]; then + wget -O unrealircd-upgrade-script.stage2 "https://www.unrealircd.org/downloads/unrealircd-upgrade-script.stage2?from=$UNREALVER" || fail "Could not download online installer" + wget -O unrealircd-upgrade-script.stage2.asc "https://www.unrealircd.org/downloads/unrealircd-upgrade-script.stage2.asc" || fail "Could not download online installer signature" +else + fetch -o unrealircd-upgrade-script.stage2 "https://www.unrealircd.org/downloads/unrealircd-upgrade-script.stage2?from=$UNREALVER" || fail "Could not download online installer" + fetch -o unrealircd-upgrade-script.stage2.asc "https://www.unrealircd.org/downloads/unrealircd-upgrade-script.stage2.asc" || fail "Could not download online installer signature" +fi # GPG verification - if available if gpg --version 1>/dev/null 2>&1; then @@ -94,7 +105,11 @@ if gpg --version 1>/dev/null 2>&1; then fi else echo "WARNING: The GnuPG (GPG/PGP) verification tool 'gpg' is not installed." - echo "Consider running 'sudo apt install gpg' or 'yum install gnupg2'" + if [[ "$OSTYPE" == "freebsd"* ]] ; then + echo "Consider running 'sudo pkg install gnupg'" + else + echo "Consider running 'sudo apt install gpg' or 'yum install gnupg2'" + fi echo "When 'gpg' is installed then the UnrealIRCd upgrade script can" echo "verify the digital signature of the download file." warn "Unable to check download integrity" @@ -103,3 +118,6 @@ fi chmod +x unrealircd-upgrade-script.stage2 ./unrealircd-upgrade-script.stage2 $* +SAVERET="$?" +rm -f unrealircd-upgrade-script.stage2 unrealircd-upgrade-script.stage2 +exit $SAVERET diff --git a/include/channel.h b/include/channel.h index 44fe84a..f229290 100644 --- a/include/channel.h +++ b/include/channel.h @@ -26,7 +26,7 @@ #define MODEBUFLEN 200 -#define ChannelExists(n) (find_channel(n, NULL)) +#define ChannelExists(n) (find_channel(n)) /* NOTE: Timestamps will be added to MODE-commands, so never make * RESYNCMODES and MODEPARAMS higher than MAXPARA-3. DALnet servers diff --git a/include/common.h b/include/common.h index a666bb9..5a0d1d6 100644 --- a/include/common.h +++ b/include/common.h @@ -98,7 +98,6 @@ extern int myncmp(const char *, const char *, int); extern char *strtoken(char **, char *, char *); extern MODVAR int global_count, max_global_count; -extern char *myctime(time_t); #ifdef _WIN32 extern int gettimeofday(struct timeval *tp, void *tzp); #endif @@ -174,18 +173,7 @@ extern MODVAR unsigned char char_atribs[]; #define EXPAR2 extchmstr[1] #define EXPAR3 extchmstr[2] #define EXPAR4 extchmstr[3] - -#ifdef PREFIX_AQ -#define CHPFIX "(qaohv)~&@%+" -#define CHPAR1 "beI" -#else -#define CHPFIX "(ohv)@%+" -#define CHPAR1 "beIqa" -#endif /* PREFIX_AQ */ - -#define CHPAR2 "k" -#define CHPAR3 "l" -#define CHPAR4 "psmntir" +#define CHPAR1 "beI" #ifdef _WIN32 /* diff --git a/include/config.h b/include/config.h index 6becea2..9156eb7 100644 --- a/include/config.h +++ b/include/config.h @@ -98,9 +98,6 @@ */ /* #undef DEBUGMODE */ -/* Similarly, DEBUG_IOENGINE can be used to debug the I/O engine. */ -/* #undef DEBUG_IOENGINE */ - /* * Full pathnames and defaults of irc system's support files. */ @@ -126,7 +123,7 @@ * Common usage for this are: a trusted bot ran by an IRCOp, that you only * want to give "flood access" and nothing else, and other such things. */ -#define FAKELAG_CONFIGURABLE +//#undef FAKELAG_CONFIGURABLE /* The default value for class::sendq */ #define DEFAULT_SENDQ 3000000 @@ -212,10 +209,25 @@ * when there is no socket data waiting for us (no clients sending anything). * Was 2000ms in 3.2.x, 1000ms for versions below 3.4-alpha4. * 500ms in UnrealIRCd 4 (?) - * 250ms in UnrealIRCd 5. + * 250ms in UnrealIRCd 5 and UnrealIRCd 6. */ #define SOCKETLOOP_MAX_DELAY 250 +/* After how much time should we timeout downloads: + * DOWNLOAD_CONNECT_TIMEOUT: for the DNS and connect() / TLS_connect() call + * DOWNLOAD_TRANSFER_TIMEOUT: for the complete transfer (including connect) + * This can't be in the configuration file, as we need it while + * fetching the configuration file.. ;) + */ +#define DOWNLOAD_CONNECT_TIMEOUT 15 +#define DOWNLOAD_TRANSFER_TIMEOUT 45 + +/* Maximum number of HTTP redirects to follow. + * Keep this reasonably low, as this may delay booting up to + * DOWNLOAD_TRANSFER_TIMEOUT * DOWNLOAD_MAX_REDIRECTS + */ +#define DOWNLOAD_MAX_REDIRECTS 2 + /* * Max time from the nickname change that still causes KILL * automaticly to switch for the current nick of that user. (seconds) @@ -233,25 +245,34 @@ #endif /* Maximum number of ModData objects that may be attached to an object */ -/* UnrealIRCd 4.0.0 - 4.0.13: 8, 8, 4, 4 - * UnrealIRCd 4.0.14+ : 12, 8, 4, 4 - * UnrealIRCd 5.0.0 : 12, 8, 8, 4, 4, 500, 500 +/* UnrealIRCd 4.0.0 - 4.0.13: 8, 8, 4, 4 + * UnrealIRCd 4.0.14+ : 12, 8, 4, 4 + * UnrealIRCd 5.0.0 : 12, 8, 8, 4, 4, 500, 500 + * UnrealIRCd 6.0.0 : 24, 12, 8, 4, 4, 500, 500 */ -#define MODDATA_MAX_CLIENT 12 -#define MODDATA_MAX_LOCAL_CLIENT 8 +#define MODDATA_MAX_CLIENT 24 +#define MODDATA_MAX_LOCAL_CLIENT 12 #define MODDATA_MAX_CHANNEL 8 #define MODDATA_MAX_MEMBER 4 #define MODDATA_MAX_MEMBERSHIP 4 #define MODDATA_MAX_LOCAL_VARIABLE 500 #define MODDATA_MAX_GLOBAL_VARIABLE 500 +/** Size of the member modes buffer, so can be max this-1 modes + * assigned to an individual user (and thus max prefixes as well). + * The default is 8, so 7 max modes, and is a bit tight. + * It allows for vhoaq (5) and then 2 additional ones from 3rd + * party modules. + */ +#define MEMBERMODESLEN 8 + /* If EXPERIMENTAL is #define'd then all users will receive a notice about * this when they connect, along with a pointer to bugs.unrealircd.org where * they can report any problems. This is mainly to help UnrealIRCd development. */ #undef EXPERIMENTAL -/* Default SSL/TLS cipherlist (except for TLS1.3, see further down). +/* Default TLS cipherlist (except for TLS1.3, see further down). * This can be changed via set::ssl::options::ciphers in the config file. */ #define UNREALIRCD_DEFAULT_CIPHERS "TLS13-CHACHA20-POLY1305-SHA256 TLS13-AES-256-GCM-SHA384 TLS13-AES-128-GCM-SHA256 EECDH+CHACHA20 EECDH+AESGCM EECDH+AES AES256-GCM-SHA384 AES128-GCM-SHA256 AES256-SHA256 AES128-SHA256 AES256-SHA AES128-SHA" @@ -261,7 +282,7 @@ */ #define UNREALIRCD_DEFAULT_CIPHERSUITES "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256" -/* Default SSL/TLS curves for ECDH(E) +/* Default TLS curves for ECDH(E) * This can be changed via set::ssl::options::ecdh-curve in the config file. * NOTE: This requires openssl 1.0.2 or newer, otherwise these defaults * are not applied, due to the missing openssl API call. @@ -280,10 +301,8 @@ #define IRCD_PIDFILE PIDFILE #ifdef DEBUGMODE - #define Debug(x) debug x #define LOGFILE LPATH #else - #define Debug(x) ; #define LOGFILE "/dev/null" #endif diff --git a/include/dbuf.h b/include/dbuf.h index fe1e88e..522b6c4 100644 --- a/include/dbuf.h +++ b/include/dbuf.h @@ -70,7 +70,7 @@ typedef struct dbufbuf { ** memory as needed. Bytes are copied into internal buffers ** from users buffer. */ -void dbuf_put(dbuf *, char *, size_t); +void dbuf_put(dbuf *, const char *, size_t); /* Dynamic buffer header */ /* Pointer to data to be stored */ /* Number of bytes to store */ diff --git a/include/dynconf.h b/include/dynconf.h index dbf4c4d..978f444 100644 --- a/include/dynconf.h +++ b/include/dynconf.h @@ -32,26 +32,12 @@ struct FloodSettings { long period[MAXFLOODOPTIONS]; }; -typedef struct NetworkConfiguration NetworkConfiguration; -struct NetworkConfiguration { - unsigned x_inah:1; - char *x_ircnetwork; - char *x_ircnet005; - char *x_defserv; - char *x_services_name; - char *x_hidden_host; - char *x_prefix_quit; - char *x_helpchan; - char *x_stats_server; - char *x_sasl_server; -}; - enum UHAllowed { UHALLOW_ALWAYS, UHALLOW_NOCHANS, UHALLOW_REJOIN, UHALLOW_NEVER }; struct ChMode { - long mode; + long mode; long extmodes; - char *extparams[EXTCMODETABLESZ]; + char *extparams[256]; }; typedef struct OperStat { @@ -70,10 +56,9 @@ typedef enum HideIdleTimePolicy { HIDE_IDLE_TIME_NEVER=1, HIDE_IDLE_TIME_ALWAYS= /** The set { } block configuration */ typedef struct Configuration Configuration; struct Configuration { - unsigned som:1; + unsigned show_opermotd:1; unsigned hide_ulines:1; unsigned flat_map:1; - unsigned allow_chatops:1; unsigned ident_check:1; unsigned fail_oper_warn:1; unsigned show_connect_info:1; @@ -86,8 +71,6 @@ struct Configuration { unsigned allow_part_if_shunned:1; unsigned disable_cap:1; unsigned check_target_nick_bans:1; - unsigned use_egd : 1; - char *dns_bindip; char *link_bindip; long throttle_period; char throttle_count; @@ -100,11 +83,10 @@ struct Configuration { char *oper_auto_join_chans; char *allow_user_stats; OperStat *allow_user_stats_ext; - int ping_warning; - int maxchannelsperuser; - int maxdccallow; - int anti_spam_quit_message_time; - char *egd_path; + int ping_warning; + int maxchannelsperuser; + int maxdccallow; + int anti_spam_quit_message_time; char *static_quit; char *static_part; TLSOptions *tls_options; @@ -122,12 +104,14 @@ struct Configuration { char *restrict_usermodes; char *restrict_channelmodes; char *restrict_extendedbans; + int named_extended_bans; char *channel_command_prefix; long handshake_data_flood_amount; long handshake_data_flood_ban_time; int handshake_data_flood_ban_action; struct ChMode modes_on_join; - int level_on_join; + int modes_on_join_set; + char *level_on_join; FloodSettings *floodsettings; int ident_connect_timeout; int ident_read_timeout; @@ -148,7 +132,6 @@ struct Configuration { int maxbanlength; int watch_away_notification; int uhnames; - NetworkConfiguration network; unsigned short default_ipv6_clone_mask; int ping_cookie; int min_nick_length; @@ -176,6 +159,17 @@ struct Configuration { BroadcastChannelMessagesOption broadcast_channel_messages; AllowedChannelChars allowed_channelchars; HideIdleTimePolicy hide_idle_time; + unsigned inah:1; + char *network_name; + char *network_name_005; + char *default_server; + char *services_name; + char *cloak_prefix; + char *prefix_quit; + char *helpchan; + char *stats_server; + char *sasl_server; + int server_notice_colors; }; extern MODVAR Configuration iConf; @@ -187,7 +181,7 @@ extern MODVAR int ipv6_disabled; #define CONN_MODES iConf.conn_modes #define OPER_MODES iConf.oper_modes #define OPER_SNOMASK iConf.oper_snomask -#define SHOWOPERMOTD iConf.som +#define SHOWOPERMOTD iConf.show_opermotd #define HIDE_ULINES iConf.hide_ulines #define FLAT_MAP iConf.flat_map #define ALLOW_CHATOPS iConf.allow_chatops @@ -197,7 +191,6 @@ extern MODVAR int ipv6_disabled; #define DONT_RESOLVE iConf.dont_resolve #define AUTO_JOIN_CHANS iConf.auto_join_chans #define OPER_AUTO_JOIN_CHANS iConf.oper_auto_join_chans -#define DNS_BINDIP iConf.dns_bindip #define LINK_BINDIP iConf.link_bindip #define IDENT_CHECK iConf.ident_check #define FAILOPER_WARN iConf.fail_oper_warn @@ -205,23 +198,17 @@ extern MODVAR int ipv6_disabled; #define NOCONNECTTLSLINFO iConf.no_connect_tls_info #define ALLOW_USER_STATS iConf.allow_user_stats #define ANTI_SPAM_QUIT_MSG_TIME iConf.anti_spam_quit_message_time -#ifdef HAVE_RAND_EGD -#define USE_EGD iConf.use_egd -#else -#define USE_EGD 0 -#endif -#define EGD_PATH iConf.egd_path -#define ircnetwork iConf.network.x_ircnetwork -#define ircnet005 iConf.network.x_ircnet005 -#define defserv iConf.network.x_defserv -#define SERVICES_NAME iConf.network.x_services_name -#define hidden_host iConf.network.x_hidden_host -#define helpchan iConf.network.x_helpchan -#define STATS_SERVER iConf.network.x_stats_server -#define SASL_SERVER iConf.network.x_sasl_server -#define iNAH iConf.network.x_inah -#define PREFIX_QUIT iConf.network.x_prefix_quit +#define NETWORK_NAME iConf.network_name +#define NETWORK_NAME_005 iConf.network_name_005 +#define DEFAULT_SERVER iConf.default_server +#define SERVICES_NAME iConf.services_name +#define CLOAK_PREFIX iConf.cloak_prefix +#define HELP_CHANNEL iConf.helpchan +#define STATS_SERVER iConf.stats_server +#define SASL_SERVER iConf.sasl_server +#define iNAH iConf.inah +#define PREFIX_QUIT iConf.prefix_quit #define STATIC_QUIT iConf.static_quit #define STATIC_PART iConf.static_part @@ -232,7 +219,7 @@ extern MODVAR int ipv6_disabled; #define THROTTLING_PERIOD iConf.throttle_period #define THROTTLING_COUNT iConf.throttle_count #define USE_BAN_VERSION iConf.use_ban_version -#define MODES_ON_JOIN iConf.modes_on_join.mode +#define MODES_ON_JOIN iConf.modes_on_join.extmodes #define LEVEL_ON_JOIN iConf.level_on_join #define IDENT_CONNECT_TIMEOUT iConf.ident_connect_timeout @@ -309,7 +296,6 @@ struct SetCheck { unsigned has_maxchannelsperuser:1; unsigned has_maxdccallow:1; unsigned has_anti_spam_quit_message_time:1; - unsigned has_egd_path:1; unsigned has_static_quit:1; unsigned has_static_part:1; unsigned has_allow_userhost_change:1; diff --git a/include/fdlist.h b/include/fdlist.h index 6bb0318..bb24f4f 100644 --- a/include/fdlist.h +++ b/include/fdlist.h @@ -7,6 +7,8 @@ typedef void (*IOCallbackFunc)(int fd, int revents, void *data); +typedef enum FDCloseMethod { FDCLOSE_SOCKET=0, FDCLOSE_FILE=1, FDCLOSE_NONE=3 } FDCloseMethod; + typedef struct fd_entry { int fd; char desc[FD_DESC_SZ]; @@ -15,14 +17,14 @@ typedef struct fd_entry { void *data; time_t deadline; unsigned char is_open; + FDCloseMethod close_method; unsigned int backend_flags; } FDEntry; extern MODVAR FDEntry fd_table[MAXCONNECTIONS + 1]; -extern int fd_open(int fd, const char *desc); -extern void fd_close(int fd); -extern int fd_unmap(int fd); +extern int fd_open(int fd, const char *desc, FDCloseMethod close_method); +extern int fd_close(int fd); extern void fd_unnotify(int fd); extern int fd_socket(int family, int type, int protocol, const char *desc); extern int fd_accept(int sockfd); diff --git a/include/h.h b/include/h.h index f1aa4fc..6b380f5 100644 --- a/include/h.h +++ b/include/h.h @@ -30,10 +30,6 @@ extern MODVAR char *extraflags; extern MODVAR int tainted; -/* for the new s_err.c */ -extern char *getreply(int); -#define rpl_str(x) getreply(x) -#define err_str(x) getreply(x) extern MODVAR Member *freemember; extern MODVAR Membership *freemembership; extern MODVAR Client me; @@ -52,7 +48,7 @@ extern MODVAR char umodestring[UMODETABLESZ+1]; #define get_recvq(x) ((x)->local->class->recvq ? (x)->local->class->recvq : DEFAULT_RECVQ) /* Configuration preprocessor */ -extern PreprocessorItem parse_preprocessor_item(char *start, char *end, char *filename, int linenumber, ConditionalConfig **cc); +extern PreprocessorItem parse_preprocessor_item(char *start, char *end, const char *filename, int linenumber, ConditionalConfig **cc); extern void preprocessor_cc_duplicate_list(ConditionalConfig *r, ConditionalConfig **out); extern void preprocessor_cc_free_level(ConditionalConfig **cc_list, int level); extern void preprocessor_cc_free_list(ConditionalConfig *cc); @@ -76,7 +72,6 @@ extern MODVAR ConfigItem_tld *conf_tld; extern MODVAR ConfigItem_oper *conf_oper; extern MODVAR ConfigItem_listen *conf_listen; extern MODVAR ConfigItem_allow *conf_allow; -extern MODVAR ConfigItem_except *conf_except; extern MODVAR ConfigItem_vhost *conf_vhost; extern MODVAR ConfigItem_link *conf_link; extern MODVAR ConfigItem_sni *conf_sni; @@ -85,9 +80,7 @@ extern MODVAR ConfigItem_deny_channel *conf_deny_channel; extern MODVAR ConfigItem_deny_link *conf_deny_link; extern MODVAR ConfigItem_allow_channel *conf_allow_channel; extern MODVAR ConfigItem_deny_version *conf_deny_version; -extern MODVAR ConfigItem_log *conf_log; extern MODVAR ConfigItem_alias *conf_alias; -extern MODVAR ConfigItem_include *conf_include; extern MODVAR ConfigItem_help *conf_help; extern MODVAR ConfigItem_offchans *conf_offchans; extern MODVAR SecurityGroup *securitygroups; @@ -97,10 +90,13 @@ extern EVENT(e_unload_module_delayed); extern EVENT(throttling_check_expire); extern void module_loadall(void); -extern long set_usermode(char *umode); -extern char *get_usermode_string_raw(long umodes); -extern ConfigFile *config_parse(char *filename, char *confdata); -extern ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned int line_offset); +extern long set_usermode(const char *umode); +extern const char *get_usermode_string(Client *acptr); +extern const char *get_usermode_string_r(Client *client, char *buf, size_t buflen); +extern const char *get_usermode_string_raw(long umodes); +extern const char *get_usermode_string_raw_r(long umodes, char *buf, size_t buflen); +extern ConfigFile *config_parse(const char *filename, char *confdata); +extern ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset); extern void config_error(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2))); extern void config_warn(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2))); extern void config_error_missing(const char *filename, int line, const char *entry); @@ -115,27 +111,25 @@ extern int config_is_blankorempty(ConfigEntry *cep, const char *block); extern MODVAR int config_verbose; extern void config_entry_free(ConfigEntry *ce); extern void config_entry_free_all(ConfigEntry *ce); -extern ConfigFile *config_load(char *filename, char *displayname); +extern ConfigFile *config_load(const char *filename, const char *displayname); extern void config_free(ConfigFile *cfptr); -extern void ipport_seperate(char *string, char **ip, char **port); -extern ConfigItem_class *find_class(char *name); -extern ConfigItem_deny_dcc *find_deny_dcc(char *name); -extern ConfigItem_oper *find_oper(char *name); -extern ConfigItem_operclass *find_operclass(char *name); -extern ConfigItem_listen *find_listen(char *ipmask, int port, int ipv6); -extern ConfigItem_sni *find_sni(char *name); -extern ConfigItem_ulines *find_uline(char *host); -extern ConfigItem_except *find_except(Client *, short type); +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_sni *find_sni(const char *name); +extern ConfigItem_ulines *find_uline(const char *host); extern ConfigItem_tld *find_tld(Client *cptr); -extern ConfigItem_link *find_link(char *servername, Client *acptr); -extern ConfigItem_ban *find_ban(Client *, char *host, short type); -extern ConfigItem_ban *find_banEx(Client *,char *host, short type, short type2); -extern ConfigItem_vhost *find_vhost(char *name); -extern ConfigItem_deny_channel *find_channel_allowed(Client *cptr, char *name); -extern ConfigItem_alias *find_alias(char *name); -extern ConfigItem_help *find_Help(char *command); +extern ConfigItem_link *find_link(const char *servername, Client *acptr); +extern ConfigItem_ban *find_ban(Client *, const char *host, short type); +extern ConfigItem_ban *find_banEx(Client *,const char *host, short type, short type2); +extern ConfigItem_vhost *find_vhost(const char *name); +extern ConfigItem_deny_channel *find_channel_allowed(Client *cptr, const char *name); +extern ConfigItem_alias *find_alias(const char *name); +extern ConfigItem_help *find_Help(const char *command); -extern OperPermission ValidatePermissionsForPath(char *path, Client *client, Client *victim, Channel *channel, void *extra); +extern OperPermission ValidatePermissionsForPath(const char *path, Client *client, Client *victim, Channel *channel, const void *extra); extern void OperClassValidatorDel(OperClassValidator *validator); extern ConfigItem_ban *find_ban_ip(Client *client); @@ -144,8 +138,8 @@ extern void append_ListItem(ListStruct *item, ListStruct **list); extern void add_ListItemPrio(ListStructPrio *, ListStructPrio **, int); extern void del_ListItem(ListStruct *, ListStruct **); extern MODVAR LoopStruct loop; -extern int del_banid(Channel *channel, char *banid); -extern int del_exbanid(Channel *channel, char *banid); +extern int del_banid(Channel *channel, const char *banid); +extern int del_exbanid(Channel *channel, const char *banid); #define REPORT_DO_DNS "NOTICE * :*** Looking up your hostname...\r\n" #define REPORT_FIN_DNS "NOTICE * :*** Found your hostname\r\n" #define REPORT_FIN_DNSC "NOTICE * :*** Found your hostname (cached)\r\n" @@ -161,48 +155,66 @@ extern MODVAR struct list_head oper_list; extern MODVAR struct list_head unknown_list; extern MODVAR struct list_head global_server_list; extern MODVAR struct list_head dead_list; -extern RealCommand *find_command(char *cmd, int flags); -extern RealCommand *find_command_simple(char *cmd); +extern RealCommand *find_command(const char *cmd, int flags); +extern RealCommand *find_command_simple(const char *cmd); extern Membership *find_membership_link(Membership *lp, Channel *ptr); extern Member *find_member_link(Member *, Client *); -extern int remove_user_from_channel(Client *, Channel *); +extern int remove_user_from_channel(Client *client, Channel *channel, int dont_log); extern void add_server_to_table(Client *); extern void remove_server_from_table(Client *); -extern void iNAH_host(Client *client, char *host); -extern void set_snomask(Client *client, char *snomask); -extern char *get_snomask_string(Client *client); +extern void iNAH_host(Client *client, const char *host); +extern void set_snomask(Client *client, const char *snomask); extern int check_tkls(Client *cptr); /* for services */ extern void send_user_joins(Client *, Client *); extern int valid_channelname(const char *); -extern int valid_server_name(char *name); -extern long get_access(Client *, Channel *); -extern int ban_check_mask(Client *, Channel *, char *, int, char **, char **, int); -extern int extban_is_ok_nuh_extban(Client *, Channel *, char *, int, int, int); -extern char *extban_conv_param_nuh_or_extban(char *); -extern char *extban_conv_param_nuh(char *); -extern Ban *is_banned(Client *, Channel *, int, char **, char **); -extern Ban *is_banned_with_nick(Client *, Channel *, int, char *, char **, char **); +extern int valid_server_name(const char *name); +extern Cmode *find_channel_mode_handler(char letter); +extern int valid_channel_access_mode_letter(char letter); +extern int check_channel_access(Client *client, Channel *channel, const char *modes); +extern int check_channel_access_membership(Membership *mb, const char *modes); +extern int check_channel_access_member(Member *mb, const char *modes); +extern int check_channel_access_string(const char *current_modes, const char *modes); +extern int check_channel_access_letter(const char *current_modes, const char letter); +extern const char *get_channel_access(Client *client, Channel *channel); +extern void add_member_mode_fast(Member *mb, Membership *mbs, char letter); +extern void del_member_mode_fast(Member *mb, Membership *mbs, char letter); +extern void add_member_mode(Client *client, Channel *channel, char letter); +extern void del_member_mode(Client *client, Channel *channel, char letter); +extern char sjoin_prefix_to_mode(char s); +extern char mode_to_sjoin_prefix(char s); +extern char mode_to_prefix(char s); +extern char prefix_to_mode(char s); +extern const char *modes_to_prefix(const char *modes); +extern const char *modes_to_sjoin_prefix(const char *modes); +extern char rank_to_mode(int rank); +extern int mode_to_rank(char mode); +extern char lowest_ranking_mode(const char *modes); +extern char lowest_ranking_prefix(const char *prefix); +extern void channel_member_modes_generate_equal_or_greater(const char *modes, char *buf, size_t buflen); +extern int ban_check_mask(BanContext *b); +extern int extban_is_ok_nuh_extban(BanContext *b); +extern const char *extban_conv_param_nuh_or_extban(BanContext *b, Extban *extban); +extern const char *extban_conv_param_nuh(BanContext *b, Extban *extban); +extern Ban *is_banned(Client *, Channel *, int, const char **, const char **); +extern Ban *is_banned_with_nick(Client *, Channel *, int, const char *, const char **, const char **); -extern void ircd_log(int, FORMAT_STRING(const char *), ...) __attribute__((format(printf,2,3))); -extern Client *find_client(char *, Client *); -extern Client *find_name(char *, Client *); -extern Client *find_nickserv(char *, Client *); -extern Client *find_person(char *, Client *); -extern Client *find_server(char *, Client *); -extern Client *find_service(char *, Client *); +extern Client *find_client(const char *, Client *); +extern Client *find_name(const char *, Client *); +extern Client *find_nickserv(const char *, Client *); +extern Client *find_user(const char *, Client *); +extern Client *find_server(const char *, Client *); +extern Client *find_service(const char *, Client *); #define find_server_quick(x) find_server(x, NULL) extern char *find_or_add(char *); extern void inittoken(); extern void reset_help(); extern MODVAR char *debugmode, *configfile, *sbrk0; -extern char *getfield(char *); -extern void set_sockhost(Client *, char *); +extern void set_sockhost(Client *, const char *); #ifdef _WIN32 -extern MODFUNC char *sock_strerror(int); +extern const char *sock_strerror(int); #endif -extern int dgets(int, char *, int); #ifdef _WIN32 extern MODVAR int debuglevel; @@ -213,86 +225,111 @@ extern MODVAR int OpenFiles; /* number of files currently open */ extern MODVAR int debuglevel, portnum, debugtty, maxusersperchannel; extern MODVAR int readcalls, udpfd, resfd; extern Client *add_connection(ConfigItem_listen *, int); -extern void add_local_domain(char *, int); extern int check_server_init(Client *); extern void close_connection(Client *); extern void close_unbound_listeners(); -extern int connect_server(ConfigItem_link *, Client *, struct hostent *); -extern void get_my_name(Client *, char *, int); extern int get_sockerr(Client *); extern int inetport(ConfigItem_listen *, char *, int, int); extern void init_sys(); extern void check_user_limit(void); extern void init_modef(); -extern int verify_hostname(char *name); +extern int verify_hostname(const char *name); -extern void report_error(char *, Client *); extern int setup_ping(); extern void set_channel_mlock(Client *, Channel *, const char *, int); -extern void restart(char *); -extern void server_reboot(char *); +extern void restart(const char *); +extern void server_reboot(const char *); extern void terminate(), write_pidfile(); extern void *safe_alloc(size_t size); extern void set_socket_buffers(int fd, int rcvbuf, int sndbuf); extern int send_queued(Client *); extern void send_queued_cb(int fd, int revents, void *data); -extern void sendto_connectnotice(Client *client, int disconnect, char *comment); -extern void sendto_serv_butone_nickcmd(Client *one, Client *client, char *umodes); -extern void sendto_message_one(Client *to, Client *from, char *sender, - char *cmd, char *nick, char *msg); -#define PREFIX_ALL 0 -#define PREFIX_HALFOP 0x1 -#define PREFIX_VOICE 0x2 -#define PREFIX_OP 0x4 -#define PREFIX_ADMIN 0x08 -#define PREFIX_OWNER 0x10 +extern void sendto_serv_butone_nickcmd(Client *one, MessageTag *mtags, Client *client, const char *umodes); +extern void sendto_message_one(Client *to, Client *from, const char *sender, const char *cmd, const char *nick, const char *msg); extern void sendto_channel(Channel *channel, Client *from, Client *skip, - int prefix, long clicap, int sendflags, + char *member_modes, long clicap, int sendflags, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,8,9))); extern void sendto_local_common_channels(Client *user, Client *skip, long clicap, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,5,6))); extern void sendto_match_servs(Channel *, Client *, FORMAT_STRING(const char *), ...) __attribute__((format(printf,3,4))); -extern void sendto_match_butone(Client *, Client *, char *, int, MessageTag *, +extern void sendto_match_butone(Client *, Client *, const char *, int, MessageTag *, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,6,7))); extern void sendto_all_butone(Client *, Client *, FORMAT_STRING(const char *), ...) __attribute__((format(printf,3,4))); extern void sendto_ops(FORMAT_STRING(const char *), ...) __attribute__((format(printf,1,2))); -extern void sendto_ops_butone(Client *, Client *, FORMAT_STRING(const char *), ...) __attribute__((format(printf,3,4))); extern void sendto_prefix_one(Client *, Client *, MessageTag *, FORMAT_STRING(const char *), ...) __attribute__((format(printf,4,5))); +extern void vsendto_prefix_one(Client *to, Client *from, MessageTag *mtags, const char *pattern, va_list vl); extern void sendto_opers(FORMAT_STRING(const char *), ...) __attribute__((format(printf,1,2))); extern void sendto_umode(int, FORMAT_STRING(const char *), ...) __attribute__((format(printf,2,3))); extern void sendto_umode_global(int, FORMAT_STRING(const char *), ...) __attribute__((format(printf,2,3))); -extern void sendto_snomask(int snomask, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,2,3))); -extern void sendto_snomask_global(int snomask, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,2,3))); extern void sendnotice(Client *to, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,2,3))); -extern void sendnumeric(Client *to, int numeric, ...); +/** Send numeric message to a client. + * @param to The recipient + * @param numeric The numeric, one of RPL_* or ERR_*, see include/numeric.h + * @param ... The parameters for the numeric + * @note Be sure to provide the correct number and type of parameters that belong to the numeric. Check include/numeric.h when in doubt! + * @section sendnumeric_examples Examples + * @subsection sendnumeric_permission_denied Send "Permission Denied" numeric + * This numeric has no parameter, so is simple: + * @code + * sendnumeric(client, ERR_NOPRIVILEGES); + * @endcode + * @subsection sendnumeric_notenoughparameters Send "Not enough parameters" numeric + * This numeric requires 1 parameter: the name of the command. + * @code + * sendnumeric(client, ERR_NEEDMOREPARAMS, "SOMECOMMAND"); + * @endcode + * @ingroup SendFunctions + */ +#define sendnumeric(to, numeric, ...) sendnumericfmt(to, numeric, STR_ ## numeric, ##__VA_ARGS__) extern void sendnumericfmt(Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,3,4))); +extern void sendtxtnumeric(Client *to, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,2,3))); +/** Build numeric message so it is ready to be sent to a client - rarely used, normally you use sendnumeric() instead. + * This function is normally only used in eg CAN_KICK and CAN_SET_TOPIC, where + * you need to set an 'errbuf' with a full IRC protocol line to reject the request + * (which then may or may not be sent depending on operoverride privileges). + * @param buf The buffer where the message should be stored to (full IRC protocol line) + * @param buflen The size of the buffer + * @param to The recipient + * @param numeric The numeric, one of RPL_* or ERR_*, see include/numeric.h + * @param ... The parameters for the numeric + * @note Be sure to provide the correct number and type of parameters that belong to the numeric. Check include/numeric.h when in doubt! + * @ingroup SendFunctions + */ +#define buildnumeric(buf, buflen, to, numeric, ...) buildnumericfmt(buf, buflen, to, numeric, STR_ ## numeric, ##__VA_ARGS__) +extern void buildnumericfmt(char *buf, size_t buflen, Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,5,6))); extern void sendto_server(Client *one, unsigned long caps, unsigned long nocaps, MessageTag *mtags, FORMAT_STRING(const char *format), ...) __attribute__((format(printf, 5, 6))); -extern void sendto_ops_and_log(FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,1,2))); - +extern void send_raw_direct(Client *user, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf, 2, 3))); extern MODVAR int writecalls, writeb[]; extern int deliver_it(Client *cptr, char *str, int len, int *want_read); extern int target_limit_exceeded(Client *client, void *target, const char *name); -extern char *canonize(char *buffer); +extern char *canonize(const char *buffer); extern int check_registered(Client *); extern int check_registered_user(Client *); -extern char *get_client_name(Client *, int); -extern char *get_client_host(Client *); -extern char *myctime(time_t); -extern char *short_date(time_t, char *buf); -extern char *long_date(time_t); -extern void exit_client(Client *client, MessageTag *recv_mtags, char *comment); -extern void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char *comment); -extern void initstats(), tstats(Client *, char *); -extern char *check_string(char *); -extern char *make_nick_user_host(char *, char *, char *); -extern char *make_nick_user_host_r(char *namebuf, char *nick, char *name, char *host); -extern char *make_user_host(char *, char *); +extern const char *get_client_name(Client *, int); +extern const char *get_client_host(Client *); +extern const char *myctime(time_t); +extern const char *short_date(time_t, char *buf); +extern const char *long_date(time_t); +extern const char *pretty_time_val(long); +extern const char *pretty_time_val_r(char *buf, size_t buflen, long timeval); +extern const char *pretty_date(time_t t); +extern time_t server_time_to_unix_time(const char *tbuf); +extern time_t rfc2616_time_to_unix_time(const char *tbuf); +extern const char *rfc2616_time(time_t clock); +extern void exit_client(Client *client, MessageTag *recv_mtags, const char *comment); +extern void exit_client_fmt(Client *client, MessageTag *recv_mtags, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf, 3, 4))); +extern void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, const char *comment); +extern void initstats(); +extern const char *check_string(const char *); +extern char *make_nick_user_host(const char *, const char *, const char *); +extern char *make_nick_user_host_r(char *namebuf, size_t namebuflen, const char *nick, const char *name, const char *host); +extern char *make_user_host(const char *, const char *); extern void parse(Client *cptr, char *buffer, int length); -extern int hunt_server(Client *, MessageTag *, char *, int, int, char **); +extern int hunt_server(Client *, MessageTag *, const char *, int, int, const char **); extern int cmd_server_estab(Client *); extern void umode_init(void); #define UMODE_GLOBAL 1 @@ -309,79 +346,68 @@ extern void free_client(Client *); extern void free_link(Link *); extern void free_ban(Ban *); extern void free_user(Client *); -extern int find_str_match_link(Link *, char *); +extern int link_list_length(Link *lp); +extern int find_str_match_link(Link *, const char *); extern void free_str_list(Link *); extern Link *make_link(); extern Ban *make_ban(); extern User *make_user(Client *); extern Server *make_server(); extern Client *make_client(Client *, Client *); +extern Channel *make_channel(const char *name); extern Member *find_channel_link(Member *, Channel *); -extern char *pretty_mask(char *); +extern char *pretty_mask(const char *); extern void add_client_to_list(Client *); extern void remove_client_from_list(Client *); -extern void initlists(); -extern struct hostent *get_res(char *); -extern struct hostent *gethost_byaddr(char *, Link *); -extern struct hostent *gethost_byname(char *, Link *); +extern void initlists(void); +extern void initlist_channels(void); +extern struct hostent *get_res(const char *); +extern struct hostent *gethost_byaddr(const char *, Link *); +extern struct hostent *gethost_byname(const char *, Link *); extern void flush_cache(); extern void init_resolver(int firsttime); extern time_t timeout_query_list(time_t); extern time_t expire_cache(time_t); -extern void del_queries(char *); +extern void del_queries(const char *); /* Hash stuff */ #define NICK_HASH_TABLE_SIZE 32768 #define CHAN_HASH_TABLE_SIZE 32768 -#define WATCH_HASH_TABLE_SIZE 32768 #define WHOWAS_HASH_TABLE_SIZE 32768 #define THROTTLING_HASH_TABLE_SIZE 8192 -#define hash_find_channel find_channel extern uint64_t siphash(const char *in, const char *k); extern uint64_t siphash_raw(const char *in, size_t len, const char *k); extern uint64_t siphash_nocase(const char *in, const char *k); extern void siphash_generate_key(char *k); extern void init_hash(void); uint64_t hash_whowas_name(const char *name); -extern int add_to_client_hash_table(char *, Client *); -extern int del_from_client_hash_table(char *, Client *); -extern int add_to_id_hash_table(char *, Client *); -extern int del_from_id_hash_table(char *, Client *); -extern int add_to_channel_hash_table(char *, Channel *); -extern void del_from_channel_hash_table(char *, Channel *); -extern int add_to_watch_hash_table(char *, Client *, int); -extern int del_from_watch_hash_table(char *, Client *); -extern int hash_check_watch(Client *, int); -extern int hash_del_watch_list(Client *); -extern void count_watch_memory(int *, u_long *); -extern Watch *hash_get_watch(char *); +extern int add_to_client_hash_table(const char *, Client *); +extern int del_from_client_hash_table(const char *, Client *); +extern int add_to_id_hash_table(const char *, Client *); +extern int del_from_id_hash_table(const char *, Client *); +extern int add_to_channel_hash_table(const char *, Channel *); +extern void del_from_channel_hash_table(const char *, Channel *); extern Channel *hash_get_chan_bucket(uint64_t); extern Client *hash_find_client(const char *, Client *); extern Client *hash_find_id(const char *, Client *); extern Client *hash_find_nickatserver(const char *, Client *); -extern Channel *find_channel(char *name, Channel *channel); +extern Channel *find_channel(const char *name); extern Client *hash_find_server(const char *, Client *); extern struct MODVAR ThrottlingBucket *ThrottlingHash[THROTTLING_HASH_TABLE_SIZE]; -extern char *find_by_aln(char *); -extern char *convert2aln(int); -extern int convertfromaln(char *); -extern char *find_server_aln(char *); -extern time_t atime(char *xtime); /* Mode externs */ -extern MODVAR long UMODE_INVISIBLE; /* 0x0001 makes user invisible */ -extern MODVAR long UMODE_OPER; /* 0x0002 Operator */ -extern MODVAR long UMODE_WALLOP; /* 0x0004 send wallops to them */ -extern MODVAR long UMODE_REGNICK; /* 0x0020 Nick set by services as registered */ -extern MODVAR long UMODE_SERVNOTICE;/* 0x0100 server notices such as kill */ -extern MODVAR long UMODE_HIDE; /* 0x8000 Hide from Nukes */ -extern MODVAR long UMODE_SECURE; /* 0x800000 User is a secure connect */ -extern MODVAR long UMODE_DEAF; /* 0x10000000 Deaf */ -extern MODVAR long UMODE_HIDEOPER; /* 0x20000000 Hide oper mode */ -extern MODVAR long UMODE_SETHOST; /* 0x40000000 used sethost */ +extern MODVAR long UMODE_INVISIBLE; /* makes user invisible */ +extern MODVAR long UMODE_OPER; /* Operator */ +extern MODVAR long UMODE_REGNICK; /* Nick set by services as registered */ +extern MODVAR long UMODE_SERVNOTICE;/* server notices such as kill */ +extern MODVAR long UMODE_HIDE; /* Hide from Nukes */ +extern MODVAR long UMODE_SECURE; /* User is a secure connect */ +extern MODVAR long UMODE_DEAF; /* Deaf */ +extern MODVAR long UMODE_HIDEOPER; /* Hide oper mode */ +extern MODVAR long UMODE_SETHOST; /* used sethost */ extern MODVAR long UMODE_HIDLE; /* hides oper idle times */ extern MODVAR long AllUmodes, SendUmodes; @@ -403,24 +429,32 @@ extern MODVAR long SNO_OPER; #ifndef HAVE_STRLCPY extern size_t strlcpy(char *dst, const char *src, size_t size); #endif +#ifndef HAVE_STRLNCPY +extern size_t strlncpy(char *dst, const char *src, size_t size, size_t n); +#endif #ifndef HAVE_STRLCAT extern size_t strlcat(char *dst, const char *src, size_t size); #endif #ifndef HAVE_STRLNCAT extern size_t strlncat(char *dst, const char *src, size_t size, size_t n); #endif +extern void strlcat_letter(char *buf, char c, size_t buflen); extern char *strldup(const char *src, size_t n); extern void dopacket(Client *, char *, int); extern void debug(int, FORMAT_STRING(const char *), ...) __attribute__((format(printf,2,3))); #if defined(DEBUGMODE) -extern void send_usage(Client *, char *); -extern void count_memory(Client *, char *); -extern int checkprotoflags(Client *, int, char *, int); +extern void send_usage(Client *, const char *); +extern void count_memory(Client *, const char *); +extern int checkprotoflags(Client *, int, const char *, int); #endif -extern char *inetntop(int af, const void *in, char *local_dummy, size_t the_size); +extern const char *inetntop(int af, const void *in, char *local_dummy, size_t the_size); + +extern void delletterfromstring(char *s, char letter); +extern void addlettertodynamicstringsorted(char **str, char letter); +extern int sort_character_lowercase_before_uppercase(char x, char y); /* Internal command stuff - not for modules */ extern MODVAR RealCommand *CommandHash[256]; @@ -442,25 +476,19 @@ extern void close_connections(void); extern int b64_encode(unsigned char const *src, size_t srclength, char *target, size_t targsize); extern int b64_decode(char const *src, unsigned char *target, size_t targsize); -extern AuthenticationType Auth_FindType(char *hash, char *type); +extern AuthenticationType Auth_FindType(const char *hash, const char *type); extern AuthConfig *AuthBlockToAuthConfig(ConfigEntry *ce); extern void Auth_FreeAuthConfig(AuthConfig *as); -extern int Auth_Check(Client *cptr, AuthConfig *as, char *para); -extern char *Auth_Hash(int type, char *para); +extern int Auth_Check(Client *cptr, AuthConfig *as, const char *para); +extern const char *Auth_Hash(int type, const char *para); extern int Auth_CheckError(ConfigEntry *ce); -extern int Auth_AutoDetectHashType(char *hash); +extern int Auth_AutoDetectHashType(const char *hash); -extern void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen); -extern int channel_canjoin(Client *client, char *name); +extern void make_cloakedhost(Client *client, const char *curr, char *buf, size_t buflen); +extern int channel_canjoin(Client *client, const char *name); extern char *collapse(char *pattern); extern void dcc_sync(Client *client); -extern void report_flines(Client *client); -extern void report_network(Client *client); -extern void report_dynconf(Client *client); -extern void count_memory(Client *cptr, char *nick); -extern void list_scache(Client *client); -extern char *oflagstr(long oflag); -extern int rehash(Client *client, int sig); +extern void request_rehash(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); @@ -468,23 +496,19 @@ extern int add_listener(ConfigItem_listen *conf); extern void link_cleanup(ConfigItem_link *link_ptr); extern void listen_cleanup(); extern int numeric_collides(long numeric); -extern u_long cres_mem(Client *client, char *nick); extern void flag_add(char ch); extern void flag_del(char ch); extern void init_dynconf(void); -extern char *pretty_time_val(long); -extern char *pretty_date(time_t t); -extern int init_conf(char *filename, int rehash); -extern void validate_configuration(void); -extern void run_configuration(void); +extern int config_read_start(void); +extern int is_config_read_finished(void); +extern int config_test(void); +extern void config_run(void); 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 stripcrlf(char *line); -extern time_t rfc2time(char *s); -extern char *rfctime(time_t t, char *buf); extern int strnatcmp(char const *a, char const *b); extern int strnatcasecmp(char const *a, char const *b); extern void outofmemory(size_t bytes); @@ -509,7 +533,7 @@ extern void *safe_alloc(size_t size); * @param dst The current pointer and the pointer where a new copy of the string will be stored. * @param str The string you want to copy */ -#define safe_strdup(dst,str) do { if (dst) free(dst); if (!(str)) dst = NULL; else dst = our_strdup(str); } while(0) +#define safe_strdup(dst,str) do { if (dst) free(dst); if ((str) == NULL) dst = NULL; else dst = our_strdup(str); } while(0) /** Return a copy of the string. Do not free any existing memory. * @param str The string to duplicate @@ -573,7 +597,7 @@ extern char *our_strdup(const char *str); extern char *our_strldup(const char *str, size_t max); extern char *our_strdup_sensitive(const char *str); -extern long config_checkval(char *value, unsigned short flags); +extern long config_checkval(const char *value, unsigned short flags); extern void config_status(FORMAT_STRING(const char *format), ...) __attribute__((format(printf,1,2))); extern void init_random(); extern u_char getrandom8(); @@ -581,10 +605,15 @@ extern uint16_t getrandom16(); extern uint32_t getrandom32(); extern void gen_random_alnum(char *buf, int numbytes); +/* Check config entry for empty/missing parameter */ +#define CheckNull(x) if ((!(x)->value) || (!(*((x)->value)))) { config_error("%s:%i: missing parameter", (x)->file->filename, (x)->line_number); errors++; continue; } +/* as above, but accepting empty string */ +#define CheckNullAllowEmpty(x) if ((!(x)->value)) { config_error("%s:%i: missing parameter", (x)->file->filename, (x)->line_number); errors++; continue; } + extern MODVAR char extchmstr[4][64]; -extern int extcmode_default_requirechop(Client *, Channel *, char, char *, int, int); -extern int extcmode_default_requirehalfop(Client *, Channel *, char, char *, int, int); +extern int extcmode_default_requirechop(Client *, Channel *, char, const char *, int, int); +extern int extcmode_default_requirehalfop(Client *, Channel *, char, const char *, int, int); extern Cmode_t extcmode_get(Cmode *); extern void extcmode_init(void); extern void make_extcmodestr(); @@ -592,8 +621,7 @@ extern void extcmode_duplicate_paramlist(void **xi, void **xo); extern void extcmode_free_paramlist(void **ar); extern void chmode_str(struct ChMode *, char *, char *, size_t, size_t); -extern char *get_client_status(Client *); -extern char *get_snomask_string_raw(long); +extern const char *get_client_status(Client *); extern void SocketLoop(void *); #ifdef _WIN32 extern void InitDebug(void); @@ -605,54 +633,46 @@ extern void CleanUp(void); extern int CountRTFSize(unsigned char *buffer); extern void IRCToRTF(unsigned char *buffer, unsigned char *string); #endif -extern void sendto_chmodemucrap(Client *, Channel *, char *); -extern void verify_opercount(Client *, char *); -extern int valid_host(char *host); -extern int count_oper_sessions(char *); +extern void verify_opercount(Client *, const char *); +extern int valid_host(const char *host, int strict); +extern int count_oper_sessions(const char *); extern char *unreal_mktemp(const char *dir, const char *suffix); -extern char *unreal_getpathname(char *filepath, char *path); -extern char *unreal_getfilename(char *path); -extern char *unreal_getmodfilename(char *path); -extern char *unreal_mkcache(const char *url); -extern int has_cached_version(const char *url); +extern char *unreal_getpathname(const char *filepath, char *path); +extern const char *unreal_getfilename(const char *path); +extern const char *unreal_getmodfilename(const char *path); extern int unreal_copyfile(const char *src, const char *dest); extern int unreal_copyfileex(const char *src, const char *dest, int tryhardlink); extern time_t unreal_getfilemodtime(const char *filename); extern void unreal_setfilemodtime(const char *filename, time_t mtime); extern void DeleteTempModules(void); extern MODVAR Extban *extbaninfo; -extern Extban *findmod_by_bantype(char c); +extern Extban *findmod_by_bantype(const char *str, const char **remainder); extern Extban *ExtbanAdd(Module *reserved, ExtbanInfo req); extern void ExtbanDel(Extban *); extern void extban_init(void); extern char *trim_str(char *str, int len); extern MODVAR char *ban_realhost, *ban_virthost, *ban_ip; -extern BanAction banact_stringtoval(char *s); -extern char *banact_valtostring(BanAction val); +extern BanAction banact_stringtoval(const char *s); +extern const char *banact_valtostring(BanAction val); extern BanAction banact_chartoval(char c); extern char banact_valtochar(BanAction val); -extern int spamfilter_gettargets(char *s, Client *client); +extern int spamfilter_gettargets(const char *s, Client *client); extern char *spamfilter_target_inttostring(int v); -extern Spamfilter *unreal_buildspamfilter(char *s); -extern char *our_strcasestr(char *haystack, char *needle); -extern int spamfilter_getconftargets(char *s); -extern void remove_oper_snomasks(Client *client); +extern char *our_strcasestr(const char *haystack, const char *needle); +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 Channel *get_channel(Client *cptr, char *chname, int flag); extern MODVAR char backupbuf[]; -extern void add_invite(Client *, Client *, Channel *, MessageTag *); -extern void del_invite(Client *, Channel *); extern int is_invited(Client *client, Channel *channel); -extern void channel_modes(Client *cptr, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size, Channel *channel); -extern MODVAR char modebuf[BUFSIZE], parabuf[BUFSIZE]; -extern int op_can_override(char *acl, Client *client,Channel *channel,void* extra); -extern Client *find_chasing(Client *client, char *user, int *chasing); +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); +extern Client *find_chasing(Client *client, const char *user, int *chasing); extern MODVAR long opermode; extern MODVAR long sajoinmode; -extern void add_user_to_channel(Channel *channel, Client *who, int flags); -extern int add_banid(Client *, Channel *, char *); -extern int add_exbanid(Client *cptr, Channel *channel, char *banid); +extern void add_user_to_channel(Channel *channel, Client *who, const char *modes); +extern int add_banid(Client *, Channel *, const char *); +extern int add_exbanid(Client *cptr, Channel *channel, const char *banid); extern int sub1_from_channel(Channel *); extern MODVAR CoreChannelModeTable corechannelmodetable[]; extern char *unreal_encodespace(char *s); @@ -664,10 +684,9 @@ extern int callbacks_check(void); extern void callbacks_switchover(void); extern int efunctions_check(void); extern void efunctions_switchover(void); -extern char *encode_ip(char *); -extern char *decode_ip(char *); -extern void sendto_fconnectnotice(Client *client, int disconnect, char *comment); -extern void sendto_one_nickcmd(Client *server, Client *client, char *umodes); +extern const char *encode_ip(const char *); +extern const char *decode_ip(const char *); +extern void sendto_one_nickcmd(Client *server, MessageTag *mtags, Client *client, const char *umodes); extern int on_dccallow_list(Client *to, Client *from); extern int add_dccallow(Client *client, Client *optr); extern int del_dccallow(Client *client, Client *optr); @@ -677,7 +696,7 @@ extern void del_async_connects(void); extern void isupport_init(void); extern void clicap_init(void); extern void efunctions_init(void); -extern void do_cmd(Client *client, MessageTag *mtags, char *cmd, int parc, char *parv[]); +extern void do_cmd(Client *client, MessageTag *mtags, const char *cmd, int parc, const char *parv[]); extern MODVAR char *me_hash; extern MODVAR int dontspread; extern MODVAR int labeled_response_inhibit; @@ -685,33 +704,35 @@ extern MODVAR int labeled_response_inhibit_end; extern MODVAR int labeled_response_force; /* Efuncs */ -extern MODVAR void (*do_join)(Client *, int, char **); -extern MODVAR void (*join_channel)(Channel *channel, Client *client, MessageTag *mtags, int flags); -extern MODVAR int (*can_join)(Client *client, Channel *channel, char *key, char *parv[]); -extern MODVAR void (*do_mode)(Channel *channel, Client *client, MessageTag *mtags, int parc, char *parv[], time_t sendts, int samode); -extern MODVAR void (*set_mode)(Channel *channel, Client *cptr, int parc, char *parv[], u_int *pcount, - char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce); -extern MODVAR void (*cmd_umode)(Client *, MessageTag *, int, char **); -extern MODVAR int (*register_user)(Client *client, char *nick, char *username, char *umode, char *virthost, char *ip); +extern MODVAR void (*do_join)(Client *, int, const char **); +extern MODVAR void (*join_channel)(Channel *channel, Client *client, MessageTag *mtags, const char *flags); +extern MODVAR int (*can_join)(Client *client, Channel *channel, const char *key, char **errmsg); +extern MODVAR void (*do_mode)(Channel *channel, Client *client, MessageTag *mtags, int parc, const char *parv[], time_t sendts, int samode); +extern MODVAR MultiLineMode *(*set_mode)(Channel *channel, Client *cptr, int parc, const char *parv[], u_int *pcount, + char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]); +extern MODVAR void (*set_channel_mode)(Channel *channel, char *modes, char *parameters); +extern MODVAR void (*cmd_umode)(Client *, MessageTag *, int, const char **); +extern MODVAR int (*register_user)(Client *client); extern MODVAR int (*tkl_hash)(unsigned int c); extern MODVAR char (*tkl_typetochar)(int type); extern MODVAR int (*tkl_chartotype)(char c); -extern MODVAR char *(*tkl_type_string)(TKL *tk); -extern MODVAR TKL *(*tkl_add_serverban)(int type, char *usermask, char *hostmask, char *reason, char *setby, +extern MODVAR const char *(*tkl_type_string)(TKL *tk); +extern MODVAR const char *(*tkl_type_config_string)(TKL *tk); +extern MODVAR TKL *(*tkl_add_serverban)(int type, const char *usermask, const char *hostmask, const char *reason, const char *setby, time_t expire_at, time_t set_at, int soft, int flags); -extern MODVAR TKL *(*tkl_add_banexception)(int type, char *usermask, char *hostmask, char *reason, char *set_by, - time_t expire_at, time_t set_at, int soft, char *bantypes, int flags); -extern MODVAR TKL *(*tkl_add_nameban)(int type, char *name, int hold, char *reason, char *setby, +extern MODVAR TKL *(*tkl_add_banexception)(int type, const char *usermask, const char *hostmask, const char *reason, const char *set_by, + time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags); +extern MODVAR TKL *(*tkl_add_nameban)(int type, const char *name, int hold, const char *reason, const char *setby, time_t expire_at, time_t set_at, int flags); -extern MODVAR TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, Match *match, char *setby, +extern MODVAR TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, Match *match, const char *setby, time_t expire_at, time_t set_at, - time_t spamf_tkl_duration, char *spamf_tkl_reason, + time_t spamf_tkl_duration, const char *spamf_tkl_reason, int flags); -extern MODVAR TKL *(*find_tkl_serverban)(int type, char *usermask, char *hostmask, int softban); -extern MODVAR TKL *(*find_tkl_banexception)(int type, char *usermask, char *hostmask, int softban); -extern MODVAR TKL *(*find_tkl_nameban)(int type, char *name, int hold); -extern MODVAR TKL *(*find_tkl_spamfilter)(int type, char *match_string, unsigned short action, unsigned short target); -extern MODVAR void (*sendnotice_tkl_del)(char *removed_by, TKL *tkl); +extern MODVAR TKL *(*find_tkl_serverban)(int type, const char *usermask, const char *hostmask, int softban); +extern MODVAR TKL *(*find_tkl_banexception)(int type, const char *usermask, const char *hostmask, int softban); +extern MODVAR TKL *(*find_tkl_nameban)(int type, const char *name, int hold); +extern MODVAR TKL *(*find_tkl_spamfilter)(int type, const char *match_string, unsigned short action, unsigned short target); +extern MODVAR void (*sendnotice_tkl_del)(const char *removed_by, TKL *tkl); extern MODVAR void (*sendnotice_tkl_add)(TKL *tkl); extern MODVAR void (*free_tkl)(TKL *tkl); extern MODVAR TKL *(*tkl_del_line)(TKL *tkl); @@ -719,50 +740,53 @@ extern MODVAR void (*tkl_check_local_remove_shun)(TKL *tmp); extern MODVAR int (*find_tkline_match)(Client *cptr, int skip_soft); extern MODVAR int (*find_shun)(Client *cptr); extern MODVAR int (*find_spamfilter_user)(Client *client, int flags); -extern MODVAR TKL *(*find_qline)(Client *cptr, char *nick, int *ishold); +extern MODVAR TKL *(*find_qline)(Client *cptr, const char *nick, int *ishold); extern MODVAR TKL *(*find_tkline_match_zap)(Client *cptr); -extern MODVAR void (*tkl_stats)(Client *cptr, int type, char *para, int *cnt); +extern MODVAR void (*tkl_stats)(Client *cptr, int type, const char *para, int *cnt); extern MODVAR void (*tkl_sync)(Client *client); -extern MODVAR void (*cmd_tkl)(Client *client, MessageTag *recv_mtags, int parc, char *parv[]); -extern MODVAR int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration); -extern MODVAR int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk); -extern MODVAR int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd); +extern MODVAR void (*cmd_tkl)(Client *client, MessageTag *recv_mtags, int parc, const char *parv[]); +extern MODVAR int (*place_host_ban)(Client *client, BanAction action, const char *reason, long duration); +extern MODVAR int (*match_spamfilter)(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, TKL **rettk); +extern MODVAR int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, const char *cmd); extern MODVAR int (*join_viruschan)(Client *client, TKL *tk, int type); -extern MODVAR unsigned char *(*StripColors)(unsigned char *text); -extern MODVAR const char *(*StripControlCodes)(unsigned char *text); -extern MODVAR void (*spamfilter_build_user_string)(char *buf, char *nick, Client *acptr); +extern MODVAR const char *(*StripColors)(const char *text); +extern MODVAR const char *(*StripControlCodes)(const char *text); +extern MODVAR void (*spamfilter_build_user_string)(char *buf, const char *nick, Client *acptr); extern MODVAR void (*send_protoctl_servers)(Client *client, int response); -extern MODVAR int (*verify_link)(Client *client, char *servername, ConfigItem_link **link_out); +extern MODVAR int (*verify_link)(Client *client, ConfigItem_link **link_out); extern MODVAR void (*send_server_message)(Client *client); extern MODVAR void (*broadcast_md_client)(ModDataInfo *mdi, Client *acptr, ModData *md); extern MODVAR void (*broadcast_md_channel)(ModDataInfo *mdi, Channel *channel, ModData *md); extern MODVAR void (*broadcast_md_member)(ModDataInfo *mdi, Channel *channel, Member *m, ModData *md); extern MODVAR void (*broadcast_md_membership)(ModDataInfo *mdi, Client *acptr, Membership *m, ModData *md); -extern MODVAR void (*broadcast_md_client_cmd)(Client *except, Client *sender, Client *acptr, char *varname, char *value); -extern MODVAR void (*broadcast_md_channel_cmd)(Client *except, Client *sender, Channel *channel, char *varname, char *value); -extern MODVAR void (*broadcast_md_member_cmd)(Client *except, Client *sender, Channel *channel, Client *acptr, char *varname, char *value); -extern MODVAR void (*broadcast_md_membership_cmd)(Client *except, Client *sender, Client *acptr, Channel *channel, char *varname, char *value); +extern MODVAR void (*broadcast_md_client_cmd)(Client *except, Client *sender, Client *acptr, const char *varname, const char *value); +extern MODVAR void (*broadcast_md_channel_cmd)(Client *except, Client *sender, Channel *channel, const char *varname, const char *value); +extern MODVAR void (*broadcast_md_member_cmd)(Client *except, Client *sender, Channel *channel, Client *acptr, const char *varname, const char *value); +extern MODVAR void (*broadcast_md_membership_cmd)(Client *except, Client *sender, Client *acptr, Channel *channel, const char *varname, const char *value); +extern MODVAR void (*moddata_add_s2s_mtags)(Client *client, MessageTag **mtags); +extern MODVAR void (*moddata_extract_s2s_mtags)(Client *client, MessageTag *mtags); extern MODVAR void (*send_moddata_client)(Client *srv, Client *acptr); extern MODVAR void (*send_moddata_channel)(Client *srv, Channel *channel); extern MODVAR void (*send_moddata_members)(Client *srv); extern MODVAR void (*broadcast_moddata_client)(Client *acptr); extern MODVAR int (*check_banned)(Client *cptr, int exitflags); extern MODVAR void (*introduce_user)(Client *to, Client *acptr); -extern MODVAR int (*check_deny_version)(Client *cptr, char *software, int protocol, char *flags); -extern MODVAR int (*match_user)(char *rmask, Client *acptr, int options); +extern MODVAR int (*check_deny_version)(Client *cptr, const char *software, int protocol, const char *flags); +extern MODVAR int (*match_user)(const char *rmask, Client *acptr, int options); extern MODVAR void (*userhost_save_current)(Client *client); extern MODVAR void (*userhost_changed)(Client *client); extern MODVAR void (*send_join_to_local_users)(Client *client, Channel *channel, MessageTag *mtags); extern MODVAR int (*do_nick_name)(char *nick); extern MODVAR int (*do_remote_nick_name)(char *nick); -extern MODVAR char *(*charsys_get_current_languages)(void); +extern MODVAR const char *(*charsys_get_current_languages)(void); extern MODVAR void (*broadcast_sinfo)(Client *acptr, Client *to, Client *except); +extern MODVAR void (*connect_server)(ConfigItem_link *aconf, Client *by, struct hostent *hp); extern MODVAR void (*parse_message_tags)(Client *cptr, char **str, MessageTag **mtag_list); -extern MODVAR char *(*mtags_to_string)(MessageTag *m, Client *acptr); -extern MODVAR int (*can_send_to_channel)(Client *cptr, Channel *channel, char **msgtext, char **errmsg, int notice); +extern MODVAR const char *(*mtags_to_string)(MessageTag *m, Client *acptr); +extern MODVAR int (*can_send_to_channel)(Client *cptr, Channel *channel, const char **msgtext, const char **errmsg, int notice); extern MODVAR void (*broadcast_md_globalvar)(ModDataInfo *mdi, ModData *md); -extern MODVAR void (*broadcast_md_globalvar_cmd)(Client *except, Client *sender, char *varname, char *value); -extern MODVAR int (*tkl_ip_hash)(char *ip); +extern MODVAR void (*broadcast_md_globalvar_cmd)(Client *except, Client *sender, const char *varname, const char *value); +extern MODVAR int (*tkl_ip_hash)(const char *ip); extern MODVAR int (*tkl_ip_hash_type)(int type); extern MODVAR int (*find_tkl_exception)(int ban_type, Client *cptr); extern MODVAR int (*del_silence)(Client *client, const char *mask); @@ -771,109 +795,118 @@ extern MODVAR int (*is_silenced)(Client *client, Client *acptr); extern MODVAR void *(*labeled_response_save_context)(void); extern MODVAR void (*labeled_response_set_context)(void *ctx); extern MODVAR void (*labeled_response_force_end)(void); -extern MODVAR void (*kick_user)(MessageTag *mtags, Channel *channel, Client *client, Client *victim, char *comment); +extern MODVAR void (*kick_user)(MessageTag *mtags, Channel *channel, Client *client, Client *victim, const char *comment); +extern MODVAR int (*watch_add)(const char *nick, Client *client, int flags); +extern MODVAR int (*watch_del)(const char *nick, Client *client, int flags); +extern MODVAR int (*watch_del_list)(Client *client, int flags); +extern MODVAR Watch *(*watch_get)(const char *nick); +extern MODVAR int (*watch_check)(Client *client, int reply, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event)); +extern MODVAR char *(*tkl_uhost)(TKL *tkl, char *buf, size_t buflen, int options); +extern MODVAR void (*do_unreal_log_remote_deliver)(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized); +extern MODVAR char *(*get_chmodes_for_user)(Client *client, const char *flags); +extern MODVAR WhoisConfigDetails (*whois_get_policy)(Client *client, Client *target, const char *name); /* /Efuncs */ -/* SSL/TLS functions */ -extern int early_init_ssl(); -extern int init_ssl(); +/* TLS functions */ +extern int early_init_tls(); +extern int init_tls(); extern int ssl_handshake(Client *); /* Handshake the accpeted con.*/ extern int ssl_client_handshake(Client *, ConfigItem_link *); /* and the initiated con.*/ -extern int ircd_SSL_accept(Client *acptr, int fd); -extern int ircd_SSL_connect(Client *acptr, int fd); +extern int unreal_tls_accept(Client *acptr, int fd); +extern int unreal_tls_connect(Client *acptr, int fd); extern int SSL_smart_shutdown(SSL *ssl); -extern void ircd_SSL_client_handshake(int, int, void *); +extern const char *ssl_error_str(int err, int my_errno); +extern void unreal_tls_client_handshake(int, int, void *); extern void SSL_set_nonblocking(SSL *s); extern SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server); -extern MODFUNC char *tls_get_cipher(SSL *ssl); +extern const char *tls_get_cipher(Client *client); extern TLSOptions *get_tls_options_for_client(Client *acptr); extern int outdated_tls_client(Client *acptr); -extern char *outdated_tls_client_build_string(char *pattern, Client *acptr); +extern const char *outdated_tls_client_build_string(const char *pattern, Client *acptr); extern int check_certificate_expiry_ctx(SSL_CTX *ctx, char **errstr); extern EVENT(tls_check_expiry); -/* End of SSL/TLS functions */ +extern MODVAR EVP_MD *sha256_function; +extern MODVAR EVP_MD *sha1_function; +extern MODVAR EVP_MD *md5_function; +/* End of TLS functions */ extern void parse_message_tags_default_handler(Client *client, char **str, MessageTag **mtag_list); -extern char *mtags_to_string_default_handler(MessageTag *m, Client *client); +extern const char *mtags_to_string_default_handler(MessageTag *m, Client *client); extern void *labeled_response_save_context_default_handler(void); extern void labeled_response_set_context_default_handler(void *ctx); extern void labeled_response_force_end_default_handler(void); extern int add_silence_default_handler(Client *client, const char *mask, int senderr); extern int del_silence_default_handler(Client *client, const char *mask); extern int is_silenced_default_handler(Client *client, Client *acptr); +extern void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized); extern MODVAR MOTDFile opermotd, svsmotd, motd, botmotd, smotd, rules; extern MODVAR int max_connection_count; -extern int add_listmode(Ban **list, Client *cptr, Channel *channel, char *banid); -extern int add_listmode_ex(Ban **list, Client *cptr, Channel *channel, char *banid, char *setby, time_t seton); -extern int del_listmode(Ban **list, Channel *channel, char *banid); +extern int add_listmode(Ban **list, Client *cptr, Channel *channel, const char *banid); +extern int add_listmode_ex(Ban **list, Client *cptr, Channel *channel, const char *banid, const char *setby, time_t seton); +extern int del_listmode(Ban **list, Channel *channel, const char *banid); extern int Halfop_mode(long mode); -extern char *clean_ban_mask(char *, int, Client *); +extern const char *clean_ban_mask(const char *, int, Client *, int); extern int find_invex(Channel *channel, Client *client); extern void DoMD5(char *mdout, const char *src, unsigned long n); extern char *md5hash(char *dst, const char *src, unsigned long n); extern char *sha256hash(char *dst, const char *src, unsigned long n); +extern void sha256hash_binary(char *dst, const char *src, unsigned long n); +extern void sha1hash_binary(char *dst, const char *src, unsigned long n); extern MODVAR TKL *tklines[TKLISTLEN]; extern MODVAR TKL *tklines_ip_hash[TKLIPHASHLEN1][TKLIPHASHLEN2]; -extern char *cmdname_by_spamftarget(int target); +extern const char *cmdname_by_spamftarget(int target); extern void unrealdns_delreq_bycptr(Client *cptr); -extern void sendtxtnumeric(Client *to, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,2,3))); -extern void unrealdns_gethostbyname_link(char *name, ConfigItem_link *conf, int ipv4_only); +extern void unrealdns_gethostbyname_link(const char *name, ConfigItem_link *conf, int ipv4_only); extern void unrealdns_delasyncconnects(void); -extern int is_autojoin_chan(char *chname); +extern int is_autojoin_chan(const char *chname); extern void unreal_free_hostent(struct hostent *he); -extern struct hostent *unreal_create_hostent(char *name, char *ip); -extern char *unreal_time_sync_error(void); +extern struct hostent *unreal_create_hostent(const char *name, const char *ip); +extern const char *unreal_time_sync_error(void); extern int unreal_time_synch(int timeout); -extern char *getcloak(Client *client); +extern const char *getcloak(Client *client); extern MODVAR unsigned char param_to_slot_mapping[256]; -extern char *cm_getparameter(Channel *channel, char mode); -extern void cm_putparameter(Channel *channel, char mode, char *str); +extern const char *cm_getparameter(Channel *channel, char mode); +extern const char *cm_getparameter_ex(void **p, char mode); +extern void cm_putparameter(Channel *channel, char mode, const char *str); +extern void cm_putparameter_ex(void **p, char mode, const char *str); extern void cm_freeparameter(Channel *channel, char mode); -extern char *cm_getparameter_ex(void **p, char mode); -extern void cm_putparameter_ex(void **p, char mode, char *str); extern void cm_freeparameter_ex(void **p, char mode, char *str); -extern int file_exists(char *file); -extern time_t get_file_time(char *fname); -extern long get_file_size(char *fname); +extern int file_exists(const char *file); +extern time_t get_file_time(const char *fname); +extern long get_file_size(const char *fname); extern void free_motd(MOTDFile *motd); /* s_serv.c */ extern void fix_timers(void); -extern char *chfl_to_sjoin_symbol(int s); +extern const char *chfl_to_sjoin_symbol(int s); extern char chfl_to_chanmode(int s); -extern void add_pending_net(Client *client, char *str); +extern void add_pending_net(Client *client, const char *str); extern void free_pending_net(Client *client); extern Client *find_non_pending_net_duplicates(Client *cptr); -extern PendingNet *find_pending_net_by_sid_butone(char *sid, Client *exempt); +extern PendingNet *find_pending_net_by_sid_butone(const char *sid, Client *exempt); extern Client *find_pending_net_duplicates(Client *cptr, Client **srv, char **sid); extern MODVAR char serveropts[]; extern MODVAR char *ISupportStrings[]; extern void read_packet(int fd, int revents, void *data); extern int process_packet(Client *cptr, char *readbuf, int length, int killsafely); -extern void sendto_realops_and_log(FORMAT_STRING(const char *fmt), ...) __attribute__((format(printf,1,2))); -extern int parse_chanmode(ParseMode *pm, char *modebuf_in, char *parabuf_in); -extern void config_report_ssl_error(void); -extern int dead_socket(Client *to, char *notice); -extern Match *unreal_create_match(MatchType type, char *str, char **error); +extern int parse_chanmode(ParseMode *pm, const char *modebuf_in, const char *parabuf_in); +extern int dead_socket(Client *to, const char *notice); +extern Match *unreal_create_match(MatchType type, const char *str, char **error); extern void unreal_delete_match(Match *m); -extern int unreal_match(Match *m, char *str); -extern int unreal_match_method_strtoval(char *str); +extern int unreal_match(Match *m, const char *str); +extern int unreal_match_method_strtoval(const char *str); extern char *unreal_match_method_valtostr(int val); -extern int mixed_network(void); extern void unreal_delete_masks(ConfigItem_mask *m); extern void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce); extern int unreal_mask_match(Client *acptr, ConfigItem_mask *m); -extern char *our_strcasestr(char *haystack, char *needle); -extern void update_conf(void); -extern MODVAR int need_34_upgrade; +extern int unreal_mask_match_string(const char *name, ConfigItem_mask *m); #ifdef _WIN32 extern MODVAR BOOL IsService; #endif -extern int match_ip46(char *a, char *b); extern void extcmodes_check_for_changes(void); extern void umodes_check_for_changes(void); -extern int config_parse_flood(char *orig, int *times, int *period); -extern int swhois_add(Client *acptr, char *tag, int priority, char *swhois, Client *from, Client *skip); -extern int swhois_delete(Client *acptr, char *tag, char *swhois, Client *from, Client *skip); +extern int config_parse_flood(const char *orig, int *times, int *period); +extern int swhois_add(Client *acptr, const char *tag, int priority, const char *swhois, Client *from, Client *skip); +extern int swhois_delete(Client *acptr, const char *tag, const char *swhois, Client *from, Client *skip); extern void remove_oper_privileges(Client *client, int broadcast_mode_change); extern int client_starttls(Client *acptr); extern void start_server_handshake(Client *cptr); @@ -882,21 +915,21 @@ 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, char *ip, int port, int ipv6); -extern int unreal_connect(int fd, char *ip, int port, int ipv6); -extern int is_valid_ip(char *str); +extern int unreal_bind(int fd, const char *ip, int port, int ipv6); +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 MODVAR Client *remote_rehash_client; extern MODVAR int debugfd; -extern void convert_to_absolute_path(char **path, char *reldir); +extern void convert_to_absolute_path(char **path, const char *reldir); extern int has_user_mode(Client *acptr, char mode); extern int has_channel_mode(Channel *channel, char mode); +extern int has_channel_mode_raw(Cmode_t m, char mode); extern Cmode_t get_extmode_bitbychar(char m); -extern long get_mode_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_ssl(Client *); +extern void reinit_tls(void); extern CMD_FUNC(cmd_error); extern CMD_FUNC(cmd_dns); extern CMD_FUNC(cmd_info); @@ -910,53 +943,53 @@ extern CMD_FUNC(cmd_module); extern CMD_FUNC(cmd_rehash); extern CMD_FUNC(cmd_die); extern CMD_FUNC(cmd_restart); -extern void cmd_alias(Client *client, MessageTag *recv_mtags, int parc, char *parv[], char *cmd); /* special! */ -extern char *pcre2_version(void); +extern void cmd_alias(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], const char *cmd); /* special! */ +extern const char *pcre2_version(void); extern int get_terminal_width(void); extern int has_common_channels(Client *c1, Client *c2); extern int user_can_see_member(Client *user, Client *target, Channel *channel); extern int invisible_user_in_channel(Client *target, Channel *channel); -extern MODVAR int ssl_client_index; +extern MODVAR int tls_client_index; extern TLSOptions *FindTLSOptionsForUser(Client *acptr); extern int IsWebsocket(Client *acptr); -extern Policy policy_strtoval(char *s); -extern char *policy_valtostr(Policy policy); +extern Policy policy_strtoval(const char *s); +extern const char *policy_valtostr(Policy policy); extern char policy_valtochar(Policy policy); -extern int verify_certificate(SSL *ssl, char *hostname, char **errstr); -extern char *certificate_name(SSL *ssl); +extern int verify_certificate(SSL *ssl, const char *hostname, char **errstr); +extern const char *certificate_name(SSL *ssl); extern void start_of_normal_client_handshake(Client *acptr); extern void clicap_pre_rehash(void); extern void clicap_post_rehash(void); extern void unload_all_unused_mtag_handlers(void); -extern void send_cap_notify(int add, char *token); +extern void send_cap_notify(int add, const char *token); extern void sendbufto_one(Client *to, char *msg, unsigned int quick); extern MODVAR int current_serial; -extern char *spki_fingerprint(Client *acptr); -extern char *spki_fingerprint_ex(X509 *x509_cert); -extern int is_module_loaded(char *name); +extern const char *spki_fingerprint(Client *acptr); +extern const char *spki_fingerprint_ex(X509 *x509_cert); +extern int is_module_loaded(const char *name); extern void close_std_descriptors(void); -extern void banned_client(Client *acptr, char *bantype, char *reason, int global, int noexit); +extern void banned_client(Client *acptr, const char *bantype, const char *reason, int global, int noexit); extern char *mystpcpy(char *dst, const char *src); -extern size_t add_sjsby(char *buf, char *setby, time_t seton); -extern MaxTarget *findmaxtarget(char *cmd); -extern void setmaxtargets(char *cmd, int limit); +extern size_t add_sjsby(char *buf, const char *setby, time_t seton); +extern MaxTarget *findmaxtarget(const char *cmd); +extern void setmaxtargets(const char *cmd, int limit); extern void freemaxtargets(void); -extern int max_targets_for_command(char *cmd); +extern int max_targets_for_command(const char *cmd); extern void set_targmax_defaults(void); -extern void parse_chanmodes_protoctl(Client *client, char *str); -extern void concat_params(char *buf, int len, int parc, char *parv[]); +extern void parse_chanmodes_protoctl(Client *client, const char *str); +extern void concat_params(char *buf, int len, int parc, const char *parv[]); extern void charsys_check_for_changes(void); -extern int maxclients; -extern int fast_badword_match(ConfigItem_badword *badword, char *line); -extern int fast_badword_replace(ConfigItem_badword *badword, char *line, char *buf, int max); -extern char *stripbadwords(char *str, ConfigItem_badword *start_bw, int *blocked); -extern int badword_config_process(ConfigItem_badword *ca, char *str); +extern MODVAR int maxclients; +extern int fast_badword_match(ConfigItem_badword *badword, const char *line); +extern int fast_badword_replace(ConfigItem_badword *badword, const char *line, char *buf, int max); +extern const char *stripbadwords(const char *str, ConfigItem_badword *start_bw, int *blocked); +extern int badword_config_process(ConfigItem_badword *ca, const char *str); extern void badword_config_free(ConfigItem_badword *ca); -extern char *badword_config_check_regex(char *s, int fastsupport, int check_broadness); -extern AllowedChannelChars allowed_channelchars_strtoval(char *str); -extern char *allowed_channelchars_valtostr(AllowedChannelChars v); -extern HideIdleTimePolicy hideidletime_strtoval(char *str); -extern char *hideidletime_valtostr(HideIdleTimePolicy v); +extern const char *badword_config_check_regex(const char *s, int fastsupport, int check_broadness); +extern AllowedChannelChars allowed_channelchars_strtoval(const char *str); +extern const char *allowed_channelchars_valtostr(AllowedChannelChars v); +extern HideIdleTimePolicy hideidletime_strtoval(const char *str); +extern const char *hideidletime_valtostr(HideIdleTimePolicy v); extern long ClientCapabilityBit(const char *token); extern int is_handshake_finished(Client *client); extern void SetCapability(Client *acptr, const char *token); @@ -966,12 +999,12 @@ extern void new_message_special(Client *sender, MessageTag *recv_mtags, MessageT extern void generate_batch_id(char *str); extern MessageTag *find_mtag(MessageTag *mtags, const char *token); extern MessageTag *duplicate_mtag(MessageTag *mtag); +#define safe_free_message_tags(x) do { if (x) free_message_tags(x); x = NULL; } while(0) extern void free_message_tags(MessageTag *m); -extern time_t server_time_to_unix_time(const char *tbuf); -extern int history_set_limit(char *object, int max_lines, long max_t); -extern int history_add(char *object, MessageTag *mtags, char *line); -extern HistoryResult *history_request(char *object, HistoryFilter *filter); -extern int history_destroy(char *object); +extern int history_set_limit(const char *object, int max_lines, long max_t); +extern int history_add(const char *object, MessageTag *mtags, const char *line); +extern HistoryResult *history_request(const char *object, HistoryFilter *filter); +extern int history_destroy(const char *object); extern int can_receive_history(Client *client); extern void history_send_result(Client *client, HistoryResult *r); extern void free_history_result(HistoryResult *r); @@ -983,61 +1016,88 @@ extern int read_int64(FILE *fd, uint64_t *t); extern int read_int32(FILE *fd, uint32_t *t); extern int read_data(FILE *fd, void *buf, size_t len); extern int write_data(FILE *fd, const void *buf, size_t len); -extern int write_str(FILE *fd, char *x); +extern int write_str(FILE *fd, const char *x); extern int read_str(FILE *fd, char **x); -extern int char_to_channelflag(char c); extern void _free_entire_name_list(NameList *n); -extern void _add_name_list(NameList **list, char *name); -extern void _del_name_list(NameList **list, char *name); -extern NameList *find_name_list(NameList *list, char *name); -extern NameList *find_name_list_match(NameList *list, char *name); +extern void _add_name_list(NameList **list, const char *name); +extern void _del_name_list(NameList **list, const char *name); +extern NameList *find_name_list(NameList *list, const char *name); +extern NameList *find_name_list_match(NameList *list, const char *name); extern int minimum_msec_since_last_run(struct timeval *tv_old, long minimum); extern int unrl_utf8_validate(const char *str, const char **end); -extern char *unrl_utf8_make_valid(const char *str); +extern char *unrl_utf8_make_valid(const char *str, char *outputbuf, size_t outputbuflen, int strict_length_check); extern void utf8_test(void); extern MODVAR int non_utf8_nick_chars_in_use; extern void short_motd(Client *client); extern int should_show_connect_info(Client *client); -extern void send_invalid_channelname(Client *client, char *channelname); +extern void send_invalid_channelname(Client *client, const char *channelname); extern int is_extended_ban(const char *str); -extern int valid_sid(char *name); +extern int is_extended_server_ban(const char *str); +extern int empty_mode(const char *m); +extern void free_multilinemode(MultiLineMode *m); +#define safe_free_multilinemode(m) do { if (m) free_multilinemode(m); m = NULL; } while(0) +extern int valid_sid(const char *name); +extern int valid_uid(const char *name); extern void parse_client_queued(Client *client); -extern char *sha256sum_file(const char *fname); +extern const char *sha256sum_file(const char *fname); extern char *filename_strip_suffix(const char *fname, const char *suffix); extern char *filename_add_suffix(const char *fname, const char *suffix); extern int filename_has_suffix(const char *fname, const char *suffix); -extern void addmultiline(MultiLine **l, char *line); +extern void addmultiline(MultiLine **l, const char *line); extern void freemultiline(MultiLine *l); #define safe_free_multiline(x) do { if (x) freemultiline(x); x = NULL; } while(0) +extern MultiLine *line2multiline(const char *str); extern void sendnotice_multiline(Client *client, MultiLine *m); extern void unreal_del_quotes(char *i); -extern char *unreal_add_quotes(char *str); -extern int unreal_add_quotes_r(char *i, char *o, size_t len); +extern const char *unreal_add_quotes(const char *str); +extern int unreal_add_quotes_r(const char *i, char *o, size_t len); extern void user_account_login(MessageTag *recv_mtags, Client *client); extern void link_generator(void); extern void update_throttling_timer_settings(void); extern int hide_idle_time(Client *client, Client *target); -extern void lost_server_link(Client *serv, FORMAT_STRING(const char *fmt), ...); -extern char *sendtype_to_cmd(SendType sendtype); +extern void lost_server_link(Client *serv, const char *tls_error_string); +extern const char *sendtype_to_cmd(SendType sendtype); extern MODVAR MessageTagHandler *mtaghandlers; -extern int security_group_valid_name(char *name); -extern int security_group_exists(char *name); -extern SecurityGroup *add_security_group(char *name, int order); -extern SecurityGroup *find_security_group(char *name); +extern int security_group_valid_name(const char *name); +extern int security_group_exists(const char *name); +extern SecurityGroup *add_security_group(const char *name, int order); +extern SecurityGroup *find_security_group(const char *name); extern void free_security_group(SecurityGroup *s); extern void set_security_group_defaults(void); extern int user_allowed_by_security_group(Client *client, SecurityGroup *s); -extern int user_allowed_by_security_group_name(Client *client, char *secgroupname); -extern void add_nvplist(NameValuePrioList **lst, int priority, char *name, char *value); -extern void add_fmt_nvplist(NameValuePrioList **lst, int priority, char *name, FORMAT_STRING(const char *format), ...) __attribute__((format(printf,4,5))); -extern NameValuePrioList *find_nvplist(NameValuePrioList *list, char *name); +extern int user_allowed_by_security_group_name(Client *client, const char *secgroupname); +#define nv_find_by_name(stru, name) do_nv_find_by_name(stru, name, ARRAY_SIZEOF((stru))) +extern long do_nv_find_by_name(NameValue *table, const char *cmd, int numelements); +#define nv_find_by_value(stru, value) do_nv_find_by_value(stru, value, ARRAY_SIZEOF((stru))) +extern const char *do_nv_find_by_value(NameValue *table, long value, int numelements); +extern void add_nvplist(NameValuePrioList **lst, int priority, const char *name, const char *value); +extern void add_fmt_nvplist(NameValuePrioList **lst, int priority, const char *name, FORMAT_STRING(const char *format), ...) __attribute__((format(printf,4,5))); +/** Combination of add_nvplist() and buildnumeric() for convenience - only used in WHOIS response functions. + * @param lst The NameValuePrioList &head + * @param priority The priority of the item being added + * @param name The name of the item being added (eg: "certfp") + * @param to The recipient + * @param numeric The numeric, one of RPL_* or ERR_*, see include/numeric.h + * @param ... The parameters for the numeric + * @note Be sure to provide the correct number and type of parameters that belong to the numeric. Check include/numeric.h when in doubt! + */ +#define add_nvplist_numeric(lst, priority, name, to, numeric, ...) add_nvplist_numeric_fmt(lst, priority, name, to, numeric, STR_ ## numeric, ##__VA_ARGS__) +extern void add_nvplist_numeric_fmt(NameValuePrioList **lst, int priority, const char *name, Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,6,7))); +extern NameValuePrioList *find_nvplist(NameValuePrioList *list, const char *name); extern void free_nvplist(NameValuePrioList *lst); -extern char *get_connect_extinfo(Client *client); -extern char *unreal_strftime(char *str); -extern void strtolower_safe(char *dst, char *src, int size); +extern const char *get_connect_extinfo(Client *client); +extern char *unreal_strftime(const char *str); +extern void strtolower(char *str); +extern void strtolower_safe(char *dst, const char *src, int size); +extern void strtoupper(char *str); +extern void strtoupper_safe(char *dst, const char *src, int size); extern int running_interactively(void); +extern int terminal_supports_color(void); extern void skip_whitespace(char **p); extern void read_until(char **p, char *stopchars); +extern int is_ip_valid(const char *ip); +extern int is_file_readable(const char *file, const char *dir); +json_t *json_string_unreal(const char *s); /* src/unrealdb.c start */ extern UnrealDB *unrealdb_open(const char *filename, UnrealDBMode mode, char *secret_block); extern int unrealdb_close(UnrealDB *c); @@ -1045,30 +1105,106 @@ extern char *unrealdb_test_db(const char *filename, char *secret_block); extern int unrealdb_write_int64(UnrealDB *c, uint64_t t); extern int unrealdb_write_int32(UnrealDB *c, uint32_t t); extern int unrealdb_write_int16(UnrealDB *c, uint16_t t); -extern int unrealdb_write_str(UnrealDB *c, char *x); +extern int unrealdb_write_str(UnrealDB *c, const char *x); extern int unrealdb_write_char(UnrealDB *c, char t); extern int unrealdb_read_int64(UnrealDB *c, uint64_t *t); extern int unrealdb_read_int32(UnrealDB *c, uint32_t *t); extern int unrealdb_read_int16(UnrealDB *c, uint16_t *t); extern int unrealdb_read_str(UnrealDB *c, char **x); extern int unrealdb_read_char(UnrealDB *c, char *t); -extern char *unrealdb_test_secret(char *name); +extern const char *unrealdb_test_secret(const char *name); extern UnrealDBConfig *unrealdb_copy_config(UnrealDBConfig *src); extern UnrealDBConfig *unrealdb_get_config(UnrealDB *db); extern void unrealdb_free_config(UnrealDBConfig *c); extern UnrealDBError unrealdb_get_error_code(void); -extern char *unrealdb_get_error_string(void); +extern const char *unrealdb_get_error_string(void); /* src/unrealdb.c end */ /* secret { } related stuff */ -extern Secret *find_secret(char *secret_name); +extern Secret *find_secret(const char *secret_name); extern void free_secret_cache(SecretCache *c); extern void free_secret(Secret *s); extern Secret *secrets; /* end */ -extern int check_password_strength(char *pass, int min_length, int strict, char **err); -extern int valid_secret_password(char *pass, char **err); +extern int check_password_strength(const char *pass, int min_length, int strict, char **err); +extern int valid_secret_password(const char *pass, char **err); extern int flood_limit_exceeded(Client *client, FloodOption opt); extern FloodSettings *find_floodsettings_block(const char *name); extern FloodSettings *get_floodsettings_for_user(Client *client, FloodOption opt); -extern MODVAR char *floodoption_names[]; -extern void flood_limit_exceeded_log(Client *client, char *floodname); +extern MODVAR const char *floodoption_names[]; +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 LogType log_type_stringtoval(const char *str); +extern const char *log_type_valtostring(LogType v); +#ifdef DEBUGMODE +#define unreal_log(...) do_unreal_log(__VA_ARGS__, log_data_source(__FILE__, __LINE__, __FUNCTION__), NULL) +#define unreal_log_raw(...) do_unreal_log_raw(__VA_ARGS__, log_data_source(__FILE__, __LINE__, __FUNCTION__), NULL) +#else +#define unreal_log(...) do_unreal_log(__VA_ARGS__, NULL) +#define unreal_log_raw(...) do_unreal_log_raw(__VA_ARGS__, NULL) +#endif +extern void do_unreal_log(LogLevel loglevel, const char *subsystem, const char *event_id, Client *client, const char *msg, ...) __attribute__((format(printf,5,0))); +extern void do_unreal_log_raw(LogLevel loglevel, const char *subsystem, const char *event_id, Client *client, const char *msg, ...); +extern void do_unreal_log_internal_from_remote(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, Client *from_server); +extern LogData *log_data_string(const char *key, const char *str); +extern LogData *log_data_char(const char *key, const char c); +extern LogData *log_data_integer(const char *key, int64_t integer); +extern LogData *log_data_timestamp(const char *key, time_t ts); +extern LogData *log_data_client(const char *key, Client *client); +extern LogData *log_data_channel(const char *key, Channel *channel); +extern LogData *log_data_source(const char *file, int line, const char *function); +extern LogData *log_data_socket_error(int fd); +extern LogData *log_data_link_block(ConfigItem_link *link); +extern LogData *log_data_tkl(const char *key, TKL *tkl); +extern LogData *log_data_tls_error(void); +extern void log_pre_rehash(void); +extern int log_tests(void); +extern void config_pre_run_log(void); +extern void log_blocks_switchover(void); +extern void postconf_defaults_log_block(void); +extern LogLevel log_level_stringtoval(const char *str); +extern const char *log_level_valtostring(LogLevel loglevel); +extern LogLevel log_level_stringtoval(const char *str); +extern int valid_event_id(const char *s); +extern int valid_subsystem(const char *s); +extern const char *timestamp_iso8601_now(void); +extern const char *timestamp_iso8601(time_t v); +extern int is_valid_snomask(char c); +extern int is_valid_snomask_string_testing(const char *str, char **wrong); +/* end of logging */ +extern void add_fake_lag(Client *client, long msec); +extern char *prefix_with_extban(const char *remainder, BanContext *b, Extban *extban, char *buf, size_t buflen); +extern GeoIPResult *geoip_client(Client *client); +extern GeoIPResult *geoip_lookup(const char *ip); +extern void free_geoip_result(GeoIPResult *r); +extern const char *get_operlogin(Client *client); +extern const char *get_operclass(Client *client); +/* url stuff */ +extern const char *unreal_mkcache(const char *url); +extern int has_cached_version(const char *url); +extern int url_is_valid(const char *); +extern const char *displayurl(const char *url); +extern char *url_getfilename(const char *url); +extern void download_file_async(const char *url, time_t cachetime, vFP callback, void *callback_data, char *original_url, int maxredirects); +extern void url_init(void); +extern EVENT(url_socket_timeout); +/* end of url stuff */ +extern char *collapse(char *pattern); +extern void clear_scache_hash_table(void); +extern void sendto_one(Client *, MessageTag *mtags, FORMAT_STRING(const char *), ...) __attribute__((format(printf,3,4))); +extern EVENT(garbage_collect); +extern EVENT(loop_event); +extern EVENT(check_pings); +extern EVENT(handshake_timeout); +extern EVENT(check_deadsockets); +extern EVENT(try_connections); +extern const char *my_itoa(int i); +extern void load_tunefile(void); +extern EVENT(save_tunefile); +extern void read_motd(const char *filename, MOTDFile *motd); +extern int target_limit_exceeded(Client *client, void *target, const char *name); +extern void make_umodestr(void); +extern void initwhowas(void); +extern void uid_init(void); +extern const char *uid_get(void); diff --git a/include/ircsprintf.h b/include/ircsprintf.h index fe78767..af30808 100644 --- a/include/ircsprintf.h +++ b/include/ircsprintf.h @@ -17,7 +17,7 @@ # define FORMAT_STRING(p) p #endif -extern char *ircvsnprintf(char *str, size_t size, const char *format, va_list); +extern char *ircvsnprintf(char *str, size_t size, const char *format, va_list) __attribute__((format(printf,3,0))); extern char *ircsnprintf(char *str, size_t size, FORMAT_STRING(const char *format), ...) __attribute__((format(printf,3,4))); #endif diff --git a/include/modules.h b/include/modules.h index 5bd22ec..62054cf 100644 --- a/include/modules.h +++ b/include/modules.h @@ -24,7 +24,7 @@ #define MAXCUSTOMHOOKS 30 #define MAXHOOKTYPES 150 #define MAXCALLBACKS 30 -#define MAXEFUNCTIONS 90 +#define MAXEFUNCTIONS 128 #if defined(_WIN32) #define MOD_EXTENSION "dll" #define DLLFUNC _declspec(dllexport) @@ -95,7 +95,6 @@ typedef enum ModuleObjectType { MOBJ_COMMAND = 3, MOBJ_HOOKTYPE = 4, MOBJ_VERSIONFLAG = 5, - MOBJ_SNOMASK = 6, MOBJ_UMODE = 7, MOBJ_COMMANDOVERRIDE = 8, MOBJ_EXTBAN = 9, @@ -110,23 +109,16 @@ typedef enum ModuleObjectType { MOBJ_HISTORY_BACKEND = 18, } ModuleObjectType; -typedef struct { - long mode; /**< Mode mask */ - char flag; /**< Mode character */ - int unset_on_deoper; /**< When set to 1 then this user mode will be unset on de-oper */ - int (*allowed)(Client *client, int what); /**< The 'is this user allowed to set this mode?' routine */ - char unloaded; /**< Internal flag to indicate module is being unloaded */ - Module *owner; /**< Module that owns this user mode */ -} Umode; - -typedef struct { - long mode; /**< Snomask mask */ - char flag; /**< Snomask character */ - int unset_on_deoper; /**< When set to 1 then this snomask will be unset on de-oper */ - int (*allowed)(Client *client, int what); /**< The 'is this user allowed to set this snomask?' routine */ - char unloaded; /**< Internal flag to indicate module is being unloaded */ - Module *owner; /**< Module that owns this snomask */ -} Snomask; +typedef struct Umode Umode; +struct Umode { + Umode *prev, *next; + long mode; /**< Mode mask */ + char letter; /**< Mode character */ + int unset_on_deoper; /**< When set to 1 then this user mode will be unset on de-oper */ + int (*allowed)(Client *client, int what); /**< The 'is this user allowed to set this mode?' routine */ + char unloaded; /**< Internal flag to indicate module is being unloaded */ + Module *owner; /**< Module that owns this user mode */ +}; typedef enum ModDataType { MODDATATYPE_LOCAL_VARIABLE = 1, @@ -138,6 +130,11 @@ typedef enum ModDataType { MODDATATYPE_MEMBERSHIP = 7, } ModDataType; +typedef enum ModDataSync { + MODDATA_SYNC_NORMAL = 1, /**< Sync normally via MD command */ + MODDATA_SYNC_EARLY = 2, /**< Attempt to (also) sync early in the UID command */ +} ModDataSync; + typedef struct ModDataInfo ModDataInfo; struct ModDataInfo { @@ -148,9 +145,11 @@ struct ModDataInfo { int slot; /**< Assigned slot */ char unloaded; /**< Module being unloaded? */ void (*free)(ModData *m); /**< Function will be called when the data needs to be freed (may be NULL if not using dynamic storage) */ - char *(*serialize)(ModData *m); /**< Function which converts the data to a string. May return NULL if 'm' contains no data (since for example m->ptr may be NULL). */ - void (*unserialize)(char *str, ModData *m); /**< Function which converts the string back to data */ - int sync; /**< Send in netsynch (when servers connect) */ + const char *(*serialize)(ModData *m); /**< Function which converts the data to a string. May return NULL if 'm' contains no data (since for example m->ptr may be NULL). */ + void (*unserialize)(const char *str, ModData *m); /**< Function which converts the string back to data */ + ModDataSync sync; /**< Send in netsynch (when servers connect) */ + int remote_write; /**< Allow remote servers to set/unset this moddata, even if it they target one of our own clients */ + int self_write; /**< Allow remote servers to set/unset moddata of their own server object (irc1.example.net writing the MD object of irc1.example.net) */ }; #define moddata_client(acptr, md) acptr->moddata[md->slot] @@ -191,6 +190,17 @@ typedef enum BypassChannelMessageRestrictionType { /** Channel mode bit/value */ typedef unsigned long Cmode_t; +typedef enum CmodeType { + CMODE_NORMAL=0, + CMODE_MEMBER=1, +} CmodeType; + +#define RANK_CHANOWNER 4000 +#define RANK_CHANADMIN 3000 +#define RANK_CHANOP 2000 +#define RANK_HALFOP 1000 +#define RANK_VOICE -1 + /** Channel mode handler. * This struct contains all extended channel mode information, * like the flag, mode, their handler functions, etc. @@ -199,15 +209,36 @@ typedef unsigned long Cmode_t; * and set the 'is_ok' function. All the rest is for parameter modes * or is optional. */ -typedef struct { - /** mode character (like 'Z') */ - char flag; +typedef struct Cmode Cmode; +struct Cmode { + Cmode *prev, *next; - /** unique flag (like 0x10) */ + /** mode character (like 'Z') */ + char letter; + + CmodeType type; + + /** If type is CMODE_NORMAL, then bitmask (eg: 0x10) that + * is used in channel->mode.mode + */ Cmode_t mode; + /** If type is CMODE_MEMBER, then the prefix used in NAMES etc (eg @) */ + char prefix; + + /** If type is CMODE_MEMBER, then the prefix used in SJOIN (eg @) */ + char sjoin_prefix; + + /** If type is CMODE_MEMBER, then the rank of this prefix. + * Higher ranking = more rights. + * This is used, for example, in NAMES without NAMESX when we can only + * show one symbol but not all. + * For the shipped modules vhoaq we use the RANK_* values. + */ + int rank; + /** Number of parameters (1 or 0) */ - int paracount; + int paracount; /** Check access or parameter of the channel mode. * @param client The client @@ -217,24 +248,24 @@ typedef struct { * @param what MODE_ADD or MODE_DEL * @returns EX_DENY, EX_ALLOW or EX_ALWAYS_DENY */ - int (*is_ok)(Client *client, Channel *channel, char mode, char *para, int checkt, int what); + int (*is_ok)(Client *client, Channel *channel, char mode, const char *para, int checkt, int what); /** Store parameter in memory for channel. * This function pointer is NULL (unused) for modes without parameters. - * @param list The list, this usually points to channel->mode.extmodeparams. + * @param list The list, this usually points to channel->mode.mode_params. * @param para The parameter to store. * @returns the head of the list, RTFS if you wonder why. * @note IMPORTANT: only allocate a new paramstruct if you need to. * Search for any current one first! Eg: in case of mode +y 5 and then +y 6 later without -y. */ - void *(*put_param)(void *list, char *para); + void *(*put_param)(void *list, const char *para); /** Get the stored parameter as a readable/printable string. * This function pointer is NULL (unused) for modes without parameters. * @param parastruct The parameter struct * @returns a pointer to the string (temporary storage) */ - char *(*get_param)(void *parastruct); + const char *(*get_param)(void *parastruct); /** Convert input parameter to output. * This converts stuff like where a MODE +l "1aaa" becomes "1". @@ -249,7 +280,7 @@ typedef struct { * In particular you MUST NOT SEND ERRORS to the client. * This should be done in is_ok() and not in conv_param(). */ - char *(*conv_param)(char *para, Client *client, Channel *channel); + const char *(*conv_param)(const char *para, Client *client, Channel *channel); /** Free and remove parameter from list. * This function pointer is NULL (unused) for modes without parameters. @@ -295,24 +326,28 @@ typedef struct { char unloaded; /** Slot number - Can be used instead of GETPARAMSLOT() */ - int slot; + int param_slot; /** Module owner */ Module *owner; -} Cmode; +}; /** The struct used to register a channel mode handler. * For documentation, see Cmode struct. */ typedef struct { - char flag; + char letter; + CmodeType type; + char prefix; + char sjoin_prefix; + int rank; int paracount; - int (*is_ok)(Client *,Channel *, char mode, char *para, int, int); - void * (*put_param)(void *, char *); - char * (*get_param)(void *); - char * (*conv_param)(char *, Client *, Channel *); + int (*is_ok)(Client *,Channel *, char mode, const char *para, int, int); + void * (*put_param)(void *, const char *); + const char * (*get_param)(void *); + const char * (*conv_param)(const char *, Client *, Channel *); void (*free_param)(void *); - void * (*dup_struct)(void *); + void * (*dup_struct)(void *); int (*sjoin_check)(Channel *, void *, void *); char local; char unset_with_param; @@ -328,7 +363,7 @@ typedef struct { #define GETPARAMHANDLERBYLETTER(x) ParamTable[GETPARAMSLOT(x)] /** Get paramter data struct - for like: ((aModejEntry *)GETPARASTRUCT(channel, 'j'))->t */ -#define GETPARASTRUCT(mychannel, mychar) channel->mode.extmodeparams[GETPARAMSLOT(mychar)] +#define GETPARASTRUCT(mychannel, mychar) channel->mode.mode_params[GETPARAMSLOT(mychar)] #define GETPARASTRUCTEX(v, mychar) v[GETPARAMSLOT(mychar)] @@ -341,16 +376,20 @@ typedef struct { /*** Extended bans ***/ -// TODO: These should be enums! +typedef enum ExtbanCheck { + EXBCHK_ACCESS=0, /**< Check access */ + EXBCHK_ACCESS_ERR=1, /**< Check access and send error */ + EXBCHK_PARAM=2 /**< Check if the parameter is valid */ +} ExtbanCheck; -#define EXBCHK_ACCESS 0 /* Check access */ -#define EXBCHK_ACCESS_ERR 1 /* Check access and send error */ -#define EXBCHK_PARAM 2 /* Check if the parameter is valid */ +typedef enum ExtbanType { + EXBTYPE_BAN=0, /**< Ban (channel mode +b) */ + EXBTYPE_EXCEPT=1, /**< Ban exception (channel mode +e) */ + EXBTYPE_INVEX=2, /**< Invite exception (channel mode +I) */ + EXBTYPE_TKL=3 /**< TKL or other generic matcher outside banning routines */ +} ExtbanType; -#define EXBTYPE_BAN 0 /* a ban */ -#define EXBTYPE_EXCEPT 1 /* an except */ -#define EXBTYPE_INVEX 2 /* an invite exception */ -#define EXBTYPE_TKL 3 /* TKL or other generic matcher outside banning routines */ +#define BCTX_CONV_OPTION_WRITE_LETTER_BANS 1 /* Always write letter extbans in output of conv_param */ #define EXTBANTABLESZ 32 @@ -363,54 +402,66 @@ typedef enum ExtbanOptions { } ExtbanOptions; typedef struct { - /** extbans module */ - Module *owner; + Client *client; /**< Client to check, can be a remote client */ + Channel *channel; /**< Channel to check */ + const char *banstr; /**< Mask string (ban) */ + int ban_check_types; /**< Ban types to check for, one or more of BANCHK_* OR'd together */ + const char *msg; /**< Message, only for some BANCHK_* types (for censoring text) */ + const char *error_msg; /**< Error message, can be NULL */ + int no_extbans; /**< Set to 1 to disable extended bans checking - only nick!user@host allowed */ + int what; /**< MODE_ADD or MODE_DEL (for is_ok) */ + ExtbanType ban_type; /**< EXBTYPE_BAN or EXBTYPE_EXCEPT (for is_ok) */ + ExtbanCheck is_ok_check;/**< One of EXBCHK_* (for is_ok) */ + int conv_options; /**< One of BCTX_CONV_OPTION_* (for conv_param) */ +} BanContext; + +typedef struct Extban Extban; + +struct Extban { + Extban *prev, *next; + /** extended ban character */ - char flag; + char letter; + + /** extended ban name */ + char *name; /** extban options */ ExtbanOptions options; - /** access checking [optional]. - * Client *: the client - * Channel *: the channel - * para: the ban parameter - * int: check type (see EXBCHK_*) - * int: what (MODE_ADD or MODE_DEL) - * int: what2 (EXBTYPE_BAN or EXBTYPE_EXCEPT) - * return value: 1=ok, 0=bad - * NOTE: just set this of NULL if you want only +hoaq to place/remove bans as usual. - * NOTE2: This has not been tested yet!! - */ - int (*is_ok)(Client *, Channel *, char *para, int, int, int); + unsigned int is_banned_events; /**< Which BANCHK_* events to listen on */ + + int (*is_ok)(BanContext *b); /** Convert input parameter to output [optional]. * like with normal bans '+b blah' gets '+b blah!*@*', and it allows - * you to limit the length of the ban too. You can set this to NULL however - * to use the value as-is. - * char *: the input parameter. + * you to limit the length of the ban too. * return value: pointer to output string (temp. storage) */ - char * (*conv_param)(char *); + const char *(*conv_param)(BanContext *b, Extban *handler); - /** Checks if the user is affected by this ban [required]. - * Called from is_banned. - * Client *: the client - * Channel *: the channel - * para: the ban entry - * int: a value of BANCHK_* (see struct.h) - * char **: optionally a message, can be NULL!! (for some BANCHK_ types) - * char **: optionally for setting an error message, can be NULL!! + /** Checks if the user is affected by this ban [optional]. + * This may be set to NULL if you have is_banned_events set to 0 (zero), + * this can be useful if you don't actually ban a user, eg for text bans. + * This function is called from is_banned() and two other places. */ - int (*is_banned)(Client *client, Channel *channel, char *para, int checktype, char **msg, char **errormsg); -} Extban; + int (*is_banned)(BanContext *b); + + /** extbans module */ + Module *owner; + + /* Set to 1 during rehash when module is unloading (which may be re-used, and then set to 0) */ + char unloaded; +}; typedef struct { - char flag; + char letter; + char *name; ExtbanOptions options; - int (*is_ok)(Client *, Channel *, char *para, int, int, int); - char * (*conv_param)(char *); - int (*is_banned)(Client *, Channel *, char *, int, char **, char **); + int (*is_ok)(BanContext *b); + const char *(*conv_param)(BanContext *b, Extban *handler); + int (*is_banned)(BanContext *b); + unsigned int is_banned_events; } ExtbanInfo; @@ -439,8 +490,8 @@ struct ClientCapability { char *name; /**< The name of the CAP */ long cap; /**< The acptr->user->proto we should set (if any, can be 0, like for sts) */ int flags; /**< A flag from CLICAP_FLAGS_* */ - int (*visible)(Client *); /**< Should the capability be visible? Note: parameter may be NULL. [optional] */ - char *(*parameter)(Client *); /**< CAP parameters. Note: parameter may be NULL. [optional] */ + int (*visible)(Client *); /**< Should the capability be visible? Note: parameter may be NULL. [optional] */ + const char *(*parameter)(Client *); /**< CAP parameters. Note: parameter may be NULL. [optional] */ MessageTagHandler *mtag_handler; /**< For reverse dependency */ Module *owner; /**< Module introducing this CAP. */ char unloaded; /**< Internal flag to indicate module is being unloaded */ @@ -450,7 +501,7 @@ typedef struct { char *name; int flags; int (*visible)(Client *); - char *(*parameter)(Client *); + const char *(*parameter)(Client *); } ClientCapabilityInfo; /** @defgroup MessagetagAPI Message tag API @@ -465,13 +516,13 @@ typedef struct { /** Message Tag Handler */ struct MessageTagHandler { MessageTagHandler *prev, *next; - char *name; /**< The name of the message-tag */ - int flags; /**< A flag of MTAG_HANDLER_FLAGS_* */ - int (*is_ok)(Client *, char *, char *); /**< Verify syntax and access rights */ - int (*can_send)(Client *); /**< Tag may be sent to this client (normally NULL!) */ - Module *owner; /**< Module introducing this CAP. */ - ClientCapability *clicap_handler; /**< Client capability handler associated with this */ - char unloaded; /**< Internal flag to indicate module is being unloaded */ + char *name; /**< The name of the message-tag */ + int flags; /**< A flag of MTAG_HANDLER_FLAGS_* */ + int (*is_ok)(Client *, const char *, const char *); /**< Verify syntax and access rights */ + int (*should_send_to_client)(Client *); /**< Tag may be sent to this client (normally NULL!) */ + Module *owner; /**< Module introducing this CAP. */ + ClientCapability *clicap_handler; /**< Client capability handler associated with this */ + char unloaded; /**< Internal flag to indicate module is being unloaded */ }; /** The struct used to register a message tag handler. @@ -480,8 +531,8 @@ struct MessageTagHandler { typedef struct { char *name; int flags; - int (*is_ok)(Client *, char *, char *); - int (*can_send)(Client *); + int (*is_ok)(Client *, const char *, const char *); + int (*should_send_to_client)(Client *); ClientCapability *clicap_handler; } MessageTagHandlerInfo; @@ -531,10 +582,10 @@ typedef struct HistoryBackend HistoryBackend; struct HistoryBackend { HistoryBackend *prev, *next; char *name; /**< The name of the history backend (eg: "mem") */ - int (*history_set_limit)(char *object, int max_lines, long max_time); /**< Impose a limit on a history object */ - int (*history_add)(char *object, MessageTag *mtags, char *line); /**< Add to history */ - HistoryResult *(*history_request)(char *object, HistoryFilter *filter); /**< Request history */ - int (*history_destroy)(char *object); /**< Destroy history of this object completely */ + int (*history_set_limit)(const char *object, int max_lines, long max_time); /**< Impose a limit on a history object */ + int (*history_add)(const char *object, MessageTag *mtags, const char *line); /**< Add to history */ + HistoryResult *(*history_request)(const char *object, HistoryFilter *filter); /**< Request history */ + int (*history_destroy)(const char *object); /**< Destroy history of this object completely */ Module *owner; /**< Module introducing this */ char unloaded; /**< Internal flag to indicate module is being unloaded */ }; @@ -544,10 +595,10 @@ struct HistoryBackend { */ typedef struct { char *name; - int (*history_set_limit)(char *object, int max_lines, long max_time); - int (*history_add)(char *object, MessageTag *mtags, char *line); - HistoryResult *(*history_request)(char *object, HistoryFilter *filter); - int (*history_destroy)(char *object); + int (*history_set_limit)(const char *object, int max_lines, long max_time); + int (*history_add)(const char *object, MessageTag *mtags, const char *line); + HistoryResult *(*history_request)(const char *object, HistoryFilter *filter); + int (*history_destroy)(const char *object); } HistoryBackendInfo; struct Hook { @@ -557,7 +608,8 @@ struct Hook { union { int (*intfunc)(); void (*voidfunc)(); - char *(*pcharfunc)(); + char *(*stringfunc)(); + const char *(*conststringfunc)(); } func; Module *owner; }; @@ -568,7 +620,9 @@ struct Callback { union { int (*intfunc)(); void (*voidfunc)(); - char *(*pcharfunc)(); + void *(*pvoidfunc)(); + char *(*stringfunc)(); + const char *(*conststringfunc)(); } func; Module *owner; char willberemoved; /* will be removed on next rehash? (eg the 'old'/'current' one) */ @@ -589,7 +643,8 @@ struct Efunction { int (*intfunc)(); void (*voidfunc)(); void *(*pvoidfunc)(); - char *(*pcharfunc)(); + char *(*stringfunc)(); + const char *(*conststringfunc)(); } func; Module *owner; char willberemoved; /* will be removed on next rehash? (eg the 'old'/'current' one) */ @@ -618,7 +673,6 @@ typedef struct ModuleObject { Command *command; Hooktype *hooktype; Versionflag *versionflag; - Snomask *snomask; Umode *umode; CommandOverride *cmdoverride; Extban *extban; @@ -717,31 +771,29 @@ extern MODVAR Hooktype Hooktypes[MAXCUSTOMHOOKS]; extern MODVAR Callback *Callbacks[MAXCALLBACKS], *RCallbacks[MAXCALLBACKS]; extern MODVAR ClientCapability *clicaps; -extern Event *EventAdd(Module *module, char *name, vFP event, void *data, long every_msec, int count); +extern Event *EventAdd(Module *module, const char *name, vFP event, void *data, long every_msec, int count); extern void EventDel(Event *event); extern Event *EventMarkDel(Event *event); -extern Event *EventFind(char *name); +extern Event *EventFind(const char *name); extern int EventMod(Event *event, EventInfo *mods); extern void DoEvents(void); extern void EventStatus(Client *client); extern void SetupEvents(void); -extern void Module_Init(void); -extern char *Module_Create(char *path); -extern char *Module_TransformPath(char *path_); -extern void Init_all_testing_modules(void); -extern void Unload_all_loaded_modules(void); -extern void Unload_all_testing_modules(void); -extern int Module_Unload(char *name); -extern vFP Module_Sym(char *name); -extern vFP Module_SymX(char *name, Module **mptr); -extern int Module_free(Module *mod); - +extern void Module_Init(void); +extern const char *Module_Create(const char *path); +extern const char *Module_TransformPath(const char *path_); +extern void Init_all_testing_modules(void); +extern void Unload_all_loaded_modules(void); +extern void Unload_all_testing_modules(void); +extern int Module_Unload(const char *name); +extern vFP Module_Sym(const char *name); +extern vFP Module_SymX(const char *name, Module **mptr); +extern int Module_free(Module *mod); #ifdef __OpenBSD__ -extern void *obsd_dlsym(void *handle, char *symbol); +extern void *obsd_dlsym(void *handle, const char *symbol); #endif - #ifdef _WIN32 extern const char *our_dlerror(void); #endif @@ -771,179 +823,115 @@ extern HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mre extern void HistoryBackendDel(HistoryBackend *m); #ifndef GCC_TYPECHECKING -#define HookAdd(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, func, NULL, NULL) -#define HookAddVoid(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, func, NULL) -#define HookAddPChar(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, NULL, func) +#define HookAdd(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, func, NULL, NULL, NULL) +#define HookAddVoid(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, func, NULL, NULL) +#define HookAddString(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, NULL, func, NULL) +#define HookAddConstString(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, NULL, NULL, func) #else #define HookAdd(module, hooktype, priority, func) \ __extension__ ({ \ ValidateHooks(hooktype, func); \ - HookAddMain(module, hooktype, priority, func, NULL, NULL); \ + HookAddMain(module, hooktype, priority, func, NULL, NULL, NULL); \ }) #define HookAddVoid(module, hooktype, priority, func) \ __extension__ ({ \ ValidateHooks(hooktype, func); \ - HookAddMain(module, hooktype, priority, NULL, func, NULL); \ + HookAddMain(module, hooktype, priority, NULL, func, NULL, NULL); \ }) -#define HookAddPChar(module, hooktype, priority, func) \ +#define HookAddString(module, hooktype, priority, func) \ __extension__ ({ \ ValidateHooks(hooktype, func); \ - HookAddMain(module, hooktype, priority, NULL, NULL, func); \ + HookAddMain(module, hooktype, priority, NULL, NULL, func, NULL); \ +}) +#define HookAddConstString(module, hooktype, priority, func) \ +__extension__ ({ \ + ValidateHooks(hooktype, func); \ + HookAddMain(module, hooktype, priority, NULL, NULL, NULL, func); \ }) #endif /* GCC_TYPCHECKING */ -extern Hook *HookAddMain(Module *module, int hooktype, int priority, int (*intfunc)(), void (*voidfunc)(), char *(*pcharfunc)()); +extern Hook *HookAddMain(Module *module, int hooktype, int priority, int (*intfunc)(), void (*voidfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()); extern Hook *HookDel(Hook *hook); -extern Hooktype *HooktypeAdd(Module *module, char *string, int *type); +extern Hooktype *HooktypeAdd(Module *module, const char *string, int *type); extern void HooktypeDel(Hooktype *hooktype, Module *module); -#define RunHook0(hooktype) do { Hook *h; for (h = Hooks[hooktype]; h; h = h->next)(*(h->func.intfunc))(); } while(0) -#define RunHook(hooktype,x) do { Hook *h; for (h = Hooks[hooktype]; h; h = h->next) (*(h->func.intfunc))(x); } while(0) -#define RunHookReturn(hooktype,x,retchk) \ +#define RunHook(hooktype,...) do { Hook *h; for (h = Hooks[hooktype]; h; h = h->next) (*(h->func.intfunc))(__VA_ARGS__); } while(0) +#define RunHookReturn(hooktype,retchk,...) \ { \ int retval; \ Hook *h; \ for (h = Hooks[hooktype]; h; h = h->next) \ { \ - retval = (*(h->func.intfunc))(x); \ + retval = (*(h->func.intfunc))(__VA_ARGS__); \ if (retval retchk) return; \ } \ } -#define RunHookReturn2(hooktype,x,y,retchk) \ +#define RunHookReturnInt(hooktype,retchk,...) \ { \ int retval; \ Hook *h; \ for (h = Hooks[hooktype]; h; h = h->next) \ { \ - retval = (*(h->func.intfunc))(x,y); \ - if (retval retchk) return; \ - } \ -} -#define RunHookReturn3(hooktype,x,y,z,retchk) \ -{ \ - int retval; \ - Hook *h; \ - for (h = Hooks[hooktype]; h; h = h->next) \ - { \ - retval = (*(h->func.intfunc))(x,y,z); \ - if (retval retchk) return; \ - } \ -} -#define RunHookReturn4(hooktype,a,b,c,d,retchk) \ -{ \ - int retval; \ - Hook *h; \ - for (h = Hooks[hooktype]; h; h = h->next) \ - { \ - retval = (*(h->func.intfunc))(a,b,c,d); \ - if (retval retchk) return; \ - } \ -} -#define RunHookReturnInt(hooktype,x,retchk) \ -{ \ - int retval; \ - Hook *h; \ - for (h = Hooks[hooktype]; h; h = h->next) \ - { \ - retval = (*(h->func.intfunc))(x); \ - if (retval retchk) return retval; \ - } \ -} -#define RunHookReturnInt2(hooktype,x,y,retchk) \ -{ \ - int retval; \ - Hook *h; \ - for (h = Hooks[hooktype]; h; h = h->next) \ - { \ - retval = (*(h->func.intfunc))(x,y); \ - if (retval retchk) return retval; \ - } \ -} -#define RunHookReturnInt3(hooktype,x,y,z,retchk) \ -{ \ - int retval; \ - Hook *h; \ - for (h = Hooks[hooktype]; h; h = h->next) \ - { \ - retval = (*(h->func.intfunc))(x,y,z); \ - if (retval retchk) return retval; \ - } \ -} -#define RunHookReturnInt4(hooktype,a,b,c,d,retchk) \ -{ \ - int retval; \ - Hook *h; \ - for (h = Hooks[hooktype]; h; h = h->next) \ - { \ - retval = (*(h->func.intfunc))(a,b,c,d); \ + retval = (*(h->func.intfunc))(__VA_ARGS__); \ if (retval retchk) return retval; \ } \ } -#define RunHookReturnVoid(hooktype,x,ret) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) if((*(hook->func.intfunc))(x) ret) return; } while(0) -#define RunHook2(hooktype,x,y) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) (*(hook->func.intfunc))(x,y); } while(0) -#define RunHook3(hooktype,a,b,c) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) (*(hook->func.intfunc))(a,b,c); } while(0) -#define RunHook4(hooktype,a,b,c,d) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) (*(hook->func.intfunc))(a,b,c,d); } while(0) -#define RunHook5(hooktype,a,b,c,d,e) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) (*(hook->func.intfunc))(a,b,c,d,e); } while(0) -#define RunHook6(hooktype,a,b,c,d,e,f) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) (*(hook->func.intfunc))(a,b,c,d,e,f); } while(0) -#define RunHook7(hooktype,a,b,c,d,e,f,g) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) (*(hook->func.intfunc))(a,b,c,d,e,f,g); } while(0) -#define RunHook8(hooktype,a,b,c,d,e,f,g,h) do { Hook *hook; for (hook = Hooks[hooktype]; hook; hook = hook->next) (*(hook->func.intfunc))(a,b,c,d,e,f,g,h); } while(0) +#define CallbackAdd(module, cbtype, func) CallbackAddMain(module, cbtype, func, NULL, NULL, NULL, NULL) +#define CallbackAddVoid(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, func, NULL, NULL, NULL) +#define CallbackAddPVoid(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, NULL, func, NULL, NULL) +#define CallbackAddString(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, NULL, NULL, func, NULL) +#define CallbackAddConstString(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, NULL, NULL, NULL, func) -#define CallbackAdd(cbtype, func) CallbackAddMain(NULL, cbtype, func, NULL, NULL) -#define CallbackAddEx(module, cbtype, func) CallbackAddMain(module, cbtype, func, NULL, NULL) -#define CallbackAddVoid(cbtype, func) CallbackAddMain(NULL, cbtype, NULL, func, NULL) -#define CallbackAddVoidEx(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, func, NULL) -#define CallbackAddPChar(cbtype, func) CallbackAddMain(NULL, cbtype, NULL, NULL, func) -#define CallbackAddPCharEx(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, NULL, func) - -extern Callback *CallbackAddMain(Module *module, int cbtype, int (*intfunc)(), void (*voidfunc)(), char *(*pcharfunc)()); +extern Callback *CallbackAddMain(Module *module, int cbtype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()); extern Callback *CallbackDel(Callback *cb); -#define EfunctionAdd(module, cbtype, func) EfunctionAddMain(module, cbtype, func, NULL, NULL, NULL) -#define EfunctionAddVoid(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, func, NULL, NULL) -#define EfunctionAddPVoid(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, func, NULL) -#define EfunctionAddPChar(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, NULL, func) +#define EfunctionAdd(module, cbtype, func) EfunctionAddMain(module, cbtype, func, NULL, NULL, NULL, NULL) +#define EfunctionAddVoid(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, func, NULL, NULL, NULL) +#define EfunctionAddPVoid(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, func, NULL, NULL) +#define EfunctionAddString(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, NULL, func, NULL) +#define EfunctionAddConstString(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, NULL, NULL, func) -extern Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*intfunc)(), void (*voidfunc)(), void *(*pvoidfunc)(), char *(*pcharfunc)()); +extern Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*intfunc)(), void (*voidfunc)(), void *(*pvoidfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()); extern Efunction *EfunctionDel(Efunction *cb); -extern Command *CommandAdd(Module *module, char *cmd, CmdFunc func, unsigned char params, int flags); -extern Command *AliasAdd(Module *module, char *cmd, AliasCmdFunc aliasfunc, unsigned char params, int flags); +extern Command *CommandAdd(Module *module, const char *cmd, CmdFunc func, unsigned char params, int flags); +extern Command *AliasAdd(Module *module, const char *cmd, AliasCmdFunc aliasfunc, unsigned char params, int flags); extern void CommandDel(Command *command); extern void CommandDelX(Command *command, RealCommand *cmd); -extern int CommandExists(char *name); -extern CommandOverride *CommandOverrideAdd(Module *module, char *cmd, OverrideCmdFunc func); -extern CommandOverride *CommandOverrideAddEx(Module *module, char *name, int priority, OverrideCmdFunc func); +extern int CommandExists(const char *name); +extern CommandOverride *CommandOverrideAdd(Module *module, const char *name, int priority, OverrideCmdFunc func); extern void CommandOverrideDel(CommandOverride *ovr); -extern void CallCommandOverride(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, char *parv[]); +extern void CallCommandOverride(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, const char *parv[]); extern void moddata_free_client(Client *acptr); extern void moddata_free_local_client(Client *acptr); extern void moddata_free_channel(Channel *channel); extern void moddata_free_member(Member *m); extern void moddata_free_membership(Membership *m); -extern ModDataInfo *findmoddata_byname(char *name, ModDataType type); -extern int moddata_client_set(Client *acptr, char *varname, char *value); -extern char *moddata_client_get(Client *acptr, char *varname); -extern int moddata_local_client_set(Client *acptr, char *varname, char *value); -extern char *moddata_local_client_get(Client *acptr, char *varname); +extern ModDataInfo *findmoddata_byname(const char *name, ModDataType type); +extern int moddata_client_set(Client *acptr, const char *varname, const char *value); +extern const char *moddata_client_get(Client *acptr, const char *varname); +extern ModData *moddata_client_get_raw(Client *client, const char *varname); +extern int moddata_local_client_set(Client *acptr, const char *varname, const char *value); +extern const char *moddata_local_client_get(Client *acptr, const char *varname); -extern int LoadPersistentPointerX(ModuleInfo *modinfo, char *varshortname, void **var, void (*free_variable)(ModData *m)); +extern int LoadPersistentPointerX(ModuleInfo *modinfo, const char *varshortname, void **var, void (*free_variable)(ModData *m)); #define LoadPersistentPointer(modinfo, var, free_variable) LoadPersistentPointerX(modinfo, #var, (void **)&var, free_variable) -extern void SavePersistentPointerX(ModuleInfo *modinfo, char *varshortname, void *var); +extern void SavePersistentPointerX(ModuleInfo *modinfo, const char *varshortname, void *var); #define SavePersistentPointer(modinfo, var) SavePersistentPointerX(modinfo, #var, var) -extern int LoadPersistentIntX(ModuleInfo *modinfo, char *varshortname, int *var); +extern int LoadPersistentIntX(ModuleInfo *modinfo, const char *varshortname, int *var); #define LoadPersistentInt(modinfo, var) LoadPersistentIntX(modinfo, #var, &var) -extern void SavePersistentIntX(ModuleInfo *modinfo, char *varshortname, int var); +extern void SavePersistentIntX(ModuleInfo *modinfo, const char *varshortname, int var); #define SavePersistentInt(modinfo, var) SavePersistentIntX(modinfo, #var, var) -extern int LoadPersistentLongX(ModuleInfo *modinfo, char *varshortname, long *var); +extern int LoadPersistentLongX(ModuleInfo *modinfo, const char *varshortname, long *var); #define LoadPersistentLong(modinfo, var) LoadPersistentLongX(modinfo, #var, &var) -extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long var); +extern void SavePersistentLongX(ModuleInfo *modinfo, const char *varshortname, long var); #define SavePersistentLong(modinfo, var) SavePersistentLongX(modinfo, #var, var) /** Hooks trigger on "events", such as a new user connecting or joining a channel, @@ -1121,8 +1109,6 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va #define HOOKTYPE_CHANNEL_SYNCED 83 /** See hooktype_can_sajoin() */ #define HOOKTYPE_CAN_SAJOIN 84 -/** See hooktype_check_init() */ -#define HOOKTYPE_CHECK_INIT 85 /** See hooktype_mode_deop() */ #define HOOKTYPE_MODE_DEOP 86 /** See hooktype_dcc_denied() */ @@ -1131,8 +1117,6 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va #define HOOKTYPE_SECURE_CONNECT 88 /** See hooktype_can_bypass_channel_message_restriction() */ #define HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION 89 -/** See hooktype_require_sasl() */ -#define HOOKTYPE_REQUIRE_SASL 90 /** See hooktype_sasl_continuation() */ #define HOOKTYPE_SASL_CONTINUATION 91 /** See hooktype_sasl_result() */ @@ -1161,6 +1145,18 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va #define HOOKTYPE_CLOSE_CONNECTION 103 /** See hooktype_connect_extinfo() */ #define HOOKTYPE_CONNECT_EXTINFO 104 +/** See hooktype_is_invited() */ +#define HOOKTYPE_IS_INVITED 105 +/** See hooktype_post_local_nickchange() */ +#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_can_set_topic() */ +#define HOOKTYPE_CAN_SET_TOPIC 110 /* Adding a new hook here? * 1) Add the #define HOOKTYPE_.... with a new number * 2) Add a hook prototype (see below) @@ -1193,7 +1189,7 @@ int hooktype_remote_connect(Client *client); * @param client The quit/disconnect reason * @return The quit reason (you may also return 'comment' if it should be unchanged) or NULL for an empty reason. */ -char *hooktype_pre_local_quit(Client *client, char *comment); +const char *hooktype_pre_local_quit(Client *client, const char *comment); /** Called when a local user quits or otherwise disconnects (function prototype for HOOKTYPE_PRE_LOCAL_QUIT). * @param client The client @@ -1201,7 +1197,7 @@ char *hooktype_pre_local_quit(Client *client, char *comment); * @param comment The quit/exit reason * @return The return value is ignored (use return 0) */ -int hooktype_local_quit(Client *client, MessageTag *mtags, char *comment); +int hooktype_local_quit(Client *client, MessageTag *mtags, const char *comment); /** Called when a remote user qutis or otherwise disconnects (function prototype for HOOKTYPE_REMOTE_QUIT). * @param client The client @@ -1209,7 +1205,7 @@ int hooktype_local_quit(Client *client, MessageTag *mtags, char *comment); * @param comment The quit/exit reason * @return The return value is ignored (use return 0) */ -int hooktype_remote_quit(Client *client, MessageTag *mtags, char *comment); +int hooktype_remote_quit(Client *client, MessageTag *mtags, const char *comment); /** Called when an unregistered user disconnects, so before the user was fully online (function prototype for HOOKTYPE_UNKUSER_QUIT). * @param client The client @@ -1217,7 +1213,7 @@ int hooktype_remote_quit(Client *client, MessageTag *mtags, char *comment); * @param comment The quit/exit reason * @return The return value is ignored (use return 0) */ -int hooktype_unkuser_quit(Client *client, MessageTag *mtags, char *comment); +int hooktype_unkuser_quit(Client *client, MessageTag *mtags, const char *comment); /** Called when a local or remote server connects / links in (function prototype for HOOKTYPE_SERVER_CONNECT). * @param client The client @@ -1262,7 +1258,7 @@ int hooktype_server_quit(Client *client, MessageTag *mtags); * @param newnick The new nick name * @return The return value is ignored (use return 0) */ -int hooktype_local_nickchange(Client *client, MessageTag *mtags, char *newnick); +int hooktype_local_nickchange(Client *client, MessageTag *mtags, const char *newnick); /** Called when a remote user changes the nick name (function prototype for HOOKTYPE_REMOTE_NICKCHANGE). * @param client The client @@ -1270,45 +1266,42 @@ int hooktype_local_nickchange(Client *client, MessageTag *mtags, char *newnick); * @param newnick The new nick name * @return The return value is ignored (use return 0) */ -int hooktype_remote_nickchange(Client *client, MessageTag *mtags, char *newnick); +int hooktype_remote_nickchange(Client *client, MessageTag *mtags, const char *newnick); /** Called when a user wants to join a channel, may the user join? (function prototype for HOOKTYPE_CAN_JOIN). * @param client The client * @param channel The channel the user wants to join * @param key The key supplied by the client - * @param parv The parameters from the JOIN. Normally you should not use this. * @return Return 0 to allow the user, any other value should be an IRC numeric (eg: ERR_BANNEDFROMCHAN). */ -int hooktype_can_join(Client *client, Channel *channel, char *key, char *parv[]); +int hooktype_can_join(Client *client, Channel *channel, const char *key, char **errmsg); -/** Called when a user wants to join a channel, may the user join? (function prototype for HOOKTYPE_PRE_LOCAL_JOIN). - * FIXME: It's not entirely clear why we have both hooktype_can_join() and hooktype_pre_local_join(). +/** Called when a user wants to join a channel (function prototype for HOOKTYPE_PRE_LOCAL_JOIN). + * IMPORTANT: Generally you want to use HOOKTYPE_CAN_JOIN / hooktype_can_join() instead!! * @param client The client * @param channel The channel the user wants to join - * @param parv The parameters from the JOIN. May contain channel key in parv[2]. + * @param key Channel key (can be NULL) * @retval HOOK_DENY Deny the join. * @retval HOOK_ALLOW Allow the join (stop processing other modules) * @retval HOOK_CONTINUE Allow the join, unless another module blocks it. */ -int hooktype_pre_local_join(Client *client, Channel *channel, char *parv[]); +int hooktype_pre_local_join(Client *client, Channel *channel, const char *key); /** Called when a local user joins a channel (function prototype for HOOKTYPE_LOCAL_JOIN). * @param client The client * @param channel The channel the user wants to join * @param mtags Message tags associated with the event - * @param parv The parameters from the JOIN. May contain channel key in parv[2]. * @return The return value is ignored (use return 0) */ -int hooktype_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); +int hooktype_local_join(Client *client, Channel *channel, MessageTag *mtags); /** Called when a remote user joins a channel (function prototype for HOOKTYPE_REMOTE_JOIN). * @param client The client * @param channel The channel the user wants to join * @param mtags Message tags associated with the event - * @param parv The parameters from the JOIN. May contain channel key in parv[2]. * @return The return value is ignored (use return 0) */ -int hooktype_remote_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); +int hooktype_remote_join(Client *client, Channel *channel, MessageTag *mtags); /** Called when a local user wants to part a channel (function prototype for HOOKTYPE_PRE_LOCAL_PART). * @param client The client @@ -1316,7 +1309,7 @@ int hooktype_remote_join(Client *client, Channel *channel, MessageTag *mtags, ch * @param comment The PART reason, this may be NULL. * @return The part reason (you may also return 'comment' if it should be unchanged) or NULL for an empty reason. */ -char *hooktype_pre_local_part(Client *client, Channel *channel, char *comment); +const char *hooktype_pre_local_part(Client *client, Channel *channel, const char *comment); /** Called when a local user parts a channel (function prototype for HOOKTYPE_LOCAL_PART). * @param client The client @@ -1325,7 +1318,7 @@ char *hooktype_pre_local_part(Client *client, Channel *channel, char *comment); * @param comment The PART reason, this may be NULL. * @return The return value is ignored (use return 0) */ -int hooktype_local_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); +int hooktype_local_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment); /** Called when a remote user parts a channel (function prototype for HOOKTYPE_REMOTE_PART). * @param client The client @@ -1334,25 +1327,25 @@ int hooktype_local_part(Client *client, Channel *channel, MessageTag *mtags, cha * @param comment The PART reason, this may be NULL. * @return The return value is ignored (use return 0) */ -int hooktype_remote_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); +int hooktype_remote_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment); /** Do not use this function, use hooktype_can_kick() instead! */ -char *hooktype_pre_local_kick(Client *client, Client *victim, Channel *channel, char *comment); +const char *hooktype_pre_local_kick(Client *client, Client *victim, Channel *channel, const char *comment); /** Called when a local user wants to kick another user from a channel (function prototype for HOOKTYPE_CAN_KICK). * @param client The client issuing the command * @param victim The victim that should be kicked * @param channel The channel the user should be kicked from * @param comment The KICK reason, this may be NULL. - * @param client_flags The access flags of 'client', one of CHFL_*, eg CHFL_CHANOP. - * @param victim_flags The access flags of 'victim', one of CHFL_*, eg CHFL_VOICE. - * @param error The error message that should be shown to the user (full IRC protocol line). + * @param client_member_modes The member modes of 'client' (eg "o"), never NULL but can be empty. + * @param victim_member_modes The member modes of 'victim' (eg "v"), never NULL but can be empty. + * @param errmsg The error message that should be shown to the user (full IRC protocol line). * @retval EX_DENY Deny the KICK (unless IRCOp with sufficient override rights). * @retval EX_ALWAYS_DENY Deny the KICK always (even if IRCOp). * @retval EX_ALLOW Allow the kick, unless another module blocks it. */ -int hooktype_can_kick(Client *client, Client *victim, Channel *channel, char *comment, long client_flags, long victim_flags, char **error); +int hooktype_can_kick(Client *client, Client *victim, Channel *channel, const char *comment, const char *client_member_modes, const char *victim_member_modes, const char **errmsg); /** Called when a local user is kicked (function prototype for HOOKTYPE_LOCAL_KICK). * @param client The client issuing the command @@ -1362,7 +1355,7 @@ int hooktype_can_kick(Client *client, Client *victim, Channel *channel, char *co * @param comment The KICK reason, this may be NULL. * @return The return value is ignored (use return 0) */ -int hooktype_local_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment); +int hooktype_local_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, const char *comment); /** Called when a remote user is kicked (function prototype for HOOKTYPE_REMOTE_KICK). * @param client The client issuing the command @@ -1372,7 +1365,7 @@ int hooktype_local_kick(Client *client, Client *victim, Channel *channel, Messag * @param comment The KICK reason, this may be NULL. * @return The return value is ignored (use return 0) */ -int hooktype_remote_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment); +int hooktype_remote_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, const char *comment); /** Called right before a message is sent to the channel (function prototype for HOOKTYPE_PRE_CHANMSG). * This function is only used by delayjoin. It cannot block a message. See hooktype_can_send_to_user() instead! @@ -1382,7 +1375,7 @@ int hooktype_remote_kick(Client *client, Client *victim, Channel *channel, Messa * @param text The text that will be sent * @return The return value is ignored (use return 0) */ -int hooktype_pre_chanmsg(Client *client, Channel *channel, MessageTag *mtags, char *text, SendType sendtype); +int hooktype_pre_chanmsg(Client *client, Channel *channel, MessageTag *mtags, const char *text, SendType sendtype); /** Called when a user wants to send a message to another user (function prototype for HOOKTYPE_CAN_SEND_TO_USER). * @param client The sender @@ -1393,7 +1386,7 @@ int hooktype_pre_chanmsg(Client *client, Channel *channel, MessageTag *mtags, ch * @retval HOOK_DENY Deny the message. The 'errmsg' will be sent to the user. * @retval HOOK_CONTINUE Allow the message, unless other modules block it. */ -int hooktype_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); +int hooktype_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); /** Called when a user wants to send a message to a channel (function prototype for HOOKTYPE_CAN_SEND_TO_CHANNEL). * @param client The sender @@ -1405,7 +1398,7 @@ int hooktype_can_send_to_user(Client *client, Client *target, char **text, char * @retval HOOK_DENY Deny the message. The 'errmsg' will be sent to the user. * @retval HOOK_CONTINUE Allow the message, unless other modules block it. */ -int hooktype_can_send_to_channel(Client *client, Channel *channel, Membership *member, char **text, char **errmsg, SendType sendtype); +int hooktype_can_send_to_channel(Client *client, Channel *channel, Membership *member, const char **text, const char **errmsg, SendType sendtype); /** Called when a message is sent from one user to another user (function prototype for HOOKTYPE_USERMSG). * @param client The sender @@ -1415,20 +1408,31 @@ int hooktype_can_send_to_channel(Client *client, Channel *channel, Membership *m * @param sendtype The message type, for example SEND_TYPE_PRIVMSG. * @return The return value is ignored (use return 0) */ -int hooktype_usermsg(Client *client, Client *to, MessageTag *mtags, char *text, SendType sendtype); +int hooktype_usermsg(Client *client, Client *to, MessageTag *mtags, const char *text, SendType sendtype); /** Called when a message is sent to a channel (function prototype for HOOKTYPE_CHANMSG). * @param client The sender * @param channel The channel * @param sendflags One of SEND_* (eg SEND_ALL, SKIP_DEAF). - * @param prefix Either zero, one or a combination of PREFIX_*. + * @param member_modes Either NULL, or a member mode like "h", "o", etc. * @param target Target string, usually this is "#channel", but it can also contain prefixes like "@#channel" * @param mtags Message tags associated with the event * @param text The text * @param sendtype The message type, for example SEND_TYPE_PRIVMSG. * @return The return value is ignored (use return 0) */ -int hooktype_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); +int hooktype_chanmsg(Client *client, Channel *channel, int sendflags, const char *member_modes, const char *target, MessageTag *mtags, const char *text, SendType sendtype); + +/** Called when a user wants to set the topic (function prototype for HOOKTYPE_CAN_SET_TOPIC). + * @param client The client issuing the command + * @param channel The channel the topic should be set for + * @param topic The topic that should be set, this may be NULL for unset. + * @param errmsg The error message that should be shown to the user (full IRC protocol line). + * @retval EX_DENY Deny the TOPIC (unless IRCOp with sufficient override rights). + * @retval EX_ALWAYS_DENY Deny the TOPIC always (even if IRCOp). + * @retval EX_ALLOW Allow the TOPIC, unless another module blocks it. + */ +int hooktype_can_set_topic(Client *client, Channel *channel, const char *topic, const char **errmsg); /** Called when a local user wants to change the channel topic (function prototype for HOOKTYPE_PRE_LOCAL_TOPIC). * @param client The client @@ -1436,7 +1440,7 @@ int hooktype_chanmsg(Client *client, Channel *channel, int sendflags, int prefix * @param topic The new requested topic * @return The new topic (you may also return 'topic'), or NULL if the topic change request should be rejected. */ -char *hooktype_pre_local_topic(Client *client, Channel *channel, char *topic); +const char *hooktype_pre_local_topic(Client *client, Channel *channel, const char *topic); /** Called when the channel topic is changed (function prototype for HOOKTYPE_TOPIC). * @param client The client @@ -1445,7 +1449,7 @@ char *hooktype_pre_local_topic(Client *client, Channel *channel, char *topic); * @param topic The new topic * @return The return value is ignored (use return 0) */ -int hooktype_topic(Client *client, Channel *channel, MessageTag *mtags, char *topic); +int hooktype_topic(Client *client, Channel *channel, MessageTag *mtags, const char *topic); /** Called when a local user changes channel modes, called early (function prototype for HOOKTYPE_PRE_LOCAL_CHANMODE). * WARNING: This does not allow you to stop or reject the channel modes. It only allows you to do stuff -before- the @@ -1459,7 +1463,7 @@ int hooktype_topic(Client *client, Channel *channel, MessageTag *mtags, char *to * @param samode Is this an SAMODE? * @return The return value is ignored (use return 0) */ -int hooktype_pre_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); +int hooktype_pre_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode); /** Called when a remote user changes channel modes, called early (function prototype for HOOKTYPE_PRE_REMOTE_CHANMODE). * WARNING: This does not allow you to stop or reject the channel modes. It only allows you to do stuff -before- the @@ -1473,7 +1477,7 @@ int hooktype_pre_local_chanmode(Client *client, Channel *channel, MessageTag *mt * @param samode Is this an SAMODE? * @return The return value is ignored (use return 0) */ -int hooktype_pre_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); +int hooktype_pre_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode); /** Called when a local user changes channel modes (function prototype for HOOKTYPE_LOCAL_CHANMODE). * @param client The client @@ -1483,9 +1487,10 @@ int hooktype_pre_remote_chanmode(Client *client, Channel *channel, MessageTag *m * @param parabuf The parameter buffer, for example "NiceOp" * @param sendts Send timestamp * @param samode Is this an SAMODE? + * @param destroy_channel Module can set this to 1 to indicate 'channel' was destroyed * @return The return value is ignored (use return 0) */ -int hooktype_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); +int hooktype_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel); /** Called when a remote user changes channel modes (function prototype for HOOKTYPE_REMOTE_CHANMODE). * @param client The client @@ -1495,9 +1500,10 @@ int hooktype_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, * @param parabuf The parameter buffer, for example "NiceOp" * @param sendts Send timestamp * @param samode Is this an SAMODE? + * @param destroy_channel Module can set this to 1 to indicate 'channel' was destroyed * @return The return value is ignored (use return 0) */ -int hooktype_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); +int hooktype_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel); /** Called when a channel mode is removed by a local or remote user (function prototype for HOOKTYPE_MODECHAR_DEL). * NOTE: This is currently not terribly useful for most modules. It is used by by the floodprot and noknock modules. @@ -1519,9 +1525,10 @@ int hooktype_modechar_add(Channel *channel, int modechar); * @param client The client * @param mtags Message tags associated with the event * @param reason The away reason, or NULL if away is unset. + * @param already_as_away Set to 1 if the user only changed their away reason. * @return The return value is ignored (use return 0) */ -int hooktype_away(Client *client, MessageTag *mtags, char *reason); +int hooktype_away(Client *client, MessageTag *mtags, const char *reason, int already_as_away); /** Called when a user wants to invite another user to a channel (function prototype for HOOKTYPE_PRE_INVITE). * @param client The client @@ -1544,14 +1551,14 @@ int hooktype_pre_invite(Client *client, Client *acptr, Channel *channel, int *ov int hooktype_invite(Client *client, Client *acptr, Channel *channel, MessageTag *mtags); /** Called when a user wants to knock on a channel (function prototype for HOOKTYPE_PRE_KNOCK). - * FIXME: where is the knock reason ? * @param client The client * @param channel The channel to knock on + * @param reason Knock reason (can be replaced if needed) * @retval HOOK_DENY Deny the knock. * @retval HOOK_ALLOW Allow the knock (stop processing other modules) * @retval HOOK_CONTINUE Allow the knock, unless another module blocks it. */ -int hooktype_pre_knock(Client *client, Channel *channel); +int hooktype_pre_knock(Client *client, Channel *channel, const char **reason); /** Called when a user knocks on a channel (function prototype for HOOKTYPE_KNOCK). * @param client The client @@ -1560,14 +1567,16 @@ int hooktype_pre_knock(Client *client, Channel *channel); * @param comment The knock reason * @return The return value is ignored (use return 0) */ -int hooktype_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment); +int hooktype_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment); /** Called when a user whoises someone (function prototype for HOOKTYPE_WHOIS). * @param client The client issuing the command * @param target The user who is the target of the /WHOIS. + * @param list The name/value/prio list that you can add information to + * that will be sent to the user as the WHOIS response. * @return The return value is ignored (use return 0) */ -int hooktype_whois(Client *client, Client *target); +int hooktype_whois(Client *client, Client *target, NameValuePrioList **list); /** Called to add letters to the WHO status column (function prototype for HOOKTYPE_WHO_STATUS). * If a user does a /WHO request, then WHO will show a number of status flags @@ -1580,7 +1589,7 @@ int hooktype_whois(Client *client, Client *target); * @param cansee If 'client' can see 'target' (eg: in same channel or -i) * @return Return 0 if no WHO status flags need to be added, otherwise return the ascii character (eg: return 'B'). */ -int hooktype_who_status(Client *client, Client *target, Channel *channel, Member *member, char *status, int cansee); +int hooktype_who_status(Client *client, Client *target, Channel *channel, Member *member, const char *status, int cansee); /** Called when an IRCOp wants to kill another user (function prototype for HOOKTYPE_PRE_KILL). * @param client The client @@ -1590,7 +1599,7 @@ int hooktype_who_status(Client *client, Client *target, Channel *channel, Member * @retval EX_ALWAYS_DENY Deny the KICK always (even if IRCOp). * @retval EX_ALLOW Allow the kick, unless another module blocks it. */ -int hooktype_pre_kill(Client *client, Client *victim, char *reason); +int hooktype_pre_kill(Client *client, Client *victim, const char *reason); /** Called when a local user kills another user (function prototype for HOOKTYPE_LOCAL_KILL). * Note that kills from remote IRCOps will show up as regular quits, so use hooktype_remote_quit() and hooktype_local_quit(). @@ -1599,15 +1608,14 @@ int hooktype_pre_kill(Client *client, Client *victim, char *reason); * @param comment The kill reason * @return The return value is ignored (use return 0) */ -int hooktype_local_kill(Client *client, Client *victim, char *comment); +int hooktype_local_kill(Client *client, Client *victim, const char *comment); -/** Called when an IRCOp /REHASH'es, and passes the parameters (function prototype for HOOKTYPE_REHASHFLAG). - * FIXME: shouldn't this be merged with hooktype_rehash() ? +/** Called when an IRCOp calls /REHASH with a -parameter (function prototype for HOOKTYPE_REHASHFLAG). * @param client The client issuing the command, or NULL if rehashing due to system signal. * @param str The rehash flag (eg: "-all") * @return The return value is ignored (use return 0) */ -int hooktype_rehashflag(Client *client, char *str); +int hooktype_rehashflag(Client *client, const char *str); /** Called when the server is rehashing (function prototype for HOOKTYPE_REHASH). * @return The return value is ignored (use return 0) @@ -1667,29 +1675,29 @@ int hooktype_configrun_ex(ConfigFile *cfptr, ConfigEntry *ce, int section, void * @param str The parameter to the STATS command, eg 'something'. * @return The return value is ignored (use return 0) */ -int hooktype_stats(Client *client, char *str); +int hooktype_stats(Client *client, const char *str); /** Called when a user becomes IRCOp or is no longer an IRCOp (function prototype for HOOKTYPE_LOCAL_OPER). * @param client The client * @param add 1 if the user becomes IRCOp, 0 if the user is no longer IRCOp + * @param oper_block The name of the oper block used to oper up * @return The return value is ignored (use return 0) */ -int hooktype_local_oper(Client *client, int add); +int hooktype_local_oper(Client *client, int add, ConfigItem_oper *oper_block); /** Called when a client sends a PASS command (function prototype for HOOKTYPE_LOCAL_PASS). * @param client The client * @param password The password supplied by the client * @return The return value is ignored (use return 0) */ -int hooktype_local_pass(Client *client, char *password); +int hooktype_local_pass(Client *client, const char *password); /** Called when a channel is created (function prototype for HOOKTYPE_CHANNEL_CREATE). - * @param client The client * @param channel The channel that just got created * @note This function is not used much, use hooktype_local_join() and hooktype_remote_join() instead. * @return The return value is ignored (use return 0) */ -int hooktype_channel_create(Client *client, Channel *channel); +int hooktype_channel_create(Channel *channel); /** Called when a channel is completely destroyed (function prototype for HOOKTYPE_CHANNEL_DESTROY). * @param channel The channel that is about to be destroyed @@ -1731,13 +1739,16 @@ int hooktype_tkl_add(Client *client, TKL *tkl); */ int hooktype_tkl_del(Client *client, TKL *tkl); -/** Called when something is logged via the ircd_log() function (function prototype for HOOKTYPE_LOG). - * @param flags One of LOG_*, such as LOG_ERROR. - * @param timebuf The time buffer, such as "[2030-01-01 12:00:00]" - * @param buf The text to be logged +/** Called when something is logged via the unreal_log() function (function prototype for HOOKTYPE_LOG). + * @param loglevel Loglevel (eg ULOG_INFO) + * @param subsystem Subsystem (eg "operoverride") + * @param event_id Event ID (eg "SAJOIN_COMMAND") + * @param msg Message(s) in text form + * @param json_serialized The associated JSON text + * @param timebuf The [xxxx] time buffer, for convenience * @return The return value is ignored (use return 0) */ -int hooktype_log(int flags, char *timebuf, char *buf); +int hooktype_log(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized, const char *timebuf); /** Called when a local user matches a spamfilter (function prototype for HOOKTYPE_LOCAL_SPAMFILTER). * @param client The client @@ -1748,7 +1759,7 @@ int hooktype_log(int flags, char *timebuf, char *buf); * @param tkl The spamfilter TKL entry that matched * @return The return value is ignored (use return 0) */ -int hooktype_local_spamfilter(Client *client, char *str, char *str_in, int type, char *target, TKL *tkl); +int hooktype_local_spamfilter(Client *client, const char *str, const char *str_in, int type, const char *target, TKL *tkl); /** Called when a user sends something to a user that has the sender silenced (function prototype for HOOKTYPE_SILENCED). * UnrealIRCd support a SILENCE list. If the target user has added someone on the silence list, eg via SILENCE +BadUser, @@ -1771,7 +1782,7 @@ int hooktype_silenced(Client *client, Client *target, SendType sendtype); * @note If you want to alter the buffer contents then replace 'readbuf' with your own buffer and set 'length' appropriately. * @return The return value is ignored (use return 0) */ -int hooktype_rawpacket_in(Client *client, char *readbuf, int *length); +int hooktype_rawpacket_in(Client *client, const char *readbuf, int *length); /** Called when a packet is received or sent (function prototype for HOOKTYPE_PACKET). * @param client The locally connected sender, this can be &me @@ -1812,11 +1823,10 @@ int hooktype_free_user(Client *client); * @param client The client * @param channel The channel * @param key The channel key - * @param parv The join parameters * @note I don't think this works? * @return Unclear.. */ -int hooktype_can_join_limitexceeded(Client *client, Channel *channel, char *key, char *parv[]); +int hooktype_can_join_limitexceeded(Client *client, Channel *channel, const char *key, char **errmsg); /** Called to check if the user is visible in the channel (function prototype for HOOKTYPE_VISIBLE_IN_CHANNEL). * For example, the delayjoin module (+d/+D) will 'return 0' here if the user is hidden due to delayed join. @@ -1904,16 +1914,6 @@ int hooktype_channel_synced(Channel *channel, int merge, int removetheirs, int n */ int hooktype_can_sajoin(Client *target, Channel *channel, Client *client); -/** Called when the hostname is initialized for a client (function prototype for HOOKTYPE_CHECK_INIT). - * This is a very specific call, it is only meant for the WEBIRC module. - * @param client The client - * @param sockname The socket name - * @param size The size of the socket name? :D - * @retval HOOK_CONTINUE Proceed normally - * @retval HOOK_DENY Reject the connection(?) - */ -int hooktype_check_init(Client *client, char *sockname, size_t size); - /** May the target user be deoped? (function prototype for HOOKTYPE_MODE_DEOP). * This is for example used by the +S (Services bot) user mode to block deop requests to services bots. * @param client The client issuing the command @@ -1921,13 +1921,14 @@ int hooktype_check_init(Client *client, char *sockname, size_t size); * @param channel The channel * @param what Always MODE_DEL at the moment * @param modechar The mode character: q/a/o/h/v - * @param my_access Cached result of get_access(), so one of CHFL_*, for example CHFL_CHANOP. - * @param badmode The error string that should be sent to the client + * @param client_access Channel member modes of 'client', eg "o", never NULL but can be empty. + * @param target_access Channel member modes of 'client', eg "h", never NULL but can be empty. + * @param reject_reason The error string that should be sent to the client * @retval HOOK_CONTINUE Proceed normally (allow it) * @retval HOOK_DENY Reject the mode change * @retval HOOK_ALWAYS_DENY Reject the mode change, even if IRCOp/Services/.. */ -int hooktype_mode_deop(Client *client, Client *victim, Channel *channel, u_int what, int modechar, long my_access, char **badmode); +int hooktype_mode_deop(Client *client, Client *victim, Channel *channel, u_int what, int modechar, const char *client_access, const char *target_access, const char **reject_reason); /** Called when a DCC request was denied by the IRCd (function prototype for HOOKTYPE_DCC_DENIED). * @param client The client who tried to send a file @@ -1937,7 +1938,7 @@ int hooktype_mode_deop(Client *client, Client *victim, Channel *channel, u_int w * @param denydcc The deny dcc { ] rule that triggered. * @return The return value is ignored (use return 0) */ -int hooktype_dcc_denied(Client *client, char *target, char *realfile, char *displayfile, ConfigItem_deny_dcc *denydcc); +int hooktype_dcc_denied(Client *client, const char *target, const char *realfile, const char *displayfile, ConfigItem_deny_dcc *denydcc); /** Called in the user accept procedure, when setting the +z user mode (function prototype for HOOKTYPE_SECURE_CONNECT). * This is only meant to be used by the WEBIRC module, so it can do -z for fake secure users. @@ -1956,13 +1957,6 @@ int hooktype_secure_connect(Client *client); */ int hooktype_can_bypass_channel_message_restriction(Client *client, Channel *channel, BypassChannelMessageRestrictionType bypass_type); -/** Called when xxxx (function prototype for HOOKTYPE_REQUIRE_SASL). - * FIXME: this hook is never called!? - * @param client The client - * @return The return value is ignored (use return 0) - */ -int hooktype_require_sasl(Client *client, char *reason); - /** Called when a SASL continuation response is received (function prototype for HOOKTYPE_SASL_CONTINUATION). * This is only used by the authprompt module, it unlikely that you need it. * @param client The client for which the SASL authentication is taking place @@ -1970,7 +1964,7 @@ int hooktype_require_sasl(Client *client, char *reason); * @retval HOOK_CONTINUE Continue as normal * @retval HOOK_DENY Do not handle the SASL request, or at least don't show the response to the client. */ -int hooktype_sasl_continuation(Client *client, char *buf); +int hooktype_sasl_continuation(Client *client, const char *buf); /** Called when a SASL result response is received (function prototype for HOOKTYPE_SASL_RESULT). * This is only used by the authprompt module. @@ -1990,7 +1984,7 @@ int hooktype_sasl_result(Client *client, int success); * @param duration The duration of the ban, 0 for permanent ban * @return The magic value 99 is used to exempt the user (=do not ban!), otherwise the ban is added. */ -int hooktype_place_host_ban(Client *client, int action, char *reason, long duration); +int hooktype_place_host_ban(Client *client, int action, const char *reason, long duration); /** Called when a TKL ban is hit by this user (function prototype for HOOKTYPE_FIND_TKLINE_MATCH). * This is called when an existing TKL entry is hit by the user. @@ -2020,7 +2014,7 @@ int hooktype_welcome(Client *client, int after_numeric); * @param buf The buffer (without message tags) * @return The return value is ignored (use return 0) */ -int hooktype_pre_command(Client *from, MessageTag *mtags, char *buf); +int hooktype_pre_command(Client *from, MessageTag *mtags, const char *buf); /** Called right after finishing a client command (function prototype for HOOKTYPE_POST_COMMAND). * This is only used by labeled-reponse. If you think this hook is useful then you @@ -2030,7 +2024,7 @@ int hooktype_pre_command(Client *from, MessageTag *mtags, char *buf); * @param buf The buffer (without message tags) * @return The return value is ignored (use return 0) */ -int hooktype_post_command(Client *from, MessageTag *mtags, char *buf); +int hooktype_post_command(Client *from, MessageTag *mtags, const char *buf); /** Called when new_message() is executed (function prototype for HOOKTYPE_NEW_MESSAGE). * When a new message with message tags is prepared, code in UnrealIRCd @@ -2044,7 +2038,7 @@ int hooktype_post_command(Client *from, MessageTag *mtags, char *buf); * @param signature Special signature when used through new_message_special() * @return The return value is ignored (use return 0) */ -void hooktype_new_message(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +void hooktype_new_message(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); /** Is the client handshake finished? (function prototype for HOOKTYPE_IS_HANDSHAKE_FINISHED). * This is called by the is_handshake_finished() function to check if the user @@ -2067,7 +2061,7 @@ int hooktype_is_handshake_finished(Client *client); * @param comment The quit message * @return The original quit message (comment), the new quit message (pointing to your own static buffer), or NULL (no quit message) */ -char *hooktype_pre_local_quit_chan(Client *client, Channel *channel, char *comment); +const char *hooktype_pre_local_quit_chan(Client *client, Channel *channel, const char *comment); /** Called when an ident lookup should be made (function prototype for HOOKTYPE_IDENT_LOOKUP). * This is used by the ident_lookup module. @@ -2077,7 +2071,7 @@ char *hooktype_pre_local_quit_chan(Client *client, Channel *channel, char *comme int hooktype_ident_lookup(Client *client); /** Called when someone logs in/out a services account (function prototype for HOOKTYPE_ACCOUNT_LOGIN). - * The account name can be found in client->user->svid. It will be the string "0" if the user is logged out. + * The account name can be found in client->user->account. It will be the string "0" if the user is logged out. * @param client The client * @param mtags Message tags associated with the event * @return The return value is ignored (use return 0) @@ -2102,6 +2096,45 @@ int hooktype_close_connection(Client *client); */ int hooktype_connect_extinfo(Client *client, NameValuePrioList **list); +/** Called when a user wants to join a channel that require invitation. + * Use hook priorities to enforce a specific policy, especially denying the invitation. + * @param client The client + * @param channel The channel client is willing to join + * @param invited Set to 0 for user who should not be invited, set to 1 if the user is invited. + * @return The return value is ignored (use return 0) + */ +int hooktype_is_invited(Client *client, Channel *channel, int *invited); + +/** Called after a local user has changed the nick name (function prototype for HOOKTYPE_POST_LOCAL_NICKCHANGE). + * @param client The client + * @param mtags Message tags associated with the event + * @param oldnick The nick name before the nick change + * @return The return value is ignored (use return 0) + */ +int hooktype_post_local_nickchange(Client *client, MessageTag *mtags, const char *oldnick); + +/** Called after a remote user has changed the nick name (function prototype for HOOKTYPE_POST_REMOTE_NICKCHANGE). + * @param client The client + * @param mtags Message tags associated with the event + * @param oldnick The nick name before the nick change + * @return The return value is ignored (use return 0) + */ +int hooktype_post_remote_nickchange(Client *client, MessageTag *mtags, const char *oldnick); + +/** Called when user name or user host has changed. + * @param client The client whose user@host has changed + * @param olduser Old username of the client + * @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); +/** 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); /** @} */ #ifdef GCC_TYPECHECKING @@ -2138,6 +2171,7 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_REMOTE_QUIT) && !ValidateHook(hooktype_remote_quit, func)) || \ ((hooktype == HOOKTYPE_PRE_LOCAL_JOIN) && !ValidateHook(hooktype_pre_local_join, func)) || \ ((hooktype == HOOKTYPE_PRE_LOCAL_KICK) && !ValidateHook(hooktype_pre_local_kick, func)) || \ + ((hooktype == HOOKTYPE_CAN_SET_TOPIC) && !ValidateHook(hooktype_can_set_topic, func)) || \ ((hooktype == HOOKTYPE_PRE_LOCAL_TOPIC) && !ValidateHook(hooktype_pre_local_topic, func)) || \ ((hooktype == HOOKTYPE_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_remote_nickchange, func)) || \ ((hooktype == HOOKTYPE_CHANNEL_CREATE) && !ValidateHook(hooktype_channel_create, func)) || \ @@ -2186,7 +2220,6 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_CHANNEL_SYNCED) && !ValidateHook(hooktype_channel_synced, func)) || \ ((hooktype == HOOKTYPE_CAN_SAJOIN) && !ValidateHook(hooktype_can_sajoin, func)) || \ ((hooktype == HOOKTYPE_WHOIS) && !ValidateHook(hooktype_whois, func)) || \ - ((hooktype == HOOKTYPE_CHECK_INIT) && !ValidateHook(hooktype_check_init, func)) || \ ((hooktype == HOOKTYPE_WHO_STATUS) && !ValidateHook(hooktype_who_status, func)) || \ ((hooktype == HOOKTYPE_MODE_DEOP) && !ValidateHook(hooktype_mode_deop, func)) || \ ((hooktype == HOOKTYPE_PRE_KILL) && !ValidateHook(hooktype_pre_kill, func)) || \ @@ -2196,7 +2229,6 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_SERVER_SYNCED) && !ValidateHook(hooktype_server_synced, func)) || \ ((hooktype == HOOKTYPE_SECURE_CONNECT) && !ValidateHook(hooktype_secure_connect, func)) || \ ((hooktype == HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION) && !ValidateHook(hooktype_can_bypass_channel_message_restriction, func)) || \ - ((hooktype == HOOKTYPE_REQUIRE_SASL) && !ValidateHook(hooktype_require_sasl, func)) || \ ((hooktype == HOOKTYPE_SASL_CONTINUATION) && !ValidateHook(hooktype_sasl_continuation, func)) || \ ((hooktype == HOOKTYPE_SASL_RESULT) && !ValidateHook(hooktype_sasl_result, func)) || \ ((hooktype == HOOKTYPE_PLACE_HOST_BAN) && !ValidateHook(hooktype_place_host_ban, func)) || \ @@ -2211,7 +2243,12 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_CONFIGRUN_EX) && !ValidateHook(hooktype_configrun_ex, func)) || \ ((hooktype == HOOKTYPE_ACCOUNT_LOGIN) && !ValidateHook(hooktype_account_login, func)) || \ ((hooktype == HOOKTYPE_CLOSE_CONNECTION) && !ValidateHook(hooktype_close_connection, func)) || \ - ((hooktype == HOOKTYPE_CONNECT_EXTINFO) && !ValidateHook(hooktype_connect_extinfo, func)) ) \ + ((hooktype == HOOKTYPE_CONNECT_EXTINFO) && !ValidateHook(hooktype_connect_extinfo, func)) || \ + ((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)) )\ _hook_error_incompatible(); #endif /* GCC_TYPECHECKING */ @@ -2222,10 +2259,11 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum /* Callback types */ #define CALLBACKTYPE_CLOAK 1 -#define CALLBACKTYPE_CLOAKKEYCSUM 2 +#define CALLBACKTYPE_CLOAK_KEY_CHECKSUM 2 #define CALLBACKTYPE_CLOAK_EX 3 #define CALLBACKTYPE_BLACKLIST_CHECK 4 #define CALLBACKTYPE_REPUTATION_STARTTIME 5 +#define CALLBACKTYPE_GEOIP_LOOKUP 6 /* To add a new efunction, only if you are an UnrealIRCd coder: * 1) Add a new entry here @@ -2241,6 +2279,7 @@ enum EfunctionType { EFUNC_CAN_JOIN, EFUNC_DO_MODE, EFUNC_SET_MODE, + EFUNC_SET_CHANNEL_MODE, EFUNC_CMD_UMODE, EFUNC_REGISTER_USER, EFUNC_TKL_HASH, @@ -2281,6 +2320,8 @@ enum EfunctionType { EFUNC_BROADCAST_MD_CHANNEL_CMD, EFUNC_BROADCAST_MD_MEMBER_CMD, EFUNC_BROADCAST_MD_MEMBERSHIP_CMD, + EFUNC_MODDATA_ADD_S2S_MTAGS, + EFUNC_MODDATA_EXTRACT_S2S_MTAGS, EFUNC_SEND_MODDATA_CLIENT, EFUNC_SEND_MODDATA_CHANNEL, EFUNC_SEND_MODDATA_MEMBERS, @@ -2293,10 +2334,12 @@ enum EfunctionType { EFUNC_DO_REMOTE_NICK_NAME, EFUNC_CHARSYS_GET_CURRENT_LANGUAGES, EFUNC_BROADCAST_SINFO, + EFUNC_CONNECT_SERVER, EFUNC_PARSE_MESSAGE_TAGS, EFUNC_MTAGS_TO_STRING, EFUNC_TKL_CHARTOTYPE, EFUNC_TKL_TYPE_STRING, + EFUNC_TKL_TYPE_CONFIG_STRING, EFUNC_CAN_SEND_TO_CHANNEL, EFUNC_CAN_SEND_TO_USER, EFUNC_BROADCAST_MD_GLOBALVAR, @@ -2321,6 +2364,15 @@ enum EfunctionType { EFUNC_LABELED_RESPONSE_SET_CONTEXT, EFUNC_LABELED_RESPONSE_FORCE_END, EFUNC_KICK_USER, + EFUNC_WATCH_ADD, + EFUNC_WATCH_DEL, + EFUNC_WATCH_DEL_LIST, + EFUNC_WATCH_GET, + EFUNC_WATCH_CHECK, + EFUNC_TKL_UHOST, + EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER, + EFUNC_GET_CHMODES_FOR_USER, + EFUNC_WHOIS_GET_POLICY, }; /* Module flags */ @@ -2354,7 +2406,7 @@ enum EfunctionType { #define MOD_LOAD() DLLFUNC int Mod_Load(ModuleInfo *modinfo) #define MOD_UNLOAD() DLLFUNC int Mod_Unload(ModuleInfo *modinfo) -#define CLOAK_KEYCRC RCallbacks[CALLBACKTYPE_CLOAKKEYCSUM] != NULL ? RCallbacks[CALLBACKTYPE_CLOAKKEYCSUM]->func.pcharfunc() : "nil" +#define CLOAK_KEY_CHECKSUM RCallbacks[CALLBACKTYPE_CLOAK_KEY_CHECKSUM] != NULL ? RCallbacks[CALLBACKTYPE_CLOAK_KEY_CHECKSUM]->func.stringfunc() : "nil" #ifdef DYNAMIC_LINKING #include "modversion.h" diff --git a/include/msg.h b/include/msg.h index 0b97314..dcae509 100644 --- a/include/msg.h +++ b/include/msg.h @@ -51,7 +51,6 @@ #define MSG_PONG "PONG" /* PONG */ #define MSG_OPER "OPER" /* OPER */ #define MSG_PASS "PASS" /* PASS */ -#define MSG_WALLOPS "WALLOPS" /* WALL */ #define MSG_TIME "TIME" /* TIME */ #define MSG_NAMES "NAMES" /* NAME */ #define MSG_ADMIN "ADMIN" /* ADMI */ diff --git a/include/numeric.h b/include/numeric.h index fce4268..a380889 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -36,7 +36,6 @@ #define RPL_ISUPPORT 005 #define RPL_REDIR 10 -#define RPL_YOURID 42 #define RPL_REMOTEISUPPORT 105 @@ -51,29 +50,23 @@ #define ERR_TOOMANYCHANNELS 405 #define ERR_WASNOSUCHNICK 406 #define ERR_TOOMANYTARGETS 407 -#define ERR_NOSUCHSERVICE 408 #define ERR_NOORIGIN 409 #define ERR_INVALIDCAPCMD 410 #define ERR_NORECIPIENT 411 #define ERR_NOTEXTTOSEND 412 -#define ERR_NOTOPLEVEL 413 -#define ERR_WILDTOPLEVEL 414 #define ERR_TOOMANYMATCHES 416 #define ERR_UNKNOWNCOMMAND 421 #define ERR_NOMOTD 422 #define ERR_NOADMININFO 423 -#define ERR_FILEERROR 424 #define ERR_NOOPERMOTD 425 #define ERR_TOOMANYAWAY 429 #define ERR_NONICKNAMEGIVEN 431 #define ERR_ERRONEUSNICKNAME 432 #define ERR_NICKNAMEINUSE 433 #define ERR_NORULES 434 -#define ERR_SERVICECONFUSED 435 -#define ERR_NICKCOLLISION 436 #define ERR_BANNICKCHANGE 437 #define ERR_NCHANGETOOFAST 438 #define ERR_TARGETTOOFAST 439 @@ -82,53 +75,36 @@ #define ERR_USERNOTINCHANNEL 441 #define ERR_NOTONCHANNEL 442 #define ERR_USERONCHANNEL 443 -#define ERR_NOLOGIN 444 -#define ERR_SUMMONDISABLED 445 -#define ERR_USERSDISABLED 446 #define ERR_NONICKCHANGE 447 #define ERR_FORBIDDENCHANNEL 448 #define ERR_NOTREGISTERED 451 -#define ERR_HOSTILENAME 455 - -#define ERR_NOHIDING 459 #define ERR_NOTFORHALFOPS 460 #define ERR_NEEDMOREPARAMS 461 #define ERR_ALREADYREGISTRED 462 -#define ERR_NOPERMFORHOST 463 #define ERR_PASSWDMISMATCH 464 #define ERR_YOUREBANNEDCREEP 465 -#define ERR_YOUWILLBEBANNED 466 -#define ERR_KEYSET 467 #define ERR_ONLYSERVERSCANCHANGE 468 -#define ERR_LINKSET 469 #define ERR_LINKCHANNEL 470 #define ERR_CHANNELISFULL 471 #define ERR_UNKNOWNMODE 472 #define ERR_INVITEONLYCHAN 473 #define ERR_BANNEDFROMCHAN 474 #define ERR_BADCHANNELKEY 475 -#define ERR_BADCHANMASK 476 #define ERR_NEEDREGGEDNICK 477 #define ERR_BANLISTFULL 478 -#define ERR_LINKFAIL 479 #define ERR_CANNOTKNOCK 480 #define ERR_NOPRIVILEGES 481 #define ERR_CHANOPRIVSNEEDED 482 -#define ERR_CANTKILLSERVER 483 -#define ERR_ATTACKDENY 484 #define ERR_KILLDENY 485 -#define ERR_NONONREG 486 #define ERR_NOTFORUSERS 487 #define ERR_SECUREONLYCHAN 489 -#define ERR_NOSWEAR 490 #define ERR_NOOPERHOST 491 -#define ERR_NOCTCP 492 #define ERR_CHANOWNPRIVNEEDED 499 @@ -138,13 +114,11 @@ #define ERR_SILELISTFULL 511 #define ERR_TOOMANYWATCH 512 -#define ERR_NEEDPONG 513 #define ERR_TOOMANYDCC 514 #define ERR_DISABLED 517 #define ERR_NOINVITE 518 -#define ERR_ADMONLY 519 #define ERR_OPERONLY 520 #define ERR_LISTSYNTAX 521 @@ -163,7 +137,6 @@ #define RPL_WHOISREGNICK 307 #define RPL_RULESSTART 308 #define RPL_ENDOFRULES 309 -#define RPL_WHOISHELPOP 310 /* -Donwulff */ #define RPL_WHOISUSER 311 #define RPL_WHOISSERVER 312 @@ -173,7 +146,6 @@ /* rpl_endofwho below (315) */ #define RPL_ENDOFWHOWAS 369 -#define RPL_WHOISCHANOP 316 /* redundant and not needed but reserved */ #define RPL_WHOISIDLE 317 #define RPL_ENDOFWHOIS 318 @@ -198,7 +170,7 @@ #define RPL_WHOISBOT 335 #define RPL_USERIP 340 #define RPL_INVITING 341 -#define RPL_SUMMONING 342 +#define RPL_WHOISCOUNTRY 344 #define RPL_VERSION 351 @@ -211,7 +183,6 @@ #define RPL_EXLIST 348 #define RPL_ENDOFEXLIST 349 -#define RPL_KILLDONE 361 #define RPL_CLOSING 362 #define RPL_CLOSEEND 363 #define RPL_LINKS 364 @@ -223,7 +194,6 @@ #define RPL_INFO 371 #define RPL_MOTD 372 -#define RPL_INFOSTART 373 #define RPL_ENDOFINFO 374 #define RPL_MOTDSTART 375 #define RPL_ENDOFMOTD 376 @@ -232,9 +202,6 @@ #define RPL_WHOISMODES 379 #define RPL_YOUREOPER 381 #define RPL_REHASHING 382 -#define RPL_YOURESERVICE 383 -#define RPL_MYPORTIS 384 -#define RPL_NOTOPERANYMORE 385 #define RPL_QLIST 386 #define RPL_ENDOFQLIST 387 #define RPL_ALIST 388 @@ -255,7 +222,6 @@ #define RPL_TRACEOPERATOR 204 #define RPL_TRACEUSER 205 #define RPL_TRACESERVER 206 -#define RPL_TRACESERVICE 207 #define RPL_TRACENEWTYPE 208 #define RPL_TRACECLASS 209 @@ -263,37 +229,28 @@ #define RPL_STATSLINKINFO 211 #define RPL_STATSCOMMANDS 212 #define RPL_STATSCLINE 213 -#define RPL_STATSOLDNLINE 214 #define RPL_STATSILINE 215 -#define RPL_STATSKLINE 216 #define RPL_STATSQLINE 217 #define RPL_STATSYLINE 218 #define RPL_ENDOFSTATS 219 -#define RPL_STATSBLINE 220 #define RPL_UMODEIS 221 -#define RPL_SQLINE_NICK 222 #define RPL_STATSGLINE 223 #define RPL_STATSTLINE 224 -#define RPL_STATSELINE 225 #define RPL_STATSNLINE 226 #define RPL_STATSVLINE 227 #define RPL_STATSBANVER 228 #define RPL_STATSSPAMF 229 #define RPL_STATSEXCEPTTKL 230 -#define RPL_SERVICEINFO 231 #define RPL_RULES 232 #define RPL_SERVICE 233 -#define RPL_SERVLIST 234 -#define RPL_SERVLISTEND 235 #define RPL_STATSLLINE 241 #define RPL_STATSUPTIME 242 #define RPL_STATSOLINE 243 #define RPL_STATSHLINE 244 -#define RPL_STATSSLINE 245 #define RPL_STATSXLINE 247 #define RPL_STATSULINE 248 #define RPL_STATSDEBUG 249 @@ -319,14 +276,6 @@ #define RPL_STATSDLINE 275 #define RPL_WHOISCERTFP 276 -#define RPL_HELPHDR 290 -#define RPL_HELPOP 291 -#define RPL_HELPTLR 292 -#define RPL_HELPHLP 293 -#define RPL_HELPFWD 294 -#define RPL_HELPIGN 295 - - /* * New /MAP format. */ @@ -335,7 +284,6 @@ #define RPL_MAPEND 007 -#define ERR_WHOSYNTAX 522 #define ERR_WHOLIMEXCEED 523 #define ERR_OPERSPVERIFY 524 @@ -357,7 +305,6 @@ #define RPL_NOWOFF 605 #define RPL_WATCHLIST 606 #define RPL_ENDOFWATCHLIST 607 -#define RPL_CLEARWATCH 608 #define RPL_NOWISAWAY 609 #define RPL_DCCSTATUS 617 @@ -365,16 +312,18 @@ #define RPL_ENDOFDCCLIST 619 #define RPL_DCCINFO 620 -#define RPL_DUMPING 640 -#define RPL_DUMPRPL 641 -#define RPL_EODUMP 642 - #define RPL_SPAMCMDFWD 659 #define RPL_STARTTLS 670 #define RPL_WHOISSECURE 671 +#define RPL_MONONLINE 730 +#define RPL_MONOFFLINE 731 +#define RPL_MONLIST 732 +#define RPL_ENDOFMONLIST 733 +#define ERR_MONLISTFULL 734 + #define ERR_MLOCKRESTRICTED 742 #define ERR_CANNOTDOCOMMAND 972 @@ -382,15 +331,242 @@ #define ERR_STARTTLS 691 +#define ERR_INVALIDMODEPARAM 696 + #define RPL_LOGGEDIN 900 #define RPL_LOGGEDOUT 901 -#define ERR_NICKLOCKED 902 #define RPL_SASLSUCCESS 903 #define ERR_SASLFAIL 904 #define ERR_SASLTOOLONG 905 #define ERR_SASLABORTED 906 -#define ERR_SASLALREADY 907 #define RPL_SASLMECHS 908 -#define ERR_NUMERICERR 999 +/* Numeric texts */ + +#define STR_RPL_WELCOME /* 001 */ ":Welcome to the %s IRC Network %s!%s@%s" +#define STR_RPL_YOURHOST /* 002 */ ":Your host is %s, running version %s" +#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_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_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" +#define STR_RPL_TRACEHANDSHAKE /* 202 */ "Handshaking %s %s" +#define STR_RPL_TRACEUNKNOWN /* 203 */ "???? %s %s" +#define STR_RPL_TRACEOPERATOR /* 204 */ "Operator %s %s [%s] %lld" +#define STR_RPL_TRACEUSER /* 205 */ "User %s %s [%s] %lld" +#define STR_RPL_TRACESERVER /* 206 */ "Server %s %dS %dC %s %s!%s@%s %lld" +#define STR_RPL_TRACENEWTYPE /* 208 */ "%s 0 %s" +#define STR_RPL_TRACECLASS /* 209 */ "Class %s %d" +#define STR_RPL_STATSHELP /* 210 */ ":%s" +#define STR_RPL_STATSCOMMANDS /* 212 */ "%s %u %lu" +#define STR_RPL_STATSCLINE /* 213 */ "%c %s * %s %d %d %s" +#define STR_RPL_STATSILINE /* 215 */ "I %s %s %d %d %s %s %d" +#define STR_RPL_STATSQLINE /* 217 */ "%c %s %lld %lld %s :%s" +#define STR_RPL_STATSYLINE /* 218 */ "Y %s %d %d %d %d %d" +#define STR_RPL_ENDOFSTATS /* 219 */ "%c :End of /STATS report" +#define STR_RPL_UMODEIS /* 221 */ "%s" +#define STR_RPL_STATSGLINE /* 223 */ "%c %s %lld %lld %s :%s" +#define STR_RPL_STATSTLINE /* 224 */ "T %s %s %s" +#define STR_RPL_STATSNLINE /* 226 */ "n %s %s" +#define STR_RPL_STATSVLINE /* 227 */ "v %s %s %s" +#define STR_RPL_STATSBANVER /* 228 */ "%s %s" +#define STR_RPL_STATSSPAMF /* 229 */ "%c %s %s %s %lld %lld %lld %s %s :%s" +#define STR_RPL_STATSEXCEPTTKL /* 230 */ "%s %s %lld %lld %s :%s" +#define STR_RPL_RULES /* 232 */ ":- %s" +#define STR_RPL_STATSLLINE /* 241 */ "%c %s * %s %d %d" +#define STR_RPL_STATSUPTIME /* 242 */ ":Server Up %lld days, %lld:%02lld:%02lld" +#define STR_RPL_STATSOLINE /* 243 */ "%c %s * %s %s %s" +#define STR_RPL_STATSHLINE /* 244 */ "%c %s * %s %d %d" +#define STR_RPL_STATSXLINE /* 247 */ "X %s %d" +#define STR_RPL_STATSULINE /* 248 */ "U %s" +#define STR_RPL_STATSDEBUG /* 249 */ ":%s" +#define STR_RPL_STATSCONN /* 250 */ ":Highest connection count: %d (%d clients)" +#define STR_RPL_LUSERCLIENT /* 251 */ ":There are %d users and %d invisible on %d servers" +#define STR_RPL_LUSEROP /* 252 */ "%d :operator(s) online" +#define STR_RPL_LUSERUNKNOWN /* 253 */ "%d :unknown connection(s)" +#define STR_RPL_LUSERCHANNELS /* 254 */ "%d :channels formed" +#define STR_RPL_LUSERME /* 255 */ ":I have %d clients and %d servers" +#define STR_RPL_ADMINME /* 256 */ ":Administrative info about %s" +#define STR_RPL_ADMINLOC1 /* 257 */ ":%s" +#define STR_RPL_ADMINLOC2 /* 258 */ ":%s" +#define STR_RPL_ADMINEMAIL /* 259 */ ":%s" +#define STR_RPL_TRACELOG /* 261 */ "File %s %d" +#define STR_RPL_TRYAGAIN /* 263 */ "%s :Flooding detected. Please wait a while and try again." +#define STR_RPL_LOCALUSERS /* 265 */ "%d %d :Current local users %d, max %d" +#define STR_RPL_GLOBALUSERS /* 266 */ "%d %d :Current global users %d, max %d" +#define STR_RPL_SILELIST /* 271 */ "%s" +#define STR_RPL_ENDOFSILELIST /* 272 */ ":End of Silence List" +#define STR_RPL_STATSDLINE /* 275 */ "%c %s %s" +#define STR_RPL_WHOISCERTFP /* 276 */ "%s :has client certificate fingerprint %s" +#define STR_RPL_AWAY /* 301 */ "%s :%s" +#define STR_RPL_USERHOST /* 302 */ ":%s %s %s %s %s" +#define STR_RPL_ISON /* 303 */ ":" +#define STR_RPL_UNAWAY /* 305 */ ":You are no longer marked as being away" +#define STR_RPL_NOWAWAY /* 306 */ ":You have been marked as being away" +#define STR_RPL_WHOISREGNICK /* 307 */ "%s :is identified for this nick" +#define STR_RPL_RULESSTART /* 308 */ ":- %s Server Rules - " +#define STR_RPL_ENDOFRULES /* 309 */ ":End of RULES command." +#define STR_RPL_WHOISUSER /* 311 */ "%s %s %s * :%s" +#define STR_RPL_WHOISSERVER /* 312 */ "%s %s :%s" +#define STR_RPL_WHOISOPERATOR /* 313 */ "%s :is %s" +#define STR_RPL_WHOWASUSER /* 314 */ "%s %s %s * :%s" +#define STR_RPL_ENDOFWHO /* 315 */ "%s :End of /WHO list." +#define STR_RPL_WHOISIDLE /* 317 */ "%s %lld %lld :seconds idle, signon time" +#define STR_RPL_ENDOFWHOIS /* 318 */ "%s :End of /WHOIS list." +#define STR_RPL_WHOISCHANNELS /* 319 */ "%s :%s" +#define STR_RPL_WHOISSPECIAL /* 320 */ "%s :%s" +#define STR_RPL_LISTSTART /* 321 */ "Channel :Users Name" +#define STR_RPL_LIST /* 322 */ "%s %d :%s %s" +#define STR_RPL_LISTEND /* 323 */ ":End of /LIST" +#define STR_RPL_CHANNELMODEIS /* 324 */ "%s %s %s" +#define STR_RPL_CREATIONTIME /* 329 */ "%s %lld" +#define STR_RPL_WHOISLOGGEDIN /* 330 */ "%s %s :is logged in as" +#define STR_RPL_NOTOPIC /* 331 */ "%s :No topic is set." +#define STR_RPL_TOPIC /* 332 */ "%s :%s" +#define STR_RPL_TOPICWHOTIME /* 333 */ "%s %s %lld" +#define STR_RPL_LISTSYNTAX /* 334 */ ":%s" +#define STR_RPL_WHOISBOT /* 335 */ "%s :is a \2Bot\2 on %s" +#define STR_RPL_INVITELIST /* 336 */ ":%s" +#define STR_RPL_ENDOFINVITELIST /* 337 */ ":End of /INVITE list." +#define STR_RPL_USERIP /* 340 */ ":%s %s %s %s %s" +#define STR_RPL_INVITING /* 341 */ "%s %s" +#define STR_RPL_WHOISCOUNTRY /* 344 */ "%s %s :is connecting from %s" +#define STR_RPL_INVEXLIST /* 346 */ "%s %s %s %lld" +#define STR_RPL_ENDOFINVEXLIST /* 347 */ "%s :End of Channel Invite List" +#define STR_RPL_EXLIST /* 348 */ "%s %s %s %lld" +#define STR_RPL_ENDOFEXLIST /* 349 */ "%s :End of Channel Exception List" +#define STR_RPL_VERSION /* 351 */ "%s.%s %s :%s%s%s [%s=%d]" +#define STR_RPL_WHOREPLY /* 352 */ "%s %s %s %s %s %s :%d %s" +#define STR_RPL_NAMREPLY /* 353 */ "%s" +#define STR_RPL_CLOSING /* 362 */ "%s :Closed. Status = %d" +#define STR_RPL_CLOSEEND /* 363 */ "%d: Connections Closed" +#define STR_RPL_LINKS /* 364 */ "%s %s :%d %s" +#define STR_RPL_ENDOFLINKS /* 365 */ "%s :End of /LINKS list." +#define STR_RPL_ENDOFNAMES /* 366 */ "%s :End of /NAMES list." +#define STR_RPL_BANLIST /* 367 */ "%s %s %s %lld" +#define STR_RPL_ENDOFBANLIST /* 368 */ "%s :End of Channel Ban List" +#define STR_RPL_ENDOFWHOWAS /* 369 */ "%s :End of WHOWAS" +#define STR_RPL_INFO /* 371 */ ":%s" +#define STR_RPL_MOTD /* 372 */ ":- %s" +#define STR_RPL_ENDOFINFO /* 374 */ ":End of /INFO list." +#define STR_RPL_MOTDSTART /* 375 */ ":- %s Message of the Day - " +#define STR_RPL_ENDOFMOTD /* 376 */ ":End of /MOTD command." +#define STR_RPL_WHOISHOST /* 378 */ "%s :is connecting from %s@%s %s" +#define STR_RPL_WHOISMODES /* 379 */ "%s :is using modes %s %s" +#define STR_RPL_YOUREOPER /* 381 */ ":You are now an IRC Operator" +#define STR_RPL_REHASHING /* 382 */ "%s :Rehashing" +#define STR_RPL_QLIST /* 386 */ "%s %s" +#define STR_RPL_ENDOFQLIST /* 387 */ "%s :End of Channel Owner List" +#define STR_RPL_ALIST /* 388 */ "%s %s" +#define STR_RPL_ENDOFALIST /* 389 */ "%s :End of Protected User List" +#define STR_RPL_TIME /* 391 */ "%s :%s" +#define STR_RPL_HOSTHIDDEN /* 396 */ "%s :is now your displayed host" +#define STR_ERR_NOSUCHNICK /* 401 */ "%s :No such nick/channel" +#define STR_ERR_NOSUCHSERVER /* 402 */ "%s :No such server" +#define STR_ERR_NOSUCHCHANNEL /* 403 */ "%s :No such channel" +#define STR_ERR_CANNOTSENDTOCHAN /* 404 */ "%s :%s (%s)" +#define STR_ERR_TOOMANYCHANNELS /* 405 */ "%s :You have joined too many channels" +#define STR_ERR_WASNOSUCHNICK /* 406 */ "%s :There was no such nickname" +#define STR_ERR_TOOMANYTARGETS /* 407 */ "%s :Too many targets. The maximum is %d for %s." +#define STR_ERR_NOORIGIN /* 409 */ ":No origin specified" +#define STR_ERR_INVALIDCAPCMD /* 410 */ "%s :Invalid CAP subcommand" +#define STR_ERR_NORECIPIENT /* 411 */ ":No recipient given (%s)" +#define STR_ERR_NOTEXTTOSEND /* 412 */ ":No text to send" +#define STR_ERR_TOOMANYMATCHES /* 416 */ "%s :%s" +#define STR_ERR_UNKNOWNCOMMAND /* 421 */ "%s :Unknown command" +#define STR_ERR_NOMOTD /* 422 */ ":MOTD File is missing" +#define STR_ERR_NOADMININFO /* 423 */ "%s :No administrative info available" +#define STR_ERR_NOOPERMOTD /* 425 */ ":OPERMOTD File is missing" +#define STR_ERR_TOOMANYAWAY /* 429 */ ":Too Many aways - Flood Protection activated" +#define STR_ERR_NONICKNAMEGIVEN /* 431 */ ":No nickname given" +#define STR_ERR_ERRONEUSNICKNAME /* 432 */ "%s :Nickname is unavailable: %s" +#define STR_ERR_NICKNAMEINUSE /* 433 */ "%s :Nickname is already in use." +#define STR_ERR_NORULES /* 434 */ ":RULES File is missing" +#define STR_ERR_BANNICKCHANGE /* 437 */ "%s :Cannot change nickname while banned on channel" +#define STR_ERR_NCHANGETOOFAST /* 438 */ "%s :Nick change too fast. Please try again later." +#define STR_ERR_TARGETTOOFAST /* 439 */ "%s :Message target change too fast. Please wait %lld seconds" +#define STR_ERR_SERVICESDOWN /* 440 */ "%s :Services are currently down. Please try again later." +#define STR_ERR_USERNOTINCHANNEL /* 441 */ "%s %s :They aren't on that channel" +#define STR_ERR_NOTONCHANNEL /* 442 */ "%s :You're not on that channel" +#define STR_ERR_USERONCHANNEL /* 443 */ "%s %s :is already on channel" +#define STR_ERR_NONICKCHANGE /* 447 */ ":Can not change nickname while on %s (+N)" +#define STR_ERR_FORBIDDENCHANNEL /* 448 */ "%s :Cannot join channel: %s" +#define STR_ERR_NOTREGISTERED /* 451 */ ":You have not registered" +#define STR_ERR_NOTFORHALFOPS /* 460 */ ":Halfops cannot set mode %c" +#define STR_ERR_NEEDMOREPARAMS /* 461 */ "%s :Not enough parameters" +#define STR_ERR_ALREADYREGISTRED /* 462 */ ":You may not reregister" +#define STR_ERR_PASSWDMISMATCH /* 464 */ ":Password Incorrect" +#define STR_ERR_YOUREBANNEDCREEP /* 465 */ ":%s" +#define STR_ERR_ONLYSERVERSCANCHANGE /* 468 */ "%s :Only servers can change that mode" +#define STR_ERR_LINKCHANNEL /* 470 */ "%s %s :[Link] %s has become full, so you are automatically being transferred to the linked channel %s" +#define STR_ERR_CHANNELISFULL /* 471 */ "%s :Cannot join channel (+l)" +#define STR_ERR_UNKNOWNMODE /* 472 */ "%c :is unknown mode char to me" +#define STR_ERR_INVITEONLYCHAN /* 473 */ "%s :Cannot join channel (+i)" +#define STR_ERR_BANNEDFROMCHAN /* 474 */ "%s :Cannot join channel (+b)" +#define STR_ERR_BADCHANNELKEY /* 475 */ "%s :Cannot join channel (+k)" +#define STR_ERR_NEEDREGGEDNICK /* 477 */ "%s :You need a registered nick to join that channel." +#define STR_ERR_BANLISTFULL /* 478 */ "%s %s :Channel ban/ignore list is full" +#define STR_ERR_CANNOTKNOCK /* 480 */ ":Cannot knock on %s (%s)" +#define STR_ERR_NOPRIVILEGES /* 481 */ ":Permission Denied- You do not have the correct IRC operator privileges" +#define STR_ERR_CHANOPRIVSNEEDED /* 482 */ "%s :You're not channel operator" +#define STR_ERR_KILLDENY /* 485 */ ":Cannot kill protected user %s." +#define STR_ERR_NOTFORUSERS /* 487 */ ":%s is a server only command" +#define STR_ERR_SECUREONLYCHAN /* 489 */ "%s :Cannot join channel (Secure connection is required)" +#define STR_ERR_NOOPERHOST /* 491 */ ":No O-lines for your host" +#define STR_ERR_CHANOWNPRIVNEEDED /* 499 */ "%s :You're not a channel owner" +#define STR_ERR_TOOMANYJOINS /* 500 */ "%s :Too many join requests. Please wait a while and try again." +#define STR_ERR_UMODEUNKNOWNFLAG /* 501 */ ":Unknown MODE flag" +#define STR_ERR_USERSDONTMATCH /* 502 */ ":Cant change mode for other users" +#define STR_ERR_SILELISTFULL /* 511 */ "%s :Your silence list is full" +#define STR_ERR_TOOMANYWATCH /* 512 */ "%s :Maximum size for WATCH-list is 128 entries" +#define STR_ERR_TOOMANYDCC /* 514 */ "%s :Your dcc allow list is full. Maximum size is %d entries" +#define STR_ERR_DISABLED /* 517 */ "%s :%s" /* ircu */ +#define STR_ERR_NOINVITE /* 518 */ ":Cannot invite (+V) at channel %s" +#define STR_ERR_OPERONLY /* 520 */ ":Cannot join channel %s (IRCops only)" +#define STR_ERR_LISTSYNTAX /* 521 */ ":Bad list syntax, type /quote list ? or /raw list ?" +#define STR_ERR_WHOLIMEXCEED /* 523 */ ":Error, /who limit of %d exceeded. Please narrow your search down and try again" +#define STR_ERR_OPERSPVERIFY /* 524 */ ":Trying to join +s or +p channel as an oper. Please invite yourself first." +#define STR_ERR_CANTSENDTOUSER /* 531 */ "%s :%s" +#define STR_RPL_REAWAY /* 597 */ "%s %s %s %lld :%s" +#define STR_RPL_GONEAWAY /* 598 */ "%s %s %s %lld :%s" +#define STR_RPL_NOTAWAY /* 599 */ "%s %s %s %lld :is no longer away" +#define STR_RPL_LOGON /* 600 */ "%s %s %s %lld :logged online" +#define STR_RPL_LOGOFF /* 601 */ "%s %s %s %lld :logged offline" +#define STR_RPL_WATCHOFF /* 602 */ "%s %s %s %lld :stopped watching" +#define STR_RPL_WATCHSTAT /* 603 */ ":You have %d and are on %d WATCH entries" +#define STR_RPL_NOWON /* 604 */ "%s %s %s %lld :is online" +#define STR_RPL_NOWOFF /* 605 */ "%s %s %s %lld :is offline" +#define STR_RPL_WATCHLIST /* 606 */ ":%s" +#define STR_RPL_ENDOFWATCHLIST /* 607 */ ":End of WATCH %c" +#define STR_RPL_NOWISAWAY /* 609 */ "%s %s %s %lld :is away" +#define STR_RPL_MAPMORE /* 610 */ ":%s%-*s --> *more*" +#define STR_RPL_DCCSTATUS /* 617 */ ":%s has been %s your DCC allow list" +#define STR_RPL_DCCLIST /* 618 */ ":%s" +#define STR_RPL_ENDOFDCCLIST /* 619 */ ":End of DCCALLOW %s" +#define STR_RPL_DCCINFO /* 620 */ ":%s" +#define STR_RPL_SPAMCMDFWD /* 659 */ "%s :Command processed, but a copy has been sent to ircops for evaluation (anti-spam) purposes. [%s]" +#define STR_RPL_STARTTLS /* 670 */ ":STARTTLS successful, go ahead with TLS handshake" /* kineircd */ +#define STR_RPL_WHOISSECURE /* 671 */ "%s :%s" /* our variation on the kineircd numeric */ +#define STR_ERR_STARTTLS /* 691 */ ":%s" +#define STR_ERR_INVALIDMODEPARAM /* 696 */ "%s %c %s :%s" +#define STR_RPL_MONONLINE /* 730 */ ":%s!%s@%s" +#define STR_RPL_MONOFFLINE /* 731 */ ":%s" +#define STR_RPL_MONLIST /* 732 */ ":%s" +#define STR_RPL_ENDOFMONLIST /* 733 */ ":End of MONITOR list" +#define STR_ERR_MONLISTFULL /* 734 */ "%d %s :Monitor list is full." +#define STR_ERR_MLOCKRESTRICTED /* 742 */ "%s %c %s :MODE cannot be set due to channel having an active MLOCK restriction policy" +#define STR_RPL_LOGGEDIN /* 900 */ "%s!%s@%s %s :You are now logged in as %s." +#define STR_RPL_LOGGEDOUT /* 901 */ "%s!%s@%s :You are now logged out." +#define STR_RPL_SASLSUCCESS /* 903 */ ":SASL authentication successful" +#define STR_ERR_SASLFAIL /* 904 */ ":SASL authentication failed" +#define STR_ERR_SASLTOOLONG /* 905 */ ":SASL message too long" +#define STR_ERR_SASLABORTED /* 906 */ ":SASL authentication aborted" +#define STR_RPL_SASLMECHS /* 908 */ "%s :are available SASL mechanisms" +#define STR_ERR_CANNOTDOCOMMAND /* 972 */ "%s :%s" +#define STR_ERR_CANNOTCHANGECHANMODE /* 974 */ "%c :%s" diff --git a/include/proto.h b/include/proto.h deleted file mode 100644 index 037d6e6..0000000 --- a/include/proto.h +++ /dev/null @@ -1,68 +0,0 @@ -/************************************************************************ - * Unreal Internet Relay Chat Daemon, include/proto.h - * (C) Dominick Meglio 2000 - * - * 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. - */ - -#ifndef proto_h -#define proto_h -/* lusers.c */ -extern void init_irccounts(void); - -/* match.c */ -extern char *collapse(char *pattern); - -/* scache.c */ -extern void clear_scache_hash_table(void); - -/* send.c */ -extern void sendto_one(Client *, MessageTag *mtags, FORMAT_STRING(const char *), ...) __attribute__((format(printf,3,4))); -extern void sendto_realops(FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,1,2))); - -/* ircd.c */ -extern EVENT(garbage_collect); -extern EVENT(loop_event); -extern EVENT(check_pings); -extern EVENT(handshake_timeout); -extern EVENT(check_deadsockets); -extern EVENT(try_connections); -/* support.c */ -extern char *my_itoa(int i); - -/* s_serv.c */ -extern void load_tunefile(void); -extern EVENT(save_tunefile); -extern void read_motd(const char *filename, MOTDFile *motd); - -/* s_user.c */ -extern int target_limit_exceeded(Client *client, void *target, const char *name); -extern void make_umodestr(void); -extern char *get_usermode_string(Client *acptr); - -/* s_misc.c */ -extern char *convert_time(time_t ltime); - -/* whowas.c */ -extern void initwhowas(void); - -/* uid.c */ -extern void uid_init(void); -extern const char *uid_get(void); - -#endif /* proto_h */ diff --git a/include/setup.h.in b/include/setup.h.in index a4e0830..18f6908 100644 --- a/include/setup.h.in +++ b/include/setup.h.in @@ -115,6 +115,9 @@ /* Define to 1 if you have the `strlncat' function. */ #undef HAVE_STRLNCAT +/* Define to 1 if you have the `strlncpy' function. */ +#undef HAVE_STRLNCPY + /* Define to 1 if you have the `syslog' function. */ #undef HAVE_SYSLOG @@ -130,9 +133,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H -/* Define if you want modes shown in /list */ -#undef LIST_SHOW_MODES - /* Define the location of the log files */ #undef LOGDIR @@ -181,9 +181,6 @@ /* Define the path of the pid file */ #undef PIDFILE -/* Define if you want +a/+q prefixes */ -#undef PREFIX_AQ - /* Define the location of private libraries */ #undef PRIVATELIBDIR diff --git a/include/struct.h b/include/struct.h index 2e1e33e..d820f2b 100644 --- a/include/struct.h +++ b/include/struct.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "common.h" #include "sys.h" #include @@ -54,6 +55,14 @@ # ifdef SYSSYSLOGH # include # endif +#ifndef UNREAL_LOGGER_CODE +/* undef these as they cause confusion with our ULOG_xxx codes */ +#undef LOG_DEBUG +#undef LOG_INFO +#undef LOG_WARNING +#undef LOG_ERROR +#undef LOG_FATAL +#endif #endif #define PCRE2_CODE_UNIT_WIDTH 8 #include "pcre2.h" @@ -92,19 +101,15 @@ typedef struct ConfigFlag_allow ConfigFlag_allow; typedef struct ConfigItem_allow_channel ConfigItem_allow_channel; typedef struct ConfigItem_allow_dcc ConfigItem_allow_dcc; typedef struct ConfigItem_vhost ConfigItem_vhost; -typedef struct ConfigItem_except ConfigItem_except; typedef struct ConfigItem_link ConfigItem_link; typedef struct ConfigItem_ban ConfigItem_ban; typedef struct ConfigItem_deny_dcc ConfigItem_deny_dcc; typedef struct ConfigItem_deny_link ConfigItem_deny_link; typedef struct ConfigItem_deny_channel ConfigItem_deny_channel; typedef struct ConfigItem_deny_version ConfigItem_deny_version; -typedef struct ConfigItem_log ConfigItem_log; -typedef struct ConfigItem_unknown ConfigItem_unknown; -typedef struct ConfigItem_unknown_ext ConfigItem_unknown_ext; typedef struct ConfigItem_alias ConfigItem_alias; typedef struct ConfigItem_alias_format ConfigItem_alias_format; -typedef struct ConfigItem_include ConfigItem_include; +typedef struct ConfigResource ConfigResource; typedef struct ConfigItem_blacklist_module ConfigItem_blacklist_module; typedef struct ConfigItem_help ConfigItem_help; typedef struct ConfigItem_offchans ConfigItem_offchans; @@ -129,9 +134,6 @@ typedef struct Mode Mode; typedef struct MessageTag MessageTag; typedef struct MOTDFile MOTDFile; /* represents a whole MOTD, including remote MOTD support info */ typedef struct MOTDLine MOTDLine; /* one line of a MOTD stored as a linked list */ -#ifdef USE_LIBCURL -typedef struct MOTDDownload MOTDDownload; /* used to coordinate download of a remote MOTD */ -#endif typedef struct RealCommand RealCommand; typedef struct CommandOverride CommandOverride; @@ -163,13 +165,11 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia #include "dbuf.h" /* THIS REALLY SHOULDN'T BE HERE!!! --msa */ #endif -#define HOSTLEN 63 /* Length of hostname. Updated to */ - /* comply with RFC1123 */ - +#define HOSTLEN 63 /* Length of hostname */ #define NICKLEN 30 #define USERLEN 10 #define REALLEN 50 -#define SVIDLEN 30 +#define ACCOUNTLEN 30 #define MAXTOPICLEN 360 /* absolute maximum permitted topic length (above this = potential desync) */ #define MAXAWAYLEN 360 /* absolute maximum permitted away length (above this = potential desync) */ #define MAXKICKLEN 360 /* absolute maximum kick length (above this = only cutoff danger) */ @@ -182,8 +182,8 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia #define READBUFSIZE 8192 /* for the read buffer */ #define MAXRECIPIENTS 20 #define MAXSILELENGTH NICKLEN+USERLEN+HOSTLEN+10 -#define IDLEN 10 -#define SIDLEN 3 +#define IDLEN 12 +#define SIDLEN 3 #define SWHOISLEN 256 #define UMODETABLESZ (sizeof(long) * 8) #define MAXCCUSERS 20 /* Maximum for set::anti-flood::max-concurrent-conversations */ @@ -203,16 +203,77 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia /* Logging types */ #define LOG_ERROR 0x0001 #define LOG_KILL 0x0002 -#define LOG_TKL 0x0004 -#define LOG_KLINE 0x0008 -#define LOG_CLIENT 0x0010 -#define LOG_SERVER 0x0020 -#define LOG_OPER 0x0040 #define LOG_SACMDS 0x0080 #define LOG_CHGCMDS 0x0100 #define LOG_OVERRIDE 0x0200 -#define LOG_SPAMFILTER 0x0400 -#define LOG_FLOOD 0x0800 + +typedef enum LogFieldType { + LOG_FIELD_INTEGER, // and unsigned? + LOG_FIELD_STRING, + LOG_FIELD_CLIENT, + LOG_FIELD_CHANNEL, + LOG_FIELD_OBJECT +} LogFieldType; + +typedef struct LogData { + LogFieldType type; + char *key; + union { + int64_t integer; + char *string; + Client *client; + Channel *channel; + json_t *object; + } value; +} LogData; + +/** New log levels for unreal_log() */ +/* Note: the reason for these high numbers is so we can easily catch + * if someone makes a mistake to use LOG_INFO (from syslog.h) instead + * of the ULOG_xxx levels. + */ +typedef enum LogLevel { + ULOG_INVALID = 0, + ULOG_DEBUG = 1000, + ULOG_INFO = 2000, + ULOG_WARNING = 3000, + ULOG_ERROR = 4000, + ULOG_FATAL = 5000 +} LogLevel; + +/** Logging types (text, json, etc) */ +typedef enum LogType { + LOG_TYPE_INVALID = 0, + LOG_TYPE_TEXT = 1, + LOG_TYPE_JSON = 2, +} LogType; + +#define LOG_CATEGORY_LEN 32 +#define LOG_EVENT_ID_LEN 64 +typedef struct LogSource LogSource; +struct LogSource { + LogSource *prev, *next; + LogLevel loglevel; + char negative; /**< 1 if negative match (eg !operoverride), 0 if normal */ + char subsystem[LOG_CATEGORY_LEN+1]; + char event_id[LOG_EVENT_ID_LEN+1]; +}; + +typedef struct Log Log; +struct Log { + Log *prev, *next; + LogSource *sources; + char destination[CHANNELLEN+1]; + char *file; + char *filefmt; + long maxsize; + int type; + int logfd; +}; + +/** This is used for deciding the in logs[] and temp_logs[] */ +typedef enum LogDestination { LOG_DEST_SNOMASK=0, LOG_DEST_OPER=1, LOG_DEST_REMOTE=2, LOG_DEST_CHANNEL=3, LOG_DEST_DISK=4 } LogDestination; +#define NUM_LOG_DESTINATIONS 5 /* ** 'offsetof' is defined in ANSI-C. The following definition @@ -251,7 +312,7 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia /** This specifies the current client status or the client type - see @link ClientStatus @endlink in particular. * You may think "server" or "client" are the only choices here, but there are many more - * such as states where the user is in the middle of an SSL/TLS handshake. + * such as states where the user is in the middle of an TLS handshake. * @defgroup ClientStatuses Client statuses / types * @{ */ @@ -259,8 +320,8 @@ typedef enum ClientStatus { 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 */ - CLIENT_STATUS_TLS_CONNECT_HANDSHAKE = -5, /**< Client is doing an SSL/TLS handshake - outgoing connection */ - CLIENT_STATUS_TLS_ACCEPT_HANDSHAKE = -4, /**< Client is doing an SSL/TLS handshake - incoming connection */ + CLIENT_STATUS_TLS_CONNECT_HANDSHAKE = -5, /**< Client is doing an TLS handshake - outgoing connection */ + CLIENT_STATUS_TLS_ACCEPT_HANDSHAKE = -4, /**< Client is doing an TLS handshake - incoming connection */ CLIENT_STATUS_HANDSHAKE = -3, /**< Client is doing a server handshake - outgoing connection */ CLIENT_STATUS_ME = -2, /**< Client is &me (this server) */ CLIENT_STATUS_UNKNOWN = -1, /**< Client is doing a hanshake. May become a server or user later, we don't know yet */ @@ -325,16 +386,17 @@ typedef enum ClientStatus { #define CLIENT_FLAG_DCCNOTICE 0x00200000 /**< Has the user seen a notice on how to use DCCALLOW already? */ #define CLIENT_FLAG_SHUNNED 0x00400000 /**< Connection is shunned (user cannot execute any commands) */ #define CLIENT_FLAG_VIRUS 0x00800000 /**< Tagged by spamfilter as a virus */ -#define CLIENT_FLAG_TLS 0x01000000 /**< Connection is using SSL/TLS */ +#define CLIENT_FLAG_TLS 0x01000000 /**< Connection is using TLS */ #define CLIENT_FLAG_NOFAKELAG 0x02000000 /**< Exemption from fake lag */ #define CLIENT_FLAG_DCCBLOCK 0x04000000 /**< Block all DCC send requests */ #define CLIENT_FLAG_MAP 0x08000000 /**< Show this entry in /MAP (only used in map module) */ #define CLIENT_FLAG_PINGWARN 0x10000000 /**< Server ping warning (remote server slow with responding to PINGs) */ #define CLIENT_FLAG_NOHANDSHAKEDELAY 0x20000000 /**< No handshake delay */ +#define CLIENT_FLAG_SERVER_DISCONNECT_LOGGED 0x40000000 /**< Server disconnect message is (already) logged */ + /** @} */ -#define SNO_DEFOPER "+kscfvGqobS" -#define SNO_DEFUSER "+ks" +#define OPER_SNOMASKS "+bBcdfkqsSoO" #define SEND_UMODES (SendUmodes) #define ALL_UMODES (AllUmodes) @@ -346,13 +408,14 @@ typedef enum ClientStatus { * Note that client protocol extensions have been moved * to the ClientCapability API which uses acptr->local->caps. */ -#define PROTO_VL 0x000040 /* Negotiated VL protocol */ -#define PROTO_VHP 0x000100 /* Send hostnames in NICKv2 even if not sethosted */ -#define PROTO_CLK 0x001000 /* Send cloaked host in the NICK command (regardless of +x/-x) */ -#define PROTO_MLOCK 0x002000 /* server supports MLOCK */ -#define PROTO_EXTSWHOIS 0x004000 /* extended SWHOIS support */ -#define PROTO_SJSBY 0x008000 /* SJOIN setby information (TS and nick) */ -#define PROTO_MTAGS 0x010000 /* Support message tags and big buffers */ +#define PROTO_VL 0x000001 /* Negotiated VL protocol */ +#define PROTO_VHP 0x000002 /* Send hostnames in NICKv2 even if not sethosted */ +#define PROTO_CLK 0x000004 /* Send cloaked host in the NICK command (regardless of +x/-x) */ +#define PROTO_MLOCK 0x000008 /* server supports MLOCK */ +#define PROTO_EXTSWHOIS 0x000010 /* extended SWHOIS support */ +#define PROTO_SJSBY 0x000020 /* SJOIN setby information (TS and nick) */ +#define PROTO_MTAGS 0x000040 /* Support message tags and big buffers */ +#define PROTO_NEXTBANS 0x000080 /* Server supports named extended bans */ /* For client capabilities: */ #define CAP_INVERT 1L @@ -368,41 +431,21 @@ typedef enum ClientStatus { #define IsDeaf(x) ((x)->umodes & UMODE_DEAF) #define IsOper(x) ((x)->umodes & UMODE_OPER) #define IsInvisible(x) ((x)->umodes & UMODE_INVISIBLE) -#define IsARegNick(x) ((x)->umodes & (UMODE_REGNICK)) #define IsRegNick(x) ((x)->umodes & UMODE_REGNICK) -#define SendWallops(x) (!IsMe(x) && IsUser(x) && ((x)->umodes & UMODE_WALLOP)) #define IsHidden(x) ((x)->umodes & UMODE_HIDE) #define IsSetHost(x) ((x)->umodes & UMODE_SETHOST) #define IsHideOper(x) ((x)->umodes & UMODE_HIDEOPER) #define SetOper(x) ((x)->umodes |= UMODE_OPER) #define SetInvisible(x) ((x)->umodes |= UMODE_INVISIBLE) -#define SetWallops(x) ((x)->umodes |= UMODE_WALLOP) #define SetRegNick(x) ((x)->umodes & UMODE_REGNICK) #define SetHidden(x) ((x)->umodes |= UMODE_HIDE) #define SetHideOper(x) ((x)->umodes |= UMODE_HIDEOPER) #define IsSecureConnect(x) ((x)->umodes & UMODE_SECURE) #define ClearOper(x) ((x)->umodes &= ~UMODE_OPER) #define ClearInvisible(x) ((x)->umodes &= ~UMODE_INVISIBLE) -#define ClearWallops(x) ((x)->umodes &= ~UMODE_WALLOP) #define ClearHidden(x) ((x)->umodes &= ~UMODE_HIDE) #define ClearHideOper(x) ((x)->umodes &= ~UMODE_HIDEOPER) -/* Snomask macros: */ -#define SendServNotice(x) (((x)->user) && ((x)->user->snomask & SNO_SNOTICE)) -#define IsKillsF(x) ((x)->user->snomask & SNO_KILLS) -#define IsClientF(x) ((x)->user->snomask & SNO_CLIENT) -#define IsFloodF(x) ((x)->user->snomask & SNO_FLOOD) -#define IsEyes(x) ((x)->user->snomask & SNO_EYES) -#define SetKillsF(x) ((x)->user->snomask |= SNO_KILLS) -#define SetClientF(x) ((x)->user->snomask |= SNO_CLIENT) -#define SetFloodF(x) ((x)->user->snomask |= SNO_FLOOD) -#define SetEyes(x) ((x)->user->snomask |= SNO_EYES) -#define ClearKillsF(x) ((x)->user->snomask &= ~SNO_KILLS) -#define ClearClientF(x) ((x)->user->snomask &= ~SNO_CLIENT) -#define ClearFloodF(x) ((x)->user->snomask &= ~SNO_FLOOD) -#define ClearEyes(x) ((x)->user->snomask &= ~SNO_EYES) - - /* Client flags macros: to check for via IsXX(), * to set via SetXX() and to clear the flag via ClearXX() */ @@ -416,6 +459,7 @@ typedef enum ClientStatus { #define IsDCCNotice(x) ((x)->flags & CLIENT_FLAG_DCCNOTICE) #define IsDead(x) ((x)->flags & CLIENT_FLAG_DEAD) #define IsDeadSocket(x) ((x)->flags & CLIENT_FLAG_DEADSOCKET) +#define IsServerDisconnectLogged(x) ((x)->flags & CLIENT_FLAG_SERVER_DISCONNECT_LOGGED) #define IsUseIdent(x) ((x)->flags & CLIENT_FLAG_USEIDENT) #define IsDNSLookup(x) ((x)->flags & CLIENT_FLAG_DNSLOOKUP) #define IsEAuth(x) ((x)->flags & CLIENT_FLAG_EAUTH) @@ -447,6 +491,7 @@ typedef enum ClientStatus { #define SetDCCNotice(x) do { (x)->flags |= CLIENT_FLAG_DCCNOTICE; } while(0) #define SetDead(x) do { (x)->flags |= CLIENT_FLAG_DEAD; } while(0) #define SetDeadSocket(x) do { (x)->flags |= CLIENT_FLAG_DEADSOCKET; } while(0) +#define SetServerDisconnectLogged(x) do { (x)->flags |= CLIENT_FLAG_SERVER_DISCONNECT_LOGGED; } while(0) #define SetUseIdent(x) do { (x)->flags |= CLIENT_FLAG_USEIDENT; } while(0) #define SetDNSLookup(x) do { (x)->flags |= CLIENT_FLAG_DNSLOOKUP; } while(0) #define SetEAuth(x) do { (x)->flags |= CLIENT_FLAG_EAUTH; } while(0) @@ -508,9 +553,9 @@ typedef enum ClientStatus { #define IsNotSpoof(x) ((x)->local->nospoof == 0) #define GetHost(x) (IsHidden(x) ? (x)->user->virthost : (x)->user->realhost) #define GetIP(x) (x->ip ? x->ip : "255.255.255.255") -#define IsLoggedIn(x) (IsRegNick(x) || (x->user && (*x->user->svid != '*') && !isdigit(*x->user->svid))) /* registered nick (+r) or just logged into services (may be -r) */ -#define IsSynched(x) (x->serv->flags.synced) -#define IsServerSent(x) (x->serv && x->serv->flags.server_sent) +#define IsLoggedIn(x) (x->user && (*x->user->account != '*') && !isdigit(*x->user->account)) /**< Logged into services */ +#define IsSynched(x) (x->server->flags.synced) +#define IsServerSent(x) (x->server && x->server->flags.server_sent) /* And more that access client stuff - but actually modularized */ #define GetReputation(client) (moddata_client_get(client, "reputation") ? atoi(moddata_client_get(client, "reputation")) : 0) /**< Get reputation value for a client */ @@ -527,33 +572,14 @@ typedef enum ClientStatus { #define SupportVHP(x) (CHECKSERVERPROTO(x, PROTO_VHP)) #define SupportCLK(x) (CHECKSERVERPROTO(x, PROTO_CLK)) #define SupportMTAGS(x) (CHECKSERVERPROTO(x, PROTO_MTAGS)) +#define SupportNEXTBANS(x) (CHECKSERVERPROTO(x, PROTO_NEXTBANS)) #define SetVL(x) ((x)->local->proto |= PROTO_VL) #define SetSJSBY(x) ((x)->local->proto |= PROTO_SJSBY) #define SetVHP(x) ((x)->local->proto |= PROTO_VHP) #define SetCLK(x) ((x)->local->proto |= PROTO_CLK) #define SetMTAGS(x) ((x)->local->proto |= PROTO_MTAGS) - -/* - * defined debugging levels - */ -#define DEBUG_FATAL 0 -#define DEBUG_ERROR 1 /* report_error() and other errors that are found */ -#define DEBUG_NOTICE 3 -#define DEBUG_DNS 4 /* used by all DNS related routines - a *lot* */ -#define DEBUG_INFO 5 /* general usful info */ -#define DEBUG_NUM 6 /* numerics */ -#define DEBUG_SEND 7 /* everything that is sent out */ -#define DEBUG_DEBUG 8 /* anything to do with debugging, ie unimportant :) */ -#define DEBUG_MALLOC 9 /* malloc/free calls */ -#define DEBUG_LIST 10 /* debug list use */ - -/* - * defines for curses in client - */ -#define DUMMY_TERM 0 -#define CURSES_TERM 1 -#define TERMCAP_TERM 2 +#define SetNEXTBANS(x) ((x)->local->proto |= PROTO_NEXTBANS) /* Dcc deny types (see src/s_extra.c) */ #define DCCDENY_HARD 0 @@ -576,12 +602,20 @@ union ModData #ifndef _WIN32 #define CHECK_LIST_ENTRY(list) if (offsetof(typeof(*list),prev) != offsetof(ListStruct,prev)) \ { \ - ircd_log(LOG_ERROR, "[BUG] %s:%d: List operation on struct with incorrect order (->prev must be 1st struct member)", __FILE__, __LINE__); \ + unreal_log(ULOG_FATAL, "main", "BUG_LIST_OPERATION", NULL, \ + "[BUG] $file:$line: List operation on struct with incorrect order ($error_details)", \ + log_data_string("error_details", "->prev must be 1st struct member"), \ + log_data_string("file", __FILE__), \ + log_data_integer("line", __LINE__)); \ abort(); \ } \ if (offsetof(typeof(*list),next) != offsetof(ListStruct,next)) \ { \ - ircd_log(LOG_ERROR, "[BUG] %s:%d: List operation on struct with incorrect order (->next must be 2nd struct member))", __FILE__, __LINE__); \ + unreal_log(ULOG_FATAL, "main", "BUG_LIST_OPERATION", NULL, \ + "[BUG] $file:$line: List operation on struct with incorrect order ($error_details)", \ + log_data_string("error_details", "->next must be 2nd struct member"), \ + log_data_string("file", __FILE__), \ + log_data_integer("line", __LINE__)); \ abort(); \ } #else @@ -591,17 +625,29 @@ union ModData #ifndef _WIN32 #define CHECK_PRIO_LIST_ENTRY(list) if (offsetof(typeof(*list),prev) != offsetof(ListStructPrio,prev)) \ { \ - ircd_log(LOG_ERROR, "[BUG] %s:%d: List operation on struct with incorrect order (->prev must be 1st struct member)", __FILE__, __LINE__); \ + unreal_log(ULOG_FATAL, "main", "BUG_LIST_OPERATION", NULL, \ + "[BUG] $file:$line: List operation on struct with incorrect order ($error_details)", \ + log_data_string("error_details", "->prev must be 1st struct member"), \ + log_data_string("file", __FILE__), \ + log_data_integer("line", __LINE__)); \ abort(); \ } \ if (offsetof(typeof(*list),next) != offsetof(ListStructPrio,next)) \ { \ - ircd_log(LOG_ERROR, "[BUG] %s:%d: List operation on struct with incorrect order (->next must be 2nd struct member))", __FILE__, __LINE__); \ + unreal_log(ULOG_FATAL, "main", "BUG_LIST_OPERATION", NULL, \ + "[BUG] $file:$line: List operation on struct with incorrect order ($error_details)", \ + log_data_string("error_details", "->next must be 2nd struct member"), \ + log_data_string("file", __FILE__), \ + log_data_integer("line", __LINE__)); \ abort(); \ } \ if (offsetof(typeof(*list),priority) != offsetof(ListStructPrio,priority)) \ { \ - ircd_log(LOG_ERROR, "[BUG] %s:%d: List operation on struct with incorrect order (->priority must be 3rd struct member))", __FILE__, __LINE__); \ + unreal_log(ULOG_FATAL, "main", "BUG_LIST_OPERATION", NULL, \ + "[BUG] $file:$line: List operation on struct with incorrect order ($error_details)", \ + log_data_string("error_details", "->priority must be 3rd struct member"), \ + log_data_string("file", __FILE__), \ + log_data_integer("line", __LINE__)); \ abort(); \ } #else @@ -610,7 +656,10 @@ union ModData #define CHECK_NULL_LIST_ITEM(item) if ((item)->prev || (item)->next) \ { \ - ircd_log(LOG_ERROR, "[BUG] %s:%d: List operation on item with non-NULL 'prev' or 'next' -- are you adding to a list twice?", __FILE__, __LINE__); \ + unreal_log(ULOG_FATAL, "main", "BUG_LIST_OPERATION_DOUBLE_ADD", NULL, \ + "[BUG] $file:$line: List operation on item with non-NULL 'prev' or 'next' -- are you adding to a list twice?", \ + log_data_string("file", __FILE__), \ + log_data_integer("line", __LINE__)); \ abort(); \ } @@ -711,43 +760,10 @@ struct MultiLine { char *line; }; -#ifdef USE_LIBCURL -struct MOTDDownload -{ - MOTDFile *themotd; -}; -#endif /* USE_LIBCURL */ - struct MOTDFile { struct MOTDLine *lines; struct tm last_modified; /* store the last modification time */ - -#ifdef USE_LIBCURL - /* - This pointer is used to communicate with an asynchronous MOTD - download. The problem is that a download may take 10 seconds or - more to complete and, in that time, the IRCd could be rehashed. - This would mean that TLD blocks are reallocated and thus the - aMotd structs would be free()d in the meantime. - - To prevent such a situation from leading to a segfault, we - introduce this remote control pointer. It works like this: - 1. read_motd() is called with a URL. A new MOTDDownload is - allocated and the pointer is placed here. This pointer is - also passed to the asynchrnous download handler. - 2.a. The download is completed and read_motd_async_downloaded() - is called with the same pointer. From this function, this pointer - if free()d. No other code may free() the pointer. Not even free_motd(). - OR - 2.b. The user rehashes the IRCd before the download is completed. - free_motd() is called, which sets motd_download->themotd to NULL - to signal to read_motd_async_downloaded() that it should ignore - the download. read_motd_async_downloaded() is eventually called - and frees motd_download. - */ - struct MOTDDownload *motd_download; -#endif /* USE_LIBCURL */ }; struct MOTDLine { @@ -758,16 +774,17 @@ struct MOTDLine { struct LoopStruct { unsigned do_garbage_collect : 1; unsigned config_test : 1; - unsigned ircd_booted : 1; - unsigned ircd_forked : 1; + unsigned booted : 1; + unsigned forked : 1; 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 ircd_rehashing : 1; - unsigned ircd_terminating : 1; + 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; - Client *rehash_save_cptr, *rehash_save_client; - int rehash_save_sig; + Client *rehash_save_client; void (*boot_function)(); }; @@ -801,7 +818,7 @@ typedef struct Whowas { struct Whowas *prev; /* for hash table... */ struct Whowas *cnext; /* for client struct linked list */ struct Whowas *cprev; /* for client struct linked list */ -} aWhowas; +} WhoWas; typedef struct SWhois SWhois; struct SWhois { @@ -848,7 +865,7 @@ struct SWhois { * Note that reading parv[parc] and beyond is OUT OF BOUNDS and will cause a crash. * E.g. parv[3] in the above example is out of bounds. */ -#define CMD_FUNC(x) void (x) (Client *client, MessageTag *recv_mtags, int parc, char *parv[]) +#define CMD_FUNC(x) void (x) (Client *client, MessageTag *recv_mtags, int parc, const char *parv[]) /** @} */ /** Command override function - used by all command override handlers. @@ -865,13 +882,13 @@ struct SWhois { * Note that reading parv[parc] and beyond is OUT OF BOUNDS and will cause a crash. * E.g. parv[3] in the above example. */ -#define CMD_OVERRIDE_FUNC(x) void (x)(CommandOverride *ovr, Client *client, MessageTag *recv_mtags, int parc, char *parv[]) +#define CMD_OVERRIDE_FUNC(x) void (x)(CommandOverride *ovr, Client *client, MessageTag *recv_mtags, int parc, const char *parv[]) -typedef void (*CmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[]); -typedef void (*AliasCmdFunc)(Client *client, MessageTag *mtags, int parc, char *parv[], char *cmd); -typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, char *parv[]); +typedef void (*CmdFunc)(Client *client, MessageTag *mtags, int parc, const char *parv[]); +typedef void (*AliasCmdFunc)(Client *client, MessageTag *mtags, int parc, const char *parv[], const char *cmd); +typedef void (*OverrideCmdFunc)(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, const char *parv[]); #include @@ -1106,21 +1123,30 @@ struct SpamExcept { /** IRC Counts, used for /LUSERS */ typedef struct IRCCounts IRCCounts; struct IRCCounts { - int clients; /* total */ - int invisible; /* invisible */ - unsigned short servers; /* servers */ - int operators; /* operators */ - int unknown; /* unknown local connections */ - int channels; /* channels */ - int me_clients; /* my clients */ - unsigned short me_servers; /* my servers */ - int me_max; /* local max */ - int global_max; /* global max */ + int clients; /* total */ + int invisible; /* invisible */ + int servers; /* servers */ + int operators; /* operators */ + int unknown; /* unknown local connections */ + int channels; /* channels */ + int me_clients; /* my clients */ + int me_servers; /* my servers */ + int me_max; /* local max */ + int global_max; /* global max */ }; /** The /LUSERS stats information */ extern MODVAR IRCCounts irccounts; +typedef struct NameValue NameValue; +/** Name and value list used in a static array, such as in conf.c */ +struct NameValue +{ + long value; + char *name; +}; + +/** Name and value list used in dynamic linked lists */ typedef struct NameValueList NameValueList; struct NameValueList { NameValueList *prev, *next; @@ -1166,21 +1192,12 @@ struct CommandOverride { OverrideCmdFunc func; }; -extern MODVAR Umode *Usermode_Table; -extern MODVAR short Usermode_highest; - -extern MODVAR Snomask *Snomask_Table; -extern MODVAR short Snomask_highest; - -extern MODVAR Cmode *Channelmode_Table; -extern MODVAR unsigned short Channelmode_highest; +extern MODVAR Umode *usermodes; +extern MODVAR Cmode *channelmodes; extern Umode *UmodeAdd(Module *module, char ch, int options, int unset_on_deoper, int (*allowed)(Client *client, int what), long *mode); extern void UmodeDel(Umode *umode); -extern Snomask *SnomaskAdd(Module *module, char ch, int (*allowed)(Client *client, int what), long *mode); -extern void SnomaskDel(Snomask *sno); - extern Cmode *CmodeAdd(Module *reserved, CmodeInfo req, Cmode_t *mode); extern void CmodeDel(Cmode *cmode); @@ -1199,16 +1216,13 @@ extern void unload_all_unused_moddata(void); #define IsServersOnlyListener(x) ((x) && ((x)->options & LISTENER_SERVERSONLY)) #define CONNECT_TLS 0x000001 -//0x000002 unused (was ziplinks) -#define CONNECT_AUTO 0x000004 -#define CONNECT_QUARANTINE 0x000008 -#define CONNECT_NODNSCACHE 0x000010 -#define CONNECT_NOHOSTCHECK 0x000020 -#define CONNECT_INSECURE 0x000040 +#define CONNECT_AUTO 0x000002 +#define CONNECT_QUARANTINE 0x000004 +#define CONNECT_INSECURE 0x000008 -#define TLSFLAG_FAILIFNOCERT 0x1 -#define TLSFLAG_NOSTARTTLS 0x8 -#define TLSFLAG_DISABLECLIENTCERT 0x10 +#define TLSFLAG_FAILIFNOCERT 0x0001 +#define TLSFLAG_NOSTARTTLS 0x0002 +#define TLSFLAG_DISABLECLIENTCERT 0x0004 /** Flood counters for local clients */ typedef struct FloodCounter { @@ -1225,9 +1239,17 @@ typedef enum FloodOption { FLD_INVITE = 3, /**< invite-flood */ FLD_KNOCK = 4, /**< knock-flood */ FLD_CONVERSATIONS = 5, /**< max-concurrent-conversations */ + FLD_LAG_PENALTY = 6, /**< lag-penalty / lag-penalty-bytes */ } FloodOption; #define MAXFLOODOPTIONS 10 +typedef struct TrafficStats TrafficStats; +struct TrafficStats { + long long messages_sent; /* IRC lines sent */ + long long messages_received; /* IRC lines received */ + long long bytes_sent; /* Bytes sent */ + long long bytes_received; /* Received bytes */ +}; /** 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 @@ -1242,7 +1264,7 @@ struct Client { struct list_head special_node; /**< For special lists (server || unknown || oper) */ LocalClient *local; /**< Additional information regarding locally connected clients */ User *user; /**< Additional information, if this client is a user */ - Server *serv; /**< Additional information, if this is a server */ + Server *server; /**< Additional information, if this is a server */ ClientStatus status; /**< Client status, one of CLIENT_STATUS_* */ struct list_head client_hash; /**< For name hash table (clientTable) */ char name[HOSTLEN + 1]; /**< Unique name of the client: nickname for users, hostname for servers */ @@ -1257,7 +1279,7 @@ struct Client { char info[REALLEN + 1]; /**< Additional client information text. For users this is gecos/realname */ char id[IDLEN + 1]; /**< Unique ID: SID or UID */ struct list_head id_hash; /**< For UID/SID hash table (idTable) */ - Client *srvptr; /**< Server on where this client is connected to (can be &me) */ + Client *uplink; /**< Server on where this client is connected to (can be &me) */ char *ip; /**< IP address of user or server (never NULL) */ ModData moddata[MODDATA_MAX_CLIENT]; /**< Client attached module data, used by the ModData system */ }; @@ -1266,10 +1288,11 @@ struct Client { */ struct LocalClient { int fd; /**< File descriptor, can be <0 if socket has been closed already. */ - SSL *ssl; /**< OpenSSL/LibreSSL struct for SSL/TLS connection */ - time_t since; /**< Time when user will next be allowed to send something (actually since0 if SASL authentication was successful */ time_t sasl_sent_time; /**< SASL: 0 or the time that the (last) AUTHENTICATE command has been sent */ - char *sni_servername; /**< Servername as sent by client via SNI (Server Name Indication) in SSL/TLS, otherwise NULL */ + char *sni_servername; /**< Servername as sent by client via SNI (Server Name Indication) in TLS, otherwise NULL */ int cap_protocol; /**< CAP protocol in use. At least 300 for any CAP capable client. 302 for 3.2, etc.. */ uint32_t nospoof; /**< Anti-spoofing random number (used in user handshake PING/PONG) */ char *passwd; /**< Password used during connect, if any (freed once connected and set to NULL) */ @@ -1315,38 +1327,27 @@ struct LocalClient { */ struct User { Membership *channel; /**< Channels that the user is in (linked list) */ - Link *invited; /**< Channels has the user been invited to (linked list) */ Link *dccallow; /**< DCCALLOW list (linked list) */ - char *away; /**< AWAY message, or NULL if not away */ - char svid[SVIDLEN + 1]; /**< Services account name or ID (SVID) */ - unsigned short joined; /**< Number of channels joined */ + char account[ACCOUNTLEN + 1]; /**< Services account name or ID (SVID) - use IsLoggedIn(client) to check if logged in */ + int joined; /**< Number of channels joined */ char username[USERLEN + 1]; /**< Username, the user portion in nick!user@host. */ char realhost[HOSTLEN + 1]; /**< Realhost, the real host of the user (IP or hostname) - usually this is not shown to other users */ char cloakedhost[HOSTLEN + 1]; /**< Cloaked host - generated by cloaking algorithm */ char *virthost; /**< Virtual host - when user has user mode +x this is the active host */ char *server; /**< Server name the user is on (?) */ SWhois *swhois; /**< Special "additional" WHOIS entries such as "a Network Administrator" */ - aWhowas *whowas; /**< Something for whowas :D :D */ - int snomask; /**< Server Notice Mask (snomask) - only for IRCOps */ - char *operlogin; /**< Which oper { } block was used to oper up, otherwise NULL - used by oper::maxlogins */ - struct { - time_t nick_t; /**< For set::anti-flood::nick-flood: time */ - time_t knock_t; /**< For set::anti-flood::knock-flood: time */ - time_t invite_t; /**< For set::anti-flood::invite-flood: time */ - unsigned char nick_c; /**< For set::anti-flood::nick-flood: counter */ - unsigned char knock_c; /**< For set::anti-flood::knock-flood: counter */ - unsigned char invite_c; /**< For set::anti-flood::invite-flood: counter */ - } flood; /**< Anti-flood counters */ - time_t lastaway; /**< Last time the user went AWAY */ + WhoWas *whowas; /**< Something for whowas :D :D */ + char *snomask; /**< Server Notice Mask (snomask) - only for IRCOps */ + char *operlogin; /**< Which oper { } block was used to oper up, otherwise NULL - used for auditting and by oper::maxlogins */ + char *away; /**< AWAY message, or NULL if not away */ + time_t away_since; /**< Last time the user went AWAY */ }; -/** Server information (local servers and remote servers), you use client->serv to access these (see also @link Client @endlink). +/** Server information (local servers and remote servers), you use client->server to access these (see also @link Client @endlink). */ struct Server { - char *up; /**< Name of uplink for this server */ char by[NICKLEN + 1]; /**< Uhhhh - who activated this connection - AGAIN? */ ConfigItem_link *conf; /**< link { } block associated with this server, or NULL */ - time_t timestamp; /**< Remotely determined connect try time */ long users; /**< Number of users on this server */ time_t boottime; /**< Startup time of server (boot time) */ struct { @@ -1424,20 +1425,29 @@ struct ConditionalConfig char *opt; /**< Only for IF_VALUE */ }; +/** Configuration file (config parser) */ struct ConfigFile { - char *cf_filename; - ConfigEntry *cf_entries; - ConfigFile *cf_next; + char *filename; /**< Filename of configuration file */ + ConfigEntry *items; /**< All items in the configuration file */ + ConfigFile *next; /**< Next configuration file */ }; +/** Configuration entry (config parser) */ struct ConfigEntry { - ConfigFile *ce_fileptr; - int ce_varlinenum, ce_fileposstart, ce_fileposend, ce_sectlinenum; - char *ce_varname, *ce_vardata; - ConfigEntry *ce_entries, *ce_prevlevel, *ce_next; - ConditionalConfig *ce_cond; + char *name; /**< Variable name */ + char *value; /**< Variable value, can be NULL */ + ConfigEntry *next; /**< Next ConfigEntry */ + ConfigEntry *items; /**< Items (children), can be NULL */ + ConfigFile *file; /**< To which configfile does this belong? */ + int line_number; /**< Line number of the variable name (this one is usually used for errors) */ + int file_position_start; /**< Position (byte) within configuration file of the start of the block, rarely used */ + int file_position_end; /**< Position (byte) within configuration file of the end of the block, rarely used */ + int section_linenumber; /**< Line number of the section (only used internally for parse errors) */ + ConfigEntry *parent; /**< Parent item, can be NULL */ + ConditionalConfig *conditional_config; /**< Used for conditional config by the main parser */ + unsigned escaped:1; }; struct ConfigFlag @@ -1524,8 +1534,7 @@ struct ConfigFlag_allow { struct ConfigItem_allow { ConfigItem_allow *prev, *next; ConfigFlag flag; - char *ip; - char *hostname; + ConfigItem_mask *mask; char *server; AuthConfig *auth; int maxperip; /**< Maximum connections permitted per IP address (locally) */ @@ -1533,7 +1542,7 @@ struct ConfigItem_allow { int port; ConfigItem_class *class; ConfigFlag_allow flags; - unsigned short ipv6_clone_mask; + int ipv6_clone_mask; }; struct OperClassACLPath @@ -1576,7 +1585,7 @@ struct OperClassCheckParams Client *client; Client *victim; Channel *channel; - void *extra; + const void *extra; }; struct ConfigItem_operclass { @@ -1596,9 +1605,10 @@ struct ConfigItem_oper { unsigned long modes, require_modes; char *vhost; int maxlogins; + int server_notice_colors; }; -/** The SSL/TLS options that are used in set::tls and otherblocks::tls-options. +/** The TLS options that are used in set::tls and otherblocks::tls-options. * NOTE: If you add something here then you must also update the * conf_tlsblock() function in s_conf.c to have it inherited * from set::tls to the other config blocks! @@ -1645,7 +1655,8 @@ struct ConfigItem_ulines { struct ConfigItem_tld { ConfigItem_tld *prev, *next; ConfigFlag_tld flag; - char *mask, *channel; + ConfigItem_mask *mask; + char *channel; char *motd_file, *rules_file, *smotd_file; char *botmotd_file, *opermotd_file; MOTDFile rules, motd, smotd, botmotd, opermotd; @@ -1663,6 +1674,7 @@ struct ConfigItem_listen { SSL_CTX *ssl_ctx; TLSOptions *tls_options; int websocket_options; /* should be in module, but lazy */ + char *websocket_forward; }; struct ConfigItem_sni { @@ -1711,13 +1723,6 @@ struct ConfigItem_link { TLSOptions *tls_options; /**< SSL Options for outgoing connection (optional) */ }; -struct ConfigItem_except { - ConfigItem_except *prev, *next; - ConfigFlag_except flag; - int type; - char *mask; -}; - struct ConfigItem_ban { ConfigItem_ban *prev, *next; ConfigFlag_ban flag; @@ -1734,7 +1739,8 @@ struct ConfigItem_deny_dcc { struct ConfigItem_deny_link { ConfigItem_deny_link *prev, *next; ConfigFlag_except flag; - char *mask, *rule, *prettyrule; + ConfigItem_mask *mask; + char *rule, *prettyrule; }; struct ConfigItem_deny_version { @@ -1764,32 +1770,6 @@ struct ConfigItem_allow_dcc { char *filename; }; -struct ConfigItem_log { - ConfigItem_log *prev, *next; - ConfigFlag flag; - char *file; /**< Filename to log to (either generated or specified) */ - char *filefmt; /**< Filename with dynamic % stuff */ - long maxsize; - int flags; - int logfd; -}; - -struct ConfigItem_unknown { - ConfigItem_unknown *prev, *next; - ConfigFlag flag; - ConfigEntry *ce; -}; - -struct ConfigItem_unknown_ext { - ConfigItem_unknown_ext *prev, *next; - ConfigFlag flag; - char *ce_varname, *ce_vardata; - ConfigFile *ce_fileptr; - int ce_varlinenum; - ConfigEntry *ce_entries; -}; - - typedef enum { ALIAS_SERVICES=1, ALIAS_STATS, ALIAS_NORMAL, ALIAS_COMMAND, ALIAS_CHANNEL, ALIAS_REAL } AliasType; @@ -1812,33 +1792,23 @@ struct ConfigItem_alias_format { Match *expr; }; -/** - * In a rehash scenario, conf_include will contain all of the included - * configs that are actually in use. It also will contain includes - * that are being processed so that the configuration may be updated. - * INCLUDE_NOTLOADED is set on all of the config files that are being - * loaded and unset on already-loaded files. See - * unload_loaded_includes() and load_includes(). - */ -#define INCLUDE_NOTLOADED 0x1 -#define INCLUDE_REMOTE 0x2 -#define INCLUDE_DLQUEUED 0x4 -/** - * Marks that an include was loaded without error. This seems to - * overlap with the INCLUDE_NOTLOADED meaning(?). --binki - */ -#define INCLUDE_USED 0x8 +#define RESOURCE_REMOTE 0x1 +#define RESOURCE_DLQUEUED 0x2 +#define RESOURCE_INCLUDE 0x4 + +typedef struct ConfigEntryWrapper ConfigEntryWrapper; +struct ConfigEntryWrapper { + ConfigEntryWrapper *prev, *next; + ConfigEntry *ce; +}; -struct ConfigItem_include { - ConfigItem_include *prev, *next; - ConfigFlag_ban flag; - char *file; -#ifdef USE_LIBCURL - char *url; - char *errorbuf; -#endif - char *included_from; - int included_from_line; +struct ConfigResource { + ConfigResource *prev, *next; + int type; + ConfigEntryWrapper *wce; /**< The place(s) where this resource is begin used */ + char *file; /**< File to read: can be a conf/something file or a downloaded file */ + char *url; /**< URL, if it is an URL */ + char *cache_file; /**< Set to filename of local cached copy, if it is available */ }; struct ConfigItem_blacklist_module { @@ -1855,7 +1825,7 @@ struct ConfigItem_help { struct ConfigItem_offchans { ConfigItem_offchans *prev, *next; - char chname[CHANNELLEN+1]; + char name[CHANNELLEN+1]; char *topic; }; @@ -1868,6 +1838,7 @@ struct SecurityGroup { int reputation_score; int webirc; int tls; + ConfigItem_mask *include_mask; }; #define HM_HOST 1 @@ -1885,14 +1856,6 @@ struct IRCStatistics { unsigned int is_cl; /* number of client connections */ unsigned int is_sv; /* number of server connections */ unsigned int is_ni; /* connection but no idea who it was */ - unsigned short is_cbs; /* bytes sent to clients */ - unsigned short is_cbr; /* bytes received to clients */ - unsigned short is_sbs; /* bytes sent to servers */ - unsigned short is_sbr; /* bytes received to servers */ - unsigned long is_cks; /* k-bytes sent to clients */ - unsigned long is_ckr; /* k-bytes received to clients */ - unsigned long is_sks; /* k-bytes sent to servers */ - unsigned long is_skr; /* k-bytes received to servers */ time_t is_cti; /* time spent connected by clients */ time_t is_sti; /* time spent connected by servers */ unsigned int is_ac; /* connections accepted */ @@ -1910,11 +1873,6 @@ struct IRCStatistics { unsigned int is_loc; /* local connections made */ }; -typedef struct MemoryInfo { - unsigned int classes; - unsigned long classesmem; -} MemoryInfo; - #define EXTCMODETABLESZ 32 /* Number of maximum paramter modes to allow. @@ -1928,13 +1886,30 @@ typedef struct MemoryInfo { * Otherwise, see the extended channel modes API, CmodeAdd(), etc. */ struct Mode { - long mode; /**< Core modes set on this channel (one of MODE_*) */ - Cmode_t extmode; /**< Other ("extended") channel modes set on this channel */ - void *extmodeparams[MAXPARAMMODES+1]; /**< Parameters for extended channel modes */ - int limit; /**< The +l limit in effect (eg: 40), if any - otherwise 0 */ - char key[KEYLEN + 1]; /**< The +k key in effect (eg: secret), if any - otherwise NULL */ + Cmode_t mode; /**< Other ("extended") channel modes set on this channel */ + void *mode_params[MAXPARAMMODES+1]; /**< Parameters for extended channel modes */ }; +/* flags for Link if used to contain Watch --k4be */ + +/* WATCH type */ +#define WATCH_FLAG_TYPE_WATCH (1<<0) /* added via /WATCH command */ +#define WATCH_FLAG_TYPE_MONITOR (1<<1) /* added via /MONITOR command */ + +/* behaviour switches */ +#define WATCH_FLAG_AWAYNOTIFY (1<<8) /* should send AWAY notifications */ + +/* watch triggering events */ +#define WATCH_EVENT_ONLINE 0 +#define WATCH_EVENT_OFFLINE 1 +#define WATCH_EVENT_AWAY 2 +#define WATCH_EVENT_NOTAWAY 3 +#define WATCH_EVENT_REAWAY 4 +#define WATCH_EVENT_USERHOST 5 +#define WATCH_EVENT_REALNAME 6 +#define WATCH_EVENT_LOGGEDIN 7 +#define WATCH_EVENT_LOGGEDOUT 8 + /* Used for notify-hash buckets... -Donwulff */ struct Watch { @@ -1962,6 +1937,8 @@ struct Link { } value; }; +#define IsInvalidChannelTS(x) ((x) <= 1000000) /**< Invalid channel creation time */ + /** * @addtogroup CommonStructs * @{ @@ -1979,13 +1956,12 @@ struct Channel { time_t topic_time; /**< Time at which the topic was last set */ int users; /**< Number of users in the channel */ Member *members; /**< List of channel members (users in the channel) */ - Link *invites; /**< List of outstanding /INVITE's from ops */ Ban *banlist; /**< List of bans (+b) */ Ban *exlist; /**< List of ban exceptions (+e) */ Ban *invexlist; /**< List of invite exceptions (+I) */ char *mode_lock; /**< Mode lock (MLOCK) applied to channel - usually by Services */ ModData moddata[MODDATA_MAX_CHANNEL]; /**< Channel attached module data, used by the ModData system */ - char chname[1]; /**< Channel name */ + char name[CHANNELLEN+1]; /**< Channel name */ }; /** user/channel member struct (channel->members). @@ -1997,7 +1973,7 @@ struct Member { struct Member *next; /**< Next entry in list */ Client *client; /**< The client */ - int flags; /**< The access of the user on this channel (one or more of CHFL_*) */ + char member_modes[MEMBERMODESLEN]; /**< The access of the user on this channel (eg "vhoqa") */ ModData moddata[MODDATA_MAX_MEMBER]; /** Member attached module data, used by the ModData system */ }; @@ -2010,7 +1986,7 @@ struct Membership { struct Membership *next; /**< Next entry in list */ struct Channel *channel; /**< The channel */ - int flags; /**< The access of the user on this channel (one or more of CHFL_*) */ + char member_modes[MEMBERMODESLEN]; /**< The (new) access of the user on this channel (eg "vhoqa") */ ModData moddata[MODDATA_MAX_MEMBERSHIP]; /**< Membership attached module data, used by the ModData system */ }; @@ -2024,97 +2000,18 @@ struct Ban { time_t when; /**< When the entry was added */ }; -/* -** Channel Related macros follow -*/ - -/* Channel related flags */ -#ifdef PREFIX_AQ - #define CHFL_CHANOP_OR_HIGHER (CHFL_CHANOP|CHFL_CHANADMIN|CHFL_CHANOWNER) - #define CHFL_HALFOP_OR_HIGHER (CHFL_CHANOWNER|CHFL_CHANADMIN|CHFL_CHANOP|CHFL_HALFOP) -#else - #define CHFL_CHANOP_OR_HIGHER (CHFL_CHANOP) - #define CHFL_HALFOP_OR_HIGHER (CHFL_CHANOP|CHFL_HALFOP) -#endif - -/** Channel flags (privileges) of users on a channel. - * This is used by Member and Membership (m->flags) to indicate the access rights of a user in a channel. - * Also used by SJOIN and MODE to set some flags while a JOIN or MODE is in process. - * @defgroup ChannelFlags Channel access flags - * @{ - */ -/** Is channel owner (+q) */ -#define is_chanowner(cptr,channel) (get_access(cptr,channel) & CHFL_CHANOWNER) -/** Is channel admin (+a) */ -#define is_chanadmin(cptr,channel) (get_access(cptr,channel) & CHFL_CHANADMIN) -/** Is channel operator or higher (+o/+a/+q) */ -#define is_chan_op(cptr,channel) (get_access(cptr,channel) & CHFL_CHANOP_OR_HIGHER) -/** Is some kind of channel op (+h/+o/+a/+q) */ -#define is_skochanop(cptr,channel) (get_access(cptr,channel) & CHFL_HALFOP_OR_HIGHER) -/** Is half-op (+h) */ -#define is_half_op(cptr,channel) (get_access(cptr,channel) & CHFL_HALFOP) -/** Has voice (+v) */ -#define has_voice(cptr,channel) (get_access(cptr,channel) & CHFL_VOICE) -/* Important: - * Do not blindly change the values of CHFL_* as they must match the - * ones in MODE_*. I already screwed this up twice. -- Syzop - * Obviously these should be decoupled in a code cleanup. - */ -#define CHFL_CHANOP 0x0001 /**< Channel operator (+o) */ -#define CHFL_VOICE 0x0002 /**< Voice (+v, can speak through bans and +m) */ -#define CHFL_DEOPPED 0x0004 /**< De-oped by a server (temporary state) */ -#define CHFL_CHANOWNER 0x0040 /**< Channel owner (+q) */ -#define CHFL_CHANADMIN 0x0080 /**< Channel admin (+a) */ -#define CHFL_HALFOP 0x0100 /**< Channel halfop (+h) */ -#define CHFL_BAN 0x0200 /**< Channel ban (+b) - not a real flag, only used in sjoin.c */ -#define CHFL_EXCEPT 0x0400 /**< Channel except (+e) - not a real flag, only used in sjoin.c */ -#define CHFL_INVEX 0x0800 /**< Channel invite exception (+I) - not a real flag, only used in sjoin.c */ -/** @} */ - -#define CHFL_REJOINING 0x8000 /* used internally by rejoin_* */ - -#define CHFL_OVERLAP (CHFL_CHANOWNER|CHFL_CHANADMIN|CHFL_CHANOP|CHFL_VOICE|CHFL_HALFOP) - /* Channel macros */ -/* Don't blindly change these MODE_* values, see comment 20 lines up! */ -#define MODE_CHANOP CHFL_CHANOP -#define MODE_VOICE CHFL_VOICE -#define MODE_PRIVATE 0x0004 -#define MODE_SECRET 0x0008 -#define MODE_MODERATED 0x0010 -#define MODE_TOPICLIMIT 0x0020 -#define MODE_CHANOWNER 0x0040 -#define MODE_CHANADMIN 0x0080 -#define MODE_HALFOP 0x0100 #define MODE_EXCEPT 0x0200 #define MODE_BAN 0x0400 -#define MODE_INVITEONLY 0x0800 -#define MODE_NOPRIVMSGS 0x1000 -#define MODE_KEY 0x2000 -#define MODE_LIMIT 0x4000 -#define MODE_RGSTR 0x8000 #define MODE_INVEX 0x8000000 -/* - * mode flags which take another parameter (With PARAmeterS) - */ -#define MODE_WPARAS (MODE_HALFOP|MODE_CHANOP|MODE_VOICE|MODE_CHANOWNER|MODE_CHANADMIN|MODE_BAN|MODE_KEY|MODE_LIMIT|MODE_EXCEPT|MODE_INVEX) -/* - * Undefined here, these are used in conjunction with the above modes in - * the source. -#define MODE_DEL 0x200000000 -#define MODE_ADD 0x400000000 - */ - -#define HoldChannel(x) (!(x)) /* name invisible */ -#define SecretChannel(x) ((x) && ((x)->mode.mode & MODE_SECRET)) +#define SecretChannel(x) ((x) && has_channel_mode((x), 's')) /* channel not shown but names are */ -#define HiddenChannel(x) ((x) && ((x)->mode.mode & MODE_PRIVATE)) +#define HiddenChannel(x) ((x) && has_channel_mode((x), 'p')) /* channel visible */ #define ShowChannel(v,c) (PubChannel(c) || IsMember((v),(c))) -#define PubChannel(x) ((!x) || ((x)->mode.mode &\ - (MODE_PRIVATE | MODE_SECRET)) == 0) +#define PubChannel(x) (!SecretChannel((x)) && !HiddenChannel((x))) #define IsChannelName(name) ((name) && (*(name) == '#')) @@ -2194,11 +2091,19 @@ struct ParseMode { char modechar; char *param; Cmode *extm; - char *modebuf; /* curr pos */ - char *parabuf; /* curr pos */ + const char *modebuf; /* curr pos */ + const char *parabuf; /* curr pos */ char buf[512]; /* internal parse buffer */ }; +#define MAXMULTILINEMODES 3 +typedef struct MultiLineMode MultiLineMode; +struct MultiLineMode { + char *modeline[MAXMULTILINEMODES+1]; + char *paramline[MAXMULTILINEMODES+1]; + int numlines; +}; + typedef struct PendingServer PendingServer; struct PendingServer { PendingServer *prev, *next; @@ -2230,15 +2135,13 @@ struct MaxTarget { #define MARK_AS_OFFICIAL_MODULE(modinf) do { if (modinf && modinf->handle) ModuleSetOptions(modinfo->handle, MOD_OPT_OFFICIAL, 1); } while(0) #define MARK_AS_GLOBAL_MODULE(modinf) do { if (modinf && modinf->handle) ModuleSetOptions(modinfo->handle, MOD_OPT_GLOBAL, 1); } while(0) -/* old.. please don't use anymore */ -#define CHANOPPFX "@" - /* used for is_banned type field: */ -#define BANCHK_JOIN 0 /* checking if a ban forbids the person from joining */ -#define BANCHK_MSG 1 /* checking if a ban forbids the person from sending messages */ -#define BANCHK_NICK 2 /* checking if a ban forbids the person from changing his/her nick */ -#define BANCHK_LEAVE_MSG 3 /* checking if a ban forbids the person from leaving a message in PART or QUIT */ -#define BANCHK_TKL 4 /* called from a server ban routine, or other match_user() usage */ +#define BANCHK_JOIN 0x0001 /* checking if a ban forbids the person from joining */ +#define BANCHK_MSG 0x0002 /* checking if a ban forbids the person from sending messages */ +#define BANCHK_NICK 0x0004 /* checking if a ban forbids the person from changing his/her nick */ +#define BANCHK_LEAVE_MSG 0x0008 /* checking if a ban forbids the person from leaving a message in PART or QUIT */ +#define BANCHK_TKL 0x0010 /* called from a server ban routine, or other match_user() usage */ +#define BANCHK_ALL (BANCHK_JOIN|BANCHK_MSG|BANCHK_NICK|BANCHK_LEAVE_MSG) /* all events except BANCHK_TKL which is special */ #define TKLISTLEN 26 #define TKLIPHASHLEN1 4 @@ -2256,8 +2159,6 @@ struct MaxTarget { #define MATCH_MASK_IS_UHOST 0x1000 #define MATCH_MASK_IS_HOST 0x2000 -#define MATCH_USE_IDENT 0x0100 - typedef enum { POLICY_ALLOW=1, POLICY_WARN=2, @@ -2303,6 +2204,19 @@ struct ConfigItem_badword { #define SKIP_DEAF 0x4 #define SKIP_CTCP 0x8 +typedef struct GeoIPResult GeoIPResult; +struct GeoIPResult { + char *country_code; + char *country_name; +}; + +typedef enum WhoisConfigDetails { + WHOIS_CONFIG_DETAILS_DEFAULT = 0, + WHOIS_CONFIG_DETAILS_NONE = 1, + WHOIS_CONFIG_DETAILS_LIMITED = 2, + WHOIS_CONFIG_DETAILS_FULL = 3, +} WhoisConfigDetails; + #endif /* __struct_include__ */ #include "dynconf.h" diff --git a/include/sys.h b/include/sys.h index dcae59d..2ea06b5 100644 --- a/include/sys.h +++ b/include/sys.h @@ -55,9 +55,6 @@ #endif /* HAVE_INTTYPES_H */ #endif /* HAVE_STDINT_H */ -#ifdef SSL -#include -#endif #ifndef _WIN32 #include #include diff --git a/include/unrealircd.h b/include/unrealircd.h index 05e77f0..abd67c7 100644 --- a/include/unrealircd.h +++ b/include/unrealircd.h @@ -10,7 +10,6 @@ #include "numeric.h" #include "msg.h" #include "mempool.h" -#include "proto.h" #include "channel.h" #include #include @@ -31,7 +30,6 @@ #include #include #include "h.h" -#include "url.h" #include "version.h" #ifdef USE_LIBCURL #include diff --git a/include/url.h b/include/url.h deleted file mode 100644 index 63584b0..0000000 --- a/include/url.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef URL_H -#define URL_H -#include "types.h" - -int MODFUNC url_is_valid(const char *); -extern const char MODFUNC *displayurl(const char *url); -char MODFUNC *url_getfilename(const char *url); -char MODFUNC *download_file(const char *, char **); -void MODFUNC download_file_async(const char *, time_t, vFP, void *callback_data); -void MODFUNC url_do_transfers_async(void); -void MODFUNC url_init(void); - -#endif diff --git a/include/version.h b/include/version.h index 281fdf9..00f5e62 100644 --- a/include/version.h +++ b/include/version.h @@ -54,9 +54,10 @@ * 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 202120 +#define UNREAL_VERSION_TIME 202148 -#define UnrealProtocol 5002 +#define UNREAL_VERSION ((UNREAL_VERSION_GENERATION << 24) + (UNREAL_VERSION_MAJOR << 16) + (UNREAL_VERSION_MINOR << 8)) +#define UnrealProtocol 6000 #define PATCH1 macro_to_str(UNREAL_VERSION_GENERATION) #define PATCH2 "." macro_to_str(UNREAL_VERSION_MAJOR) #define PATCH3 "." macro_to_str(UNREAL_VERSION_MINOR) diff --git a/include/whowas.h b/include/whowas.h index f639f89..ceaefed 100644 --- a/include/whowas.h +++ b/include/whowas.h @@ -59,7 +59,7 @@ void off_history(Client *); ** nickname within the timelimit. Returns NULL, if no ** one found... */ -Client *get_history(char *, time_t); +Client *get_history(const char *, time_t); /* Nick name */ /* Time limit in seconds */ diff --git a/include/windows/setup.h b/include/windows/setup.h index f079072..12d49c4 100644 --- a/include/windows/setup.h +++ b/include/windows/setup.h @@ -35,8 +35,6 @@ #define PIDFILE PERMDATADIR"/unrealircd.pid" #define NO_U_TYPES #define NEED_U_INT32_T -#define PREFIX_AQ -#define LIST_SHOW_MODES #define strcasecmp _stricmp #define strncasecmp _strnicmp #define HAVE_EXPLICIT_BZERO @@ -57,13 +55,13 @@ #define _WIN32_WINNT 0x0601 /* Generation version number (e.g.: 3 for Unreal3*) */ -#define UNREAL_VERSION_GENERATION 5 +#define UNREAL_VERSION_GENERATION 6 /* Major version number (e.g.: 2 for Unreal3.2*) */ -#define UNREAL_VERSION_MAJOR 2 +#define UNREAL_VERSION_MAJOR 0 /* Minor version number (e.g.: 1 for Unreal3.2.1) */ -#define UNREAL_VERSION_MINOR 0 +#define UNREAL_VERSION_MINOR 1 /* Version suffix such as a beta marker or release candidate marker. (e.g.: -rcX for unrealircd-3.2.9-rcX) */ diff --git a/src/Makefile.in b/src/Makefile.in index a50e095..62d4388 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -24,15 +24,15 @@ 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 \ match.o modules.o parse.o mempool.o operclass.o \ - conf_preprocessor.o conf.o debug.o dispatch.o numeric.o \ + conf_preprocessor.o conf.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 \ api-moddata.o api-extban.o api-isupport.o api-command.o \ api-clicap.o api-messagetag.o api-history-backend.o api-efunctions.o \ api-event.o \ - crypt_blowfish.o unrealdb.o updconf.o crashreport.o modulemanager.o \ - utf8.o \ + crypt_blowfish.o unrealdb.o crashreport.o modulemanager.o \ + utf8.o log.o \ openssl_hostname_validation.o $(URL) SRC=$(OBJS:%.o=%.c) @@ -48,13 +48,16 @@ INCLUDES = ../include/channel.h \ ../include/ircsprintf.h \ ../include/license.h \ ../include/modules.h ../include/modversion.h ../include/msg.h \ - ../include/numeric.h ../include/proto.h \ + ../include/numeric.h \ ../include/resource.h ../include/setup.h \ ../include/struct.h ../include/sys.h \ - ../include/types.h ../include/url.h \ + ../include/types.h \ ../include/version.h ../include/whowas.h \ ../include/openssl_hostname_validation.h +.SUFFIXES: +.SUFFIXES: .c .h .o + all: build build: @@ -80,170 +83,16 @@ mods: version.c: version.c.SH $(SHELL) version.c.SH -version.o: version.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c version.c - -parse.o: parse.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c parse.c - -socket.o: socket.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c socket.c - -dispatch.o: dispatch.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c dispatch.c - -dbuf.o: dbuf.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c dbuf.c - -auth.o: auth.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c auth.c - -send.o: send.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c send.c - -tls.o: tls.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c tls.c - -match.o: match.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c match.c - -modules.o: modules.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c modules.c - -mempool.o: mempool.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c mempool.c - -support.o: support.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c support.c - -userload.o: userload.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c userload.c - -aliases.o: aliases.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c aliases.c +%.o: %.c $(INCLUDES) + $(CC) $(CFLAGS) $(BINCFLAGS) -c $< clean: $(RM) -f *.o *.so *~ core ircd version.c; \ - cd modules; make clean + cd modules; ${MAKE} clean cleandir: clean depend: makedepend -I${INCLUDEDIR} ${SRC} -channel.o: channel.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c channel.c - -ircd.o: ircd.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c ircd.c - -list.o: list.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c list.c - -dns.o: dns.c $(INCLUDES) ../include/dns.h - $(CC) $(CFLAGS) $(BINCFLAGS) -c dns.c - -fdlist.o: fdlist.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c fdlist.c - -conf_preprocessor.o: conf_preprocessor.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c conf_preprocessor.c - -conf.o: conf.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c conf.c - -debug.o: debug.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c debug.c - -numeric.o: numeric.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c numeric.c - -misc.o: misc.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c misc.c - -scache.o: scache.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c scache.c - -ircsprintf.o: ircsprintf.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c ircsprintf.c - -user.o: user.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c user.c - -serv.o: serv.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c serv.c - -whowas.o: whowas.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c whowas.c - -hash.o: hash.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c hash.c - -crule.o: crule.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c crule.c - -random.o: random.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c random.c - -operclass.o: operclass.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c operclass.c - -api-usermode.o: api-usermode.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-usermode.c - -api-event.o: api-event.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-event.c - -api-channelmode.o: api-channelmode.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-channelmode.c - -api-moddata.o: api-moddata.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-moddata.c - -api-extban.o: api-extban.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-extban.c - -api-command.o: api-command.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-command.c - -api-isupport.o: api-isupport.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-isupport.c - -api-clicap.o: api-clicap.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-clicap.c - -api-messagetag.o: api-messagetag.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-messagetag.c - -api-history-backend.o: api-history-backend.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-history-backend.c - -api-efunctions.o: api-efunctions.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c api-efunctions.c - -crypt_blowfish.o: crypt_blowfish.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c crypt_blowfish.c - -unrealdb.o: unrealdb.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c unrealdb.c - -updconf.o: updconf.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c updconf.c - -crashreport.o: crashreport.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c crashreport.c - -modulemanager.o: modulemanager.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c modulemanager.c - -utf8.o: utf8.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c utf8.c - -openssl_hostname_validation.o: openssl_hostname_validation.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c openssl_hostname_validation.c - -url.o: url.c $(INCLUDES) - $(CC) $(CFLAGS) $(BINCFLAGS) -c url.c - # DO NOT DELETE THIS LINE -- make depend depends on it. - diff --git a/src/aliases.c b/src/aliases.c index 22f299b..7ec46f3 100644 --- a/src/aliases.c +++ b/src/aliases.c @@ -42,11 +42,12 @@ void strrangetok(char *in, char *out, char tok, short first, short last) { /* cmd_alias is a special type of command, it has an extra argument 'cmd'. */ static int recursive_alias = 0; -void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char *cmd) +void cmd_alias(Client *client, MessageTag *mtags, int parc, const char *parv[], const char *cmd) { ConfigItem_alias *alias; Client *acptr; int ret; + char request[BUFSIZE]; if (!(alias = find_alias(cmd))) { @@ -64,7 +65,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * if (alias->type == ALIAS_SERVICES) { - if (SERVICES_NAME && (acptr = find_person(alias->nick, NULL))) + if (SERVICES_NAME && (acptr = find_user(alias->nick, NULL))) { if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL)) return; @@ -76,7 +77,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * } else if (alias->type == ALIAS_STATS) { - if (STATS_SERVER && (acptr = find_person(alias->nick, NULL))) + if (STATS_SERVER && (acptr = find_user(alias->nick, NULL))) { if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL)) return; @@ -88,7 +89,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * } else if (alias->type == ALIAS_NORMAL) { - if ((acptr = find_person(alias->nick, NULL))) + if ((acptr = find_user(alias->nick, NULL))) { if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL)) return; @@ -106,19 +107,19 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * else if (alias->type == ALIAS_CHANNEL) { Channel *channel; - if ((channel = find_channel(alias->nick, NULL))) + if ((channel = find_channel(alias->nick))) { - char *msg = parv[1]; - char *errmsg = NULL; + const char *msg = parv[1]; + const char *errmsg = NULL; if (can_send_to_channel(client, channel, &msg, &errmsg, 0)) { - if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, cmd, channel->chname, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, cmd, channel->name, 0, NULL)) return; new_message(client, NULL, &mtags); sendto_channel(channel, client, client->direction, - PREFIX_ALL, 0, SEND_ALL|SKIP_DEAF, mtags, + NULL, 0, SEND_ALL|SKIP_DEAF, mtags, ":%s PRIVMSG %s :%s", - client->name, channel->chname, parv[1]); + client->name, channel->name, parv[1]); free_message_tags(mtags); return; } @@ -132,7 +133,10 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * char *ptr = ""; if (!(parc < 2 || *parv[1] == '\0')) - ptr = parv[1]; + { + strlcpy(request, parv[1], sizeof(request)); + ptr = request; + } for (format = alias->format; format; format = format->next) { @@ -201,7 +205,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * if (format->type == ALIAS_SERVICES) { - if (SERVICES_NAME && (acptr = find_person(format->nick, NULL))) + if (SERVICES_NAME && (acptr = find_user(format->nick, NULL))) { if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL)) return; @@ -212,7 +216,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * } else if (format->type == ALIAS_STATS) { - if (STATS_SERVER && (acptr = find_person(format->nick, NULL))) + if (STATS_SERVER && (acptr = find_user(format->nick, NULL))) { if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL)) return; @@ -223,7 +227,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * } else if (format->type == ALIAS_NORMAL) { - if ((acptr = find_person(format->nick, NULL))) + if ((acptr = find_user(format->nick, NULL))) { if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL)) return; @@ -241,19 +245,19 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * else if (format->type == ALIAS_CHANNEL) { Channel *channel; - if ((channel = find_channel(format->nick, NULL))) + if ((channel = find_channel(format->nick))) { - char *msg = output; - char *errmsg = NULL; + const char *msg = output; + const char *errmsg = NULL; if (!can_send_to_channel(client, channel, &msg, &errmsg, 0)) { - if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, cmd, channel->chname, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, cmd, channel->name, 0, NULL)) return; new_message(client, NULL, &mtags); sendto_channel(channel, client, client->direction, - PREFIX_ALL, 0, SEND_ALL|SKIP_DEAF, mtags, + NULL, 0, SEND_ALL|SKIP_DEAF, mtags, ":%s PRIVMSG %s :%s", - client->name, channel->chname, parv[1]); + client->name, channel->name, parv[1]); free_message_tags(mtags); return; } diff --git a/src/api-channelmode.c b/src/api-channelmode.c index 3237da9..1a5beee 100644 --- a/src/api-channelmode.c +++ b/src/api-channelmode.c @@ -33,10 +33,8 @@ * @{ */ -/** Table with details on each channel mode handler */ -Cmode *Channelmode_Table = NULL; -/** Highest index in Channelmode_Table */ -unsigned short Channelmode_highest = 0; +/** List of all channel modes, their handlers, etc */ +Cmode *channelmodes = NULL; /** @} */ @@ -48,6 +46,7 @@ char extchmstr[4][64]; /* Private functions (forward declaration) and variables */ static void make_cmodestr(void); static char previous_chanmodes[256]; +static char previous_prefix[256]; static Cmode *ParamTable[MAXPARAMMODES+1]; static void unload_extcmode_commit(Cmode *cmode); @@ -55,6 +54,7 @@ static void unload_extcmode_commit(Cmode *cmode); void make_extcmodestr() { char *p; + Cmode *cm; int i; extchmstr[0][0] = extchmstr[1][0] = extchmstr[2][0] = extchmstr[3][0] = '\0'; @@ -64,31 +64,30 @@ void make_extcmodestr() /* type 2: 1 par to set/unset (has .unset_with_param) */ p = extchmstr[1]; - for (i=0; i <= Channelmode_highest; i++) - if (Channelmode_Table[i].paracount && Channelmode_Table[i].flag && - Channelmode_Table[i].unset_with_param) - *p++ = Channelmode_Table[i].flag; + for (cm=channelmodes; cm; cm = cm->next) + if (cm->paracount && cm->letter && cm->unset_with_param && (cm->type != CMODE_MEMBER)) + *p++ = cm->letter; *p = '\0'; /* type 3: 1 param to set, 0 params to unset (does not have .unset_with_param) */ p = extchmstr[2]; - for (i=0; i <= Channelmode_highest; i++) - if (Channelmode_Table[i].paracount && Channelmode_Table[i].flag && - !Channelmode_Table[i].unset_with_param) - *p++ = Channelmode_Table[i].flag; + for (cm=channelmodes; cm; cm = cm->next) + if (cm->paracount && cm->letter && !cm->unset_with_param) + *p++ = cm->letter; *p = '\0'; /* type 4: paramless modes */ p = extchmstr[3]; - for (i=0; i <= Channelmode_highest; i++) - if (!Channelmode_Table[i].paracount && Channelmode_Table[i].flag) - *p++ = Channelmode_Table[i].flag; + for (cm=channelmodes; cm; cm = cm->next) + if (!cm->paracount && cm->letter) + *p++ = cm->letter; *p = '\0'; } /** Create the string that is used in numeric 004 */ static void make_cmodestr(void) { + Cmode *cm; char *p = &cmodestring[0]; CoreChannelModeTable *tab = &corechannelmodetable[0]; int i; @@ -98,35 +97,36 @@ static void make_cmodestr(void) p++; tab++; } - for (i=0; i <= Channelmode_highest; i++) - if (Channelmode_Table[i].flag) - *p++ = Channelmode_Table[i].flag; + for (cm=channelmodes; cm; cm = cm->next) + if (cm->letter) + *p++ = cm->letter; *p = '\0'; } /** Check for changes - if any are detected, we broadcast the change */ -void extcmodes_check_for_changes(void) +void extcmodes_check_for_changed_channel_modes(void) { char chanmodes[256]; ISupport *isup; + //sort_cmodes(); make_cmodestr(); make_extcmodestr(); snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR1, EXPAR1); - safe_strdup(me.serv->features.chanmodes[0], chanmodes); - snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR2, EXPAR2); - safe_strdup(me.serv->features.chanmodes[1], chanmodes); - snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR3, EXPAR3); - safe_strdup(me.serv->features.chanmodes[2], chanmodes); - snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR4, EXPAR4); - safe_strdup(me.serv->features.chanmodes[3], chanmodes); + safe_strdup(me.server->features.chanmodes[0], chanmodes); + snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR2); + safe_strdup(me.server->features.chanmodes[1], chanmodes); + snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR3); + safe_strdup(me.server->features.chanmodes[2], chanmodes); + snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR4); + safe_strdup(me.server->features.chanmodes[3], chanmodes); ircsnprintf(chanmodes, sizeof(chanmodes), "%s,%s,%s,%s", - me.serv->features.chanmodes[0], - me.serv->features.chanmodes[1], - me.serv->features.chanmodes[2], - me.serv->features.chanmodes[3]); + me.server->features.chanmodes[0], + me.server->features.chanmodes[1], + me.server->features.chanmodes[2], + me.server->features.chanmodes[3]); isup = ISupportFind("CHANMODES"); if (!isup) @@ -139,10 +139,10 @@ void extcmodes_check_for_changes(void) if (*previous_chanmodes && strcmp(chanmodes, previous_chanmodes)) { - ircd_log(LOG_ERROR, "Channel modes changed at runtime: %s -> %s", - previous_chanmodes, chanmodes); - sendto_realops("Channel modes changed at runtime: %s -> %s", - previous_chanmodes, chanmodes); + unreal_log(ULOG_INFO, "mode", "CHANNEL_MODES_CHANGED", NULL, + "Channel modes changed at runtime: $old_channel_modes -> $new_channel_modes", + log_data_string("old_channel_modes", previous_chanmodes), + log_data_string("new_channel_modes", chanmodes)); /* Broadcast change to all (locally connected) servers */ sendto_server(NULL, 0, 0, NULL, "PROTOCTL CHANMODES=%s", chanmodes); } @@ -150,40 +150,152 @@ void extcmodes_check_for_changes(void) strlcpy(previous_chanmodes, chanmodes, sizeof(previous_chanmodes)); } +void make_prefix(char **isupport_prefix, char **isupport_statusmsg) +{ + static char prefix[256]; + static char prefix_prefix[256]; + char prefix_modes[256]; + int rank[256]; + Cmode *cm; + int n; + + *prefix = *prefix_prefix = *prefix_modes = '\0'; + + for (n=0, cm=channelmodes; cm && n < ARRAY_SIZEOF(rank)-1; cm = cm->next) + { + if ((cm->type == CMODE_MEMBER) && cm->letter) + { + strlcat_letter(prefix_prefix, cm->prefix, sizeof(prefix_prefix)); + strlcat_letter(prefix_modes, cm->letter, sizeof(prefix_modes)); + rank[n] = cm->rank; + n++; + } + } + + if (*prefix_prefix) + { + int i, j; + /* Now sort the damn thing */ + for (i=0; i < n; i++) + { + for (j=i+1; j < n; j++) + { + if (rank[i] < rank[j]) + { + /* swap */ + char save; + int save_rank; + save = prefix_prefix[i]; + prefix_prefix[i] = prefix_prefix[j]; + prefix_prefix[j] = save; + save = prefix_modes[i]; + prefix_modes[i] = prefix_modes[j]; + prefix_modes[j] = save; + save_rank = rank[i]; + rank[i] = rank[j]; + rank[j] = save_rank; + } + } + } + snprintf(prefix, sizeof(prefix), "(%s)%s", prefix_modes, prefix_prefix); + } + + *isupport_prefix = prefix; + *isupport_statusmsg = prefix_prefix; +} + +void extcmodes_check_for_changed_prefixes(void) +{ + ISupport *isup; + char *prefix, *statusmsg; + + make_prefix(&prefix, &statusmsg); + ISupportSet(NULL, "PREFIX", prefix); + ISupportSet(NULL, "STATUSMSG", statusmsg); + + if (*previous_prefix && strcmp(prefix, previous_prefix)) + { + unreal_log(ULOG_INFO, "mode", "PREFIX_CHANGED", NULL, + "Prefix changed at runtime: $old_prefix -> $new_prefix", + log_data_string("old_prefix", previous_prefix), + log_data_string("new_prefix", prefix)); + /* Broadcast change to all (locally connected) servers */ + sendto_server(NULL, 0, 0, NULL, "PROTOCTL PREFIX=%s", prefix); + } + + strlcpy(previous_prefix, prefix, sizeof(previous_prefix)); +} + +/** Check for changes - if any are detected, we broadcast the change */ +void extcmodes_check_for_changes(void) +{ + extcmodes_check_for_changed_channel_modes(); + extcmodes_check_for_changed_prefixes(); +} + /** Initialize the extended channel modes system */ void extcmode_init(void) { - Cmode_t val = 1; - int i; - Channelmode_Table = safe_alloc(sizeof(Cmode) * EXTCMODETABLESZ); - for (i = 0; i < EXTCMODETABLESZ; i++) - { - Channelmode_Table[i].mode = val; - val *= 2; - } - Channelmode_highest = 0; memset(&extchmstr, 0, sizeof(extchmstr)); memset(¶m_to_slot_mapping, 0, sizeof(param_to_slot_mapping)); *previous_chanmodes = '\0'; + *previous_prefix = '\0'; } /** Update letter->slot mapping and slot->handler mapping */ -void extcmode_para_addslot(Cmode *c, int slot) +void extcmode_para_addslot(Cmode *cm, int slot) { if ((slot < 0) || (slot > MAXPARAMMODES)) abort(); - c->slot = slot; - ParamTable[slot] = c; - param_to_slot_mapping[c->flag] = slot; + cm->param_slot = slot; + ParamTable[slot] = cm; + param_to_slot_mapping[cm->letter] = slot; } /** Update letter->slot mapping and slot->handler mapping */ -void extcmode_para_delslot(Cmode *c, int slot) +void extcmode_para_delslot(Cmode *cm, int slot) { if ((slot < 0) || (slot > MAXPARAMMODES)) abort(); ParamTable[slot] = NULL; - param_to_slot_mapping[c->flag] = 0; + param_to_slot_mapping[cm->letter] = 0; +} + +void channelmode_add_sorted(Cmode *n) +{ + Cmode *m; + + if (channelmodes == NULL) + { + channelmodes = n; + return; + } + + for (m = channelmodes; m; m = m->next) + { + if (m->letter == '\0') + abort(); + if (sort_character_lowercase_before_uppercase(n->letter, m->letter)) + { + /* Insert us before */ + if (m->prev) + m->prev->next = n; + else + channelmodes = n; /* new head */ + n->prev = m->prev; + + n->next = m; + m->prev = n; + return; + } + if (!m->next) + { + /* Append us at end */ + m->next = n; + n->prev = m; + return; + } + } } /** @defgroup ChannelModeAPI Channel mode API @@ -198,19 +310,17 @@ void extcmode_para_delslot(Cmode *c, int slot) */ Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode) { - short i = 0, j = 0; int paraslot = -1; int existing = 0; + Cmode *cm; - while (i < EXTCMODETABLESZ) + for (cm=channelmodes; cm; cm = cm->next) { - if (!Channelmode_Table[i].flag) - break; - else if (Channelmode_Table[i].flag == req.flag) + if (cm->letter == req.letter) { - if (Channelmode_Table[i].unloaded) + if (cm->unloaded) { - Channelmode_Table[i].unloaded = 0; + cm->unloaded = 0; existing = 1; break; } else { @@ -219,22 +329,67 @@ Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode) return NULL; } } - i++; - } - if (i == EXTCMODETABLESZ) - { - Debug((DEBUG_DEBUG, "CmodeAdd failed, no space")); - if (module) - module->errorcode = MODERR_NOSPACE; - return NULL; } - if (req.paracount == 1) + if (!cm) + { + long l, found = 0; + + if (req.type == CMODE_NORMAL) + { + for (l = 1; l < LONG_MAX/2; l *= 2) + { + found = 0; + for (cm=channelmodes; cm; cm = cm->next) + { + if (cm->mode == l) + { + found = 1; + break; + } + } + if (!found) + break; + } + /* If 'found' is still true, then we are out of space */ + if (found) + { + unreal_log(ULOG_ERROR, "module", "CHANNEL_MODE_OUT_OF_SPACE", NULL, + "CmodeAdd: out of space!!!"); + if (module) + module->errorcode = MODERR_NOSPACE; + return NULL; + } + cm = safe_alloc(sizeof(Cmode)); + cm->letter = req.letter; + cm->mode = l; + *mode = cm->mode; + } else if (req.type == CMODE_MEMBER) + { + if (!req.prefix || !req.sjoin_prefix || !req.paracount || + !req.unset_with_param || !req.rank) + { + unreal_log(ULOG_ERROR, "module", "CMODEADD_API_ERROR", NULL, + "CmodeAdd(): module is missing required information. " + "Module: $module_name", + log_data_string("module_name", module->header->name)); + module->errorcode = MODERR_INVALID; + return NULL; + } + cm = safe_alloc(sizeof(Cmode)); + cm->letter = req.letter; + } else { + abort(); + } + channelmode_add_sorted(cm); + } + + if ((req.paracount == 1) && (req.type == CMODE_NORMAL)) { if (existing) { /* Re-use parameter slot of the module with the same modechar that is unloading */ - paraslot = Channelmode_Table[i].slot; + paraslot = cm->param_slot; } else { @@ -243,7 +398,8 @@ Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode) { if (paraslot == MAXPARAMMODES - 1) { - Debug((DEBUG_DEBUG, "CmodeAdd failed, no space for parameter")); + unreal_log(ULOG_ERROR, "module", "CHANNEL_MODE_OUT_OF_SPACE", NULL, + "CmodeAdd: out of space!!! Place 2."); if (module) module->errorcode = MODERR_NOSPACE; return NULL; @@ -252,39 +408,40 @@ Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode) } } - *mode = Channelmode_Table[i].mode; - /* Update extended channel mode table highest */ - Channelmode_Table[i].flag = req.flag; - Channelmode_Table[i].paracount = req.paracount; - Channelmode_Table[i].is_ok = req.is_ok; - Channelmode_Table[i].put_param = req.put_param; - Channelmode_Table[i].get_param = req.get_param; - Channelmode_Table[i].conv_param = req.conv_param; - Channelmode_Table[i].free_param = req.free_param; - Channelmode_Table[i].dup_struct = req.dup_struct; - Channelmode_Table[i].sjoin_check = req.sjoin_check; - Channelmode_Table[i].local = req.local; - Channelmode_Table[i].unset_with_param = req.unset_with_param; - Channelmode_Table[i].owner = module; - Channelmode_Table[i].unloaded = 0; - - for (j = 0; j < EXTCMODETABLESZ; j++) - if (Channelmode_Table[j].flag) - if (j > Channelmode_highest) - Channelmode_highest = j; + cm->letter = req.letter; + cm->type = req.type; + cm->prefix = req.prefix; + cm->sjoin_prefix = req.sjoin_prefix; + cm->rank = req.rank; + cm->paracount = req.paracount; + cm->is_ok = req.is_ok; + cm->put_param = req.put_param; + cm->get_param = req.get_param; + cm->conv_param = req.conv_param; + cm->free_param = req.free_param; + cm->dup_struct = req.dup_struct; + cm->sjoin_check = req.sjoin_check; + cm->local = req.local; + cm->unset_with_param = req.unset_with_param; + cm->owner = module; + cm->unloaded = 0; + + if (cm->type == CMODE_NORMAL) + { + *mode = cm->mode; + if (cm->paracount == 1) + extcmode_para_addslot(cm, paraslot); + } - if (Channelmode_Table[i].paracount == 1) - extcmode_para_addslot(&Channelmode_Table[i], paraslot); - if (module) { ModuleObject *cmodeobj = safe_alloc(sizeof(ModuleObject)); - cmodeobj->object.cmode = &Channelmode_Table[i]; + cmodeobj->object.cmode = cm; cmodeobj->type = MOBJ_CMODE; AddListItem(cmodeobj, module->objects); module->errorcode = MODERR_NOERROR; } - return &(Channelmode_Table[i]); + return cm; } /** Delete a previously registered channel mode - not called by modules. @@ -304,7 +461,7 @@ void CmodeDel(Cmode *cmode) } cmode->owner = NULL; } - if (loop.ircd_rehashing) + if (loop.rehashing) cmode->unloaded = 1; else unload_extcmode_commit(cmode); @@ -322,80 +479,110 @@ static void unload_extcmode_commit(Cmode *cmode) Channel *channel; if (!cmode) - return; + return; - /* Unset channel mode and send MODE to everyone */ - - if (cmode->paracount == 0) + if (cmode->type == CMODE_NORMAL) { - /* Paramless mode, easy */ - for (channel = channels; channel; channel = channel->nextch) + /* Unset channel mode and send MODE to everyone */ + if (cmode->paracount == 0) { - if (channel->mode.extmode & cmode->mode) + /* Paramless mode, easy */ + for (channel = channels; channel; channel = channel->nextch) { - MessageTag *mtags = NULL; - - new_message(&me, NULL, &mtags); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s -%c", - me.name, channel->chname, cmode->flag); - sendto_server(NULL, 0, 0, mtags, - ":%s MODE %s -%c 0", - me.id, channel->chname, cmode->flag); - free_message_tags(mtags); - - channel->mode.extmode &= ~cmode->mode; - } - } - } else - { - /* Parameter mode, more complicated */ - for (channel = channels; channel; channel = channel->nextch) - { - if (channel->mode.extmode & cmode->mode) - { - MessageTag *mtags = NULL; - - new_message(&me, NULL, &mtags); - if (cmode->unset_with_param) + if (channel->mode.mode & cmode->mode) { - char *param = cmode->get_param(GETPARASTRUCT(channel, cmode->flag)); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s -%c %s", - me.name, channel->chname, cmode->flag, param); - sendto_server(NULL, 0, 0, mtags, - ":%s MODE %s -%c %s 0", - me.id, channel->chname, cmode->flag, param); - } else { + MessageTag *mtags = NULL; + + new_message(&me, NULL, &mtags); sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -%c", - me.name, channel->chname, cmode->flag); + me.name, channel->name, cmode->letter); sendto_server(NULL, 0, 0, mtags, ":%s MODE %s -%c 0", - me.id, channel->chname, cmode->flag); - } - free_message_tags(mtags); + me.id, channel->name, cmode->letter); + free_message_tags(mtags); - cmode->free_param(GETPARASTRUCT(channel, cmode->flag)); - channel->mode.extmode &= ~cmode->mode; + channel->mode.mode &= ~cmode->mode; + } + } + } else + { + /* Parameter mode, more complicated */ + for (channel = channels; channel; channel = channel->nextch) + { + if (channel->mode.mode & cmode->mode) + { + MessageTag *mtags = NULL; + + new_message(&me, NULL, &mtags); + if (cmode->unset_with_param) + { + const char *param = cmode->get_param(GETPARASTRUCT(channel, cmode->letter)); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, + ":%s MODE %s -%c %s", + me.name, channel->name, cmode->letter, param); + sendto_server(NULL, 0, 0, mtags, + ":%s MODE %s -%c %s 0", + me.id, channel->name, cmode->letter, param); + } else { + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, + ":%s MODE %s -%c", + me.name, channel->name, cmode->letter); + sendto_server(NULL, 0, 0, mtags, + ":%s MODE %s -%c 0", + me.id, channel->name, cmode->letter); + } + free_message_tags(mtags); + + cmode->free_param(GETPARASTRUCT(channel, cmode->letter)); + channel->mode.mode &= ~cmode->mode; + } + } + extcmode_para_delslot(cmode, cmode->param_slot); + } + } else + if (cmode->type == CMODE_MEMBER) + { + for (channel = channels; channel; channel = channel->nextch) + { + Member *m; + for (m = channel->members; m; m = m->next) + { + if (strchr(m->member_modes, cmode->letter)) + { + MessageTag *mtags = NULL; + + new_message(&me, NULL, &mtags); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, + ":%s MODE %s -%c %s", + me.name, channel->name, cmode->letter, m->client->name); + sendto_server(NULL, 0, 0, mtags, + ":%s MODE %s -%c %s 0", + me.id, channel->name, cmode->letter, m->client->id); + free_message_tags(mtags); + del_member_mode(m->client, channel, cmode->letter); + } } } - extcmode_para_delslot(cmode, cmode->slot); } - cmode->flag = '\0'; + DelListItem(cmode, channelmodes); + safe_free(cmode); } /** Unload all unused channel modes after a REHASH */ void unload_all_unused_extcmodes(void) { - int i; + Cmode *cm, *cm_next; - for (i = 0; i < EXTCMODETABLESZ; i++) - if (Channelmode_Table[i].flag && Channelmode_Table[i].unloaded) + for (cm=channelmodes; cm; cm = cm_next) + { + cm_next = cm->next; + if (cm->letter && cm->unloaded) { - unload_extcmode_commit(&Channelmode_Table[i]); + unload_extcmode_commit(cm); } + } } @@ -407,18 +594,29 @@ void unload_all_unused_extcmodes(void) * @param channel The channel * @param mode The mode character (eg: 'f') */ -char *cm_getparameter(Channel *channel, char mode) +const char *cm_getparameter(Channel *channel, char mode) { return GETPARAMHANDLERBYLETTER(mode)->get_param(GETPARASTRUCT(channel, mode)); } +/** Get parameter for a channel mode - special version for SJOIN. + * This version doesn't take a channel, but a mode.mode_params. + * It is only used by SJOIN and should not be used in 3rd party modules. + * @param p The list, eg oldmode.mode_params + * @param mode The mode letter + */ +const char *cm_getparameter_ex(void **p, char mode) +{ + return GETPARAMHANDLERBYLETTER(mode)->get_param(GETPARASTRUCTEX(p, mode)); +} + /** Set parameter for a channel mode. * @param channel The channel * @param mode The mode character (eg: 'f') * @param str The parameter string * @note Module users should not use this function directly, it is only used by MODE and SJOIN. */ -void cm_putparameter(Channel *channel, char mode, char *str) +void cm_putparameter(Channel *channel, char mode, const char *str) { GETPARASTRUCT(channel, mode) = GETPARAMHANDLERBYLETTER(mode)->put_param(GETPARASTRUCT(channel, mode), str); } @@ -433,25 +631,15 @@ void cm_freeparameter(Channel *channel, char mode) GETPARASTRUCT(channel, mode) = NULL; } -/** Get parameter for a channel mode - special version for SJOIN. - * This version doesn't take a channel, but a mode.extmodeparams. - * It is only used by SJOIN and should not be used in 3rd party modules. - * @param p The list, eg oldmode.extmodeparams - * @param mode The mode letter - */ -char *cm_getparameter_ex(void **p, char mode) -{ - return GETPARAMHANDLERBYLETTER(mode)->get_param(GETPARASTRUCTEX(p, mode)); -} /** Set parameter for a channel mode - special version for SJOIN. - * This version doesn't take a channel, but a mode.extmodeparams. + * This version doesn't take a channel, but a mode.mode_params. * It is only used by SJOIN and should not be used in 3rd party modules. - * @param p The list, eg oldmode.extmodeparams + * @param p The list, eg oldmode.mode_params * @param mode The mode letter * @param str The mode parameter string to set */ -void cm_putparameter_ex(void **p, char mode, char *str) +void cm_putparameter_ex(void **p, char mode, const char *str) { GETPARASTRUCTEX(p, mode) = GETPARAMHANDLERBYLETTER(mode)->put_param(GETPARASTRUCTEX(p, mode), str); } @@ -465,9 +653,9 @@ void cm_putparameter_ex(void **p, char mode, char *str) * @param what MODE_ADD / MODE_DEL (???) * @returns EX_ALLOW or EX_DENY */ -int extcmode_default_requirechop(Client *client, Channel *channel, char mode, char *para, int checkt, int what) +int extcmode_default_requirechop(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) { - if (IsUser(client) && is_chan_op(client, channel)) + if (IsUser(client) && check_channel_access(client, channel, "oaq")) return EX_ALLOW; if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ sendnumeric(client, ERR_NOTFORHALFOPS, mode); @@ -483,9 +671,9 @@ int extcmode_default_requirechop(Client *client, Channel *channel, char mode, ch * @param what MODE_ADD / MODE_DEL (???) * @returns EX_ALLOW or EX_DENY */ -int extcmode_default_requirehalfop(Client *client, Channel *channel, char mode, char *para, int checkt, int what) +int extcmode_default_requirehalfop(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) { - if (IsUser(client) && (is_chan_op(client, channel) || is_half_op(client, channel))) + if (IsUser(client) && (check_channel_access(client, channel, "oaq") || check_channel_access(client, channel, "h"))) return EX_ALLOW; return EX_DENY; } @@ -505,10 +693,10 @@ void extcmode_duplicate_paramlist(void **xi, void **xo) handler = CMP_GETHANDLERBYSLOT(i); if (!handler) continue; /* nothing there.. */ - inx = xi[handler->slot]; /* paramter data of input is here */ + inx = xi[handler->param_slot]; /* paramter data of input is here */ if (!inx) continue; /* not set */ - xo[handler->slot] = handler->dup_struct(inx); /* call dup_struct with that input and set the output param to that */ + xo[handler->param_slot] = handler->dup_struct(inx); /* call dup_struct with that input and set the output param to that */ } } @@ -525,8 +713,8 @@ void extcmode_free_paramlist(void **ar) handler = GETPARAMHANDLERBYSLOT(i); if (!handler) continue; /* nothing here... */ - handler->free_param(ar[handler->slot]); - ar[handler->slot] = NULL; + handler->free_param(ar[handler->param_slot]); + ar[handler->param_slot] = NULL; } } @@ -535,17 +723,450 @@ void extcmode_free_paramlist(void **ar) /** Internal function: returns 1 if the specified module has 1 or more extended channel modes registered */ int module_has_extcmode_param_mode(Module *mod) { - int i = 0; + Cmode *cm; - while (i < EXTCMODETABLESZ) - { - if ((Channelmode_Table[i].flag) && - (Channelmode_Table[i].owner == mod) && - (Channelmode_Table[i].paracount)) - { + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->letter) && (cm->owner == mod) && (cm->paracount)) return 1; - } - i++; - } + return 0; } + +/** Channel member privileges - getting, setting, checking vhoaq status, etc. + * These functions get or set the access rights of channel members, such as +vhoaq. + * They can also convert between modes (vhoaq), prefixes and sjoin prefixes. + * @defgroup ChannelMember Channel members access privileges + * @{ + */ + +/** Retrieve channel access for a user on a channel, returns modes eg "qao". + * @param client The client + * @param channel The channel + * @returns The modes, sorted by high ranking to lower ranking, eg "qao". + * An empty string ("") is returned when not in the channel or no modes. + */ +const char *get_channel_access(Client *client, Channel *channel) +{ + Membership *mb; + + mb = find_membership_link(client->user->channel, channel); + if (!mb) + return ""; + return mb->member_modes; +} + +/** Check channel access for user. + * @param client The client to check + * @param channel The channel to check + * @param modes Which mode(s) to check for + * @returns If the client in channel has any of the modes set, 1 is returned. + * Otherwise 0 is returned, which is also the case if the user is + * not a user or is not in the channel at all. + */ +int check_channel_access(Client *client, Channel *channel, const char *modes) +{ + Membership *mb; + const char *p; + + if (!IsUser(client)) + return 0; /* eg server */ + + mb = find_membership_link(client->user->channel, channel); + if (!mb) + return 0; /* not a member */ + + for (p = mb->member_modes; *p; p++) + if (strchr(modes, *p)) + return 1; /* match new style */ + + return 0; /* nomatch */ +} + +/** Check channel access for user. + * @param client The client to check + * @param channel The channel to check + * @param modes Which mode(s) to check for + * @returns If the client in channel has any of the modes set, 1 is returned. + * Otherwise 0 is returned, which is also the case if the user is + * not a user or is not in the channel at all. + */ +int check_channel_access_membership(Membership *mb, const char *modes) +{ + const char *p; + + if (!mb) + return 0; + + for (p = mb->member_modes; *p; p++) + if (strchr(modes, *p)) + return 1; /* match new style */ + + return 0; /* nomatch */ +} + +/** Check channel access for user. + * @param client The client to check + * @param channel The channel to check + * @param modes Which mode(s) to check for + * @returns If the client in channel has any of the modes set, 1 is returned. + * Otherwise 0 is returned, which is also the case if the user is + * not a user or is not in the channel at all. + */ +int check_channel_access_member(Member *mb, const char *modes) +{ + const char *p; + + if (!mb) + return 0; + + for (p = mb->member_modes; *p; p++) + if (strchr(modes, *p)) + return 1; /* match new style */ + + return 0; /* nomatch */ +} + +/** Check channel access for user. + * @param current Flags currently set on the client (eg mb->member_modes) + * @param modes Which mode(s) to check for + * @returns If the client in channel has any of the modes set, 1 is returned. + * Otherwise 0 is returned. + */ +int check_channel_access_string(const char *current_modes, const char *modes) +{ + const char *p; + + for (p = current_modes; *p; p++) + if (strchr(modes, *p)) + return 1; + + return 0; /* nomatch */ +} + +/** Check channel access for user. + * @param current Flags currently set on the client (eg mb->member_modes) + * @param letter Which mode letter to check for + * @returns If the client in channel has any of the modes set, 1 is returned. + * Otherwise 0 is returned. + */ +int check_channel_access_letter(const char *current_modes, const char letter) +{ + return strchr(current_modes, letter) ? 1 : 0; +} + +Cmode *find_channel_mode_handler(char letter) +{ + Cmode *cm; + + for (cm=channelmodes; cm; cm = cm->next) + if (cm->letter == letter) + return cm; + return NULL; +} + +/** Is 'letter' a valid mode used for access/levels/ranks? (vhoaq and such) + * @param letter The channel mode letter to check, eg 'v' + * @returns 1 if valid, 0 if the channel mode does not exist or is not a level mode. + */ +int valid_channel_access_mode_letter(char letter) +{ + Cmode *cm; + + if ((cm = find_channel_mode_handler(letter)) && (cm->type == CMODE_MEMBER)) + return 1; + + return 0; +} + +void addlettertomstring(char *str, char letter) +{ + Cmode *cm; + int n; + int my_rank; + char *p; + + if (!(cm = find_channel_mode_handler(letter)) || (cm->type != CMODE_MEMBER)) + return; // should we BUG on this? if something makes it this far, it can never be good right? + + my_rank = cm->rank; + + n = strlen(str); + if (n >= MEMBERMODESLEN-1) + return; // panic! + + for (p = str; *p; p++) + { + cm = find_channel_mode_handler(*p); + if (!cm) + continue; /* wtf */ + if (cm->rank < my_rank) + { + /* We need to insert us here */ + n = strlen(p); + memmove(p+1, p, n+1); // +1 for NUL byte + *p = letter; + return; + } + } + /* We should be at the end */ + str[n] = letter; + str[n+1] = '\0'; +} + +void add_member_mode_fast(Member *mb, Membership *mbs, char letter) +{ + addlettertomstring(mb->member_modes, letter); + addlettertomstring(mbs->member_modes, letter); +} + +void del_member_mode_fast(Member *mb, Membership *mbs, char letter) +{ + delletterfromstring(mb->member_modes, letter); + delletterfromstring(mbs->member_modes, letter); +} + +int find_mbs(Client *client, Channel *channel, Member **mb, Membership **mbs) +{ + *mbs = NULL; + + if (!(*mb = find_member_link(channel->members, client))) + return 0; + + if (!(*mbs = find_membership_link(client->user->channel, channel))) + return 0; + + return 1; +} + +void add_member_mode(Client *client, Channel *channel, char letter) +{ + Member *mb; + Membership *mbs; + + if (!find_mbs(client, channel, &mb, &mbs)) + return; + + add_member_mode_fast(mb, mbs, letter); +} + +void del_member_mode(Client *client, Channel *channel, char letter) +{ + Member *mb; + Membership *mbs; + + if (!find_mbs(client, channel, &mb, &mbs)) + return; + + del_member_mode_fast(mb, mbs, letter); +} + +char sjoin_prefix_to_mode(char s) +{ + Cmode *cm; + + /* Filter this out early to avoid spurious results */ + if (s == '\0') + return '\0'; + + /* First the hardcoded list modes: */ + if (s == '&') + return 'b'; + if (s == '"') + return 'e'; + if (s == '\'') + return 'I'; + + /* Now the dynamic ones (+vhoaq): */ + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->sjoin_prefix == s) && (cm->type == CMODE_MEMBER)) + return cm->letter; + + /* Not found */ + return '\0'; +} + +char mode_to_sjoin_prefix(char s) +{ + Cmode *cm; + + /* Filter this out early to avoid spurious results */ + if (s == '\0') + return '\0'; + + /* First the hardcoded list modes: */ + if (s == 'b') + return '&'; + if (s == 'e') + return '"'; + if (s == 'I') + return '\''; + + /* Now the dynamic ones (+vhoaq): */ + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->letter == s) && (cm->type == CMODE_MEMBER)) + return cm->sjoin_prefix; + + /* Not found */ + return '\0'; +} + +const char *modes_to_sjoin_prefix(const char *modes) +{ + static char buf[MEMBERMODESLEN]; + const char *m; + char f; + + *buf = '\0'; + for (m = modes; *m; m++) + { + f = mode_to_sjoin_prefix(*m); + if (f) + strlcat_letter(buf, f, sizeof(buf)); + } + + return buf; +} + +char mode_to_prefix(char s) +{ + Cmode *cm; + + /* Filter this out early to avoid spurious results */ + if (s == '\0') + return '\0'; + + /* Now the dynamic ones (+vhoaq): */ + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->letter == s) && (cm->type == CMODE_MEMBER)) + return cm->prefix; + + /* Not found */ + return '\0'; +} + +const char *modes_to_prefix(const char *modes) +{ + static char buf[MEMBERMODESLEN]; + const char *m; + char f; + + *buf = '\0'; + for (m = modes; *m; m++) + { + f = mode_to_prefix(*m); + if (f) + strlcat_letter(buf, f, sizeof(buf)); + } + + return buf; +} + +char prefix_to_mode(char s) +{ + Cmode *cm; + + /* Filter this out early to avoid spurious results */ + if (s == '\0') + return '\0'; + + /* Now the dynamic ones (+vhoaq): */ + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->prefix == s) && (cm->type == CMODE_MEMBER)) + return cm->letter; + + /* Not found */ + return '\0'; +} + +char rank_to_mode(int rank) +{ + Cmode *cm; + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->type == CMODE_MEMBER) && (cm->rank == rank)) + return cm->letter; + return '\0'; +} + +int mode_to_rank(char mode) +{ + Cmode *cm; + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->type == CMODE_MEMBER) && (cm->letter == mode)) + return cm->rank; + return '\0'; +} + +int prefix_to_rank(char prefix) +{ + Cmode *cm; + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->type == CMODE_MEMBER) && (cm->prefix == prefix)) + return cm->rank; + return '\0'; +} + +char rank_to_prefix(int rank) +{ + Cmode *cm; + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->type == CMODE_MEMBER) && (cm->rank == rank)) + return cm->prefix; + return '\0'; +} + +char lowest_ranking_prefix(const char *prefix) +{ + const char *p; + int winning_rank = INT_MAX; + + for (p = prefix; *p; p++) + { + int rank = prefix_to_rank(*p); + if (rank < winning_rank) + winning_rank = rank; + } + if (winning_rank == INT_MAX) + return '\0'; /* No result */ + return rank_to_prefix(winning_rank); +} + +char lowest_ranking_mode(const char *mode) +{ + const char *p; + int winning_rank = INT_MAX; + + for (p = mode; *p; p++) + { + int rank = mode_to_rank(*p); + if (rank < winning_rank) + winning_rank = rank; + } + if (winning_rank == INT_MAX) + return '\0'; /* No result */ + return rank_to_mode(winning_rank); +} + +/** Generate all member modes that are equal or greater than 'modes'. + * Eg calling this with "o" would generate "oaq" with the default loaded modules. + * This is used in sendto_channel() to make multiple check_channel_access_member() + * calls more easy / faster. + */ +void channel_member_modes_generate_equal_or_greater(const char *modes, char *buf, size_t buflen) +{ + const char *p; + int rank; + Cmode *cm; + + *buf = '\0'; + + /* First we must grab the lowest ranking mode, eg 'vhoaq' results in rank for 'v' */ + rank = lowest_ranking_mode(modes); + if (!rank) + return; /* zero matches */ + + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->type == CMODE_MEMBER) && (cm->rank >= rank)) + strlcat_letter(buf, cm->letter, buflen); +} + +/** @} */ diff --git a/src/api-clicap.c b/src/api-clicap.c index 79daa26..f760b90 100644 --- a/src/api-clicap.c +++ b/src/api-clicap.c @@ -79,7 +79,9 @@ long ClientCapabilityBit(const char *token) #ifdef DEBUGMODE if (!clicap) { - ircd_log(LOG_ERROR, "WARNING: ClientCapabilityBit(): unknown token '%s'", token); + unreal_log(ULOG_WARNING, "main", "BUG_CLIENTCAPABILITYBIT_UNKNOWN_TOKEN", NULL, + "[BUG] ClientCapabilityBit() check for unknown token: $token", + log_data_string("token", token)); } #endif @@ -102,7 +104,7 @@ long clicap_allocate_cap(void) ClientCapability *clicap; /* The first bit (v=1) is used by the "invert" marker */ - for (v=2; v < LONG_MAX; v = v * 2) + for (v=2; v; v <<= 1) { unsigned char found = 0; for (clicap = clicaps; clicap; clicap = clicap->next) @@ -162,8 +164,8 @@ ClientCapability *ClientCapabilityAdd(Module *module, ClientCapabilityInfo *clic v = clicap_allocate_cap(); if (v == 0) { - sendto_realops("ClientCapabilityAdd: out of space!!!"); - ircd_log(LOG_ERROR, "ClientCapabilityAdd: out of space!!!"); + unreal_log(ULOG_ERROR, "module", "CLIENTCAPABILITY_OUT_OF_SPACE", NULL, + "ClientCapabilityAdd: out of space!!!"); if (module) module->errorcode = MODERR_NOSPACE; return NULL; @@ -204,8 +206,9 @@ ClientCapability *ClientCapabilityAdd(Module *module, ClientCapabilityInfo *clic void unload_clicap_commit(ClientCapability *clicap) { /* This is an unusual operation, I think we should log it. */ - ircd_log(LOG_ERROR, "Unloading client capability '%s'", clicap->name); - sendto_realops("Unloading client capability '%s'", clicap->name); + unreal_log(ULOG_INFO, "module", "UNLOAD_CLICAP", NULL, + "Unloading client capability '$token'", + log_data_string("token", clicap->name)); /* NOTE: Stripping the CAP from local clients is done * in clicap_post_rehash(), so not here. @@ -245,7 +248,7 @@ void ClientCapabilityDel(ClientCapability *clicap) clicap->owner = NULL; } - if (loop.ircd_rehashing) + if (loop.rehashing) clicap->unloaded = 1; else unload_clicap_commit(clicap); @@ -263,7 +266,12 @@ void unload_all_unused_caps(void) } } -#define MAXCLICAPS 64 +#define ADVERTISEONLYCAPS 16 +/* Advertise only caps are not counted anywhere, this only provides space in rehash temporary storage arrays. + * If exceeded, the caps just won't be stored and will be re-added safely. --k4be + */ + +#define MAXCLICAPS ((int)(sizeof(long)*8 - 1 + ADVERTISEONLYCAPS)) /* how many cap bits will fit in `long`? */ static char *old_caps[MAXCLICAPS]; /**< List of old CAP names - used for /rehash */ int old_caps_proto[MAXCLICAPS]; /**< List of old CAP protocol values - used for /rehash */ @@ -279,7 +287,9 @@ void clicap_pre_rehash(void) { if (i == MAXCLICAPS) { - ircd_log(LOG_ERROR, "More than %d caps loaded - what???", MAXCLICAPS); + unreal_log(ULOG_ERROR, "module", "BUG_TOO_MANY_CLIENTCAPABILITIES", NULL, + "[BUG] clicap_pre_rehash: More than $count caps loaded - this should never happen", + log_data_integer("count", MAXCLICAPS)); break; } safe_strdup(old_caps[i], clicap->name); @@ -318,13 +328,13 @@ void clicap_post_rehash(void) int i; int found; - if (!loop.ircd_rehashing) + if (!loop.rehashing) return; /* First boot */ /* Let's deal with CAP DEL first: * Go through the old caps and see what's missing now. */ - for (i = 0; old_caps[i]; i++) + for (i = 0; i < MAXCLICAPS && old_caps[i]; i++) { name = old_caps[i]; found = 0; @@ -351,7 +361,7 @@ void clicap_post_rehash(void) { name = clicap->name; found = 0; - for (i = 0; old_caps[i]; i++) + for (i = 0; i < MAXCLICAPS && old_caps[i]; i++) { if (!strcmp(old_caps[i], name)) { @@ -368,6 +378,6 @@ void clicap_post_rehash(void) } /* Now free the old caps. */ - for (i = 0; old_caps[i]; i++) + for (i = 0; i < MAXCLICAPS && old_caps[i]; i++) safe_free(old_caps[i]); } diff --git a/src/api-command.c b/src/api-command.c index e32b5c7..f61e2bf 100644 --- a/src/api-command.c +++ b/src/api-command.c @@ -23,8 +23,8 @@ #include "unrealircd.h" /* Forward declarations */ -static Command *CommandAddInternal(Module *module, char *cmd, CmdFunc func, AliasCmdFunc aliasfunc, unsigned char params, int flags); -static RealCommand *add_Command_backend(char *cmd); +static Command *CommandAddInternal(Module *module, const char *cmd, CmdFunc func, AliasCmdFunc aliasfunc, unsigned char params, int flags); +static RealCommand *add_Command_backend(const char *cmd); /** @defgroup CommandAPI Command API * @{ @@ -32,7 +32,7 @@ static RealCommand *add_Command_backend(char *cmd); /** Returns 1 if the specified command exists */ -int CommandExists(char *name) +int CommandExists(const char *name) { RealCommand *p; @@ -53,7 +53,7 @@ int CommandExists(char *name) * @param flags Who may execute this command - one or more CMD_* flags * @returns The newly registered command, or NULL in case of error (eg: already exist) */ -Command *CommandAdd(Module *module, char *cmd, CmdFunc func, unsigned char params, int flags) +Command *CommandAdd(Module *module, const char *cmd, CmdFunc func, unsigned char params, int flags) { if (flags & CMD_ALIAS) { @@ -75,7 +75,7 @@ Command *CommandAdd(Module *module, char *cmd, CmdFunc func, unsigned char param * @param flags Who may execute this command - one or more CMD_* flags * @returns The newly registered command (alias), or NULL in case of error (eg: already exist) */ -Command *AliasAdd(Module *module, char *cmd, AliasCmdFunc aliasfunc, unsigned char params, int flags) +Command *AliasAdd(Module *module, const char *cmd, AliasCmdFunc aliasfunc, unsigned char params, int flags) { if (!(flags & CMD_ALIAS)) flags |= CMD_ALIAS; @@ -84,7 +84,7 @@ Command *AliasAdd(Module *module, char *cmd, AliasCmdFunc aliasfunc, unsigned ch /** @} */ -static Command *CommandAddInternal(Module *module, char *cmd, CmdFunc func, AliasCmdFunc aliasfunc, unsigned char params, int flags) +static Command *CommandAddInternal(Module *module, const char *cmd, CmdFunc func, AliasCmdFunc aliasfunc, unsigned char params, int flags) { Command *command = NULL; RealCommand *c; @@ -191,7 +191,7 @@ void CommandDel(Command *command) * @note If mtags is NULL then new message tags are created for the command * (and destroyed before return). */ -void do_cmd(Client *client, MessageTag *mtags, char *cmd, int parc, char *parv[]) +void do_cmd(Client *client, MessageTag *mtags, const char *cmd, int parc, const char *parv[]) { RealCommand *cmptr; @@ -233,7 +233,7 @@ void init_CommandHash(void) CommandAdd(NULL, MSG_MODULE, cmd_module, MAXPARA, CMD_USER); } -static RealCommand *add_Command_backend(char *cmd) +static RealCommand *add_Command_backend(const char *cmd) { RealCommand *c = safe_alloc(sizeof(RealCommand)); @@ -250,7 +250,7 @@ static RealCommand *add_Command_backend(char *cmd) */ /** Find a command by name and flags */ -RealCommand *find_command(char *cmd, int flags) +RealCommand *find_command(const char *cmd, int flags) { RealCommand *p; for (p = CommandHash[toupper(*cmd)]; p; p = p->next) { @@ -269,7 +269,7 @@ RealCommand *find_command(char *cmd, int flags) } /** Find a command by name (no access rights check) */ -RealCommand *find_command_simple(char *cmd) +RealCommand *find_command_simple(const char *cmd) { RealCommand *c; diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 4412af1..d6dbfdc 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -34,47 +34,50 @@ static Efunction *Efunctions[MAXEFUNCTIONS]; /* Efunction objects (used for reha static EfunctionsList efunction_table[MAXEFUNCTIONS]; /* Efuncs */ -void (*do_join)(Client *client, int parc, char *parv[]); -void (*join_channel)(Channel *channel, Client *client, MessageTag *mtags, int flags); -int (*can_join)(Client *client, Channel *channel, char *key, char *parv[]); -void (*do_mode)(Channel *channel, Client *client, MessageTag *mtags, int parc, char *parv[], time_t sendts, int samode); -void (*set_mode)(Channel *channel, Client *client, int parc, char *parv[], u_int *pcount, - char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce); -void (*cmd_umode)(Client *client, MessageTag *mtags, int parc, char *parv[]); -int (*register_user)(Client *client, char *nick, char *username, char *umode, char *virthost, char *ip); +void (*do_join)(Client *client, int parc, const char *parv[]); +void (*join_channel)(Channel *channel, Client *client, MessageTag *mtags, const char *member_modes); +int (*can_join)(Client *client, Channel *channel, const char *key, char **errmsg); +void (*do_mode)(Channel *channel, Client *client, MessageTag *mtags, int parc, const char *parv[], time_t sendts, int samode); +MultiLineMode *(*set_mode)(Channel *channel, Client *client, int parc, const char *parv[], u_int *pcount, + char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]); +void (*set_channel_mode)(Channel *channel, char *modes, char *parameters); +void (*cmd_umode)(Client *client, MessageTag *mtags, int parc, const char *parv[]); +int (*register_user)(Client *client); int (*tkl_hash)(unsigned int c); char (*tkl_typetochar)(int type); int (*tkl_chartotype)(char c); -char *(*tkl_type_string)(TKL *tk); -TKL *(*tkl_add_serverban)(int type, char *usermask, char *hostmask, char *reason, char *setby, +const char *(*tkl_type_string)(TKL *tk); +const char *(*tkl_type_config_string)(TKL *tk); +char *(*tkl_uhost)(TKL *tkl, char *buf, size_t buflen, int options); +TKL *(*tkl_add_serverban)(int type, const char *usermask, const char *hostmask, const char *reason, const char *setby, time_t expire_at, time_t set_at, int soft, int flags); -TKL *(*tkl_add_nameban)(int type, char *name, int hold, char *reason, char *setby, +TKL *(*tkl_add_nameban)(int type, const char *name, int hold, const char *reason, const char *setby, time_t expire_at, time_t set_at, int flags); -TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, Match *match, char *setby, +TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, Match *match, const char *setby, time_t expire_at, time_t set_at, - time_t spamf_tkl_duration, char *spamf_tkl_reason, + time_t spamf_tkl_duration, const char *spamf_tkl_reason, int flags); -TKL *(*tkl_add_banexception)(int type, char *usermask, char *hostmask, char *reason, char *set_by, - time_t expire_at, time_t set_at, int soft, char *bantypes, int flags); +TKL *(*tkl_add_banexception)(int type, const char *usermask, const char *hostmask, const char *reason, const char *set_by, + time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags); TKL *(*tkl_del_line)(TKL *tkl); void (*tkl_check_local_remove_shun)(TKL *tmp); int (*find_tkline_match)(Client *client, int skip_soft); int (*find_shun)(Client *client); int(*find_spamfilter_user)(Client *client, int flags); -TKL *(*find_qline)(Client *client, char *nick, int *ishold); +TKL *(*find_qline)(Client *client, const char *nick, int *ishold); TKL *(*find_tkline_match_zap)(Client *client); -void (*tkl_stats)(Client *client, int type, char *para, int *cnt); +void (*tkl_stats)(Client *client, int type, const char *para, int *cnt); void (*tkl_sync)(Client *client); -void (*cmd_tkl)(Client *client, MessageTag *mtags, int parc, char *parv[]); -int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration); -int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk); -int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd); +void (*cmd_tkl)(Client *client, MessageTag *mtags, int parc, const char *parv[]); +int (*place_host_ban)(Client *client, BanAction action, const char *reason, long duration); +int (*match_spamfilter)(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, TKL **rettk); +int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, const char *cmd); int (*join_viruschan)(Client *client, TKL *tk, int type); -unsigned char *(*StripColors)(unsigned char *text); -const char *(*StripControlCodes)(unsigned char *text); -void (*spamfilter_build_user_string)(char *buf, char *nick, Client *client); +const char *(*StripColors)(const char *text); +const char *(*StripControlCodes)(const char *text); +void (*spamfilter_build_user_string)(char *buf, const char *nick, Client *client); void (*send_protoctl_servers)(Client *client, int response); -int (*verify_link)(Client *client, char *servername, ConfigItem_link **link_out); +int (*verify_link)(Client *client, ConfigItem_link **link_out); void (*introduce_user)(Client *to, Client *client); void (*send_server_message)(Client *client); void (*broadcast_md_client)(ModDataInfo *mdi, Client *client, ModData *md); @@ -82,37 +85,40 @@ void (*broadcast_md_channel)(ModDataInfo *mdi, Channel *channel, ModData *md); void (*broadcast_md_member)(ModDataInfo *mdi, Channel *channel, Member *m, ModData *md); void (*broadcast_md_membership)(ModDataInfo *mdi, Client *client, Membership *m, ModData *md); int (*check_banned)(Client *client, int exitflags); -int (*check_deny_version)(Client *client, char *software, int protocol, char *flags); -void (*broadcast_md_client_cmd)(Client *except, Client *sender, Client *acptr, char *varname, char *value); -void (*broadcast_md_channel_cmd)(Client *except, Client *sender, Channel *channel, char *varname, char *value); -void (*broadcast_md_member_cmd)(Client *except, Client *sender, Channel *channel, Client *acptr, char *varname, char *value); -void (*broadcast_md_membership_cmd)(Client *except, Client *sender, Client *acptr, Channel *channel, char *varname, char *value); +int (*check_deny_version)(Client *client, const char *software, int protocol, const char *flags); +void (*broadcast_md_client_cmd)(Client *except, Client *sender, Client *acptr, const char *varname, const char *value); +void (*broadcast_md_channel_cmd)(Client *except, Client *sender, Channel *channel, const char *varname, const char *value); +void (*broadcast_md_member_cmd)(Client *except, Client *sender, Channel *channel, Client *acptr, const char *varname, const char *value); +void (*broadcast_md_membership_cmd)(Client *except, Client *sender, Client *acptr, Channel *channel, const char *varname, const char *value); +void (*moddata_add_s2s_mtags)(Client *client, MessageTag **mtags); +void (*moddata_extract_s2s_mtags)(Client *client, MessageTag *mtags); void (*send_moddata_client)(Client *srv, Client *client); void (*send_moddata_channel)(Client *srv, Channel *channel); void (*send_moddata_members)(Client *srv); void (*broadcast_moddata_client)(Client *client); -int (*match_user)(char *rmask, Client *client, int options); +int (*match_user)(const char *rmask, Client *client, int options); void (*userhost_changed)(Client *client); void (*userhost_save_current)(Client *client); void (*send_join_to_local_users)(Client *client, Channel *channel, MessageTag *mtags); int (*do_nick_name)(char *nick); int (*do_remote_nick_name)(char *nick); -char *(*charsys_get_current_languages)(void); +const char *(*charsys_get_current_languages)(void); void (*broadcast_sinfo)(Client *client, Client *to, Client *except); +void (*connect_server)(ConfigItem_link *aconf, Client *by, struct hostent *hp); void (*parse_message_tags)(Client *client, char **str, MessageTag **mtag_list); -char *(*mtags_to_string)(MessageTag *m, Client *client); -int (*can_send_to_channel)(Client *client, Channel *channel, char **msgtext, char **errmsg, int notice); +const char *(*mtags_to_string)(MessageTag *m, Client *client); +int (*can_send_to_channel)(Client *client, Channel *channel, const char **msgtext, const char **errmsg, int notice); void (*broadcast_md_globalvar)(ModDataInfo *mdi, ModData *md); -void (*broadcast_md_globalvar_cmd)(Client *except, Client *sender, char *varname, char *value); -int (*tkl_ip_hash)(char *ip); +void (*broadcast_md_globalvar_cmd)(Client *except, Client *sender, const char *varname, const char *value); +int (*tkl_ip_hash)(const char *ip); int (*tkl_ip_hash_type)(int type); -void (*sendnotice_tkl_del)(char *removed_by, TKL *tkl); +void (*sendnotice_tkl_del)(const char *removed_by, TKL *tkl); void (*sendnotice_tkl_add)(TKL *tkl); void (*free_tkl)(TKL *tkl); -TKL *(*find_tkl_serverban)(int type, char *usermask, char *hostmask, int softban); -TKL *(*find_tkl_banexception)(int type, char *usermask, char *hostmask, int softban); -TKL *(*find_tkl_nameban)(int type, char *name, int hold); -TKL *(*find_tkl_spamfilter)(int type, char *match_string, unsigned short action, unsigned short target); +TKL *(*find_tkl_serverban)(int type, const char *usermask, const char *hostmask, int softban); +TKL *(*find_tkl_banexception)(int type, const char *usermask, const char *hostmask, int softban); +TKL *(*find_tkl_nameban)(int type, const char *name, int hold); +TKL *(*find_tkl_spamfilter)(int type, const char *match_string, unsigned short action, unsigned short target); int (*find_tkl_exception)(int ban_type, Client *client); int (*is_silenced)(Client *client, Client *acptr); int (*del_silence)(Client *client, const char *mask); @@ -120,9 +126,17 @@ int (*add_silence)(Client *client, const char *mask, int senderr); void *(*labeled_response_save_context)(void); void (*labeled_response_set_context)(void *ctx); void (*labeled_response_force_end)(void); -void (*kick_user)(MessageTag *mtags, Channel *channel, Client *client, Client *victim, char *comment); +void (*kick_user)(MessageTag *mtags, Channel *channel, Client *client, Client *victim, const char *comment); +int (*watch_add)(const char *nick, Client *client, int flags); +int (*watch_del)(const char *nick, Client *client, int flags); +int (*watch_del_list)(Client *client, int flags); +Watch *(*watch_get)(const char *nick); +int (*watch_check)(Client *client, int reply, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event)); +void (*do_unreal_log_remote_deliver)(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized); +char *(*get_chmodes_for_user)(Client *client, const char *flags); +WhoisConfigDetails (*whois_get_policy)(Client *client, Client *target, const char *name); -Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*cfunc)()) +Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) { Efunction *p; @@ -140,8 +154,10 @@ Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), p->func.voidfunc = vfunc; if (pvfunc) p->func.pvoidfunc = pvfunc; - if (cfunc) - p->func.pcharfunc = cfunc; + if (stringfunc) + p->func.stringfunc = stringfunc; + if (conststringfunc) + p->func.conststringfunc = conststringfunc; p->type = eftype; p->owner = module; AddListItem(p, Efunctions[eftype]); @@ -256,7 +272,9 @@ void efunctions_switchover(void) continue; if (!efunction_table[i].funcptr) { - ircd_log(LOG_ERROR, "[BUG] efunctions_switchover(): someone forgot to initialize the function table for efunc %d", i); + unreal_log(ULOG_FATAL, "module", "BUG_EFUNCTIONS_SWITCHOVER", NULL, + "[BUG] efunctions_switchover(): someone forgot to initialize the function table for efunc $efunction_number", + log_data_integer("efunction_number", i)); abort(); } *efunction_table[i].funcptr = e->func.voidfunc; /* This is the new one. */ @@ -273,10 +291,19 @@ void efunctions_switchover(void) } } -#define efunc_init_function(what, func, default_func) efunc_init_function_(what, #func, (void *)&func, default_func) +#define efunc_init_function(what, func, default_func) efunc_init_function_(what, #func, (void *)&func, (void *)default_func) void efunc_init_function_(EfunctionType what, char *name, void *func, void *default_func) { + if (what >= MAXEFUNCTIONS) + { + /* increase MAXEFUNCTIONS if you ever encounter that --k4be */ + unreal_log(ULOG_FATAL, "module", "BUG_EFUNC_INIT_FUNCTION_TOO_MANY", NULL, + "Too many efunctions! ($efunctions_request > $efunctions_max)", + log_data_integer("efunctions_request", what), + log_data_integer("efunctions_max", MAXEFUNCTIONS)); + abort(); + } safe_strdup(efunction_table[what].name, name); efunction_table[what].funcptr = func; efunction_table[what].deffunc = default_func; @@ -290,6 +317,7 @@ void efunctions_init(void) efunc_init_function(EFUNC_CAN_JOIN, can_join, NULL); efunc_init_function(EFUNC_DO_MODE, do_mode, NULL); efunc_init_function(EFUNC_SET_MODE, set_mode, NULL); + efunc_init_function(EFUNC_SET_CHANNEL_MODE, set_channel_mode, NULL); efunc_init_function(EFUNC_CMD_UMODE, cmd_umode, NULL); efunc_init_function(EFUNC_REGISTER_USER, register_user, NULL); efunc_init_function(EFUNC_TKL_HASH, tkl_hash, NULL); @@ -327,6 +355,8 @@ void efunctions_init(void) efunc_init_function(EFUNC_BROADCAST_MD_CHANNEL_CMD, broadcast_md_channel_cmd, NULL); efunc_init_function(EFUNC_BROADCAST_MD_MEMBER_CMD, broadcast_md_member_cmd, NULL); efunc_init_function(EFUNC_BROADCAST_MD_MEMBERSHIP_CMD, broadcast_md_membership_cmd, NULL); + efunc_init_function(EFUNC_MODDATA_ADD_S2S_MTAGS, moddata_add_s2s_mtags, NULL); + efunc_init_function(EFUNC_MODDATA_EXTRACT_S2S_MTAGS, moddata_extract_s2s_mtags, NULL); efunc_init_function(EFUNC_SEND_MODDATA_CLIENT, send_moddata_client, NULL); efunc_init_function(EFUNC_SEND_MODDATA_CHANNEL, send_moddata_channel, NULL); efunc_init_function(EFUNC_SEND_MODDATA_MEMBERS, send_moddata_members, NULL); @@ -339,10 +369,12 @@ void efunctions_init(void) efunc_init_function(EFUNC_DO_REMOTE_NICK_NAME, do_remote_nick_name, NULL); efunc_init_function(EFUNC_CHARSYS_GET_CURRENT_LANGUAGES, charsys_get_current_languages, NULL); efunc_init_function(EFUNC_BROADCAST_SINFO, broadcast_sinfo, NULL); + efunc_init_function(EFUNC_CONNECT_SERVER, connect_server, NULL); efunc_init_function(EFUNC_PARSE_MESSAGE_TAGS, parse_message_tags, &parse_message_tags_default_handler); efunc_init_function(EFUNC_MTAGS_TO_STRING, mtags_to_string, &mtags_to_string_default_handler); efunc_init_function(EFUNC_TKL_CHARTOTYPE, tkl_chartotype, NULL); efunc_init_function(EFUNC_TKL_TYPE_STRING, tkl_type_string, NULL); + efunc_init_function(EFUNC_TKL_TYPE_CONFIG_STRING, tkl_type_config_string, NULL); efunc_init_function(EFUNC_CAN_SEND_TO_CHANNEL, can_send_to_channel, NULL); efunc_init_function(EFUNC_BROADCAST_MD_GLOBALVAR, broadcast_md_globalvar, NULL); efunc_init_function(EFUNC_BROADCAST_MD_GLOBALVAR_CMD, broadcast_md_globalvar_cmd, NULL); @@ -365,4 +397,13 @@ void efunctions_init(void) efunc_init_function(EFUNC_LABELED_RESPONSE_SET_CONTEXT, labeled_response_set_context, labeled_response_set_context_default_handler); efunc_init_function(EFUNC_LABELED_RESPONSE_FORCE_END, labeled_response_force_end, labeled_response_force_end_default_handler); efunc_init_function(EFUNC_KICK_USER, kick_user, NULL); + efunc_init_function(EFUNC_WATCH_ADD, watch_add, NULL); + efunc_init_function(EFUNC_WATCH_DEL, watch_del, NULL); + efunc_init_function(EFUNC_WATCH_DEL_LIST, watch_del_list, NULL); + efunc_init_function(EFUNC_WATCH_GET, watch_get, NULL); + efunc_init_function(EFUNC_WATCH_CHECK, watch_check, NULL); + efunc_init_function(EFUNC_TKL_UHOST, tkl_uhost, NULL); + efunc_init_function(EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER, do_unreal_log_remote_deliver, do_unreal_log_remote_deliver_default_handler); + efunc_init_function(EFUNC_GET_CHMODES_FOR_USER, get_chmodes_for_user, NULL); + efunc_init_function(EFUNC_WHOIS_GET_POLICY, whois_get_policy, NULL); } diff --git a/src/api-event.c b/src/api-event.c index 8c328d2..799edb7 100644 --- a/src/api-event.c +++ b/src/api-event.c @@ -45,7 +45,7 @@ extern EVENT(unrealdb_expire_secret_cache); * can be later, in case of high load, in very extreme cases even up to 1000 or 2000 * msec later but that would be very unusual. Just saying, it's not a guarantee.. */ -Event *EventAdd(Module *module, char *name, vFP event, void *data, long every_msec, int count) +Event *EventAdd(Module *module, const char *name, vFP event, void *data, long every_msec, int count) { Event *newevent; @@ -56,16 +56,6 @@ Event *EventAdd(Module *module, char *name, vFP event, void *data, long every_ms return NULL; } - if ((every_msec < 100) && (count == 0)) - { - ircd_log(LOG_ERROR, "[BUG] EventAdd() '%s' from module '%s' with suspiciously low every_msec value (%ld). " - "Note that it is in milliseconds now (1000 = 1 second)!", - name, - module ? module->header->name : "???", - every_msec); - every_msec = 100; - } - newevent = safe_alloc(sizeof(Event)); safe_strdup(newevent->name, name); newevent->count = count; @@ -128,12 +118,16 @@ static void EventDelReal(Event *e) { if (!e->deleted) { - ircd_log(LOG_ERROR, "EventDelReal called while e->deleted is 0. This cannot happen. Event name: %s.", e->name); + unreal_log(ULOG_FATAL, "module", "BUG_EVENTDELREAL_ZERO", NULL, + "[BUG] EventDelReal called while e->deleted is 0. This cannot happen. Event name: $event_name", + log_data_string("event_name", e->name)); abort(); } if (e->owner) { - ircd_log(LOG_ERROR, "EventDelReal called while e->owner is non-NULL. This cannot happen. Event name: %s.", e->name); + unreal_log(ULOG_FATAL, "module", "BUG_EVENTDELREAL_NULL", NULL, + "[BUG] EventDelReal called while e->owner is NULL. This cannot happen. Event name: $event_name", + log_data_string("event_name", e->name)); abort(); } safe_free(e->name); @@ -153,7 +147,7 @@ static void CleanupEvents(void) } } -Event *EventFind(char *name) +Event *EventFind(const char *name) { Event *eventptr; @@ -173,19 +167,7 @@ int EventMod(Event *event, EventInfo *mods) } if (mods->flags & EMOD_EVERY) - { - if (mods->every_msec < 100) - { - ircd_log(LOG_ERROR, "[BUG] EventMod() for '%s' from module '%s' with suspiciously low every_msec value (%lld). " - "Note that it is in milliseconds now (1000 = 1 second)!", - event->name, - event->owner ? event->owner->header->name : "???", - (long long)mods->every_msec); - mods->every_msec = 100; - } - event->every_msec = mods->every_msec; - } if (mods->flags & EMOD_HOWMANY) event->count = mods->count; if (mods->flags & EMOD_NAME) @@ -240,7 +222,7 @@ void SetupEvents(void) 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, "try_connections", try_connections, NULL, 2000, 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/api-extban.c b/src/api-extban.c index fdf7f8b..a750689 100644 --- a/src/api-extban.c +++ b/src/api-extban.c @@ -22,144 +22,285 @@ #include "unrealircd.h" -Extban MODVAR ExtBan_Table[EXTBANTABLESZ]; /* this should be fastest */ -unsigned MODVAR short ExtBan_highest = 0; +/** List of all extbans, their handlers, etc */ +MODVAR Extban *extbans = NULL; void set_isupport_extban(void) { - int i; - char extbanstr[EXTBANTABLESZ+1], *m; + char extbanstr[512]; + Extban *e; + char *p = extbanstr; + + for (e = extbans; e; e = e->next) + *p++ = e->letter; + *p = '\0'; - m = extbanstr; - for (i = 0; i <= ExtBan_highest; i++) - { - if (ExtBan_Table[i].flag) - *m++ = ExtBan_Table[i].flag; - } - *m = 0; ISupportSetFmt(NULL, "EXTBAN", "~,%s", extbanstr); } -Extban *findmod_by_bantype(char c) +Extban *findmod_by_bantype(const char *str, const char **remainder) { -int i; + Extban *e; + int ban_name_length; + const char *p = strchr(str, ':'); - for (i=0; i <= ExtBan_highest; i++) - if (ExtBan_Table[i].flag == c) - return &ExtBan_Table[i]; + if (!p || !p[1]) + { + if (remainder) + *remainder = NULL; + return NULL; + } + if (remainder) + *remainder = p+1; + + ban_name_length = p - str - 1; + + for (e=extbans; e; e = e->next) + { + if ((ban_name_length == 1) && (e->letter == str[1])) + return e; + if (e->name) + { + int namelen = strlen(e->name); + if ((namelen == ban_name_length) && !strncmp(e->name, str+1, namelen)) + return e; + } + } return NULL; } +/* Check if this is a valid extended ban name */ +int is_valid_extban_name(const char *p) +{ + if (!*p) + return 0; /* empty name */ + for (; *p; p++) + if (!isalnum(*p) && !strchr("_-", *p)) + return 0; + return 1; +} + +static void extban_add_sorted(Extban *n) +{ + Extban *m; + + if (extbans == NULL) + { + extbans = n; + return; + } + + for (m = extbans; m; m = m->next) + { + if (m->letter == '\0') + abort(); + if (sort_character_lowercase_before_uppercase(n->letter, m->letter)) + { + /* Insert us before */ + if (m->prev) + m->prev->next = n; + else + extbans = n; /* new head */ + n->prev = m->prev; + + n->next = m; + m->prev = n; + return; + } + if (!m->next) + { + /* Append us at end */ + m->next = n; + n->prev = m; + return; + } + } +} + Extban *ExtbanAdd(Module *module, ExtbanInfo req) { - int slot; + Extban *e; + int existing = 0; - if (findmod_by_bantype(req.flag)) + if (!req.name) { - if (module) - module->errorcode = MODERR_EXISTS; - return NULL; - } - - /* TODO: perhaps some sanity checking on a-zA-Z0-9? */ - for (slot = 0; slot < EXTBANTABLESZ; slot++) - if (ExtBan_Table[slot].flag == '\0') - break; - if (slot >= EXTBANTABLESZ - 1) - { - if (module) - module->errorcode = MODERR_NOSPACE; + module->errorcode = MODERR_INVALID; + unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, + "ExtbanAdd(): name must be specified for ban (new in U6). Module: $module_name", + log_data_string("module_name", module->header->name)); return NULL; } - ExtBan_Table[slot].flag = req.flag; - ExtBan_Table[slot].is_ok = req.is_ok; - ExtBan_Table[slot].conv_param = req.conv_param; - ExtBan_Table[slot].is_banned = req.is_banned; - ExtBan_Table[slot].owner = module; - ExtBan_Table[slot].options = req.options; + + if (!req.is_banned_events && req.is_banned) + { + module->errorcode = MODERR_INVALID; + unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, + "ExtbanAdd(): module must indicate via .is_banned_events on which BANCHK_* " + "events to listen on (new in U6). Module: $module_name", + log_data_string("module_name", module->header->name)); + return NULL; + } + + if (!isalnum(req.letter)) + { + module->errorcode = MODERR_INVALID; + unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, + "ExtbanAdd(): module tried to add extban which is not alphanumeric. " + "Module: $module_name", + log_data_string("module_name", module->header->name)); + return NULL; + } + + if (!is_valid_extban_name(req.name)) + { + module->errorcode = MODERR_INVALID; + unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, + "ExtbanAdd(): module tried to add extban with an invalid name ($extban_name). " + "Module: $module_name", + log_data_string("module_name", module->header->name), + log_data_string("extban_name", req.name)); + return NULL; + } + + for (e=extbans; e; e = e->next) + { + if (e->letter == req.letter) + { + if (e->unloaded) + { + e->unloaded = 0; + existing = 1; + break; + } else { + if (module) + module->errorcode = MODERR_EXISTS; + return NULL; + } + } + } + + if (!e) + { + /* Not found, create */ + e = safe_alloc(sizeof(Extban)); + e->letter = req.letter; + extban_add_sorted(e); + } + e->letter = req.letter; + safe_strdup(e->name, req.name); + e->is_ok = req.is_ok; + e->conv_param = req.conv_param; + e->is_banned = req.is_banned; + e->is_banned_events = req.is_banned_events; + e->owner = module; + e->options = req.options; if (module) { ModuleObject *banobj = safe_alloc(sizeof(ModuleObject)); - banobj->object.extban = &ExtBan_Table[slot]; + banobj->object.extban = e; banobj->type = MOBJ_EXTBAN; AddListItem(banobj, module->objects); module->errorcode = MODERR_NOERROR; } - ExtBan_highest = slot; set_isupport_extban(); - return &ExtBan_Table[slot]; + return e; } -void ExtbanDel(Extban *eb) +static void unload_extban_commit(Extban *e) { - /* Just zero it all away.. */ + /* Should we mass unban everywhere? + * Hmmm. Not needed per se, user can always unset + * themselves. Leaning towards no atm. + */ + // noop - if (eb->owner) + /* Then unload the extban */ + DelListItem(e, extbans); + safe_free(e); + set_isupport_extban(); +} + +void ExtbanDel(Extban *e) +{ + /* Always free the module object */ + if (e->owner) { ModuleObject *banobj; - for (banobj = eb->owner->objects; banobj; banobj = banobj->next) + for (banobj = e->owner->objects; banobj; banobj = banobj->next) { - if (banobj->type == MOBJ_EXTBAN && banobj->object.extban == eb) + if (banobj->type == MOBJ_EXTBAN && banobj->object.extban == e) { - DelListItem(banobj, eb->owner->objects); + DelListItem(banobj, e->owner->objects); safe_free(banobj); break; } } } - memset(eb, 0, sizeof(Extban)); - set_isupport_extban(); - /* Hmm do we want to go trough all chans and remove the bans? - * I would say 'no' because perhaps we are just reloading, - * and else.. well... screw them? - */ -} -/* NOTE: the routines below can safely assume the ban has at - * least the '~t:' part (t=type). -- Syzop - */ + /* Whether we can actually (already) free the Extban, it depends... */ + if (loop.rehashing) + e->unloaded = 1; + else + unload_extban_commit(e); +} /** General is_ok for n!u@h stuff that also deals with recursive extbans. */ -int extban_is_ok_nuh_extban(Client *client, Channel* channel, char *para, int checkt, int what, int what2) +int extban_is_ok_nuh_extban(BanContext *b) { - char *mask = (para + 3); - Extban *p = NULL; int isok; static int extban_is_ok_recursion = 0; /* Mostly copied from clean_ban_mask - but note MyUser checks aren't needed here: extban->is_ok() according to cmd_mode isn't called for nonlocal. */ - if (is_extended_ban(mask)) + if (is_extended_ban(b->banstr)) { - if (extban_is_ok_recursion) - return 0; /* Fail: more than one stacked extban */ + const char *nextbanstr; + Extban *extban = NULL; - if ((checkt == EXBCHK_PARAM) && RESTRICT_EXTENDEDBANS && !ValidatePermissionsForPath("immune:restrict-extendedbans",client,NULL,channel,NULL)) + /* We're dealing with a stacked extended ban. + * Rules: + * 1) You can only stack once, so: ~x:~y:something and not ~x:~y:~z... + * 2) The second item may never be an action modifier, nor have the + * EXTBOPT_NOSTACKCHILD letter set (for things like a textban). + */ + + if (extban_is_ok_recursion) + return 0; /* Rule #1 violation (more than one stacked extban) */ + + if ((b->is_ok_check == EXBCHK_PARAM) && RESTRICT_EXTENDEDBANS && !ValidatePermissionsForPath("immune:restrict-extendedbans",b->client,NULL,b->channel,NULL)) { /* Test if this specific extban has been disabled. * (We can be sure RESTRICT_EXTENDEDBANS is not *. Else this extended ban wouldn't be happening at all.) */ - if (strchr(RESTRICT_EXTENDEDBANS, mask[1])) + if (strchr(RESTRICT_EXTENDEDBANS, b->banstr[1])) { - sendnotice(client, "Setting/removing of extended bantypes '%s' has been disabled.", RESTRICT_EXTENDEDBANS); + sendnotice(b->client, "Setting/removing of extended bantypes '%s' has been disabled.", RESTRICT_EXTENDEDBANS); return 0; /* Fail */ } } - p = findmod_by_bantype(mask[1]); - if (!p) + extban = findmod_by_bantype(b->banstr, &nextbanstr); + if (!extban) { - if (what == MODE_DEL) + if (b->what == MODE_DEL) { return 1; /* Always allow killing unknowns. */ } return 0; /* Don't add unknown extbans. */ } - /* Now we have to ask the stacked extban if it's ok. */ - if (p->is_ok) + + if ((extban->options & EXTBOPT_ACTMODIFIER) || (extban->options & EXTBOPT_NOSTACKCHILD)) { + /* Rule #2 violation */ + return 0; + } + + /* Now we have to ask the stacked extban if it's ok. */ + if (extban->is_ok) + { + b->banstr = nextbanstr; extban_is_ok_recursion++; - isok = p->is_ok(client, channel, mask, checkt, what, what2); + isok = extban->is_ok(b); extban_is_ok_recursion--; return isok; } @@ -171,19 +312,15 @@ int extban_is_ok_nuh_extban(Client *client, Channel* channel, char *para, int ch * to ensure the parameter is nick!user@host. * most of the code is just copied from clean_ban_mask. */ -char *extban_conv_param_nuh(char *para) +const char *extban_conv_param_nuh(BanContext *b, Extban *extban) { char *cp, *user, *host, *mask, *ret = NULL; static char retbuf[USERLEN + NICKLEN + HOSTLEN + 32]; char tmpbuf[USERLEN + NICKLEN + HOSTLEN + 32]; - char pfix[8]; - if (strlen(para)<3) - return NULL; /* normally impossible */ - - strlcpy(tmpbuf, para, sizeof(retbuf)); - mask = tmpbuf + 3; - strlcpy(pfix, tmpbuf, mask - tmpbuf + 1); + /* Work on a copy */ + strlcpy(tmpbuf, b->banstr, sizeof(retbuf)); + mask = tmpbuf; if (!*mask) return NULL; /* empty extban */ @@ -202,13 +339,13 @@ char *extban_conv_param_nuh(char *para) if (!ret) ret = make_nick_user_host(trim_str(cp,NICKLEN), trim_str(user,USERLEN), trim_str(host,HOSTLEN)); - ircsnprintf(retbuf, sizeof(retbuf), "%s%s", pfix, ret); + strlcpy(retbuf, ret, sizeof(retbuf)); return retbuf; } /** conv_param to deal with stacked extbans. */ -char *extban_conv_param_nuh_or_extban(char *para) +const char *extban_conv_param_nuh_or_extban(BanContext *b, Extban *self_extban) { #if (USERLEN + NICKLEN + HOSTLEN + 32) > 256 #error "wtf?" @@ -217,83 +354,77 @@ char *extban_conv_param_nuh_or_extban(char *para) static char printbuf[256]; char *mask; char tmpbuf[USERLEN + NICKLEN + HOSTLEN + 32]; - char bantype = para[1]; - char *ret = NULL; - Extban *p = NULL; + const char *ret = NULL; + const char *nextbanstr; + Extban *extban = NULL; static int extban_recursion = 0; - if ((strlen(para)>3) && is_extended_ban(para+3)) - { - /* We're dealing with a stacked extended ban. - * Rules: - * 1) You can only stack once, so: ~x:~y:something and not ~x:~y:~z... - * 2) The first item must be an action modifier, such as ~q/~n/~j - * 3) The second item may never be an action modifier, nor have the - * EXTBOPT_NOSTACKCHILD flag set (for things like a textban). - */ - - /* Rule #1. Yes the recursion check is also in extban_is_ok_nuh_extban, - * but it's possible to get here without the is_ok() function ever - * being called (think: non-local client). And no, don't delete it - * there either. It needs to be in BOTH places. -- Syzop - */ - if (extban_recursion) - return NULL; + if (!is_extended_ban(b->banstr)) + return extban_conv_param_nuh(b, self_extban); - /* Rule #2 */ - p = findmod_by_bantype(para[1]); - if (p && !(p->options & EXTBOPT_ACTMODIFIER)) - { - /* Rule #2 violation */ - return NULL; - } - - strlcpy(tmpbuf, para, sizeof(tmpbuf)); - mask = tmpbuf + 3; - /* Already did restrict-extended bans check. */ - p = findmod_by_bantype(mask[1]); - if (!p) - { - /* Handling unknown bantypes in is_ok. Assume that it's ok here. */ - return para; - } - if ((p->options & EXTBOPT_ACTMODIFIER) || (p->options & EXTBOPT_NOSTACKCHILD)) - { - /* Rule #3 violation */ - return NULL; - } - - if (p->conv_param) - { - extban_recursion++; - ret = p->conv_param(mask); - extban_recursion--; - if (ret) - { - /* - * If bans are stacked, then we have to use two buffers - * to prevent ircsnprintf() from going into a loop. - */ - ircsnprintf(printbuf, sizeof(printbuf), "~%c:%s", bantype, ret); /* Make sure our extban prefix sticks. */ - memcpy(retbuf, printbuf, sizeof(retbuf)); - return retbuf; - } - else - { - return NULL; /* Fail. */ - } - } - /* I honestly don't know what the deal is with the 80 char cap in clean_ban_mask is about. So I'm leaving it out here. -- aquanight */ - /* I don't know why it's 80, but I like a limit anyway. A ban of 500 characters can never be good... -- Syzop */ - if (strlen(para) > 80) - { - strlcpy(retbuf, para, 128); - return retbuf; - } - return para; - } - else + /* We're dealing with a stacked extended ban. + * Rules: + * 1) You can only stack once, so: ~x:~y:something and not ~x:~y:~z... + * 2) The second item may never be an action modifier, nor have the + * EXTBOPT_NOSTACKCHILD letter set (for things like a textban). + */ + + /* Rule #1. Yes the recursion check is also in extban_is_ok_nuh_extban, + * but it's possible to get here without the is_ok() function ever + * being called (think: non-local client). And no, don't delete it + * there either. It needs to be in BOTH places. -- Syzop + */ + if (extban_recursion) + return NULL; + + strlcpy(tmpbuf, b->banstr, sizeof(tmpbuf)); + extban = findmod_by_bantype(tmpbuf, &nextbanstr); + + if (!extban) { - return extban_conv_param_nuh(para); + /* Handling unknown bantypes in is_ok. Assume that it's ok here. */ + return b->banstr; } + + b->banstr = nextbanstr; + + if ((extban->options & EXTBOPT_ACTMODIFIER) || (extban->options & EXTBOPT_NOSTACKCHILD)) + { + /* Rule #2 violation */ + return NULL; + } + + if (extban->conv_param) + { + //BanContext *b = safe_alloc(sizeof(BanContext)); + //b->banstr = mask; <-- this is redundant right? we can use existing 'b' context?? + extban_recursion++; + ret = extban->conv_param(b, extban); + extban_recursion--; + ret = prefix_with_extban(ret, b, extban, retbuf, sizeof(retbuf)); + //safe_free(b); + return ret; + } + /* I honestly don't know what the deal is with the 80 char cap in clean_ban_mask is about. So I'm leaving it out here. -- aquanight */ + /* I don't know why it's 80, but I like a limit anyway. A ban of 500 characters can never be good... -- Syzop */ + if (strlen(b->banstr) > 80) + { + strlcpy(retbuf, b->banstr, 128); + return retbuf; + } + return b->banstr; +} + +char *prefix_with_extban(const char *remainder, BanContext *b, Extban *extban, char *buf, size_t buflen) +{ + /* Yes, we support this because it makes code at the caller cleaner */ + if (remainder == NULL) + return NULL; + + if (iConf.named_extended_bans && !(b->conv_options & BCTX_CONV_OPTION_WRITE_LETTER_BANS)) + snprintf(buf, buflen, "~%s:%s", extban->name, remainder); + else + snprintf(buf, buflen, "~%c:%s", extban->letter, remainder); + + return buf; } diff --git a/src/api-history-backend.c b/src/api-history-backend.c index 2c1668d..a569b55 100644 --- a/src/api-history-backend.c +++ b/src/api-history-backend.c @@ -60,13 +60,15 @@ HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mreq) { HistoryBackend *m; int exists = 0; + ModuleObject *mobj; if (!mreq->history_add || !mreq->history_request || !mreq->history_destroy || !mreq->history_set_limit) { - if (module) - module->errorcode = MODERR_INVALID; - ircd_log(LOG_ERROR, "HistoryBackendAdd(): missing a handler for add/del/request/destroy/set_limit"); + module->errorcode = MODERR_INVALID; + unreal_log(ULOG_ERROR, "module", "HISTORYBACKENDADD_API_ERROR", NULL, + "HistoryBackendAdd(): missing a handler for add/del/request/destroy/set_limit. Module: $module_name", + log_data_string("module_name", module->header->name)); return NULL; } m = HistoryBackendFind(mreq->name); @@ -77,8 +79,7 @@ HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mreq) { m->unloaded = 0; } else { - if (module) - module->errorcode = MODERR_EXISTS; + module->errorcode = MODERR_EXISTS; return NULL; } } else { @@ -97,14 +98,11 @@ HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mreq) if (!exists) AddListItem(m, historybackends); - if (module) - { - ModuleObject *mobj = safe_alloc(sizeof(ModuleObject)); - mobj->type = MOBJ_HISTORY_BACKEND; - mobj->object.history_backend = m; - AddListItem(mobj, module->objects); - module->errorcode = MODERR_NOERROR; - } + mobj = safe_alloc(sizeof(ModuleObject)); + mobj->type = MOBJ_HISTORY_BACKEND; + mobj->object.history_backend = m; + AddListItem(mobj, module->objects); + module->errorcode = MODERR_NOERROR; return m; } @@ -138,7 +136,7 @@ void HistoryBackendDel(HistoryBackend *m) m->owner = NULL; } - if (loop.ircd_rehashing) + if (loop.rehashing) m->unloaded = 1; else unload_history_backend_commit(m); @@ -156,7 +154,7 @@ void unload_all_unused_history_backends(void) } } -int history_add(char *object, MessageTag *mtags, char *line) +int history_add(const char *object, MessageTag *mtags, const char *line) { HistoryBackend *hb; @@ -166,7 +164,7 @@ int history_add(char *object, MessageTag *mtags, char *line) return 1; } -HistoryResult *history_request(char *object, HistoryFilter *filter) +HistoryResult *history_request(const char *object, HistoryFilter *filter) { HistoryBackend *hb = historybackends; HistoryResult *r; @@ -183,7 +181,7 @@ HistoryResult *history_request(char *object, HistoryFilter *filter) return NULL; } -int history_destroy(char *object) +int history_destroy(const char *object) { HistoryBackend *hb; @@ -193,7 +191,7 @@ int history_destroy(char *object) return 1; } -int history_set_limit(char *object, int max_lines, long max_t) +int history_set_limit(const char *object, int max_lines, long max_t) { HistoryBackend *hb; @@ -230,7 +228,7 @@ int can_receive_history(Client *client) return 0; } -static void history_send_result_line(Client *client, HistoryLogLine *l, char *batchid) +static void history_send_result_line(Client *client, HistoryLogLine *l, const char *batchid) { if (BadPtr(batchid)) { @@ -238,9 +236,10 @@ static void history_send_result_line(Client *client, HistoryLogLine *l, char *ba } else { MessageTag *m = safe_alloc(sizeof(MessageTag)); m->name = "batch"; - m->value = batchid; + m->value = strdup(batchid); AddListItem(m, l->mtags); sendto_one(client, l->mtags, "%s", l->line); + safe_free(m->value); DelListItem(m, l->mtags); safe_free(m); } diff --git a/src/api-isupport.c b/src/api-isupport.c index a378e5b..29c0433 100644 --- a/src/api-isupport.c +++ b/src/api-isupport.c @@ -86,18 +86,12 @@ void isupport_init(void) { ISupportSet(NULL, "INVEX", NULL); ISupportSet(NULL, "EXCEPTS", NULL); -#ifdef PREFIX_AQ - ISupportSet(NULL, "STATUSMSG", "~&@%+"); -#else - ISupportSet(NULL, "STATUSMSG", "@%+"); -#endif ISupportSet(NULL, "ELIST", "MNUCT"); ISupportSet(NULL, "CASEMAPPING", "ascii"); - ISupportSet(NULL, "NETWORK", ircnet005); + ISupportSet(NULL, "NETWORK", NETWORK_NAME_005); ISupportSetFmt(NULL, "CHANMODES", - CHPAR1 "%s," CHPAR2 "%s," CHPAR3 "%s," CHPAR4 "%s", + CHPAR1 "%s,%s,%s,%s", EXPAR1, EXPAR2, EXPAR3, EXPAR4); - ISupportSet(NULL, "PREFIX", CHPFIX); ISupportSet(NULL, "CHANTYPES", "#"); ISupportSetFmt(NULL, "MODES", "%d", MAXMODEPARAMS); ISupportSetFmt(NULL, "SILENCE", "%d", SILENCE_LIMIT); @@ -118,7 +112,6 @@ void isupport_init(void) ISupportSetFmt(NULL, "MAXLIST", "b:%d,e:%d,I:%d", MAXBANS, MAXBANS, MAXBANS); ISupportSetFmt(NULL, "CHANLIMIT", "#:%d", MAXCHANNELSPERUSER); ISupportSetFmt(NULL, "MAXCHANNELS", "%d", MAXCHANNELSPERUSER); - ISupportSet(NULL, "HCN", NULL); ISupportSet(NULL, "SAFELIST", NULL); ISupportSet(NULL, "NAMESX", NULL); if (UHNAMES_ENABLED) diff --git a/src/api-messagetag.c b/src/api-messagetag.c index 293848d..074f8fc 100644 --- a/src/api-messagetag.c +++ b/src/api-messagetag.c @@ -52,15 +52,21 @@ MessageTagHandler *MessageTagHandlerAdd(Module *module, MessageTagHandlerInfo *m /* Some consistency checks to avoid a headache for module devs later on: */ if ((mreq->flags & MTAG_HANDLER_FLAGS_NO_CAP_NEEDED) && mreq->clicap_handler) { - ircd_log(LOG_ERROR, "MessageTagHandlerAdd(): .flags is set to MTAG_HANDLER_FLAGS_NO_CAP_NEEDED " - "but a .clicap_handler is passed as well. These options are mutually " - "exclusive, choose one or the other."); + unreal_log(ULOG_ERROR, "module", "MESSAGETAGHANDLERADD_API_ERROR", NULL, + "MessageTagHandlerAdd() from module $module_name: " + ".flags is set to MTAG_HANDLER_FLAGS_NO_CAP_NEEDED " + "but a .clicap_handler is passed as well. These options are mutually " + "exclusive, choose one or the other.", + log_data_string("module_name", module->header->name)); abort(); } else if (!(mreq->flags & MTAG_HANDLER_FLAGS_NO_CAP_NEEDED) && !mreq->clicap_handler) { - ircd_log(LOG_ERROR, "MessageTagHandlerAdd(): no .clicap_handler is passed. If the " - "message tag really does not require a cap then you must " - "set .flags to MTAG_HANDLER_FLAGS_NO_CAP_NEEDED"); + unreal_log(ULOG_ERROR, "module", "MESSAGETAGHANDLERADD_API_ERROR", NULL, + "MessageTagHandlerAdd() from module $module_name: " + "no .clicap_handler is passed. If the " + "message tag really does not require a cap then you must " + "set .flags to MTAG_HANDLER_FLAGS_NO_CAP_NEEDED", + log_data_string("module_name", module->header->name)); abort(); } @@ -85,7 +91,7 @@ MessageTagHandler *MessageTagHandlerAdd(Module *module, MessageTagHandlerInfo *m m->owner = module; m->flags = mreq->flags; m->is_ok = mreq->is_ok; - m->can_send = mreq->can_send; + m->should_send_to_client = mreq->should_send_to_client; m->clicap_handler = mreq->clicap_handler; /* Update reverse dependency (if any) */ @@ -141,7 +147,7 @@ void MessageTagHandlerDel(MessageTagHandler *m) m->owner = NULL; } - if (loop.ircd_rehashing) + if (loop.rehashing) m->unloaded = 1; else unload_mtag_handler_commit(m); @@ -152,8 +158,9 @@ void MessageTagHandlerDel(MessageTagHandler *m) static void unload_mtag_handler_commit(MessageTagHandler *m) { /* This is an unusual operation, I think we should log it. */ - ircd_log(LOG_ERROR, "Unloading message-tag handler for '%s'", m->name); - sendto_realops("Unloading message-tag handler for '%s'", m->name); + unreal_log(ULOG_INFO, "module", "UNLOAD_MESSAGE_TAG", NULL, + "Unloading message-tag handler for '$token'", + log_data_string("token", m->name)); /* Remove reverse dependency, if any */ if (m->clicap_handler) diff --git a/src/api-moddata.c b/src/api-moddata.c index 84be03b..1dd5035 100644 --- a/src/api-moddata.c +++ b/src/api-moddata.c @@ -65,6 +65,8 @@ ModDataInfo *ModDataAdd(Module *module, ModDataInfo req) ((req.type == MODDATATYPE_MEMBER) && (slotav >= MODDATA_MAX_MEMBER)) || ((req.type == MODDATATYPE_MEMBERSHIP) && (slotav >= MODDATA_MAX_MEMBERSHIP))) { + unreal_log(ULOG_ERROR, "module", "MOD_DATA_OUT_OF_SPACE", NULL, + "ModDataAdd: out of space!!!"); if (module) module->errorcode = MODERR_NOSPACE; return NULL; @@ -80,6 +82,8 @@ moddataadd_isok: m->serialize = req.serialize; m->unserialize = req.unserialize; m->sync = req.sync; + m->remote_write = req.remote_write; + m->self_write = req.self_write; m->owner = module; if (new_struct) @@ -272,7 +276,7 @@ void ModDataDel(ModDataInfo *md) md->owner = NULL; } - if (loop.ircd_rehashing) + if (loop.rehashing) md->unloaded = 1; else unload_moddata_commit(md); @@ -290,7 +294,7 @@ ModDataInfo *md, *md_next; } } -ModDataInfo *findmoddata_byname(char *name, ModDataType type) +ModDataInfo *findmoddata_byname(const char *name, ModDataType type) { ModDataInfo *md; @@ -313,7 +317,7 @@ int module_has_moddata(Module *mod) } /** Set ModData for client (via variable name, string value) */ -int moddata_client_set(Client *client, char *varname, char *value) +int moddata_client_set(Client *client, const char *varname, const char *value) { ModDataInfo *md; @@ -344,7 +348,7 @@ int moddata_client_set(Client *client, char *varname, char *value) } /** Get ModData for client (via variable name) */ -char *moddata_client_get(Client *client, char *varname) +const char *moddata_client_get(Client *client, const char *varname) { ModDataInfo *md; @@ -356,8 +360,21 @@ char *moddata_client_get(Client *client, char *varname) return md->serialize(&moddata_client(client, md)); /* can be NULL */ } +/** Get ModData for client (via variable name) */ +ModData *moddata_client_get_raw(Client *client, const char *varname) +{ + ModDataInfo *md; + + md = findmoddata_byname(varname, MODDATATYPE_CLIENT); + + if (!md) + return NULL; + + return &moddata_client(client, md); /* can be NULL */ +} + /** Set ModData for LocalClient (via variable name, string value) */ -int moddata_local_client_set(Client *client, char *varname, char *value) +int moddata_local_client_set(Client *client, const char *varname, const char *value) { ModDataInfo *md; @@ -391,7 +408,7 @@ int moddata_local_client_set(Client *client, char *varname, char *value) } /** Get ModData for LocalClient (via variable name) */ -char *moddata_local_client_get(Client *client, char *varname) +const char *moddata_local_client_get(Client *client, const char *varname) { ModDataInfo *md; @@ -407,7 +424,7 @@ char *moddata_local_client_get(Client *client, char *varname) } /** Set local variable moddata (via variable name, string value) */ -int moddata_local_variable_set(char *varname, char *value) +int moddata_local_variable_set(const char *varname, const char *value) { ModDataInfo *md; @@ -432,7 +449,7 @@ int moddata_local_variable_set(char *varname, char *value) } /** Set global variable moddata (via variable name, string value) */ -int moddata_global_variable_set(char *varname, char *value) +int moddata_global_variable_set(const char *varname, const char *value) { ModDataInfo *md; diff --git a/src/api-usermode.c b/src/api-usermode.c index b195c4f..0789840 100644 --- a/src/api-usermode.c +++ b/src/api-usermode.c @@ -24,16 +24,11 @@ char umodestring[UMODETABLESZ+1]; -Umode *Usermode_Table = NULL; -short Usermode_highest = 0; +/** User modes and their handlers */ +Umode *usermodes = NULL; -Snomask *Snomask_Table = NULL; -short Snomask_highest = 0; - -/* client->umodes (32 bits): 26 used, 6 free */ long UMODE_INVISIBLE = 0L; /* makes user invisible */ long UMODE_OPER = 0L; /* Operator */ -long UMODE_WALLOP = 0L; /* send wallops to them */ long UMODE_REGNICK = 0L; /* Nick set by services as registered */ long UMODE_SERVNOTICE = 0L; /* server notices such as kill */ long UMODE_HIDE = 0L; /* Hide from Nukes */ @@ -63,32 +58,13 @@ long SendUmodes; /* All umodes which are sent to other servers (global umodes) * /* Forward declarations */ int umode_hidle_allow(Client *client, int what); +static void unload_usermode_commit(Umode *m); -void umode_init(void) +void umode_init(void) { - long val = 1; - int i; - Usermode_Table = safe_alloc(sizeof(Umode) * UMODETABLESZ); - for (i = 0; i < UMODETABLESZ; i++) - { - Usermode_Table[i].mode = val; - val *= 2; - } - Usermode_highest = 0; - - Snomask_Table = safe_alloc(sizeof(Snomask) * UMODETABLESZ); - val = 1; - for (i = 0; i < UMODETABLESZ; i++) - { - Snomask_Table[i].mode = val; - val *= 2; - } - Snomask_highest = 0; - - /* Set up modes */ + /* Some built-in modes */ UmodeAdd(NULL, 'i', UMODE_GLOBAL, 0, umode_allow_all, &UMODE_INVISIBLE); UmodeAdd(NULL, 'o', UMODE_GLOBAL, 1, umode_allow_opers, &UMODE_OPER); - UmodeAdd(NULL, 'w', UMODE_GLOBAL, 0, umode_allow_all, &UMODE_WALLOP); UmodeAdd(NULL, 'r', UMODE_GLOBAL, 0, umode_allow_none, &UMODE_REGNICK); UmodeAdd(NULL, 's', UMODE_LOCAL, 0, umode_allow_all, &UMODE_SERVNOTICE); UmodeAdd(NULL, 'x', UMODE_GLOBAL, 0, umode_allow_all, &UMODE_HIDE); @@ -97,34 +73,17 @@ void umode_init(void) UmodeAdd(NULL, 'H', UMODE_GLOBAL, 1, umode_allow_opers, &UMODE_HIDEOPER); UmodeAdd(NULL, 't', UMODE_GLOBAL, 0, umode_allow_unset, &UMODE_SETHOST); UmodeAdd(NULL, 'I', UMODE_GLOBAL, 0, umode_hidle_allow, &UMODE_HIDLE); - SnomaskAdd(NULL, 'k', umode_allow_opers, &SNO_KILLS); - SnomaskAdd(NULL, 'c', umode_allow_opers, &SNO_CLIENT); - SnomaskAdd(NULL, 'f', umode_allow_opers, &SNO_FLOOD); - SnomaskAdd(NULL, 'F', umode_allow_opers, &SNO_FCLIENT); - SnomaskAdd(NULL, 'j', umode_allow_opers, &SNO_JUNK); - SnomaskAdd(NULL, 'v', umode_allow_opers, &SNO_VHOST); - SnomaskAdd(NULL, 'e', umode_allow_opers, &SNO_EYES); - SnomaskAdd(NULL, 'G', umode_allow_opers, &SNO_TKL); - SnomaskAdd(NULL, 'n', umode_allow_opers, &SNO_NICKCHANGE); - SnomaskAdd(NULL, 'N', umode_allow_opers, &SNO_FNICKCHANGE); - SnomaskAdd(NULL, 'q', umode_allow_opers, &SNO_QLINE); - SnomaskAdd(NULL, 'S', umode_allow_opers, &SNO_SPAMF); - SnomaskAdd(NULL, 's', umode_allow_opers, &SNO_SNOTICE); - SnomaskAdd(NULL, 'o', umode_allow_opers, &SNO_OPER); } void make_umodestr(void) { - int i; - char *m; + Umode *um; + char *p = umodestring; - m = umodestring; - for (i = 0; i <= Usermode_highest; i++) - { - if (Usermode_Table[i].flag) - *m++ = Usermode_Table[i].flag; - } - *m = '\0'; + for (um=usermodes; um; um = um->next) + if (um->letter) + *p++ = um->letter; + *p = '\0'; } static char previous_umodestring[256]; @@ -132,7 +91,7 @@ static char previous_umodestring[256]; void umodes_check_for_changes(void) { make_umodestr(); - safe_strdup(me.serv->features.usermodes, umodestring); + safe_strdup(me.server->features.usermodes, umodestring); if (!*previous_umodestring) { @@ -142,10 +101,10 @@ void umodes_check_for_changes(void) if (*previous_umodestring && strcmp(umodestring, previous_umodestring)) { - ircd_log(LOG_ERROR, "User modes changed at runtime: %s -> %s", - previous_umodestring, umodestring); - sendto_realops("User modes changed at runtime: %s -> %s", - previous_umodestring, umodestring); + unreal_log(ULOG_INFO, "mode", "USER_MODES_CHANGED", NULL, + "User modes changed at runtime: $old_user_modes -> $new_user_modes", + log_data_string("old_user_modes", previous_umodestring), + log_data_string("new_user_modes", umodestring)); /* Broadcast change to all (locally connected) servers */ sendto_server(NULL, 0, 0, NULL, "PROTOCTL USERMODES=%s", umodestring); } @@ -153,102 +112,134 @@ void umodes_check_for_changes(void) strlcpy(previous_umodestring, umodestring, sizeof(previous_umodestring)); } +void usermode_add_sorted(Umode *n) +{ + Umode *m; + + if (usermodes == NULL) + { + usermodes = n; + return; + } + + for (m = usermodes; m; m = m->next) + { + if (m->letter == '\0') + abort(); + if (sort_character_lowercase_before_uppercase(n->letter, m->letter)) + { + /* Insert us before */ + if (m->prev) + m->prev->next = n; + else + usermodes = n; /* new head */ + n->prev = m->prev; + + n->next = m; + m->prev = n; + return; + } + if (!m->next) + { + /* Append us at end */ + m->next = n; + n->prev = m; + return; + } + } +} + + /* UmodeAdd: * Add a usermode with character 'ch', if global is set to 1 the usermode is global * (sent to other servers) otherwise it's a local usermode */ Umode *UmodeAdd(Module *module, char ch, int global, int unset_on_deoper, int (*allowed)(Client *client, int what), long *mode) { - short i = 0; - short j = 0; - short save = -1; - while (i < UMODETABLESZ) + Umode *um; + int existing = 0; + + for (um=usermodes; um; um = um->next) { - if (!Usermode_Table[i].flag && save == -1) - save = i; - else if (Usermode_Table[i].flag == ch) + if (um->letter == ch) { - if (Usermode_Table[i].unloaded) + if (um->unloaded) { - save = i; - Usermode_Table[i].unloaded = 0; + um->unloaded = 0; + existing = 1; break; - } - else - { + } else { if (module) module->errorcode = MODERR_EXISTS; return NULL; } } - i++; } - i = save; - if (i != UMODETABLESZ) + + if (!um) { - Usermode_Table[i].flag = ch; - Usermode_Table[i].allowed = allowed; - Usermode_Table[i].unset_on_deoper = unset_on_deoper; - Debug((DEBUG_DEBUG, "UmodeAdd(%c) returning %04lx", - ch, Usermode_Table[i].mode)); - /* Update usermode table highest */ - for (j = 0; j < UMODETABLESZ; j++) - if (Usermode_Table[i].flag) - if (i > Usermode_highest) - Usermode_highest = i; - make_umodestr(); - AllUmodes |= Usermode_Table[i].mode; - if (global) - SendUmodes |= Usermode_Table[i].mode; - *mode = Usermode_Table[i].mode; - Usermode_Table[i].owner = module; - if (module) + /* Not found, create */ + long l, found = 0; + for (l = 1; l < LONG_MAX/2; l *= 2) { - ModuleObject *umodeobj = safe_alloc(sizeof(ModuleObject)); - umodeobj->object.umode = &(Usermode_Table[i]); - umodeobj->type = MOBJ_UMODE; - AddListItem(umodeobj, module->objects); - module->errorcode = MODERR_NOERROR; + found = 0; + for (um=usermodes; um; um = um->next) + { + if (um->mode == l) + { + found = 1; + break; + } + } + if (!found) + break; } - return &(Usermode_Table[i]); + /* If 'found' is still true, then we are out of space */ + if (found) + { + unreal_log(ULOG_ERROR, "module", "USER_MODE_OUT_OF_SPACE", NULL, + "UmodeAdd: out of space!!!"); + if (module) + module->errorcode = MODERR_NOSPACE; + return NULL; + } + um = safe_alloc(sizeof(Umode)); + um->letter = ch; + um->mode = l; + usermode_add_sorted(um); } - else + + um->letter = ch; + um->allowed = allowed; + um->unset_on_deoper = unset_on_deoper; + make_umodestr(); + AllUmodes |= um->mode; + if (global) + SendUmodes |= um->mode; + *mode = um->mode; + um->owner = module; + if (module) { - Debug((DEBUG_DEBUG, "UmodeAdd failed, no space")); - if (module) - module->errorcode = MODERR_NOSPACE; - return NULL; + ModuleObject *umodeobj = safe_alloc(sizeof(ModuleObject)); + umodeobj->object.umode = um; + umodeobj->type = MOBJ_UMODE; + AddListItem(umodeobj, module->objects); + module->errorcode = MODERR_NOERROR; } + return um; } void UmodeDel(Umode *umode) { - if (loop.ircd_rehashing) - umode->unloaded = 1; - else + /* Always free the module object */ + if (umode->owner) { - Client *client; - list_for_each_entry(client, &client_list, client_node) - { - long oldumode = 0; - if (!IsUser(client)) - continue; - oldumode = client->umodes; - client->umodes &= ~umode->mode; - if (MyUser(client)) - send_umode_out(client, 1, oldumode); - } - umode->flag = '\0'; - AllUmodes &= ~(umode->mode); - SendUmodes &= ~(umode->mode); - make_umodestr(); - } - - if (umode->owner) { ModuleObject *umodeobj; - for (umodeobj = umode->owner->objects; umodeobj; umodeobj = umodeobj->next) { - if (umodeobj->type == MOBJ_UMODE && umodeobj->object.umode == umode) { + for (umodeobj = umode->owner->objects; umodeobj; umodeobj = umodeobj->next) + { + if (umodeobj->type == MOBJ_UMODE && umodeobj->object.umode == umode) + { DelListItem(umodeobj, umode->owner->objects); safe_free(umodeobj); break; @@ -256,100 +247,13 @@ void UmodeDel(Umode *umode) } umode->owner = NULL; } - return; -} -Snomask *SnomaskAdd(Module *module, char ch, int (*allowed)(Client *client, int what), long *mode) -{ - short i = 0; - short j = 0; - short save = -1; - while (i < UMODETABLESZ) - { - if (!Snomask_Table[i].flag && save == -1) - save = i; - else if (Snomask_Table[i].flag == ch) - { - if (Snomask_Table[i].unloaded) - { - save = i; - Snomask_Table[i].unloaded = 0; - break; - } - else - { - if (module) - module->errorcode = MODERR_EXISTS; - return NULL; - } - } - i++; - } - i = save; - if (i != UMODETABLESZ) - { - Snomask_Table[i].flag = ch; - Snomask_Table[i].allowed = allowed; - /* Update usermode table highest */ - for (j = 0; j < UMODETABLESZ; j++) - if (Snomask_Table[i].flag) - if (i > Snomask_highest) - Snomask_highest = i; - *mode = Snomask_Table[i].mode; - Snomask_Table[i].owner = module; - if (module) - { - ModuleObject *snoobj = safe_alloc(sizeof(ModuleObject)); - snoobj->object.snomask = &(Snomask_Table[i]); - snoobj->type = MOBJ_SNOMASK; - AddListItem(snoobj, module->objects); - module->errorcode = MODERR_NOERROR; - } - return &(Snomask_Table[i]); - } + /* Whether we can actually (already) free the Umode depends... */ + + if (loop.rehashing) + umode->unloaded = 1; else - { - Debug((DEBUG_DEBUG, "SnomaskAdd failed, no space")); - *mode = 0; - if (module) - module->errorcode = MODERR_NOSPACE; - return NULL; - } -} - -void SnomaskDel(Snomask *sno) -{ - if (loop.ircd_rehashing) - sno->unloaded = 1; - else - { - Client *client; - - list_for_each_entry(client, &lclient_list, lclient_node) - { - long oldsno; - if (!client || !IsUser(client)) - continue; - oldsno = client->user->snomask; - client->user->snomask &= ~sno->mode; - if (oldsno != client->user->snomask) - sendnumeric(client, RPL_SNOMASK, get_snomask_string_raw(client->user->snomask)); - } - - sno->flag = '\0'; - } - if (sno->owner) { - ModuleObject *snoobj; - for (snoobj = sno->owner->objects; snoobj; snoobj = snoobj->next) { - if (snoobj->type == MOBJ_SNOMASK && snoobj->object.snomask == sno) { - DelListItem(snoobj, sno->owner->objects); - safe_free(snoobj); - break; - } - } - sno->owner = NULL; - } - return; + unload_usermode_commit(umode); } int umode_allow_all(Client *client, int what) @@ -392,68 +296,42 @@ int umode_hidle_allow(Client *client, int what) return 0; /* if set::hide-idle-time is 'never' or 'always' then +I makes no sense */ } -void unload_all_unused_umodes(void) +static void unload_usermode_commit(Umode *um) { - long removed_umode = 0; - int i; Client *client; - for (i = 0; i < UMODETABLESZ; i++) - { - if (Usermode_Table[i].unloaded) - removed_umode |= Usermode_Table[i].mode; - } - if (!removed_umode) /* Nothing was unloaded */ + long removed_umode; + + if (!um) return; + + removed_umode = um->mode; + + /* First send the -mode regarding all users */ list_for_each_entry(client, &lclient_list, lclient_node) { - long oldumode = 0; - if (!IsUser(client)) - continue; - oldumode = client->umodes; - client->umodes &= ~(removed_umode); - if (MyUser(client)) - send_umode_out(client, 1, oldumode); - } - for (i = 0; i < UMODETABLESZ; i++) - { - if (Usermode_Table[i].unloaded) + if (MyUser(client) && (client->umodes & removed_umode)) { - AllUmodes &= ~(Usermode_Table[i].mode); - SendUmodes &= ~(Usermode_Table[i].mode); - Usermode_Table[i].flag = '\0'; - Usermode_Table[i].unloaded = 0; + long oldumode = client->umodes; + client->umodes &= ~(removed_umode); + send_umode_out(client, 1, oldumode); } } + + /* Then unload the mode */ + DelListItem(um, usermodes); + safe_free(um); make_umodestr(); } -void unload_all_unused_snomasks(void) +void unload_all_unused_umodes(void) { - Client *client; - long removed_sno = 0; - int i; + Umode *um, *um_next; - for (i = 0; i < UMODETABLESZ; i++) + for (um=usermodes; um; um = um_next) { - if (Snomask_Table[i].unloaded) - { - removed_sno |= Snomask_Table[i].mode; - Snomask_Table[i].flag = '\0'; - Snomask_Table[i].unloaded = 0; - } - } - if (!removed_sno) /* Nothing was unloaded */ - return; - - list_for_each_entry(client, &lclient_list, lclient_node) - { - long oldsno; - if (!client || !IsUser(client)) - continue; - oldsno = client->user->snomask; - client->user->snomask &= ~(removed_sno); - if (oldsno != client->user->snomask) - sendnumeric(client, RPL_SNOMASK, get_snomask_string_raw(client->user->snomask)); + um_next = um->next; + if (um->letter && um->unloaded) + unload_usermode_commit(um); } } @@ -463,25 +341,24 @@ void unload_all_unused_snomasks(void) * This used to be a bit more complex but nowadays we just erase all * snomasks since all of them are IRCOp-only. Easy. */ -void remove_oper_snomasks(Client *client) +void remove_all_snomasks(Client *client) { - client->user->snomask = 0; + safe_free(client->user->snomask); + client->umodes &= ~UMODE_SERVNOTICE; } /* * This function removes any oper-only user modes from the user. - * You may also want to call remove_oper_snomasks(), see above. + * You may also want to call remove_all_snomasks(), see above. */ void remove_oper_modes(Client *client) { -int i; + Umode *um; - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].flag) - continue; - if (Usermode_Table[i].unset_on_deoper) - client->umodes &= ~Usermode_Table[i].mode; + if (um->unset_on_deoper) + client->umodes &= ~um->mode; } /* Bit of a hack, since this is a dynamic permission umode */ @@ -493,7 +370,7 @@ void remove_oper_privileges(Client *client, int broadcast_mode_change) { long oldumodes = client->umodes; remove_oper_modes(client); - remove_oper_snomasks(client); + remove_all_snomasks(client); if (broadcast_mode_change && (client->umodes != oldumodes)) send_umode_out(client, 1, oldumodes); if (MyUser(client)) /* only do if it's our client, remote servers will send a SWHOIS cmd */ @@ -501,15 +378,14 @@ void remove_oper_privileges(Client *client, int broadcast_mode_change) } /** Return long integer mode for a user mode character (eg: 'x' -> 0x10) */ -long find_user_mode(char flag) +long find_user_mode(char letter) { - int i; + Umode *um; + + for (um = usermodes; um; um = um->next) + if ((um->letter == letter) && !um->unloaded) + return um->mode; - for (i = 0; i < UMODETABLESZ; i++) - { - if ((Usermode_Table[i].flag == flag) && !(Usermode_Table[i].unloaded)) - return Usermode_Table[i].mode; - } return 0; } diff --git a/src/auth.c b/src/auth.c index 39403a5..4cf28e0 100644 --- a/src/auth.c +++ b/src/auth.c @@ -46,10 +46,10 @@ AuthTypeList MODVAR AuthTypeLists[] = { }; /* Helper function for Auth_AutoDetectHashType() */ -static int parsepass(char *str, char **salt, char **hash) +static int parsepass(const char *str, char **salt, char **hash) { static char saltbuf[512], hashbuf[512]; - char *p; + const char *p; int max; /* Syntax: $$ */ @@ -72,7 +72,7 @@ static int parsepass(char *str, char **salt, char **hash) /** Auto detect hash type for input hash 'hash'. * Will fallback to AUTHTYPE_PLAINTEXT when not found (or invalid). */ -int Auth_AutoDetectHashType(char *hash) +int Auth_AutoDetectHashType(const char *hash) { static char hashbuf[256]; char *saltstr, *hashstr; @@ -80,12 +80,12 @@ int Auth_AutoDetectHashType(char *hash) if (!strchr(hash, '$')) { - /* SHA256 SSL fingerprint perhaps? + /* SHA256 certificate fingerprint perhaps? * These are exactly 64 bytes (00112233..etc..) or 95 bytes (00:11:22:33:etc) in size. */ if ((strlen(hash) == 64) || (strlen(hash) == 95)) { - char *p; + const char *p; char *hexchars = "0123456789abcdefABCDEF"; for (p = hash; *p; p++) if ((*p != ':') && !strchr(hexchars, *p)) @@ -96,7 +96,7 @@ int Auth_AutoDetectHashType(char *hash) if (strlen(hash) == 44) { - char *p; + const char *p; char *b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; for (p = hash; *p; p++) if (!strchr(b64chars, *p)) @@ -134,7 +134,7 @@ int Auth_AutoDetectHashType(char *hash) * than trying to determine the type on the 'hash' parameter. * Or leave NULL, then we use hash autodetection. */ -AuthenticationType Auth_FindType(char *hash, char *type) +AuthenticationType Auth_FindType(const char *hash, const char *type) { if (type) { @@ -163,25 +163,25 @@ int Auth_CheckError(ConfigEntry *ce) AuthenticationType type = AUTHTYPE_PLAINTEXT; X509 *x509_filecert = NULL; FILE *x509_f = NULL; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: authentication module failure: missing parameter", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return -1; } - if (ce->ce_entries && ce->ce_entries->ce_next) + if (ce->items && ce->items->next) { config_error("%s:%i: you may not have multiple authentication methods", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return -1; } - type = Auth_FindType(ce->ce_vardata, ce->ce_entries ? ce->ce_entries->ce_varname : NULL); + type = Auth_FindType(ce->value, ce->items ? ce->items->name : NULL); if (type == -1) { config_error("%s:%i: authentication module failure: %s is not an implemented/enabled authentication method", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_entries->ce_varname); + ce->file->filename, ce->line_number, + ce->items->name); return -1; } @@ -189,19 +189,19 @@ int Auth_CheckError(ConfigEntry *ce) { case AUTHTYPE_UNIXCRYPT: /* If our data is like 1 or none, we just let em through .. */ - if (strlen(ce->ce_vardata) < 2) + if (strlen(ce->value) < 2) { config_error("%s:%i: authentication module failure: AUTHTYPE_UNIXCRYPT: no salt (crypt strings will always be >2 in length)", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return -1; } break; case AUTHTYPE_TLS_CLIENTCERT: - convert_to_absolute_path(&ce->ce_vardata, CONFDIR); - if (!(x509_f = fopen(ce->ce_vardata, "r"))) + convert_to_absolute_path(&ce->value, CONFDIR); + if (!(x509_f = fopen(ce->value, "r"))) { config_error("%s:%i: authentication module failure: AUTHTYPE_TLS_CLIENTCERT: error opening file %s: %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata, strerror(errno)); + ce->file->filename, ce->line_number, ce->value, strerror(errno)); return -1; } x509_filecert = PEM_read_X509(x509_f, NULL, NULL, NULL); @@ -209,7 +209,7 @@ int Auth_CheckError(ConfigEntry *ce) if (!x509_filecert) { config_error("%s:%i: authentication module failure: AUTHTYPE_TLS_CLIENTCERT: PEM_read_X509 errored in file %s (format error?)", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); + ce->file->filename, ce->line_number, ce->value); return -1; } X509_free(x509_filecert); @@ -226,19 +226,19 @@ int Auth_CheckError(ConfigEntry *ce) * with normally at least 5000 rounds (unless deliberately weakened * by the user). */ - if ((type == AUTHTYPE_UNIXCRYPT) && strncmp(ce->ce_vardata, "$5", 2) && - strncmp(ce->ce_vardata, "$6", 2) && !strstr(ce->ce_vardata, "$rounds")) + if ((type == AUTHTYPE_UNIXCRYPT) && strncmp(ce->value, "$5", 2) && + strncmp(ce->value, "$6", 2) && !strstr(ce->value, "$rounds")) { config_warn("%s:%i: Using simple crypt for authentication is not recommended. " "Consider using the more secure auth-type 'argon2' instead. " "See https://www.unrealircd.org/docs/Authentication_types for the complete list.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); /* do not return, not an error. */ } - if ((type == AUTHTYPE_PLAINTEXT) && (strlen(ce->ce_vardata) > PASSWDLEN)) + if ((type == AUTHTYPE_PLAINTEXT) && (strlen(ce->value) > PASSWDLEN)) { config_error("%s:%i: passwords length may not exceed %d", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, PASSWDLEN); + ce->file->filename, ce->line_number, PASSWDLEN); return -1; } return 1; @@ -252,12 +252,12 @@ AuthConfig *AuthBlockToAuthConfig(ConfigEntry *ce) AuthenticationType type = AUTHTYPE_PLAINTEXT; AuthConfig *as = NULL; - type = Auth_FindType(ce->ce_vardata, ce->ce_entries ? ce->ce_entries->ce_varname : NULL); + type = Auth_FindType(ce->value, ce->items ? ce->items->name : NULL); if (type == AUTHTYPE_INVALID) type = AUTHTYPE_PLAINTEXT; as = safe_alloc(sizeof(AuthConfig)); - safe_strdup(as->data, ce->ce_vardata); + safe_strdup(as->data, ce->value); as->type = type; return as; } @@ -279,7 +279,7 @@ void Auth_FreeAuthConfig(AuthConfig *as) #define RAWSALTLEN 6 #define REALSALTLEN 12 -static int authcheck_argon2(Client *client, AuthConfig *as, char *para) +static int authcheck_argon2(Client *client, AuthConfig *as, const char *para) { argon2_type hashtype; @@ -304,7 +304,7 @@ static int authcheck_argon2(Client *client, AuthConfig *as, char *para) return 0; /* NO MATCH or error */ } -static int authcheck_bcrypt(Client *client, AuthConfig *as, char *para) +static int authcheck_bcrypt(Client *client, AuthConfig *as, const char *para) { char data[512]; /* NOTE: only 64 required by BF_crypt() */ char *str; @@ -324,7 +324,7 @@ static int authcheck_bcrypt(Client *client, AuthConfig *as, char *para) return 0; /* NO MATCH */ } -static int authcheck_tls_clientcert(Client *client, AuthConfig *as, char *para) +static int authcheck_tls_clientcert(Client *client, AuthConfig *as, const char *para) { X509 *x509_clientcert = NULL; X509 *x509_filecert = NULL; @@ -358,11 +358,11 @@ static int authcheck_tls_clientcert(Client *client, AuthConfig *as, char *para) return 1; } -static int authcheck_tls_clientcert_fingerprint(Client *client, AuthConfig *as, char *para) +static int authcheck_tls_clientcert_fingerprint(Client *client, AuthConfig *as, const char *para) { int i, k; char hexcolon[EVP_MAX_MD_SIZE * 3 + 1]; - char *fp; + const char *fp; if (!client->local->ssl) return 0; @@ -389,12 +389,12 @@ static int authcheck_tls_clientcert_fingerprint(Client *client, AuthConfig *as, return 1; } -static int authcheck_spkifp(Client *client, AuthConfig *as, char *para) +static int authcheck_spkifp(Client *client, AuthConfig *as, const char *para) { - char *fp = spki_fingerprint(client); + const char *fp = spki_fingerprint(client); if (!fp) - return 0; /* auth failed: not SSL (or other failure) */ + return 0; /* auth failed: not TLS or some other failure */ if (strcasecmp(as->data, fp)) return 0; /* auth failed: mismatch */ @@ -420,7 +420,7 @@ static int authcheck_spkifp(Client *client, AuthConfig *as, char *para) * - The return value was different in versions before UnrealIRCd 5.0.0! * - In older versions a NULL 'as' was treated as an allow, now it's deny. */ -int Auth_Check(Client *client, AuthConfig *as, char *para) +int Auth_Check(Client *client, AuthConfig *as, const char *para) { extern char *crypt(); char *res; @@ -435,8 +435,9 @@ int Auth_Check(Client *client, AuthConfig *as, char *para) return 0; if (!strcmp(as->data, "changemeplease") && !strcmp(para, as->data)) { - sendto_realops("Rejecting default password 'changemeplease'. " - "Please change the password in the configuration file."); + unreal_log(ULOG_INFO, "auth", "AUTH_REJECT_DEFAULT_PASSWORD", client, + "Rejecting default password 'changemeplease'. " + "Please change the password in the configuration file."); return 0; } /* plain text compare */ @@ -479,7 +480,7 @@ int Auth_Check(Client *client, AuthConfig *as, char *para) #define UNREALIRCD_ARGON2_DEFAULT_HASH_LENGTH 32 #define UNREALIRCD_ARGON2_DEFAULT_SALT_LENGTH (128/8) -static char *mkpass_argon2(char *para) +static char *mkpass_argon2(const char *para) { static char buf[512]; char salt[UNREALIRCD_ARGON2_DEFAULT_SALT_LENGTH]; @@ -511,7 +512,7 @@ static char *mkpass_argon2(char *para) return buf; } -static char *mkpass_bcrypt(char *para) +static char *mkpass_bcrypt(const char *para) { static char buf[128]; char data[512]; /* NOTE: only 64 required by BF_crypt() */ @@ -547,7 +548,7 @@ static char *mkpass_bcrypt(char *para) * @param text The password in plaintext. * @returns The hashed password. */ -char *Auth_Hash(AuthenticationType type, char *text) +const char *Auth_Hash(AuthenticationType type, const char *text) { switch (type) { diff --git a/src/buildmod b/src/buildmod index 6fdfe22..da21717 100755 --- a/src/buildmod +++ b/src/buildmod @@ -1,4 +1,5 @@ #!/bin/sh +MAKE="$1" echo "" echo "Checking for updates for third party modules..." # We can't use the "unrealircd" script, since possibly the ircd @@ -13,7 +14,7 @@ if [ "$x" != "*.c" ]; then x="`echo $x|sed 's/\.c//'`" if [ ! -f $x.so -o $x.c -nt $x.so ]; then echo "Building 3rd party module $x..." - make custommodule MODULEFILE=$x || (echo "*****"; echo "Building 3rd party module $x failed."; echo "Contact the module author of the $x module (not the UnrealIRCd team), or simply delete the $PWD/$x.c file"; echo "*****"; exit 1) + $MAKE custommodule MODULEFILE=$x || (echo "*****"; echo "Building 3rd party module $x failed."; echo "Contact the module author of the $x module (not the UnrealIRCd team), or simply delete the $PWD/$x.c file"; echo "*****"; exit 1) fi fi done diff --git a/src/channel.c b/src/channel.c index a15fddf..a465f20 100644 --- a/src/channel.c +++ b/src/channel.c @@ -40,31 +40,15 @@ long sajoinmode = 0; */ Channel *channels = NULL; -/* some buffers for rebuilding channel/nick lists with comma's */ +/* A buffer for rebuilding channel/nick lists with comma's */ static char buf[BUFSIZE]; -/** Mode buffer (eg: "+sntkl") */ -MODVAR char modebuf[BUFSIZE]; -/** Parameter buffer (eg: "key 123") */ -MODVAR char parabuf[BUFSIZE]; + +static mp_pool_t *channel_pool = NULL; /** This describes the letters, modes and options for core channel modes. * These are +ntmispklr and also the list modes +vhoaq and +beI. */ CoreChannelModeTable corechannelmodetable[] = { - {MODE_LIMIT, 'l', 1, 1}, - {MODE_VOICE, 'v', 1, 1}, - {MODE_HALFOP, 'h', 0, 1}, - {MODE_CHANOP, 'o', 0, 1}, - {MODE_PRIVATE, 'p', 0, 0}, - {MODE_SECRET, 's', 0, 0}, - {MODE_MODERATED, 'm', 1, 0}, - {MODE_NOPRIVMSGS, 'n', 1, 0}, - {MODE_TOPICLIMIT, 't', 1, 0}, - {MODE_INVITEONLY, 'i', 1, 0}, - {MODE_KEY, 'k', 1, 1}, - {MODE_RGSTR, 'r', 0, 0}, - {MODE_CHANADMIN, 'a', 0, 1}, - {MODE_CHANOWNER, 'q', 0, 1}, {MODE_BAN, 'b', 1, 1}, {MODE_EXCEPT, 'e', 1, 1}, /* exception ban */ {MODE_INVEX, 'I', 1, 1}, /* invite-only exception */ @@ -74,14 +58,8 @@ CoreChannelModeTable corechannelmodetable[] = { /** The advertised supported channel modes in the 004 numeric */ char cmodestring[512]; -/* Some forward declarations */ -char *clean_ban_mask(char *, int, Client *); -void channel_modes(Client *client, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size, Channel *channel); -int sub1_from_channel(Channel *); -void del_invite(Client *, Channel *); - /** Returns 1 if the IRCOp can override or is a remote connection */ -inline int op_can_override(char *acl, Client *client,Channel *channel,void* extra) +inline int op_can_override(const char *acl, Client *client, Channel *channel, void* extra) { #ifndef NO_OPEROVERRIDE if (MyUser(client) && !(ValidatePermissionsForPath(acl,client,NULL,channel,extra))) @@ -106,17 +84,6 @@ int Halfop_mode(long mode) return TRUE; } - -/** Returns the length (entry count) of a +beI list */ -static int list_length(Link *lp) -{ - int count = 0; - - for (; lp; lp = lp->next) - count++; - return count; -} - /** Find client in a Member linked list (eg: channel->members) */ Member *find_member_link(Member *lp, Client *ptr) { @@ -156,8 +123,6 @@ static Member *make_member(void) for (i = 1; i <= (4072/sizeof(Member)); ++i) { lp = safe_alloc(sizeof(Member)); - lp->client = NULL; - lp->flags = 0; lp->next = freemember; freemember = lp; } @@ -176,8 +141,6 @@ static void free_member(Member *lp) moddata_free_member(lp); memset(lp, 0, sizeof(Member)); lp->next = freemember; - lp->client = NULL; - lp->flags = 0; freemember = lp; } @@ -228,7 +191,7 @@ static void free_membership(Membership *m) * only after searching through the nick history. * @returns The client (if found) or NULL (if not found). */ -Client *find_chasing(Client *client, char *user, int *chasing) +Client *find_chasing(Client *client, const char *user, int *chasing) { Client *who = find_client(user, NULL); @@ -254,9 +217,10 @@ Client *find_chasing(Client *client, char *user, int *chasing) } /** Return 1 if the bans are identical, taking into account special handling for extbans */ -int identical_ban(char *one, char *two) +int identical_ban(const char *one, const char *two) { - if (is_extended_ban(one)) +#if 0 + if (is_extended_ban(one) && is_extended_ban(two)) { /* compare the first 3 characters case-sensitive and if identical then compare * the remainder of the string case-insensitive. @@ -267,6 +231,14 @@ int identical_ban(char *one, char *two) if (!mycmp(one, two)) return 1; } +#else + /* Actually I think we can live with this nowadays. + * We are pushing towards named extbans, and all the + * letter extbans that could clash no longer exist. + */ + if (!mycmp(one, two)) + return 1; +#endif return 0; } @@ -274,14 +246,14 @@ int identical_ban(char *one, char *two) * the specified channel. (Extended version with * set by nick and set on timestamp) */ -int add_listmode_ex(Ban **list, Client *client, Channel *channel, char *banid, char *setby, time_t seton) +int add_listmode_ex(Ban **list, Client *client, Channel *channel, const char *banid, const char *setby, time_t seton) { Ban *ban; int cnt = 0, len; int do_not_add = 0; - if (MyUser(client)) - collapse(banid); + //if (MyUser(client)) + // collapse(banid); len = strlen(banid); if (!*list && ((len > MAXBANLENGTH) || (MAXBANS < 1))) @@ -289,7 +261,7 @@ int add_listmode_ex(Ban **list, Client *client, Channel *channel, char *banid, c if (MyUser(client)) { /* Only send the error to local clients */ - sendnumeric(client, ERR_BANLISTFULL, channel->chname, banid); + sendnumeric(client, ERR_BANLISTFULL, channel->name, banid); } do_not_add = 1; } @@ -318,7 +290,7 @@ int add_listmode_ex(Ban **list, Client *client, Channel *channel, char *banid, c if (MyUser(client)) { /* Only send the error to local clients */ - sendnumeric(client, ERR_BANLISTFULL, channel->chname, banid); + sendnumeric(client, ERR_BANLISTFULL, channel->name, banid); } return -1; } @@ -345,20 +317,20 @@ int add_listmode_ex(Ban **list, Client *client, Channel *channel, char *banid, c /** Add a listmode (+beI) with the specified banid to * the specified channel. (Simplified version) */ -int add_listmode(Ban **list, Client *client, Channel *channel, char *banid) +int add_listmode(Ban **list, Client *client, Channel *channel, const char *banid) { char *setby = client->name; char nuhbuf[NICKLEN+USERLEN+HOSTLEN+4]; if (IsUser(client) && (iConf.ban_setter == SETTER_NICK_USER_HOST)) - setby = make_nick_user_host_r(nuhbuf, client->name, client->user->username, GetHost(client)); + setby = make_nick_user_host_r(nuhbuf, sizeof(nuhbuf), client->name, client->user->username, GetHost(client)); return add_listmode_ex(list, client, channel, banid, setby, TStime()); } /** Delete a listmode (+beI) from a channel that matches the specified banid. */ -int del_listmode(Ban **list, Channel *channel, char *banid) +int del_listmode(Ban **list, Channel *channel, const char *banid) { Ban **ban; Ban *tmp; @@ -390,43 +362,37 @@ int del_listmode(Ban **list, Channel *channel, char *banid) * @returns A pointer to the ban struct if banned, otherwise NULL. * @comments Simple wrapper for is_banned_with_nick() */ -inline Ban *is_banned(Client *client, Channel *channel, int type, char **msg, char **errmsg) +inline Ban *is_banned(Client *client, Channel *channel, int type, const char **msg, const char **errmsg) { return is_banned_with_nick(client, channel, type, NULL, msg, errmsg); } /** ban_check_mask - Checks if the user matches the specified n!u@h mask -or- run an extended ban. - * @param client Client to check (can be remote client) - * @param channel Channel to check - * @param banstr Mask string to check user - * @param type Type of ban to check for (BANCHK_*) - * @param msg Message, only for some BANCHK_* types, otherwise NULL. - * @param errmsg Error message, could be NULL - * @param no_extbans 0 to check extbans, nonzero to disable extban checking. - * @returns Nonzero if the mask/extban succeeds. Zero if it doesn't. - * @comments This is basically extracting the mask and extban check from is_banned_with_nick, but with being a bit more strict in what an extban is. - * Strange things could happen if this is called outside standard ban checking. + * This is basically extracting the mask and extban check from is_banned_with_nick, + * but with being a bit more strict in what an extban is. + * Strange things could happen if this is called outside standard ban checking. + * @param b Ban context, see BanContext + * @returns Nonzero if the mask/extban succeeds. Zero if it doesn't. */ -inline int ban_check_mask(Client *client, Channel *channel, char *banstr, int type, char **msg, char **errmsg, int no_extbans) +inline int ban_check_mask(BanContext *b) { - Extban *extban = NULL; - if (!no_extbans && is_extended_ban(banstr)) + if (!b->no_extbans && is_extended_ban(b->banstr)) { /* Is an extended ban. */ - extban = findmod_by_bantype(banstr[1]); - if (!extban) + const char *nextbanstr; + Extban *extban = findmod_by_bantype(b->banstr, &nextbanstr); + if (!extban || !(extban->is_banned_events & b->ban_check_types)) { return 0; - } - else - { - return extban->is_banned(client, channel, banstr, type, msg, errmsg); + } else { + b->banstr = nextbanstr; + return extban->is_banned(b); } } else { /* Is a n!u@h mask. */ - return match_user(banstr, client, MATCH_CHECK_ALL); + return match_user(b->banstr, b->client, MATCH_CHECK_ALL); } } @@ -438,10 +404,11 @@ inline int ban_check_mask(Client *client, Channel *channel, char *banstr, int ty * @param msg Message, only for some BANCHK_* types, otherwise NULL * @returns A pointer to the ban struct if banned, otherwise NULL. */ -Ban *is_banned_with_nick(Client *client, Channel *channel, int type, char *nick, char **msg, char **errmsg) +Ban *is_banned_with_nick(Client *client, Channel *channel, int type, const char *nick, const char **msg, const char **errmsg) { Ban *ban, *ex; char savednick[NICKLEN+1]; + BanContext *b = safe_alloc(sizeof(BanContext)); /* It's not really doable to pass 'nick' to all the ban layers, * including extbans (with stacking) and so on. Or at least not @@ -460,13 +427,20 @@ Ban *is_banned_with_nick(Client *client, Channel *channel, int type, char *nick, strlcpy(client->name, nick, sizeof(client->name)); } + b->client = client; + b->channel = channel; + b->ban_check_types = type; + if (msg) + b->msg = *msg; + /* We check +b first, if a +b is found we then see if there is a +e. * If a +e was found we return NULL, if not, we return the ban. */ for (ban = channel->banlist; ban; ban = ban->next) { - if (ban_check_mask(client, channel, ban->banstr, type, msg, errmsg, 0)) + b->banstr = ban->banstr; + if (ban_check_mask(b)) break; } @@ -475,7 +449,8 @@ Ban *is_banned_with_nick(Client *client, Channel *channel, int type, char *nick, /* Ban found, now check for +e */ for (ex = channel->exlist; ex; ex = ex->next) { - if (ban_check_mask(client, channel, ex->banstr, type, msg, errmsg, 0)) + b->banstr = ex->banstr; + if (ban_check_mask(b)) { /* except matched */ ban = NULL; @@ -491,6 +466,13 @@ Ban *is_banned_with_nick(Client *client, Channel *channel, int type, char *nick, strlcpy(client->name, savednick, sizeof(client->name)); } + /* OUT: */ + if (msg) + *msg = b->msg; + if (errmsg) + *errmsg = b->error_msg; + + safe_free(b); return ban; } @@ -499,37 +481,43 @@ Ban *is_banned_with_nick(Client *client, Channel *channel, int type, char *nick, * and also the Membership struct to the client->user->channel linked list. * @note This does NOT send the JOIN, it only does the linked list stuff. */ -void add_user_to_channel(Channel *channel, Client *who, int flags) +void add_user_to_channel(Channel *channel, Client *client, const char *modes) { Member *m; Membership *mb; + const char *p; - if (who->user) - { - m = make_member(); - m->client = who; - m->flags = flags; - m->next = channel->members; - channel->members = m; - channel->users++; + if (!client->user) + return; - mb = make_membership(); - mb->channel = channel; - mb->next = who->user->channel; - mb->flags = flags; - who->user->channel = mb; - who->user->joined++; - RunHook2(HOOKTYPE_JOIN_DATA, who, channel); - } + m = make_member(); + m->client = client; + m->next = channel->members; + channel->members = m; + channel->users++; + + mb = make_membership(); + mb->channel = channel; + mb->next = client->user->channel; + client->user->channel = mb; + client->user->joined++; + + for (p = modes; *p; p++) + add_member_mode_fast(m, mb, *p); + + RunHook(HOOKTYPE_JOIN_DATA, client, channel); } /** Remove the user from the channel. - * This doesn't send any PART etc. It does the free'ing of - * membership etc. It will also DESTROY the channel if the - * user was the last user (and the channel is not +P), - * via sub1_from_channel(), that is. + * This frees the memberships, decreases the user counts, + * destroys the channel if needed, etc. + * This does not send any PART/KICK/..! + * @param client The client that is removed from the channel + * @param channel The channel + * @param dont_log Set to 1 if it should not be logged as a part, + * for example if you are already logging it as a kick. */ -int remove_user_from_channel(Client *client, Channel *channel) +int remove_user_from_channel(Client *client, Channel *channel, int dont_log) { Member **m; Member *m2; @@ -561,56 +549,46 @@ int remove_user_from_channel(Client *client, Channel *channel) /* Update user record to reflect 1 less joined */ client->user->joined--; + if (!dont_log) + { + if (MyUser(client)) + { + unreal_log(ULOG_INFO, "part", "LOCAL_CLIENT_PART", client, + "User $client left $channel", + log_data_channel("channel", channel)); + } else { + unreal_log(ULOG_INFO, "part", "REMOTE_CLIENT_PART", client, + "User $client left $channel", + log_data_channel("channel", channel)); + } + } + /* Now sub1_from_channel() will deal with the channel record * and destroy the channel if needed. */ return sub1_from_channel(channel); } -/** Get channel access flags (CHFL_*) for a client in a channel. - * @param client The client - * @param channel The channel - * @returns One or more of CHFL_* (eg: CHFL_CHANOP|CHFL_CHANADMIN) - * @note If the user is not found, then 0 is returned. - * If the user has no access rights, then 0 is returned as well. - */ -long get_access(Client *client, Channel *channel) -{ - Membership *lp; - if (channel && IsUser(client)) - if ((lp = find_membership_link(client->user->channel, channel))) - return lp->flags; - return 0; -} - /** Returns 1 if channel has this channel mode set and 0 if not */ int has_channel_mode(Channel *channel, char mode) { - CoreChannelModeTable *tab = &corechannelmodetable[0]; - int i; + Cmode *cm; - /* Extended channel modes */ - for (i=0; i <= Channelmode_highest; i++) - { - if ((Channelmode_Table[i].flag == mode) && (channel->mode.extmode & Channelmode_Table[i].mode)) + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->letter == mode) && (channel->mode.mode & cm->mode)) return 1; - } - /* Built-in channel modes */ - while (tab->mode != 0x0) - { - if ((channel->mode.mode & tab->mode) && (tab->flag == mode)) + return 0; /* Not found */ +} + +/** Returns 1 if channel has this mode is set and 0 if not */ +int has_channel_mode_raw(Cmode_t m, char mode) +{ + Cmode *cm; + + for (cm=channelmodes; cm; cm = cm->next) + if ((cm->letter == mode) && (m & cm->mode)) return 1; - tab++; - } - - /* Special handling for +l (needed??) */ - if (channel->mode.limit && (mode == 'l')) - return 1; - - /* Special handling for +k (needed??) */ - if (channel->mode.key[0] && (mode == 'k')) - return 1; return 0; /* Not found */ } @@ -618,126 +596,88 @@ int has_channel_mode(Channel *channel, char mode) /** Get the extended channel mode 'bit' value (eg: 0x20) by character (eg: 'Z') */ Cmode_t get_extmode_bitbychar(char m) { - int extm; - for (extm=0; extm <= Channelmode_highest; extm++) - { - if (Channelmode_Table[extm].flag == m) - return Channelmode_Table[extm].mode; - } + Cmode *cm; + + for (cm=channelmodes; cm; cm = cm->next) + if (cm->letter == m) + return cm->mode; + return 0; } -/** Get the extended channel mode character (eg: 'Z') by the 'bit' value (eg: 0x20) */ -long get_mode_bitbychar(char m) -{ - CoreChannelModeTable *tab = &corechannelmodetable[0]; - - while(tab->mode != 0x0) - { - if (tab->flag == m) - return tab->mode; - tab++;; - } - return 0; -} - /** Write the "simple" list of channel modes for channel channel onto buffer mbuf with the parameters in pbuf. + * @param client The client requesting the mode list (can be NULL) + * @param mbuf Modes will be stored here + * @param pbuf Mode parameters will be stored here + * @param mbuf_size Length of the mbuf buffer + * @param pbuf_size Length of the pbuf buffer + * @param channel The channel to fetch modes from + * @param hide_local_modes If set to 1 then we will hide local channel modes like Z and d + * (eg: if you intend to send the buffer to a remote server) */ -/* TODO: this function has many security issues and needs an audit, maybe even a recode */ -void channel_modes(Client *client, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size, Channel *channel) +void channel_modes(Client *client, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size, Channel *channel, int hide_local_modes) { - CoreChannelModeTable *tab = &corechannelmodetable[0]; - int ismember; - int i; + int ismember = 0; + Cmode *cm; - if (!(mbuf_size && pbuf_size)) return; + if (!mbuf_size || !pbuf_size) + return; - ismember = (IsMember(client, channel) || IsServer(client) || IsMe(client) || IsULine(client)) ? 1 : 0; + if (!client || IsMember(client, channel) || IsServer(client) || IsMe(client) || IsULine(client)) + ismember = 1; *pbuf = '\0'; + strlcpy(mbuf, "+", mbuf_size); - *mbuf++ = '+'; - mbuf_size--; /* Paramless first */ - while (mbuf_size && tab->mode != 0x0) + for (cm=channelmodes; cm; cm = cm->next) { - if ((channel->mode.mode & tab->mode)) - if (!tab->parameters) { - *mbuf++ = tab->flag; - mbuf_size--; - } - tab++; - } - for (i=0; i <= Channelmode_highest; i++) - { - if (!mbuf_size) break; - if (Channelmode_Table[i].flag && !Channelmode_Table[i].paracount && - (channel->mode.extmode & Channelmode_Table[i].mode)) { - *mbuf++ = Channelmode_Table[i].flag; - mbuf_size--; - } - } - if (channel->mode.limit) - { - if (mbuf_size) { - *mbuf++ = 'l'; - mbuf_size--; - } - if (ismember) { - ircsnprintf(pbuf, pbuf_size, "%d ", channel->mode.limit); - pbuf_size-=strlen(pbuf); - pbuf+=strlen(pbuf); - } - } - if (*channel->mode.key) - { - if (mbuf_size) { - *mbuf++ = 'k'; - mbuf_size--; - } - if (ismember && pbuf_size) { - ircsnprintf(pbuf, pbuf_size, "%s ", channel->mode.key); - pbuf_size-=strlen(pbuf); - pbuf+=strlen(pbuf); + if (cm->letter && + !cm->paracount && + !(hide_local_modes && cm->local) && + (channel->mode.mode & cm->mode)) + { + strlcat_letter(mbuf, cm->letter, mbuf_size); } } - for (i=0; i <= Channelmode_highest; i++) + for (cm=channelmodes; cm; cm = cm->next) { - if (Channelmode_Table[i].flag && Channelmode_Table[i].paracount && - (channel->mode.extmode & Channelmode_Table[i].mode)) { - char flag = Channelmode_Table[i].flag; - if (mbuf_size) { - *mbuf++ = flag; - mbuf_size--; - } + if (cm->letter && + cm->paracount && + !(hide_local_modes && cm->local) && + (channel->mode.mode & cm->mode)) + { + char flag = cm->letter; + + if (mbuf_size) + strlcat_letter(mbuf, flag, mbuf_size); + if (ismember) { - ircsnprintf(pbuf, pbuf_size, "%s ", cm_getparameter(channel, flag)); - pbuf_size-=strlen(pbuf); - pbuf+=strlen(pbuf); + strlcat(pbuf, cm_getparameter(channel, flag), pbuf_size); + strlcat(pbuf, " ", pbuf_size); } } } /* Remove the trailing space from the parameters -- codemastr */ - if (*pbuf) pbuf[strlen(pbuf)-1]=0; - - if (!mbuf_size) mbuf--; - *mbuf++ = '\0'; - return; + if (*pbuf) + pbuf[strlen(pbuf)-1]='\0'; } /** Make a pretty mask from the input string - only used by SILENCE */ -char *pretty_mask(char *mask) +char *pretty_mask(const char *mask_in) { - char *cp; - char *user; - char *host; + char mask[512]; + char *cp, *user, *host; + + strlcpy(mask, mask_in, sizeof(mask)); if ((user = strchr((cp = mask), '!'))) *user++ = '\0'; + if ((host = strrchr(user ? user : cp, '@'))) { *host++ = '\0'; @@ -745,7 +685,9 @@ char *pretty_mask(char *mask) return make_nick_user_host(NULL, cp, host); } else if (!user && strchr(cp, '.')) + { return make_nick_user_host(NULL, NULL, cp); + } return make_nick_user_host(cp, user, host); } @@ -772,31 +714,30 @@ char *trim_str(char *str, int len) * @param mask The ban mask * @param what MODE_DEL or MODE_ADD * @param client The client adding/removing this ban mask + * @param conv_options Options for BanContext.conv_options (eg BCTX_CONV_OPTION_WRITE_LETTER_BANS) * @returns pointer to correct banmask or NULL in case of error * @note A pointer is returned to a static buffer, which is overwritten * on next clean_ban_mask or make_nick_user_host call. */ -char *clean_ban_mask(char *mask, int what, Client *client) +const char *clean_ban_mask(const char *mask_in, int what, Client *client, int conv_options) { char *cp, *x; char *user; char *host; - Extban *p; - static char maskbuf[512]; + static char mask[512]; + + /* Strip any ':' at beginning since that would cause a desync */ + for (; (*mask_in && (*mask_in == ':')); mask_in++); + if (!*mask_in) + return NULL; /* Work on a copy */ - strlcpy(maskbuf, mask, sizeof(maskbuf)); - mask = maskbuf; + strlcpy(mask, mask_in, sizeof(mask)); cp = strchr(mask, ' '); if (cp) *cp = '\0'; - /* Strip any ':' at beginning since that would cause a desync */ - for (; (*mask && (*mask == ':')); mask++); - if (!*mask) - return NULL; - /* Forbid ASCII <= 32 in all bans */ for (x = mask; *x; x++) if (*x <= ' ') @@ -805,6 +746,9 @@ char *clean_ban_mask(char *mask, int what, Client *client) /* Extended ban? */ if (is_extended_ban(mask)) { + const char *nextbanstr; + Extban *extban; + if (RESTRICT_EXTENDEDBANS && MyUser(client) && !ValidatePermissionsForPath("immune:restrict-extendedbans",client,NULL,NULL,NULL)) { if (!strcmp(RESTRICT_EXTENDEDBANS, "*")) @@ -819,8 +763,9 @@ char *clean_ban_mask(char *mask, int what, Client *client) return NULL; } } - p = findmod_by_bantype(mask[1]); - if (!p) + + extban = findmod_by_bantype(mask, &nextbanstr); + if (!extban) { /* extended bantype not supported, what to do? * Here are the rules: @@ -833,8 +778,21 @@ char *clean_ban_mask(char *mask, int what, Client *client) return mask; /* allow it */ return NULL; /* reject */ } - if (p->conv_param) - return p->conv_param(mask); + + if (extban->conv_param) + { + const char *ret; + static char retbuf[512]; + BanContext *b = safe_alloc(sizeof(BanContext)); + b->client = client; + b->what = what; + b->banstr = nextbanstr; + b->conv_options = conv_options; + ret = extban->conv_param(b, extban); + ret = prefix_with_extban(ret, b, extban, retbuf, sizeof(retbuf)); + safe_free(b); + return ret; + } /* else, do some basic sanity checks and cut it off at 80 bytes */ if ((mask[1] != ':') || (mask[2] == '\0')) return NULL; /* require a ":" after extban type */ @@ -864,11 +822,23 @@ char *clean_ban_mask(char *mask, int what, Client *client) int find_invex(Channel *channel, Client *client) { Ban *inv; + BanContext *b = safe_alloc(sizeof(BanContext)); + + b->client = client; + b->channel = channel; + b->ban_check_types = BANCHK_JOIN; for (inv = channel->invexlist; inv; inv = inv->next) - if (ban_check_mask(client, channel, inv->banstr, BANCHK_JOIN, NULL, NULL, 0)) + { + b->banstr = inv->banstr; + if (ban_check_mask(b)) + { + safe_free(b); return 1; + } + } + safe_free(b); return 0; } @@ -924,134 +894,77 @@ int valid_channelname(const char *cname) return 1; /* Valid */ } -/** Get existing channel 'chname' or create a new one. - * @param client User creating or searching this channel - * @param chname Channel name +void initlist_channels(void) +{ + channel_pool = mp_pool_new(sizeof(Channel), 512 * 1024); +} + +/** Create channel 'name' (or if it exists, return the existing one) + * @param name Channel name * @param flag If set to 'CREATE' then the channel is * created if it does not exist. * @returns Pointer to channel (new or existing). * @note Be sure to call valid_channelname() first before * you blindly call this function! */ -Channel *get_channel(Client *client, char *chname, int flag) +Channel *make_channel(const char *name) { Channel *channel; - int len; + int len; + char *p; + char namebuf[CHANNELLEN+1]; - if (BadPtr(chname)) + if (BadPtr(name)) return NULL; - len = strlen(chname); - if (MyUser(client) && len > CHANNELLEN) + /* Copy and silently truncate */ + strlcpy(namebuf, name, sizeof(namebuf)); + + /* Copied from valid_channelname(), the minimal requirements */ + for (p = namebuf; *p; p++) { - len = CHANNELLEN; - *(chname + CHANNELLEN) = '\0'; - } - if ((channel = find_channel(chname, NULL))) - return (channel); - if (flag == CREATE) - { - channel = safe_alloc(sizeof(Channel) + len); - strlcpy(channel->chname, chname, len + 1); - if (channels) - channels->prevch = channel; - channel->topic = NULL; - channel->topic_nick = NULL; - channel->prevch = NULL; - channel->nextch = channels; - channel->creationtime = MyUser(client) ? TStime() : 0; - channels = channel; - add_to_channel_hash_table(chname, channel); - irccounts.channels++; - RunHook2(HOOKTYPE_CHANNEL_CREATE, client, channel); + if (*p < 33 || *p == ',' || *p == ':') + { + *p = '\0'; + break; + } } + + /* Exists? Return it. */ + if ((channel = find_channel(name))) + return channel; + + channel = mp_pool_get(channel_pool); + memset(channel, 0, sizeof(Channel)); + + strlcpy(channel->name, name, sizeof(channel->name)); + + if (channels) + channels->prevch = channel; + + channel->topic = NULL; + channel->topic_nick = NULL; + channel->prevch = NULL; + channel->nextch = channels; + channel->creationtime = TStime(); + channels = channel; + add_to_channel_hash_table(channel->name, channel); + irccounts.channels++; + + RunHook(HOOKTYPE_CHANNEL_CREATE, channel); + return channel; } -/** Register an invite from someone to a channel - so they can bypass +i etc. - * @param from The person sending the invite - * @param to The person who is invited to join - * @param channel The channel - * @param mtags Message tags associated with this INVITE command - */ -void add_invite(Client *from, Client *to, Channel *channel, MessageTag *mtags) -{ - Link *inv, *tmp; - - del_invite(to, channel); - /* If too many invite entries then delete the oldest one */ - if (list_length(to->user->invited) >= MAXCHANNELSPERUSER) - { - for (tmp = to->user->invited; tmp->next; tmp = tmp->next) - ; - del_invite(to, tmp->value.channel); - - } - /* We get pissy over too many invites per channel as well now, - * since otherwise mass-inviters could take up some major - * resources -Donwulff - */ - if (list_length(channel->invites) >= MAXCHANNELSPERUSER) - { - for (tmp = channel->invites; tmp->next; tmp = tmp->next) - ; - del_invite(tmp->value.client, channel); - } - /* - * add client to the beginning of the channel invite list - */ - inv = make_link(); - inv->value.client = to; - inv->next = channel->invites; - channel->invites = inv; - /* - * add channel to the beginning of the client invite list - */ - inv = make_link(); - inv->value.channel = channel; - inv->next = to->user->invited; - to->user->invited = inv; - - RunHook4(HOOKTYPE_INVITE, from, to, channel, mtags); -} - -/** Delete a previous invite of someone to a channel. - * @param client The client who was invited - * @param channel The channel to which the person was invited - */ -void del_invite(Client *client, Channel *channel) -{ - Link **inv, *tmp; - - for (inv = &(channel->invites); (tmp = *inv); inv = &tmp->next) - if (tmp->value.client == client) - { - *inv = tmp->next; - free_link(tmp); - break; - } - - for (inv = &(client->user->invited); (tmp = *inv); inv = &tmp->next) - if (tmp->value.channel == channel) - { - *inv = tmp->next; - free_link(tmp); - break; - } -} - /** Is the user 'client' invited to channel 'channel' by a chanop? * @param client The client who was invited * @param channel The channel to which the person was invited */ int is_invited(Client *client, Channel *channel) { - Link *lp; - - for (lp = client->user->invited; lp; lp = lp->next) - if (lp->value.channel == channel) - return 1; - return 0; + int invited = 0; + RunHook(HOOKTYPE_IS_INVITED, client, channel, &invited); + return invited; } /** Subtract one user from channel i. Free the channel if it became empty. @@ -1072,7 +985,7 @@ int sub1_from_channel(Channel *channel) channel->users = 0; /* to be sure */ /* If the channel is +P then this hook will actually stop destruction. */ - RunHook2(HOOKTYPE_CHANNEL_DESTROY, channel, &should_destroy); + RunHook(HOOKTYPE_CHANNEL_DESTROY, channel, &should_destroy); if (!should_destroy) return 0; @@ -1082,9 +995,6 @@ int sub1_from_channel(Channel *channel) moddata_free_channel(channel); - while ((lp = channel->invites)) - del_invite(lp->value.client, channel); - while (channel->banlist) { ban = channel->banlist; @@ -1111,7 +1021,7 @@ int sub1_from_channel(Channel *channel) } /* free extcmode params */ - extcmode_free_paramlist(channel->mode.extmodeparams); + extcmode_free_paramlist(channel->mode.mode_params); safe_free(channel->mode_lock); safe_free(channel->topic); @@ -1124,10 +1034,10 @@ int sub1_from_channel(Channel *channel) if (channel->nextch) channel->nextch->prevch = channel->prevch; - del_from_channel_hash_table(channel->chname, channel); + del_from_channel_hash_table(channel->name, channel); irccounts.channels--; - safe_free(channel); + mp_pool_release(channel); return 1; } @@ -1143,7 +1053,7 @@ void set_channel_mlock(Client *client, Channel *channel, const char *newmlock, i if (propagate) { sendto_server(client, 0, 0, NULL, ":%s MLOCK %lld %s :%s", - client->id, (long long)channel->creationtime, channel->chname, + client->id, (long long)channel->creationtime, channel->name, BadPtr(channel->mode_lock) ? "" : channel->mode_lock); } } @@ -1159,14 +1069,14 @@ void set_channel_mlock(Client *client, Channel *channel, const char *newmlock, i * int ret; * for (ret = parse_chanmode(&pm, modebuf, parabuf); ret; ret = parse_chanmode(&pm, NULL, NULL)) * { - * ircd_log(LOG_ERROR, "Got %c%c %s", - * pm.what == MODE_ADD ? '+' : '-', - * pm.modechar, - * pm.param ? pm.param : ""); + * unreal_log(ULOG_INFO, "test", "TEST", "Got %c%c %s", + * pm.what == MODE_ADD ? '+' : '-', + * pm.modechar, + * pm.param ? pm.param : ""); * } * @endcode */ -int parse_chanmode(ParseMode *pm, char *modebuf_in, char *parabuf_in) +int parse_chanmode(ParseMode *pm, const char *modebuf_in, const char *parabuf_in) { if (modebuf_in) { @@ -1196,7 +1106,7 @@ int parse_chanmode(ParseMode *pm, char *modebuf_in, char *parabuf_in) else { CoreChannelModeTable *tab = &corechannelmodetable[0]; - int i; + Cmode *cm; int eatparam = 0; /* Set some defaults */ @@ -1216,32 +1126,31 @@ int parse_chanmode(ParseMode *pm, char *modebuf_in, char *parabuf_in) /* INTERNAL MODE */ if (tab->parameters) { - if ((pm->what == MODE_DEL) && (tab->flag == 'l')) - eatparam = 0; /* -l is special: no parameter required */ - else - eatparam = 1; /* all other internal parameter modes do require a parameter on unset */ + eatparam = 1; } } else { /* EXTENDED CHANNEL MODE */ int found = 0; - for (i=0; i <= Channelmode_highest; i++) - if (Channelmode_Table[i].flag == *pm->modebuf) + for (cm=channelmodes; cm; cm = cm->next) + { + if (cm->letter == *pm->modebuf) { found = 1; break; } + } if (!found) { /* Not found. Will be ignored, just move on.. */ pm->modebuf++; continue; } - pm->extm = &Channelmode_Table[i]; - if (Channelmode_Table[i].paracount == 1) + pm->extm = cm; + if (cm->paracount == 1) { if (pm->what == MODE_ADD) eatparam = 1; - else if (Channelmode_Table[i].unset_with_param) + else if (cm->unset_with_param) eatparam = 1; /* else 0 (if MODE_DEL && !unset_with_param) */ } @@ -1252,7 +1161,7 @@ int parse_chanmode(ParseMode *pm, char *modebuf_in, char *parabuf_in) /* Hungry.. */ if (pm->parabuf && *pm->parabuf) { - char *start, *end; + const char *start, *end; for (; *pm->parabuf == ' '; pm->parabuf++); /* skip whitespace */ start = pm->parabuf; if (*pm->parabuf == '\0') @@ -1274,6 +1183,7 @@ int parse_chanmode(ParseMode *pm, char *modebuf_in, char *parabuf_in) strlcpy(pm->buf, start, sizeof(pm->buf)); pm->parabuf = pm->parabuf + strlen(pm->parabuf); /* point to \0 at end */ } + stripcrlf(pm->buf); /* needed for unreal_server_compat.c */ pm->param = pm->buf; } else { pm->modebuf++; @@ -1319,7 +1229,7 @@ int user_can_see_member(Client *user, Client *target, Channel *channel) } /* We must ensure that user is allowed to "see" target */ - if (j != 0 && !(is_skochanop(target, channel) || has_voice(target,channel)) && !is_skochanop(user, channel)) + if (j != 0 && !(check_channel_access(target, channel, "hoaq") || check_channel_access(target,channel, "v")) && !check_channel_access(user, channel, "hoaq")) return 0; return 1; @@ -1341,7 +1251,7 @@ int invisible_user_in_channel(Client *target, Channel *channel) } /* We must ensure that user is allowed to "see" target */ - if (j != 0 && !(is_skochanop(target, channel) || has_voice(target,channel))) + if (j != 0 && !(check_channel_access(target, channel, "hoaq") || check_channel_access(target,channel, "v"))) return 1; return 0; @@ -1352,9 +1262,9 @@ int invisible_user_in_channel(Client *target, Channel *channel) * @param client The client to send the message to. * @param channelname The (invalid) channel that the user tried to join. */ -void send_invalid_channelname(Client *client, char *channelname) +void send_invalid_channelname(Client *client, const char *channelname) { - char *reason; + const char *reason; if (*channelname != '#') { @@ -1383,10 +1293,60 @@ void send_invalid_channelname(Client *client, char *channelname) /** Is the provided string possibly an extended ban? * Note that it still may not exist, it just tests the first part. + * @param str The string to check (eg "~account:xyz") */ int is_extended_ban(const char *str) { - if ((str[0] == '~') && (str[1] != '\0') && (str[2] == ':')) + const char *p; + + if (*str != '~') + return 0; + for (p = str+1; *p; p++) + { + if (!isalnum(*p)) + { + if (*p == ':') + return 1; + } + } + return 0; +} + +/** Is the provided string possibly an extended server ban? + * Actually this is only a very light check. + * It may still not exist, it just tests the first part. + * @param str The string to check (eg "~account:xyz") + * The only difference between this and is_extended_ban() + * is that we allow a % at the beginning for soft-bans. + * @see is_extended_ban() + */ +int is_extended_server_ban(const char *str) +{ + if (*str == '%') + str++; + return is_extended_ban(str); +} + +/** Check if it is an empty (useless) mode, namely "", "+" or "-". + * Typically called as: empty_mode(modebuf) + */ +int empty_mode(const char *m) +{ + if (!*m || (((m[0] == '+') || (m[0] == '-')) && m[1] == '\0')) return 1; return 0; } + +/** Free everything of/in a MultiLineMode */ +void free_multilinemode(MultiLineMode *m) +{ + int i; + if (m == NULL) + return; + for (i=0; i < m->numlines; i++) + { + safe_free(m->modeline[i]); + safe_free(m->paramline[i]); + } + safe_free(m); +} diff --git a/src/conf.c b/src/conf.c index 694f266..f3d78df 100644 --- a/src/conf.c +++ b/src/conf.c @@ -31,13 +31,6 @@ struct ConfigCommand int (*testfunc)(ConfigFile *conf, ConfigEntry *ce); }; -typedef struct NameValue NameValue; -struct NameValue -{ - long flag; - char *name; -}; - /* Config commands */ @@ -65,7 +58,6 @@ static int _conf_deny_version (ConfigFile *conf, ConfigEntry *ce); static int _conf_require (ConfigFile *conf, ConfigEntry *ce); static int _conf_allow_channel (ConfigFile *conf, ConfigEntry *ce); static int _conf_loadmodule (ConfigFile *conf, ConfigEntry *ce); -static int _conf_log (ConfigFile *conf, ConfigEntry *ce); static int _conf_alias (ConfigFile *conf, ConfigEntry *ce); static int _conf_help (ConfigFile *conf, ConfigEntry *ce); static int _conf_offchans (ConfigFile *conf, ConfigEntry *ce); @@ -99,7 +91,6 @@ static int _test_deny (ConfigFile *conf, ConfigEntry *ce); static int _test_allow_channel (ConfigFile *conf, ConfigEntry *ce); static int _test_loadmodule (ConfigFile *conf, ConfigEntry *ce); static int _test_blacklist_module (ConfigFile *conf, ConfigEntry *ce); -static int _test_log (ConfigFile *conf, ConfigEntry *ce); static int _test_alias (ConfigFile *conf, ConfigEntry *ce); static int _test_help (ConfigFile *conf, ConfigEntry *ce); static int _test_offchans (ConfigFile *conf, ConfigEntry *ce); @@ -124,7 +115,7 @@ static ConfigCommand _ConfigCommands[] = { { "link", _conf_link, _test_link }, { "listen", _conf_listen, _test_listen }, { "loadmodule", NULL, _test_loadmodule}, - { "log", _conf_log, _test_log }, + { "log", config_run_log, config_test_log }, { "me", _conf_me, _test_me }, { "official-channels", _conf_offchans, _test_offchans }, { "oper", _conf_oper, _test_oper }, @@ -158,22 +149,6 @@ static NameValue _LinkFlags[] = { { CONNECT_TLS, "tls" }, }; -/* This MUST be alphabetized */ -static NameValue _LogFlags[] = { - { LOG_CHGCMDS, "chg-commands" }, - { LOG_CLIENT, "connects" }, - { LOG_ERROR, "errors" }, - { LOG_FLOOD, "flood" }, - { LOG_KILL, "kills" }, - { LOG_KLINE, "kline" }, - { LOG_OPER, "oper" }, - { LOG_OVERRIDE, "oper-override" }, - { LOG_SACMDS, "sadmin-commands" }, - { LOG_SERVER, "server-connects" }, - { LOG_SPAMFILTER, "spamfilter" }, - { LOG_TKL, "tkl" }, -}; - /* This MUST be alphabetized */ static NameValue _TLSFlags[] = { { TLSFLAG_FAILIFNOCERT, "fail-if-no-clientcert" }, @@ -194,28 +169,25 @@ struct SetCheck settings; * Utilities */ -void port_range(char *string, int *start, int *end); -long config_checkval(char *value, unsigned short flags); +void port_range(const char *string, int *start, int *end); +long config_checkval(const char *value, unsigned short flags); /* * Parser */ -ConfigFile *config_load(char *filename, char *displayname); +ConfigFile *config_load(const char *filename, const char *displayname); void config_free(ConfigFile *cfptr); -ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned int line_offset); -ConfigFile *config_parse(char *filename, char *confdata); -ConfigEntry *config_find_entry(ConfigEntry *ce, char *name); +ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset); +ConfigFile *config_parse(const char *filename, char *confdata); +ConfigEntry *config_find_entry(ConfigEntry *ce, const char *name); -extern void add_entropy_configfile(struct stat *st, char *buf); -extern void unload_all_unused_snomasks(void); +extern void add_entropy_configfile(struct stat *st, const char *buf); extern void unload_all_unused_umodes(void); extern void unload_all_unused_extcmodes(void); extern void unload_all_unused_caps(void); extern void unload_all_unused_history_backends(void); - int reloadable_perm_module_unloaded(void); - int tls_tests(void); /* Conf sub-sub-functions */ @@ -226,10 +198,11 @@ void free_tls_options(TLSOptions *tlsoptions); /* * Config parser (IRCd) */ -int init_conf(char *rootconf, int rehash); -int load_conf(char *filename, const char *original_path); +int config_read_file(const char *filename, const char *display_name); void config_rehash(); -int config_run(); +int config_run_blocks(); +int config_test_blocks(); + /* * Configuration linked lists */ @@ -247,7 +220,6 @@ ConfigItem_operclass *conf_operclass = NULL; ConfigItem_listen *conf_listen = NULL; ConfigItem_sni *conf_sni = NULL; ConfigItem_allow *conf_allow = NULL; -ConfigItem_except *conf_except = NULL; ConfigItem_vhost *conf_vhost = NULL; ConfigItem_link *conf_link = NULL; ConfigItem_ban *conf_ban = NULL; @@ -255,9 +227,8 @@ ConfigItem_deny_channel *conf_deny_channel = NULL; ConfigItem_allow_channel *conf_allow_channel = NULL; ConfigItem_deny_link *conf_deny_link = NULL; ConfigItem_deny_version *conf_deny_version = NULL; -ConfigItem_log *conf_log = NULL; ConfigItem_alias *conf_alias = NULL; -ConfigItem_include *conf_include = NULL; +ConfigResource *config_resources = NULL; ConfigItem_blacklist_module *conf_blacklist_module = NULL; ConfigItem_help *conf_help = NULL; ConfigItem_offchans *conf_offchans = NULL; @@ -274,25 +245,19 @@ MODVAR Client *remote_rehash_client = NULL; MODVAR int config_error_flag = 0; int config_verbose = 0; -MODVAR int need_34_upgrade = 0; int need_operclass_permissions_upgrade = 0; +int invalid_snomasks_encountered = 0; int have_tls_listeners = 0; char *port_6667_ip = NULL; -void add_include(const char *filename, const char *included_from, int included_from_line); -#ifdef USE_LIBCURL -void add_remote_include(const char *, const char *, int, const char *, const char *included_from, int included_from_line); -void update_remote_include(ConfigItem_include *inc, const char *file, int, const char *errorbuf); -int remote_include(ConfigEntry *ce); -#endif -void unload_notloaded_includes(void); -void load_includes(void); -void unload_loaded_includes(void); -int rehash_internal(Client *client, int sig); -int is_blacklisted_module(char *name); +int add_config_resource(const char *resource, int type, ConfigEntry *ce); +void resource_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *rs_key); +void free_all_config_resources(void); +int rehash_internal(Client *client); +int is_blacklisted_module(const char *name); /** Return the printable string of a 'cep' location, such as set::something::xyz */ -char *config_var(ConfigEntry *cep) +const char *config_var(ConfigEntry *cep) { static char buf[256]; ConfigEntry *e; @@ -305,9 +270,9 @@ char *config_var(ConfigEntry *cep) buf[0] = '\0'; /* First, walk back to the top */ - for (e = cep; e; e = e->ce_prevlevel) + for (e = cep; e; e = e->parent) { - elem[numel++] = e->ce_varname; + elem[numel++] = e->name; if (numel == 15) break; } @@ -323,9 +288,12 @@ char *config_var(ConfigEntry *cep) return buf; } -void port_range(char *string, int *start, int *end) +void port_range(const char *string, int *start, int *end) { - char *c = strchr(string, '-'); + char buf[256]; + char *c; + strlcpy(buf, string, sizeof(buf)); + c = strchr(buf, '-'); if (!c) { int tmp = atoi(string); @@ -346,18 +314,21 @@ void port_range(char *string, int *start, int *end) * RETURNS: 0 for parse error, 1 if ok. * REMARK: times&period should be ints! */ -int config_parse_flood(char *orig, int *times, int *period) +int config_parse_flood(const char *orig, int *times, int *period) { -char *x; + char buf[256]; + char *x; + + strlcpy(buf, orig, sizeof(buf)); *times = *period = 0; - x = strchr(orig, ':'); + x = strchr(buf, ':'); /* 'blah', ':blah', '1:' */ - if (!x || (x == orig) || (*(x+1) == '\0')) + if (!x || (x == buf) || (*(x+1) == '\0')) return 0; *x = '\0'; - *times = atoi(orig); + *times = atoi(buf); *period = config_checkval(x+1, CFG_TIME); *x = ':'; /* restore */ return 1; @@ -441,6 +412,12 @@ int flood_option_is_for_everyone(const char *name) return text_in_array(name, opts); } +/** Free a FloodSettings struct */ +void free_floodsettings(FloodSettings *f) +{ + safe_free(f->name); + safe_free(f); +} /** Parses a value like '5:60s' into a flood setting that we can store. * @param str The string to parse (eg: '5:60s') @@ -485,7 +462,8 @@ int config_parse_flood_generic(const char *str, Configuration *conf, char *block return 1; } -long config_checkval(char *orig, unsigned short flags) { +long config_checkval(const char *orig, unsigned short flags) +{ char *value = raw_strdup(orig); char *text; long ret = 0; @@ -594,24 +572,20 @@ long config_checkval(char *orig, unsigned short flags) { /** Free configuration setting for set::modes-on-join */ void free_conf_channelmodes(struct ChMode *store) { - int i; - - store->mode = 0; - store->extmodes = 0; - for (i = 0; i < EXTCMODETABLESZ; i++) - safe_free(store->extparams[i]); + memset(store, 0, sizeof(struct ChMode)); } /* Set configuration, used for set::modes-on-join */ -void conf_channelmodes(char *modes, struct ChMode *store, int warn) +void conf_channelmodes(const char *modes, struct ChMode *store) { - CoreChannelModeTable *tab; + Cmode *cm; + const char *m; char *params = strchr(modes, ' '); char *parambuf = NULL; - char *param = NULL; + const char *param = NULL; + const char *param_in; char *save = NULL; - - warn = 0; // warn is broken + int found; /* Free existing parameters first (no inheritance) */ free_conf_channelmodes(store); @@ -623,107 +597,86 @@ void conf_channelmodes(char *modes, struct ChMode *store, int warn) param = strtoken(&save, parambuf, " "); } - for (; *modes && *modes != ' '; modes++) + for (m = modes; *m && *m != ' '; m++) { - if (*modes == '+') + if (*m == '+') continue; - if (*modes == '-') - /* When a channel is created it has no modes, so just ignore if the - * user asks us to unset anything -- codemastr - */ + + if (*m == '-') { - while (*modes && *modes != '+') - modes++; + /* When a channel is created it has no modes, so just ignore if the + * user asks us to unset anything -- codemastr + */ + while (*m && *m != '+') + m++; continue; } - for (tab = &corechannelmodetable[0]; tab->mode; tab++) + + found = 0; + for (cm=channelmodes; cm; cm = cm->next) { - if (tab->flag == *modes) + if (!(cm->letter)) + continue; + if (*m == cm->letter) { - if (tab->parameters) + found = 1; + if (cm->paracount) { - /* INCOMPATIBLE */ - break; + if (!param) + { + config_warn("set::modes-on-join '%s'. Parameter missing for mode %c.", modes, *m); + break; + } + param_in = param; /* save it */ + param = cm->conv_param(param, NULL, NULL); + if (!param) + { + config_warn("set::modes-on-join '%s'. Parameter for mode %c is invalid (%s).", modes, *m, param_in); + break; /* invalid parameter fmt, do not set mode. */ + } + safe_strdup(store->extparams[cm->letter], param); + /* Get next parameter */ + param = strtoken(&save, NULL, " "); } - store->mode |= tab->mode; + store->extmodes |= cm->mode; break; } } - /* Try extcmodes */ - if (!tab->mode) - { - int i; - for (i=0; i <= Channelmode_highest; i++) - { - if (!(Channelmode_Table[i].flag)) - continue; - if (*modes == Channelmode_Table[i].flag) - { - if (Channelmode_Table[i].paracount) - { - if (!param) - break; - param = Channelmode_Table[i].conv_param(param, NULL, NULL); - if (!param) - break; /* invalid parameter fmt, do not set mode. */ - store->extparams[i] = raw_strdup(param); - /* Get next parameter */ - param = strtoken(&save, NULL, " "); - } - store->extmodes |= Channelmode_Table[i].mode; - break; - } - } - } + if (!found) + config_warn("set::modes-on-join '%s'. Channel mode %c not found.", modes, *m); } safe_free(parambuf); } void chmode_str(struct ChMode *modes, char *mbuf, char *pbuf, size_t mbuf_size, size_t pbuf_size) { - CoreChannelModeTable *tab; - int i; + Cmode *cm; if (!(mbuf_size && pbuf_size)) return; *pbuf = 0; *mbuf++ = '+'; - if (--mbuf_size == 0) return; - for (tab = &corechannelmodetable[0]; tab->mode; tab++) + + for (cm=channelmodes; cm; cm = cm->next) { - if (modes->mode & tab->mode) - { - if (!tab->parameters) - { - *mbuf++ = tab->flag; - if (!--mbuf_size) - { - *--mbuf=0; - break; - } - } - } - } - for (i=0; i <= Channelmode_highest; i++) - { - if (!(Channelmode_Table[i].flag)) + if (!(cm->letter)) continue; - if (modes->extmodes & Channelmode_Table[i].mode) + if (modes->extmodes & cm->mode) { if (mbuf_size) { - *mbuf++ = Channelmode_Table[i].flag; + *mbuf++ = cm->letter; if (!--mbuf_size) { *--mbuf=0; break; } } - if (Channelmode_Table[i].paracount) + if (cm->paracount) { - strlcat(pbuf, modes->extparams[i], pbuf_size); + strlcat(pbuf, modes->extparams[cm->letter], pbuf_size); strlcat(pbuf, " ", pbuf_size); } } @@ -731,87 +684,26 @@ void chmode_str(struct ChMode *modes, char *mbuf, char *pbuf, size_t mbuf_size, *mbuf=0; } -int channellevel_to_int(char *s) +const char *channellevel_to_string(const char *s) { /* Requested at http://bugs.unrealircd.org/view.php?id=3852 */ if (!strcmp(s, "none")) - return CHFL_DEOPPED; + return ""; if (!strcmp(s, "voice")) - return CHFL_VOICE; + return "v"; if (!strcmp(s, "halfop")) - return CHFL_HALFOP; + return "h"; if (!strcmp(s, "op") || !strcmp(s, "chanop")) - return CHFL_CHANOP; - if (!strcmp(s, "protect") || !strcmp(s, "chanprot")) -#ifdef PREFIX_AQ - return CHFL_CHANADMIN; -#else - return CHFL_CHANOP|CHFL_CHANADMIN; -#endif + return "o"; + if (!strcmp(s, "protect") || !strcmp(s, "chanprot") || !strcmp(s, "chanadmin") || !strcmp(s, "admin")) + return "a"; if (!strcmp(s, "owner") || !strcmp(s, "chanowner")) -#ifdef PREFIX_AQ - return CHFL_CHANOWNER; -#else - return CHFL_CHANOP|CHFL_CHANOWNER; -#endif + return "q"; - return 0; /* unknown or unsupported */ + return NULL; /* unknown or unsupported */ } -/* Channel flag (eg: CHFL_CHANOWNER) to SJOIN symbol (eg: *). - * WARNING: Do not confuse SJOIN symbols with prefixes in /NAMES! - */ -char *chfl_to_sjoin_symbol(int s) -{ - switch(s) - { - case CHFL_VOICE: - return "+"; - case CHFL_HALFOP: - return "%"; - case CHFL_CHANOP: - return "@"; - case CHFL_CHANADMIN: -#ifdef PREFIX_AQ - return "~"; -#else - return "~@"; -#endif - case CHFL_CHANOWNER: -#ifdef PREFIX_AQ - return "*"; -#else - return "*@"; -#endif - case CHFL_DEOPPED: - default: - return ""; - } - /* NOT REACHED */ -} - -char chfl_to_chanmode(int s) -{ - switch(s) - { - case CHFL_VOICE: - return 'v'; - case CHFL_HALFOP: - return 'h'; - case CHFL_CHANOP: - return 'o'; - case CHFL_CHANADMIN: - return 'a'; - case CHFL_CHANOWNER: - return 'q'; - case CHFL_DEOPPED: - default: - return '\0'; - } - /* NOT REACHED */ -} - -Policy policy_strtoval(char *s) +Policy policy_strtoval(const char *s) { if (!s) return 0; @@ -828,7 +720,7 @@ Policy policy_strtoval(char *s) return 0; } -char *policy_valtostr(Policy policy) +const char *policy_valtostr(Policy policy) { if (policy == POLICY_ALLOW) return "allow"; @@ -850,7 +742,7 @@ char policy_valtochar(Policy policy) return '?'; } -AllowedChannelChars allowed_channelchars_strtoval(char *str) +AllowedChannelChars allowed_channelchars_strtoval(const char *str) { if (!strcmp(str, "ascii")) return ALLOWED_CHANNELCHARS_ASCII; @@ -861,7 +753,7 @@ AllowedChannelChars allowed_channelchars_strtoval(char *str) return 0; } -char *allowed_channelchars_valtostr(AllowedChannelChars v) +const char *allowed_channelchars_valtostr(AllowedChannelChars v) { switch(v) { @@ -879,7 +771,7 @@ char *allowed_channelchars_valtostr(AllowedChannelChars v) } /* Used for set::automatic-ban-target and set::manual-ban-target */ -BanTarget ban_target_strtoval(char *str) +BanTarget ban_target_strtoval(const char *str) { if (!strcmp(str, "ip")) return BAN_TARGET_IP; @@ -897,7 +789,7 @@ BanTarget ban_target_strtoval(char *str) } /* Used for set::automatic-ban-target and set::manual-ban-target */ -char *ban_target_valtostr(BanTarget v) +const char *ban_target_valtostr(BanTarget v) { switch(v) { @@ -918,7 +810,7 @@ char *ban_target_valtostr(BanTarget v) } } -HideIdleTimePolicy hideidletime_strtoval(char *str) +HideIdleTimePolicy hideidletime_strtoval(const char *str) { if (!strcmp(str, "never")) return HIDE_IDLE_TIME_NEVER; @@ -931,7 +823,7 @@ HideIdleTimePolicy hideidletime_strtoval(char *str) return 0; } -char *hideidletime_valtostr(HideIdleTimePolicy v) +const char *hideidletime_valtostr(HideIdleTimePolicy v) { switch(v) { @@ -948,7 +840,7 @@ char *hideidletime_valtostr(HideIdleTimePolicy v) } } -ConfigFile *config_load(char *filename, char *displayname) +ConfigFile *config_load(const char *filename, const char *displayname) { struct stat sb; int fd; @@ -1013,10 +905,10 @@ void config_free(ConfigFile *cfptr) for(;cfptr;cfptr=nptr) { - nptr = cfptr->cf_next; - if (cfptr->cf_entries) - config_entry_free_all(cfptr->cf_entries); - safe_free(cfptr->cf_filename); + nptr = cfptr->next; + if (cfptr->items) + config_entry_free_all(cfptr->items); + safe_free(cfptr->filename); safe_free(cfptr); } } @@ -1043,7 +935,7 @@ void unreal_del_quotes(char *i) } /** Add quotes to a line, eg some"thing becomes some\"thing - extended version */ -int unreal_add_quotes_r(char *i, char *o, size_t len) +int unreal_add_quotes_r(const char *i, char *o, size_t len) { if (len == 0) return 0; @@ -1079,7 +971,7 @@ int unreal_add_quotes_r(char *i, char *o, size_t len) } /** Add quotes to a line, eg some"thing becomes some\"thing */ -char *unreal_add_quotes(char *str) +const char *unreal_add_quotes(const char *str) { static char qbuf[2048]; @@ -1088,14 +980,15 @@ char *unreal_add_quotes(char *str) return qbuf; } -ConfigFile *config_parse(char *filename, char *confdata){ +ConfigFile *config_parse(const char *filename, char *confdata) +{ return config_parse_with_offset(filename, confdata, 0); } /* This is the internal parser, made by Chris Behrens & Fred Jacobs <2005. * Enhanced (or mutilated) by Bram Matthys over the years (2015-2019). */ -ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned int line_offset) +ConfigFile *config_parse_with_offset(const char *filename, char *confdata, unsigned int line_offset) { char *ptr; char *start; @@ -1110,8 +1003,8 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in ConditionalConfig *cc, *cc_list = NULL; curcf = safe_alloc(sizeof(ConfigFile)); - safe_strdup(curcf->cf_filename, filename); - lastce = &(curcf->cf_entries); + safe_strdup(curcf->filename, filename); + lastce = &(curcf->items); curce = NULL; cursection = NULL; /* Replace \r's with spaces .. ugly ugly -Stskeeps */ @@ -1131,8 +1024,8 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in break; } *lastce = curce; - lastce = &(curce->ce_next); - curce->ce_fileposend = (ptr - confdata); + lastce = &(curce->next); + curce->file_position_end = (ptr - confdata); curce = NULL; break; case '{': @@ -1144,7 +1037,7 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in errors++; continue; } - else if (curce->ce_entries) + else if (curce->items) { config_error("%s:%i: New section start but previous section did not end properly. " "Check line %d and the line(s) before, you are likely missing a '};' there.\n", @@ -1152,8 +1045,8 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in errors++; continue; } - curce->ce_sectlinenum = linenumber; - lastce = &(curce->ce_entries); + curce->section_linenumber = linenumber; + lastce = &(curce->items); cursection = curce; curce = NULL; break; @@ -1176,20 +1069,20 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in continue; } curce = cursection; - cursection->ce_fileposend = (ptr - confdata); - cursection = cursection->ce_prevlevel; + cursection->file_position_end = (ptr - confdata); + cursection = cursection->parent; if (!cursection) - lastce = &(curcf->cf_entries); + lastce = &(curcf->items); else - lastce = &(cursection->ce_entries); - for(;*lastce;lastce = &((*lastce)->ce_next)) + lastce = &(cursection->items); + for(;*lastce;lastce = &((*lastce)->next)) continue; if (*(ptr+1) != ';') { /* Simulate closing ; so you can get away with } instead of ugly }; */ *lastce = curce; - lastce = &(curce->ce_next); - curce->ce_fileposend = (ptr - confdata); + lastce = &(curce->next); + curce->file_position_end = (ptr - confdata); curce = NULL; } break; @@ -1239,17 +1132,21 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in } } break; + case '\'': + if (curce) + curce->escaped = 1; + /* fallthrough */ case '\"': - if (curce && curce->ce_varlinenum != linenumber && cursection) + if (curce && curce->line_number != linenumber && cursection) { config_error("%s:%i: Missing semicolon (';') at end of line. " "Line %d must end with a ; character\n", - filename, curce->ce_varlinenum, curce->ce_varlinenum); + filename, curce->line_number, curce->line_number); errors++; *lastce = curce; - lastce = &(curce->ce_next); - curce->ce_fileposend = (ptr - confdata); + lastce = &(curce->next); + curce->file_position_end = (ptr - confdata); curce = NULL; } @@ -1258,14 +1155,18 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in { if (*ptr == '\\') { - if ((ptr[1] == '\\') || (ptr[1] == '"')) + if (strchr("\\\"'", ptr[1])) { /* \\ or \" in config file (escaped) */ ptr++; /* skip */ continue; } } - else if ((*ptr == '\"') || (*ptr == '\n')) + else if (*ptr == '\n') + break; + else if (curce && curce->escaped && (*ptr == '\'')) + break; + else if ((!curce || !curce->escaped) && (*ptr == '"')) break; } if (!*ptr || (*ptr == '\n')) @@ -1279,7 +1180,7 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in } if (curce) { - if (curce->ce_vardata) + if (curce->value) { config_error("%s:%i: Extra data detected. Perhaps missing a ';' or one too many?\n", filename, linenumber); @@ -1287,22 +1188,22 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in } else { - safe_strldup(curce->ce_vardata, start, ptr-start+1); - preprocessor_replace_defines(&curce->ce_vardata, curce); - unreal_del_quotes(curce->ce_vardata); + safe_strldup(curce->value, start, ptr-start+1); + preprocessor_replace_defines(&curce->value, curce); + unreal_del_quotes(curce->value); } } else { curce = safe_alloc(sizeof(ConfigEntry)); - curce->ce_varlinenum = linenumber; - curce->ce_fileptr = curcf; - curce->ce_prevlevel = cursection; - curce->ce_fileposstart = (start - confdata); - safe_strldup(curce->ce_varname, start, ptr-start+1); - preprocessor_replace_defines(&curce->ce_varname, curce); - unreal_del_quotes(curce->ce_varname); - preprocessor_cc_duplicate_list(cc_list, &curce->ce_cond); + curce->line_number = linenumber; + curce->file = curcf; + curce->parent = cursection; + curce->file_position_start = (start - confdata); + safe_strldup(curce->name, start, ptr-start+1); + preprocessor_replace_defines(&curce->name, curce); + unreal_del_quotes(curce->name); + preprocessor_cc_duplicate_list(cc_list, &curce->conditional_config); } break; case '\n': @@ -1372,11 +1273,11 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in if (curce) config_error("%s: End of file reached but directive or block at line %i did not end properly. " "Perhaps a missing ; (semicolon) somewhere?\n", - filename, curce->ce_varlinenum); + filename, curce->line_number); else if (cursection) config_error("%s: End of file reached but the section which starts at line %i did never end properly. " "Perhaps a missing }; ?\n", - filename, cursection->ce_sectlinenum); + filename, cursection->section_linenumber); else config_error("%s: Unexpected end of file. Some line or block did not end properly. " "Look for any missing } and };\n", filename); @@ -1387,7 +1288,7 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in } if (curce) { - if (curce->ce_vardata) + if (curce->value) { config_error("%s:%i: Extra data detected. Check for a missing ; character at or around line %d\n", filename, linenumber, linenumber-1); @@ -1395,23 +1296,23 @@ ConfigFile *config_parse_with_offset(char *filename, char *confdata, unsigned in } else { - safe_strldup(curce->ce_vardata, start, ptr-start+1); - preprocessor_replace_defines(&curce->ce_vardata, curce); + safe_strldup(curce->value, start, ptr-start+1); + preprocessor_replace_defines(&curce->value, curce); } } else { curce = safe_alloc(sizeof(ConfigEntry)); memset(curce, 0, sizeof(ConfigEntry)); - curce->ce_varlinenum = linenumber; - curce->ce_fileptr = curcf; - curce->ce_prevlevel = cursection; - curce->ce_fileposstart = (start - confdata); - safe_strldup(curce->ce_varname, start, ptr-start+1); - preprocessor_replace_defines(&curce->ce_varname, curce); - if (curce->ce_cond) - abort(); // hmm this can be reached? FIXME! - preprocessor_cc_duplicate_list(cc_list, &curce->ce_cond); + curce->line_number = linenumber; + curce->file = curcf; + curce->parent = cursection; + curce->file_position_start = (start - confdata); + safe_strldup(curce->name, start, ptr-start+1); + preprocessor_replace_defines(&curce->name, curce); + if (curce->conditional_config) + abort(); + preprocessor_cc_duplicate_list(cc_list, &curce->conditional_config); } if ((*ptr == ';') || (*ptr == '\n')) ptr--; @@ -1425,7 +1326,7 @@ breakout: { config_error("%s: End of file reached but directive or block at line %i did not end properly. " "Perhaps a missing ; (semicolon) somewhere?\n", - filename, curce->ce_varlinenum); + filename, curce->line_number); errors++; config_entry_free_all(curce); } @@ -1433,7 +1334,7 @@ breakout: { config_error("%s: End of file reached but the section which starts at line %i did never end properly. " "Perhaps a missing }; ?\n", - filename, cursection->ce_sectlinenum); + filename, cursection->section_linenumber); errors++; } @@ -1455,13 +1356,13 @@ void config_entry_free_all(ConfigEntry *ce) for(;ce;ce=nptr) { - nptr = ce->ce_next; - if (ce->ce_entries) - config_entry_free_all(ce->ce_entries); - safe_free(ce->ce_varname); - safe_free(ce->ce_vardata); - if (ce->ce_cond) - preprocessor_cc_free_list(ce->ce_cond); + nptr = ce->next; + if (ce->items) + config_entry_free_all(ce->items); + safe_free(ce->name); + safe_free(ce->value); + if (ce->conditional_config) + preprocessor_cc_free_list(ce->conditional_config); safe_free(ce); } } @@ -1471,21 +1372,21 @@ void config_entry_free_all(ConfigEntry *ce) */ void config_entry_free(ConfigEntry *ce) { - if (ce->ce_entries) - config_entry_free_all(ce->ce_entries); - safe_free(ce->ce_varname); - safe_free(ce->ce_vardata); - if (ce->ce_cond) - preprocessor_cc_free_list(ce->ce_cond); + if (ce->items) + config_entry_free_all(ce->items); + safe_free(ce->name); + safe_free(ce->value); + if (ce->conditional_config) + preprocessor_cc_free_list(ce->conditional_config); safe_free(ce); } -ConfigEntry *config_find_entry(ConfigEntry *ce, char *name) +ConfigEntry *config_find_entry(ConfigEntry *ce, const char *name) { ConfigEntry *cep; - for (cep = ce; cep; cep = cep->ce_next) - if (cep->ce_varname && !strcmp(cep->ce_varname, name)) + for (cep = ce; cep; cep = cep->next) + if (cep->name && !strcmp(cep->name, name)) break; return cep; } @@ -1501,8 +1402,7 @@ void config_error(FORMAT_STRING(const char *format), ...) va_end(ap); if ((ptr = strchr(buffer, '\n')) != NULL) *ptr = '\0'; - ircd_log(LOG_ERROR, "config error: %s", buffer); - sendto_realops("error: %s", buffer); + unreal_log_raw(ULOG_ERROR, "config", "CONFIG_ERROR_GENERIC", NULL, buffer); if (remote_rehash_client) sendnotice(remote_rehash_client, "error: %s", buffer); /* We cannot live with this */ @@ -1560,8 +1460,7 @@ void config_status(FORMAT_STRING(const char *format), ...) va_end(ap); if ((ptr = strchr(buffer, '\n')) != NULL) *ptr = '\0'; - ircd_log(LOG_ERROR, "%s", buffer); - sendto_realops("%s", buffer); + unreal_log_raw(ULOG_INFO, "config", "CONFIG_INFO_GENERIC", NULL, buffer); if (remote_rehash_client) sendnotice(remote_rehash_client, "%s", buffer); } @@ -1577,8 +1476,7 @@ void config_warn(FORMAT_STRING(const char *format), ...) va_end(ap); if ((ptr = strchr(buffer, '\n')) != NULL) *ptr = '\0'; - ircd_log(LOG_ERROR, "[warning] %s", buffer); - sendto_realops("[warning] %s", buffer); + unreal_log_raw(ULOG_WARNING, "config", "CONFIG_WARNING_GENERIC", NULL, buffer); if (remote_rehash_client) sendnotice(remote_rehash_client, "[warning] %s", buffer); } @@ -1593,50 +1491,38 @@ int config_test_openfile(ConfigEntry *cep, int flags, mode_t mode, const char *e { int fd; - if(!cep->ce_vardata) + if (!cep->value) { - if(fatal) + if (fatal) config_error("%s:%i: %s: : no file specified", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, + cep->file->filename, + cep->line_number, entry); else config_warn("%s:%i: %s: : no file specified", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, + cep->file->filename, + cep->line_number, entry); return 1; } /* There's not much checking that can be done for asynchronously downloaded files */ -#ifdef USE_LIBCURL - if(url_is_valid(cep->ce_vardata)) + if (url_is_valid(cep->value)) { - if(allow_url) + if (allow_url) return 0; /* but we can check if a URL is used wrongly :-) */ config_warn("%s:%i: %s: %s: URL used where not allowed", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - entry, cep->ce_vardata); - if(fatal) + cep->file->filename, + cep->line_number, + entry, cep->value); + if (fatal) return 1; else return 0; } -#else - if (strstr(cep->ce_vardata, "://")) - { - config_error("%s:%d: %s: UnrealIRCd was not compiled with remote includes support " - "so you cannot use URLs here.", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - entry); - return 1; - } -#endif /* USE_LIBCURL */ /* * Make sure that files are created with the correct mode. This is @@ -1645,25 +1531,25 @@ int config_test_openfile(ConfigEntry *cep, int flags, mode_t mode, const char *e * and that we deal with all of the bugs that come with complexity. * The only files we may be creating are the tunefile and pidfile so far. */ - if(flags & O_CREAT) - fd = open(cep->ce_vardata, flags, mode); + if (flags & O_CREAT) + fd = open(cep->value, flags, mode); else - fd = open(cep->ce_vardata, flags); - if(fd == -1) + fd = open(cep->value, flags); + if (fd == -1) { - if(fatal) + if (fatal) config_error("%s:%i: %s: %s: %s", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, + cep->file->filename, + cep->line_number, entry, - cep->ce_vardata, + cep->value, strerror(errno)); else config_warn("%s:%i: %s: %s: %s", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, + cep->file->filename, + cep->line_number, entry, - cep->ce_vardata, + cep->value, strerror(errno)); return 1; } @@ -1673,16 +1559,16 @@ int config_test_openfile(ConfigEntry *cep, int flags, mode_t mode, const char *e int config_is_blankorempty(ConfigEntry *cep, const char *block) { - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, block, - cep->ce_varname); + config_error_empty(cep->file->filename, cep->line_number, block, + cep->name); return 1; } return 0; } -ConfigCommand *config_binary_search(char *cmd) { +ConfigCommand *config_binary_search(const char *cmd) { int start = 0; int stop = ARRAY_SIZEOF(_ConfigCommands)-1; int mid; @@ -1702,7 +1588,8 @@ ConfigCommand *config_binary_search(char *cmd) { void free_iConf(Configuration *i) { - safe_free(i->dns_bindip); + FloodSettings *f, *f_next; + safe_free(i->link_bindip); safe_free(i->kline_address); safe_free(i->gline_address); @@ -1711,7 +1598,6 @@ void free_iConf(Configuration *i) safe_free(i->oper_auto_join_chans); safe_free(i->allow_user_stats); // allow_user_stats_ext is freed elsewhere - safe_free(i->egd_path); safe_free(i->static_quit); safe_free(i->static_part); free_tls_options(i->tls_options); @@ -1725,6 +1611,7 @@ void free_iConf(Configuration *i) safe_free(i->restrict_channelmodes); safe_free(i->restrict_extendedbans); safe_free(i->channel_command_prefix); + safe_free(i->level_on_join); safe_free(i->spamfilter_ban_reason); safe_free(i->spamfilter_virus_help_channel); // spamexcept is freed elsewhere @@ -1734,25 +1621,30 @@ void free_iConf(Configuration *i) safe_free(i->reject_message_unauthorized); safe_free(i->reject_message_kline); safe_free(i->reject_message_gline); - // network struct: - safe_free(i->network.x_ircnetwork); - safe_free(i->network.x_ircnet005); - safe_free(i->network.x_defserv); - safe_free(i->network.x_services_name); - safe_free(i->network.x_hidden_host); - safe_free(i->network.x_prefix_quit); - safe_free(i->network.x_helpchan); - safe_free(i->network.x_stats_server); - safe_free(i->network.x_sasl_server); + safe_free(i->network_name); + safe_free(i->network_name_005); + safe_free(i->default_server); + safe_free(i->services_name); + safe_free(i->cloak_prefix); + safe_free(i->prefix_quit); + safe_free(i->helpchan); + safe_free(i->stats_server); + safe_free(i->sasl_server); + // anti-flood: + for (f = i->floodsettings; f; f = f_next) + { + f_next = f->next; + free_floodsettings(f); + } + i->floodsettings = NULL; } -int config_test(); - void config_setdefaultsettings(Configuration *i) { char tmp[512]; - safe_strdup(i->oper_snomask, SNO_DEFOPER); + safe_strdup(i->oper_snomask, OPER_SNOMASKS); + i->server_notice_colors = 1; i->ident_read_timeout = 7; i->ident_connect_timeout = 3; i->ban_version_tkl_time = 86400; /* 1d */ @@ -1765,12 +1657,11 @@ void config_setdefaultsettings(Configuration *i) i->maxchannelsperuser = 10; i->maxdccallow = 10; safe_strdup(i->channel_command_prefix, "`!."); - conf_channelmodes("+nt", &i->modes_on_join, 0); i->conn_modes = set_usermode("+ixw"); i->check_target_nick_bans = 1; i->maxbans = 60; i->maxbanlength = 2048; - i->level_on_join = CHFL_CHANOP; + safe_strdup(i->level_on_join, "o"); i->watch_away_notification = 1; i->uhnames = 1; i->ping_cookie = 1; @@ -1783,10 +1674,10 @@ void config_setdefaultsettings(Configuration *i) i->kick_length = 307; i->quit_length = 307; safe_strdup(i->link_bindip, "*"); - safe_strdup(i->network.x_hidden_host, "Clk"); + safe_strdup(i->cloak_prefix, "Clk"); if (!ipv6_capable()) DISABLE_IPV6 = 1; - safe_strdup(i->network.x_prefix_quit, "Quit"); + safe_strdup(i->prefix_quit, "Quit"); i->max_unknown_connections_per_ip = 3; i->handshake_timeout = 30; i->sasl_timeout = 15; @@ -1807,6 +1698,7 @@ void config_setdefaultsettings(Configuration *i) 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 */ config_parse_flood_generic("10:15", i, "known-users", FLD_CONVERSATIONS); /* 10 users, new user every 15s */ + config_parse_flood_generic("180:750", i, "known-users", FLD_LAG_PENALTY); /* 180 bytes / 750 msec */ /* - 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 */ @@ -1814,8 +1706,9 @@ void config_setdefaultsettings(Configuration *i) 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 */ config_parse_flood_generic("4:15", i, "unknown-users", FLD_CONVERSATIONS); /* 4 users, new user every 15s */ + config_parse_flood_generic("90:1000", i, "unknown-users", FLD_LAG_PENALTY); /* 90 bytes / 1000 msec */ - /* SSL/TLS options */ + /* TLS options */ i->tls_options = safe_alloc(sizeof(TLSOptions)); snprintf(tmp, sizeof(tmp), "%s/tls/server.cert.pem", CONFDIR); safe_strdup(i->tls_options->certificate_file, tmp); @@ -1862,19 +1755,8 @@ void config_setdefaultsettings(Configuration *i) i->hide_idle_time = HIDE_IDLE_TIME_OPER_USERMODE; i->who_limit = 100; -} -static void make_default_logblock(void) -{ - ConfigItem_log *ca = safe_alloc(sizeof(ConfigItem_log)); - - config_status("No log { } block found -- logging everything to 'ircd.log'"); - - safe_strdup(ca->file, "ircd.log"); - convert_to_absolute_path(&ca->file, LOGDIR); - ca->flags |= LOG_CHGCMDS|LOG_CLIENT|LOG_ERROR|LOG_KILL|LOG_KLINE|LOG_OPER|LOG_OVERRIDE|LOG_SACMDS|LOG_SERVER|LOG_SPAMFILTER|LOG_TKL; - ca->logfd = -1; - AddListItem(ca, conf_log); + i->named_extended_bans = 1; } /** Similar to config_setdefaultsettings but this one is applied *AFTER* @@ -1886,13 +1768,20 @@ void postconf_defaults(void) TKL *tk; char *encoded; + if (!iConf.modes_on_join_set) + { + /* We could not do this in config_setdefaultsettings() + * because the channel mode modules were not initialized yet. + */ + conf_channelmodes("+nt", &iConf.modes_on_join); + } if (!iConf.plaintext_policy_user_message) { /* The message depends on whether it's reject or warn.. */ if (iConf.plaintext_policy_user == POLICY_DENY) - addmultiline(&iConf.plaintext_policy_user_message, "Insecure connection. Please reconnect using SSL/TLS."); + addmultiline(&iConf.plaintext_policy_user_message, "Insecure connection. Please reconnect using TLS."); else if (iConf.plaintext_policy_user == POLICY_WARN) - addmultiline(&iConf.plaintext_policy_user_message, "WARNING: Insecure connection. Please consider using SSL/TLS."); + addmultiline(&iConf.plaintext_policy_user_message, "WARNING: Insecure connection. Please consider using TLS."); } if (!iConf.plaintext_policy_oper_message) @@ -1900,76 +1789,32 @@ void postconf_defaults(void) /* The message depends on whether it's reject or warn.. */ if (iConf.plaintext_policy_oper == POLICY_DENY) { - addmultiline(&iConf.plaintext_policy_oper_message, "You need to use a secure connection (SSL/TLS) in order to /OPER."); + addmultiline(&iConf.plaintext_policy_oper_message, "You need to use a secure connection (TLS) in order to /OPER."); addmultiline(&iConf.plaintext_policy_oper_message, "See https://www.unrealircd.org/docs/FAQ#oper-requires-tls"); } else if (iConf.plaintext_policy_oper == POLICY_WARN) - addmultiline(&iConf.plaintext_policy_oper_message, "WARNING: You /OPER'ed up from an insecure connection. Please consider using SSL/TLS."); + addmultiline(&iConf.plaintext_policy_oper_message, "WARNING: You /OPER'ed up from an insecure connection. Please consider using TLS."); } if (!iConf.outdated_tls_policy_user_message) { /* The message depends on whether it's reject or warn.. */ if (iConf.outdated_tls_policy_user == POLICY_DENY) - safe_strdup(iConf.outdated_tls_policy_user_message, "Your IRC client is using an outdated SSL/TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); + safe_strdup(iConf.outdated_tls_policy_user_message, "Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); else if (iConf.outdated_tls_policy_user == POLICY_WARN) - safe_strdup(iConf.outdated_tls_policy_user_message, "WARNING: Your IRC client is using an outdated SSL/TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); + safe_strdup(iConf.outdated_tls_policy_user_message, "WARNING: Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); } if (!iConf.outdated_tls_policy_oper_message) { /* The message depends on whether it's reject or warn.. */ if (iConf.outdated_tls_policy_oper == POLICY_DENY) - safe_strdup(iConf.outdated_tls_policy_oper_message, "Your IRC client is using an outdated SSL/TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); + safe_strdup(iConf.outdated_tls_policy_oper_message, "Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); else if (iConf.outdated_tls_policy_oper == POLICY_WARN) - safe_strdup(iConf.outdated_tls_policy_oper_message, "WARNING: Your IRC client is using an outdated SSL/TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); + safe_strdup(iConf.outdated_tls_policy_oper_message, "WARNING: Your IRC client is using an outdated TLS protocol or ciphersuite ($protocol-$cipher). Please upgrade your IRC client."); } - /* We got a chicken-and-egg problem here.. antries added without reason or ban-time - * field should use the config default (set::spamfilter::ban-reason/ban-time) but - * this isn't (or might not) be known yet when parsing spamfilter entries.. - * so we do a VERY UGLY mass replace here.. unless someone else has a better idea. - */ - - encoded = unreal_encodespace(SPAMFILTER_BAN_REASON); - if (!encoded) - abort(); /* hack to trace 'impossible' bug... */ - // FIXME: remove this stuff with ~server~, why not just use -config- - // which is more meaningful. - for (tk = tklines[tkl_hash('q')]; tk; tk = tk->next) - { - if (tk->type != TKL_NAME) - continue; - if (!tk->set_by) - { - if (me.name[0] != '\0') - safe_strdup(tk->set_by, me.name); - else - safe_strdup(tk->set_by, conf_me->name ? conf_me->name : "~server~"); - } - } - - for (tk = tklines[tkl_hash('f')]; tk; tk = tk->next) - { - if (tk->type != TKL_SPAMF) - continue; /* global entry or something else.. */ - if (!strcmp(tk->ptr.spamfilter->tkl_reason, "")) - { - safe_strdup(tk->ptr.spamfilter->tkl_reason, encoded); - tk->ptr.spamfilter->tkl_duration = SPAMFILTER_BAN_TIME; - } - /* This one is even more ugly, but our config crap is VERY confusing :[ */ - if (!tk->set_by) - { - if (me.name[0] != '\0') - safe_strdup(tk->set_by, me.name); - else - safe_strdup(tk->set_by, conf_me->name ? conf_me->name : "~server~"); - } - } - - if (!conf_log) - make_default_logblock(); + postconf_defaults_log_block(); } void postconf_fixes(void) @@ -2007,7 +1852,7 @@ RealCommand *cmptr; * has been read and almost all values have been set. This is to deal with * things like adding a default log { } block if there is none and that kind * of things. - * This function is called by init_conf(), both on boot and on rehash. + * This function is called by config_test(), both on boot and on rehash. */ void postconf(void) { @@ -2016,6 +1861,11 @@ void postconf(void) do_weird_shun_stuff(); isupport_init(); /* for all the 005 values that changed.. */ tls_check_expiry(NULL); + +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + if (loop.rehashing) + reinit_tls(); +#endif } int isanyserverlinked(void) @@ -2044,41 +1894,11 @@ void applymeblock(void) strlcpy(me.id, conf_me->sid, sizeof(me.id)); } -void upgrade_conf_to_34(void) -{ - config_error("******************************************************************"); - config_error("This *seems* an UnrealIRCd 3.2.x configuration file."); - -#ifdef _WIN32 - if (!IsService) - config_error("In next screen you will be prompted to automatically upgrade the configuration file(s)."); - else - { - config_error("We offer a configuration file converter to convert 3.2.x conf's to 4.x, however this " - "is not available when running as a service. If you want to use it, make UnrealIRCd " - "run in GUI mode by running 'unreal uninstall'. Then start UnrealIRCd.exe and when " - "it prompts you to convert the configuration click 'Yes'. Check if UnrealIRCd boots properly. " - "Once everything is looking good you can run 'unreal install' to make UnrealIRCd run " - "as a service again."); /* TODO: make this unnecessary :D */ - } -#else - config_error("To upgrade it to the new 4.x format, run: ./unrealircd upgrade-conf"); -#endif - - config_error("******************************************************************"); - /* TODO: win32 may require a different error */ -} - -/** Reset config tests (before running the config test) */ -void config_test_reset(void) -{ -} - /** Run config test and all post config tests. */ int config_test_all(void) { - if ((config_test() < 0) || (callbacks_check() < 0) || (efunctions_check() < 0) || - reloadable_perm_module_unloaded() || !tls_tests()) + if ((config_test_blocks() < 0) || (callbacks_check() < 0) || (efunctions_check() < 0) || + reloadable_perm_module_unloaded() || !tls_tests() || !log_tests()) { return 0; } @@ -2101,19 +1921,19 @@ int config_loadmodules(void) int fatal_ret = 0, ret; - for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) + for (cfptr = conf; cfptr; cfptr = cfptr->next) { if (config_verbose > 1) - config_status("Testing %s", cfptr->cf_filename); - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) + config_status("Testing %s", cfptr->filename); + for (ce = cfptr->items; ce; ce = ce->next) { - if (!strcmp(ce->ce_varname, "loadmodule")) + if (!strcmp(ce->name, "loadmodule")) { - if (ce->ce_cond) + if (ce->conditional_config) { config_error("%s:%d: Currently you cannot have a 'loadmodule' statement " "within an @if block, sorry.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 0; } ret = _conf_loadmodule(cfptr, ce); @@ -2139,262 +1959,299 @@ int config_loadmodules(void) return 1; /* SUCCESS */ } -int init_conf(char *rootconf, int rehash) +/** Reject the configuration load. + * This is called both from boot and from rehash. + */ +void config_load_failed(void) { - char *old_pid_file = NULL; + if (conf) + unreal_log(ULOG_ERROR, "config", "CONFIG_NOT_LOADED", NULL, "IRCd configuration failed to load"); + Unload_all_testing_modules(); + free_all_config_resources(); + config_free(conf); + conf = NULL; + free_iConf(&tempiConf); +#ifdef _WIN32 + if (!loop.rehashing) + win_error(); /* GUI popup */ +#endif +} + +int config_read_start(void) +{ + int ret; config_status("Loading IRCd configuration.."); + loop.config_load_failed = 0; + if (conf) { config_error("%s:%i - Someone forgot to clean up", __FILE__, __LINE__); return -1; } + + /* We set this to 1 because otherwise we may call rehash_internal() + * already from config_read_file() which is too soon (race). + */ + loop.rehash_download_busy = 1; + add_config_resource(configfile, RESOURCE_INCLUDE, NULL); + ret = config_read_file(configfile, configfile); + loop.rehash_download_busy = 0; + if (ret < 0) + { + config_load_failed(); + return -1; + } + return 1; +} + +int is_config_read_finished(void) +{ + ConfigResource *rs; + + if (loop.rehash_download_busy) + return 0; + + for (rs = config_resources; rs; rs = rs->next) + { + if (rs->type & RESOURCE_DLQUEUED) + { + //config_status("Waiting for %s...", rs->url); + return 0; + } + } + + return 1; +} + +int config_test(void) +{ + char *old_pid_file = NULL; + + if (loop.config_load_failed) + { + /* An error was already printed to the user. + * This happens in case of a failed loaded remote URL + */ + config_load_failed(); + return -1; + } + + config_status("Testing IRCd configuration.."); + memset(&tempiConf, 0, sizeof(iConf)); memset(&settings, 0, sizeof(settings)); memset(&requiredstuff, 0, sizeof(requiredstuff)); memset(&nicklengths, 0, sizeof(nicklengths)); config_setdefaultsettings(&tempiConf); clicap_pre_rehash(); + log_pre_rehash(); free_config_defines(); - /* - * the rootconf must be listed in the conf_include for include - * recursion prevention code and sanity checking code to be - * made happy :-). Think of it as us implicitly making an - * in-memory config file that looks like: - * - * include "unrealircd.conf"; - */ - add_include(rootconf, "[thin air]", -1); - if ((load_conf(rootconf, rootconf) > 0) && config_loadmodules()) - { - preprocessor_resolve_conditionals_all(PREPROCESSOR_PHASE_MODULE); - config_test_reset(); - if (!config_test_all()) - { - config_error("IRCd configuration failed to pass testing"); -#ifdef _WIN32 - if (!rehash) - win_error(); -#endif - Unload_all_testing_modules(); - unload_notloaded_includes(); - config_free(conf); - conf = NULL; - free_iConf(&tempiConf); - return -1; - } - callbacks_switchover(); - efunctions_switchover(); - set_targmax_defaults(); - set_security_group_defaults(); - if (rehash) - { - Hook *h; - safe_strdup(old_pid_file, conf_files->pid_file); - unrealdns_delasyncconnects(); - config_rehash(); - Unload_all_loaded_modules(); - /* Notify permanent modules of the rehash */ - for (h = Hooks[HOOKTYPE_REHASH]; h; h = h->next) - { - if (!h->owner) - continue; - if (!(h->owner->options & MOD_OPT_PERM)) - continue; - (*(h->func.intfunc))(); - } - unload_loaded_includes(); - } - load_includes(); - Init_all_testing_modules(); - if (config_run() < 0) - { - config_error("Bad case of config errors. Server will now die. This really shouldn't happen"); -#ifdef _WIN32 - if (!rehash) - win_error(); -#endif - abort(); - } - applymeblock(); - if (old_pid_file && strcmp(old_pid_file, conf_files->pid_file)) - { - sendto_ops("pidfile is being rewritten to %s, please delete %s", - conf_files->pid_file, - old_pid_file); - write_pidfile(); - } - safe_free(old_pid_file); - } - else + if (!config_loadmodules()) { - config_error("IRCd configuration failed to load"); - Unload_all_testing_modules(); - unload_notloaded_includes(); - config_free(conf); - conf = NULL; - free_iConf(&tempiConf); -#ifdef _WIN32 - if (!rehash) - win_error(); -#endif + config_load_failed(); return -1; } + + preprocessor_resolve_conditionals_all(PREPROCESSOR_PHASE_MODULE); + + if (!config_test_all()) + { + config_error("IRCd configuration failed to pass testing"); + config_load_failed(); + return -1; + } + callbacks_switchover(); + efunctions_switchover(); + set_targmax_defaults(); + set_security_group_defaults(); + if (loop.rehashing) + { + Hook *h; + safe_strdup(old_pid_file, conf_files->pid_file); + unrealdns_delasyncconnects(); + config_rehash(); + Unload_all_loaded_modules(); + + /* Notify permanent modules of the rehash */ + for (h = Hooks[HOOKTYPE_REHASH]; h; h = h->next) + { + if (!h->owner) + continue; + if (!(h->owner->options & MOD_OPT_PERM)) + continue; + (*(h->func.intfunc))(); + } + } + config_pre_run_log(); + + Init_all_testing_modules(); + + if (config_run_blocks() < 0) + { + config_error("Bad case of config errors. Server will now die. This really shouldn't happen"); +#ifdef _WIN32 + if (!loop.rehashing) + win_error(); +#endif + abort(); + } + + applymeblock(); + + if (old_pid_file && strcmp(old_pid_file, conf_files->pid_file)) + { + write_pidfile(); + unlink(old_pid_file); + } + safe_free(old_pid_file); + config_free(conf); conf = NULL; - if (rehash) + if (loop.rehashing) { module_loadall(); - RunHook0(HOOKTYPE_REHASH_COMPLETE); + RunHook(HOOKTYPE_REHASH_COMPLETE); } postconf(); - config_status("Configuration loaded."); + unreal_log(ULOG_INFO, "config", "CONFIG_LOADED", NULL, "Configuration loaded"); clicap_post_rehash(); unload_all_unused_mtag_handlers(); return 0; } +void config_parse_and_queue_urls(ConfigEntry *ce) +{ + for (; ce; ce = ce->next) + { + if (loop.config_load_failed) + break; + if (ce->name && !strcmp(ce->name, "include")) + continue; /* handled elsewhere */ + if (ce->value && !ce->escaped && url_is_valid(ce->value)) + add_config_resource(ce->value, 0, ce); + if (ce->items) + config_parse_and_queue_urls(ce->items); + } +} + /** - * Processes filename as part of the IRCd's configuration. + * Read configuration file into ConfigEntry items and add it to the 'conf' + * list. This checks the file for parse errors, but doesn't do much + * otherwise. Only: module blacklist checking and checking for "include" + * items to see if we need to read and parse more configuration files + * that are included from this one. * - * One _must_ call add_include() or add_remote_include() before - * calling load_conf(). This way, include recursion may be detected - * and reported to the user as an error instead of causing the IRCd to - * hang in an infinite recursion, eat up memory, and eventually - * overflow its stack ;-). (reported by warg). - * - * This function will set INCLUDE_USED on the config_include list - * entry if the config file loaded without error. + * One _must_ call add_config_resource() before calling config_read_file(). + * This way, include recursion may be detected and reported to the user + * as an error instead of causing the IRCd to hang in an infinite + * recursion, eat up memory, and eventually overflow its stack ;-). * * @param filename the file where the conf may be read from - * @param original_path the path or URL used to refer to this file. + * @param display_name The path or URL used to refer to this file. * (mostly to support remote includes' URIs for recursive include detection). * @return 1 on success, a negative number on error */ -int load_conf(char *filename, const char *original_path) +int config_read_file(const char *filename, const char *display_name) { ConfigFile *cfptr, *cfptr2, **cfptr3; ConfigEntry *ce; - ConfigItem_include *inc, *my_inc; + ConfigResource *rs; int ret; int counter; if (config_verbose > 0) config_status("Loading config file %s ..", filename); - need_34_upgrade = 0; need_operclass_permissions_upgrade = 0; - /* - * Check if we're accidentally including a file a second + /* Check if we're accidentally including a file a second * time. We should expect to find one entry in this list: the * entry for our current file. + * Note that no user should be able to trigger this, this + * can only happen if we have buggy code somewhere. */ counter = 0; - my_inc = NULL; - for (inc = conf_include; inc; inc = inc->next) + for (rs = config_resources; rs; rs = rs->next) { - /* - * ignore files which were part of a _previous_ - * successful rehash. - */ - if (!(inc->flag.type & INCLUDE_NOTLOADED)) - continue; - - if (!counter) - my_inc = inc; - - if (!strcmp(filename, inc->file)) - { - counter ++; - continue; - } -#ifdef _WIN32 - if (!strcasecmp(filename, inc->file)) - { - counter ++; - continue; - } +#ifndef _WIN32 + if (rs->file && !strcmp(filename, rs->file)) +#else + if (rs->file && !strcasecmp(filename, rs->file)) #endif -#ifdef USE_LIBCURL - if (inc->url && !strcmp(original_path, inc->url)) { counter ++; continue; } -#endif + if (rs->url && !strcmp(display_name, rs->url)) + { + counter ++; + continue; + } } - if (counter < 1 || !my_inc) + if (counter > 1) { - /* - * The following is simply for debugging/[sanity - * checking]. To make sure that functions call - * add_include() or add_remote_include() before - * calling us. - */ - config_error("I don't have a record for %s being included." - " Perhaps someone forgot to call add_include()?", - filename); - abort(); - } - if (counter > 1 || my_inc->flag.type & INCLUDE_USED) - { - config_error("%s:%d:include: Config file %s has been loaded before %d time." - " You may include each file only once.", - my_inc->included_from, my_inc->included_from_line, - filename, counter - 1); + unreal_log(ULOG_ERROR, "config", "CONFIG_BUG_DUPLICATE_RESOURCE", NULL, + "[BUG] Config file $file has been loaded $counter times. " + "This should not happen. Someone forgot to call " + "add_config_resource() or check its return value!", + log_data_string("file", filename), + log_data_integer("counter", counter)); return -1; } /* end include recursion checking code */ - if ((cfptr = config_load(filename, NULL))) + if ((cfptr = config_load(filename, display_name))) { - for (cfptr3 = &conf, cfptr2 = conf; cfptr2; cfptr2 = cfptr2->cf_next) - cfptr3 = &cfptr2->cf_next; + for (cfptr3 = &conf, cfptr2 = conf; cfptr2; cfptr2 = cfptr2->next) + cfptr3 = &cfptr2->next; *cfptr3 = cfptr; if (config_verbose > 1) config_status("Loading module blacklist in %s", filename); - preprocessor_resolve_conditionals_ce(&cfptr->cf_entries, PREPROCESSOR_PHASE_INITIAL); + preprocessor_resolve_conditionals_ce(&cfptr->items, PREPROCESSOR_PHASE_INITIAL); - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) - if (!strcmp(ce->ce_varname, "blacklist-module")) + for (ce = cfptr->items; ce; ce = ce->next) + if (!strcmp(ce->name, "blacklist-module")) _test_blacklist_module(cfptr, ce); - /* Load modules */ - if (config_verbose > 1) - config_status("Loading modules in %s", filename); - if (need_34_upgrade) - upgrade_conf_to_34(); + /* Load urls */ + config_parse_and_queue_urls(cfptr->items); + + if(loop.config_load_failed) /* something bad happened while processing urls */ + return -1; /* Load includes */ if (config_verbose > 1) config_status("Searching through %s for include files..", filename); - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) - if (!strcmp(ce->ce_varname, "include")) + + for (ce = cfptr->items; ce; ce = ce->next) + { + if (!strcmp(ce->name, "include")) { - if (ce->ce_cond) + if (ce->conditional_config) { config_error("%s:%d: Currently you cannot have an 'include' statement " "within an @if block, sorry. However, you CAN do it the other " "way around, that is: put the @if within the included file itself.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return -1; } ret = _conf_include(cfptr, ce); - if (need_34_upgrade) - upgrade_conf_to_34(); if (ret < 0) return ret; } - my_inc->flag.type |= INCLUDE_USED; + } return 1; } else { - config_error("Could not load config file %s", filename); + unreal_log(ULOG_ERROR, "config", "CONFIG_LOAD_FILE_FAILED", NULL, + "Could not load configuration file: $resource", + log_data_string("resource", display_name), + log_data_string("filename", filename)); #ifdef _WIN32 if (!strcmp(filename, "conf/unrealircd.conf")) { @@ -2445,13 +2302,12 @@ void remove_config_tkls(void) } } -void config_rehash() +void config_rehash() { ConfigItem_oper *oper_ptr; ConfigItem_class *class_ptr; ConfigItem_ulines *uline_ptr; ConfigItem_allow *allow_ptr; - ConfigItem_except *except_ptr; ConfigItem_ban *ban_ptr; ConfigItem_link *link_ptr; ConfigItem_listen *listen_ptr; @@ -2462,7 +2318,6 @@ void config_rehash() ConfigItem_allow_channel *allow_channel_ptr; ConfigItem_admin *admin_ptr; ConfigItem_deny_version *deny_version_ptr; - ConfigItem_log *log_ptr; ConfigItem_alias *alias_ptr; ConfigItem_help *help_ptr; ConfigItem_offchans *of_ptr; @@ -2507,13 +2362,10 @@ void config_rehash() next = (ListStruct *)link_ptr->next; if (link_ptr->refcount == 0) { - Debug((DEBUG_ERROR, "s_conf: deleting block %s (refcount 0)", link_ptr->servername)); delete_linkblock(link_ptr); } else { - Debug((DEBUG_ERROR, "s_conf: marking block %s (refcount %d) as temporary", - link_ptr->servername, link_ptr->refcount)); link_ptr->flag.temporary = 1; } } @@ -2540,19 +2392,11 @@ void config_rehash() for (allow_ptr = conf_allow; allow_ptr; allow_ptr = (ConfigItem_allow *) next) { next = (ListStruct *)allow_ptr->next; - safe_free(allow_ptr->ip); - safe_free(allow_ptr->hostname); + unreal_delete_masks(allow_ptr->mask); Auth_FreeAuthConfig(allow_ptr->auth); DelListItem(allow_ptr, conf_allow); safe_free(allow_ptr); } - for (except_ptr = conf_except; except_ptr; except_ptr = (ConfigItem_except *) next) - { - next = (ListStruct *)except_ptr->next; - safe_free(except_ptr->mask); - DelListItem(except_ptr, conf_except); - safe_free(except_ptr); - } /* Free ban realname { }, ban server { } and ban version { } */ for (ban_ptr = conf_ban; ban_ptr; ban_ptr = (ConfigItem_ban *) next) { @@ -2614,7 +2458,7 @@ void config_rehash() for (deny_link_ptr = conf_deny_link; deny_link_ptr; deny_link_ptr = (ConfigItem_deny_link *) next) { next = (ListStruct *)deny_link_ptr->next; safe_free(deny_link_ptr->prettyrule); - safe_free(deny_link_ptr->mask); + unreal_delete_masks(deny_link_ptr->mask); crule_free(&deny_link_ptr->rule); DelListItem(deny_link_ptr, conf_deny_link); safe_free(deny_link_ptr); @@ -2657,14 +2501,6 @@ void config_rehash() conf_drpass->dieauth = NULL; safe_free(conf_drpass); } - for (log_ptr = conf_log; log_ptr; log_ptr = (ConfigItem_log *)next) { - next = (ListStruct *)log_ptr->next; - if (log_ptr->logfd != -1) - fd_close(log_ptr->logfd); - safe_free(log_ptr->file); - DelListItem(log_ptr, conf_log); - safe_free(log_ptr); - } for (alias_ptr = conf_alias; alias_ptr; alias_ptr = (ConfigItem_alias *)next) { RealCommand *cmptr = find_command(alias_ptr->alias, 0); ConfigItem_alias_format *fmt; @@ -2797,7 +2633,16 @@ int config_post_test() return errors; } -int config_run() +/** Make the "read" config the "live" config */ +void config_switchover(void) +{ + free_iConf(&iConf); + memcpy(&iConf, &tempiConf, sizeof(iConf)); + memset(&tempiConf, 0, sizeof(tempiConf)); + log_blocks_switchover(); +} + +int config_run_blocks() { ConfigEntry *ce; ConfigFile *cfptr; @@ -2807,13 +2652,13 @@ int config_run() ConfigItem_allow *allow; /* Stage 1: set block first */ - for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) + for (cfptr = conf; cfptr; cfptr = cfptr->next) { if (config_verbose > 1) - config_status("Running %s", cfptr->cf_filename); - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) + config_status("Running %s", cfptr->filename); + for (ce = cfptr->items; ce; ce = ce->next) { - if (!strcmp(ce->ce_varname, "set")) + if (!strcmp(ce->name, "set")) { if (_conf_set(cfptr, ce) < 0) errors++; @@ -2822,13 +2667,13 @@ int config_run() } /* Stage 2: now class blocks */ - for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) + for (cfptr = conf; cfptr; cfptr = cfptr->next) { if (config_verbose > 1) - config_status("Running %s", cfptr->cf_filename); - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) + config_status("Running %s", cfptr->filename); + for (ce = cfptr->items; ce; ce = ce->next) { - if (!strcmp(ce->ce_varname, "class")) + if (!strcmp(ce->name, "class")) { if (_conf_class(cfptr, ce) < 0) errors++; @@ -2837,23 +2682,23 @@ int config_run() } /* Stage 3: now all the rest */ - for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) + for (cfptr = conf; cfptr; cfptr = cfptr->next) { if (config_verbose > 1) - config_status("Running %s", cfptr->cf_filename); - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) + 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() (secret). + * or via config_test_blocks() (secret). */ - if (!strcmp(ce->ce_varname, "set") || - !strcmp(ce->ce_varname, "class") || - !strcmp(ce->ce_varname, "secret")) + if (!strcmp(ce->name, "set") || + !strcmp(ce->name, "class") || + !strcmp(ce->name, "secret")) { continue; } - if ((cc = config_binary_search(ce->ce_varname))) { + if ((cc = config_binary_search(ce->name))) { if ((cc->conffunc) && (cc->conffunc(cfptr, ce) < 0)) errors++; } @@ -2870,31 +2715,15 @@ int config_run() } } - /* - * transfer default values from set::ipv6_clones_mask into - * each individual allow block. If other similar things like - * this stack up here, perhaps this shoul be moved to another - * function. - */ - for(allow = conf_allow; allow; allow = allow->next) - if(!allow->ipv6_clone_mask) - allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask; - - /* ^^^ TODO: due to the two-stage model now we can do it in conf_allow again - * and remove it here. - */ - close_unbound_listeners(); listen_cleanup(); close_unbound_listeners(); loop.do_bancheck = 1; - free_iConf(&iConf); - memcpy(&iConf, &tempiConf, sizeof(iConf)); - memset(&tempiConf, 0, sizeof(tempiConf)); + config_switchover(); update_throttling_timer_settings(); /* initialize conf_files with defaults if the block isn't set: */ - if(!conf_files) + if (!conf_files) _conf_files(NULL, NULL); if (errors > 0) @@ -2905,26 +2734,7 @@ int config_run() } -NameValue *config_binary_flags_search(NameValue *table, char *cmd, int size) { - int start = 0; - int stop = size-1; - int mid; - while (start <= stop) { - mid = (start+stop)/2; - - if (smycmp(cmd,table[mid].name) < 0) { - stop = mid-1; - } - else if (strcmp(cmd,table[mid].name) == 0) { - return &(table[mid]); - } - else - start = mid+1; - } - return NULL; -} - -int config_test() +int config_test_blocks() { ConfigEntry *ce; ConfigFile *cfptr; @@ -2932,16 +2742,29 @@ int config_test() int errors = 0; Hook *h; - need_34_upgrade = 0; + invalid_snomasks_encountered = 0; - for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) + /* First, all the log { } blocks everywhere */ + for (cfptr = conf; cfptr; cfptr = cfptr->next) { if (config_verbose > 1) - config_status("Testing %s", cfptr->cf_filename); - /* First test and run the secret { } blocks */ - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) + config_status("Testing %s", cfptr->filename); + /* First test and run the log { } blocks */ + for (ce = cfptr->items; ce; ce = ce->next) { - if (!strcmp(ce->ce_varname, "secret")) + 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")) { int n = _test_secret(cfptr, ce); errors += n; @@ -2950,21 +2773,22 @@ int config_test() } } /* First test the set { } block */ - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) + for (ce = cfptr->items; ce; ce = ce->next) { - if (!strcmp(ce->ce_varname, "set")) + if (!strcmp(ce->name, "set")) errors += _test_set(cfptr, ce); } /* Now test all the rest */ - for (ce = cfptr->cf_entries; ce; ce = ce->ce_next) + for (ce = cfptr->items; ce; ce = ce->next) { /* These are already processed, so skip them here.. */ - if (!strcmp(ce->ce_varname, "secret") || - !strcmp(ce->ce_varname, "set")) + if (!strcmp(ce->name, "secret") || + !strcmp(ce->name, "set") || + !strcmp(ce->name, "log")) { continue; } - if ((cc = config_binary_search(ce->ce_varname))) { + if ((cc = config_binary_search(ce->name))) { if (cc->testfunc) errors += (cc->testfunc(cfptr, ce)); } @@ -3003,10 +2827,10 @@ int config_test() if (!used) { config_error("%s:%i: unknown directive %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_varname); + ce->file->filename, ce->line_number, + ce->name); errors++; - if (strchr(ce->ce_varname, ':')) + if (strchr(ce->name, ':')) { config_error("You cannot use :: in a directive, you have to write them out. " "For example 'set::auto-join #something' needs to be written as: " @@ -3023,10 +2847,12 @@ int config_test() config_error("%i errors encountered", errors); } - if (need_34_upgrade) + if (invalid_snomasks_encountered) { - upgrade_conf_to_34(); + config_error("It seems your set::snomask-on-oper and/or oper::snomask needs to be updated. Are you perhaps upgrading from an older version to UnrealIRCd 6?"); + config_error("See https://www.unrealircd.org/docs/Upgrading_from_5.x#Update_your_snomasks"); } + return (errors > 0 ? -1 : 1); } @@ -3034,7 +2860,7 @@ int config_test() * Service functions */ -ConfigItem_alias *find_alias(char *name) +ConfigItem_alias *find_alias(const char *name) { ConfigItem_alias *e; @@ -3049,7 +2875,7 @@ ConfigItem_alias *find_alias(char *name) return NULL; } -ConfigItem_class *find_class(char *name) +ConfigItem_class *find_class(const char *name) { ConfigItem_class *e; @@ -3065,7 +2891,7 @@ ConfigItem_class *find_class(char *name) } -ConfigItem_oper *find_oper(char *name) +ConfigItem_oper *find_oper(const char *name) { ConfigItem_oper *e; @@ -3080,7 +2906,7 @@ ConfigItem_oper *find_oper(char *name) return NULL; } -ConfigItem_operclass *find_operclass(char *name) +ConfigItem_operclass *find_operclass(const char *name) { ConfigItem_operclass *e; @@ -3095,7 +2921,7 @@ ConfigItem_operclass *find_operclass(char *name) return NULL; } -int count_oper_sessions(char *name) +int count_oper_sessions(const char *name) { int count = 0; Client *client; @@ -3109,7 +2935,7 @@ int count_oper_sessions(char *name) return count; } -ConfigItem_listen *find_listen(char *ipmask, int port, int ipv6) +ConfigItem_listen *find_listen(const char *ipmask, int port, int ipv6) { ConfigItem_listen *e; @@ -3126,7 +2952,7 @@ ConfigItem_listen *find_listen(char *ipmask, int port, int ipv6) /** Find an SNI match. * @param name The hostname to look for (eg: irc.xyz.com). */ -ConfigItem_sni *find_sni(char *name) +ConfigItem_sni *find_sni(const char *name) { ConfigItem_sni *e; @@ -3141,7 +2967,7 @@ ConfigItem_sni *find_sni(char *name) return NULL; } -ConfigItem_ulines *find_uline(char *host) +ConfigItem_ulines *find_uline(const char *host) { ConfigItem_ulines *ulines; @@ -3157,28 +2983,13 @@ ConfigItem_ulines *find_uline(char *host) } -ConfigItem_except *find_except(Client *client, short type) -{ - ConfigItem_except *excepts; - - for(excepts = conf_except; excepts; excepts = excepts->next) - { - if (excepts->flag.type == type) - { - if (match_user(excepts->mask, client, MATCH_CHECK_REAL)) - return excepts; - } - } - return NULL; -} - ConfigItem_tld *find_tld(Client *client) { ConfigItem_tld *tld; for (tld = conf_tld; tld; tld = tld->next) { - if (match_user(tld->mask, client, MATCH_CHECK_REAL)) + if (unreal_mask_match(client, tld->mask)) { if ((tld->options & TLD_TLS) && !IsSecureConnect(client)) continue; @@ -3192,7 +3003,7 @@ ConfigItem_tld *find_tld(Client *client) } -ConfigItem_link *find_link(char *servername, Client *client) +ConfigItem_link *find_link(const char *servername, Client *client) { ConfigItem_link *link; @@ -3209,7 +3020,7 @@ ConfigItem_link *find_link(char *servername, Client *client) /** Find a ban of type CONF_BAN_*, which is currently only * CONF_BAN_SERVER, CONF_BAN_VERSION and CONF_BAN_REALNAME */ -ConfigItem_ban *find_ban(Client *client, char *host, short type) +ConfigItem_ban *find_ban(Client *client, const char *host, short type) { ConfigItem_ban *ban; @@ -3233,7 +3044,7 @@ ConfigItem_ban *find_ban(Client *client, char *host, short type) * CONF_BAN_SERVER, CONF_BAN_VERSION and CONF_BAN_REALNAME * This is the extended version, only used by cmd_svsnline. */ -ConfigItem_ban *find_banEx(Client *client, char *host, short type, short type2) +ConfigItem_ban *find_banEx(Client *client, const char *host, short type, short type2) { ConfigItem_ban *ban; @@ -3253,7 +3064,7 @@ ConfigItem_ban *find_banEx(Client *client, char *host, short type, short type2) return NULL; } -ConfigItem_vhost *find_vhost(char *name) +ConfigItem_vhost *find_vhost(const char *name) { ConfigItem_vhost *vhost; @@ -3268,7 +3079,7 @@ ConfigItem_vhost *find_vhost(char *name) /** returns NULL if allowed and struct if denied */ -ConfigItem_deny_channel *find_channel_allowed(Client *client, char *name) +ConfigItem_deny_channel *find_channel_allowed(Client *client, const char *name) { ConfigItem_deny_channel *dchannel; ConfigItem_allow_channel *achannel; @@ -3313,29 +3124,33 @@ void init_dynconf(void) memset(&tempiConf, 0, sizeof(iConf)); } -char *pretty_time_val(long timeval) +const char *pretty_time_val_r(char *buf, size_t buflen, long timeval) { - static char buf[512]; - if (timeval == 0) return "0"; buf[0] = 0; if (timeval/86400) - snprintf(buf, sizeof(buf), "%ldd", timeval/86400); + snprintf(buf, buflen, "%ldd", timeval/86400); if ((timeval/3600) % 24) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%ldh", (timeval/3600)%24); + snprintf(buf+strlen(buf), buflen-strlen(buf), "%ldh", (timeval/3600)%24); if ((timeval/60)%60) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%ldm", (timeval/60)%60); + snprintf(buf+strlen(buf), buflen-strlen(buf), "%ldm", (timeval/60)%60); if ((timeval%60)) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%lds", timeval%60); + snprintf(buf+strlen(buf), buflen-strlen(buf), "%lds", timeval%60); return buf; } +const char *pretty_time_val(long timeval) +{ + static char buf[512]; + return pretty_time_val_r(buf, sizeof(buf), timeval); +} + /* This converts a relative path to an absolute path, but only if necessary. */ -void convert_to_absolute_path(char **path, char *reldir) +void convert_to_absolute_path(char **path, const char *reldir) { char *s; @@ -3345,6 +3160,11 @@ void convert_to_absolute_path(char **path, char *reldir) if (strstr(*path, "://")) return; /* URL: don't touch */ +#ifdef _WIN32 + if (!strncmp(*path, "cache/", 6)) + return; /* downloaded from URL: don't touch (is only relative path on Windows) */ +#endif + if ((**path == '/') || (**path == '\\')) return; /* already absolute path */ @@ -3371,7 +3191,7 @@ char *convert_to_absolute_path_duplicate(char *path, char *reldir) * Actual config parser funcs */ -int _conf_include(ConfigFile *conf, ConfigEntry *ce) +int _conf_include(ConfigFile *conf, ConfigEntry *ce) { int ret = 0; #ifdef GLOBH @@ -3382,75 +3202,64 @@ int _conf_include(ConfigFile *conf, ConfigEntry *ce) WIN32_FIND_DATA FindData; char cPath[MAX_PATH], *cSlash = NULL, *path; #endif - if (!ce->ce_vardata) + if (!ce->value) { config_status("%s:%i: include: no filename given", - ce->ce_fileptr->cf_filename, - ce->ce_varlinenum); + ce->file->filename, + ce->line_number); return -1; } - if (!strcmp(ce->ce_vardata, "help.conf")) - need_34_upgrade = 1; + convert_to_absolute_path(&ce->value, CONFDIR); - convert_to_absolute_path(&ce->ce_vardata, CONFDIR); - -#ifdef USE_LIBCURL - if (url_is_valid(ce->ce_vardata)) - return remote_include(ce); -#else - if (strstr(ce->ce_vardata, "://")) + if (url_is_valid(ce->value)) { - config_error("%s:%d: URL specified: %s", - ce->ce_fileptr->cf_filename, - ce->ce_varlinenum, - ce->ce_vardata); - config_error("UnrealIRCd was not compiled with remote includes support " - "so you cannot use URLs. You are suggested to re-run ./Config " - "and answer YES to the question about remote includes."); - return -1; + add_config_resource(ce->value, RESOURCE_INCLUDE, ce); + return 0; } -#endif #if !defined(_WIN32) && !defined(_AMIGA) && !defined(OSXTIGER) && DEFAULT_PERMISSIONS != 0 - (void)chmod(ce->ce_vardata, DEFAULT_PERMISSIONS); + (void)chmod(ce->value, DEFAULT_PERMISSIONS); #endif #ifdef GLOBH #if defined(__OpenBSD__) && defined(GLOB_LIMIT) - glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK|GLOB_LIMIT, NULL, &files); + glob(ce->value, GLOB_NOSORT|GLOB_NOCHECK|GLOB_LIMIT, NULL, &files); #else - glob(ce->ce_vardata, GLOB_NOSORT|GLOB_NOCHECK, NULL, &files); + glob(ce->value, GLOB_NOSORT|GLOB_NOCHECK, NULL, &files); #endif if (!files.gl_pathc) { globfree(&files); config_status("%s:%i: include %s: invalid file given", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata); + ce->file->filename, ce->line_number, + ce->value); return -1; } - for (i = 0; i < files.gl_pathc; i++) { - add_include(files.gl_pathv[i], ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(files.gl_pathv[i], files.gl_pathv[i]); - if (ret < 0) + for (i = 0; i < files.gl_pathc; i++) + { + if (add_config_resource(files.gl_pathv[i], RESOURCE_INCLUDE, ce)) { - globfree(&files); - return ret; + ret = config_read_file(files.gl_pathv[i], files.gl_pathv[i]); + if (ret < 0) + { + globfree(&files); + return ret; + } } } globfree(&files); #elif defined(_WIN32) memset(cPath, 0, MAX_PATH); - if (strchr(ce->ce_vardata, '/') || strchr(ce->ce_vardata, '\\')) { - strlcpy(cPath,ce->ce_vardata,MAX_PATH); + if (strchr(ce->value, '/') || strchr(ce->value, '\\')) { + strlcpy(cPath,ce->value,MAX_PATH); cSlash=cPath+strlen(cPath); while(*cSlash != '\\' && *cSlash != '/' && cSlash > cPath) cSlash--; *(cSlash+1)=0; } - if ( (hFind = FindFirstFile(ce->ce_vardata, &FindData)) == INVALID_HANDLE_VALUE ) + if ( (hFind = FindFirstFile(ce->value, &FindData)) == INVALID_HANDLE_VALUE ) { config_status("%s:%i: include %s: invalid file given", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata); + ce->file->filename, ce->line_number, + ce->value); return -1; } if (cPath) { @@ -3458,15 +3267,16 @@ int _conf_include(ConfigFile *conf, ConfigEntry *ce) strcpy(path, cPath); strcat(path, FindData.cFileName); - add_include(path, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(path, path); - safe_free(path); - + if (add_config_resource(path, RESOURCE_INCLUDE, ce)) + { + ret = config_read_file(path, path); + safe_free(path); + } } else { - add_include(FindData.cFileName, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(FindData.cFileName, FindData.cFileName); + if (add_config_resource(FindData.cFileName, RESOURCE_INCLUDE, ce)) + ret = config_read_file(FindData.cFileName, FindData.cFileName); } if (ret < 0) { @@ -3481,24 +3291,26 @@ int _conf_include(ConfigFile *conf, ConfigEntry *ce) strcpy(path,cPath); strcat(path,FindData.cFileName); - add_include(path, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(path, path); - safe_free(path); - if (ret < 0) - break; + if (add_config_resource(path, RESOURCE_INCLUDE, ce)) + { + ret = config_read_file(path, path); + safe_free(path); + if (ret < 0) + break; + } } else { - add_include(FindData.cFileName, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(FindData.cFileName, FindData.cFileName); + if (add_config_resource(FindData.cFileName, RESOURCE_INCLUDE, ce)) + ret = config_read_file(FindData.cFileName, FindData.cFileName); } } FindClose(hFind); if (ret < 0) return ret; #else - add_include(ce->ce_vardata, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(ce->ce_vardata, ce->ce_vardata); + if (add_config_resource(ce->value, RESOURCE_INCLUDE, ce)) + ret = config_read_file(ce->value, ce->value); return ret; #endif return 1; @@ -3514,12 +3326,12 @@ int _conf_admin(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; ConfigItem_admin *ca; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { ca = safe_alloc(sizeof(ConfigItem_admin)); if (!conf_admin) conf_admin_tail = ca; - safe_strdup(ca->line, cep->ce_varname); + safe_strdup(ca->line, cep->name); AddListItem(ca, conf_admin); } return 1; @@ -3532,17 +3344,17 @@ int _test_admin(ConfigFile *conf, ConfigEntry *ce) if (requiredstuff.conf_admin) { - config_warn_duplicate(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "admin"); + config_warn_duplicate(ce->file->filename, ce->line_number, "admin"); return 0; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (strlen(cep->ce_varname) > 500) + if (strlen(cep->name) > 500) { config_error("%s:%i: oversized data in admin block", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum); + cep->file->filename, + cep->line_number); errors++; continue; } @@ -3558,19 +3370,19 @@ int _conf_me(ConfigFile *conf, ConfigEntry *ce) if (!conf_me) conf_me = safe_alloc(sizeof(ConfigItem_me)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "name")) + if (!strcmp(cep->name, "name")) { - safe_strdup(conf_me->name, cep->ce_vardata); + safe_strdup(conf_me->name, cep->value); } - else if (!strcmp(cep->ce_varname, "info")) + else if (!strcmp(cep->name, "info")) { - safe_strdup(conf_me->info, cep->ce_vardata); + safe_strdup(conf_me->info, cep->value); } - else if (!strcmp(cep->ce_varname, "sid")) + else if (!strcmp(cep->name, "sid")) { - safe_strdup(conf_me->sid, cep->ce_vardata); + safe_strdup(conf_me->sid, cep->value); } } return 1; @@ -3584,69 +3396,69 @@ int _test_me(ConfigFile *conf, ConfigEntry *ce) if (requiredstuff.conf_me) { - config_warn_duplicate(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me"); + config_warn_duplicate(ce->file->filename, ce->line_number, "me"); return 0; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "me")) continue; /* me::name */ - if (!strcmp(cep->ce_varname, "name")) + if (!strcmp(cep->name, "name")) { if (has_name) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "me::name"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "me::name"); continue; } has_name = 1; - if (!strchr(cep->ce_vardata, '.')) + if (!strchr(cep->value, '.')) { config_error("%s:%i: illegal me::name, must be fully qualified hostname", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum); + cep->file->filename, + cep->line_number); errors++; } - if (!valid_host(cep->ce_vardata)) - { - config_error("%s:%i: illegal me::name contains invalid character(s) [only a-z, 0-9, _, -, . are allowed]", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum); - errors++; - } - if (strlen(cep->ce_vardata) > HOSTLEN) + if (strlen(cep->value) > HOSTLEN) { config_error("%s:%i: illegal me::name, must be less or equal to %i characters", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, HOSTLEN); + cep->file->filename, + cep->line_number, HOSTLEN); + errors++; + } + if (!valid_server_name(cep->value)) + { + config_error("%s:%i: illegal me::name contains invalid character(s) [only a-z, 0-9, _, -, . are allowed]", + cep->file->filename, + cep->line_number); errors++; } } /* me::info */ - else if (!strcmp(cep->ce_varname, "info")) + else if (!strcmp(cep->name, "info")) { char *p; char valid = 0; if (has_info) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "me::info"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "me::info"); continue; } has_info = 1; - if (strlen(cep->ce_vardata) > (REALLEN-1)) + if (strlen(cep->value) > (REALLEN-1)) { config_error("%s:%i: too long me::info, must be max. %i characters", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, + cep->file->filename, cep->line_number, REALLEN-1); errors++; } /* Valid me::info? Any data except spaces is ok */ - for (p=cep->ce_vardata; *p; p++) + for (p=cep->value; *p; p++) { if (*p != ' ') { @@ -3657,65 +3469,65 @@ int _test_me(ConfigFile *conf, ConfigEntry *ce) if (!valid) { config_error("%s:%i: empty me::info, should be a server description.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "numeric")) + else if (!strcmp(cep->name, "numeric")) { config_error("%s:%i: me::numeric has been removed, you must now specify a Server ID (SID) instead. " "Edit your configuration file and change 'numeric' to 'sid' and make up " "a server id of exactly 3 characters, starting with a digit, eg: \"001\" or \"0AB\".", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } - else if (!strcmp(cep->ce_varname, "sid")) + else if (!strcmp(cep->name, "sid")) { if (has_sid) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "me::sid"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "me::sid"); continue; } has_sid = 1; - if (!valid_sid(cep->ce_vardata)) + if (!valid_sid(cep->value)) { config_error("%s:%i: me::sid must be 3 characters long, begin with a number, " "and the 2nd and 3rd character must be a number or uppercase letter. " "Example: \"001\" and \"0AB\" is good. \"AAA\" and \"0ab\" are bad. ", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } - if (!isdigit(*cep->ce_vardata)) + if (!isdigit(*cep->value)) { config_error("%s:%i: me::sid must be 3 characters long and begin with a number", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } /* Unknown entry */ else { - config_error_unknown(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "me", cep->ce_varname); + config_error_unknown(ce->file->filename, ce->line_number, + "me", cep->name); errors++; } } if (!has_name) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::name"); + config_error_missing(ce->file->filename, ce->line_number, "me::name"); errors++; } if (!has_info) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::info"); + config_error_missing(ce->file->filename, ce->line_number, "me::info"); errors++; } if (!has_sid) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "me::sid"); + config_error_missing(ce->file->filename, ce->line_number, "me::sid"); errors++; } requiredstuff.conf_me = 1; @@ -3750,29 +3562,29 @@ int _conf_files(ConfigFile *conf, ConfigEntry *ce) * hack to allow initialization of conf_files (above) when there is no files block in * CPATH. The caller calls _conf_files(NULL, NULL); to do this. We return here because * the for loop's initialization of cep would segfault otherwise. We return 1 because - * if config_run() calls us with a NULL ce, it's got a bug...but we can't detect that. + * if config_run_blocks() calls us with a NULL ce, it's got a bug...but we can't detect that. */ - if(!ce) + if (!ce) return 1; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "motd")) - safe_strdup(conf_files->motd_file, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "shortmotd")) - safe_strdup(conf_files->smotd_file, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "opermotd")) - safe_strdup(conf_files->opermotd_file, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "svsmotd")) - safe_strdup(conf_files->svsmotd_file, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "botmotd")) - safe_strdup(conf_files->botmotd_file, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "rules")) - safe_strdup(conf_files->rules_file, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "tunefile")) - safe_strdup(conf_files->tune_file, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "pidfile")) - safe_strdup(conf_files->pid_file, cep->ce_vardata); + if (!strcmp(cep->name, "motd")) + safe_strdup(conf_files->motd_file, cep->value); + else if (!strcmp(cep->name, "shortmotd")) + safe_strdup(conf_files->smotd_file, cep->value); + else if (!strcmp(cep->name, "opermotd")) + safe_strdup(conf_files->opermotd_file, cep->value); + else if (!strcmp(cep->name, "svsmotd")) + safe_strdup(conf_files->svsmotd_file, cep->value); + else if (!strcmp(cep->name, "botmotd")) + safe_strdup(conf_files->botmotd_file, cep->value); + else if (!strcmp(cep->name, "rules")) + safe_strdup(conf_files->rules_file, cep->value); + else if (!strcmp(cep->name, "tunefile")) + safe_strdup(conf_files->tune_file, cep->value); + else if (!strcmp(cep->name, "pidfile")) + safe_strdup(conf_files->pid_file, cep->value); } return 1; } @@ -3785,120 +3597,120 @@ int _test_files(ConfigFile *conf, ConfigEntry *ce) char has_botmotd = 0, has_opermotd = 0, has_svsmotd = 0; char has_pidfile = 0, has_tunefile = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { /* files::motd */ - if (!strcmp(cep->ce_varname, "motd")) + if (!strcmp(cep->name, "motd")) { if (has_motd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::motd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::motd"); continue; } - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); + convert_to_absolute_path(&cep->value, CONFDIR); config_test_openfile(cep, O_RDONLY, 0, "files::motd", 0, 1); has_motd = 1; } /* files::smotd */ - else if (!strcmp(cep->ce_varname, "shortmotd")) + else if (!strcmp(cep->name, "shortmotd")) { if (has_smotd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::shortmotd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::shortmotd"); continue; } - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); + convert_to_absolute_path(&cep->value, CONFDIR); config_test_openfile(cep, O_RDONLY, 0, "files::shortmotd", 0, 1); has_smotd = 1; } /* files::rules */ - else if (!strcmp(cep->ce_varname, "rules")) + else if (!strcmp(cep->name, "rules")) { if (has_rules) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::rules"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::rules"); continue; } - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); + convert_to_absolute_path(&cep->value, CONFDIR); config_test_openfile(cep, O_RDONLY, 0, "files::rules", 0, 1); has_rules = 1; } /* files::botmotd */ - else if (!strcmp(cep->ce_varname, "botmotd")) + else if (!strcmp(cep->name, "botmotd")) { if (has_botmotd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::botmotd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::botmotd"); continue; } - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); + convert_to_absolute_path(&cep->value, CONFDIR); config_test_openfile(cep, O_RDONLY, 0, "files::botmotd", 0, 1); has_botmotd = 1; } /* files::opermotd */ - else if (!strcmp(cep->ce_varname, "opermotd")) + else if (!strcmp(cep->name, "opermotd")) { if (has_opermotd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::opermotd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::opermotd"); continue; } - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); + convert_to_absolute_path(&cep->value, CONFDIR); config_test_openfile(cep, O_RDONLY, 0, "files::opermotd", 0, 1); has_opermotd = 1; } /* files::svsmotd * This config stuff should somehow be inside of modules/svsmotd.c!!!... right? */ - else if (!strcmp(cep->ce_varname, "svsmotd")) + else if (!strcmp(cep->name, "svsmotd")) { if (has_svsmotd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::svsmotd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::svsmotd"); continue; } - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); + convert_to_absolute_path(&cep->value, CONFDIR); /* svsmotd can't be a URL because we have to be able to write to it */ config_test_openfile(cep, O_RDONLY, 0, "files::svsmotd", 0, 0); has_svsmotd = 1; } /* files::pidfile */ - else if (!strcmp(cep->ce_varname, "pidfile")) + else if (!strcmp(cep->name, "pidfile")) { if (has_pidfile) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::pidfile"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::pidfile"); continue; } - convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); + convert_to_absolute_path(&cep->value, PERMDATADIR); errors += config_test_openfile(cep, O_WRONLY | O_CREAT, 0600, "files::pidfile", 1, 0); has_pidfile = 1; } /* files::tunefile */ - else if (!strcmp(cep->ce_varname, "tunefile")) + else if (!strcmp(cep->name, "tunefile")) { if (has_tunefile) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "files::tunefile"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "files::tunefile"); continue; } - convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); + convert_to_absolute_path(&cep->value, PERMDATADIR); errors += config_test_openfile(cep, O_RDWR | O_CREAT, 0600, "files::tunefile", 1, 0); has_tunefile = 1; } /* */ else { - config_error("%s:%d: Unknown directive: \"%s\" in files {}", cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%d: Unknown directive: \"%s\" in files {}", cep->file->filename, + cep->line_number, cep->name); errors ++; } } @@ -3915,18 +3727,18 @@ OperClassACLEntry* _conf_parseACLEntry(ConfigEntry *ce) OperClassACLEntry *entry = NULL; entry = safe_alloc(sizeof(OperClassACLEntry)); - if (!strcmp(ce->ce_varname,"allow")) + if (!strcmp(ce->name,"allow")) entry->type = OPERCLASSENTRY_ALLOW; else entry->type = OPERCLASSENTRY_DENY; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { OperClassACLEntryVar *var = safe_alloc(sizeof(OperClassACLEntryVar)); - safe_strdup(var->name, cep->ce_varname); - if (cep->ce_vardata) + safe_strdup(var->name, cep->name); + if (cep->value) { - safe_strdup(var->value, cep->ce_vardata); + safe_strdup(var->value, cep->value); } AddListItem(var,entry->variables); } @@ -3934,7 +3746,7 @@ OperClassACLEntry* _conf_parseACLEntry(ConfigEntry *ce) return entry; } -OperClassACL* _conf_parseACL(char *name, ConfigEntry *ce) +OperClassACL* _conf_parseACL(const char *name, ConfigEntry *ce) { ConfigEntry *cep; OperClassACL *acl = NULL; @@ -3942,15 +3754,15 @@ OperClassACL* _conf_parseACL(char *name, ConfigEntry *ce) acl = safe_alloc(sizeof(OperClassACL)); safe_strdup(acl->name, name); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "deny") || !strcmp(cep->ce_varname, "allow")) + if (!strcmp(cep->name, "deny") || !strcmp(cep->name, "allow")) { OperClassACLEntry *entry = _conf_parseACLEntry(cep); AddListItem(entry,acl->entries); } else { - OperClassACL *subAcl = _conf_parseACL(cep->ce_varname,cep); + OperClassACL *subAcl = _conf_parseACL(cep->name,cep); AddListItem(subAcl,acl->acls); } } @@ -3965,19 +3777,19 @@ int _conf_operclass(ConfigFile *conf, ConfigEntry *ce) ConfigItem_operclass *operClass = NULL; operClass = safe_alloc(sizeof(ConfigItem_operclass)); operClass->classStruct = safe_alloc(sizeof(OperClass)); - safe_strdup(operClass->classStruct->name, ce->ce_vardata); + safe_strdup(operClass->classStruct->name, ce->value); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "parent")) + if (!strcmp(cep->name, "parent")) { - safe_strdup(operClass->classStruct->ISA, cep->ce_vardata); + safe_strdup(operClass->classStruct->ISA, cep->value); } - else if (!strcmp(cep->ce_varname, "permissions")) + else if (!strcmp(cep->name, "permissions")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - OperClassACL *acl = _conf_parseACL(cepp->ce_varname,cepp); + OperClassACL *acl = _conf_parseACL(cepp->name,cepp); AddListItem(acl,operClass->classStruct->acls); } } @@ -3993,7 +3805,7 @@ void new_permissions_system(ConfigFile *conf, ConfigEntry *ce) return; /* error already shown */ config_error("%s:%i: UnrealIRCd 4.2.1 and higher have a new operclass permissions system.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); config_error("Please see https://www.unrealircd.org/docs/FAQ#New_operclass_permissions"); config_error("(additional errors regarding this are suppressed)"); /* @@ -4011,44 +3823,44 @@ int _test_operclass(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; int errors = 0; - if (!ce->ce_vardata) + if (!ce->value) { - config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "operclass"); + config_error_noname(ce->file->filename, ce->line_number, "operclass"); errors++; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "parent")) + if (!strcmp(cep->name, "parent")) { if (has_parent) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "operclass::parent"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "operclass::parent"); continue; } has_parent = 1; continue; } else - if (!strcmp(cep->ce_varname, "permissions")) + if (!strcmp(cep->name, "permissions")) { if (has_permissions) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::permissions"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::permissions"); continue; } has_permissions = 1; continue; } else - if (!strcmp(cep->ce_varname, "privileges")) + if (!strcmp(cep->name, "privileges")) { new_permissions_system(conf, cep); errors++; return errors; } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "operclass", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "operclass", cep->name); errors++; continue; } @@ -4056,7 +3868,7 @@ int _test_operclass(ConfigFile *conf, ConfigEntry *ce) if (!has_permissions) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "oper::permissions"); errors++; } @@ -4075,69 +3887,75 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce) ConfigItem_oper *oper = NULL; oper = safe_alloc(sizeof(ConfigItem_oper)); - safe_strdup(oper->name, ce->ce_vardata); + safe_strdup(oper->name, ce->value); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + oper->server_notice_colors = tempiConf.server_notice_colors; /* default */ + + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "operclass")) - safe_strdup(oper->operclass, cep->ce_vardata); - if (!strcmp(cep->ce_varname, "password")) + if (!strcmp(cep->name, "operclass")) + safe_strdup(oper->operclass, cep->value); + if (!strcmp(cep->name, "password")) oper->auth = AuthBlockToAuthConfig(cep); - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { - oper->class = find_class(cep->ce_vardata); + oper->class = find_class(cep->value); if (!oper->class || (oper->class->flag.temporary == 1)) { config_status("%s:%i: illegal oper::class, unknown class '%s' using default of class 'default'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata); + cep->file->filename, cep->line_number, + cep->value); oper->class = default_class; } } - else if (!strcmp(cep->ce_varname, "swhois")) + else if (!strcmp(cep->name, "swhois")) { SWhois *s; - if (cep->ce_entries) + if (cep->items) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { s = safe_alloc(sizeof(SWhois)); - safe_strdup(s->line, cepp->ce_varname); + safe_strdup(s->line, cepp->name); safe_strdup(s->setby, "oper"); AddListItem(s, oper->swhois); } } else - if (cep->ce_vardata) + if (cep->value) { s = safe_alloc(sizeof(SWhois)); - safe_strdup(s->line, cep->ce_vardata); + safe_strdup(s->line, cep->value); safe_strdup(s->setby, "oper"); AddListItem(s, oper->swhois); } } - else if (!strcmp(cep->ce_varname, "snomask")) + else if (!strcmp(cep->name, "snomask")) { - safe_strdup(oper->snomask, cep->ce_vardata); + safe_strdup(oper->snomask, cep->value); } - else if (!strcmp(cep->ce_varname, "modes")) + else if (!strcmp(cep->name, "server-notice-colors")) { - oper->modes = set_usermode(cep->ce_vardata); + oper->server_notice_colors = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "require-modes")) + else if (!strcmp(cep->name, "modes")) { - oper->require_modes = set_usermode(cep->ce_vardata); + oper->modes = set_usermode(cep->value); } - else if (!strcmp(cep->ce_varname, "maxlogins")) + else if (!strcmp(cep->name, "require-modes")) { - oper->maxlogins = atoi(cep->ce_vardata); + oper->require_modes = set_usermode(cep->value); } - else if (!strcmp(cep->ce_varname, "mask")) + else if (!strcmp(cep->name, "maxlogins")) + { + oper->maxlogins = atoi(cep->value); + } + else if (!strcmp(cep->name, "mask")) { unreal_add_masks(&oper->mask, cep); } - else if (!strcmp(cep->ce_varname, "vhost")) + else if (!strcmp(cep->name, "vhost")) { - safe_strdup(oper->vhost, cep->ce_vardata); + safe_strdup(oper->vhost, cep->value); } } AddListItem(oper, conf_oper); @@ -4152,15 +3970,15 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; int errors = 0; - if (!ce->ce_vardata) + if (!ce->value) { - config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "oper"); + config_error_noname(ce->file->filename, ce->line_number, "oper"); errors++; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { /* Regular variables */ - if (!cep->ce_entries) + if (!cep->items) { if (config_is_blankorempty(cep, "oper")) { @@ -4168,154 +3986,157 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) continue; } /* oper::password */ - if (!strcmp(cep->ce_varname, "password")) + if (!strcmp(cep->name, "password")) { if (has_password) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::password"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::password"); continue; } has_password = 1; if (Auth_CheckError(cep) < 0) errors++; - if (ce->ce_vardata && cep->ce_vardata && - !strcmp(ce->ce_vardata, "bobsmith") && - !strcmp(cep->ce_vardata, "test")) + if (ce->value && cep->value && + !strcmp(ce->value, "bobsmith") && + !strcmp(cep->value, "test")) { config_error("%s:%i: please change the the name and password of the " "default 'bobsmith' oper block", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } continue; } /* oper::operclass */ - else if (!strcmp(cep->ce_varname, "operclass")) + else if (!strcmp(cep->name, "operclass")) { if (has_operclass) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::operclass"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::operclass"); continue; } has_operclass = 1; continue; } /* oper::class */ - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { if (has_class) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::class"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::class"); continue; } has_class = 1; } /* oper::swhois */ - else if (!strcmp(cep->ce_varname, "swhois")) + else if (!strcmp(cep->name, "swhois")) { } /* oper::vhost */ - else if (!strcmp(cep->ce_varname, "vhost")) + else if (!strcmp(cep->name, "vhost")) { if (has_vhost) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::vhost"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::vhost"); continue; } has_vhost = 1; } /* oper::snomask */ - else if (!strcmp(cep->ce_varname, "snomask")) + else if (!strcmp(cep->name, "snomask")) { + char *wrong_snomask; if (has_snomask) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::snomask"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::snomask"); continue; } + if (!is_valid_snomask_string_testing(cep->value, &wrong_snomask)) + { + config_error("%s:%i: oper::snomask contains unknown snomask letter(s) '%s'", + cep->file->filename, cep->line_number, wrong_snomask); + errors++; + invalid_snomasks_encountered++; + } has_snomask = 1; } + else if (!strcmp(cep->name, "server-notice-colors")) + { + } /* oper::modes */ - else if (!strcmp(cep->ce_varname, "modes")) + else if (!strcmp(cep->name, "modes")) { char *p; - for (p = cep->ce_vardata; *p; p++) + for (p = cep->value; *p; p++) if (strchr("orzS", *p)) { config_error("%s:%i: oper::modes may not include mode '%c'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); + cep->file->filename, cep->line_number, *p); errors++; } if (has_modes) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::modes"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::modes"); continue; } has_modes = 1; } /* oper::require-modes */ - else if (!strcmp(cep->ce_varname, "require-modes")) + else if (!strcmp(cep->name, "require-modes")) { char *p; - for (p = cep->ce_vardata; *p; p++) + for (p = cep->value; *p; p++) if (strchr("o", *p)) { config_warn("%s:%i: oper::require-modes probably shouldn't include mode '%c'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); + cep->file->filename, cep->line_number, *p); } if (has_require_modes) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::require-modes"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::require-modes"); continue; } has_require_modes = 1; } /* oper::maxlogins */ - else if (!strcmp(cep->ce_varname, "maxlogins")) + else if (!strcmp(cep->name, "maxlogins")) { int l; if (has_maxlogins) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::maxlogins"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::maxlogins"); continue; } has_maxlogins = 1; - l = atoi(cep->ce_vardata); + l = atoi(cep->value); if ((l < 0) || (l > 5000)) { config_error("%s:%i: oper::maxlogins: value out of range (%d) should be 0-5000", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, l); + cep->file->filename, cep->line_number, l); errors++; continue; } } - /* oper::flags */ - else if (!strcmp(cep->ce_varname, "flags")) + else if (!strcmp(cep->name, "mask")) { - config_error("%s:%i: oper::flags no longer exists. UnrealIRCd 4 uses a new style oper block.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); - errors++; - need_34_upgrade = 1; - } - else if (!strcmp(cep->ce_varname, "mask")) - { - if (cep->ce_vardata || cep->ce_entries) + if (cep->value || cep->items) has_mask = 1; } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "oper", cep->name); errors++; continue; } @@ -4323,39 +4144,21 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) /* Sections */ else { - /* oper::flags {} */ - if (!strcmp(cep->ce_varname, "flags")) - { - config_error("%s:%i: oper::flags no longer exists. UnrealIRCd 4 uses a new style oper block.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); - errors++; - need_34_upgrade = 1; - continue; - } - /* oper::from {} */ - else if (!strcmp(cep->ce_varname, "from")) - { - config_error("%s:%i: oper::from::userhost is now called oper::mask", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); - errors++; - need_34_upgrade = 1; - continue; - } - else if (!strcmp(cep->ce_varname, "swhois")) + if (!strcmp(cep->name, "swhois")) { /* ok */ } - else if (!strcmp(cep->ce_varname, "mask")) + else if (!strcmp(cep->name, "mask")) { - if (cep->ce_vardata || cep->ce_entries) + if (cep->value || cep->items) has_mask = 1; } - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) { if (has_password) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper::password"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "oper::password"); continue; } has_password = 1; @@ -4364,8 +4167,8 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "oper", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "oper", cep->name); errors++; continue; } @@ -4373,27 +4176,26 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) } if (!has_password) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "oper::password"); errors++; } if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "oper::mask"); errors++; } if (!has_class) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "oper::class"); errors++; } if (!has_operclass) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "oper::operclass"); - need_34_upgrade = 1; errors++; } @@ -4410,10 +4212,10 @@ int _conf_class(ConfigFile *conf, ConfigEntry *ce) ConfigItem_class *class; unsigned char isnew = 0; - if (!(class = find_class(ce->ce_vardata))) + if (!(class = find_class(ce->value))) { class = safe_alloc(sizeof(ConfigItem_class)); - safe_strdup(class->name, ce->ce_vardata); + safe_strdup(class->name, ce->value); isnew = 1; } else @@ -4422,26 +4224,26 @@ int _conf_class(ConfigFile *conf, ConfigEntry *ce) class->flag.temporary = 0; class->options = 0; /* RESET OPTIONS */ } - safe_strdup(class->name, ce->ce_vardata); + safe_strdup(class->name, ce->value); class->connfreq = 15; /* default */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "pingfreq")) - class->pingfreq = config_checkval(cep->ce_vardata,CFG_TIME); - else if (!strcmp(cep->ce_varname, "connfreq")) - class->connfreq = config_checkval(cep->ce_vardata,CFG_TIME); - else if (!strcmp(cep->ce_varname, "maxclients")) - class->maxclients = atol(cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "sendq")) - class->sendq = config_checkval(cep->ce_vardata,CFG_SIZE); - else if (!strcmp(cep->ce_varname, "recvq")) - class->recvq = config_checkval(cep->ce_vardata,CFG_SIZE); - else if (!strcmp(cep->ce_varname, "options")) + if (!strcmp(cep->name, "pingfreq")) + class->pingfreq = config_checkval(cep->value,CFG_TIME); + else if (!strcmp(cep->name, "connfreq")) + class->connfreq = config_checkval(cep->value,CFG_TIME); + else if (!strcmp(cep->name, "maxclients")) + class->maxclients = atol(cep->value); + else if (!strcmp(cep->name, "sendq")) + class->sendq = config_checkval(cep->value,CFG_SIZE); + else if (!strcmp(cep->name, "recvq")) + class->recvq = config_checkval(cep->value,CFG_SIZE); + else if (!strcmp(cep->name, "options")) { - for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) - if (!strcmp(cep2->ce_varname, "nofakelag")) + for (cep2 = cep->items; cep2; cep2 = cep2->next) + if (!strcmp(cep2->name, "nofakelag")) class->options |= CLASS_OPT_NOFAKELAG; } } @@ -4457,32 +4259,32 @@ int _test_class(ConfigFile *conf, ConfigEntry *ce) char has_pingfreq = 0, has_connfreq = 0, has_maxclients = 0, has_sendq = 0; char has_recvq = 0; - if (!ce->ce_vardata) + if (!ce->value) { - config_error_noname(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "class"); + config_error_noname(ce->file->filename, ce->line_number, "class"); return 1; } - if (!strcasecmp(ce->ce_vardata, "default")) + if (!strcasecmp(ce->value, "default")) { config_error("%s:%d: Class cannot be named 'default', this class name is reserved for internal use.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "options")) + if (!strcmp(cep->name, "options")) { - for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) + for (cep2 = cep->items; cep2; cep2 = cep2->next) { #ifdef FAKELAG_CONFIGURABLE - if (!strcmp(cep2->ce_varname, "nofakelag")) + if (!strcmp(cep2->name, "nofakelag")) ; else #endif { config_error("%s:%d: Unknown option '%s' in class::options", - cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep2->ce_varname); + cep2->file->filename, cep2->line_number, cep2->name); errors++; } } @@ -4493,124 +4295,124 @@ int _test_class(ConfigFile *conf, ConfigEntry *ce) continue; } /* class::pingfreq */ - else if (!strcmp(cep->ce_varname, "pingfreq")) + else if (!strcmp(cep->name, "pingfreq")) { - int v = config_checkval(cep->ce_vardata,CFG_TIME); + int v = config_checkval(cep->value,CFG_TIME); if (has_pingfreq) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "class::pingfreq"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "class::pingfreq"); continue; } has_pingfreq = 1; if ((v < 30) || (v > 600)) { config_error("%s:%i: class::pingfreq should be a reasonable value (30-600)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; continue; } } /* class::maxclients */ - else if (!strcmp(cep->ce_varname, "maxclients")) + else if (!strcmp(cep->name, "maxclients")) { long l; if (has_maxclients) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "class::maxclients"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "class::maxclients"); continue; } has_maxclients = 1; - l = atol(cep->ce_vardata); + l = atol(cep->value); if ((l < 1) || (l > 1000000)) { config_error("%s:%i: class::maxclients with illegal value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } /* class::connfreq */ - else if (!strcmp(cep->ce_varname, "connfreq")) + else if (!strcmp(cep->name, "connfreq")) { long l; if (has_connfreq) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "class::connfreq"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "class::connfreq"); continue; } has_connfreq = 1; - l = config_checkval(cep->ce_vardata,CFG_TIME); + l = config_checkval(cep->value,CFG_TIME); if ((l < 5) || (l > 604800)) { config_error("%s:%i: class::connfreq with illegal value (must be >5 and <7d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } /* class::sendq */ - else if (!strcmp(cep->ce_varname, "sendq")) + else if (!strcmp(cep->name, "sendq")) { long l; if (has_sendq) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "class::sendq"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "class::sendq"); continue; } has_sendq = 1; - l = config_checkval(cep->ce_vardata,CFG_SIZE); + l = config_checkval(cep->value,CFG_SIZE); if ((l <= 0) || (l > 2000000000)) { config_error("%s:%i: class::sendq with illegal value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } /* class::recvq */ - else if (!strcmp(cep->ce_varname, "recvq")) + else if (!strcmp(cep->name, "recvq")) { long l; if (has_recvq) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "class::recvq"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "class::recvq"); continue; } has_recvq = 1; - l = config_checkval(cep->ce_vardata,CFG_SIZE); + l = config_checkval(cep->value,CFG_SIZE); if ((l < 512) || (l > 32768)) { config_error("%s:%i: class::recvq with illegal value (must be >512 and <32k)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } /* Unknown */ else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "class", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "class", cep->name); errors++; continue; } } if (!has_pingfreq) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "class::pingfreq"); errors++; } if (!has_maxclients) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "class::maxclients"); errors++; } if (!has_sendq) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "class::sendq"); errors++; } @@ -4627,16 +4429,16 @@ int _conf_drpass(ConfigFile *conf, ConfigEntry *ce) conf_drpass = safe_alloc(sizeof(ConfigItem_drpass)); } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "restart")) + if (!strcmp(cep->name, "restart")) { if (conf_drpass->restartauth) Auth_FreeAuthConfig(conf_drpass->restartauth); conf_drpass->restartauth = AuthBlockToAuthConfig(cep); } - else if (!strcmp(cep->ce_varname, "die")) + else if (!strcmp(cep->name, "die")) { if (conf_drpass->dieauth) Auth_FreeAuthConfig(conf_drpass->dieauth); @@ -4653,7 +4455,7 @@ int _test_drpass(ConfigFile *conf, ConfigEntry *ce) int errors = 0; char has_restart = 0, has_die = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "drpass")) { @@ -4661,12 +4463,12 @@ int _test_drpass(ConfigFile *conf, ConfigEntry *ce) continue; } /* drpass::restart */ - if (!strcmp(cep->ce_varname, "restart")) + if (!strcmp(cep->name, "restart")) { if (has_restart) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "drpass::restart"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "drpass::restart"); continue; } has_restart = 1; @@ -4675,12 +4477,12 @@ int _test_drpass(ConfigFile *conf, ConfigEntry *ce) continue; } /* drpass::die */ - else if (!strcmp(cep->ce_varname, "die")) + else if (!strcmp(cep->name, "die")) { if (has_die) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "drpass::die"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "drpass::die"); continue; } has_die = 1; @@ -4691,8 +4493,8 @@ int _test_drpass(ConfigFile *conf, ConfigEntry *ce) /* Unknown */ else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "drpass", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "drpass", cep->name); errors++; continue; } @@ -4708,10 +4510,10 @@ int _conf_ulines(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; ConfigItem_ulines *ca; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { ca = safe_alloc(sizeof(ConfigItem_ulines)); - safe_strdup(ca->servername, cep->ce_varname); + safe_strdup(ca->servername, cep->name); AddListItem(ca, conf_ulines); } return 1; @@ -4730,48 +4532,48 @@ int _conf_tld(ConfigFile *conf, ConfigEntry *ce) ca = safe_alloc(sizeof(ConfigItem_tld)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) - safe_strdup(ca->mask, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "motd")) + if (!strcmp(cep->name, "mask")) + unreal_add_masks(&ca->mask, cep); + else if (!strcmp(cep->name, "motd")) { - safe_strdup(ca->motd_file, cep->ce_vardata); - read_motd(cep->ce_vardata, &ca->motd); + safe_strdup(ca->motd_file, cep->value); + read_motd(cep->value, &ca->motd); } - else if (!strcmp(cep->ce_varname, "shortmotd")) + else if (!strcmp(cep->name, "shortmotd")) { - safe_strdup(ca->smotd_file, cep->ce_vardata); - read_motd(cep->ce_vardata, &ca->smotd); + safe_strdup(ca->smotd_file, cep->value); + read_motd(cep->value, &ca->smotd); } - else if (!strcmp(cep->ce_varname, "opermotd")) + else if (!strcmp(cep->name, "opermotd")) { - safe_strdup(ca->opermotd_file, cep->ce_vardata); - read_motd(cep->ce_vardata, &ca->opermotd); + safe_strdup(ca->opermotd_file, cep->value); + read_motd(cep->value, &ca->opermotd); } - else if (!strcmp(cep->ce_varname, "botmotd")) + else if (!strcmp(cep->name, "botmotd")) { - safe_strdup(ca->botmotd_file, cep->ce_vardata); - read_motd(cep->ce_vardata, &ca->botmotd); + safe_strdup(ca->botmotd_file, cep->value); + read_motd(cep->value, &ca->botmotd); } - else if (!strcmp(cep->ce_varname, "rules")) + else if (!strcmp(cep->name, "rules")) { - safe_strdup(ca->rules_file, cep->ce_vardata); - read_motd(cep->ce_vardata, &ca->rules); + safe_strdup(ca->rules_file, cep->value); + read_motd(cep->value, &ca->rules); } - else if (!strcmp(cep->ce_varname, "options")) + else if (!strcmp(cep->name, "options")) { ConfigEntry *cepp; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "ssl") || !strcmp(cepp->ce_varname, "tls")) + if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) ca->options |= TLD_TLS; - else if (!strcmp(cepp->ce_varname, "remote")) + else if (!strcmp(cepp->name, "remote")) ca->options |= TLD_REMOTE; } } - else if (!strcmp(cep->ce_varname, "channel")) - safe_strdup(ca->channel, cep->ce_vardata); + else if (!strcmp(cep->name, "channel")) + safe_strdup(ca->channel, cep->value); } AddListItem(ca, conf_tld); return 1; @@ -4785,189 +4587,184 @@ int _test_tld(ConfigFile *conf, ConfigEntry *ce) char has_mask = 0, has_motd = 0, has_rules = 0, has_shortmotd = 0, has_channel = 0; char has_opermotd = 0, has_botmotd = 0, has_options = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata && strcmp(cep->ce_varname, "options")) + if (!cep->value && strcmp(cep->name, "options")) { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "tld", cep->ce_varname); + config_error_empty(cep->file->filename, cep->line_number, + "tld", cep->name); errors++; continue; } /* tld::mask */ - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - if (has_mask) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::mask"); - continue; - } - has_mask = 1; + if (cep->value || cep->items) + has_mask = 1; } /* tld::motd */ - else if (!strcmp(cep->ce_varname, "motd")) + else if (!strcmp(cep->name, "motd")) { if (has_motd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::motd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "tld::motd"); continue; } has_motd = 1; - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); - if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) + convert_to_absolute_path(&cep->value, CONFDIR); + if (((fd = open(cep->value, O_RDONLY)) == -1)) { config_error("%s:%i: tld::motd: %s: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata, strerror(errno)); + cep->file->filename, cep->line_number, + cep->value, strerror(errno)); errors++; } else close(fd); } /* tld::rules */ - else if (!strcmp(cep->ce_varname, "rules")) + else if (!strcmp(cep->name, "rules")) { if (has_rules) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::rules"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "tld::rules"); continue; } has_rules = 1; - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); - if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) + convert_to_absolute_path(&cep->value, CONFDIR); + if (((fd = open(cep->value, O_RDONLY)) == -1)) { config_error("%s:%i: tld::rules: %s: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata, strerror(errno)); + cep->file->filename, cep->line_number, + cep->value, strerror(errno)); errors++; } else close(fd); } /* tld::channel */ - else if (!strcmp(cep->ce_varname, "channel")) + else if (!strcmp(cep->name, "channel")) { if (has_channel) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::channel"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "tld::channel"); continue; } has_channel = 1; } /* tld::shortmotd */ - else if (!strcmp(cep->ce_varname, "shortmotd")) + else if (!strcmp(cep->name, "shortmotd")) { if (has_shortmotd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::shortmotd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "tld::shortmotd"); continue; } has_shortmotd = 1; - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); - if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) + convert_to_absolute_path(&cep->value, CONFDIR); + if (((fd = open(cep->value, O_RDONLY)) == -1)) { config_error("%s:%i: tld::shortmotd: %s: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata, strerror(errno)); + cep->file->filename, cep->line_number, + cep->value, strerror(errno)); errors++; } else close(fd); } /* tld::opermotd */ - else if (!strcmp(cep->ce_varname, "opermotd")) + else if (!strcmp(cep->name, "opermotd")) { if (has_opermotd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::opermotd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "tld::opermotd"); continue; } has_opermotd = 1; - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); - if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) + convert_to_absolute_path(&cep->value, CONFDIR); + if (((fd = open(cep->value, O_RDONLY)) == -1)) { config_error("%s:%i: tld::opermotd: %s: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata, strerror(errno)); + cep->file->filename, cep->line_number, + cep->value, strerror(errno)); errors++; } else close(fd); } /* tld::botmotd */ - else if (!strcmp(cep->ce_varname, "botmotd")) + else if (!strcmp(cep->name, "botmotd")) { if (has_botmotd) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::botmotd"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "tld::botmotd"); continue; } has_botmotd = 1; - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); - if (((fd = open(cep->ce_vardata, O_RDONLY)) == -1)) + convert_to_absolute_path(&cep->value, CONFDIR); + if (((fd = open(cep->value, O_RDONLY)) == -1)) { config_error("%s:%i: tld::botmotd: %s: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata, strerror(errno)); + cep->file->filename, cep->line_number, + cep->value, strerror(errno)); errors++; } else close(fd); } /* tld::options */ - else if (!strcmp(cep->ce_varname, "options")) { + else if (!strcmp(cep->name, "options")) { ConfigEntry *cep2; if (has_options) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "tld::options"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "tld::options"); continue; } has_options = 1; - for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) + for (cep2 = cep->items; cep2; cep2 = cep2->next) { - if (strcmp(cep2->ce_varname, "ssl") && - strcmp(cep2->ce_varname, "tls") && - strcmp(cep2->ce_varname, "remote")) + if (strcmp(cep2->name, "ssl") && + strcmp(cep2->name, "tls") && + strcmp(cep2->name, "remote")) { - config_error_unknownopt(cep2->ce_fileptr->cf_filename, - cep2->ce_varlinenum, "tld", cep2->ce_varname); + config_error_unknownopt(cep2->file->filename, + cep2->line_number, "tld", cep2->name); errors++; } } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "tld", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "tld", cep->name); errors++; continue; } } if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "tld::mask"); errors++; } if (!has_motd) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "tld::motd"); errors++; } if (!has_rules) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "tld::rules"); errors++; } @@ -4985,26 +4782,26 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) int tmpflags =0; Hook *h; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "ip")) + if (!strcmp(cep->name, "ip")) { - ip = cep->ce_vardata; + ip = cep->value; } else - if (!strcmp(cep->ce_varname, "port")) + if (!strcmp(cep->name, "port")) { - port_range(cep->ce_vardata, &start, &end); + port_range(cep->value, &start, &end); if ((start < 0) || (start > 65535) || (end < 0) || (end > 65535)) return -1; /* this is already validated in _test_listen, but okay.. */ } else - if (!strcmp(cep->ce_varname, "options")) + if (!strcmp(cep->name, "options")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - NameValue *ofp; - if ((ofp = config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags)))) + long v; + if ((v = nv_find_by_name(_ListenerFlags, cepp->name))) { - tmpflags |= ofp->flag; + tmpflags |= v; } else { for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { @@ -5015,7 +4812,7 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) } } } else - if (!strcmp(cep->ce_varname, "ssl-options") || !strcmp(cep->ce_varname, "tls-options")) + if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) { tlsconfig = cep; } else @@ -5070,23 +4867,25 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) conf_tlsblock(conf, tlsconfig, listen->tls_options); listen->ssl_ctx = init_ctx(listen->tls_options, 1); } + + safe_free(listen->websocket_forward); /* For modules that hook CONFIG_LISTEN and CONFIG_LISTEN_OPTIONS. * Yeah, ugly we have this here.. * and again about 100 lines down too. */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "ip")) + if (!strcmp(cep->name, "ip")) ; - else if (!strcmp(cep->ce_varname, "port")) + else if (!strcmp(cep->name, "port")) ; - else if (!strcmp(cep->ce_varname, "options")) + else if (!strcmp(cep->name, "options")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { NameValue *ofp; - if (!config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags))) + if (!nv_find_by_name(_ListenerFlags, cepp->name)) { for (h = Hooks[HOOKTYPE_CONFIGRUN_EX]; h; h = h->next) { @@ -5097,7 +4896,7 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) } } } else - if (!strcmp(cep->ce_varname, "ssl-options") || !strcmp(cep->ce_varname, "tls-options")) + if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) ; else { @@ -5153,21 +4952,23 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) conf_tlsblock(conf, tlsconfig, listen->tls_options); listen->ssl_ctx = init_ctx(listen->tls_options, 1); } + + safe_free(listen->websocket_forward); + /* For modules that hook CONFIG_LISTEN and CONFIG_LISTEN_OPTIONS. * Yeah, ugly we have this here.. */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "ip")) + if (!strcmp(cep->name, "ip")) ; - else if (!strcmp(cep->ce_varname, "port")) + else if (!strcmp(cep->name, "port")) ; - else if (!strcmp(cep->ce_varname, "options")) + else if (!strcmp(cep->name, "options")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - NameValue *ofp; - if (!config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags))) + if (!nv_find_by_name(_ListenerFlags, cepp->name)) { for (h = Hooks[HOOKTYPE_CONFIGRUN_EX]; h; h = h->next) { @@ -5178,7 +4979,7 @@ int _conf_listen(ConfigFile *conf, ConfigEntry *ce) } } } else - if (!strcmp(cep->ce_varname, "ssl-options") || !strcmp(cep->ce_varname, "tls-options")) + if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) ; else { @@ -5205,16 +5006,14 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) char *ip = NULL; Hook *h; - if (ce->ce_vardata) + if (ce->value) { config_error("%s:%i: listen block has a new syntax, see https://www.unrealircd.org/docs/Listen_block", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - - need_34_upgrade = 1; + ce->file->filename, ce->line_number); return 1; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { int used_by_module = 0; @@ -5247,19 +5046,18 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) errors += errs; } } - if (!strcmp(cep->ce_varname, "options")) + if (!strcmp(cep->name, "options")) { if (has_options) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "listen::options"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "listen::options"); continue; } has_options = 1; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - NameValue *ofp; - if (!(ofp = config_binary_flags_search(_ListenerFlags, cepp->ce_varname, ARRAY_SIZEOF(_ListenerFlags)))) + if (!nv_find_by_name(_ListenerFlags, cepp->name)) { /* Check if a module knows about this listen::options::something */ int used_by_module = 0; @@ -5293,63 +5091,63 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) } if (!used_by_module) { - config_error_unknownopt(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "listen::options", cepp->ce_varname); + config_error_unknownopt(cepp->file->filename, + cepp->line_number, "listen::options", cepp->name); errors++; continue; } } - if (!strcmp(cepp->ce_varname, "ssl") || !strcmp(cepp->ce_varname, "tls")) + if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) have_tls_listeners = 1; /* for ssl config test */ } } else - if (!strcmp(cep->ce_varname, "ssl-options") || !strcmp(cep->ce_varname, "tls-options")) + if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) { test_tlsblock(conf, cep, &errors); } else - if (!cep->ce_vardata) + if (!cep->value) { if (!used_by_module) { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "listen", cep->ce_varname); + config_error_empty(cep->file->filename, + cep->line_number, "listen", cep->name); errors++; } continue; /* always */ } else - if (!strcmp(cep->ce_varname, "ip")) + if (!strcmp(cep->name, "ip")) { has_ip = 1; - if (strcmp(cep->ce_vardata, "*") && !is_valid_ip(cep->ce_vardata)) + if (strcmp(cep->value, "*") && !is_valid_ip(cep->value)) { config_error("%s:%i: listen: illegal listen::ip (%s). Must be either '*' or contain a valid IP.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); return 1; } - ip = cep->ce_vardata; + ip = cep->value; } else - if (!strcmp(cep->ce_varname, "host")) + if (!strcmp(cep->name, "host")) { config_error("%s:%i: listen: unknown option listen::host, did you mean listen::ip?", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } else - if (!strcmp(cep->ce_varname, "port")) + if (!strcmp(cep->name, "port")) { int start = 0, end = 0; has_port = 1; - port_range(cep->ce_vardata, &start, &end); + port_range(cep->value, &start, &end); if (start == end) { if ((start < 1) || (start > 65535)) { config_error("%s:%i: listen: illegal port (must be 1..65535)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); return 1; } } @@ -5358,21 +5156,21 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) if (end < start) { config_error("%s:%i: listen: illegal port range end value is less than starting value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); return 1; } if (end - start >= 100) { config_error("%s:%i: listen: you requested port %d-%d, that's %d ports " "(and thus consumes %d sockets) this is probably not what you want.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, start, end, + cep->file->filename, cep->line_number, start, end, end - start + 1, end - start + 1); return 1; } if ((start < 1) || (start > 65535) || (end < 1) || (end > 65535)) { config_error("%s:%i: listen: illegal port range values must be between 1 and 65535", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); return 1; } } @@ -5383,8 +5181,8 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) { if (!used_by_module) { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "listen", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "listen", cep->name); errors++; } continue; /* always */ @@ -5394,14 +5192,14 @@ int _test_listen(ConfigFile *conf, ConfigEntry *ce) if (!has_ip) { config_error("%s:%d: listen block requires an listen::ip", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } if (!has_port) { config_error("%s:%d: listen block requires an listen::port", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } @@ -5419,9 +5217,9 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce) ConfigItem_allow *allow; Hook *h; - if (ce->ce_vardata) + if (ce->value) { - if (!strcmp(ce->ce_vardata, "channel")) + if (!strcmp(ce->value, "channel")) return (_conf_allow_channel(conf, ce)); else { @@ -5436,68 +5234,61 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce) } } allow = safe_alloc(sizeof(ConfigItem_allow)); + allow->ipv6_clone_mask = tempiConf.default_ipv6_clone_mask; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "ip")) + if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "ip") || !strcmp(cep->name, "hostname")) { - safe_strdup(allow->ip, cep->ce_vardata); + unreal_add_masks(&allow->mask, cep); } - else if (!strcmp(cep->ce_varname, "hostname")) - safe_strdup(allow->hostname, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) allow->auth = AuthBlockToAuthConfig(cep); - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { - allow->class = find_class(cep->ce_vardata); + allow->class = find_class(cep->value); if (!allow->class || (allow->class->flag.temporary == 1)) { config_status("%s:%i: illegal allow::class, unknown class '%s' using default of class 'default'", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - cep->ce_vardata); + cep->file->filename, + cep->line_number, + cep->value); allow->class = default_class; } } - else if (!strcmp(cep->ce_varname, "maxperip")) - allow->maxperip = atoi(cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "global-maxperip")) - allow->global_maxperip = atoi(cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "redirect-server")) - safe_strdup(allow->server, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "redirect-port")) - allow->port = atoi(cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "ipv6-clone-mask")) + else if (!strcmp(cep->name, "maxperip")) + allow->maxperip = atoi(cep->value); + else if (!strcmp(cep->name, "global-maxperip")) + allow->global_maxperip = atoi(cep->value); + else if (!strcmp(cep->name, "redirect-server")) + safe_strdup(allow->server, cep->value); + else if (!strcmp(cep->name, "redirect-port")) + allow->port = atoi(cep->value); + else if (!strcmp(cep->name, "ipv6-clone-mask")) { /* * If this item isn't set explicitly by the * user, the value will temporarily be - * zero. Defaults are applied in config_run(). + * zero. Defaults are applied in config_run_blocks(). */ - allow->ipv6_clone_mask = atoi(cep->ce_vardata); + allow->ipv6_clone_mask = atoi(cep->value); } - else if (!strcmp(cep->ce_varname, "options")) + else if (!strcmp(cep->name, "options")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "noident")) + if (!strcmp(cepp->name, "noident")) allow->flags.noident = 1; - else if (!strcmp(cepp->ce_varname, "useip")) + else if (!strcmp(cepp->name, "useip")) allow->flags.useip = 1; - else if (!strcmp(cepp->ce_varname, "ssl") || !strcmp(cepp->ce_varname, "tls")) + else if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) allow->flags.tls = 1; - else if (!strcmp(cepp->ce_varname, "reject-on-auth-failure")) + else if (!strcmp(cepp->name, "reject-on-auth-failure")) allow->flags.reject_on_auth_failure = 1; } } } - if (!allow->hostname) - safe_strdup(allow->hostname, "*@NOMATCH"); - - if (!allow->ip) - safe_strdup(allow->ip, "*@NOMATCH"); - /* Default: global-maxperip = maxperip+1 */ if (allow->global_maxperip == 0) allow->global_maxperip = allow->maxperip+1; @@ -5515,13 +5306,14 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep, *cepp; int errors = 0; Hook *h; - char has_ip = 0, has_hostname = 0, has_maxperip = 0, has_global_maxperip = 0, has_password = 0, has_class = 0; + char has_ip = 0, has_hostname = 0, has_mask = 0; + char has_maxperip = 0, has_global_maxperip = 0, has_password = 0, has_class = 0; char has_redirectserver = 0, has_redirectport = 0, has_options = 0; int hostname_possible_silliness = 0; - if (ce->ce_vardata) + if (ce->value) { - if (!strcmp(ce->ce_vardata, "channel")) + if (!strcmp(ce->value, "channel")) return (_test_allow_channel(conf, ce)); else { @@ -5554,106 +5346,112 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce) } if (!used) { config_error("%s:%i: allow item with unknown type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } return errors; } } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (strcmp(cep->ce_varname, "options") && config_is_blankorempty(cep, "allow")) + if (strcmp(cep->name, "options") && + strcmp(cep->name, "mask") && + config_is_blankorempty(cep, "allow")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "ip")) + if (!strcmp(cep->name, "ip")) { if (has_ip) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::ip"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::ip"); continue; } has_ip = 1; } - else if (!strcmp(cep->ce_varname, "maxperip")) + else if (!strcmp(cep->name, "hostname")) { - int v = atoi(cep->ce_vardata); + if (has_hostname) + { + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::hostname"); + continue; + } + has_hostname = 1; + if (!strcmp(cep->value, "*@*") || !strcmp(cep->value, "*")) + hostname_possible_silliness = 1; + } + else if (!strcmp(cep->name, "mask")) + { + has_mask = 1; + } + else if (!strcmp(cep->name, "maxperip")) + { + int v = atoi(cep->value); if (has_maxperip) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::maxperip"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::maxperip"); continue; } has_maxperip = 1; if ((v <= 0) || (v > 1000000)) { config_error("%s:%i: allow::maxperip with illegal value (must be 1-1000000)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "global-maxperip")) + else if (!strcmp(cep->name, "global-maxperip")) { - int v = atoi(cep->ce_vardata); + int v = atoi(cep->value); if (has_global_maxperip) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::global-maxperip"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::global-maxperip"); continue; } has_global_maxperip = 1; if ((v <= 0) || (v > 1000000)) { config_error("%s:%i: allow::global-maxperip with illegal value (must be 1-1000000)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "ipv6-clone-mask")) + else if (!strcmp(cep->name, "ipv6-clone-mask")) { /* keep this in sync with _test_set() */ int ipv6mask; - ipv6mask = atoi(cep->ce_vardata); + ipv6mask = atoi(cep->value); if (ipv6mask == 0) { config_error("%s:%d: allow::ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } if (ipv6mask > 128) { config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, + cep->file->filename, cep->line_number, ipv6mask); errors++; } if (ipv6mask <= 32) { config_warn("%s:%d: allow::ipv6-clone-mask was given a very small value.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); } } - else if (!strcmp(cep->ce_varname, "hostname")) - { - if (has_hostname) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::hostname"); - continue; - } - has_hostname = 1; - if (!strcmp(cep->ce_vardata, "*@*") || !strcmp(cep->ce_vardata, "*")) - hostname_possible_silliness = 1; - } - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) { if (has_password) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::password"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::password"); continue; } has_password = 1; @@ -5661,112 +5459,131 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce) if (Auth_CheckError(cep) < 0) errors++; } - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { if (has_class) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::class"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::class"); continue; } has_class = 1; } - else if (!strcmp(cep->ce_varname, "redirect-server")) + else if (!strcmp(cep->name, "redirect-server")) { if (has_redirectserver) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::redirect-server"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::redirect-server"); continue; } has_redirectserver = 1; } - else if (!strcmp(cep->ce_varname, "redirect-port")) + else if (!strcmp(cep->name, "redirect-port")) { if (has_redirectport) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::redirect-port"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::redirect-port"); continue; } has_redirectport = 1; } - else if (!strcmp(cep->ce_varname, "options")) + else if (!strcmp(cep->name, "options")) { if (has_options) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow::options"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow::options"); continue; } has_options = 1; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "noident")) + if (!strcmp(cepp->name, "noident")) {} - else if (!strcmp(cepp->ce_varname, "useip")) + else if (!strcmp(cepp->name, "useip")) {} - else if (!strcmp(cepp->ce_varname, "ssl") || !strcmp(cepp->ce_varname, "tls")) + else if (!strcmp(cepp->name, "ssl") || !strcmp(cepp->name, "tls")) {} - else if (!strcmp(cepp->ce_varname, "reject-on-auth-failure")) + else if (!strcmp(cepp->name, "reject-on-auth-failure")) {} - else if (!strcmp(cepp->ce_varname, "sasl")) + else if (!strcmp(cepp->name, "sasl")) { config_error("%s:%d: The option allow::options::sasl no longer exists. " "Please use a require authentication { } block instead, which " "is more flexible and provides the same functionality. See " "https://www.unrealircd.org/docs/Require_authentication_block", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; } else { - config_error_unknownopt(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "allow", cepp->ce_varname); + config_error_unknownopt(cepp->file->filename, + cepp->line_number, "allow", cepp->name); errors++; } } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "allow", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "allow", cep->name); errors++; continue; } } - if (!has_ip && !has_hostname) + if (has_mask && (has_ip || has_hostname)) { - config_error("%s:%d: allow block needs an allow::ip or allow::hostname", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + config_error("%s:%d: The allow block uses allow::mask, but you also have an allow::ip and allow::hostname.", + ce->file->filename, ce->line_number); + config_error("Please delete your allow::ip and allow::hostname entries and/or integrate them into allow::mask"); + } else + if (has_ip) + { + config_warn("%s:%d: The allow block uses allow::mask nowadays. Rename your allow::ip item to allow::mask.", + ce->file->filename, ce->line_number); + config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information"); + } else + if (has_hostname) + { + config_warn("%s:%d: The allow block uses allow::mask nowadays. Rename your allow::hostname item to allow::mask.", + ce->file->filename, ce->line_number); + config_warn("See https://www.unrealircd.org/docs/FAQ#allow-mask for more information"); + } else + if (!has_mask) + { + config_error("%s:%d: allow block needs an allow::mask", + ce->file->filename, ce->line_number); errors++; } if (has_ip && has_hostname) { - config_warn("%s:%d: allow block has both allow::ip and allow::hostname which is no longer permitted.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - need_34_upgrade = 1; + config_error("%s:%d: allow block has both allow::ip and allow::hostname, this is no longer permitted.", + ce->file->filename, ce->line_number); + config_error("Please integrate your allow::ip and allow::hostname items into a single allow::mask block"); + errors++; } else if (hostname_possible_silliness) { - config_warn("%s:%d: allow block contains 'hostname *;'. This means means that users " - "without a valid hostname (unresolved IP's) will be unable to connect. " - "You most likely want to use 'ip *;' instead.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + config_error("%s:%d: allow block contains 'hostname *;'. This means means that users " + "without a valid hostname (unresolved IP's) will be unable to connect. " + "You most likely want to use 'mask *;' instead.", + ce->file->filename, ce->line_number); } if (!has_class) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "allow::class"); errors++; } if (!has_maxperip) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "allow::maxperip"); errors++; } @@ -5781,21 +5598,21 @@ int _conf_allow_channel(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *mask = NULL; /* First, search for ::class, if any */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "class")) - class = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "class")) + class = cep->value; + else if (!strcmp(cep->name, "mask")) mask = cep; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "channel")) + if (!strcmp(cep->name, "channel")) { /* This way, we permit multiple ::channel items in one allow block */ allow = safe_alloc(sizeof(ConfigItem_allow_channel)); - safe_strdup(allow->channel, cep->ce_vardata); + safe_strdup(allow->channel, cep->value); if (class) safe_strdup(allow->class, class); if (mask) @@ -5811,7 +5628,7 @@ int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; int errors = 0; char has_channel = 0, has_class = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "allow channel")) { @@ -5819,34 +5636,34 @@ int _test_allow_channel(ConfigFile *conf, ConfigEntry *ce) continue; } - if (!strcmp(cep->ce_varname, "channel")) + if (!strcmp(cep->name, "channel")) { has_channel = 1; } - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { if (has_class) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow channel::class"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow channel::class"); continue; } has_class = 1; } - else if (!strcmp(cep->ce_varname, "mask")) + else if (!strcmp(cep->name, "mask")) { } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "allow channel", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "allow channel", cep->name); errors++; } } if (!has_channel) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "allow channel::channel"); errors++; } @@ -5874,20 +5691,20 @@ int _test_except(ConfigFile *conf, ConfigEntry *ce) Hook *h; int used = 0; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: except without type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - if (!strcmp(ce->ce_vardata, "tkl")) + if (!strcmp(ce->value, "tkl")) { config_warn("%s:%i: except tkl { } is now called except ban { }. " "Simply rename the block from 'except tkl' to 'except ban' " "to get rid of this warning.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - safe_strdup(ce->ce_vardata, "ban"); /* awww */ + ce->file->filename, ce->line_number); + safe_strdup(ce->value, "ban"); /* awww */ } for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) @@ -5920,8 +5737,8 @@ int _test_except(ConfigFile *conf, ConfigEntry *ce) if (!used) { config_error("%s:%i: unknown except type %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata); + ce->file->filename, ce->line_number, + ce->value); return 1; } @@ -5937,12 +5754,12 @@ int _conf_vhost(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep, *cepp; vhost = safe_alloc(sizeof(ConfigItem_vhost)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "vhost")) + if (!strcmp(cep->name, "vhost")) { char *user, *host; - user = strtok(cep->ce_vardata, "@"); + user = strtok(cep->value, "@"); host = strtok(NULL, ""); if (!host) safe_strdup(vhost->virthost, user); @@ -5952,31 +5769,31 @@ int _conf_vhost(ConfigFile *conf, ConfigEntry *ce) safe_strdup(vhost->virthost, host); } } - else if (!strcmp(cep->ce_varname, "login")) - safe_strdup(vhost->login, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "login")) + safe_strdup(vhost->login, cep->value); + else if (!strcmp(cep->name, "password")) vhost->auth = AuthBlockToAuthConfig(cep); - else if (!strcmp(cep->ce_varname, "mask")) + else if (!strcmp(cep->name, "mask")) { unreal_add_masks(&vhost->mask, cep); } - else if (!strcmp(cep->ce_varname, "swhois")) + else if (!strcmp(cep->name, "swhois")) { SWhois *s; - if (cep->ce_entries) + if (cep->items) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { s = safe_alloc(sizeof(SWhois)); - safe_strdup(s->line, cepp->ce_varname); + safe_strdup(s->line, cepp->name); safe_strdup(s->setby, "vhost"); AddListItem(s, vhost->swhois); } } else - if (cep->ce_vardata) + if (cep->value) { s = safe_alloc(sizeof(SWhois)); - safe_strdup(s->line, cep->ce_vardata); + safe_strdup(s->line, cep->value); safe_strdup(s->setby, "vhost"); AddListItem(s, vhost->swhois); } @@ -5992,30 +5809,30 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; char has_vhost = 0, has_login = 0, has_password = 0, has_mask = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "vhost")) + if (!strcmp(cep->name, "vhost")) { char *at, *tmp, *host; if (has_vhost) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "vhost::vhost"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "vhost::vhost"); continue; } has_vhost = 1; - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "vhost", "vhost"); + config_error_empty(cep->file->filename, + cep->line_number, "vhost", "vhost"); errors++; continue; } - if ((at = strchr(cep->ce_vardata, '@'))) + if ((at = strchr(cep->value, '@'))) { - for (tmp = cep->ce_vardata; tmp != at; tmp++) + for (tmp = cep->value; tmp != at; tmp++) { - if (*tmp == '~' && tmp == cep->ce_vardata) + if (*tmp == '~' && tmp == cep->value) continue; if (!isallowed(*tmp)) break; @@ -6023,112 +5840,103 @@ int _test_vhost(ConfigFile *conf, ConfigEntry *ce) if (tmp != at) { config_error("%s:%i: vhost::vhost contains an invalid ident", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } host = at+1; } else - host = cep->ce_vardata; + host = cep->value; if (!*host) { config_error("%s:%i: vhost::vhost does not have a host set", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } else { - if (!valid_host(host)) + if (!valid_host(host, 0)) { config_error("%s:%i: vhost::vhost contains an invalid host", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } } - else if (!strcmp(cep->ce_varname, "login")) + else if (!strcmp(cep->name, "login")) { if (has_login) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "vhost::login"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "vhost::login"); } has_login = 1; - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "vhost", "login"); + config_error_empty(cep->file->filename, + cep->line_number, "vhost", "login"); errors++; continue; } } - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) { if (has_password) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "vhost::password"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "vhost::password"); } has_password = 1; - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "vhost", "password"); + config_error_empty(cep->file->filename, + cep->line_number, "vhost", "password"); errors++; continue; } if (Auth_CheckError(cep) < 0) errors++; } - else if (!strcmp(cep->ce_varname, "from")) - { - config_error("%s:%i: vhost::from::userhost is now called oper::mask", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); - errors++; - need_34_upgrade = 1; - continue; - } - else if (!strcmp(cep->ce_varname, "mask")) + else if (!strcmp(cep->name, "mask")) { has_mask = 1; } - else if (!strcmp(cep->ce_varname, "swhois")) + else if (!strcmp(cep->name, "swhois")) { /* multiple is ok */ } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "vhost", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "vhost", cep->name); errors++; } } if (!has_vhost) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "vhost::vhost"); errors++; } if (!has_login) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "vhost::login"); errors++; } if (!has_password) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "vhost::password"); errors++; } if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "vhost::mask"); errors++; } - // TODO: 3.2.x -> 4.x upgrading hints return errors; } @@ -6137,22 +5945,22 @@ int _test_sni(ConfigFile *conf, ConfigEntry *ce) int errors = 0; ConfigEntry *cep; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: sni block needs a name, eg: sni irc.xyz.com {", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "ssl-options") || !strcmp(cep->ce_varname, "tls-options")) + if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) { test_tlsblock(conf, cep, &errors); } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "sni", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "sni", cep->name); errors++; continue; } @@ -6168,13 +5976,13 @@ int _conf_sni(ConfigFile *conf, ConfigEntry *ce) char *name; ConfigItem_sni *sni = NULL; - name = ce->ce_vardata; + name = ce->value; if (!name) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "ssl-options") || !strcmp(cep->ce_varname, "tls-options")) + if (!strcmp(cep->name, "ssl-options") || !strcmp(cep->name, "tls-options")) { tlsconfig = cep; } @@ -6200,15 +6008,15 @@ int _conf_help(ConfigFile *conf, ConfigEntry *ce) MOTDLine *last = NULL, *temp; ca = safe_alloc(sizeof(ConfigItem_help)); - if (!ce->ce_vardata) + if (!ce->value) ca->command = NULL; else - safe_strdup(ca->command, ce->ce_vardata); + safe_strdup(ca->command, ce->value); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { temp = safe_alloc(sizeof(MOTDLine)); - safe_strdup(temp->line, cep->ce_varname); + safe_strdup(temp->line, cep->name); temp->next = NULL; if (!last) ca->text = temp; @@ -6224,18 +6032,18 @@ int _conf_help(ConfigFile *conf, ConfigEntry *ce) int _test_help(ConfigFile *conf, ConfigEntry *ce) { int errors = 0; ConfigEntry *cep; - if (!ce->ce_entries) + if (!ce->items) { config_error("%s:%i: empty help block", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (strlen(cep->ce_varname) > 500) + if (strlen(cep->name) > 500) { config_error("%s:%i: oversized help item", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; continue; } @@ -6243,179 +6051,47 @@ int _test_help(ConfigFile *conf, ConfigEntry *ce) { return errors; } -int _conf_log(ConfigFile *conf, ConfigEntry *ce) -{ - ConfigEntry *cep, *cepp; - ConfigItem_log *ca; - NameValue *ofp = NULL; - - ca = safe_alloc(sizeof(ConfigItem_log)); - ca->logfd = -1; - if (strchr(ce->ce_vardata, '%')) - safe_strdup(ca->filefmt, ce->ce_vardata); - else - safe_strdup(ca->file, ce->ce_vardata); - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "maxsize")) - { - ca->maxsize = config_checkval(cep->ce_vardata,CFG_SIZE); - } - else if (!strcmp(cep->ce_varname, "flags")) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if ((ofp = config_binary_flags_search(_LogFlags, cepp->ce_varname, ARRAY_SIZEOF(_LogFlags)))) - ca->flags |= ofp->flag; - } - } - } - AddListItem(ca, conf_log); - return 1; - -} - -int _test_log(ConfigFile *conf, ConfigEntry *ce) { - int fd, errors = 0; - ConfigEntry *cep, *cepp; - char has_flags = 0, has_maxsize = 0; - char *fname; - - if (!ce->ce_vardata) - { - config_error("%s:%i: log block without filename", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - return 1; - } - if (!ce->ce_entries) - { - config_error("%s:%i: empty log block", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - return 1; - } - - /* Convert to absolute path (if needed) unless it's "syslog" */ - if (strcmp(ce->ce_vardata, "syslog")) - convert_to_absolute_path(&ce->ce_vardata, LOGDIR); - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "flags")) - { - if (has_flags) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "log::flags"); - continue; - } - has_flags = 1; - if (!cep->ce_entries) - { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "log", cep->ce_varname); - errors++; - continue; - } - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (!config_binary_flags_search(_LogFlags, cepp->ce_varname, ARRAY_SIZEOF(_LogFlags))) - { - config_error_unknownflag(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "log", cepp->ce_varname); - errors++; - } - } - } - else if (!strcmp(cep->ce_varname, "maxsize")) - { - if (has_maxsize) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "log::maxsize"); - continue; - } - has_maxsize = 1; - if (!cep->ce_vardata) - { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "log", cep->ce_varname); - errors++; - } - } - else - { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "log", cep->ce_varname); - errors++; - continue; - } - } - - if (!has_flags) - { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "log::flags"); - errors++; - } - - fname = unreal_strftime(ce->ce_vardata); - if ((fd = fd_fileopen(fname, O_WRONLY|O_CREAT)) == -1) - { - config_error("%s:%i: Couldn't open logfile (%s) for writing: %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - fname, strerror(errno)); - errors++; - } else - { - fd_close(fd); - } - - return errors; -} - int _conf_link(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp, *ceppp; ConfigItem_link *link = NULL; - NameValue *ofp; link = safe_alloc(sizeof(ConfigItem_link)); - safe_strdup(link->servername, ce->ce_vardata); + safe_strdup(link->servername, ce->value); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "incoming")) + if (!strcmp(cep->name, "incoming")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "mask")) + if (!strcmp(cepp->name, "mask")) { unreal_add_masks(&link->incoming.mask, cepp); } } } - else if (!strcmp(cep->ce_varname, "outgoing")) + else if (!strcmp(cep->name, "outgoing")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "bind-ip")) - safe_strdup(link->outgoing.bind_ip, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "hostname")) - safe_strdup(link->outgoing.hostname, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "port")) - link->outgoing.port = atoi(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "options")) + if (!strcmp(cepp->name, "bind-ip")) + safe_strdup(link->outgoing.bind_ip, cepp->value); + else if (!strcmp(cepp->name, "hostname")) + safe_strdup(link->outgoing.hostname, cepp->value); + else if (!strcmp(cepp->name, "port")) + link->outgoing.port = atoi(cepp->value); + else if (!strcmp(cepp->name, "options")) { - /* TODO: options still need to be split */ link->outgoing.options = 0; - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - if ((ofp = config_binary_flags_search(_LinkFlags, ceppp->ce_varname, ARRAY_SIZEOF(_LinkFlags)))) - link->outgoing.options |= ofp->flag; + long v; + if ((v = nv_find_by_name(_LinkFlags, ceppp->name))) + link->outgoing.options |= v; } } - else if (!strcmp(cepp->ce_varname, "ssl-options") || !strcmp(cepp->ce_varname, "tls-options")) + else if (!strcmp(cepp->name, "ssl-options") || !strcmp(cepp->name, "tls-options")) { link->tls_options = safe_alloc(sizeof(TLSOptions)); conf_tlsblock(conf, cepp, link->tls_options); @@ -6423,38 +6099,39 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce) } } } - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) link->auth = AuthBlockToAuthConfig(cep); - else if (!strcmp(cep->ce_varname, "hub")) - safe_strdup(link->hub, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "leaf")) - safe_strdup(link->leaf, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "leaf-depth") || !strcmp(cep->ce_varname, "leafdepth")) - link->leaf_depth = atoi(cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "hub")) + safe_strdup(link->hub, cep->value); + else if (!strcmp(cep->name, "leaf")) + safe_strdup(link->leaf, cep->value); + else if (!strcmp(cep->name, "leaf-depth") || !strcmp(cep->name, "leafdepth")) + link->leaf_depth = atoi(cep->value); + else if (!strcmp(cep->name, "class")) { - link->class = find_class(cep->ce_vardata); + link->class = find_class(cep->value); if (!link->class || (link->class->flag.temporary == 1)) { config_status("%s:%i: illegal link::class, unknown class '%s' using default of class 'default'", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - cep->ce_vardata); + cep->file->filename, + cep->line_number, + cep->value); link->class = default_class; } link->class->xrefcount++; } - else if (!strcmp(cep->ce_varname, "verify-certificate")) + else if (!strcmp(cep->name, "verify-certificate")) { - link->verify_certificate = config_checkval(cep->ce_vardata, CFG_YESNO); + link->verify_certificate = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "options")) + else if (!strcmp(cep->name, "options")) { link->options = 0; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if ((ofp = config_binary_flags_search(_LinkFlags, cepp->ce_varname, ARRAY_SIZEOF(_LinkFlags)))) - link->options |= ofp->flag; + long v; + if ((v = nv_find_by_name(_LinkFlags, cepp->name))) + link->options |= v; } } } @@ -6463,20 +6140,19 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce) if (!link->hub && !link->leaf) safe_strdup(link->hub, "*"); - AddListItem(link, conf_link); + AppendListItem(link, conf_link); return 0; } /** Helper function for erroring on duplicate items. - * TODO: make even more friendy for dev's? */ int config_detect_duplicate(int *var, ConfigEntry *ce, int *errors) { if (*var) { config_error("%s:%d: Duplicate %s directive", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_varname); + ce->file->filename, ce->line_number, + ce->name); (*errors)++; return 1; } else { @@ -6495,31 +6171,31 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) int has_outgoing_options = 0, has_hub = 0, has_leaf = 0, has_leaf_depth = 0; int has_password = 0, has_class = 0, has_options = 0; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: link without servername. Expected: link servername { ... }", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - if (!strchr(ce->ce_vardata, '.')) + if (!strchr(ce->value, '.')) { config_error("%s:%i: link: bogus server name. Expected: link servername { ... }", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "incoming")) + if (!strcmp(cep->name, "incoming")) { config_detect_duplicate(&has_incoming, cep, &errors); - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "mask")) + if (!strcmp(cepp->name, "mask")) { - if (cepp->ce_vardata || cepp->ce_entries) + if (cepp->value || cepp->items) has_incoming_mask = 1; else if (config_is_blankorempty(cepp, "link::incoming")) @@ -6530,12 +6206,12 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) } } } - else if (!strcmp(cep->ce_varname, "outgoing")) + else if (!strcmp(cep->name, "outgoing")) { config_detect_duplicate(&has_outgoing, cep, &errors); - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "bind-ip")) + if (!strcmp(cepp->name, "bind-ip")) { if (config_is_blankorempty(cepp, "link::outgoing")) { @@ -6545,7 +6221,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) config_detect_duplicate(&has_outgoing_bind_ip, cepp, &errors); // todo: ipv4 vs ipv6 } - else if (!strcmp(cepp->ce_varname, "hostname")) + else if (!strcmp(cepp->name, "hostname")) { if (config_is_blankorempty(cepp, "link::outgoing")) { @@ -6553,14 +6229,14 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) continue; } config_detect_duplicate(&has_outgoing_hostname, cepp, &errors); - if (strchr(cepp->ce_vardata, '*') || strchr(cepp->ce_vardata, '?')) + if (strchr(cepp->value, '*') || strchr(cepp->value, '?')) { config_error("%s:%i: hostname in link::outgoing(!) cannot contain wildcards", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; } } - else if (!strcmp(cepp->ce_varname, "port")) + else if (!strcmp(cepp->name, "port")) { if (config_is_blankorempty(cepp, "link::outgoing")) { @@ -6569,40 +6245,39 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) } config_detect_duplicate(&has_outgoing_port, cepp, &errors); } - else if (!strcmp(cepp->ce_varname, "options")) + else if (!strcmp(cepp->name, "options")) { config_detect_duplicate(&has_outgoing_options, cepp, &errors); - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - if (!strcmp(ceppp->ce_varname, "autoconnect")) + if (!strcmp(ceppp->name, "autoconnect")) ; - else if (!strcmp(ceppp->ce_varname, "ssl") || !strcmp(ceppp->ce_varname, "tls")) + else if (!strcmp(ceppp->name, "ssl") || !strcmp(ceppp->name, "tls")) ; - else if (!strcmp(ceppp->ce_varname, "insecure")) + else if (!strcmp(ceppp->name, "insecure")) ; else { - config_error_unknownopt(ceppp->ce_fileptr->cf_filename, - ceppp->ce_varlinenum, "link::outgoing", ceppp->ce_varname); + config_error_unknownopt(ceppp->file->filename, + ceppp->line_number, "link::outgoing", ceppp->name); errors++; } - // TODO: validate more options (?) and use list rather than code here... } } - else if (!strcmp(cepp->ce_varname, "ssl-options") || !strcmp(cepp->ce_varname, "tls-options")) + else if (!strcmp(cepp->name, "ssl-options") || !strcmp(cepp->name, "tls-options")) { test_tlsblock(conf, cepp, &errors); } else { config_error("%s:%d: Unknown directive '%s'", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, + cepp->file->filename, cepp->line_number, config_var(cepp)); errors++; } } } - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) { config_detect_duplicate(&has_password, cep, &errors); if (Auth_CheckError(cep) < 0) @@ -6615,15 +6290,14 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) (auth->type != AUTHTYPE_TLS_CLIENTCERTFP) && (auth->type != AUTHTYPE_SPKIFP)) { config_error("%s:%i: password in link block should be plaintext OR should be the " - "SSL or SPKI fingerprint of the remote link (=better)", - /* TODO: mention some faq or wiki item for more information */ - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + "certificate or SPKI fingerprint of the remote link (=better)", + cep->file->filename, cep->line_number); errors++; } Auth_FreeAuthConfig(auth); } } - else if (!strcmp(cep->ce_varname, "hub")) + else if (!strcmp(cep->name, "hub")) { if (config_is_blankorempty(cep, "link")) { @@ -6632,7 +6306,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) } config_detect_duplicate(&has_hub, cep, &errors); } - else if (!strcmp(cep->ce_varname, "leaf")) + else if (!strcmp(cep->name, "leaf")) { if (config_is_blankorempty(cep, "link")) { @@ -6641,7 +6315,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) } config_detect_duplicate(&has_leaf, cep, &errors); } - else if (!strcmp(cep->ce_varname, "leaf-depth") || !strcmp(cep->ce_varname, "leafdepth")) + else if (!strcmp(cep->name, "leaf-depth") || !strcmp(cep->name, "leafdepth")) { if (config_is_blankorempty(cep, "link")) { @@ -6650,7 +6324,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) } config_detect_duplicate(&has_leaf_depth, cep, &errors); } - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { if (config_is_blankorempty(cep, "link")) { @@ -6659,14 +6333,14 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) } config_detect_duplicate(&has_class, cep, &errors); } - else if (!strcmp(cep->ce_varname, "ciphers")) + else if (!strcmp(cep->name, "ciphers")) { config_error("%s:%d: link::ciphers has been moved to link::outgoing::ssl-options::ciphers, " "see https://www.unrealircd.org/docs/FAQ#link::ciphers_no_longer_works", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } - else if (!strcmp(cep->ce_varname, "verify-certificate")) + else if (!strcmp(cep->name, "verify-certificate")) { if (config_is_blankorempty(cep, "link")) { @@ -6674,27 +6348,27 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) continue; } } - else if (!strcmp(cep->ce_varname, "options")) + else if (!strcmp(cep->name, "options")) { config_detect_duplicate(&has_options, cep, &errors); - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "quarantine")) + if (!strcmp(cepp->name, "quarantine")) ; else { config_error("%s:%d: link::options only has one possible option ('quarantine', rarely used). " "Option '%s' is unrecognized. " "Perhaps you meant to set an outgoing option in link::outgoing::options instead?", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); + cepp->file->filename, cepp->line_number, cepp->name); errors++; } } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "link", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "link", cep->name); errors++; continue; } @@ -6703,9 +6377,8 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) if (!has_incoming && !has_outgoing) { config_error("%s:%d: link block needs at least an incoming or outgoing section.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; - need_34_upgrade = 1; } if (has_incoming) @@ -6713,7 +6386,7 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) /* If we have an incoming sub-block then we need at least 'mask' and 'password' */ if (!has_incoming_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::incoming::mask"); + config_error_missing(ce->file->filename, ce->line_number, "link::incoming::mask"); errors++; } } @@ -6723,12 +6396,12 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) /* If we have an outgoing sub-block then we need at least a hostname and port */ if (!has_outgoing_hostname) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::outgoing::hostname"); + config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::hostname"); errors++; } if (!has_outgoing_port) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::outgoing::port"); + config_error_missing(ce->file->filename, ce->line_number, "link::outgoing::port"); errors++; } } @@ -6736,12 +6409,12 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) /* The only other generic options that are required are 'class' and 'password' */ if (!has_password) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, "link::password"); + config_error_missing(ce->file->filename, ce->line_number, "link::password"); errors++; } if (!has_class) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "link::class"); errors++; } @@ -6756,11 +6429,11 @@ int _conf_ban(ConfigFile *conf, ConfigEntry *ce) Hook *h; ca = safe_alloc(sizeof(ConfigItem_ban)); - if (!strcmp(ce->ce_vardata, "realname")) + if (!strcmp(ce->value, "realname")) ca->flag.type = CONF_BAN_REALNAME; - else if (!strcmp(ce->ce_vardata, "server")) + else if (!strcmp(ce->value, "server")) ca->flag.type = CONF_BAN_SERVER; - else if (!strcmp(ce->ce_vardata, "version")) + else if (!strcmp(ce->value, "version")) { ca->flag.type = CONF_BAN_VERSION; tempiConf.use_ban_version = 1; /* enable CTCP VERSION on connect */ @@ -6776,16 +6449,16 @@ int _conf_ban(ConfigFile *conf, ConfigEntry *ce) } return 0; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - safe_strdup(ca->mask, cep->ce_vardata); + safe_strdup(ca->mask, cep->value); } - else if (!strcmp(cep->ce_varname, "reason")) - safe_strdup(ca->reason, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "action")) - ca->action = banact_stringtoval(cep->ce_vardata); + else if (!strcmp(cep->name, "reason")) + safe_strdup(ca->reason, cep->value); + else if (!strcmp(cep->name, "action")) + ca->action = banact_stringtoval(cep->value); } AddListItem(ca, conf_ban); return 0; @@ -6799,17 +6472,17 @@ int _test_ban(ConfigFile *conf, ConfigEntry *ce) char type = 0; char has_mask = 0, has_action = 0, has_reason = 0; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: ban without type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - else if (!strcmp(ce->ce_vardata, "server")) + else if (!strcmp(ce->value, "server")) {} - else if (!strcmp(ce->ce_vardata, "realname")) + else if (!strcmp(ce->value, "realname")) {} - else if (!strcmp(ce->ce_vardata, "version")) + else if (!strcmp(ce->value, "version")) type = 'v'; else { @@ -6842,53 +6515,53 @@ int _test_ban(ConfigFile *conf, ConfigEntry *ce) } if (!used) { config_error("%s:%i: unknown ban type %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata); + ce->file->filename, ce->line_number, + ce->value); return 1; } return errors; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "ban")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { if (has_mask) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "ban::mask"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "ban::mask"); continue; } has_mask = 1; } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { if (has_reason) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "ban::reason"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "ban::reason"); continue; } has_reason = 1; } - else if (!strcmp(cep->ce_varname, "action")) + else if (!strcmp(cep->name, "action")) { if (has_action) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "ban::action"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "ban::action"); } has_action = 1; - if (!banact_stringtoval(cep->ce_vardata)) + if (!banact_stringtoval(cep->value)) { config_error("%s:%i: ban::action has unknown action type '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata); + cep->file->filename, cep->line_number, + cep->value); errors++; } } @@ -6896,20 +6569,20 @@ int _test_ban(ConfigFile *conf, ConfigEntry *ce) if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "ban::mask"); errors++; } if (!has_reason) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "ban::reason"); errors++; } if (has_action && type != 'v') { config_error("%s:%d: ban::action specified even though type is not 'version'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } return errors; @@ -6923,7 +6596,7 @@ int _conf_require(ConfigFile *conf, ConfigEntry *ce) char *hostmask = NULL; char *reason = NULL; - if (strcmp(ce->ce_vardata, "authentication") && strcmp(ce->ce_vardata, "sasl")) + if (strcmp(ce->value, "authentication") && strcmp(ce->value, "sasl")) { /* Some other block... run modules... */ int value; @@ -6936,12 +6609,12 @@ int _conf_require(ConfigFile *conf, ConfigEntry *ce) return 0; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { char buf[512], *p; - strlcpy(buf, cep->ce_vardata, sizeof(buf)); + strlcpy(buf, cep->value, sizeof(buf)); p = strchr(buf, '@'); if (p) { @@ -6949,11 +6622,11 @@ int _conf_require(ConfigFile *conf, ConfigEntry *ce) safe_strdup(usermask, buf); safe_strdup(hostmask, p); } else { - safe_strdup(hostmask, cep->ce_vardata); + safe_strdup(hostmask, cep->value); } } - else if (!strcmp(cep->ce_varname, "reason")) - safe_strdup(reason, cep->ce_vardata); + else if (!strcmp(cep->name, "reason")) + safe_strdup(reason, cep->value); } if (!usermask) @@ -6976,18 +6649,18 @@ int _test_require(ConfigFile *conf, ConfigEntry *ce) Hook *h; char has_mask = 0, has_reason = 0; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: require without type, did you mean 'require authentication'?", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - if (!strcmp(ce->ce_vardata, "authentication")) + if (!strcmp(ce->value, "authentication")) {} - else if (!strcmp(ce->ce_vardata, "sasl")) + else if (!strcmp(ce->value, "sasl")) { config_warn("%s:%i: the 'require sasl' block is now called 'require authentication'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); } else { @@ -7020,36 +6693,36 @@ int _test_require(ConfigFile *conf, ConfigEntry *ce) } if (!used) { config_error("%s:%i: unknown require type '%s'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata); + ce->file->filename, ce->line_number, + ce->value); return 1; } return errors; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "require")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { if (has_mask) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "require::mask"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "require::mask"); continue; } has_mask = 1; } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { if (has_reason) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "require::reason"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "require::reason"); continue; } has_reason = 1; @@ -7058,45 +6731,43 @@ int _test_require(ConfigFile *conf, ConfigEntry *ce) if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "require::mask"); errors++; } if (!has_reason) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "require::reason"); errors++; } return errors; } -#define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } -#define CheckNullAllowEmpty(x) if ((!(x)->ce_vardata)) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } -#define CheckDuplicate(cep, name, display) if (settings.has_##name) { config_warn_duplicate((cep)->ce_fileptr->cf_filename, cep->ce_varlinenum, "set::" display); continue; } else settings.has_##name = 1 +#define CheckDuplicate(cep, name, display) if (settings.has_##name) { config_warn_duplicate((cep)->file->filename, cep->line_number, "set::" display); continue; } else settings.has_##name = 1 void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) { ConfigEntry *cepp, *ceppp; int errors = 0; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "renegotiate-timeout")) + if (!strcmp(cepp->name, "renegotiate-timeout")) { } - else if (!strcmp(cepp->ce_varname, "renegotiate-bytes")) + else if (!strcmp(cepp->name, "renegotiate-bytes")) { } - else if (!strcmp(cepp->ce_varname, "ciphers") || !strcmp(cepp->ce_varname, "server-cipher-list")) + else if (!strcmp(cepp->name, "ciphers") || !strcmp(cepp->name, "server-cipher-list")) { CheckNull(cepp); } - else if (!strcmp(cepp->ce_varname, "ciphersuites")) + else if (!strcmp(cepp->name, "ciphersuites")) { CheckNull(cepp); } - else if (!strcmp(cepp->ce_varname, "ecdh-curves")) + else if (!strcmp(cepp->name, "ecdh-curves")) { CheckNull(cepp); #ifndef HAS_SSL_CTX_SET1_CURVES_LIST @@ -7107,7 +6778,7 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) errors++; #endif } - else if (!strcmp(cepp->ce_varname, "protocols")) + else if (!strcmp(cepp->name, "protocols")) { char copy[512], *p, *name; int v = 0; @@ -7115,7 +6786,7 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) char modifier; CheckNull(cepp); - strlcpy(copy, cepp->ce_vardata, sizeof(copy)); + strlcpy(copy, cepp->value, sizeof(copy)); for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ",")) { modifier = '\0'; @@ -7142,11 +6813,11 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) #ifdef SSL_OP_NO_TLSv1_3 config_warn("%s:%i: %s: unknown protocol '%s'. " "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2,TLSv1.3", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, config_var(cepp), name); + cepp->file->filename, cepp->line_number, config_var(cepp), name); #else config_warn("%s:%i: %s: unknown protocol '%s'. " "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, config_var(cepp), name); + cepp->file->filename, cepp->line_number, config_var(cepp), name); #endif } @@ -7163,28 +6834,28 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) if (v == 0) { config_error("%s:%i: %s: no protocols enabled. Hint: set at least TLSv1.2", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, config_var(cepp)); + cepp->file->filename, cepp->line_number, config_var(cepp)); errors++; } } - else if (!strcmp(cepp->ce_varname, "certificate") || - !strcmp(cepp->ce_varname, "key") || - !strcmp(cepp->ce_varname, "trusted-ca-file")) + else if (!strcmp(cepp->name, "certificate") || + !strcmp(cepp->name, "key") || + !strcmp(cepp->name, "trusted-ca-file")) { char *path; CheckNull(cepp); - path = convert_to_absolute_path_duplicate(cepp->ce_vardata, CONFDIR); + path = convert_to_absolute_path_duplicate(cepp->value, CONFDIR); if (!file_exists(path)) { config_error("%s:%i: %s: could not open '%s': %s", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, config_var(cepp), + cepp->file->filename, cepp->line_number, config_var(cepp), path, strerror(errno)); safe_free(path); errors++; } safe_free(path); } - else if (!strcmp(cepp->ce_varname, "dh")) + else if (!strcmp(cepp->name, "dh")) { /* Support for this undocumented option was silently dropped in 5.0.0. * Since 5.0.7 we print a warning about it, since you never know @@ -7192,10 +6863,10 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) */ config_warn("%s:%d: Not reading DH file '%s'. UnrealIRCd does not support old DH(E), we use modern ECDHE/EECDH. " "Just remove the 'dh' directive from your config file to get rid of this warning.", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, - cepp->ce_vardata ? cepp->ce_vardata : ""); + cepp->file->filename, cepp->line_number, + cepp->value ? cepp->value : ""); } - else if (!strcmp(cepp->ce_varname, "outdated-protocols")) + else if (!strcmp(cepp->name, "outdated-protocols")) { char copy[512], *p, *name; int v = 0; @@ -7203,7 +6874,7 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) char modifier; CheckNull(cepp); - strlcpy(copy, cepp->ce_vardata, sizeof(copy)); + strlcpy(copy, cepp->value, sizeof(copy)); for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ",")) { if (!strcasecmp(name, "All")) @@ -7221,64 +6892,66 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) #ifdef SSL_OP_NO_TLSv1_3 config_warn("%s:%i: %s: unknown protocol '%s'. " "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2,TLSv1.3", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, config_var(cepp), name); + cepp->file->filename, cepp->line_number, config_var(cepp), name); #else config_warn("%s:%i: %s: unknown protocol '%s'. " "Valid protocols are: TLSv1,TLSv1.1,TLSv1.2", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, config_var(cepp), name); + cepp->file->filename, cepp->line_number, config_var(cepp), name); #endif } } } - else if (!strcmp(cepp->ce_varname, "outdated-ciphers")) + else if (!strcmp(cepp->name, "outdated-ciphers")) { CheckNull(cepp); } - else if (!strcmp(cepp->ce_varname, "options")) + else if (!strcmp(cepp->name, "options")) { - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) - if (!config_binary_flags_search(_TLSFlags, ceppp->ce_varname, ARRAY_SIZEOF(_TLSFlags))) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) + { + if (!nv_find_by_name(_TLSFlags, ceppp->name)) { - config_error("%s:%i: unknown SSL/TLS option '%s'", - ceppp->ce_fileptr->cf_filename, - ceppp->ce_varlinenum, ceppp->ce_varname); + config_error("%s:%i: unknown TLS option '%s'", + ceppp->file->filename, + ceppp->line_number, ceppp->name); errors ++; } + } } - else if (!strcmp(cepp->ce_varname, "sts-policy")) + else if (!strcmp(cepp->name, "sts-policy")) { int has_port = 0; int has_duration = 0; - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - if (!strcmp(ceppp->ce_varname, "port")) + if (!strcmp(ceppp->name, "port")) { int port; CheckNull(ceppp); - port = atoi(ceppp->ce_vardata); + port = atoi(ceppp->value); if ((port < 1) || (port > 65535)) { config_error("%s:%i: invalid port number specified in sts-policy::port (%d)", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, port); + ceppp->file->filename, ceppp->line_number, port); errors++; } has_port = 1; } - else if (!strcmp(ceppp->ce_varname, "duration")) + else if (!strcmp(ceppp->name, "duration")) { long duration; CheckNull(ceppp); - duration = config_checkval(ceppp->ce_vardata, CFG_TIME); + duration = config_checkval(ceppp->value, CFG_TIME); if (duration < 1) { config_error("%s:%i: invalid duration specified in sts-policy::duration (%ld seconds)", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, duration); + ceppp->file->filename, ceppp->line_number, duration); errors++; } has_duration = 1; } - else if (!strcmp(ceppp->ce_varname, "preload")) + else if (!strcmp(ceppp->name, "preload")) { CheckNull(ceppp); } @@ -7286,20 +6959,20 @@ void test_tlsblock(ConfigFile *conf, ConfigEntry *cep, int *totalerrors) if (!has_port) { config_error("%s:%i: sts-policy block without port", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; } if (!has_duration) { config_error("%s:%i: sts-policy block without duration", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; } } else { config_error("%s:%i: unknown directive %s", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, + cepp->file->filename, cepp->line_number, config_var(cepp)); errors++; } @@ -7351,27 +7024,27 @@ void conf_tlsblock(ConfigFile *conf, ConfigEntry *cep, TLSOptions *tlsoptions) } /* Now process the options */ - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "ciphers") || !strcmp(cepp->ce_varname, "server-cipher-list")) + if (!strcmp(cepp->name, "ciphers") || !strcmp(cepp->name, "server-cipher-list")) { - safe_strdup(tlsoptions->ciphers, cepp->ce_vardata); + safe_strdup(tlsoptions->ciphers, cepp->value); } - else if (!strcmp(cepp->ce_varname, "ciphersuites")) + else if (!strcmp(cepp->name, "ciphersuites")) { - safe_strdup(tlsoptions->ciphersuites, cepp->ce_vardata); + safe_strdup(tlsoptions->ciphersuites, cepp->value); } - else if (!strcmp(cepp->ce_varname, "ecdh-curves")) + else if (!strcmp(cepp->name, "ecdh-curves")) { - safe_strdup(tlsoptions->ecdh_curves, cepp->ce_vardata); + safe_strdup(tlsoptions->ecdh_curves, cepp->value); } - else if (!strcmp(cepp->ce_varname, "protocols")) + else if (!strcmp(cepp->name, "protocols")) { char copy[512], *p, *name; int option; char modifier; - strlcpy(copy, cepp->ce_vardata, sizeof(copy)); + strlcpy(copy, cepp->value, sizeof(copy)); tlsoptions->protocols = 0; for (name = strtoken(&p, copy, ","); name; name = strtoken(&p, NULL, ",")) { @@ -7406,61 +7079,60 @@ void conf_tlsblock(ConfigFile *conf, ConfigEntry *cep, TLSOptions *tlsoptions) } } } - else if (!strcmp(cepp->ce_varname, "certificate")) + else if (!strcmp(cepp->name, "certificate")) { - convert_to_absolute_path(&cepp->ce_vardata, CONFDIR); - safe_strdup(tlsoptions->certificate_file, cepp->ce_vardata); + convert_to_absolute_path(&cepp->value, CONFDIR); + safe_strdup(tlsoptions->certificate_file, cepp->value); } - else if (!strcmp(cepp->ce_varname, "key")) + else if (!strcmp(cepp->name, "key")) { - convert_to_absolute_path(&cepp->ce_vardata, CONFDIR); - safe_strdup(tlsoptions->key_file, cepp->ce_vardata); + convert_to_absolute_path(&cepp->value, CONFDIR); + safe_strdup(tlsoptions->key_file, cepp->value); } - else if (!strcmp(cepp->ce_varname, "trusted-ca-file")) + else if (!strcmp(cepp->name, "trusted-ca-file")) { - convert_to_absolute_path(&cepp->ce_vardata, CONFDIR); - safe_strdup(tlsoptions->trusted_ca_file, cepp->ce_vardata); + convert_to_absolute_path(&cepp->value, CONFDIR); + safe_strdup(tlsoptions->trusted_ca_file, cepp->value); } - else if (!strcmp(cepp->ce_varname, "outdated-protocols")) + else if (!strcmp(cepp->name, "outdated-protocols")) { - safe_strdup(tlsoptions->outdated_protocols, cepp->ce_vardata); + safe_strdup(tlsoptions->outdated_protocols, cepp->value); } - else if (!strcmp(cepp->ce_varname, "outdated-ciphers")) + else if (!strcmp(cepp->name, "outdated-ciphers")) { - safe_strdup(tlsoptions->outdated_ciphers, cepp->ce_vardata); + safe_strdup(tlsoptions->outdated_ciphers, cepp->value); } - else if (!strcmp(cepp->ce_varname, "renegotiate-bytes")) + else if (!strcmp(cepp->name, "renegotiate-bytes")) { - tlsoptions->renegotiate_bytes = config_checkval(cepp->ce_vardata, CFG_SIZE); + tlsoptions->renegotiate_bytes = config_checkval(cepp->value, CFG_SIZE); } - else if (!strcmp(cepp->ce_varname, "renegotiate-timeout")) + else if (!strcmp(cepp->name, "renegotiate-timeout")) { - tlsoptions->renegotiate_timeout = config_checkval(cepp->ce_vardata, CFG_TIME); + tlsoptions->renegotiate_timeout = config_checkval(cepp->value, CFG_TIME); } - else if (!strcmp(cepp->ce_varname, "options")) + else if (!strcmp(cepp->name, "options")) { tlsoptions->options = 0; - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - ofl = config_binary_flags_search(_TLSFlags, ceppp->ce_varname, ARRAY_SIZEOF(_TLSFlags)); - if (ofl) /* this should always be true */ - tlsoptions->options |= ofl->flag; + long v = nv_find_by_name(_TLSFlags, ceppp->name); + tlsoptions->options |= v; } } - else if (!strcmp(cepp->ce_varname, "sts-policy")) + else if (!strcmp(cepp->name, "sts-policy")) { /* We do not inherit ::sts-policy if there is a specific block for this one... */ tlsoptions->sts_port = 0; tlsoptions->sts_duration = 0; tlsoptions->sts_preload = 0; - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - if (!strcmp(ceppp->ce_varname, "port")) - tlsoptions->sts_port = atoi(ceppp->ce_vardata); - else if (!strcmp(ceppp->ce_varname, "duration")) - tlsoptions->sts_duration = config_checkval(ceppp->ce_vardata, CFG_TIME); - else if (!strcmp(ceppp->ce_varname, "preload")) - tlsoptions->sts_preload = config_checkval(ceppp->ce_vardata, CFG_YESNO); + if (!strcmp(ceppp->name, "port")) + tlsoptions->sts_port = atoi(ceppp->value); + else if (!strcmp(ceppp->name, "duration")) + tlsoptions->sts_duration = config_checkval(ceppp->value, CFG_TIME); + else if (!strcmp(ceppp->name, "preload")) + tlsoptions->sts_preload = config_checkval(ceppp->value, CFG_YESNO); } } } @@ -7471,258 +7143,291 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep, *cepp, *ceppp, *cep4; Hook *h; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "kline-address")) { - safe_strdup(tempiConf.kline_address, cep->ce_vardata); + if (!strcmp(cep->name, "kline-address")) { + safe_strdup(tempiConf.kline_address, cep->value); } - if (!strcmp(cep->ce_varname, "gline-address")) { - safe_strdup(tempiConf.gline_address, cep->ce_vardata); + if (!strcmp(cep->name, "gline-address")) { + safe_strdup(tempiConf.gline_address, cep->value); } - else if (!strcmp(cep->ce_varname, "modes-on-connect")) { - tempiConf.conn_modes = (long) set_usermode(cep->ce_vardata); + else if (!strcmp(cep->name, "modes-on-connect")) { + tempiConf.conn_modes = (long) set_usermode(cep->value); } - else if (!strcmp(cep->ce_varname, "modes-on-oper")) { - tempiConf.oper_modes = (long) set_usermode(cep->ce_vardata); + else if (!strcmp(cep->name, "modes-on-oper")) { + tempiConf.oper_modes = (long) set_usermode(cep->value); } - else if (!strcmp(cep->ce_varname, "modes-on-join")) { - conf_channelmodes(cep->ce_vardata, &tempiConf.modes_on_join, 0); + else if (!strcmp(cep->name, "modes-on-join")) { + conf_channelmodes(cep->value, &tempiConf.modes_on_join); + tempiConf.modes_on_join_set = 1; } - else if (!strcmp(cep->ce_varname, "snomask-on-oper")) { - safe_strdup(tempiConf.oper_snomask, cep->ce_vardata); + else if (!strcmp(cep->name, "snomask-on-oper")) { + safe_strdup(tempiConf.oper_snomask, cep->value); } - else if (!strcmp(cep->ce_varname, "level-on-join")) { - tempiConf.level_on_join = channellevel_to_int(cep->ce_vardata); + else if (!strcmp(cep->name, "server-notice-colors")) { + tempiConf.server_notice_colors = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "static-quit")) { - safe_strdup(tempiConf.static_quit, cep->ce_vardata); + else if (!strcmp(cep->name, "level-on-join")) { + const char *res = channellevel_to_string(cep->value); /* 'halfop', etc */ + if (!res) + { + /* This check needs to be here, in config run, because + * now the channel modules are initialized and we know + * which ones are available. This same information is + * not available during config test, so we can't test + * for it there like we normally do. + */ + if (!valid_channel_access_mode_letter(*cep->value)) + { + config_warn("%s:%d: set::level-on-join: Unknown mode (access level) '%c'. " + "That mode does not exist or is not a valid access mode " + "like vhoaq.", + cep->file->filename, cep->line_number, + *cep->value); + config_warn("Falling back to to set::level-on-join none; now. " + "This is probably not what you want!!!"); + } + res = cep->value; /* if we reach this.. then it is a single letter */ + } + safe_strdup(tempiConf.level_on_join, res); } - else if (!strcmp(cep->ce_varname, "static-part")) { - safe_strdup(tempiConf.static_part, cep->ce_vardata); + else if (!strcmp(cep->name, "static-quit")) { + safe_strdup(tempiConf.static_quit, cep->value); } - else if (!strcmp(cep->ce_varname, "who-limit")) { - tempiConf.who_limit = atol(cep->ce_vardata); + else if (!strcmp(cep->name, "static-part")) { + safe_strdup(tempiConf.static_part, cep->value); } - else if (!strcmp(cep->ce_varname, "maxbans")) { - tempiConf.maxbans = atol(cep->ce_vardata); + else if (!strcmp(cep->name, "who-limit")) { + tempiConf.who_limit = atol(cep->value); } - else if (!strcmp(cep->ce_varname, "maxbanlength")) { - tempiConf.maxbanlength = atol(cep->ce_vardata); + else if (!strcmp(cep->name, "maxbans")) { + tempiConf.maxbans = atol(cep->value); } - else if (!strcmp(cep->ce_varname, "silence-limit")) { - tempiConf.silence_limit = atol(cep->ce_vardata); + else if (!strcmp(cep->name, "maxbanlength")) { + tempiConf.maxbanlength = atol(cep->value); } - else if (!strcmp(cep->ce_varname, "auto-join")) { - safe_strdup(tempiConf.auto_join_chans, cep->ce_vardata); + else if (!strcmp(cep->name, "silence-limit")) { + tempiConf.silence_limit = atol(cep->value); } - else if (!strcmp(cep->ce_varname, "oper-auto-join")) { - safe_strdup(tempiConf.oper_auto_join_chans, cep->ce_vardata); + else if (!strcmp(cep->name, "auto-join")) { + safe_strdup(tempiConf.auto_join_chans, cep->value); } - else if (!strcmp(cep->ce_varname, "check-target-nick-bans")) { - tempiConf.check_target_nick_bans = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->name, "oper-auto-join")) { + safe_strdup(tempiConf.oper_auto_join_chans, cep->value); } - else if (!strcmp(cep->ce_varname, "ping-cookie")) { - tempiConf.ping_cookie = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->name, "check-target-nick-bans")) { + tempiConf.check_target_nick_bans = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "watch-away-notification")) { - tempiConf.watch_away_notification = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->name, "ping-cookie")) { + tempiConf.ping_cookie = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "uhnames")) { - tempiConf.uhnames = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->name, "watch-away-notification")) { + tempiConf.watch_away_notification = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "allow-userhost-change")) { - if (!strcasecmp(cep->ce_vardata, "always")) + else if (!strcmp(cep->name, "uhnames")) { + tempiConf.uhnames = config_checkval(cep->value, CFG_YESNO); + } + else if (!strcmp(cep->name, "allow-userhost-change")) { + if (!strcasecmp(cep->value, "always")) tempiConf.userhost_allowed = UHALLOW_ALWAYS; - else if (!strcasecmp(cep->ce_vardata, "never")) + else if (!strcasecmp(cep->value, "never")) tempiConf.userhost_allowed = UHALLOW_NEVER; - else if (!strcasecmp(cep->ce_vardata, "not-on-channels")) + else if (!strcasecmp(cep->value, "not-on-channels")) tempiConf.userhost_allowed = UHALLOW_NOCHANS; else tempiConf.userhost_allowed = UHALLOW_REJOIN; } - else if (!strcmp(cep->ce_varname, "channel-command-prefix")) { - safe_strdup(tempiConf.channel_command_prefix, cep->ce_vardata); + else if (!strcmp(cep->name, "channel-command-prefix")) { + safe_strdup(tempiConf.channel_command_prefix, cep->value); } - else if (!strcmp(cep->ce_varname, "restrict-usermodes")) { + else if (!strcmp(cep->name, "restrict-usermodes")) { int i; - char *p = safe_alloc(strlen(cep->ce_vardata) + 1), *x = p; + char *p = safe_alloc(strlen(cep->value) + 1), *x = p; /* The data should be something like 'Gw' or something, * but just in case users use '+Gw' then ignore the + (and -). */ - for (i=0; i < strlen(cep->ce_vardata); i++) - if ((cep->ce_vardata[i] != '+') && (cep->ce_vardata[i] != '-')) - *x++ = cep->ce_vardata[i]; + for (i=0; i < strlen(cep->value); i++) + if ((cep->value[i] != '+') && (cep->value[i] != '-')) + *x++ = cep->value[i]; *x = '\0'; tempiConf.restrict_usermodes = p; } - else if (!strcmp(cep->ce_varname, "restrict-channelmodes")) { + else if (!strcmp(cep->name, "restrict-channelmodes")) { int i; - char *p = safe_alloc(strlen(cep->ce_vardata) + 1), *x = p; + char *p = safe_alloc(strlen(cep->value) + 1), *x = p; /* The data should be something like 'GL' or something, * but just in case users use '+GL' then ignore the + (and -). */ - for (i=0; i < strlen(cep->ce_vardata); i++) - if ((cep->ce_vardata[i] != '+') && (cep->ce_vardata[i] != '-')) - *x++ = cep->ce_vardata[i]; + for (i=0; i < strlen(cep->value); i++) + if ((cep->value[i] != '+') && (cep->value[i] != '-')) + *x++ = cep->value[i]; *x = '\0'; tempiConf.restrict_channelmodes = p; } - else if (!strcmp(cep->ce_varname, "restrict-extendedbans")) { - safe_strdup(tempiConf.restrict_extendedbans, cep->ce_vardata); + else if (!strcmp(cep->name, "restrict-extendedbans")) { + safe_strdup(tempiConf.restrict_extendedbans, cep->value); } - else if (!strcmp(cep->ce_varname, "anti-spam-quit-message-time")) { - tempiConf.anti_spam_quit_message_time = config_checkval(cep->ce_vardata,CFG_TIME); + else if (!strcmp(cep->name, "named-extended-bans")) { + tempiConf.named_extended_bans = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "allow-user-stats")) { - if (!cep->ce_entries) + else if (!strcmp(cep->name, "anti-spam-quit-message-time")) { + tempiConf.anti_spam_quit_message_time = config_checkval(cep->value,CFG_TIME); + } + else if (!strcmp(cep->name, "allow-user-stats")) { + if (!cep->items) { - safe_strdup(tempiConf.allow_user_stats, cep->ce_vardata); + safe_strdup(tempiConf.allow_user_stats, cep->value); } else { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { OperStat *os = safe_alloc(sizeof(OperStat)); - safe_strdup(os->flag, cepp->ce_varname); + safe_strdup(os->flag, cepp->name); AddListItem(os, tempiConf.allow_user_stats_ext); } } } - else if (!strcmp(cep->ce_varname, "maxchannelsperuser")) { - tempiConf.maxchannelsperuser = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "maxchannelsperuser")) { + tempiConf.maxchannelsperuser = atoi(cep->value); } - else if (!strcmp(cep->ce_varname, "ping-warning")) { - tempiConf.ping_warning = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "ping-warning")) { + tempiConf.ping_warning = atoi(cep->value); } - else if (!strcmp(cep->ce_varname, "maxdccallow")) { - tempiConf.maxdccallow = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "maxdccallow")) { + tempiConf.maxdccallow = atoi(cep->value); } - else if (!strcmp(cep->ce_varname, "max-targets-per-command")) + else if (!strcmp(cep->name, "max-targets-per-command")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { int v; - if (!strcmp(cepp->ce_vardata, "max")) + if (!strcmp(cepp->value, "max")) v = MAXTARGETS_MAX; else - v = atoi(cepp->ce_vardata); - setmaxtargets(cepp->ce_varname, v); + v = atoi(cepp->value); + setmaxtargets(cepp->name, v); } } - else if (!strcmp(cep->ce_varname, "network-name")) { + else if (!strcmp(cep->name, "network-name")) { char *tmp; - safe_strdup(tempiConf.network.x_ircnetwork, cep->ce_vardata); - for (tmp = cep->ce_vardata; *cep->ce_vardata; cep->ce_vardata++) { - if (*cep->ce_vardata == ' ') - *cep->ce_vardata='-'; + safe_strdup(tempiConf.network_name, cep->value); + for (tmp = cep->value; *cep->value; cep->value++) { + if (*cep->value == ' ') + *cep->value='-'; } - safe_strdup(tempiConf.network.x_ircnet005, tmp); - cep->ce_vardata = tmp; + safe_strdup(tempiConf.network_name_005, tmp); + cep->value = tmp; } - else if (!strcmp(cep->ce_varname, "default-server")) { - safe_strdup(tempiConf.network.x_defserv, cep->ce_vardata); + else if (!strcmp(cep->name, "default-server")) { + safe_strdup(tempiConf.default_server, cep->value); } - else if (!strcmp(cep->ce_varname, "services-server")) { - safe_strdup(tempiConf.network.x_services_name, cep->ce_vardata); + else if (!strcmp(cep->name, "services-server")) { + safe_strdup(tempiConf.services_name, cep->value); } - else if (!strcmp(cep->ce_varname, "sasl-server")) { - safe_strdup(tempiConf.network.x_sasl_server, cep->ce_vardata); + else if (!strcmp(cep->name, "sasl-server")) { + safe_strdup(tempiConf.sasl_server, cep->value); } - else if (!strcmp(cep->ce_varname, "stats-server")) { - safe_strdup(tempiConf.network.x_stats_server, cep->ce_vardata); + else if (!strcmp(cep->name, "stats-server")) { + safe_strdup(tempiConf.stats_server, cep->value); } - else if (!strcmp(cep->ce_varname, "help-channel")) { - safe_strdup(tempiConf.network.x_helpchan, cep->ce_vardata); + else if (!strcmp(cep->name, "help-channel")) { + safe_strdup(tempiConf.helpchan, cep->value); } - else if (!strcmp(cep->ce_varname, "hiddenhost-prefix")) { - safe_strdup(tempiConf.network.x_hidden_host, cep->ce_vardata); + else if (!strcmp(cep->name, "cloak-prefix") || !strcmp(cep->name, "hiddenhost-prefix")) { + safe_strdup(tempiConf.cloak_prefix, cep->value); } - else if (!strcmp(cep->ce_varname, "hide-ban-reason")) { - tempiConf.hide_ban_reason = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->name, "hide-ban-reason")) { + tempiConf.hide_ban_reason = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "prefix-quit")) { - if (!strcmp(cep->ce_vardata, "0") || !strcmp(cep->ce_vardata, "no")) - safe_free(tempiConf.network.x_prefix_quit); + else if (!strcmp(cep->name, "prefix-quit")) { + if (!strcmp(cep->value, "0") || !strcmp(cep->value, "no")) + safe_free(tempiConf.prefix_quit); else - safe_strdup(tempiConf.network.x_prefix_quit, cep->ce_vardata); + safe_strdup(tempiConf.prefix_quit, cep->value); } - else if (!strcmp(cep->ce_varname, "link")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { - if (!strcmp(cepp->ce_varname, "bind-ip")) { - safe_strdup(tempiConf.link_bindip, cepp->ce_vardata); + else if (!strcmp(cep->name, "link")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { + if (!strcmp(cepp->name, "bind-ip")) { + safe_strdup(tempiConf.link_bindip, cepp->value); } } } - else if (!strcmp(cep->ce_varname, "dns")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { - if (!strcmp(cepp->ce_varname, "bind-ip")) { - safe_strdup(tempiConf.dns_bindip, cepp->ce_vardata); - } - } - } - else if (!strcmp(cep->ce_varname, "anti-flood")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + else if (!strcmp(cep->name, "anti-flood")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + int lag_penalty = -1; + int lag_penalty_bytes = -1; + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - if (!strcmp(ceppp->ce_varname, "handshake-data-flood")) + if (!strcmp(ceppp->name, "handshake-data-flood")) { - for (cep4 = ceppp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = ceppp->items; cep4; cep4 = cep4->next) { - if (!strcmp(cep4->ce_varname, "amount")) - tempiConf.handshake_data_flood_amount = config_checkval(cep4->ce_vardata, CFG_SIZE); - else if (!strcmp(cep4->ce_varname, "ban-time")) - tempiConf.handshake_data_flood_ban_time = config_checkval(cep4->ce_vardata, CFG_TIME); - else if (!strcmp(cep4->ce_varname, "ban-action")) - tempiConf.handshake_data_flood_ban_action = banact_stringtoval(cep4->ce_vardata); + if (!strcmp(cep4->name, "amount")) + tempiConf.handshake_data_flood_amount = config_checkval(cep4->value, CFG_SIZE); + else if (!strcmp(cep4->name, "ban-time")) + tempiConf.handshake_data_flood_ban_time = config_checkval(cep4->value, CFG_TIME); + else if (!strcmp(cep4->name, "ban-action")) + tempiConf.handshake_data_flood_ban_action = banact_stringtoval(cep4->value); } } - else if (!strcmp(ceppp->ce_varname, "away-flood")) + else if (!strcmp(ceppp->name, "away-flood")) { - config_parse_flood_generic(ceppp->ce_vardata, &tempiConf, cepp->ce_varname, FLD_AWAY); + config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_AWAY); } - else if (!strcmp(ceppp->ce_varname, "nick-flood")) + else if (!strcmp(ceppp->name, "nick-flood")) { - config_parse_flood_generic(ceppp->ce_vardata, &tempiConf, cepp->ce_varname, FLD_NICK); + config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_NICK); } - else if (!strcmp(ceppp->ce_varname, "join-flood")) + else if (!strcmp(ceppp->name, "join-flood")) { - config_parse_flood_generic(ceppp->ce_vardata, &tempiConf, cepp->ce_varname, FLD_JOIN); + config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_JOIN); } - else if (!strcmp(ceppp->ce_varname, "invite-flood")) + else if (!strcmp(ceppp->name, "invite-flood")) { - config_parse_flood_generic(ceppp->ce_vardata, &tempiConf, cepp->ce_varname, FLD_INVITE); + config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_INVITE); } - else if (!strcmp(ceppp->ce_varname, "knock-flood")) + else if (!strcmp(ceppp->name, "knock-flood")) { - config_parse_flood_generic(ceppp->ce_vardata, &tempiConf, cepp->ce_varname, FLD_KNOCK); + config_parse_flood_generic(ceppp->value, &tempiConf, cepp->name, FLD_KNOCK); } - else if (!strcmp(ceppp->ce_varname, "connect-flood")) + else if (!strcmp(ceppp->name, "lag-penalty")) + { + lag_penalty = atoi(ceppp->value); + } + else if (!strcmp(ceppp->name, "lag-penalty-bytes")) + { + lag_penalty_bytes = config_checkval(ceppp->value, CFG_SIZE); + if (lag_penalty_bytes <= 0) + lag_penalty_bytes = INT_MAX; + } + else if (!strcmp(ceppp->name, "connect-flood")) { int cnt, period; - config_parse_flood(ceppp->ce_vardata, &cnt, &period); + config_parse_flood(ceppp->value, &cnt, &period); tempiConf.throttle_count = cnt; tempiConf.throttle_period = period; } - if (!strcmp(ceppp->ce_varname, "max-concurrent-conversations")) + if (!strcmp(ceppp->name, "max-concurrent-conversations")) { /* We use a hack here to make it fit our storage format */ char buf[64]; int users=0; long every=0; - for (cep4 = ceppp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = ceppp->items; cep4; cep4 = cep4->next) { - if (!strcmp(cep4->ce_varname, "users")) + if (!strcmp(cep4->name, "users")) { - users = atoi(cep4->ce_vardata); + users = atoi(cep4->value); } else - if (!strcmp(cep4->ce_varname, "new-user-every")) + if (!strcmp(cep4->name, "new-user-every")) { - every = config_checkval(cep4->ce_vardata, CFG_TIME); + every = config_checkval(cep4->value, CFG_TIME); } } snprintf(buf, sizeof(buf), "%d:%ld", users, every); - config_parse_flood_generic(buf, &tempiConf, cepp->ce_varname, FLD_CONVERSATIONS); + config_parse_flood_generic(buf, &tempiConf, cepp->name, FLD_CONVERSATIONS); } else { @@ -7734,52 +7439,59 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) } } } + if ((lag_penalty != -1) && (lag_penalty_bytes != -1)) + { + /* We use a hack here to make it fit our storage format */ + char buf[64]; + snprintf(buf, sizeof(buf), "%d:%d", lag_penalty_bytes, lag_penalty); + config_parse_flood_generic(buf, &tempiConf, cepp->name, FLD_LAG_PENALTY); + } } } - else if (!strcmp(cep->ce_varname, "options")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { - if (!strcmp(cepp->ce_varname, "hide-ulines")) { + else if (!strcmp(cep->name, "options")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { + if (!strcmp(cepp->name, "hide-ulines")) { tempiConf.hide_ulines = 1; } - else if (!strcmp(cepp->ce_varname, "flat-map")) { + else if (!strcmp(cepp->name, "flat-map")) { tempiConf.flat_map = 1; } - else if (!strcmp(cepp->ce_varname, "show-opermotd")) { - tempiConf.som = 1; + else if (!strcmp(cepp->name, "show-opermotd")) { + tempiConf.show_opermotd = 1; } - else if (!strcmp(cepp->ce_varname, "identd-check")) { + else if (!strcmp(cepp->name, "identd-check")) { tempiConf.ident_check = 1; } - else if (!strcmp(cepp->ce_varname, "fail-oper-warn")) { + else if (!strcmp(cepp->name, "fail-oper-warn")) { tempiConf.fail_oper_warn = 1; } - else if (!strcmp(cepp->ce_varname, "show-connect-info")) { + else if (!strcmp(cepp->name, "show-connect-info")) { tempiConf.show_connect_info = 1; } - else if (!strcmp(cepp->ce_varname, "no-connect-tls-info")) { + else if (!strcmp(cepp->name, "no-connect-tls-info")) { tempiConf.no_connect_tls_info = 1; } - else if (!strcmp(cepp->ce_varname, "dont-resolve")) { + else if (!strcmp(cepp->name, "dont-resolve")) { tempiConf.dont_resolve = 1; } - else if (!strcmp(cepp->ce_varname, "mkpasswd-for-everyone")) { + else if (!strcmp(cepp->name, "mkpasswd-for-everyone")) { tempiConf.mkpasswd_for_everyone = 1; } - else if (!strcmp(cepp->ce_varname, "allow-insane-bans")) { + else if (!strcmp(cepp->name, "allow-insane-bans")) { tempiConf.allow_insane_bans = 1; } - else if (!strcmp(cepp->ce_varname, "allow-part-if-shunned")) { + else if (!strcmp(cepp->name, "allow-part-if-shunned")) { tempiConf.allow_part_if_shunned = 1; } - else if (!strcmp(cepp->ce_varname, "disable-cap")) { + else if (!strcmp(cepp->name, "disable-cap")) { tempiConf.disable_cap = 1; } - else if (!strcmp(cepp->ce_varname, "disable-ipv6")) { + else if (!strcmp(cepp->name, "disable-ipv6")) { /* other code handles this */ } } } - else if (!strcmp(cep->ce_varname, "cloak-keys")) + else if (!strcmp(cep->name, "cloak-keys")) { for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) { @@ -7789,34 +7501,34 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) break; } } - else if (!strcmp(cep->ce_varname, "ident")) + else if (!strcmp(cep->name, "ident")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "connect-timeout")) - tempiConf.ident_connect_timeout = config_checkval(cepp->ce_vardata,CFG_TIME); - if (!strcmp(cepp->ce_varname, "read-timeout")) - tempiConf.ident_read_timeout = config_checkval(cepp->ce_vardata,CFG_TIME); + if (!strcmp(cepp->name, "connect-timeout")) + tempiConf.ident_connect_timeout = config_checkval(cepp->value,CFG_TIME); + if (!strcmp(cepp->name, "read-timeout")) + tempiConf.ident_read_timeout = config_checkval(cepp->value,CFG_TIME); } } - else if (!strcmp(cep->ce_varname, "spamfilter")) + else if (!strcmp(cep->name, "spamfilter")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "ban-time")) - tempiConf.spamfilter_ban_time = config_checkval(cepp->ce_vardata,CFG_TIME); - else if (!strcmp(cepp->ce_varname, "ban-reason")) - safe_strdup(tempiConf.spamfilter_ban_reason, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "virus-help-channel")) - safe_strdup(tempiConf.spamfilter_virus_help_channel, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "virus-help-channel-deny")) - tempiConf.spamfilter_vchan_deny = config_checkval(cepp->ce_vardata,CFG_YESNO); - else if (!strcmp(cepp->ce_varname, "except")) + if (!strcmp(cepp->name, "ban-time")) + tempiConf.spamfilter_ban_time = config_checkval(cepp->value,CFG_TIME); + else if (!strcmp(cepp->name, "ban-reason")) + safe_strdup(tempiConf.spamfilter_ban_reason, cepp->value); + else if (!strcmp(cepp->name, "virus-help-channel")) + safe_strdup(tempiConf.spamfilter_virus_help_channel, cepp->value); + else if (!strcmp(cepp->name, "virus-help-channel-deny")) + tempiConf.spamfilter_vchan_deny = config_checkval(cepp->value,CFG_YESNO); + else if (!strcmp(cepp->name, "except")) { char *name, *p; SpamExcept *e; - safe_strdup(tempiConf.spamexcept_line, cepp->ce_vardata); - for (name = strtoken(&p, cepp->ce_vardata, ","); name; name = strtoken(&p, NULL, ",")) + safe_strdup(tempiConf.spamexcept_line, cepp->value); + for (name = strtoken(&p, cepp->value, ","); name; name = strtoken(&p, NULL, ",")) { if (*name == ' ') name++; @@ -7828,186 +7540,185 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) } } } - else if (!strcmp(cepp->ce_varname, "detect-slow-warn")) + else if (!strcmp(cepp->name, "detect-slow-warn")) { - tempiConf.spamfilter_detectslow_warn = atol(cepp->ce_vardata); + tempiConf.spamfilter_detectslow_warn = atol(cepp->value); } - else if (!strcmp(cepp->ce_varname, "detect-slow-fatal")) + else if (!strcmp(cepp->name, "detect-slow-fatal")) { - tempiConf.spamfilter_detectslow_fatal = atol(cepp->ce_vardata); + tempiConf.spamfilter_detectslow_fatal = atol(cepp->value); } - else if (!strcmp(cepp->ce_varname, "stop-on-first-match")) + else if (!strcmp(cepp->name, "stop-on-first-match")) { - tempiConf.spamfilter_stop_on_first_match = config_checkval(cepp->ce_vardata, CFG_YESNO); + tempiConf.spamfilter_stop_on_first_match = config_checkval(cepp->value, CFG_YESNO); } } } - else if (!strcmp(cep->ce_varname, "default-bantime")) + else if (!strcmp(cep->name, "default-bantime")) { - tempiConf.default_bantime = config_checkval(cep->ce_vardata,CFG_TIME); + tempiConf.default_bantime = config_checkval(cep->value,CFG_TIME); } - else if (!strcmp(cep->ce_varname, "ban-version-tkl-time")) + else if (!strcmp(cep->name, "ban-version-tkl-time")) { - tempiConf.ban_version_tkl_time = config_checkval(cep->ce_vardata,CFG_TIME); + tempiConf.ban_version_tkl_time = config_checkval(cep->value,CFG_TIME); } - else if (!strcmp(cep->ce_varname, "min-nick-length")) { - int v = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "min-nick-length")) { + int v = atoi(cep->value); tempiConf.min_nick_length = v; } - else if (!strcmp(cep->ce_varname, "nick-length")) { - int v = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "nick-length")) { + int v = atoi(cep->value); tempiConf.nick_length = v; } - else if (!strcmp(cep->ce_varname, "topic-length")) { - int v = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "topic-length")) { + int v = atoi(cep->value); tempiConf.topic_length = v; } - else if (!strcmp(cep->ce_varname, "away-length")) { - int v = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "away-length")) { + int v = atoi(cep->value); tempiConf.away_length = v; } - else if (!strcmp(cep->ce_varname, "kick-length")) { - int v = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "kick-length")) { + int v = atoi(cep->value); tempiConf.kick_length = v; } - else if (!strcmp(cep->ce_varname, "quit-length")) { - int v = atoi(cep->ce_vardata); + else if (!strcmp(cep->name, "quit-length")) { + int v = atoi(cep->value); tempiConf.quit_length = v; } - else if (!strcmp(cep->ce_varname, "ssl") || !strcmp(cep->ce_varname, "tls")) { + else if (!strcmp(cep->name, "ssl") || !strcmp(cep->name, "tls")) { /* no need to alloc tempiConf.tls_options since config_defaults() already ensures it exists */ conf_tlsblock(conf, cep, tempiConf.tls_options); } - else if (!strcmp(cep->ce_varname, "plaintext-policy")) + else if (!strcmp(cep->name, "plaintext-policy")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "user")) - tempiConf.plaintext_policy_user = policy_strtoval(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "oper")) - tempiConf.plaintext_policy_oper = policy_strtoval(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "server")) - tempiConf.plaintext_policy_server = policy_strtoval(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "user-message")) - addmultiline(&tempiConf.plaintext_policy_user_message, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "oper-message")) - addmultiline(&tempiConf.plaintext_policy_oper_message, cepp->ce_vardata); + if (!strcmp(cepp->name, "user")) + tempiConf.plaintext_policy_user = policy_strtoval(cepp->value); + else if (!strcmp(cepp->name, "oper")) + tempiConf.plaintext_policy_oper = policy_strtoval(cepp->value); + else if (!strcmp(cepp->name, "server")) + tempiConf.plaintext_policy_server = policy_strtoval(cepp->value); + else if (!strcmp(cepp->name, "user-message")) + addmultiline(&tempiConf.plaintext_policy_user_message, cepp->value); + else if (!strcmp(cepp->name, "oper-message")) + addmultiline(&tempiConf.plaintext_policy_oper_message, cepp->value); } } - else if (!strcmp(cep->ce_varname, "outdated-tls-policy")) + else if (!strcmp(cep->name, "outdated-tls-policy")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "user")) - tempiConf.outdated_tls_policy_user = policy_strtoval(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "oper")) - tempiConf.outdated_tls_policy_oper = policy_strtoval(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "server")) - tempiConf.outdated_tls_policy_server = policy_strtoval(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "user-message")) - safe_strdup(tempiConf.outdated_tls_policy_user_message, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "oper-message")) - safe_strdup(tempiConf.outdated_tls_policy_oper_message, cepp->ce_vardata); + if (!strcmp(cepp->name, "user")) + tempiConf.outdated_tls_policy_user = policy_strtoval(cepp->value); + else if (!strcmp(cepp->name, "oper")) + tempiConf.outdated_tls_policy_oper = policy_strtoval(cepp->value); + else if (!strcmp(cepp->name, "server")) + tempiConf.outdated_tls_policy_server = policy_strtoval(cepp->value); + else if (!strcmp(cepp->name, "user-message")) + safe_strdup(tempiConf.outdated_tls_policy_user_message, cepp->value); + else if (!strcmp(cepp->name, "oper-message")) + safe_strdup(tempiConf.outdated_tls_policy_oper_message, cepp->value); } } - else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask")) + else if (!strcmp(cep->name, "default-ipv6-clone-mask")) { - tempiConf.default_ipv6_clone_mask = atoi(cep->ce_vardata); + tempiConf.default_ipv6_clone_mask = atoi(cep->value); } - else if (!strcmp(cep->ce_varname, "hide-list")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + else if (!strcmp(cep->name, "hide-list")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "deny-channel")) + if (!strcmp(cepp->name, "deny-channel")) { tempiConf.hide_list = 1; /* if we would expand this later then change this to a bitmask or struct or whatever */ } } } - else if (!strcmp(cep->ce_varname, "max-unknown-connections-per-ip")) + else if (!strcmp(cep->name, "max-unknown-connections-per-ip")) { - tempiConf.max_unknown_connections_per_ip = atoi(cep->ce_vardata); + tempiConf.max_unknown_connections_per_ip = atoi(cep->value); } - else if (!strcmp(cep->ce_varname, "handshake-timeout")) + else if (!strcmp(cep->name, "handshake-timeout")) { - tempiConf.handshake_timeout = config_checkval(cep->ce_vardata, CFG_TIME); + tempiConf.handshake_timeout = config_checkval(cep->value, CFG_TIME); } - else if (!strcmp(cep->ce_varname, "sasl-timeout")) + else if (!strcmp(cep->name, "sasl-timeout")) { - tempiConf.sasl_timeout = config_checkval(cep->ce_vardata, CFG_TIME); + tempiConf.sasl_timeout = config_checkval(cep->value, CFG_TIME); } - else if (!strcmp(cep->ce_varname, "handshake-delay")) + else if (!strcmp(cep->name, "handshake-delay")) { - tempiConf.handshake_delay = config_checkval(cep->ce_vardata, CFG_TIME); + tempiConf.handshake_delay = config_checkval(cep->value, CFG_TIME); } - else if (!strcmp(cep->ce_varname, "automatic-ban-target")) + else if (!strcmp(cep->name, "automatic-ban-target")) { - tempiConf.automatic_ban_target = ban_target_strtoval(cep->ce_vardata); + tempiConf.automatic_ban_target = ban_target_strtoval(cep->value); } - else if (!strcmp(cep->ce_varname, "manual-ban-target")) + else if (!strcmp(cep->name, "manual-ban-target")) { - tempiConf.manual_ban_target = ban_target_strtoval(cep->ce_vardata); + tempiConf.manual_ban_target = ban_target_strtoval(cep->value); } - else if (!strcmp(cep->ce_varname, "reject-message")) + else if (!strcmp(cep->name, "reject-message")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "too-many-connections")) - safe_strdup(tempiConf.reject_message_too_many_connections, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "server-full")) - safe_strdup(tempiConf.reject_message_server_full, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "unauthorized")) - safe_strdup(tempiConf.reject_message_unauthorized, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "kline")) - safe_strdup(tempiConf.reject_message_kline, cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "gline")) - safe_strdup(tempiConf.reject_message_gline, cepp->ce_vardata); + if (!strcmp(cepp->name, "too-many-connections")) + safe_strdup(tempiConf.reject_message_too_many_connections, cepp->value); + else if (!strcmp(cepp->name, "server-full")) + safe_strdup(tempiConf.reject_message_server_full, cepp->value); + else if (!strcmp(cepp->name, "unauthorized")) + safe_strdup(tempiConf.reject_message_unauthorized, cepp->value); + else if (!strcmp(cepp->name, "kline")) + safe_strdup(tempiConf.reject_message_kline, cepp->value); + else if (!strcmp(cepp->name, "gline")) + safe_strdup(tempiConf.reject_message_gline, cepp->value); } } - else if (!strcmp(cep->ce_varname, "topic-setter")) + else if (!strcmp(cep->name, "topic-setter")) { - if (!strcmp(cep->ce_vardata, "nick")) + if (!strcmp(cep->value, "nick")) tempiConf.topic_setter = SETTER_NICK; - else if (!strcmp(cep->ce_vardata, "nick-user-host")) + else if (!strcmp(cep->value, "nick-user-host")) tempiConf.topic_setter = SETTER_NICK_USER_HOST; } - else if (!strcmp(cep->ce_varname, "ban-setter")) + else if (!strcmp(cep->name, "ban-setter")) { - if (!strcmp(cep->ce_vardata, "nick")) + if (!strcmp(cep->value, "nick")) tempiConf.ban_setter = SETTER_NICK; - else if (!strcmp(cep->ce_vardata, "nick-user-host")) + else if (!strcmp(cep->value, "nick-user-host")) tempiConf.ban_setter = SETTER_NICK_USER_HOST; } - else if (!strcmp(cep->ce_varname, "ban-setter-sync") || !strcmp(cep->ce_varname, "ban-setter-synch")) + else if (!strcmp(cep->name, "ban-setter-sync") || !strcmp(cep->name, "ban-setter-synch")) { - tempiConf.ban_setter_sync = config_checkval(cep->ce_vardata, CFG_YESNO); + tempiConf.ban_setter_sync = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "part-instead-of-quit-on-comment-change")) + else if (!strcmp(cep->name, "part-instead-of-quit-on-comment-change")) { - tempiConf.part_instead_of_quit_on_comment_change = config_checkval(cep->ce_vardata, CFG_YESNO); + tempiConf.part_instead_of_quit_on_comment_change = config_checkval(cep->value, CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "broadcast-channel-messages")) + else if (!strcmp(cep->name, "broadcast-channel-messages")) { - if (!strcmp(cep->ce_vardata, "auto")) + if (!strcmp(cep->value, "auto")) tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_AUTO; - else if (!strcmp(cep->ce_vardata, "always")) + else if (!strcmp(cep->value, "always")) tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_ALWAYS; - else if (!strcmp(cep->ce_vardata, "never")) + else if (!strcmp(cep->value, "never")) tempiConf.broadcast_channel_messages = BROADCAST_CHANNEL_MESSAGES_NEVER; } - else if (!strcmp(cep->ce_varname, "allowed-channelchars")) + else if (!strcmp(cep->name, "allowed-channelchars")) { - tempiConf.allowed_channelchars = allowed_channelchars_strtoval(cep->ce_vardata); + tempiConf.allowed_channelchars = allowed_channelchars_strtoval(cep->value); } - else if (!strcmp(cep->ce_varname, "hide-idle-time")) + else if (!strcmp(cep->name, "hide-idle-time")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "policy")) - tempiConf.hide_idle_time = hideidletime_strtoval(cepp->ce_vardata); + if (!strcmp(cepp->name, "policy")) + tempiConf.hide_idle_time = hideidletime_strtoval(cepp->value); } - } - else + } else { int value; for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) @@ -8028,62 +7739,61 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) int errors = 0; Hook *h; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "kline-address")) { + if (!strcmp(cep->name, "kline-address")) { CheckNull(cep); CheckDuplicate(cep, kline_address, "kline-address"); - if (!strchr(cep->ce_vardata, '@') && !strchr(cep->ce_vardata, ':')) + if (!strchr(cep->value, '@') && !strchr(cep->value, ':')) { config_error("%s:%i: set::kline-address must be an e-mail or an URL", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; continue; } - else if (match_simple("*@unrealircd.com", cep->ce_vardata) || match_simple("*@unrealircd.org",cep->ce_vardata) || match_simple("unreal-*@lists.sourceforge.net",cep->ce_vardata)) + else if (match_simple("*@unrealircd.com", cep->value) || match_simple("*@unrealircd.org",cep->value) || match_simple("unreal-*@lists.sourceforge.net",cep->value)) { config_error("%s:%i: set::kline-address may not be an UnrealIRCd Team address", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; continue; } } - else if (!strcmp(cep->ce_varname, "gline-address")) { + else if (!strcmp(cep->name, "gline-address")) { CheckNull(cep); CheckDuplicate(cep, gline_address, "gline-address"); - if (!strchr(cep->ce_vardata, '@') && !strchr(cep->ce_vardata, ':')) + if (!strchr(cep->value, '@') && !strchr(cep->value, ':')) { config_error("%s:%i: set::gline-address must be an e-mail or an URL", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; continue; } - else if (match_simple("*@unrealircd.com", cep->ce_vardata) || match_simple("*@unrealircd.org",cep->ce_vardata) || match_simple("unreal-*@lists.sourceforge.net",cep->ce_vardata)) + else if (match_simple("*@unrealircd.com", cep->value) || match_simple("*@unrealircd.org",cep->value) || match_simple("unreal-*@lists.sourceforge.net",cep->value)) { config_error("%s:%i: set::gline-address may not be an UnrealIRCd Team address", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; continue; } } - else if (!strcmp(cep->ce_varname, "modes-on-connect")) { + else if (!strcmp(cep->name, "modes-on-connect")) { char *p; CheckNull(cep); CheckDuplicate(cep, modes_on_connect, "modes-on-connect"); - for (p = cep->ce_vardata; *p; p++) + for (p = cep->value; *p; p++) if (strchr("orzSHqtW", *p)) { config_error("%s:%i: set::modes-on-connect may not include mode '%c'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); + cep->file->filename, cep->line_number, *p); errors++; } - set_usermode(cep->ce_vardata); } - else if (!strcmp(cep->ce_varname, "modes-on-join")) { + else if (!strcmp(cep->name, "modes-on-join")) { char *c; struct ChMode temp; memset(&temp, 0, sizeof(temp)); CheckNull(cep); CheckDuplicate(cep, modes_on_join, "modes-on-join"); - for (c = cep->ce_vardata; *c; c++) + for (c = cep->value; *c; c++) { if (*c == ' ') break; /* don't check the parameter ;p */ @@ -8097,340 +7807,340 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) case 'b': case 'e': case 'I': - case 'k': - case 'l': config_error("%s:%i: set::modes-on-join may not contain +%c", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *c); + cep->file->filename, cep->line_number, *c); errors++; break; } } - conf_channelmodes(cep->ce_vardata, &temp, 1); - if (temp.mode & MODE_SECRET && temp.mode & MODE_PRIVATE) - { - config_error("%s:%i: set::modes-on-join has both +s and +p", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); - errors++; - } - + /* We can't really verify much here. + * The channel mode modules have not been initialized + * yet at this point, so we can't really verify much + * here. + */ } - else if (!strcmp(cep->ce_varname, "modes-on-oper")) { + else if (!strcmp(cep->name, "modes-on-oper")) { char *p; CheckNull(cep); CheckDuplicate(cep, modes_on_oper, "modes-on-oper"); - for (p = cep->ce_vardata; *p; p++) + for (p = cep->value; *p; p++) if (strchr("orzS", *p)) { config_error("%s:%i: set::modes-on-oper may not include mode '%c'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); + cep->file->filename, cep->line_number, *p); errors++; } - set_usermode(cep->ce_vardata); + set_usermode(cep->value); } - else if (!strcmp(cep->ce_varname, "snomask-on-oper")) { + else if (!strcmp(cep->name, "snomask-on-oper")) { + char *wrong_snomask; CheckNull(cep); CheckDuplicate(cep, snomask_on_oper, "snomask-on-oper"); + if (!is_valid_snomask_string_testing(cep->value, &wrong_snomask)) + { + config_error("%s:%i: set::snomask-on-oper contains unknown snomask letter(s) '%s'", + cep->file->filename, cep->line_number, wrong_snomask); + errors++; + invalid_snomasks_encountered++; + } } - else if (!strcmp(cep->ce_varname, "level-on-join")) { + else if (!strcmp(cep->name, "server-notice-colors")) { + CheckNull(cep); + } + else if (!strcmp(cep->name, "level-on-join")) { CheckNull(cep); CheckDuplicate(cep, level_on_join, "level-on-join"); - if (!channellevel_to_int(cep->ce_vardata)) + if (!channellevel_to_string(cep->value) && (strlen(cep->value) != 1)) { - config_error("%s:%i: set::level-on-join: unknown value '%s', should be one of: none, voice, halfop, op, protect, owner", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + config_error("%s:%i: set::level-on-join: unknown value '%s', should be one of: " + "'none', 'voice', 'halfop', 'op', 'admin', 'owner', or a single letter (eg 'o')", + cep->file->filename, cep->line_number, cep->value); errors++; } } - else if (!strcmp(cep->ce_varname, "static-quit")) { + else if (!strcmp(cep->name, "static-quit")) { CheckNull(cep); CheckDuplicate(cep, static_quit, "static-quit"); } - else if (!strcmp(cep->ce_varname, "static-part")) { + else if (!strcmp(cep->name, "static-part")) { CheckNull(cep); CheckDuplicate(cep, static_part, "static-part"); } - else if (!strcmp(cep->ce_varname, "who-limit")) { + else if (!strcmp(cep->name, "who-limit")) { CheckNull(cep); CheckDuplicate(cep, who_limit, "who-limit"); - if (!config_checkval(cep->ce_vardata,CFG_SIZE)) + if (!config_checkval(cep->value,CFG_SIZE)) { config_error("%s:%i: set::who-limit: value must be at least 1", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "maxbans")) { + else if (!strcmp(cep->name, "maxbans")) { CheckNull(cep); CheckDuplicate(cep, maxbans, "maxbans"); } - else if (!strcmp(cep->ce_varname, "maxbanlength")) { + else if (!strcmp(cep->name, "maxbanlength")) { CheckNull(cep); CheckDuplicate(cep, maxbanlength, "maxbanlength"); } - else if (!strcmp(cep->ce_varname, "silence-limit")) { + else if (!strcmp(cep->name, "silence-limit")) { CheckNull(cep); CheckDuplicate(cep, silence_limit, "silence-limit"); } - else if (!strcmp(cep->ce_varname, "auto-join")) { + else if (!strcmp(cep->name, "auto-join")) { CheckNull(cep); CheckDuplicate(cep, auto_join, "auto-join"); } - else if (!strcmp(cep->ce_varname, "oper-auto-join")) { + else if (!strcmp(cep->name, "oper-auto-join")) { CheckNull(cep); CheckDuplicate(cep, oper_auto_join, "oper-auto-join"); } - else if (!strcmp(cep->ce_varname, "check-target-nick-bans")) { + else if (!strcmp(cep->name, "check-target-nick-bans")) { CheckNull(cep); CheckDuplicate(cep, check_target_nick_bans, "check-target-nick-bans"); } - else if (!strcmp(cep->ce_varname, "pingpong-warning")) { + else if (!strcmp(cep->name, "pingpong-warning")) { config_error("%s:%i: set::pingpong-warning no longer exists (the warning is always off)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); - need_34_upgrade = 1; + cep->file->filename, cep->line_number); errors++; } - else if (!strcmp(cep->ce_varname, "ping-cookie")) { + else if (!strcmp(cep->name, "ping-cookie")) { CheckNull(cep); CheckDuplicate(cep, ping_cookie, "ping-cookie"); } - else if (!strcmp(cep->ce_varname, "watch-away-notification")) { + else if (!strcmp(cep->name, "watch-away-notification")) { CheckNull(cep); CheckDuplicate(cep, watch_away_notification, "watch-away-notification"); } - else if (!strcmp(cep->ce_varname, "uhnames")) { + else if (!strcmp(cep->name, "uhnames")) { CheckNull(cep); CheckDuplicate(cep, uhnames, "uhnames"); } - else if (!strcmp(cep->ce_varname, "channel-command-prefix")) { + else if (!strcmp(cep->name, "channel-command-prefix")) { CheckNullAllowEmpty(cep); CheckDuplicate(cep, channel_command_prefix, "channel-command-prefix"); } - else if (!strcmp(cep->ce_varname, "allow-userhost-change")) { + else if (!strcmp(cep->name, "allow-userhost-change")) { CheckNull(cep); CheckDuplicate(cep, allow_userhost_change, "allow-userhost-change"); - if (strcasecmp(cep->ce_vardata, "always") && - strcasecmp(cep->ce_vardata, "never") && - strcasecmp(cep->ce_vardata, "not-on-channels") && - strcasecmp(cep->ce_vardata, "force-rejoin")) + if (strcasecmp(cep->value, "always") && + strcasecmp(cep->value, "never") && + strcasecmp(cep->value, "not-on-channels") && + strcasecmp(cep->value, "force-rejoin")) { config_error("%s:%i: set::allow-userhost-change is invalid", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum); + cep->file->filename, + cep->line_number); errors++; continue; } } - else if (!strcmp(cep->ce_varname, "anti-spam-quit-message-time")) { + else if (!strcmp(cep->name, "anti-spam-quit-message-time")) { CheckNull(cep); CheckDuplicate(cep, anti_spam_quit_message_time, "anti-spam-quit-message-time"); } - else if (!strcmp(cep->ce_varname, "oper-only-stats")) + else if (!strcmp(cep->name, "oper-only-stats")) { config_warn("%s:%d: We no longer use a blacklist for stats (set::oper-only-stats) but " "have a whitelist now instead (set::allow-user-stats). ", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_warn("Simply delete the oper-only-stats line from your configuration file %s around line %d to get rid of this warning", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); continue; } - else if (!strcmp(cep->ce_varname, "allow-user-stats")) + else if (!strcmp(cep->name, "allow-user-stats")) { CheckDuplicate(cep, allow_user_stats, "allow-user-stats"); - if (!cep->ce_entries) - { - CheckNull(cep); - } - else - { - /* TODO: check the entries for existence? - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - } */ - } + CheckNull(cep); } - else if (!strcmp(cep->ce_varname, "maxchannelsperuser")) { + else if (!strcmp(cep->name, "maxchannelsperuser")) { CheckNull(cep); CheckDuplicate(cep, maxchannelsperuser, "maxchannelsperuser"); - tempi = atoi(cep->ce_vardata); + tempi = atoi(cep->value); if (tempi < 1) { config_error("%s:%i: set::maxchannelsperuser must be > 0", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum); + cep->file->filename, + cep->line_number); errors++; continue; } } - else if (!strcmp(cep->ce_varname, "ping-warning")) { + else if (!strcmp(cep->name, "ping-warning")) { CheckNull(cep); CheckDuplicate(cep, ping_warning, "ping-warning"); - tempi = atoi(cep->ce_vardata); + tempi = atoi(cep->value); /* it is pointless to allow setting higher than 170 */ if (tempi > 170) { config_error("%s:%i: set::ping-warning must be < 170", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum); + cep->file->filename, + cep->line_number); errors++; continue; } } - else if (!strcmp(cep->ce_varname, "maxdccallow")) { + else if (!strcmp(cep->name, "maxdccallow")) { CheckNull(cep); CheckDuplicate(cep, maxdccallow, "maxdccallow"); } - else if (!strcmp(cep->ce_varname, "max-targets-per-command")) + else if (!strcmp(cep->name, "max-targets-per-command")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcasecmp(cepp->ce_varname, "NAMES") || !strcasecmp(cepp->ce_varname, "WHOWAS")) + if (!strcasecmp(cepp->name, "NAMES") || !strcasecmp(cepp->name, "WHOWAS")) { - if (atoi(cepp->ce_vardata) != 1) + if (atoi(cepp->value) != 1) { config_error("%s:%i: set::max-targets-per-command::%s: " "this command is hardcoded at a maximum of 1 " "and cannot be configured to accept more.", - cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, - cepp->ce_varname); + cepp->file->filename, + cepp->line_number, + cepp->name); errors++; } } else - if (!strcasecmp(cepp->ce_varname, "USERHOST") || - !strcasecmp(cepp->ce_varname, "USERIP") || - !strcasecmp(cepp->ce_varname, "ISON") || - !strcasecmp(cepp->ce_varname, "WATCH")) + if (!strcasecmp(cepp->name, "USERHOST") || + !strcasecmp(cepp->name, "USERIP") || + !strcasecmp(cepp->name, "ISON") || + !strcasecmp(cepp->name, "WATCH")) { - if (strcmp(cepp->ce_vardata, "max")) + if (strcmp(cepp->value, "max")) { config_error("%s:%i: set::max-targets-per-command::%s: " "this command is hardcoded at a maximum of 'max' " "and cannot be changed. This because it is " "highly discouraged to change it.", - cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, - cepp->ce_varname); + cepp->file->filename, + cepp->line_number, + cepp->name); errors++; } } /* Now check the value syntax in general: */ - if (strcmp(cepp->ce_vardata, "max")) /* anything other than 'max'.. */ + if (strcmp(cepp->value, "max")) /* anything other than 'max'.. */ { - int v = atoi(cepp->ce_vardata); + int v = atoi(cepp->value); if ((v < 1) || (v > 20)) { config_error("%s:%i: set::max-targets-per-command::%s: " "value should be 1-20 or 'max'", - cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, - cepp->ce_varname); + cepp->file->filename, + cepp->line_number, + cepp->name); errors++; } } } } - else if (!strcmp(cep->ce_varname, "network-name")) { + else if (!strcmp(cep->name, "network-name")) { char *p; CheckNull(cep); CheckDuplicate(cep, network_name, "network-name"); - for (p = cep->ce_vardata; *p; p++) + for (p = cep->value; *p; p++) if ((*p < ' ') || (*p > '~')) { config_error("%s:%i: set::network-name can only contain ASCII characters 33-126. Invalid character = '%c'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, *p); + cep->file->filename, cep->line_number, *p); errors++; break; } } - else if (!strcmp(cep->ce_varname, "default-server")) { + else if (!strcmp(cep->name, "default-server")) { CheckNull(cep); CheckDuplicate(cep, default_server, "default-server"); } - else if (!strcmp(cep->ce_varname, "services-server")) { + else if (!strcmp(cep->name, "services-server")) { CheckNull(cep); CheckDuplicate(cep, services_server, "services-server"); } - else if (!strcmp(cep->ce_varname, "sasl-server")) { + else if (!strcmp(cep->name, "sasl-server")) { CheckNull(cep); CheckDuplicate(cep, sasl_server, "sasl-server"); } - else if (!strcmp(cep->ce_varname, "stats-server")) { + else if (!strcmp(cep->name, "stats-server")) { CheckNull(cep); CheckDuplicate(cep, stats_server, "stats-server"); } - else if (!strcmp(cep->ce_varname, "help-channel")) { + else if (!strcmp(cep->name, "help-channel")) { CheckNull(cep); CheckDuplicate(cep, help_channel, "help-channel"); } - else if (!strcmp(cep->ce_varname, "hiddenhost-prefix")) { + else if (!strcmp(cep->name, "cloak-prefix") || !strcmp(cep->name, "hiddenhost-prefix")) { CheckNull(cep); - CheckDuplicate(cep, hiddenhost_prefix, "hiddenhost-prefix"); - if (strchr(cep->ce_vardata, ' ') || (*cep->ce_vardata == ':')) + CheckDuplicate(cep, hiddenhost_prefix, "cloak-prefix"); + if (strchr(cep->value, ' ') || (*cep->value == ':')) { - config_error("%s:%i: set::hiddenhost-prefix must not contain spaces or be prefixed with ':'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + config_error("%s:%i: set::cloak-prefix must not contain spaces or be prefixed with ':'", + cep->file->filename, cep->line_number); errors++; continue; } } - else if (!strcmp(cep->ce_varname, "prefix-quit")) { + else if (!strcmp(cep->name, "prefix-quit")) { CheckNull(cep); CheckDuplicate(cep, prefix_quit, "prefix-quit"); } - else if (!strcmp(cep->ce_varname, "hide-ban-reason")) { + else if (!strcmp(cep->name, "hide-ban-reason")) { CheckNull(cep); CheckDuplicate(cep, hide_ban_reason, "hide-ban-reason"); } - else if (!strcmp(cep->ce_varname, "restrict-usermodes")) + else if (!strcmp(cep->name, "restrict-usermodes")) { CheckNull(cep); CheckDuplicate(cep, restrict_usermodes, "restrict-usermodes"); - if (cep->ce_varname) { + if (cep->name) { int warn = 0; char *p; - for (p = cep->ce_vardata; *p; p++) + for (p = cep->value; *p; p++) if ((*p == '+') || (*p == '-')) warn = 1; if (warn) { config_status("%s:%i: warning: set::restrict-usermodes: should only contain modechars, no + or -.\n", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); } } } - else if (!strcmp(cep->ce_varname, "restrict-channelmodes")) + else if (!strcmp(cep->name, "restrict-channelmodes")) { CheckNull(cep); CheckDuplicate(cep, restrict_channelmodes, "restrict-channelmodes"); - if (cep->ce_varname) { + if (cep->name) { int warn = 0; char *p; - for (p = cep->ce_vardata; *p; p++) + for (p = cep->value; *p; p++) if ((*p == '+') || (*p == '-')) warn = 1; if (warn) { config_status("%s:%i: warning: set::restrict-channelmodes: should only contain modechars, no + or -.\n", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); } } } - else if (!strcmp(cep->ce_varname, "restrict-extendedbans")) + else if (!strcmp(cep->name, "restrict-extendedbans")) { CheckDuplicate(cep, restrict_extendedbans, "restrict-extendedbans"); CheckNull(cep); } - else if (!strcmp(cep->ce_varname, "link")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { + else if (!strcmp(cep->name, "named-extended-bans")) + { + CheckNull(cep); + } + else if (!strcmp(cep->name, "link")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "bind-ip")) { + if (!strcmp(cepp->name, "bind-ip")) { CheckDuplicate(cepp, link_bind_ip, "link::bind-ip"); - if (strcmp(cepp->ce_vardata, "*")) + if (strcmp(cepp->value, "*")) { - if (!is_valid_ip(cepp->ce_vardata)) + if (!is_valid_ip(cepp->value)) { config_error("%s:%i: set::link::bind-ip (%s) is not a valid IP", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, - cepp->ce_vardata); + cepp->file->filename, cepp->line_number, + cepp->value); errors++; continue; } @@ -8438,66 +8148,33 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) } } } - else if (!strcmp(cep->ce_varname, "dns")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { - CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "nameserver") || - !strcmp(cepp->ce_varname, "timeout") || - !strcmp(cepp->ce_varname, "retries")) - { - config_error("%s:%i: set::dns::%s no longer exist in UnrealIRCd 4. " - "Please remove it from your configuration file.", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); - errors++; - } else - if (!strcmp(cepp->ce_varname, "bind-ip")) { - CheckDuplicate(cepp, dns_bind_ip, "dns::bind-ip"); - if (strcmp(cepp->ce_vardata, "*")) - { - if (!is_valid_ip(cepp->ce_vardata)) - { - config_error("%s:%i: set::dns::bind-ip (%s) is not a valid IP", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, - cepp->ce_vardata); - errors++; - continue; - } - } - } - else - { - config_error_unknownopt(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::dns", - cepp->ce_varname); - errors++; - } - } - } - else if (!strcmp(cep->ce_varname, "throttle")) { + else if (!strcmp(cep->name, "throttle")) { config_error("%s:%i: set::throttle has been renamed. you now use " "set::anti-flood::connect-flood :. " "Or just remove the throttle block and you get the default " "of 3 per 60 seconds.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; - need_34_upgrade = 1; continue; } - else if (!strcmp(cep->ce_varname, "anti-flood")) + else if (!strcmp(cep->name, "anti-flood")) { int anti_flood_old = 0; int anti_flood_old_and_default = 0; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { + int has_lag_penalty = 0; + int has_lag_penalty_bytes = 0; + /* Test for old options: */ - if (flood_option_is_old(cepp->ce_varname)) + if (flood_option_is_old(cepp->name)) { /* Special code if the user is using 100% of the defaults */ - if (cepp->ce_vardata && - ((!strcmp(cepp->ce_varname, "nick-flood") && !strcmp(cepp->ce_vardata, "3:60")) || - (!strcmp(cepp->ce_varname, "connect-flood") && cepp->ce_vardata && !strcmp(cepp->ce_vardata, "3:60")) || - (!strcmp(cepp->ce_varname, "away-flood") && cepp->ce_vardata && !strcmp(cepp->ce_vardata, "4:120")))) + if (cepp->value && + ((!strcmp(cepp->name, "nick-flood") && !strcmp(cepp->value, "3:60")) || + (!strcmp(cepp->name, "connect-flood") && cepp->value && !strcmp(cepp->value, "3:60")) || + (!strcmp(cepp->name, "away-flood") && cepp->value && !strcmp(cepp->value, "4:120")))) { anti_flood_old_and_default = 1; } else @@ -8507,219 +8184,237 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) continue; } - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - int everyone = !strcmp(cepp->ce_varname, "everyone") ? 1 : 0; - int for_everyone = flood_option_is_for_everyone(ceppp->ce_varname); + int everyone = !strcmp(cepp->name, "everyone") ? 1 : 0; + int for_everyone = flood_option_is_for_everyone(ceppp->name); if (everyone && !for_everyone) { config_error("%s:%i: %s cannot be in the set::anti-flood::everyone block. " "You can put it in 'known-users' or 'unknown-users' instead.", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, - ceppp->ce_varname); + ceppp->file->filename, ceppp->line_number, + ceppp->name); errors++; continue; } else if (!everyone && for_everyone) { config_error("%s:%i: %s must be in the set::anti-flood::everyone block, not anywhere else.", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, - ceppp->ce_varname); + ceppp->file->filename, ceppp->line_number, + ceppp->name); errors++; continue; } /* Now comes the actual config check for each element... */ - if (!strcmp(ceppp->ce_varname, "max-concurrent-conversations")) + if (!strcmp(ceppp->name, "max-concurrent-conversations")) { - for (cep4 = ceppp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = ceppp->items; cep4; cep4 = cep4->next) { CheckNull(cep4); - if (!strcmp(cep4->ce_varname, "users")) + if (!strcmp(cep4->name, "users")) { - int v = atoi(cep4->ce_vardata); + int v = atoi(cep4->value); if ((v < 1) || (v > MAXCCUSERS)) { config_error("%s:%i: set::anti-flood::max-concurrent-conversations::users: " "value should be between 1 and %d", - cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum, MAXCCUSERS); + cep4->file->filename, cep4->line_number, MAXCCUSERS); errors++; } } else - if (!strcmp(cep4->ce_varname, "new-user-every")) + if (!strcmp(cep4->name, "new-user-every")) { - long v = config_checkval(cep4->ce_vardata, CFG_TIME); + long v = config_checkval(cep4->value, CFG_TIME); if ((v < 1) || (v > 120)) { config_error("%s:%i: set::anti-flood::max-concurrent-conversations::new-user-every: " "value should be between 1 and 120 seconds", - cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum); + cep4->file->filename, cep4->line_number); errors++; } } else { - config_error_unknownopt(cep4->ce_fileptr->cf_filename, - cep4->ce_varlinenum, "set::anti-flood", - cep4->ce_varname); + config_error_unknownopt(cep4->file->filename, + cep4->line_number, "set::anti-flood", + cep4->name); errors++; } } continue; /* required here, due to checknull directly below */ } - else if (!strcmp(ceppp->ce_varname, "unknown-flood-amount") || - !strcmp(ceppp->ce_varname, "unknown-flood-bantime")) + else if (!strcmp(ceppp->name, "unknown-flood-amount") || + !strcmp(ceppp->name, "unknown-flood-bantime")) { config_error("%s:%i: set::anti-flood::%s: this setting has been moved. " "See https://www.unrealircd.org/docs/Anti-flood_settings#handshake-data-flood", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, ceppp->ce_varname); + ceppp->file->filename, ceppp->line_number, ceppp->name); errors++; continue; } - else if (!strcmp(ceppp->ce_varname, "handshake-data-flood")) + else if (!strcmp(ceppp->name, "handshake-data-flood")) { - for (cep4 = ceppp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = ceppp->items; cep4; cep4 = cep4->next) { - if (!strcmp(cep4->ce_varname, "amount")) + if (!strcmp(cep4->name, "amount")) { long v; CheckNull(cep4); - v = config_checkval(cep4->ce_vardata, CFG_SIZE); + v = config_checkval(cep4->value, CFG_SIZE); if (v < 1024) { config_error("%s:%i: set::anti-flood::handshake-data-flood::amount must be at least 1024 bytes", - cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum); + cep4->file->filename, cep4->line_number); errors++; } } else - if (!strcmp(cep4->ce_varname, "ban-action")) + if (!strcmp(cep4->name, "ban-action")) { CheckNull(cep4); - if (!banact_stringtoval(cep4->ce_vardata)) + if (!banact_stringtoval(cep4->value)) { config_error("%s:%i: set::anti-flood::handshake-data-flood::ban-action has unknown action type '%s'", - cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum, - cep4->ce_vardata); + cep4->file->filename, cep4->line_number, + cep4->value); errors++; } } else - if (!strcmp(cep4->ce_varname, "ban-time")) + if (!strcmp(cep4->name, "ban-time")) { CheckNull(cep4); } else { - config_error_unknownopt(cep4->ce_fileptr->cf_filename, - cep4->ce_varlinenum, "set::anti-flood::handshake-data-flood", - cep4->ce_varname); + config_error_unknownopt(cep4->file->filename, + cep4->line_number, "set::anti-flood::handshake-data-flood", + cep4->name); errors++; } } } - else if (!strcmp(ceppp->ce_varname, "away-count")) + else if (!strcmp(ceppp->name, "away-count")) { - int temp = atol(ceppp->ce_vardata); + int temp = atol(ceppp->value); CheckNull(ceppp); if (temp < 1 || temp > 255) { config_error("%s:%i: set::anti-flood::away-count must be between 1 and 255", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } - else if (!strcmp(ceppp->ce_varname, "away-period")) + else if (!strcmp(ceppp->name, "away-period")) { CheckNull(ceppp); - int temp = config_checkval(ceppp->ce_vardata, CFG_TIME); + int temp = config_checkval(ceppp->value, CFG_TIME); if (temp < 10) { config_error("%s:%i: set::anti-flood::away-period must be greater than 9", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } - else if (!strcmp(ceppp->ce_varname, "away-flood")) + else if (!strcmp(ceppp->name, "away-flood")) { int cnt, period; CheckNull(ceppp); - if (!config_parse_flood(ceppp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(ceppp->value, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 10)) { config_error("%s:%i: set::anti-flood::away-flood error. Syntax is ':' (eg 5:60), " "count should be 1-255, period should be greater than 9", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } - else if (!strcmp(ceppp->ce_varname, "nick-flood")) + else if (!strcmp(ceppp->name, "nick-flood")) { int cnt, period; CheckNull(ceppp); - if (!config_parse_flood(ceppp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(ceppp->value, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 5)) { config_error("%s:%i: set::anti-flood::nick-flood error. Syntax is ':' (eg 5:60), " "count should be 1-255, period should be greater than 4", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } - else if (!strcmp(ceppp->ce_varname, "join-flood")) + else if (!strcmp(ceppp->name, "join-flood")) { int cnt, period; CheckNull(ceppp); - if (!config_parse_flood(ceppp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(ceppp->value, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 5)) { config_error("%s:%i: join-flood error. Syntax is ':' (eg 5:60), " "count should be 1-255, period should be greater than 4", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } - else if (!strcmp(ceppp->ce_varname, "invite-flood")) + else if (!strcmp(ceppp->name, "invite-flood")) { int cnt, period; CheckNull(ceppp); - if (!config_parse_flood(ceppp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(ceppp->value, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 5)) { config_error("%s:%i: set::anti-flood::invite-flood error. Syntax is ':' (eg 5:60), " "count should be 1-255, period should be greater than 4", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } - else if (!strcmp(ceppp->ce_varname, "knock-flood")) + else if (!strcmp(ceppp->name, "knock-flood")) { int cnt, period; CheckNull(ceppp); - if (!config_parse_flood(ceppp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(ceppp->value, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 5)) { config_error("%s:%i: set::anti-flood::knock-flood error. Syntax is ':' (eg 5:60), " "count should be 1-255, period should be greater than 4", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } - else if (!strcmp(ceppp->ce_varname, "connect-flood")) + else if (!strcmp(ceppp->name, "lag-penalty")) + { + int v; + CheckNull(ceppp); + v = atoi(ceppp->value); + has_lag_penalty = 1; + if ((v < 0) || (v > 10000)) + { + config_error("%s:%i: set::anti-flood::%s::lag-penalty: value is in milliseconds and should be between 0 and 10000", + ceppp->file->filename, ceppp->line_number, cepp->name); + errors++; + } + } + else if (!strcmp(ceppp->name, "lag-penalty-bytes")) + { + has_lag_penalty_bytes = 1; + CheckNull(ceppp); + } + else if (!strcmp(ceppp->name, "connect-flood")) { int cnt, period; CheckNull(ceppp); - if (strcmp(cepp->ce_varname, "everyone")) + if (strcmp(cepp->name, "everyone")) { config_error("%s:%i: connect-flood must be in the set::anti-flood::everyone block, not anywhere else.", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; continue; } - if (!config_parse_flood(ceppp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(ceppp->value, &cnt, &period) || (cnt < 1) || (cnt > 255) || (period < 1) || (period > 3600)) { config_error("%s:%i: set::anti-flood::connect-flood: Syntax is ':' (eg 5:60), " "count should be 1-255, period should be 1-3600", - ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum); + ceppp->file->filename, ceppp->line_number); errors++; } } @@ -8756,21 +8451,27 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) } if (!used) { - config_error_unknownopt(ceppp->ce_fileptr->cf_filename, - ceppp->ce_varlinenum, "set::anti-flood", - ceppp->ce_varname); + config_error_unknownopt(ceppp->file->filename, + ceppp->line_number, "set::anti-flood", + ceppp->name); errors++; } continue; } } + if (has_lag_penalty+has_lag_penalty_bytes == 1) + { + config_error("%s:%i: set::anti-flood::%s: if you use lag-penalty then you must also add an lag-penalty-bytes item (and vice-versa)", + cepp->file->filename, cepp->line_number, cepp->name); + errors++; + } } /* Now the warnings: */ if (anti_flood_old == 1) { config_warn("%s:%d: the set::anti-flood block has been reorganized to be more flexible. " "Your custom anti-flood settings have NOT been read.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_warn("See https://www.unrealircd.org/docs/Anti-flood_settings for the new block style,"); config_warn("OR: simply remove all the anti-flood options from the conf to get rid of this " "warning and use the built-in defaults."); @@ -8778,74 +8479,73 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) if (anti_flood_old_and_default == 1) { config_warn("%s:%d: the set::anti-flood block has been reorganized to be more flexible.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_warn("To fix this warning, delete the anti-flood block from your configuration file " "(file %s around line %d), this will make UnrealIRCd use the built-in defaults.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_warn("If you want to learn more about the new functionality you can visit " "https://www.unrealircd.org/docs/Anti-flood_settings"); } } - else if (!strcmp(cep->ce_varname, "options")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { - if (!strcmp(cepp->ce_varname, "hide-ulines")) + else if (!strcmp(cep->name, "options")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { + if (!strcmp(cepp->name, "hide-ulines")) { CheckDuplicate(cepp, options_hide_ulines, "options::hide-ulines"); } - else if (!strcmp(cepp->ce_varname, "flat-map")) { + else if (!strcmp(cepp->name, "flat-map")) { CheckDuplicate(cepp, options_flat_map, "options::flat-map"); } - else if (!strcmp(cepp->ce_varname, "show-opermotd")) { + else if (!strcmp(cepp->name, "show-opermotd")) { CheckDuplicate(cepp, options_show_opermotd, "options::show-opermotd"); } - else if (!strcmp(cepp->ce_varname, "identd-check")) { + else if (!strcmp(cepp->name, "identd-check")) { CheckDuplicate(cepp, options_identd_check, "options::identd-check"); } - else if (!strcmp(cepp->ce_varname, "fail-oper-warn")) { + else if (!strcmp(cepp->name, "fail-oper-warn")) { CheckDuplicate(cepp, options_fail_oper_warn, "options::fail-oper-warn"); } - else if (!strcmp(cepp->ce_varname, "show-connect-info")) { + else if (!strcmp(cepp->name, "show-connect-info")) { CheckDuplicate(cepp, options_show_connect_info, "options::show-connect-info"); } - else if (!strcmp(cepp->ce_varname, "no-connect-tls-info")) { + else if (!strcmp(cepp->name, "no-connect-tls-info")) { CheckDuplicate(cepp, options_no_connect_tls_info, "options::no-connect-tls-info"); } - else if (!strcmp(cepp->ce_varname, "dont-resolve")) { + else if (!strcmp(cepp->name, "dont-resolve")) { CheckDuplicate(cepp, options_dont_resolve, "options::dont-resolve"); } - else if (!strcmp(cepp->ce_varname, "mkpasswd-for-everyone")) { + else if (!strcmp(cepp->name, "mkpasswd-for-everyone")) { CheckDuplicate(cepp, options_mkpasswd_for_everyone, "options::mkpasswd-for-everyone"); } - else if (!strcmp(cepp->ce_varname, "allow-insane-bans")) { + else if (!strcmp(cepp->name, "allow-insane-bans")) { CheckDuplicate(cepp, options_allow_insane_bans, "options::allow-insane-bans"); } - else if (!strcmp(cepp->ce_varname, "allow-part-if-shunned")) { + else if (!strcmp(cepp->name, "allow-part-if-shunned")) { CheckDuplicate(cepp, options_allow_part_if_shunned, "options::allow-part-if-shunned"); } - else if (!strcmp(cepp->ce_varname, "disable-cap")) { + else if (!strcmp(cepp->name, "disable-cap")) { CheckDuplicate(cepp, options_disable_cap, "options::disable-cap"); } - else if (!strcmp(cepp->ce_varname, "disable-ipv6")) { + else if (!strcmp(cepp->name, "disable-ipv6")) { CheckDuplicate(cepp, options_disable_ipv6, "options::disable-ipv6"); DISABLE_IPV6 = 1; /* ugly ugly. needs to be done here because at conf runtime is too late. */ } else { - config_error_unknownopt(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::options", - cepp->ce_varname); + config_error_unknownopt(cepp->file->filename, + cepp->line_number, "set::options", + cepp->name); errors++; continue; } } } - else if (!strcmp(cep->ce_varname, "hosts")) { - config_error("%s:%i: set::hosts has been removed in UnrealIRCd 4. You can use oper::vhost now.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + else if (!strcmp(cep->name, "hosts")) { + config_error("%s:%i: set::hosts has been removed. You can use oper::vhost now.", + cep->file->filename, cep->line_number); errors++; - need_34_upgrade = 1; } - else if (!strcmp(cep->ce_varname, "cloak-keys")) + else if (!strcmp(cep->name, "cloak-keys")) { CheckDuplicate(cep, cloak_keys, "cloak-keys"); for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) @@ -8867,488 +8567,486 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) errors += errs; } } - else if (!strcmp(cep->ce_varname, "ident")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + else if (!strcmp(cep->name, "ident")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { int is_ok = 0; CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "connect-timeout")) + if (!strcmp(cepp->name, "connect-timeout")) { is_ok = 1; CheckDuplicate(cepp, ident_connect_timeout, "ident::connect-timeout"); } - else if (!strcmp(cepp->ce_varname, "read-timeout")) + else if (!strcmp(cepp->name, "read-timeout")) { is_ok = 1; CheckDuplicate(cepp, ident_read_timeout, "ident::read-timeout"); } if (is_ok) { - int v = config_checkval(cepp->ce_vardata,CFG_TIME); + int v = config_checkval(cepp->value,CFG_TIME); if ((v > 60) || (v < 1)) { config_error("%s:%i: set::ident::%s value out of range (%d), should be between 1 and 60.", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname, v); + cepp->file->filename, cepp->line_number, cepp->name, v); errors++; continue; } } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::ident", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::ident", + cepp->name); errors++; continue; } } } - else if (!strcmp(cep->ce_varname, "timesync") || !strcmp(cep->ce_varname, "timesynch")) + else if (!strcmp(cep->name, "timesync") || !strcmp(cep->name, "timesynch")) { config_warn("%s:%i: Timesync support has been removed from UnrealIRCd. " "Please remove any set::timesync blocks you may have.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_warn("Use the time synchronization feature of your OS/distro instead!"); } - else if (!strcmp(cep->ce_varname, "spamfilter")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + else if (!strcmp(cep->name, "spamfilter")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "ban-time")) + if (!strcmp(cepp->name, "ban-time")) { long x; CheckDuplicate(cepp, spamfilter_ban_time, "spamfilter::ban-time"); - x = config_checkval(cepp->ce_vardata,CFG_TIME); + x = config_checkval(cepp->value,CFG_TIME); if ((x < 0) > (x > 2000000000)) { config_error("%s:%i: set::spamfilter:ban-time: value '%ld' out of range", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x); + cep->file->filename, cep->line_number, x); errors++; continue; } } else - if (!strcmp(cepp->ce_varname, "ban-reason")) + if (!strcmp(cepp->name, "ban-reason")) { CheckDuplicate(cepp, spamfilter_ban_reason, "spamfilter::ban-reason"); } - else if (!strcmp(cepp->ce_varname, "virus-help-channel")) + else if (!strcmp(cepp->name, "virus-help-channel")) { CheckDuplicate(cepp, spamfilter_virus_help_channel, "spamfilter::virus-help-channel"); - if ((cepp->ce_vardata[0] != '#') || (strlen(cepp->ce_vardata) > CHANNELLEN)) + if ((cepp->value[0] != '#') || (strlen(cepp->value) > CHANNELLEN)) { config_error("%s:%i: set::spamfilter:virus-help-channel: " "specified channelname is too long or contains invalid characters (%s)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cepp->ce_vardata); + cep->file->filename, cep->line_number, + cepp->value); errors++; continue; } } else - if (!strcmp(cepp->ce_varname, "virus-help-channel-deny")) + if (!strcmp(cepp->name, "virus-help-channel-deny")) { CheckDuplicate(cepp, spamfilter_virus_help_channel_deny, "spamfilter::virus-help-channel-deny"); } else - if (!strcmp(cepp->ce_varname, "except")) + if (!strcmp(cepp->name, "except")) { CheckDuplicate(cepp, spamfilter_except, "spamfilter::except"); } else #ifdef SPAMFILTER_DETECTSLOW - if (!strcmp(cepp->ce_varname, "detect-slow-warn")) + if (!strcmp(cepp->name, "detect-slow-warn")) { } else - if (!strcmp(cepp->ce_varname, "detect-slow-fatal")) + if (!strcmp(cepp->name, "detect-slow-fatal")) { } else #endif - if (!strcmp(cepp->ce_varname, "stop-on-first-match")) + if (!strcmp(cepp->name, "stop-on-first-match")) { } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::spamfilter", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::spamfilter", + cepp->name); errors++; continue; } } } -/* TODO: FIX THIS */ - else if (!strcmp(cep->ce_varname, "default-bantime")) + else if (!strcmp(cep->name, "default-bantime")) { long x; CheckDuplicate(cep, default_bantime, "default-bantime"); CheckNull(cep); - x = config_checkval(cep->ce_vardata,CFG_TIME); + x = config_checkval(cep->value,CFG_TIME); if ((x < 0) > (x > 2000000000)) { config_error("%s:%i: set::default-bantime: value '%ld' out of range", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x); + cep->file->filename, cep->line_number, x); errors++; } } - else if (!strcmp(cep->ce_varname, "ban-version-tkl-time")) { + else if (!strcmp(cep->name, "ban-version-tkl-time")) { long x; CheckDuplicate(cep, ban_version_tkl_time, "ban-version-tkl-time"); CheckNull(cep); - x = config_checkval(cep->ce_vardata,CFG_TIME); + x = config_checkval(cep->value,CFG_TIME); if ((x < 0) > (x > 2000000000)) { config_error("%s:%i: set::ban-version-tkl-time: value '%ld' out of range", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, x); + cep->file->filename, cep->line_number, x); errors++; } } - else if (!strcmp(cep->ce_varname, "min-nick-length")) { + else if (!strcmp(cep->name, "min-nick-length")) { int v; CheckDuplicate(cep, min_nick_length, "min-nick-length"); CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if ((v <= 0) || (v > NICKLEN)) { config_error("%s:%i: set::min-nick-length: value '%d' out of range (should be 1-%d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v, NICKLEN); + cep->file->filename, cep->line_number, v, NICKLEN); errors++; } else nicklengths.min = v; } - else if (!strcmp(cep->ce_varname, "nick-length")) { + else if (!strcmp(cep->name, "nick-length")) { int v; CheckDuplicate(cep, nick_length, "nick-length"); CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if ((v <= 0) || (v > NICKLEN)) { config_error("%s:%i: set::nick-length: value '%d' out of range (should be 1-%d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v, NICKLEN); + cep->file->filename, cep->line_number, v, NICKLEN); errors++; } else nicklengths.max = v; } - else if (!strcmp(cep->ce_varname, "topic-length")) { + else if (!strcmp(cep->name, "topic-length")) { int v; CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if ((v <= 0) || (v > MAXTOPICLEN)) { config_error("%s:%i: set::topic-length: value '%d' out of range (should be 1-%d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v, MAXTOPICLEN); + cep->file->filename, cep->line_number, v, MAXTOPICLEN); errors++; } } - else if (!strcmp(cep->ce_varname, "away-length")) { + else if (!strcmp(cep->name, "away-length")) { int v; CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if ((v <= 0) || (v > MAXAWAYLEN)) { config_error("%s:%i: set::away-length: value '%d' out of range (should be 1-%d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v, MAXAWAYLEN); + cep->file->filename, cep->line_number, v, MAXAWAYLEN); errors++; } } - else if (!strcmp(cep->ce_varname, "kick-length")) { + else if (!strcmp(cep->name, "kick-length")) { int v; CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if ((v <= 0) || (v > MAXKICKLEN)) { config_error("%s:%i: set::kick-length: value '%d' out of range (should be 1-%d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v, MAXKICKLEN); + cep->file->filename, cep->line_number, v, MAXKICKLEN); errors++; } } - else if (!strcmp(cep->ce_varname, "quit-length")) { + else if (!strcmp(cep->name, "quit-length")) { int v; CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if ((v <= 0) || (v > MAXQUITLEN)) { config_error("%s:%i: set::quit-length: value '%d' out of range (should be 1-%d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v, MAXQUITLEN); + cep->file->filename, cep->line_number, v, MAXQUITLEN); errors++; } } - else if (!strcmp(cep->ce_varname, "ssl") || !strcmp(cep->ce_varname, "tls")) { + else if (!strcmp(cep->name, "ssl") || !strcmp(cep->name, "tls")) { test_tlsblock(conf, cep, &errors); } - else if (!strcmp(cep->ce_varname, "plaintext-policy")) + else if (!strcmp(cep->name, "plaintext-policy")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "user") || - !strcmp(cepp->ce_varname, "oper") || - !strcmp(cepp->ce_varname, "server")) + if (!strcmp(cepp->name, "user") || + !strcmp(cepp->name, "oper") || + !strcmp(cepp->name, "server")) { Policy policy; CheckNull(cepp); - policy = policy_strtoval(cepp->ce_vardata); + policy = policy_strtoval(cepp->value); if (!policy) { config_error("%s:%i: set::plaintext-policy::%s: needs to be one of: 'allow', 'warn' or 'reject'", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); + cepp->file->filename, cepp->line_number, cepp->name); errors++; } - } else if (!strcmp(cepp->ce_varname, "user-message") || - !strcmp(cepp->ce_varname, "oper-message")) + } else if (!strcmp(cepp->name, "user-message") || + !strcmp(cepp->name, "oper-message")) { CheckNull(cepp); } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::plaintext-policy", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::plaintext-policy", + cepp->name); errors++; continue; } } } - else if (!strcmp(cep->ce_varname, "outdated-tls-policy")) + else if (!strcmp(cep->name, "outdated-tls-policy")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "user") || - !strcmp(cepp->ce_varname, "oper") || - !strcmp(cepp->ce_varname, "server")) + if (!strcmp(cepp->name, "user") || + !strcmp(cepp->name, "oper") || + !strcmp(cepp->name, "server")) { Policy policy; CheckNull(cepp); - policy = policy_strtoval(cepp->ce_vardata); + policy = policy_strtoval(cepp->value); if (!policy) { config_error("%s:%i: set::outdated-tls-policy::%s: needs to be one of: 'allow', 'warn' or 'reject'", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); + cepp->file->filename, cepp->line_number, cepp->name); errors++; } - } else if (!strcmp(cepp->ce_varname, "user-message") || - !strcmp(cepp->ce_varname, "oper-message")) + } else if (!strcmp(cepp->name, "user-message") || + !strcmp(cepp->name, "oper-message")) { CheckNull(cepp); } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::outdated-tls-policy", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::outdated-tls-policy", + cepp->name); errors++; continue; } } } - else if (!strcmp(cep->ce_varname, "default-ipv6-clone-mask")) + else if (!strcmp(cep->name, "default-ipv6-clone-mask")) { /* keep this in sync with _test_allow() */ int ipv6mask; - ipv6mask = atoi(cep->ce_vardata); + ipv6mask = atoi(cep->value); if (ipv6mask == 0) { config_error("%s:%d: set::default-ipv6-clone-mask given a value of zero. This cannnot be correct, as it would treat all IPv6 hosts as one host.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } if (ipv6mask > 128) { config_error("%s:%d: set::default-ipv6-clone-mask was set to %d. The maximum value is 128.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, + cep->file->filename, cep->line_number, ipv6mask); errors++; } if (ipv6mask <= 32) { config_warn("%s:%d: set::default-ipv6-clone-mask was given a very small value.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); } } - else if (!strcmp(cep->ce_varname, "hide-list")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + else if (!strcmp(cep->name, "hide-list")) { + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "deny-channel")) + if (!strcmp(cepp->name, "deny-channel")) { } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::hide-list", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::hide-list", + cepp->name); errors++; continue; } } } - else if (!strcmp(cep->ce_varname, "max-unknown-connections-per-ip")) { + else if (!strcmp(cep->name, "max-unknown-connections-per-ip")) { int v; CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if (v < 1) { config_error("%s:%i: set::max-unknown-connections-per-ip: value should be at least 1.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "handshake-timeout")) { + else if (!strcmp(cep->name, "handshake-timeout")) { int v; CheckNull(cep); - v = config_checkval(cep->ce_vardata, CFG_TIME); + v = config_checkval(cep->value, CFG_TIME); if (v < 5) { config_error("%s:%i: set::handshake-timeout: value should be at least 5 seconds.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "sasl-timeout")) { + else if (!strcmp(cep->name, "sasl-timeout")) { int v; CheckNull(cep); - v = config_checkval(cep->ce_vardata, CFG_TIME); + v = config_checkval(cep->value, CFG_TIME); if (v < 5) { config_error("%s:%i: set::sasl-timeout: value should be at least 5 seconds.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "handshake-delay")) + else if (!strcmp(cep->name, "handshake-delay")) { int v; CheckNull(cep); - v = config_checkval(cep->ce_vardata, CFG_TIME); + v = config_checkval(cep->value, CFG_TIME); if (v >= 10) { config_error("%s:%i: set::handshake-delay: value should be less than 10 seconds.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "ban-include-username")) + else if (!strcmp(cep->name, "ban-include-username")) { config_error("%s:%i: set::ban-include-username is no longer supported. " "Use set { automatic-ban-target userip; }; instead.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_error("See https://www.unrealircd.org/docs/Set_block#set::automatic-ban-target " "for more information and options."); errors++; } - else if (!strcmp(cep->ce_varname, "automatic-ban-target")) + else if (!strcmp(cep->name, "automatic-ban-target")) { CheckNull(cep); - if (!ban_target_strtoval(cep->ce_vardata)) + if (!ban_target_strtoval(cep->value)) { config_error("%s:%i: set::automatic-ban-target: value '%s' is not recognized. " "See https://www.unrealircd.org/docs/Set_block#set::automatic-ban-target", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } } - else if (!strcmp(cep->ce_varname, "manual-ban-target")) + else if (!strcmp(cep->name, "manual-ban-target")) { CheckNull(cep); - if (!ban_target_strtoval(cep->ce_vardata)) + if (!ban_target_strtoval(cep->value)) { config_error("%s:%i: set::manual-ban-target: value '%s' is not recognized. " "See https://www.unrealircd.org/docs/Set_block#set::manual-ban-target", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } } - else if (!strcmp(cep->ce_varname, "reject-message")) + else if (!strcmp(cep->name, "reject-message")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "password-mismatch")) + if (!strcmp(cepp->name, "password-mismatch")) ; - else if (!strcmp(cepp->ce_varname, "too-many-connections")) + else if (!strcmp(cepp->name, "too-many-connections")) ; - else if (!strcmp(cepp->ce_varname, "server-full")) + else if (!strcmp(cepp->name, "server-full")) ; - else if (!strcmp(cepp->ce_varname, "unauthorized")) + else if (!strcmp(cepp->name, "unauthorized")) ; - else if (!strcmp(cepp->ce_varname, "kline")) + else if (!strcmp(cepp->name, "kline")) ; - else if (!strcmp(cepp->ce_varname, "gline")) + else if (!strcmp(cepp->name, "gline")) ; else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::reject-message", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::reject-message", + cepp->name); errors++; continue; } } } - else if (!strcmp(cep->ce_varname, "topic-setter")) + else if (!strcmp(cep->name, "topic-setter")) { CheckNull(cep); - if (strcmp(cep->ce_vardata, "nick") && strcmp(cep->ce_vardata, "nick-user-host")) + if (strcmp(cep->value, "nick") && strcmp(cep->value, "nick-user-host")) { config_error("%s:%i: set::topic-setter: value should be 'nick' or 'nick-user-host'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "ban-setter")) + else if (!strcmp(cep->name, "ban-setter")) { CheckNull(cep); - if (strcmp(cep->ce_vardata, "nick") && strcmp(cep->ce_vardata, "nick-user-host")) + if (strcmp(cep->value, "nick") && strcmp(cep->value, "nick-user-host")) { config_error("%s:%i: set::ban-setter: value should be 'nick' or 'nick-user-host'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "ban-setter-sync") || !strcmp(cep->ce_varname, "ban-setter-synch")) + else if (!strcmp(cep->name, "ban-setter-sync") || !strcmp(cep->name, "ban-setter-synch")) { CheckNull(cep); } - else if (!strcmp(cep->ce_varname, "part-instead-of-quit-on-comment-change")) + else if (!strcmp(cep->name, "part-instead-of-quit-on-comment-change")) { CheckNull(cep); } - else if (!strcmp(cep->ce_varname, "broadcast-channel-messages")) + else if (!strcmp(cep->name, "broadcast-channel-messages")) { CheckNull(cep); - if (strcmp(cep->ce_vardata, "auto") && - strcmp(cep->ce_vardata, "always") && - strcmp(cep->ce_vardata, "never")) + if (strcmp(cep->value, "auto") && + strcmp(cep->value, "always") && + strcmp(cep->value, "never")) { config_error("%s:%i: set::broadcast-channel-messages: value should be 'auto', 'always' or 'never'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "allowed-channelchars")) + else if (!strcmp(cep->name, "allowed-channelchars")) { CheckNull(cep); - if (!allowed_channelchars_strtoval(cep->ce_vardata)) + if (!allowed_channelchars_strtoval(cep->value)) { config_error("%s:%i: set::allowed-channelchars: value should be one of: 'ascii', 'utf8' or 'any'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "hide-idle-time")) + else if (!strcmp(cep->name, "hide-idle-time")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "policy")) + if (!strcmp(cepp->name, "policy")) { - if (!hideidletime_strtoval(cepp->ce_vardata)) + if (!hideidletime_strtoval(cepp->value)) { config_error("%s:%i: set::hide-idle-time::policy: value should be one of: 'never', 'always', 'usermode' or 'oper-usermode'", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; } } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::hide-idle-time", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::hide-idle-time", + cepp->name); errors++; continue; } } - } - else + } else { int used = 0; for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) @@ -9379,8 +9077,8 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) } if (!used) { config_error("%s:%i: unknown directive set::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_varname); + cep->file->filename, cep->line_number, + cep->name); errors++; } } @@ -9390,44 +9088,25 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) int _conf_loadmodule(ConfigFile *conf, ConfigEntry *ce) { - char *ret; - if (!ce->ce_vardata) + const char *ret; + if (!ce->value) { config_status("%s:%i: loadmodule without filename", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return -1; } - if (strstr(ce->ce_vardata, "commands.so") || strstr(ce->ce_vardata, "commands.dll")) - { - config_error("%s:%i: You are trying to load the 'commands' module, this is no longer supported. " - "Fix this by editing your configuration file: remove the loadmodule line for commands and add the following line instead: " - "include \"modules.default.conf\";", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - need_34_upgrade = 1; - return -1; - } - if (strstr(ce->ce_vardata, "modules/cloak") && !strcmp(conf->cf_filename, "modules.conf")) - { - config_error("You seem to have an include for 'modules.conf'."); - config_error("If you have this because you are upgrading from 3.4-alpha3 to"); - config_error("UnrealIRCd 4 then please change the include \"modules.conf\";"); - config_error("into an include \"modules.default.conf\"; (probably in your"); - config_error("conf/unrealircd.conf). Yeah, we changed the file name."); - // TODO ^: silly win32 wrapping prevents this from being displayed otherwise. PLZ FIX! ! - /* let it continue to load anyway? */ - } - if (is_blacklisted_module(ce->ce_vardata)) + if (is_blacklisted_module(ce->value)) { /* config_warn("%s:%i: Module '%s' is blacklisted, not loading", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); */ + ce->file->filename, ce->line_number, ce->value); */ return 1; } - if ((ret = Module_Create(ce->ce_vardata))) { - config_status("%s:%i: loadmodule %s: failed to load: %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata, ret); + if ((ret = Module_Create(ce->value))) { + config_error("%s:%i: loadmodule %s: failed to load: %s", + ce->file->filename, ce->line_number, + ce->value, ret); return -1; } return 1; @@ -9440,17 +9119,17 @@ int _test_loadmodule(ConfigFile *conf, ConfigEntry *ce) int _test_blacklist_module(ConfigFile *conf, ConfigEntry *ce) { - char *path; + const char *path; ConfigItem_blacklist_module *m; - if (!ce->ce_vardata) + if (!ce->value) { config_status("%s:%i: blacklist-module: no module name given to blacklist", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return -1; } - path = Module_TransformPath(ce->ce_vardata); + path = Module_TransformPath(ce->value); /* Is it a good idea to warn about this? * Yes, the user may have made a typo, thinking (s)he blacklisted something @@ -9462,20 +9141,20 @@ int _test_blacklist_module(ConfigFile *conf, ConfigEntry *ce) if (!file_exists(path)) { config_warn("%s:%i: blacklist-module for '%s' but module does not exist anyway", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); + ce->file->filename, ce->line_number, ce->value); /* fallthrough */ } m = safe_alloc(sizeof(ConfigItem_blacklist_module)); - safe_strdup(m->name, ce->ce_vardata); + safe_strdup(m->name, ce->value); AddListItem(m, conf_blacklist_module); return 0; } -int is_blacklisted_module(char *name) +int is_blacklisted_module(const char *name) { - char *path = Module_TransformPath(name); + const char *path = Module_TransformPath(name); ConfigItem_blacklist_module *m; for (m = conf_blacklist_module; m; m = m->next) @@ -9487,37 +9166,39 @@ int is_blacklisted_module(char *name) void start_listeners(void) { - ConfigItem_listen *listenptr; + ConfigItem_listen *listener; int failed = 0, ports_bound = 0; char boundmsg_ipv4[512], boundmsg_ipv6[512]; + int last_errno = 0; *boundmsg_ipv4 = *boundmsg_ipv6 = '\0'; - for (listenptr = conf_listen; listenptr; listenptr = listenptr->next) + for (listener = conf_listen; listener; listener = listener->next) { /* Try to bind to any ports that are not yet bound and not marked as temporary */ - if (!(listenptr->options & LISTENER_BOUND) && !listenptr->flag.temporary) + if (!(listener->options & LISTENER_BOUND) && !listener->flag.temporary) { - if (add_listener(listenptr) == -1) + if (add_listener(listener) == -1) { - ircd_log(LOG_ERROR, "Failed to bind to %s:%i", listenptr->ip, listenptr->port); + /* Error already printed upstream */ failed = 1; + last_errno = ERRNO; } else { - if (loop.ircd_booted) + if (loop.booted) { - ircd_log(LOG_ERROR, "UnrealIRCd is now also listening on %s:%d (%s)%s", - listenptr->ip, listenptr->port, - listenptr->ipv6 ? "IPv6" : "IPv4", - listenptr->options & LISTENER_TLS ? " (SSL/TLS)" : ""); + unreal_log(ULOG_INFO, "listen", "LISTEN_ADDED", NULL, + "UnrealIRCd is now also listening on $listen_ip:$listen_port", + log_data_string("listen_ip", listener->ip), + log_data_integer("listen_port", listener->port)); } else { - if (listenptr->ipv6) + if (listener->ipv6) snprintf(boundmsg_ipv6+strlen(boundmsg_ipv6), sizeof(boundmsg_ipv6)-strlen(boundmsg_ipv6), - "%s:%d%s, ", listenptr->ip, listenptr->port, - listenptr->options & LISTENER_TLS ? "(SSL/TLS)" : ""); + "%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, ", listenptr->ip, listenptr->port, - listenptr->options & LISTENER_TLS ? "(SSL/TLS)" : ""); + "%s:%d%s, ", listener->ip, listener->port, + listener->options & LISTENER_TLS ? "(TLS)" : ""); } } } @@ -9525,58 +9206,83 @@ void start_listeners(void) /* NOTE: do not merge this with code above (nor in an else block), * as add_listener() affects this flag. */ - if (listenptr->options & LISTENER_BOUND) + if (listener->options & LISTENER_BOUND) ports_bound++; } if (ports_bound == 0) { - ircd_log(LOG_ERROR, "IRCd could not listen on any ports. If you see 'Address already in use' errors " - "above then most likely the IRCd is already running (or something else is using the " - "specified ports). If you are sure the IRCd is not running then verify your " - "listen blocks, maybe you have to bind to a specific IP rather than \"*\"."); +#ifdef _WIN32 + if (last_errno == WSAEADDRINUSE) +#else + if (last_errno == EADDRINUSE) +#endif + { + /* We can be specific */ + unreal_log(ULOG_FATAL, "listen", "ALL_LISTEN_PORTS_FAILED", NULL, + "Unable to listen on any ports. " + "Most likely UnrealIRCd is already running."); + } else { + unreal_log(ULOG_FATAL, "listen", "ALL_LISTEN_PORTS_FAILED", NULL, + "Unable to listen on any ports. " + "Please verify that no other process is using the ports. " + "Also, on some IRCd shells you may have to use listen::bind-ip " + "with a specific IP assigned to you (rather than \"*\")."); + } exit(-1); } - if (failed && !loop.ircd_booted) + if (failed && !loop.booted) { - ircd_log(LOG_ERROR, "Could not listen on all specified addresses/ports. See errors above. " - "Please fix your listen { } blocks and/or make sure no other programs " - "are listening on the same port."); + unreal_log(ULOG_FATAL, "listen", "SOME_LISTEN_PORTS_FAILED", NULL, + "Unable to listen on all ports (some of them succeeded, some of them failed). " + "Please verify that no other process is using the port(s). " + "Also, on some IRCd shells you may have to use listen::bind-ip " + "with a specific IP assigned to you (rather than \"*\")."); exit(-1); } - if (!loop.ircd_booted) + if (!loop.booted) { if (strlen(boundmsg_ipv4) > 2) boundmsg_ipv4[strlen(boundmsg_ipv4)-2] = '\0'; if (strlen(boundmsg_ipv6) > 2) boundmsg_ipv6[strlen(boundmsg_ipv6)-2] = '\0'; - ircd_log(LOG_ERROR, "UnrealIRCd is now listening on the following addresses/ports:"); - ircd_log(LOG_ERROR, "IPv4: %s", *boundmsg_ipv4 ? boundmsg_ipv4 : ""); - ircd_log(LOG_ERROR, "IPv6: %s", *boundmsg_ipv6 ? boundmsg_ipv6 : ""); + if (!*boundmsg_ipv4) + strlcpy(boundmsg_ipv4, "", sizeof(boundmsg_ipv4)); + if (!*boundmsg_ipv6) + strlcpy(boundmsg_ipv6, "", sizeof(boundmsg_ipv6)); + + unreal_log(ULOG_INFO, "listen", "LISTENING", NULL, + "UnrealIRCd is now listening on the following addresses/ports:\n" + "IPv4: $ipv4_port_list\n" + "IPv6: $ipv6_port_list\n", + log_data_string("ipv4_port_list", boundmsg_ipv4), + log_data_string("ipv6_port_list", boundmsg_ipv6)); } } /* Actually use configuration */ -void run_configuration(void) +void config_run(void) { + extcmodes_check_for_changes(); start_listeners(); + free_all_config_resources(); } int _conf_offchans(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep, *cepp; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { ConfigItem_offchans *of = safe_alloc(sizeof(ConfigItem_offchans)); - strlcpy(of->chname, cep->ce_varname, CHANNELLEN+1); - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + strlcpy(of->name, cep->name, CHANNELLEN+1); + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "topic")) - safe_strdup(of->topic, cepp->ce_vardata); + if (!strcmp(cepp->name, "topic")) + safe_strdup(of->topic, cepp->value); } AddListItem(of, conf_offchans); } @@ -9588,10 +9294,10 @@ int _test_offchans(ConfigFile *conf, ConfigEntry *ce) int errors = 0; ConfigEntry *cep, *cep2; - if (!ce->ce_entries) + if (!ce->items) { config_error("%s:%i: empty official-channels block", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } @@ -9600,45 +9306,45 @@ int _test_offchans(ConfigFile *conf, ConfigEntry *ce) "and then making the channel permanent (MODE #channel +P). " "The channel will then be stored in a database to preserve it between restarts."); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (strlen(cep->ce_varname) > CHANNELLEN) + if (strlen(cep->name) > CHANNELLEN) { config_error("%s:%i: official-channels: '%s' name too long (max %d characters).", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname, CHANNELLEN); + cep->file->filename, cep->line_number, cep->name, CHANNELLEN); errors++; continue; } - if (!valid_channelname(cep->ce_varname)) + if (!valid_channelname(cep->name)) { config_error("%s:%i: official-channels: '%s' is not a valid channel name.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; continue; } - for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) + for (cep2 = cep->items; cep2; cep2 = cep2->next) { - if (!cep2->ce_vardata) + if (!cep2->value) { - config_error_empty(cep2->ce_fileptr->cf_filename, - cep2->ce_varlinenum, "official-channels", - cep2->ce_varname); + config_error_empty(cep2->file->filename, + cep2->line_number, "official-channels", + cep2->name); errors++; continue; } - if (!strcmp(cep2->ce_varname, "topic")) + if (!strcmp(cep2->name, "topic")) { - if (strlen(cep2->ce_vardata) > MAXTOPICLEN) + if (strlen(cep2->value) > MAXTOPICLEN) { config_error("%s:%i: official-channels::%s: topic too long (max %d characters).", - cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname, MAXTOPICLEN); + cep2->file->filename, cep2->line_number, cep->name, MAXTOPICLEN); errors++; continue; } } else { - config_error_unknown(cep2->ce_fileptr->cf_filename, - cep2->ce_varlinenum, "official-channels", - cep2->ce_varname); + config_error_unknown(cep2->file->filename, + cep2->line_number, "official-channels", + cep2->name); errors++; continue; } @@ -9654,70 +9360,70 @@ int _conf_alias(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep, *cepp; RealCommand *cmptr; - if ((cmptr = find_command(ce->ce_vardata, CMD_ALIAS))) + if ((cmptr = find_command(ce->value, CMD_ALIAS))) CommandDelX(NULL, cmptr); - if (find_command_simple(ce->ce_vardata)) + if (find_command_simple(ce->value)) { config_warn("%s:%i: Alias '%s' would conflict with command (or server token) '%s', alias not added.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata, ce->ce_vardata); + ce->file->filename, ce->line_number, + ce->value, ce->value); return 0; } - if ((alias = find_alias(ce->ce_vardata))) + if ((alias = find_alias(ce->value))) DelListItem(alias, conf_alias); alias = safe_alloc(sizeof(ConfigItem_alias)); - safe_strdup(alias->alias, ce->ce_vardata); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + safe_strdup(alias->alias, ce->value); + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "format")) { + if (!strcmp(cep->name, "format")) { format = safe_alloc(sizeof(ConfigItem_alias_format)); - safe_strdup(format->format, cep->ce_vardata); - format->expr = unreal_create_match(MATCH_PCRE_REGEX, cep->ce_vardata, NULL); + safe_strdup(format->format, cep->value); + format->expr = unreal_create_match(MATCH_PCRE_REGEX, cep->value, NULL); if (!format->expr) abort(); /* Impossible due to _test_alias earlier */ - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { - if (!strcmp(cepp->ce_varname, "nick") || - !strcmp(cepp->ce_varname, "target") || - !strcmp(cepp->ce_varname, "command")) { - safe_strdup(format->nick, cepp->ce_vardata); + for (cepp = cep->items; cepp; cepp = cepp->next) { + if (!strcmp(cepp->name, "nick") || + !strcmp(cepp->name, "target") || + !strcmp(cepp->name, "command")) { + safe_strdup(format->nick, cepp->value); } - else if (!strcmp(cepp->ce_varname, "parameters")) { - safe_strdup(format->parameters, cepp->ce_vardata); + else if (!strcmp(cepp->name, "parameters")) { + safe_strdup(format->parameters, cepp->value); } - else if (!strcmp(cepp->ce_varname, "type")) { - if (!strcmp(cepp->ce_vardata, "services")) + else if (!strcmp(cepp->name, "type")) { + if (!strcmp(cepp->value, "services")) format->type = ALIAS_SERVICES; - else if (!strcmp(cepp->ce_vardata, "stats")) + else if (!strcmp(cepp->value, "stats")) format->type = ALIAS_STATS; - else if (!strcmp(cepp->ce_vardata, "normal")) + else if (!strcmp(cepp->value, "normal")) format->type = ALIAS_NORMAL; - else if (!strcmp(cepp->ce_vardata, "channel")) + else if (!strcmp(cepp->value, "channel")) format->type = ALIAS_CHANNEL; - else if (!strcmp(cepp->ce_vardata, "real")) + else if (!strcmp(cepp->value, "real")) format->type = ALIAS_REAL; } } AddListItem(format, alias->format); } - else if (!strcmp(cep->ce_varname, "nick") || !strcmp(cep->ce_varname, "target")) + else if (!strcmp(cep->name, "nick") || !strcmp(cep->name, "target")) { - safe_strdup(alias->nick, cep->ce_vardata); + safe_strdup(alias->nick, cep->value); } - else if (!strcmp(cep->ce_varname, "type")) { - if (!strcmp(cep->ce_vardata, "services")) + else if (!strcmp(cep->name, "type")) { + if (!strcmp(cep->value, "services")) alias->type = ALIAS_SERVICES; - else if (!strcmp(cep->ce_vardata, "stats")) + else if (!strcmp(cep->value, "stats")) alias->type = ALIAS_STATS; - else if (!strcmp(cep->ce_vardata, "normal")) + else if (!strcmp(cep->value, "normal")) alias->type = ALIAS_NORMAL; - else if (!strcmp(cep->ce_vardata, "channel")) + else if (!strcmp(cep->value, "channel")) alias->type = ALIAS_CHANNEL; - else if (!strcmp(cep->ce_vardata, "command")) + else if (!strcmp(cep->value, "command")) alias->type = ALIAS_COMMAND; } - else if (!strcmp(cep->ce_varname, "spamfilter")) - alias->spamfilter = config_checkval(cep->ce_vardata, CFG_YESNO); + else if (!strcmp(cep->name, "spamfilter")) + alias->spamfilter = config_checkval(cep->value, CFG_YESNO); } if (BadPtr(alias->nick) && alias->type != ALIAS_COMMAND) { safe_strdup(alias->nick, alias->alias); @@ -9735,99 +9441,97 @@ int _test_alias(ConfigFile *conf, ConfigEntry *ce) { char has_type = 0, has_target = 0, has_format = 0; char type = 0; - if (!ce->ce_entries) + if (!ce->items) { config_error("%s:%i: empty alias block", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: alias without name", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } - else if (!find_command(ce->ce_vardata, CMD_ALIAS) && find_command(ce->ce_vardata, 0)) { + else if (!find_command(ce->value, CMD_ALIAS) && find_command(ce->value, 0)) { config_status("%s:%i: %s is an existing command, can not add alias", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); + ce->file->filename, ce->line_number, ce->value); errors++; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "alias")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "format")) { + if (!strcmp(cep->name, "format")) { char *err = NULL; Match *expr; char has_type = 0, has_target = 0, has_parameters = 0; has_format = 1; - expr = unreal_create_match(MATCH_PCRE_REGEX, cep->ce_vardata, &err); + expr = unreal_create_match(MATCH_PCRE_REGEX, cep->value, &err); if (!expr) { config_error("%s:%i: alias::format contains an invalid regex: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err); - config_error("Upgrading from 3.2.x to UnrealIRCd 4? Note that regex changed from POSIX Regex " - "to PCRE Regex!"); /* TODO: refer to some url ? */ + cep->file->filename, cep->line_number, err); } else { unreal_delete_match(expr); } - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { + for (cepp = cep->items; cepp; cepp = cepp->next) { if (config_is_blankorempty(cepp, "alias::format")) { errors++; continue; } - if (!strcmp(cepp->ce_varname, "nick") || - !strcmp(cepp->ce_varname, "command") || - !strcmp(cepp->ce_varname, "target")) + if (!strcmp(cepp->name, "nick") || + !strcmp(cepp->name, "command") || + !strcmp(cepp->name, "target")) { if (has_target) { - config_warn_duplicate(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, + config_warn_duplicate(cepp->file->filename, + cepp->line_number, "alias::format::target"); continue; } has_target = 1; } - else if (!strcmp(cepp->ce_varname, "type")) + else if (!strcmp(cepp->name, "type")) { if (has_type) { - config_warn_duplicate(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, + config_warn_duplicate(cepp->file->filename, + cepp->line_number, "alias::format::type"); continue; } has_type = 1; - if (!strcmp(cepp->ce_vardata, "services")) + if (!strcmp(cepp->value, "services")) ; - else if (!strcmp(cepp->ce_vardata, "stats")) + else if (!strcmp(cepp->value, "stats")) ; - else if (!strcmp(cepp->ce_vardata, "normal")) + else if (!strcmp(cepp->value, "normal")) ; - else if (!strcmp(cepp->ce_vardata, "channel")) + else if (!strcmp(cepp->value, "channel")) ; - else if (!strcmp(cepp->ce_vardata, "real")) + else if (!strcmp(cepp->value, "real")) ; else { config_error("%s:%i: unknown alias type", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; } } - else if (!strcmp(cepp->ce_varname, "parameters")) + else if (!strcmp(cepp->name, "parameters")) { if (has_parameters) { - config_warn_duplicate(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, + config_warn_duplicate(cepp->file->filename, + cepp->line_number, "alias::format::parameters"); continue; } @@ -9835,89 +9539,89 @@ int _test_alias(ConfigFile *conf, ConfigEntry *ce) { } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "alias::format", - cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "alias::format", + cepp->name); errors++; } } if (!has_target) { - config_error_missing(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "alias::format::target"); + config_error_missing(cep->file->filename, + cep->line_number, "alias::format::target"); errors++; } if (!has_type) { - config_error_missing(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "alias::format::type"); + config_error_missing(cep->file->filename, + cep->line_number, "alias::format::type"); errors++; } if (!has_parameters) { - config_error_missing(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "alias::format::parameters"); + config_error_missing(cep->file->filename, + cep->line_number, "alias::format::parameters"); errors++; } } - else if (!strcmp(cep->ce_varname, "nick") || !strcmp(cep->ce_varname, "target")) + else if (!strcmp(cep->name, "nick") || !strcmp(cep->name, "target")) { if (has_target) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "alias::target"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "alias::target"); continue; } has_target = 1; } - else if (!strcmp(cep->ce_varname, "type")) { + else if (!strcmp(cep->name, "type")) { if (has_type) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "alias::type"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "alias::type"); continue; } has_type = 1; - if (!strcmp(cep->ce_vardata, "services")) + if (!strcmp(cep->value, "services")) ; - else if (!strcmp(cep->ce_vardata, "stats")) + else if (!strcmp(cep->value, "stats")) ; - else if (!strcmp(cep->ce_vardata, "normal")) + else if (!strcmp(cep->value, "normal")) ; - else if (!strcmp(cep->ce_vardata, "channel")) + else if (!strcmp(cep->value, "channel")) ; - else if (!strcmp(cep->ce_vardata, "command")) + else if (!strcmp(cep->value, "command")) type = 'c'; else { config_error("%s:%i: unknown alias type", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } - else if (!strcmp(cep->ce_varname, "spamfilter")) + else if (!strcmp(cep->name, "spamfilter")) ; else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "alias", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "alias", cep->name); errors++; } } if (!has_type) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "alias::type"); errors++; } if (!has_format && type == 'c') { config_error("%s:%d: alias::type is 'command' but no alias::format was specified", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } else if (has_format && type != 'c') { config_error("%s:%d: alias::format specified when type is not 'command'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } return errors; @@ -9927,11 +9631,11 @@ int _conf_deny(ConfigFile *conf, ConfigEntry *ce) { Hook *h; - if (!strcmp(ce->ce_vardata, "channel")) + if (!strcmp(ce->value, "channel")) _conf_deny_channel(conf, ce); - else if (!strcmp(ce->ce_vardata, "link")) + else if (!strcmp(ce->value, "link")) _conf_deny_link(conf, ce); - else if (!strcmp(ce->ce_vardata, "version")) + else if (!strcmp(ce->value, "version")) _conf_deny_version(conf, ce); else { @@ -9953,29 +9657,29 @@ int _conf_deny_channel(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; deny = safe_alloc(sizeof(ConfigItem_deny_channel)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "channel")) + if (!strcmp(cep->name, "channel")) { - safe_strdup(deny->channel, cep->ce_vardata); + safe_strdup(deny->channel, cep->value); } - else if (!strcmp(cep->ce_varname, "redirect")) + else if (!strcmp(cep->name, "redirect")) { - safe_strdup(deny->redirect, cep->ce_vardata); + safe_strdup(deny->redirect, cep->value); } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { - safe_strdup(deny->reason, cep->ce_vardata); + safe_strdup(deny->reason, cep->value); } - else if (!strcmp(cep->ce_varname, "warn")) + else if (!strcmp(cep->name, "warn")) { - deny->warn = config_checkval(cep->ce_vardata,CFG_YESNO); + deny->warn = config_checkval(cep->value,CFG_YESNO); } - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { - safe_strdup(deny->class, cep->ce_vardata); + safe_strdup(deny->class, cep->value); } - else if (!strcmp(cep->ce_varname, "mask")) + else if (!strcmp(cep->name, "mask")) { unreal_add_masks(&deny->mask, cep); } @@ -9989,21 +9693,21 @@ int _conf_deny_link(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; deny = safe_alloc(sizeof(ConfigItem_deny_link)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - safe_strdup(deny->mask, cep->ce_vardata); + unreal_add_masks(&deny->mask, cep); } - else if (!strcmp(cep->ce_varname, "rule")) + else if (!strcmp(cep->name, "rule")) { - deny->rule = (char *)crule_parse(cep->ce_vardata); - safe_strdup(deny->prettyrule, cep->ce_vardata); + deny->rule = (char *)crule_parse(cep->value); + safe_strdup(deny->prettyrule, cep->value); } - else if (!strcmp(cep->ce_varname, "type")) { - if (!strcmp(cep->ce_vardata, "all")) + else if (!strcmp(cep->name, "type")) { + if (!strcmp(cep->value, "all")) deny->flag.type = CRULE_ALL; - else if (!strcmp(cep->ce_vardata, "auto")) + else if (!strcmp(cep->value, "auto")) deny->flag.type = CRULE_AUTO; } } @@ -10017,19 +9721,19 @@ int _conf_deny_version(ConfigFile *conf, ConfigEntry *ce) ConfigEntry *cep; deny = safe_alloc(sizeof(ConfigItem_deny_version)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - safe_strdup(deny->mask, cep->ce_vardata); + safe_strdup(deny->mask, cep->value); } - else if (!strcmp(cep->ce_varname, "version")) + else if (!strcmp(cep->name, "version")) { - safe_strdup(deny->version, cep->ce_vardata); + safe_strdup(deny->version, cep->value); } - else if (!strcmp(cep->ce_varname, "flags")) + else if (!strcmp(cep->name, "flags")) { - safe_strdup(deny->flags, cep->ce_vardata); + safe_strdup(deny->flags, cep->value); } } AddListItem(deny, conf_deny_version); @@ -10042,241 +9746,253 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce) int errors = 0; Hook *h; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: deny without type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - if (!strcmp(ce->ce_vardata, "channel")) + if (!strcmp(ce->value, "channel")) { char has_channel = 0, has_warn = 0, has_reason = 0, has_redirect = 0, has_class = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "deny channel")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "channel")) + if (!strcmp(cep->name, "channel")) { if (has_channel) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny channel::channel"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny channel::channel"); continue; } has_channel = 1; } - else if (!strcmp(cep->ce_varname, "redirect")) + else if (!strcmp(cep->name, "redirect")) { if (has_redirect) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny channel::redirect"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny channel::redirect"); continue; } has_redirect = 1; } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { if (has_reason) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny channel::reason"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny channel::reason"); continue; } has_reason = 1; } - else if (!strcmp(cep->ce_varname, "warn")) + else if (!strcmp(cep->name, "warn")) { if (has_warn) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny channel::warn"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny channel::warn"); continue; } has_warn = 1; } - else if (!strcmp(cep->ce_varname, "class")) + else if (!strcmp(cep->name, "class")) { if (has_class) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny channel::class"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny channel::class"); continue; } has_class = 1; } - else if (!strcmp(cep->ce_varname, "mask")) + else if (!strcmp(cep->name, "mask")) { } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny channel", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "deny channel", cep->name); errors++; } } if (!has_channel) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny channel::channel"); errors++; } if (!has_reason) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny channel::reason"); errors++; } } - else if (!strcmp(ce->ce_vardata, "link")) + else if (!strcmp(ce->value, "link")) { char has_mask = 0, has_rule = 0, has_type = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (config_is_blankorempty(cep, "deny link")) + if (!cep->items) { - errors++; - continue; - } - if (!strcmp(cep->ce_varname, "mask")) - { - if (has_mask) + if (config_is_blankorempty(cep, "deny link")) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny link::mask"); - continue; - } - has_mask = 1; - } - else if (!strcmp(cep->ce_varname, "rule")) - { - int val = 0; - if (has_rule) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny link::rule"); - continue; - } - has_rule = 1; - if ((val = crule_test(cep->ce_vardata))) - { - config_error("%s:%i: deny link::rule contains an invalid expression: %s", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - crule_errstring(val)); errors++; - } - } - else if (!strcmp(cep->ce_varname, "type")) - { - if (has_type) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny link::type"); continue; } - has_type = 1; - if (!strcmp(cep->ce_vardata, "auto")) - ; - else if (!strcmp(cep->ce_vardata, "all")) - ; - else { - config_status("%s:%i: unknown deny link type", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + else if (!strcmp(cep->name, "mask")) + { + has_mask = 1; + } else if (!strcmp(cep->name, "rule")) + { + int val = 0; + if (has_rule) + { + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny link::rule"); + continue; + } + has_rule = 1; + if ((val = crule_test(cep->value))) + { + config_error("%s:%i: deny link::rule contains an invalid expression: %s", + cep->file->filename, + cep->line_number, + crule_errstring(val)); + errors++; + } + } + else if (!strcmp(cep->name, "type")) + { + if (has_type) + { + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny link::type"); + continue; + } + has_type = 1; + if (!strcmp(cep->value, "auto")) + ; + else if (!strcmp(cep->value, "all")) + ; + else { + config_status("%s:%i: unknown deny link type", + cep->file->filename, cep->line_number); + errors++; + } + } + else + { + config_error_unknown(cep->file->filename, + cep->line_number, "deny link", cep->name); errors++; } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny link", cep->ce_varname); - errors++; + // Sections + if (!strcmp(cep->name, "mask")) + { + if (cep->value || cep->items) + has_mask = 1; + } + else + { + config_error_unknown(cep->file->filename, + cep->line_number, "deny link", cep->name); + errors++; + continue; + } } } if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny link::mask"); errors++; } if (!has_rule) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny link::rule"); errors++; } if (!has_type) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny link::type"); errors++; } } - else if (!strcmp(ce->ce_vardata, "version")) + else if (!strcmp(ce->value, "version")) { char has_mask = 0, has_version = 0, has_flags = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "deny version")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { if (has_mask) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny version::mask"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny version::mask"); continue; } has_mask = 1; } - else if (!strcmp(cep->ce_varname, "version")) + else if (!strcmp(cep->name, "version")) { if (has_version) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny version::version"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny version::version"); continue; } has_version = 1; } - else if (!strcmp(cep->ce_varname, "flags")) + else if (!strcmp(cep->name, "flags")) { if (has_flags) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny version::flags"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny version::flags"); continue; } has_flags = 1; } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny version", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "deny version", cep->name); errors++; } } if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny version::mask"); errors++; } if (!has_version) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny version::version"); errors++; } if (!has_flags) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny version::flags"); errors++; } @@ -10312,8 +10028,8 @@ int _test_deny(ConfigFile *conf, ConfigEntry *ce) } if (!used) { config_error("%s:%i: unknown deny type %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata); + ce->file->filename, ce->line_number, + ce->value); return 1; } return errors; @@ -10327,59 +10043,62 @@ int _test_security_group(ConfigFile *conf, ConfigEntry *ce) int errors = 0; ConfigEntry *cep; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: security-group block needs a name, eg: security-group web-users {", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } else { - if (!strcasecmp(ce->ce_vardata, "unknown-users")) + if (!strcasecmp(ce->value, "unknown-users")) { config_error("%s:%i: The 'unknown-users' group is a special group that is the " "inverse of 'known-users', you cannot create or adjust it in the " "config file, as it is created automatically by UnrealIRCd.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; return errors; } - if (!security_group_valid_name(ce->ce_vardata)) + if (!security_group_valid_name(ce->value)) { config_error("%s:%i: security-group block name '%s' contains invalid characters or is too long. " "Only letters, numbers, underscore and hyphen are allowed.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); + ce->file->filename, ce->line_number, ce->value); errors++; } } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "webirc")) + if (!strcmp(cep->name, "webirc")) { CheckNull(cep); } else - if (!strcmp(cep->ce_varname, "identified")) + if (!strcmp(cep->name, "identified")) { CheckNull(cep); } else - if (!strcmp(cep->ce_varname, "tls")) + if (!strcmp(cep->name, "tls")) { CheckNull(cep); } else - if (!strcmp(cep->ce_varname, "reputation-score")) + if (!strcmp(cep->name, "reputation-score")) { int v; CheckNull(cep); - v = atoi(cep->ce_vardata); + v = atoi(cep->value); if ((v < 1) || (v > 10000)) { config_error("%s:%i: security-group::reputation-score needs to be a value of 1-10000", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } else + if (!strcmp(cep->name, "include-mask")) { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "security-group", cep->ce_varname); + } else + { + config_error_unknown(cep->file->filename, cep->line_number, + "security-group", cep->name); errors++; continue; } @@ -10391,29 +10110,33 @@ int _test_security_group(ConfigFile *conf, ConfigEntry *ce) int _conf_security_group(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; - SecurityGroup *s = add_security_group(ce->ce_vardata, 1); + SecurityGroup *s = add_security_group(ce->value, 1); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "webirc")) - s->webirc = config_checkval(cep->ce_vardata, CFG_YESNO); - else if (!strcmp(cep->ce_varname, "identified")) - s->identified = config_checkval(cep->ce_vardata, CFG_YESNO); - else if (!strcmp(cep->ce_varname, "tls")) - s->tls = config_checkval(cep->ce_vardata, CFG_YESNO); - else if (!strcmp(cep->ce_varname, "reputation-score")) - s->reputation_score = atoi(cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "priority")) + if (!strcmp(cep->name, "webirc")) + s->webirc = config_checkval(cep->value, CFG_YESNO); + else if (!strcmp(cep->name, "identified")) + s->identified = config_checkval(cep->value, CFG_YESNO); + else if (!strcmp(cep->name, "tls")) + s->tls = config_checkval(cep->value, CFG_YESNO); + else if (!strcmp(cep->name, "reputation-score")) + s->reputation_score = atoi(cep->value); + else if (!strcmp(cep->name, "priority")) { - s->priority = atoi(cep->ce_vardata); + s->priority = atoi(cep->value); DelListItem(s, securitygroups); AddListItemPrio(s, securitygroups, s->priority); } + else if (!strcmp(cep->name, "include-mask")) + { + unreal_add_masks(&s->include_mask, cep); + } } return 1; } -Secret *find_secret(char *secret_name) +Secret *find_secret(const char *secret_name) { Secret *s; for (s = secrets; s; s = s->next) @@ -10444,7 +10167,7 @@ void free_secret(Secret *s) safe_free(s); } -char *_conf_secret_read_password_file(char *fname) +char *_conf_secret_read_password_file(const char *fname) { char *pwd, *err; int fd, n; @@ -10482,7 +10205,7 @@ char *_conf_secret_read_password_file(char *fname) return pwd; } -char *_conf_secret_read_prompt(char *blockname) +char *_conf_secret_read_prompt(const char *blockname) { char *pwd, *pwd_prompt; char buf[256]; @@ -10513,54 +10236,54 @@ int _test_secret(ConfigFile *conf, ConfigEntry *ce) char *err; Secret *existing; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: secret block needs a name, eg: secret xyz {", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; - return errors; /* need to return here since we dereference ce->ce_vardata later.. */ + return errors; /* need to return here since we dereference ce->value later.. */ } else { - if (!security_group_valid_name(ce->ce_vardata)) + if (!security_group_valid_name(ce->value)) { config_error("%s:%i: secret block name '%s' contains invalid characters or is too long. " "Only letters, numbers, underscore and hyphen are allowed.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); + ce->file->filename, ce->line_number, ce->value); errors++; } } - existing = find_secret(ce->ce_vardata); + existing = find_secret(ce->value); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "password")) + if (!strcmp(cep->name, "password")) { int n; has_password = 1; CheckNull(cep); - if (cep->ce_entries || - (((n = Auth_AutoDetectHashType(cep->ce_vardata))) && ((n == AUTHTYPE_BCRYPT) || (n == AUTHTYPE_ARGON2)))) + if (cep->items || + (((n = Auth_AutoDetectHashType(cep->value))) && ((n == AUTHTYPE_BCRYPT) || (n == AUTHTYPE_ARGON2)))) { config_error("%s:%d: you cannot use hashed passwords here, see " "https://www.unrealircd.org/docs/Secret_block#secret-plaintext", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; continue; } - if (!valid_secret_password(cep->ce_vardata, &err)) + if (!valid_secret_password(cep->value, &err)) { config_error("%s:%d: secret::password does not meet password complexity requirements: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err); + cep->file->filename, cep->line_number, err); errors++; } } else - if (!strcmp(cep->ce_varname, "password-file")) + if (!strcmp(cep->name, "password-file")) { char *str; has_password_file = 1; CheckNull(cep); - convert_to_absolute_path(&cep->ce_vardata, CONFDIR); - if (!file_exists(cep->ce_vardata) && existing && existing->password) + convert_to_absolute_path(&cep->value, CONFDIR); + if (!file_exists(cep->value) && existing && existing->password) { /* Silently ignore the case where a secret block already * has the password read and now the file is no longer available. @@ -10569,59 +10292,59 @@ int _test_secret(ConfigFile *conf, ConfigEntry *ce) */ } else { - str = _conf_secret_read_password_file(cep->ce_vardata); + str = _conf_secret_read_password_file(cep->value); if (!str) { config_error("%s:%d: secret::password-file: error reading password from file, see error from above.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } safe_free_sensitive(str); } } else - if (!strcmp(cep->ce_varname, "password-prompt")) + if (!strcmp(cep->name, "password-prompt")) { #ifdef _WIN32 config_error("%s:%d: secret::password-prompt is not implemented in Windows at the moment, sorry!", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_error("Choose a different method to enter passwords or use *NIX"); errors++; return errors; #endif has_password_prompt = 1; - if (loop.ircd_booted && !find_secret(ce->ce_vardata)) + if (loop.booted && !find_secret(ce->value)) { config_error("%s:%d: you cannot add a new secret { } block that uses password-prompt and then /REHASH. " "With 'password-prompt' you can only add such a password on boot.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_error("Either use a different method to enter passwords or restart the IRCd on the console."); errors++; } - if (!loop.ircd_booted && !running_interactively()) + if (!loop.booted && !running_interactively()) { config_error("ERROR: IRCd is not running interactively, but via a cron job or something similar."); config_error("%s:%d: unable to prompt for password since IRCd is not started in a terminal", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); config_error("Either use a different method to enter passwords or start the IRCd in a terminal/SSH/.."); } } else - if (!strcmp(cep->ce_varname, "password-url")) + if (!strcmp(cep->name, "password-url")) { config_error("%s:%d: secret::password-url is not supported yet in this UnrealIRCd version.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "secret", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "secret", cep->name); errors++; continue; } - if (cep->ce_entries) + if (cep->items) { config_error("%s:%d: secret::%s does not support sub-options (%s)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_varname, cep->ce_entries->ce_varname); + cep->file->filename, cep->line_number, + cep->name, cep->items->name); errors++; } } @@ -10629,7 +10352,7 @@ int _test_secret(ConfigFile *conf, ConfigEntry *ce) if (!has_password && !has_password_file && !has_password_prompt) { config_error("%s:%d: secret { } block must contain 1 of: password OR password-file OR password-prompt", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } @@ -10645,21 +10368,21 @@ int _conf_secret(ConfigFile *conf, ConfigEntry *ce) { ConfigEntry *cep; Secret *s; - Secret *existing = find_secret(ce->ce_vardata); + Secret *existing = find_secret(ce->value); s = safe_alloc(sizeof(Secret)); - safe_strdup(s->name, ce->ce_vardata); + safe_strdup(s->name, ce->value); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "password")) + if (!strcmp(cep->name, "password")) { - safe_strdup_sensitive(s->password, cep->ce_vardata); - destroy_string(cep->ce_vardata); /* destroy the original */ + safe_strdup_sensitive(s->password, cep->value); + destroy_string(cep->value); /* destroy the original */ } else - if (!strcmp(cep->ce_varname, "password-file")) + if (!strcmp(cep->name, "password-file")) { - if (!file_exists(cep->ce_vardata) && existing && existing->password) + if (!file_exists(cep->value) && existing && existing->password) { /* Silently ignore the case where a secret block already * has the password read and now the file is no longer available. @@ -10668,14 +10391,14 @@ int _conf_secret(ConfigFile *conf, ConfigEntry *ce) */ } else { - s->password = _conf_secret_read_password_file(cep->ce_vardata); + s->password = _conf_secret_read_password_file(cep->value); } } else - if (!strcmp(cep->ce_varname, "password-prompt")) + if (!strcmp(cep->name, "password-prompt")) { - if (!loop.ircd_booted && running_interactively()) + if (!loop.booted && running_interactively()) { - s->password = _conf_secret_read_prompt(ce->ce_vardata); + s->password = _conf_secret_read_prompt(ce->value); if (!s->password || !valid_secret_password(s->password, NULL)) { config_error("Invalid password entered on console (does not meet complexity requirements)"); @@ -10713,150 +10436,128 @@ int _conf_secret(ConfigFile *conf, ConfigEntry *ce) return 1; } -#ifdef USE_LIBCURL -static void conf_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *inc_key) +void resource_download_complete(const char *url, const char *file, const char *errorbuf, int cached, void *rs_key) { - ConfigItem_include *inc; + ConfigResource *rs = (ConfigResource *)rs_key; - if (!loop.ircd_rehashing) - return; + rs->type &= ~RESOURCE_DLQUEUED; - /* - use inc_key to find the correct include block. This - should be cheaper than using the full URL. - */ - for (inc = conf_include; inc; inc = inc->next) - { - if ( inc_key != (void *)inc ) - continue; - if (!(inc->flag.type & INCLUDE_REMOTE)) - continue; - if (inc->flag.type & INCLUDE_NOTLOADED) - continue; - if (strcasecmp(url, inc->url)) - continue; - - inc->flag.type &= ~INCLUDE_DLQUEUED; - break; - } - if (!inc) - { - ircd_log(LOG_ERROR, "Downloaded remote include which matches no include statement."); - return; - } + if (config_verbose) + config_status("resource_download_complete() for %s [%s]", url, errorbuf?errorbuf:"success"); if (!file && !cached) - update_remote_include(inc, file, 0, errorbuf); /* DOWNLOAD FAILED */ + { + /* DOWNLOAD FAILED */ + if (rs->cache_file) + { + unreal_log(ULOG_ERROR, "config", "DOWNLOAD_FAILED_SOFT", NULL, + "$file:$line_number: Failed to download '$url': $error_message\n" + "Using a cached copy instead.", + log_data_string("file", rs->wce->ce->file->filename), + log_data_integer("line_number", rs->wce->ce->line_number), + log_data_string("url", displayurl(url)), + log_data_string("error_message", errorbuf)); + safe_strdup(rs->file, rs->cache_file); + } else { + unreal_log(ULOG_ERROR, "config", "DOWNLOAD_FAILED_HARD", NULL, + "$file:$line_number: Failed to download '$url': $error_message", + log_data_string("file", rs->wce->ce->file->filename), + log_data_integer("line_number", rs->wce->ce->line_number), + log_data_string("url", displayurl(url)), + log_data_string("error_message", errorbuf)); + /* Set error condition, this so config_read_file() later will stop. */ + loop.config_load_failed = 1; + /* We keep the other transfers running since they may raise (more) errors. + * Which can be helpful so you can differentiate between an error of an + * include on one server, or complete lack of internet connectvitity. + */ + } + } else { - char *urlfile = url_getfilename(url); - char *file_basename = unreal_getfilename(urlfile); - char *tmp = unreal_mktemp(TMPDIR, file_basename ? file_basename : "download.conf"); - safe_free(urlfile); - if (cached) { - unreal_copyfileex(inc->file, tmp, 1); - unreal_copyfileex(inc->file, unreal_mkcache(url), 0); - update_remote_include(inc, tmp, 0, NULL); + /* Copy from cache */ + safe_strdup(rs->file, rs->cache_file); + } else { + /* Copy to cache */ + const char *cache_file = unreal_mkcache(url); + unreal_copyfileex(file, cache_file, 1); + safe_strdup(rs->file, cache_file); } - else + } + + if (rs->file) + { + if (rs->type & RESOURCE_INCLUDE) { - /* - copy/hardlink file to another file because our caller will - remove(file). - */ - unreal_copyfileex(file, tmp, 1); - update_remote_include(inc, tmp, 0, NULL); - unreal_copyfileex(file, unreal_mkcache(url), 0); + if (config_read_file(rs->file, (char *)displayurl(rs->url)) < 0) + loop.config_load_failed = 1; + } else { + ConfigEntryWrapper *wce; + for (wce = rs->wce; wce; wce = wce->next) + safe_strdup(wce->ce->value, rs->file); // now information of url is lost, hm!! } } - for (inc = conf_include; inc; inc = inc->next) - { - if (inc->flag.type & INCLUDE_DLQUEUED) - return; - } - rehash_internal(loop.rehash_save_client, loop.rehash_save_sig); -} -#endif -int rehash(Client *client, int sig) + /* 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. + * There is no guarantee that the request will be done immediately + * (eg: it won't in case of remote includes). + * @param client The client requesting the /REHASH. + * If this is NULL then the rehash was requested + * via a signal to the process or GUI. + */ +void request_rehash(Client *client) { -#ifdef USE_LIBCURL - ConfigItem_include *inc; - char found_remote = 0; - if (loop.ircd_rehashing) + if (loop.rehashing) { - if (!sig) + if (client) sendnotice(client, "A rehash is already in progress"); - return 0; + return; } - /* Log who or what did the rehash: */ - if (sig) - { - ircd_log(LOG_ERROR, "Rehashing configuration file (SIGHUP signal received)"); - } else - if (client && client->user) - { - ircd_log(LOG_ERROR, "Rehashing configuration file (requested by %s!%s@%s)", - client->name, client->user->username, client->user->realhost); - } else - if (client) - { - ircd_log(LOG_ERROR, "Rehashing configuration file (requested by %s)", - client->name); - } - - loop.ircd_rehashing = 1; + loop.rehashing = 1; loop.rehash_save_client = client; - loop.rehash_save_sig = sig; - for (inc = conf_include; inc; inc = inc->next) + config_read_start(); + /* If we already have everything, then can we proceed with the rehash */ + if (is_config_read_finished()) { - time_t modtime; - if (!(inc->flag.type & INCLUDE_REMOTE)) - continue; - - if (inc->flag.type & INCLUDE_NOTLOADED) - continue; - found_remote = 1; - modtime = unreal_getfilemodtime(inc->file); - inc->flag.type |= INCLUDE_DLQUEUED; - - /* - use (void *)inc as the key for finding which - include block conf_download_complete() should use. - */ - download_file_async(inc->url, modtime, conf_download_complete, (void *)inc); + rehash_internal(client); + return; } - if (!found_remote) - return rehash_internal(client, sig); - return 0; -#else - loop.ircd_rehashing = 1; - return rehash_internal(client, sig); -#endif + /* Otherwise, I/O events will take care of it later + * after all remote includes have been downloaded. + */ } -int rehash_internal(Client *client, int sig) +int rehash_internal(Client *client) { - if (sig == 1) - sendto_ops("Got signal SIGHUP, reloading %s file", configfile); - loop.ircd_rehashing = 1; /* double checking.. */ - if (init_conf(configfile, 1) == 0) - run_configuration(); - if (sig == 1) - reread_motdsandrules(); - unload_all_unused_snomasks(); + /* 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.. */ + + if (config_test() == 0) + config_run(); + /* TODO: uh.. are we supposed to do all this for a failed rehash too? maybe some but not all? */ + reread_motdsandrules(); unload_all_unused_umodes(); unload_all_unused_extcmodes(); unload_all_unused_caps(); unload_all_unused_history_backends(); // unload_all_unused_moddata(); -- this will crash - extcmodes_check_for_changes(); umodes_check_for_changes(); charsys_check_for_changes(); - loop.ircd_rehashing = 0; + loop.rehashing = 0; remote_rehash_client = NULL; return 1; } @@ -10884,8 +10585,6 @@ void link_cleanup(ConfigItem_link *link_ptr) void delete_linkblock(ConfigItem_link *link_ptr) { - Debug((DEBUG_ERROR, "delete_linkblock: deleting %s, refcount=%d", - link_ptr->servername, link_ptr->refcount)); if (link_ptr->class) { link_ptr->class->xrefcount--; @@ -10904,8 +10603,6 @@ void delete_linkblock(ConfigItem_link *link_ptr) void delete_classblock(ConfigItem_class *class_ptr) { - Debug((DEBUG_ERROR, "delete_classblock: deleting %s, clients=%d, xrefcount=%d", - class_ptr->name, class_ptr->clients, class_ptr->xrefcount)); safe_free(class_ptr->name); DelListItem(class_ptr, conf_class); safe_free(class_ptr); @@ -10924,6 +10621,7 @@ void listen_cleanup() safe_free(listen_ptr->ip); free_tls_options(listen_ptr->tls_options); DelListItem(listen_ptr, conf_listen); + safe_free(listen_ptr->websocket_forward); safe_free(listen_ptr); i++; } @@ -10933,279 +10631,153 @@ void listen_cleanup() close_unbound_listeners(); } -#ifdef USE_LIBCURL -char *find_remote_include(char *url, char **errorbuf) +ConfigResource *find_config_resource(const char *resource) { - ConfigItem_include *inc; + ConfigResource *rs; - for (inc = conf_include; inc; inc = inc->next) + for (rs = config_resources; rs; rs = rs->next) { - if (!(inc->flag.type & INCLUDE_NOTLOADED)) - continue; - if (!(inc->flag.type & INCLUDE_REMOTE)) - continue; - if (!strcasecmp(url, inc->url)) - { - *errorbuf = inc->errorbuf; - return inc->file; - } +#ifdef _WIN32 + if (rs->file && !strcasecmp(resource, rs->file)) + return rs; +#else + if (rs->file && !strcmp(resource, rs->file)) + return rs; +#endif + if (rs->url && !strcasecmp(resource, rs->url)) + return rs; } return NULL; } -char *find_loaded_remote_include(char *url) +/* Add configuration resource to list. + * For files this doesn't do terribly much, except that you can use + * the return value to judge on whether you should call config_read_file() or not. + * For urls this adds the resource to the list of links to be downloaded. + * @param resource File or URL of the resource + * @param type A RESOURCE_ type such as RESOURCE_INCLUDE + * @param ce The ConfigEntry where the add_config_resource() happened + * for, such as the include block, etc. + * @returns 0 if the file is already on our list (so no need to load it!) + */ +int add_config_resource(const char *resource, int type, ConfigEntry *ce) { - ConfigItem_include *inc; + ConfigResource *rs; + ConfigEntryWrapper *wce; - for (inc = conf_include; inc; inc = inc->next) + if (config_verbose) + config_status("add_config_resource() for '%s", resource); + + wce = safe_alloc(sizeof(ConfigEntryWrapper)); + wce->ce = ce; + + rs = find_config_resource(resource); + if (rs) { - if ((inc->flag.type & INCLUDE_NOTLOADED)) - continue; - if (!(inc->flag.type & INCLUDE_REMOTE)) - continue; - if (!strcasecmp(url, inc->url)) - return inc->file; + /* Existing entry, add us to the list of + * items who are interested in this resource ;) + */ + AddListItem(wce, rs->wce); + return 0; } - return NULL; -} + /* New entry */ + rs = safe_alloc(sizeof(ConfigResource)); + rs->wce = wce; + AddListItem(rs, config_resources); -/** - * Non-asynchronous remote inclusion to give a user better feedback - * when first starting his IRCd. - * - * The asynchronous friend is rehash() which merely queues remote - * includes for download using download_file_async(). - */ -int remote_include(ConfigEntry *ce) -{ - char *errorbuf = NULL; - char *url = ce->ce_vardata; - char *file = find_remote_include(url, &errorbuf); - int ret; - if (!loop.ircd_rehashing || (loop.ircd_rehashing && !file && !errorbuf)) + if (!url_is_valid(resource)) { - char *error; - if (config_verbose > 0) - config_status("Downloading %s", displayurl(url)); - file = download_file(url, &error); - if (!file) + safe_strdup(rs->file, resource); + } else { + const char *cache_file; + time_t modtime; + + safe_strdup(rs->url, resource); + rs->type = type|RESOURCE_REMOTE|RESOURCE_DLQUEUED; + + cache_file = unreal_mkcache(rs->url); + modtime = unreal_getfilemodtime(cache_file); + if (modtime > 0) { - if (has_cached_version(url)) + safe_strdup(rs->cache_file, cache_file); /* Cached copy is available */ + /* Check if there is an "url-refresh" argument */ + ConfigEntry *cep, *prev = NULL; + for (cep = ce->items; cep; cep = cep->next) { - config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - displayurl(url), error); - safe_strdup(file, unreal_mkcache(url)); - /* Let it pass to load_conf()... */ - } else { - config_error("%s:%i: include: error downloading '%s': %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - displayurl(url), error); - return -1; - } - } else { - unreal_copyfileex(file, unreal_mkcache(url), 0); - } - add_remote_include(file, url, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(file, url); - safe_free(file); - return ret; - } - else - { - if (errorbuf) - { - if (has_cached_version(url)) - { - config_warn("%s:%i: include: error downloading '%s': %s -- using cached version instead.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - displayurl(url), errorbuf); - /* Let it pass to load_conf()... */ - safe_strdup(file, unreal_mkcache(url)); - } else { - config_error("%s:%i: include: error downloading '%s': %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - displayurl(url), errorbuf); - return -1; - } - } - if (config_verbose > 0) - config_status("Loading %s from download", url); - add_remote_include(file, url, 0, NULL, ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - ret = load_conf(file, url); - return ret; - } - return 0; -} -#endif - -/** - * Add an item to the conf_include list for the specified file. - * - * Checks for whether or not we're performing recursive includes - * belong in conf_load() because that function is able to return an - * error code. Any checks in here will end up being ignored by callers - * and thus will gain us nothing. - * - * @param file path to the include file. - */ -void add_include(const char *file, const char *included_from, int included_from_line) -{ - ConfigItem_include *inc; - - inc = safe_alloc(sizeof(ConfigItem_include)); - safe_strdup(inc->file, file); - inc->flag.type = INCLUDE_NOTLOADED; - safe_strdup(inc->included_from, included_from); - inc->included_from_line = included_from_line; - AddListItem(inc, conf_include); -} - -#ifdef USE_LIBCURL -/** - * Adds a remote include entry to the config_include list. - * - * This is to be called whenever the included_from and - * included_from_line parameters are known. This means that during a - * rehash when downloads are done asynchronously, you call this with - * the inclued_from and included_from_line information. After the - * download is complete and you know there it is stored in the FS, - * call update_remote_include(). - */ -void add_remote_include(const char *file, const char *url, int flags, const char *errorbuf, const char *included_from, int included_from_line) -{ - ConfigItem_include *inc; - - /* we rely on safe_alloc() zeroing the ConfigItem_include */ - inc = safe_alloc(sizeof(ConfigItem_include)); - if (included_from) - { - safe_strdup(inc->included_from, included_from); - inc->included_from_line = included_from_line; - } - safe_strdup(inc->url, url); - - update_remote_include(inc, file, INCLUDE_NOTLOADED|INCLUDE_REMOTE|flags, errorbuf); - AddListItem(inc, conf_include); -} - -/** - * Update certain information in a remote include's config_include list entry. - * - * @param file the place on disk where the downloaded remote include - * may be found - * @param flags additional flags to set on the config_include entry - * @param errorbuf non-NULL if there were errors encountered in - * downloading. The error will be stored into the config_include - * entry. - */ -void update_remote_include(ConfigItem_include *inc, const char *file, int flags, const char *errorbuf) -{ - /* - * file may be NULL when errorbuf is non-NULL and vice-versa. - */ - if (file) - safe_strdup(inc->file, file); - inc->flag.type |= flags; - - if (errorbuf) - safe_strdup(inc->errorbuf, errorbuf); -} -#endif - -/** - * Clean up conf_include after a rehash fails because of a - * configuration file error. - * - * Duplicates some in unload_loaded_include(). - */ -void unload_notloaded_includes(void) -{ - ConfigItem_include *inc, *next; - - for (inc = conf_include; inc; inc = next) - { - next = inc->next; - if ((inc->flag.type & INCLUDE_NOTLOADED) || !(inc->flag.type & INCLUDE_USED)) - { -#ifdef USE_LIBCURL - if (inc->flag.type & INCLUDE_REMOTE) - { - /* Delete the file, but only if it's not a cached version */ - if (strncmp(inc->file, CACHEDIR, strlen(CACHEDIR))) + if (!strcmp(cep->name, "url-refresh")) { - remove(inc->file); + /* First find out the time value of url-refresh... (eg '7d' -> 86400*7) */ + long refresh_time = 0; + if (cep->value) + refresh_time = config_checkval(cep->value, CFG_TIME); + /* Then remove the config item so it is not seen by the rest of unrealircd. + * Can't use DelListItem() here as ConfigEntry has no ->prev, only ->next. + */ + if (prev) + prev->next = cep->next; /* (skip over us) */ + else + ce->items = cep->next; /* (new head) */ + /* ..and free it */ + config_entry_free(cep); + /* And now check if the current cached copy is recent enough */ + if (TStime() - modtime < refresh_time) + { + /* Don't download, use cached copy */ + //config_status("DEBUG: using cached copy due to url-refresh %ld", refresh_time); + resource_download_complete(rs->url, NULL, NULL, 1, rs); + return 1; + } else { + //config_status("DEBUG: requires download attempt, out of date url-refresh %ld < %ld", refresh_time, TStime() - modtime); + } + break; // MUST break now as we touched the linked list. } - safe_free(inc->url); - safe_free(inc->errorbuf); + prev = cep; } -#endif - safe_free(inc->file); - safe_free(inc->included_from); - DelListItem(inc, conf_include); - safe_free(inc); } + download_file_async(rs->url, modtime, resource_download_complete, (void *)rs, NULL, DOWNLOAD_MAX_REDIRECTS); } + return 1; } -/** - * Clean up conf_include after a successful rehash to make way for - * load_includes(). - */ -void unload_loaded_includes(void) +void free_all_config_resources(void) { - ConfigItem_include *inc, *next; + ConfigResource *rs, *next; + ConfigEntryWrapper *wce, *wce_next; - for (inc = conf_include; inc; inc = next) + for (rs = config_resources; rs; rs = next) { - next = inc->next; - if (!(inc->flag.type & INCLUDE_NOTLOADED) || !(inc->flag.type & INCLUDE_USED)) + next = rs->next; + for (wce = rs->wce; wce; wce = wce_next) { -#ifdef USE_LIBCURL - if (inc->flag.type & INCLUDE_REMOTE) - { - /* Delete the file, but only if it's not a cached version */ - if (strncmp(inc->file, CACHEDIR, strlen(CACHEDIR))) - { - remove(inc->file); - } - safe_free(inc->url); - safe_free(inc->errorbuf); - } -#endif - safe_free(inc->file); - safe_free(inc->included_from); - DelListItem(inc, conf_include); - safe_free(inc); + wce_next = wce->next; + safe_free(wce); } + rs->wce = NULL; + if (rs->type & RESOURCE_REMOTE) + { + /* Delete the file, but only if it's not a cached version */ + if (rs->file && strncmp(rs->file, CACHEDIR, strlen(CACHEDIR))) + { + remove(rs->file); + } + safe_free(rs->url); + } + safe_free(rs->file); + safe_free(rs->cache_file); + DelListItem(rs, config_resources); + safe_free(rs); } } -/** - * Mark loaded includes as loaded by removing the INCLUDE_NOTLOADED - * flag. Meant to be called only after calling - * unload_loaded_includes(). - */ -void load_includes(void) -{ - ConfigItem_include *inc; - - /* Doing this for all the includes should actually be faster - * than only doing it for includes that are not-loaded - */ - for (inc = conf_include; inc; inc = inc->next) - inc->flag.type &= ~INCLUDE_NOTLOADED; -} - int tls_tests(void) { if (have_tls_listeners == 0) { - config_error("Your server is not listening on any SSL/TLS ports."); + config_error("Your server is not listening on any TLS ports."); config_status("Add this to your unrealircd.conf: listen { ip %s; port 6697; options { tls; }; };", port_6667_ip ? port_6667_ip : "*"); - config_status("See https://www.unrealircd.org/docs/FAQ#Your_server_is_not_listening_on_any_SSL_ports"); + config_status("See https://www.unrealircd.org/docs/FAQ#no-tls-ports"); return 0; } @@ -11245,7 +10817,7 @@ int reloadable_perm_module_unloaded(void) return ret; } -char *link_generator_spkifp(TLSOptions *tlsoptions) +const char *link_generator_spkifp(TLSOptions *tlsoptions) { SSL_CTX *ctx; SSL *ssl; @@ -11267,7 +10839,7 @@ void link_generator(void) TLSOptions *tlsopt = iConf.tls_options; /* never null */ int port = 0; char *ip = NULL; - char *spkifp; + const char *spkifp; for (lstn = conf_listen; lstn; lstn = lstn->next) { @@ -11286,7 +10858,7 @@ void link_generator(void) if (!port) { - printf("You don't have any listen { } blocks that are serversonly.\n"); + printf("You don't have any listen { } blocks that are serversonly (and have tls enabled).\n"); printf("It is recommended to have at least one. Add this to your configuration file:\n"); printf("listen { ip *; port 6900; options { tls; serversonly; }; };\n"); exit(1); @@ -11295,7 +10867,7 @@ void link_generator(void) spkifp = link_generator_spkifp(tlsopt); if (!spkifp) { - printf("Could not calculate spkifp. Maybe you have uncommon SSL/TLS options set? Odd...\n"); + printf("Could not calculate spkifp. Maybe you have uncommon TLS options set? Odd...\n"); exit(1); } diff --git a/src/conf_preprocessor.c b/src/conf_preprocessor.c index 6551fcb..9be3338 100644 --- a/src/conf_preprocessor.c +++ b/src/conf_preprocessor.c @@ -19,7 +19,7 @@ static inline int ValidVarCharacter(char x) return 0; } -PreprocessorItem evaluate_preprocessor_if(char *statement, char *filename, int linenumber, ConditionalConfig **cc_out) +PreprocessorItem evaluate_preprocessor_if(char *statement, const char *filename, int linenumber, ConditionalConfig **cc_out) { char *p=statement, *name; int negative = 0; @@ -176,7 +176,7 @@ PreprocessorItem evaluate_preprocessor_if(char *statement, char *filename, int l return PREPROCESSOR_ERROR; } -PreprocessorItem evaluate_preprocessor_define(char *statement, char *filename, int linenumber) +PreprocessorItem evaluate_preprocessor_define(char *statement, const char *filename, int linenumber) { char *p = statement; char *name, *name_terminator; @@ -246,7 +246,7 @@ PreprocessorItem evaluate_preprocessor_define(char *statement, char *filename, return PREPROCESSOR_DEFINE; } -PreprocessorItem parse_preprocessor_item(char *start, char *end, char *filename, int linenumber, ConditionalConfig **cc) +PreprocessorItem parse_preprocessor_item(char *start, char *end, const char *filename, int linenumber, ConditionalConfig **cc) { char buf[512]; int max; @@ -262,7 +262,7 @@ PreprocessorItem parse_preprocessor_item(char *start, char *end, char *filename return evaluate_preprocessor_define(buf+7, filename, linenumber); else if (!strncmp(buf, "@if ", 4)) return evaluate_preprocessor_if(buf+4, filename, linenumber, cc); - else if (!strcmp(buf, "@endif")) + else if (!strncmp(buf, "@endif", 6)) return PREPROCESSOR_ENDIF; config_error("%s:%i: Unknown preprocessor directive: %s", filename, linenumber, buf); @@ -382,29 +382,29 @@ int preprocessor_resolve_if(ConditionalConfig *cc, PreprocessorPhase phase) void preprocessor_resolve_conditionals_ce(ConfigEntry **ce_list, PreprocessorPhase phase) { - ConfigEntry *ce, *ce_next, *ce_prev; + ConfigEntry *ce, *next, *ce_prev; ConfigEntry *cep, *cep_next, *cep_prev; ce_prev = NULL; - for (ce = *ce_list; ce; ce = ce_next) + for (ce = *ce_list; ce; ce = next) { - ce_next = ce->ce_next; + next = ce->next; /* This is for an @if before a block start */ - if (!preprocessor_resolve_if(ce->ce_cond, phase)) + if (!preprocessor_resolve_if(ce->conditional_config, phase)) { /* Delete this entry */ if (ce == *ce_list) { /* we are head, so new head */ - *ce_list = ce->ce_next; /* can be NULL now */ + *ce_list = ce->next; /* can be NULL now */ } else { /* non-head */ - ce_prev->ce_next = ce->ce_next; /* can be NULL now */ + ce_prev->next = ce->next; /* can be NULL now */ } config_entry_free(ce); continue; } - preprocessor_resolve_conditionals_ce(&ce->ce_entries, phase); + preprocessor_resolve_conditionals_ce(&ce->items, phase); ce_prev = ce; } } @@ -413,8 +413,8 @@ void preprocessor_resolve_conditionals_all(PreprocessorPhase phase) { ConfigFile *cfptr; - for (cfptr = conf; cfptr; cfptr = cfptr->cf_next) - preprocessor_resolve_conditionals_ce(&cfptr->cf_entries, phase); + for (cfptr = conf; cfptr; cfptr = cfptr->next) + preprocessor_resolve_conditionals_ce(&cfptr->items, phase); } /** Frees the list of config_defines, so all @defines */ @@ -502,7 +502,7 @@ void preprocessor_replace_defines(char **item, ConfigEntry *ce) if ((limit > 2) && ((*varend == '\0') || strchr("\t ,.", *varend))) { config_warn("%s:%d: Variable %s used here but there's no @define for it earlier.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, varname); + ce->file->filename, ce->line_number, varname); } #endif value = varname; /* not found? then use varname, including the '$' */ diff --git a/src/crashreport.c b/src/crashreport.c index bd7e83c..270c363 100644 --- a/src/crashreport.c +++ b/src/crashreport.c @@ -109,7 +109,7 @@ char *find_best_asan_log(void) } } closedir(fd); - return BadPtr(best_fname) ? NULL : best_fname; + return *best_fname ? best_fname : NULL; #else return NULL; #endif @@ -531,7 +531,7 @@ char *generate_crash_report(char *coredump, int *thirdpartymods) #define CRASH_REPORT_HOST "crash.unrealircd.org" -SSL_CTX *crashreport_init_ssl(void) +SSL_CTX *crashreport_init_tls(void) { SSL_CTX *ctx_client; char buf[512]; @@ -587,7 +587,7 @@ int crashreport_send(char *fname) delimiter); snprintf(footer, sizeof(footer), "\r\n--%s--\r\n", delimiter); - ctx_client = crashreport_init_ssl(); + ctx_client = crashreport_init_tls(); if (!ctx_client) { printf("ERROR: TLS initalization failure (I)\n"); diff --git a/src/crule.c b/src/crule.c index faa9e6a..4baf20e 100644 --- a/src/crule.c +++ b/src/crule.c @@ -186,7 +186,7 @@ int crule_via(int numargs, void *crulearg[]) { if (!match_simple((char *)crulearg[1], client->name)) continue; - if (!match_simple((char *)crulearg[0], client->serv->up)) + if (!match_simple((char *)crulearg[0], client->uplink->name)) continue; return (1); } @@ -372,11 +372,6 @@ char *crule_parse(char *rule) } if (ruleroot != NULL) crule_free((char **)&ruleroot); -#if !defined(CR_DEBUG) && !defined(CR_CHKCONF) - Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule)); -#else - (void)fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule); -#endif return NULL; } diff --git a/src/dbuf.c b/src/dbuf.c index 62cb40f..23786d5 100644 --- a/src/dbuf.c +++ b/src/dbuf.c @@ -61,7 +61,7 @@ void dbuf_queue_init(dbuf *dyn) INIT_LIST_HEAD(&dyn->dbuf_list); } -void dbuf_put(dbuf *dyn, char *buf, size_t length) +void dbuf_put(dbuf *dyn, const char *buf, size_t length) { struct dbufbuf *block; size_t amount; diff --git a/src/debug.c b/src/debug.c index e28f898..e4d7635 100644 --- a/src/debug.c +++ b/src/debug.c @@ -48,9 +48,6 @@ MODVAR char serveropts[] = { 'Y', #endif '6', -#ifdef USE_SSL - 'e', -#endif #ifndef NO_OPEROVERRIDE 'O', #endif @@ -150,11 +147,16 @@ void debug(int level, FORMAT_STRING(const char *form), ...) SET_ERRNO(err); } -int checkprotoflags(Client *client, int flags, char *file, int line) +int checkprotoflags(Client *client, int flags, const char *file, int line) { if (!MyConnect(client)) - ircd_log(LOG_ERROR, "[Debug] [BUG] ERROR: %s:%d: IsToken(<%s>,%d) on remote client", - file, line, client->name, flags); + { + unreal_log(ULOG_ERROR, "main", "BUG_ISTOKEN_REMOTE_CLIENT", client, + "IsToken($token_value) used on remote client in $file:$line", + log_data_integer("token_value", flags), + log_data_string("file", file), + log_data_integer("line", line)); + } return ((client->local->proto & flags) == flags) ? 1 : 0; } #endif diff --git a/src/dispatch.c b/src/dispatch.c index 37540f3..096243c 100644 --- a/src/dispatch.c +++ b/src/dispatch.c @@ -39,6 +39,11 @@ #include #endif +/* Not sure if this is suitable for production, + * but let's turn it on for U6 development. + */ +//#define DETECT_HIGH_CPU + /*************************************************************************************** * Backend-independent functions. fd_setselect() and friends * ***************************************************************************************/ @@ -47,14 +52,18 @@ void fd_setselect(int fd, int flags, IOCallbackFunc iocb, void *data) FDEntry *fde; int changed = 0; #if 0 - ircd_log(LOG_ERROR, "fd_setselect(): fd %d flags %d func %p", fd, flags, &iocb); + unreal_log(ULOG_DEBUG, "io", "IO_DEBUG_FD_SETSELECT", NULL, + "fd_setselect(): fd $fd flags $fd_flags function $function_pointer", + log_data_integer("fd", fd), + log_data_integer("fd_flags", flags), + log_data_integer("function_pointer", (long long)iocb)); #endif if ((fd < 0) || (fd >= MAXCONNECTIONS)) { - sendto_realops("[BUG] trying to modify fd #%d in fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); - ircd_log(LOG_ERROR, "[BUG] trying to modify fd #%d in fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); + unreal_log(ULOG_ERROR, "io", "BUG_FD_SETSELECT_OUT_OF_RANGE", NULL, + "[BUG] trying to modify fd $fd in fd table, but MAXCONNECTIONS is $maxconnections", + log_data_integer("fd", fd), + log_data_integer("maxconnections", MAXCONNECTIONS)); #ifdef DEBUGMODE abort(); #endif @@ -143,7 +152,11 @@ void fd_debug(fd_set *f, int highest, char *name) //if (fcntl(i, F_GETFL) < 0) int nonb = 1; if (ioctlsocket(i, FIONBIO, &nonb) < 0) - ircd_log(LOG_ERROR, "fd_debug: FD #%d is invalid!!!", i); + { + unreal_log(ULOG_ERROR, "io", "FD_DEBUG", NULL, + "[BUG] fd_debug: fd $fd is invalid!!!", + log_data_integer("fd", i)); + } } } } @@ -168,10 +181,6 @@ void fd_select(time_t delay) to.tv_sec = delay / 1000; to.tv_usec = (delay % 1000) * 1000; -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "fd_select() on 0-%d...", highest_fd+1); -#endif - #ifdef _WIN32 num = select(highest_fd + 1, &work_read_fds, &work_write_fds, &work_except_fds, &to); #else @@ -179,8 +188,9 @@ void fd_select(time_t delay) #endif if (num < 0) { - extern void report_baderror(char *text, Client *client); - report_baderror("select %s:%s", &me); + unreal_log(ULOG_FATAL, "io", "SELECT_ERROR", NULL, + "select() returned error ($socket_error) -- SERIOUS TROUBLE!", + log_data_socket_error(-1)); /* DEBUG the actual problem: */ memcpy(&work_read_fds, &read_fds, sizeof(fd_set)); memcpy(&work_write_fds, &write_fds, sizeof(fd_set)); @@ -204,10 +214,6 @@ void fd_select(time_t delay) if (!fde->is_open) continue; -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "fd_select(): checking %d...", fd); -#endif - if (FD_ISSET(fd, &work_read_fds)) evflags |= FD_SELECT_READ; @@ -223,10 +229,6 @@ void fd_select(time_t delay) if (!evflags) continue; -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "fd_select(): events for %d (%d)... processing...", fd, evflags); -#endif - if (evflags & FD_SELECT_READ) { iocb = fde->read_callback; @@ -280,7 +282,9 @@ void fd_fork() continue; #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[BUG?] kevent returned %d", errno); + unreal_log(ULOG_ERROR, "io", "KEVENT_FAILED", NULL, + "[io] fd_fork(): kevent returned error: $system_error", + log_data_string("system_error", strerror(errno))); #endif } } @@ -308,8 +312,13 @@ void fd_refresh(int fd) #ifdef DEBUGMODE if (ERRNO != P_EWOULDBLOCK && ERRNO != P_EAGAIN) { - ircd_log(LOG_ERROR, "[BUG?] fd_refresh(): kevent returned %d for fd %d for read callback (%s)", - errno, fd, (fde->read_callback ? "add" : "delete")); + int save_err = errno; + unreal_log(ULOG_ERROR, "io", "KEVENT_FAILED_REFRESH", NULL, + "fd_refresh(): kevent returned error for fd $fd ($fd_action) ($callback): $system_error", + log_data_string("system_error", strerror(save_err)), + log_data_integer("fd", fd), + log_data_string("fd_action", (fde->read_callback ? "add" : "delete")), + log_data_string("callback", "read_callback")); } #endif } @@ -323,8 +332,13 @@ void fd_refresh(int fd) #ifdef DEBUGMODE if (ERRNO != P_EWOULDBLOCK && ERRNO != P_EAGAIN && fde->write_callback) { - ircd_log(LOG_ERROR, "[BUG?] fd_refresh(): kevent returned %d for fd %d for write callback (%s)", - errno, fd, "add" /*(fde->write_callback ? "add" : "delete")*/); + int save_err = errno; + unreal_log(ULOG_ERROR, "io", "KEVENT_FAILED_REFRESH", NULL, + "[io] fd_refresh(): kevent returned error for fd $fd ($fd_action) ($callback): $system_error", + log_data_string("system_error", strerror(save_err)), + log_data_integer("fd", fd), + log_data_string("fd_action", "add"), + log_data_string("callback", "write_callback")); } #endif } @@ -440,11 +454,15 @@ void fd_refresh(int fd) if (epoll_ctl(epoll_fd, op, fd, &ep_event) != 0) { - if (ERRNO == P_EWOULDBLOCK || ERRNO == P_EAGAIN) + int save_errno = errno; + if ((save_errno == P_EWOULDBLOCK) || (save_errno == P_EAGAIN)) return; - ircd_log(LOG_ERROR, "[BUG] fd_refresh(): epoll_ctl returned error %d (%s) for fd %d (%s)", - errno, STRERROR(ERRNO), fd, fde->desc); + unreal_log(ULOG_ERROR, "io", "EPOLL_CTL_FAILED", NULL, + "[io] fd_refresh(): epoll_ctl returned error for fd $fd ($fd_description): $system_error", + log_data_string("system_error", strerror(save_errno)), + log_data_integer("fd", fd), + log_data_string("fd_description", fde->desc)); return; } @@ -455,7 +473,7 @@ void fd_select(time_t delay) { int num, p, revents, fd; struct epoll_event *epfd; -#ifdef DEBUG_IOENGINE +#ifdef DETECT_HIGH_CPU int read_callbacks = 0, write_callbacks = 0; struct timeval oldt, t; long long tdiff; @@ -467,7 +485,7 @@ void fd_select(time_t delay) if (num <= 0) return; -#ifdef DEBUG_IOENGINE +#ifdef DETECT_HIGH_CPU gettimeofday(&oldt, NULL); #endif @@ -499,7 +517,7 @@ void fd_select(time_t delay) if (iocb != NULL) iocb(fd, evflags, fde->data); -#ifdef DEBUG_IOENGINE +#ifdef DETECT_HIGH_CPU read_callbacks++; #endif } @@ -511,7 +529,7 @@ void fd_select(time_t delay) if (iocb != NULL) iocb(fd, evflags, fde->data); -#ifdef DEBUG_IOENGINE +#ifdef DETECT_HIGH_CPU write_callbacks++; #endif } @@ -524,14 +542,18 @@ void fd_select(time_t delay) #endif } -#ifdef DEBUG_IOENGINE +#ifdef DETECT_HIGH_CPU gettimeofday(&t, NULL); tdiff = ((t.tv_sec - oldt.tv_sec) * 1000000) + (t.tv_usec - oldt.tv_usec); if (tdiff > 1000000) { - sendto_realops_and_log("WARNING: Slow I/O engine or high load: fd_select() took %lld ms! read_callbacks=%d, write_callbacks=%d", - tdiff / 1000, read_callbacks, write_callbacks); + unreal_log(ULOG_WARNING, "io", "HIGH_LOAD", NULL, + "HIGH CPU LOAD! fd_select() took $time_msec msec " + "(read: $num_read_callbacks, write: $num_write_callbacks)", + log_data_integer("time_msec", tdiff/1000), + log_data_integer("num_read_callbacks", read_callbacks), + log_data_integer("num_write_callbacks", write_callbacks)); } #endif } @@ -601,7 +623,7 @@ void fd_select(time_t delay) pfd = &pollfds[p]; revents = pfd->revents; - fd = pfd->local->fd; + fd = pfd->fd; if (revents == 0 || fd == -1) continue; diff --git a/src/dns.c b/src/dns.c index edaa674..d8cb660 100644 --- a/src/dns.c +++ b/src/dns.c @@ -40,9 +40,9 @@ void unrealdns_cb_nametoip_verify(void *arg, int status, int timeouts, struct ho void unrealdns_cb_nametoip_link(void *arg, int status, int timeouts, struct hostent *he); void unrealdns_delasyncconnects(void); static uint64_t unrealdns_hash_ip(const char *ip); -static void unrealdns_addtocache(char *name, char *ip); -static char *unrealdns_findcache_ip(char *ip); -struct hostent *unreal_create_hostent(char *name, char *ip); +static void unrealdns_addtocache(const char *name, const char *ip); +static const char *unrealdns_findcache_ip(const char *ip); +struct hostent *unreal_create_hostent(const char *name, const char *ip); static void unrealdns_freeandremovereq(DNSReq *r); void unrealdns_removecacherecord(DNSCache *c); @@ -87,10 +87,7 @@ static void unrealdns_sock_state_cb(void *data, ares_socket_t fd, int read, int if (!read && !write) { - /* Socket is going to be closed *BY C-ARES*.. - * so don't call fd_close() but fd_unmap(). - */ - fd_unmap(fd); + fd_close(fd); return; } @@ -108,7 +105,11 @@ static void unrealdns_sock_state_cb(void *data, ares_socket_t fd, int read, int */ static int unrealdns_sock_create_cb(ares_socket_t fd, int type, void *data) { - fd_open(fd, "DNS Resolver Socket"); + /* NOTE: We use FDCLOSE_NONE here because c-ares + * will take care of the closing. So *WE* must + * never close the socket. + */ + fd_open(fd, "DNS Resolver Socket", FDCLOSE_NONE); return ARES_SUCCESS; } @@ -174,12 +175,10 @@ void reinit_resolver(Client *client) { EventDel(unrealdns_timeout_hdl); - sendto_ops_and_log("%s requested reinitalization of resolver!", client->name); - sendto_realops("Destroying resolver channel, along with all currently pending queries..."); + unreal_log(ULOG_INFO, "dns", "REINIT_RESOLVER", client, + "$client requested reinitalization of the DNS resolver"); ares_destroy(resolver_channel); - sendto_realops("Initializing resolver again..."); init_resolver(0); - sendto_realops("Reinitalization finished successfully."); } void unrealdns_addreqtolist(DNSReq *r) @@ -202,7 +201,7 @@ void unrealdns_addreqtolist(DNSReq *r) struct hostent *unrealdns_doclient(Client *client) { DNSReq *r; - char *cache_name; + const char *cache_name; cache_name = unrealdns_findcache_ip(client->ip); if (cache_name) @@ -233,7 +232,7 @@ struct hostent *unrealdns_doclient(Client *client) /** Resolve a name to an IP, for a link block. */ -void unrealdns_gethostbyname_link(char *name, ConfigItem_link *conf, int ipv4_only) +void unrealdns_gethostbyname_link(const char *name, ConfigItem_link *conf, int ipv4_only) { DNSReq *r; @@ -283,27 +282,6 @@ void unrealdns_cb_iptoname(void *arg, int status, int timeouts, struct hostent * ares_gethostbyname(resolver_channel, he->h_name, ipv6 ? AF_INET6 : AF_INET, unrealdns_cb_nametoip_verify, newr); } -/* - returns: - 1 = good hostname - 0 = bad hostname - */ -int verify_hostname(char *name) -{ -char *p; - - if (strlen(name) > HOSTLEN) - return 0; - - /* No underscores or other illegal characters */ - for (p = name; *p; p++) - if (!isalnum(*p) && !strchr(".-", *p)) - return 0; - - return 1; -} - - void unrealdns_cb_nametoip_verify(void *arg, int status, int timeouts, struct hostent *he) { DNSReq *r = (DNSReq *)arg; @@ -348,13 +326,16 @@ void unrealdns_cb_nametoip_verify(void *arg, int status, int timeouts, struct ho goto bad; } - if (!verify_hostname(r->name)) + if (!valid_host(r->name, 1)) { /* Hostname is bad, don't cache and consider unresolved */ proceed_normal_client_handshake(client, NULL); goto bad; } + /* Get rid of stupid uppercase DNS names... */ + strtolower(r->name); + /* Entry was found, verified, and can be added to cache */ unrealdns_addtocache(r->name, client->ip); @@ -372,7 +353,7 @@ void unrealdns_cb_nametoip_link(void *arg, int status, int timeouts, struct host int n; struct hostent *he2; char ipbuf[HOSTLEN+1]; - char *ip = NULL; + const char *ip = NULL; if (!r->linkblock) { @@ -393,8 +374,9 @@ void unrealdns_cb_nametoip_link(void *arg, int status, int timeouts, struct host } /* fatal error while resolving */ - sendto_ops_and_log("Unable to resolve hostname '%s', when trying to connect to server %s.", - r->name, r->linkblock->servername); + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_RESOLVING", NULL, + "Unable to resolve hostname $link_block.hostname, when trying to connect to server $link_block.", + log_data_link_block(r->linkblock)); r->linkblock->refcount--; unrealdns_freeandremovereq(r); return; @@ -405,8 +387,9 @@ void unrealdns_cb_nametoip_link(void *arg, int status, int timeouts, struct host !(ip = inetntop(r->ipv6 ? AF_INET6 : AF_INET, he->h_addr_list[0], ipbuf, sizeof(ipbuf)))) { /* Illegal response -- fatal */ - sendto_ops_and_log("Unable to resolve hostname '%s', when trying to connect to server %s.", - r->name, r->linkblock->servername); + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_RESOLVING", NULL, + "Unable to resolve hostname $link_block.hostname, when trying to connect to server $link_block.", + log_data_link_block(r->linkblock)); unrealdns_freeandremovereq(r); return; } @@ -417,22 +400,9 @@ void unrealdns_cb_nametoip_link(void *arg, int status, int timeouts, struct host safe_strdup(r->linkblock->connect_ip, ip); he2 = unreal_create_hostent(he->h_name, ip); - switch ((n = connect_server(r->linkblock, r->client, he2))) - { - case 0: - sendto_ops_and_log("Trying to activate link with server %s[%s]...", r->linkblock->servername, ip); - break; - case -1: - sendto_ops_and_log("Couldn't connect to server %s[%s].", r->linkblock->servername, ip); - break; - case -2: - /* Should not happen since he is not NULL */ - sendto_ops_and_log("Hostname %s is unknown for server %s (!?).", r->linkblock->outgoing.hostname, r->linkblock->servername); - break; - default: - sendto_ops_and_log("Connection to server %s failed: %s", r->linkblock->servername, STRERROR(n)); - } - + /* Try to connect to the server */ + connect_server(r->linkblock, r->client, he2); + unrealdns_freeandremovereq(r); /* DONE */ } @@ -442,7 +412,7 @@ static uint64_t unrealdns_hash_ip(const char *ip) return siphash(ip, siphashkey_dns_ip) % DNS_HASH_SIZE; } -static void unrealdns_addtocache(char *name, char *ip) +static void unrealdns_addtocache(const char *name, const char *ip) { unsigned int hashv; DNSCache *c; @@ -494,7 +464,7 @@ static void unrealdns_addtocache(char *name, char *ip) /** Search the cache for a confirmed ip->name and name->ip match, by address. * @returns The resolved hostname, or NULL if not found in cache. */ -static char *unrealdns_findcache_ip(char *ip) +static const char *unrealdns_findcache_ip(const char *ip) { unsigned int hashv; DNSCache *c; @@ -562,17 +532,11 @@ DNSCache *c, *next; { next = c->next; if (c->expires < TStime()) - { -#if 0 - sendto_realops(client, "[Syzop/DNS] Expire: %s [%s] (%ld < %ld)", - c->name, c->ip, c->expires, TStime()); -#endif unrealdns_removecacherecord(c); - } } } -struct hostent *unreal_create_hostent(char *name, char *ip) +struct hostent *unreal_create_hostent(const char *name, const char *ip) { struct hostent *he; @@ -646,7 +610,7 @@ CMD_FUNC(cmd_dns) { DNSCache *c; DNSReq *r; - char *param; + const char *param; if (!ValidatePermissionsForPath("server:dns",client,NULL,NULL,NULL)) { @@ -673,8 +637,8 @@ CMD_FUNC(cmd_dns) } else if (*param == 'c') /* CLEAR CACHE */ { - sendto_realops("%s (%s@%s) cleared the DNS cache list (/QUOTE DNS c)", - client->name, client->user->username, client->user->realhost); + unreal_log(ULOG_INFO, "dns", "DNS_CACHE_CLEARED", client, + "DNS cache cleared by $client"); while (cache_list) { @@ -702,7 +666,8 @@ CMD_FUNC(cmd_dns) ares_get_servers(resolver_channel, &serverlist); for (ns = serverlist; ns; ns = ns->next) { - char ipbuf[128], *ip; + char ipbuf[128]; + const char *ip; i++; ip = inetntop(ns->family, &ns->addr, ipbuf, sizeof(ipbuf)); diff --git a/src/fdlist.c b/src/fdlist.c index 0d9dc98..ae57aee 100644 --- a/src/fdlist.c +++ b/src/fdlist.c @@ -24,16 +24,23 @@ */ FDEntry fd_table[MAXCONNECTIONS + 1]; -int fd_open(int fd, const char *desc) +/** Notify I/O engine that a file descriptor opened. + * @param fd The file descriptor + * @param desc Description for in the fd table + * @param close_method Tell what a subsequent call to fd_close() should do, + * eg close the socket, file or don't close anything. + * @returns The file descriptor 'fd' or -1 in case of fatal error. + */ +int fd_open(int fd, const char *desc, FDCloseMethod close_method) { FDEntry *fde; if ((fd < 0) || (fd >= MAXCONNECTIONS)) { - sendto_realops("[BUG] trying to add fd #%d to fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); - ircd_log(LOG_ERROR, "[BUG] trying to add fd #%d to fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); + unreal_log(ULOG_ERROR, "io", "BUG_FD_OPEN_OUT_OF_RANGE", NULL, + "[BUG] trying to add fd $fd to fd table, but MAXCONNECTIONS is $maxconnections", + log_data_integer("fd", fd), + log_data_integer("maxconnections", MAXCONNECTIONS)); #ifdef DEBUGMODE abort(); #endif @@ -46,6 +53,7 @@ int fd_open(int fd, const char *desc) fde->fd = fd; fde->is_open = 1; fde->backend_flags = 0; + fde->close_method = close_method; strlcpy(fde->desc, desc, FD_DESC_SZ); return fde->fd; @@ -71,20 +79,28 @@ int fd_fileopen(const char *path, unsigned int flags) snprintf(comment, sizeof comment, "File: %s", unreal_getfilename(pathbuf)); - return fd_open(fd, comment); + return fd_open(fd, comment, FDCLOSE_FILE); } -int fd_unmap(int fd) +/** Internal function to unmap and optionally close the fd. + */ +/** Remove file descriptor from our table and possibly close the fd. + * The fd is closed (or not) according to the method specified in fd_open(). + * @param fd The file descriptor + * @returns 1 on success, 0 on failure + */ +int fd_close(int fd) { FDEntry *fde; unsigned int befl; + FDCloseMethod close_method; if ((fd < 0) || (fd >= MAXCONNECTIONS)) { - sendto_realops("[BUG] trying to close fd #%d in fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); - ircd_log(LOG_ERROR, "[BUG] trying to close fd #%d in fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); + unreal_log(ULOG_ERROR, "io", "BUG_FD_CLOSE_OUT_OF_RANGE", NULL, + "[BUG] trying to close fd $fd to fd table, but MAXCONNECTIONS is $maxconnections", + log_data_integer("fd", fd), + log_data_integer("maxconnections", MAXCONNECTIONS)); #ifdef DEBUGMODE abort(); #endif @@ -94,10 +110,9 @@ int fd_unmap(int fd) fde = &fd_table[fd]; if (!fde->is_open) { - sendto_realops("[BUG] trying to close fd #%d in fd table, but this FD isn't reported open", - fd); - ircd_log(LOG_ERROR, "[BUG] trying to close fd #%d in fd table, but this FD isn't reported open", - fd); + unreal_log(ULOG_ERROR, "io", "BUG_FD_CLOSE_NOT_OPEN", NULL, + "[BUG] trying to close fd $fd to fd table, but FD is (already) closed", + log_data_integer("fd", fd)); #ifdef DEBUGMODE abort(); #endif @@ -105,6 +120,7 @@ int fd_unmap(int fd) } befl = fde->backend_flags; + close_method = fde->close_method; memset(fde, 0, sizeof(FDEntry)); fde->fd = fd; @@ -112,25 +128,29 @@ int fd_unmap(int fd) /* only notify the backend if it is actively tracking the FD */ if (befl) fd_refresh(fd); - + + /* Finally, close the file or socket if requested to do so */ + switch (close_method) + { + case FDCLOSE_SOCKET: + CLOSE_SOCK(fd); + break; + case FDCLOSE_FILE: + close(fd); + break; + case FDCLOSE_NONE: + default: + break; + } + return 1; } -void fd_close(int fd) -{ - if (!fd_unmap(fd)) - return; - - CLOSE_SOCK(fd); -} - /* Deregister I/O notification for this file descriptor */ void fd_unnotify(int fd) { -FDEntry *fde; -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "fd_unnotify(): fd=%d", fd); -#endif + FDEntry *fde; + if ((fd < 0) || (fd >= MAXCONNECTIONS)) return; @@ -150,7 +170,7 @@ int fd_socket(int family, int type, int protocol, const char *desc) if (fd < 0) return -1; - return fd_open(fd, desc); + return fd_open(fd, desc, FDCLOSE_SOCKET); } int fd_accept(int sockfd) @@ -162,7 +182,7 @@ int fd_accept(int sockfd) if (fd < 0) return -1; - return fd_open(fd, buf); + return fd_open(fd, buf, FDCLOSE_SOCKET); } void fd_desc(int fd, const char *desc) @@ -171,10 +191,10 @@ void fd_desc(int fd, const char *desc) if ((fd < 0) || (fd >= MAXCONNECTIONS)) { - sendto_realops("[BUG] trying to modify fd #%d in fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); - ircd_log(LOG_ERROR, "[BUG] trying to modify fd #%d in fd table, but MAXCONNECTIONS is %d", - fd, MAXCONNECTIONS); + unreal_log(ULOG_ERROR, "io", "BUG_FD_DESC_OUT_OF_RANGE", NULL, + "[BUG] trying to fd_desc fd $fd in fd table, but MAXCONNECTIONS is $maxconnections", + log_data_integer("fd", fd), + log_data_integer("maxconnections", MAXCONNECTIONS)); #ifdef DEBUGMODE abort(); #endif @@ -184,10 +204,9 @@ void fd_desc(int fd, const char *desc) fde = &fd_table[fd]; if (!fde->is_open) { - sendto_realops("[BUG] trying to modify fd #%d in fd table, but this FD isn't reported open", - fd); - ircd_log(LOG_ERROR, "[BUG] trying to modify fd #%d in fd table, but this FD isn't reported open", - fd); + unreal_log(ULOG_ERROR, "io", "BUG_FD_DESC_NOT_OPEN", NULL, + "[BUG] trying to fd_desc fd $fd in fd table, but FD is (already) closed", + log_data_integer("fd", fd)); #ifdef DEBUGMODE abort(); #endif diff --git a/src/hash.c b/src/hash.c index e5dd203..0901af9 100644 --- a/src/hash.c +++ b/src/hash.c @@ -260,11 +260,9 @@ void siphash_generate_key(char *k) static struct list_head clientTable[NICK_HASH_TABLE_SIZE]; static struct list_head idTable[NICK_HASH_TABLE_SIZE]; static Channel *channelTable[CHAN_HASH_TABLE_SIZE]; -static Watch *watchTable[WATCH_HASH_TABLE_SIZE]; static char siphashkey_nick[SIPHASH_KEY_LENGTH]; static char siphashkey_chan[SIPHASH_KEY_LENGTH]; -static char siphashkey_watch[SIPHASH_KEY_LENGTH]; static char siphashkey_whowas[SIPHASH_KEY_LENGTH]; static char siphashkey_throttling[SIPHASH_KEY_LENGTH]; @@ -277,7 +275,6 @@ void init_hash(void) siphash_generate_key(siphashkey_nick); siphash_generate_key(siphashkey_chan); - siphash_generate_key(siphashkey_watch); siphash_generate_key(siphashkey_whowas); siphash_generate_key(siphashkey_throttling); @@ -288,7 +285,6 @@ void init_hash(void) INIT_LIST_HEAD(&idTable[i]); memset(channelTable, 0, sizeof(channelTable)); - memset(watchTable, 0, sizeof(watchTable)); memset(ThrottlingHash, 0, sizeof(ThrottlingHash)); /* do not call init_throttling() here, as @@ -310,11 +306,6 @@ uint64_t hash_channel_name(const char *name) return siphash_nocase(name, siphashkey_chan) % CHAN_HASH_TABLE_SIZE; } -uint64_t hash_watch_nick_name(const char *name) -{ - return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE; -} - uint64_t hash_whowas_name(const char *name) { return siphash_nocase(name, siphashkey_whowas) % WHOWAS_HASH_TABLE_SIZE; @@ -323,7 +314,7 @@ uint64_t hash_whowas_name(const char *name) /* * add_to_client_hash_table */ -int add_to_client_hash_table(char *name, Client *client) +int add_to_client_hash_table(const char *name, Client *client) { unsigned int hashv; /* @@ -349,7 +340,7 @@ int add_to_client_hash_table(char *name, Client *client) /* * add_to_client_hash_table */ -int add_to_id_hash_table(char *name, Client *client) +int add_to_id_hash_table(const char *name, Client *client) { unsigned int hashv; hashv = hash_client_name(name); @@ -360,7 +351,7 @@ int add_to_id_hash_table(char *name, Client *client) /* * add_to_channel_hash_table */ -int add_to_channel_hash_table(char *name, Channel *channel) +int add_to_channel_hash_table(const char *name, Channel *channel) { unsigned int hashv; @@ -372,7 +363,7 @@ int add_to_channel_hash_table(char *name, Channel *channel) /* * del_from_client_hash_table */ -int del_from_client_hash_table(char *name, Client *client) +int del_from_client_hash_table(const char *name, Client *client) { if (!list_empty(&client->client_hash)) list_del(&client->client_hash); @@ -382,7 +373,7 @@ int del_from_client_hash_table(char *name, Client *client) return 0; } -int del_from_id_hash_table(char *name, Client *client) +int del_from_id_hash_table(const char *name, Client *client) { if (!list_empty(&client->id_hash)) list_del(&client->id_hash); @@ -395,7 +386,7 @@ int del_from_id_hash_table(char *name, Client *client) /* * del_from_channel_hash_table */ -void del_from_channel_hash_table(char *name, Channel *channel) +void del_from_channel_hash_table(const char *name, Channel *channel) { Channel *tmp, *prev = NULL; unsigned int hashv; @@ -465,7 +456,7 @@ Client *hash_find_nickatserver(const char *str, Client *def) if (serv) *serv++ = '\0'; - client = find_person(nick, NULL); + client = find_user(nick, NULL); if (!client) return NULL; /* client not found */ @@ -509,14 +500,14 @@ Client *hash_find_server(const char *server, Client *def) /** Find a client by name. * This searches in the list of all types of clients, user/person, servers or an unregistered clients. - * If you know what type of client to search for, then use find_server() or find_person() instead! + * If you know what type of client to search for, then use find_server() or find_user() instead! * @param name The name to search for (eg: "nick" or "irc.example.net") * @param requester The client that is searching for this name * @note If 'requester' is a server or NULL, then we also check * the ID table, otherwise not. * @returns If the client is found then the Client is returned, otherwise NULL. */ -Client *find_client(char *name, Client *requester) +Client *find_client(const char *name, Client *requester) { if (requester == NULL || IsServer(requester)) { @@ -537,7 +528,7 @@ Client *find_client(char *name, Client *requester) * the ID table, otherwise not. * @returns If the server is found then the Client is returned, otherwise NULL. */ -Client *find_server(char *name, Client *requester) +Client *find_server(const char *name, Client *requester) { if (name) { @@ -550,14 +541,14 @@ Client *find_server(char *name, Client *requester) return NULL; } -/** Find a person (a user). +/** Find a user (a person) * @param name The name to search for (eg: "nick" or "001ABCDEFG") * @param requester The client that is searching for this name * @note If 'requester' is a server or NULL, then we also check * the ID table, otherwise not. * @returns If the user is found then the Client is returned, otherwise NULL. */ -Client *find_person(char *name, Client *requester) /* TODO: this should have been called find_user() to be consistent */ +Client *find_user(const char *name, Client *requester) { Client *c2ptr; @@ -572,22 +563,20 @@ Client *find_person(char *name, Client *requester) /* TODO: this should have bee /** Find a channel by name. * @param name The channel name to search for - * @param default_result If the channel is not found, this value is returned. - * @returns If the channel exists then the Channel is returned, otherwise default_result is returned. + * @returns If the channel exists then the Channel is returned, otherwise NULL. */ -Channel *find_channel(char *name, Channel *default_result) +Channel *find_channel(const char *name) { unsigned int hashv; - Channel *tmp; + Channel *channel; hashv = hash_channel_name(name); - for (tmp = channelTable[hashv]; tmp; tmp = tmp->hnextch) - { - if (smycmp(name, tmp->chname) == 0) - return tmp; - } - return default_result; + for (channel = channelTable[hashv]; channel; channel = channel->hnextch) + if (smycmp(name, channel->name) == 0) + return channel; + + return NULL; } /** @} */ @@ -599,303 +588,6 @@ Channel *hash_get_chan_bucket(uint64_t hashv) return channelTable[hashv]; } -void count_watch_memory(int *count, u_long *memory) -{ - int i = WATCH_HASH_TABLE_SIZE; - Watch *anptr; - - while (i--) - { - anptr = watchTable[i]; - while (anptr) - { - (*count)++; - (*memory) += sizeof(Watch)+strlen(anptr->nick); - anptr = anptr->hnext; - } - } -} - -/* - * add_to_watch_hash_table - */ -int add_to_watch_hash_table(char *nick, Client *client, int awaynotify) -{ - unsigned int hashv; - Watch *anptr; - Link *lp; - - - /* Get the right bucket... */ - hashv = hash_watch_nick_name(nick); - - /* Find the right nick (header) in the bucket, or NULL... */ - if ((anptr = (Watch *)watchTable[hashv])) - while (anptr && mycmp(anptr->nick, nick)) - anptr = anptr->hnext; - - /* If found NULL (no header for this nick), make one... */ - if (!anptr) { - anptr = (Watch *)safe_alloc(sizeof(Watch)+strlen(nick)); - anptr->lasttime = timeofday; - strcpy(anptr->nick, nick); - - anptr->watch = NULL; - - anptr->hnext = watchTable[hashv]; - watchTable[hashv] = anptr; - } - /* Is this client already on the watch-list? */ - if ((lp = anptr->watch)) - while (lp && (lp->value.client != client)) - lp = lp->next; - - /* No it isn't, so add it in the bucket and client addint it */ - if (!lp) { - lp = anptr->watch; - anptr->watch = make_link(); - anptr->watch->value.client = client; - anptr->watch->flags = awaynotify; - anptr->watch->next = lp; - - lp = make_link(); - lp->next = client->local->watch; - lp->value.wptr = anptr; - lp->flags = awaynotify; - client->local->watch = lp; - client->local->watches++; - } - - return 0; -} - -/* - * hash_check_watch - */ -int hash_check_watch(Client *client, int reply) -{ - unsigned int hashv; - Watch *anptr; - Link *lp; - int awaynotify = 0; - - if ((reply == RPL_GONEAWAY) || (reply == RPL_NOTAWAY) || (reply == RPL_REAWAY)) - awaynotify = 1; - - /* Get us the right bucket */ - hashv = hash_watch_nick_name(client->name); - - /* Find the right header in this bucket */ - if ((anptr = (Watch *)watchTable[hashv])) - while (anptr && mycmp(anptr->nick, client->name)) - anptr = anptr->hnext; - if (!anptr) - return 0; /* This nick isn't on watch */ - - /* Update the time of last change to item */ - anptr->lasttime = TStime(); - - /* Send notifies out to everybody on the list in header */ - for (lp = anptr->watch; lp; lp = lp->next) - { - if (!awaynotify) - { - sendnumeric(lp->value.client, reply, - client->name, - (IsUser(client) ? client->user->username : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), anptr->lasttime, client->info); - } - else - { - /* AWAY or UNAWAY */ - if (!lp->flags) - continue; /* skip away/unaway notification for users not interested in them */ - - if (reply == RPL_NOTAWAY) - sendnumeric(lp->value.client, reply, - client->name, - (IsUser(client) ? client->user->username : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), client->user->lastaway); - else /* RPL_GONEAWAY / RPL_REAWAY */ - sendnumeric(lp->value.client, reply, - client->name, - (IsUser(client) ? client->user->username : ""), - (IsUser(client) ? - (IsHidden(client) ? client->user->virthost : client-> - user->realhost) : ""), client->user->lastaway, client->user->away); - } - } - - return 0; -} - -/* - * hash_get_watch - */ -Watch *hash_get_watch(char *nick) -{ - unsigned int hashv; - Watch *anptr; - - hashv = hash_watch_nick_name(nick); - - if ((anptr = (Watch *)watchTable[hashv])) - while (anptr && mycmp(anptr->nick, nick)) - anptr = anptr->hnext; - - return anptr; -} - -/* - * del_from_watch_hash_table - */ -int del_from_watch_hash_table(char *nick, Client *client) -{ - unsigned int hashv; - Watch *anptr, *nlast = NULL; - Link *lp, *last = NULL; - - /* Get the bucket for this nick... */ - hashv = hash_watch_nick_name(nick); - - /* Find the right header, maintaining last-link pointer... */ - if ((anptr = (Watch *)watchTable[hashv])) - while (anptr && mycmp(anptr->nick, nick)) { - nlast = anptr; - anptr = anptr->hnext; - } - if (!anptr) - return 0; /* No such watch */ - - /* Find this client from the list of notifies... with last-ptr. */ - if ((lp = anptr->watch)) - while (lp && (lp->value.client != client)) { - last = lp; - lp = lp->next; - } - if (!lp) - return 0; /* No such client to watch */ - - /* Fix the linked list under header, then remove the watch entry */ - if (!last) - anptr->watch = lp->next; - else - last->next = lp->next; - free_link(lp); - - /* Do the same regarding the links in client-record... */ - last = NULL; - if ((lp = client->local->watch)) - while (lp && (lp->value.wptr != anptr)) { - last = lp; - lp = lp->next; - } - - /* - * Give error on the odd case... probobly not even neccessary - * No error checking in ircd is unneccessary ;) -Cabal95 - */ - if (!lp) - sendto_ops("WATCH debug error: del_from_watch_hash_table " - "found a watch entry with no client " - "counterpoint processing nick %s on client %p!", - nick, client->user); - else { - if (!last) /* First one matched */ - client->local->watch = lp->next; - else - last->next = lp->next; - free_link(lp); - } - /* In case this header is now empty of notices, remove it */ - if (!anptr->watch) { - if (!nlast) - watchTable[hashv] = anptr->hnext; - else - nlast->hnext = anptr->hnext; - safe_free(anptr); - } - - /* Update count of notifies on nick */ - client->local->watches--; - - return 0; -} - -/* - * hash_del_watch_list - */ -int hash_del_watch_list(Client *client) -{ - unsigned int hashv; - Watch *anptr; - Link *np, *lp, *last; - - - if (!(np = client->local->watch)) - return 0; /* Nothing to do */ - - client->local->watch = NULL; /* Break the watch-list for client */ - while (np) { - /* Find the watch-record from hash-table... */ - anptr = np->value.wptr; - last = NULL; - for (lp = anptr->watch; lp && (lp->value.client != client); - lp = lp->next) - last = lp; - - /* Not found, another "worst case" debug error */ - if (!lp) - sendto_ops("WATCH Debug error: hash_del_watch_list " - "found a WATCH entry with no table " - "counterpoint processing client %s!", - client->name); - else { - /* Fix the watch-list and remove entry */ - if (!last) - anptr->watch = lp->next; - else - last->next = lp->next; - free_link(lp); - - /* - * If this leaves a header without notifies, - * remove it. Need to find the last-pointer! - */ - if (!anptr->watch) { - Watch *np2, *nl; - - hashv = hash_watch_nick_name(anptr->nick); - - nl = NULL; - np2 = watchTable[hashv]; - while (np2 != anptr) { - nl = np2; - np2 = np2->hnext; - } - - if (nl) - nl->hnext = anptr->hnext; - else - watchTable[hashv] = anptr->hnext; - safe_free(anptr); - } - } - - lp = np; /* Save last pointer processed */ - np = np->next; /* Jump to the next pointer */ - free_link(lp); /* Free the previous */ - } - - client->local->watches = 0; - - return 0; -} - /* Throttling - originally by Stskeeps */ /* Note that we call this set::anti-flood::connect-flood nowadays */ @@ -925,15 +617,7 @@ void update_throttling_timer_settings(void) EventMod(EventFind("throttling_check_expire"), &eInfo); } -void init_throttling() -{ - EventAdd(NULL, "throttling_check_expire", throttling_check_expire, NULL, 123456, 0); - /* Note: the every_ms value (123,456) will be adjusted on boot and rehash - * via the update_throttling_timer_settings() function. - */ -} - -uint64_t hash_throttling(char *ip) +uint64_t hash_throttling(const char *ip) { return siphash(ip, siphashkey_throttling) % THROTTLING_HASH_TABLE_SIZE; } diff --git a/src/ircd.c b/src/ircd.c index ed1e0d4..dbb5554 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -34,7 +34,6 @@ time_t timeofday = 0; struct timeval timeofday_tv; int tainted = 0; LoopStruct loop; -MODVAR MemoryInfo StatsZ; #ifndef _WIN32 uid_t irc_uid = 0; gid_t irc_gid = 0; @@ -51,12 +50,8 @@ extern SERVICE_STATUS IRCDStatus; MODVAR unsigned char conf_debuglevel = 0; -#ifdef USE_LIBCURL -extern void url_init(void); -#endif - -void server_reboot(char *); -void restart(char *); +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); @@ -82,7 +77,7 @@ void s_die() Client *client; if (!IsService) { - loop.ircd_terminating = 1; + loop.terminating = 1; unload_all_modules(); list_for_each_entry(client, &lclient_list, lclient_node) @@ -97,7 +92,7 @@ void s_die() ControlService(hService, SERVICE_CONTROL_STOP, &status); } #else - loop.ircd_terminating = 1; + loop.terminating = 1; unload_all_modules(); unlink(conf_files ? conf_files->pid_file : IRCD_PIDFILE); exit(0); @@ -128,7 +123,7 @@ static void s_reloadcert() } #endif // #ifndef _WIN32 -void restart(char *mesg) +void restart(const char *mesg) { server_reboot(mesg); } @@ -174,12 +169,13 @@ void ignore_this_signal() #endif /* #ifndef _WIN32 */ -void server_reboot(char *mesg) +void server_reboot(const char *mesg) { int i; Client *client; - sendto_realops("Aieeeee!!! Restarting server... %s", mesg); - Debug((DEBUG_NOTICE, "Restarting server... %s", mesg)); + 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); @@ -206,12 +202,6 @@ void server_reboot(char *mesg) CleanUp(); WinExec(cmdLine, SW_SHOWDEFAULT); } -#endif -#ifndef _WIN32 - Debug((DEBUG_FATAL, "Couldn't restart server: %s", strerror(errno))); -#else - Debug((DEBUG_FATAL, "Couldn't restart server: %s", - strerror(GetLastError()))); #endif unload_all_modules(); #ifdef _WIN32 @@ -254,7 +244,7 @@ EVENT(garbage_collect) int ii; if (loop.do_garbage_collect == 1) - sendto_realops("Doing garbage collection .."); + unreal_log(ULOG_INFO, "main", "GARBAGE_COLLECT_STARTED", NULL, "Doing garbage collection..."); if (freelinks > HOW_MANY_FREELINKS_ALLOWED) { ii = freelinks; while (freelink && (freelinks > HOW_MANY_FREELINKS_ALLOWED)) { @@ -265,65 +255,20 @@ EVENT(garbage_collect) } if (loop.do_garbage_collect == 1) { loop.do_garbage_collect = 0; - sendto_realops - ("Cleaned up %i garbage blocks", (ii - freelinks)); + unreal_log(ULOG_INFO, "main", "GARBAGE_COLLECT_STARTED", NULL, "Cleaned up $count garbage blocks", + (ii - freelinks)); } } if (loop.do_garbage_collect == 1) loop.do_garbage_collect = 0; } -/** Perform autoconnect to servers that are not linked yet. */ -EVENT(try_connections) -{ - ConfigItem_link *aconf; - ConfigItem_deny_link *deny; - Client *client; - int confrq; - ConfigItem_class *class; - - for (aconf = conf_link; aconf; aconf = aconf->next) - { - /* We're only interested in autoconnect blocks that are valid. Also, we ignore temporary link blocks. */ - if (!(aconf->outgoing.options & CONNECT_AUTO) || !aconf->outgoing.hostname || (aconf->flag.temporary == 1)) - continue; - - class = aconf->class; - - /* Only do one connection attempt per seconds (for the same server) */ - if ((aconf->hold > TStime())) - continue; - - confrq = class->connfreq; - aconf->hold = TStime() + confrq; - - client = find_client(aconf->servername, NULL); - if (client) - continue; /* Server already connected (or connecting) */ - - if (class->clients >= class->maxclients) - continue; /* Class is full */ - - /* Check connect rules to see if we're allowed to try the link */ - for (deny = conf_deny_link; deny; deny = deny->next) - if (match_simple(deny->mask, aconf->servername) && crule_eval(deny->rule)) - break; - - if (!deny && connect_server(aconf, NULL, NULL) == 0) - sendto_ops_and_log("Trying to activate link with server %s[%s]...", - aconf->servername, aconf->outgoing.hostname); - - } -} - /** Does this user match any TKL's? */ int match_tkls(Client *client) { ConfigItem_ban *bconf = NULL; char banbuf[1024]; - char killflag = 0; - /* Process dynamic *LINES */ if (find_tkline_match(client, 0)) return 1; /* user killed */ @@ -334,35 +279,25 @@ int match_tkls(Client *client) { /* Check ban realname { } */ if (!ValidatePermissionsForPath("immune",client,NULL,NULL,NULL) && (bconf = find_ban(NULL, client->info, CONF_BAN_REALNAME))) - killflag++; - } + { + unreal_log(ULOG_INFO, "tkl", "BAN_REALNAME", client, + "Banned client $client.details due to realname ban: $reason", + bconf->reason ? bconf->reason : "no reason"); - /* If user is meant to be killed, take action: */ - if (killflag) - { - if (IsUser(client)) - sendto_realops("Ban active for %s (%s)", - get_client_name(client, FALSE), - bconf->reason ? bconf->reason : "no reason"); - - if (IsServer(client)) - sendto_realops("Ban active for server %s (%s)", - get_client_name(client, FALSE), - bconf->reason ? bconf->reason : "no reason"); - - if (bconf->reason) { - if (IsUser(client)) - snprintf(banbuf, sizeof(banbuf), "User has been banned (%s)", bconf->reason); - else - snprintf(banbuf, sizeof(banbuf), "Banned (%s)", bconf->reason); - exit_client(client, NULL, banbuf); - } else { - if (IsUser(client)) - exit_client(client, NULL, "User has been banned"); - else - exit_client(client, NULL, "Banned"); + if (bconf->reason) { + if (IsUser(client)) + snprintf(banbuf, sizeof(banbuf), "User has been banned (%s)", bconf->reason); + else + snprintf(banbuf, sizeof(banbuf), "Banned (%s)", bconf->reason); + exit_client(client, NULL, banbuf); + } else { + if (IsUser(client)) + exit_client(client, NULL, "User has been banned"); + else + exit_client(client, NULL, "Banned"); + } + return 1; /* stop processing, client is dead now */ } - return 1; /* stop processing this user, as (s)he is dead now. */ } if (loop.do_bancheck_spamf_user && IsUser(client) && find_spamfilter_user(client, SPAMFLAG_NOWARN)) @@ -385,14 +320,10 @@ EVENT(handshake_timeout) list_for_each_entry_safe(client, next, &unknown_list, lclient_node) { - if (client->local->firsttime && ((TStime() - client->local->firsttime) > iConf.handshake_timeout)) + if (client->local->creationtime && ((TStime() - client->local->creationtime) > iConf.handshake_timeout)) { - if (client->serv && *client->serv->by) - { - /* If this is a handshake timeout to an outgoing server then notify ops & log it */ - sendto_ops_and_log("Connection handshake timeout while trying to link to server '%s' (%s)", - client->name, client->ip?client->ip:""); - } + if (client->server && *client->server->by) + continue; /* handled by server module */ exit_client(client, NULL, "Registration Timeout"); continue; @@ -407,37 +338,31 @@ void check_ping(Client *client) int ping = 0; ping = client->local->class ? client->local->class->pingfreq : iConf.handshake_timeout; - Debug((DEBUG_DEBUG, "c(%s)=%d p %d a %lld", client->name, - client->status, ping, - (long long)(TStime() - client->local->lasttime))); /* If ping is less than or equal to the last time we received a command from them */ - if (ping > (TStime() - client->local->lasttime)) + if (ping > (TStime() - client->local->last_msg_received)) return; /* some recent command was executed */ if ( /* If we have sent a ping */ (IsPingSent(client) /* And they had 2x ping frequency to respond */ - && ((TStime() - client->local->lasttime) >= (2 * ping))) + && ((TStime() - client->local->last_msg_received) >= (2 * ping))) || /* Or isn't registered and time spent is larger than ping (CONNECTTIMEOUT).. */ - (!IsRegistered(client) && (TStime() - client->local->since >= ping)) + (!IsRegistered(client) && (TStime() - client->local->fake_lag >= ping)) ) { if (IsServer(client) || IsConnecting(client) || IsHandshake(client) || IsTLSConnectHandshake(client)) { - sendto_umode_global(UMODE_OPER, "No response from %s, closing link", - get_client_name(client, FALSE)); - ircd_log(LOG_ERROR, "No response from %s, closing link", - get_client_name(client, FALSE)); + unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, + "Lost server link to $client [$client.ip]: No response (Ping timeout)", + client->server->conf ? log_data_link_block(client->server->conf) : NULL); + SetServerDisconnectLogged(client); } - if (IsTLSAcceptHandshake(client)) - Debug((DEBUG_DEBUG, "ssl accept handshake timeout: %s (%lld-%lld > %lld)", client->local->sockhost, - (long long)TStime(), (long long)client->local->since, (long long)ping)); ircsnprintf(scratch, sizeof(scratch), "Ping timeout: %lld seconds", - (long long) (TStime() - client->local->lasttime)); + (long long) (TStime() - client->local->last_msg_received)); exit_client(client, NULL, scratch); return; } @@ -447,17 +372,19 @@ void check_ping(Client *client) SetPingSent(client); ClearPingWarning(client); /* not nice but does the job */ - client->local->lasttime = TStime() - ping; + client->local->last_msg_received = TStime() - ping; sendto_one(client, NULL, "PING :%s", me.name); } else if (!IsPingWarning(client) && PINGWARNING > 0 && (IsServer(client) || IsHandshake(client) || IsConnecting(client) || IsTLSConnectHandshake(client)) && - (TStime() - client->local->lasttime) >= (ping + PINGWARNING)) + (TStime() - client->local->last_msg_received) >= (ping + PINGWARNING)) { SetPingWarning(client); - sendto_realops("Warning, no response from %s for %d seconds", - get_client_name(client, FALSE), PINGWARNING); + unreal_log(ULOG_WARNING, "link", "LINK_UNRELIABLE", client, + "Warning, no response from $client for $time_delta seconds", + log_data_integer("time_delta", PINGWARNING), + client->server->conf ? log_data_link_block(client->server->conf) : NULL); } return; @@ -496,9 +423,6 @@ EVENT(check_deadsockets) /* No need to notify opers here. It's already done when dead socket is set */ if (IsDeadSocket(client)) { -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "Closing deadsock: %d/%s", client->local->fd, client->name); -#endif ClearDeadSocket(client); /* CPR. So we send the error. */ exit_client(client, NULL, client->local->error_str ? client->local->error_str : "Dead socket"); continue; @@ -510,9 +434,6 @@ EVENT(check_deadsockets) /* No need to notify opers here. It's already done when dead socket is set */ if (IsDeadSocket(client)) { -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "Closing deadsock: %d/%s", client->local->fd, client->name); -#endif ClearDeadSocket(client); /* CPR. So we send the error. */ exit_client(client, NULL, client->local->error_str ? client->local->error_str : "Dead socket"); continue; @@ -562,21 +483,6 @@ char chess[] = { 85, 110, 114, 101, 97, 108, 0 }; -static void version_check_logerror(char *fmt, ...) -{ -va_list va; -char buf[1024]; - - va_start(va, fmt); - vsnprintf(buf, sizeof(buf), fmt, va); - va_end(va); -#ifndef _WIN32 - fprintf(stderr, "[!!!] %s\n", buf); -#else - win_log("[!!!] %s", buf); -#endif -} - extern void applymeblock(void); extern MODVAR Event *events; @@ -595,41 +501,20 @@ void fix_timers(void) list_for_each_entry(client, &lclient_list, lclient_node) { - if (client->local->since > TStime()) - { - Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->since %ld -> %ld", - client->name, client->local->since, TStime())); - client->local->since = TStime(); - } - if (client->local->lasttime > TStime()) - { - Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->lasttime %ld -> %ld", - client->name, client->local->lasttime, TStime())); - client->local->lasttime = TStime(); - } - if (client->local->last > TStime()) - { - Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->last %ld -> %ld", - client->name, client->local->last, TStime())); - client->local->last = TStime(); - } + if (client->local->fake_lag > TStime()) + client->local->fake_lag = TStime(); + if (client->local->last_msg_received > TStime()) + client->local->last_msg_received = TStime(); + if (client->local->idle_since > TStime()) + client->local->idle_since = TStime(); /* users */ if (MyUser(client)) { - if (client->local->nextnick > TStime()) - { - Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->nextnick %ld -> %ld", - client->name, client->local->nextnick, TStime())); - client->local->nextnick = TStime(); - } + if (client->local->next_nick_allowed > TStime()) + client->local->next_nick_allowed = TStime(); if (client->local->nexttarget > TStime()) - { - Debug((DEBUG_DEBUG, "fix_timers(): %s: client->local->nexttarget %ld -> %ld", - client->name, client->local->nexttarget, TStime())); client->local->nexttarget = TStime(); - } - } } @@ -658,7 +543,6 @@ void fix_timers(void) thr->since = TStime(); } } - Debug((DEBUG_DEBUG, "fix_timers(): removed %d throttling item(s)", cnt)); /* Make sure autoconnect for servers still works (lnk->hold) */ for (lnk = conf_link; lnk; lnk = lnk->next) @@ -668,37 +552,34 @@ void fix_timers(void) if (lnk->hold > TStime() + t) { lnk->hold = TStime() + (t / 2); /* compromise */ - Debug((DEBUG_DEBUG, "fix_timers(): link '%s' hold-time adjusted to %ld", lnk->servername, lnk->hold)); } } } #ifndef _WIN32 +/* Generate 3 cloak keys and print to console */ static void generate_cloakkeys() { - /* Generate 3 cloak keys */ -#define GENERATE_CLOAKKEY_MINLEN 50 -#define GENERATE_CLOAKKEY_MAXLEN 60 /* Length of cloak keys to generate. */ - char keyBuf[GENERATE_CLOAKKEY_MAXLEN + 1]; + #define GENERATE_CLOAKKEY_LEN 80 /* Length of cloak keys to generate. */ + char keyBuf[GENERATE_CLOAKKEY_LEN + 1]; int keyNum; - int keyLen; int charIndex; short has_upper; short has_lower; short has_num; - fprintf(stderr, "Here are 3 random cloak keys:\n"); + 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; - keyLen = (getrandom8() % (GENERATE_CLOAKKEY_MAXLEN - GENERATE_CLOAKKEY_MINLEN + 1)) + GENERATE_CLOAKKEY_MINLEN; - for (charIndex = 0; charIndex < keyLen; ++charIndex) + for (charIndex = 0; charIndex < sizeof(keyBuf)-1; ++charIndex) { switch (getrandom8() % 3) { @@ -716,14 +597,15 @@ static void generate_cloakkeys() break; } } - keyBuf[keyLen] = '\0'; + keyBuf[sizeof(keyBuf)-1] = '\0'; if (has_upper && has_lower && has_num) - (void)fprintf(stderr, "%s\n", keyBuf); + 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 @@ -743,38 +625,35 @@ void detect_timeshift_and_warn(void) if (oldtimeofday == 0) oldtimeofday = timeofday; /* pretend everything is ok the first time.. */ - if (mytdiff(timeofday, oldtimeofday) < NEGATIVE_SHIFT_WARN) { + if (mytdiff(timeofday, oldtimeofday) < NEGATIVE_SHIFT_WARN) + { /* tdiff = # of seconds of time set backwards (positive number! eg: 60) */ time_t tdiff = oldtimeofday - timeofday; - ircd_log(LOG_ERROR, "WARNING: Time running backwards! Clock set back ~%lld seconds (%lld -> %lld)", - (long long)tdiff, (long long)oldtimeofday, (long long)timeofday); - ircd_log(LOG_ERROR, "[TimeShift] Resetting a few timers to prevent IRCd freeze!"); - sendto_realops("WARNING: Time running backwards! Clock set back ~%lld seconds (%lld -> %lld)", - (long long)tdiff, (long long)oldtimeofday, (long long)timeofday); - sendto_realops("Incorrect time for IRC servers is a serious problem. " - "Time being set backwards (system clock changed) is " - "even more serious and can cause clients to freeze, channels to be " - "taken over, and other issues."); - sendto_realops("Please be sure your clock is always synchronized before " - "the IRCd is started!"); - sendto_realops("[TimeShift] Resetting a few timers to prevent IRCd freeze!"); + unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_BACKWARDS", NULL, + "System clock jumped back in time ~$time_delta seconds ($time_from -> $time_to)\n" + "Incorrect time for IRC servers is a serious problem. " + "Time being set backwards (system clock changed) is " + "even more serious and can cause clients to freeze, channels to be " + "taken over, and other issues.\n" + "Please be sure your clock is always synchronized before the IRCd is started!", + log_data_integer("time_delta", tdiff), + log_data_timestamp("time_from", oldtimeofday), + log_data_timestamp("time_to", timeofday)); fix_timers(); } else if (mytdiff(timeofday, oldtimeofday) > POSITIVE_SHIFT_WARN) /* do not set too low or you get false positives */ { /* tdiff = # of seconds of time set forward (eg: 60) */ time_t tdiff = timeofday - oldtimeofday; - ircd_log(LOG_ERROR, "WARNING: Time jumped ~%lld seconds ahead! (%lld -> %lld)", - (long long)tdiff, (long long)oldtimeofday, (long long)timeofday); - ircd_log(LOG_ERROR, "[TimeShift] Resetting some timers!"); - sendto_realops("WARNING: Time jumped ~%lld seconds ahead! (%lld -> %lld)", - (long long)tdiff, (long long)oldtimeofday, (long long)timeofday); - sendto_realops("Incorrect time for IRC servers is a serious problem. " - "Time being adjusted (by changing the system clock) " - "more than a few seconds forward/backward can lead to serious issues."); - sendto_realops("Please be sure your clock is always synchronized before " - "the IRCd is started!"); - sendto_realops("[TimeShift] Resetting some timers!"); + unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_FORWARDS", NULL, + "System clock jumped ~$time_delta seconds forward ($time_from -> $time_to)\n" + "Incorrect time for IRC servers is a serious problem. " + "Time being adjusted (by changing the system clock) " + "more than a few seconds forward/backward can lead to serious issues.\n" + "Please be sure your clock is always synchronized before the IRCd is started!", + log_data_integer("time_delta", tdiff), + log_data_timestamp("time_from", oldtimeofday), + log_data_timestamp("time_to", timeofday)); fix_timers(); } @@ -784,13 +663,11 @@ void detect_timeshift_and_warn(void) lasthighwarn = timeofday; if (timeofday - lasthighwarn > 300) { - ircd_log(LOG_ERROR, "[TimeShift] The (IRCd) clock was set backwards. " - "Waiting for time to be OK again. This will be in %lld seconds", - (long long)(highesttimeofday - timeofday)); - sendto_realops("[TimeShift] The (IRCd) clock was set backwards. Timers, nick- " - "and channel-timestamps are possibly incorrect. This message will " - "repeat itself until we catch up with the original time, which will be " - "in %lld seconds", (long long)(highesttimeofday - timeofday)); + unreal_log(ULOG_WARNING, "system", "SYSTEM_CLOCK_JUMP_BACKWARDS_PREVIOUSLY", NULL, + "The system clock previously went backwards. Waiting for time to be OK again. This will be in $time_delta seconds.", + log_data_integer("time_delta", highesttimeofday - timeofday), + log_data_timestamp("time_from", highesttimeofday), + log_data_timestamp("time_to", timeofday)); lasthighwarn = timeofday; } } else { @@ -909,7 +786,6 @@ int InitUnrealIRCd(int argc, char *argv[]) #else WSAStartup(wVersionRequested, &wsaData); #endif - memset(&StatsZ, 0, sizeof(StatsZ)); setup_signals(); memset(&irccounts, '\0', sizeof(irccounts)); @@ -918,10 +794,10 @@ int InitUnrealIRCd(int argc, char *argv[]) mp_pool_init(); dbuf_init(); initlists(); + initlist_channels(); -#ifdef USE_LIBCURL + early_init_tls(); url_init(); -#endif tkl_init(); umode_init(); extcmode_init(); @@ -967,7 +843,7 @@ int InitUnrealIRCd(int argc, char *argv[]) #ifndef _WIN32 case 'P':{ short type; - char *result; + const char *result; srandom(TStime()); type = Auth_FindType(NULL, p); if (type == -1) @@ -1001,11 +877,10 @@ int InitUnrealIRCd(int argc, char *argv[]) exit(0); } #endif -#if 1 +#if 0 case 'S': - //charsys_dump_table(p ? p : "*"); - unrealdb_test(); - exit(0); + charsys_dump_table(p ? p : "*"); + //unrealdb_test(); #endif #ifndef _WIN32 case 't': @@ -1065,14 +940,6 @@ int InitUnrealIRCd(int argc, char *argv[]) fprintf(stderr, "It is impossible to get here\n"); exit(0); } - case 'U': - if (chdir(CONFDIR) < 0) - { - fprintf(stderr, "Unable to change to '%s' directory\n", CONFDIR); - exit(1); - } - update_conf(); - exit(0); case 'R': report_crash(); exit(0); @@ -1142,7 +1009,8 @@ int InitUnrealIRCd(int argc, char *argv[]) #ifndef _WIN32 fprintf(stderr, "%s", unreallogo); fprintf(stderr, " v%s\n\n", VERSIONONLY); - fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop), Gottem and i\n\n"); + fprintf(stderr, "UnrealIRCd is brought to you by Bram Matthys (Syzop),\n" + "Krzysztof Beresztant (k4be), Gottem and i\n\n"); fprintf(stderr, "Using the following libraries:\n"); fprintf(stderr, "* %s\n", SSLeay_version(SSLEAY_VERSION)); @@ -1170,7 +1038,7 @@ int InitUnrealIRCd(int argc, char *argv[]) (void)chmod(CPATH, DEFAULT_PERMISSIONS); #endif init_dynconf(); - early_init_ssl(); + init_sys(); /* * Add default class */ @@ -1181,10 +1049,17 @@ int InitUnrealIRCd(int argc, char *argv[]) default_class->sendq = DEFAULT_RECVQ; default_class->name = "default"; AddListItem(default_class, conf_class); - if (init_conf(configfile, 0) < 0) - { + if (config_read_start() < 0) exit(-1); + while (!is_config_read_finished()) + { + gettimeofday(&timeofday_tv, NULL); + timeofday = timeofday_tv.tv_sec; + url_socket_timeout(NULL); + fd_select(500); } + if (config_test() < 0) + exit(-1); booted = TRUE; load_tunefile(); make_umodestr(); @@ -1192,7 +1067,6 @@ int InitUnrealIRCd(int argc, char *argv[]) me.local->fd = -1; SetMe(&me); make_server(&me); - extcmodes_check_for_changes(); umodes_check_for_changes(); charsys_check_for_changes(); clicap_init(); @@ -1202,37 +1076,30 @@ int InitUnrealIRCd(int argc, char *argv[]) exit(-4); } -#ifndef _WIN32 - fprintf(stderr, "Initializing TLS..\n"); -#endif - if (!init_ssl()) + if (!init_tls()) { - config_error("Failed to load SSL/TLS (see errors above). UnrealIRCd can not start."); + config_error("Failed to load TLS (see errors above). UnrealIRCd can not start."); #ifdef _WIN32 win_error(); /* display error dialog box */ #endif exit(9); } + unreal_log(ULOG_INFO, "config", "CONFIG_PASSED", NULL, "Configuration test passed OK"); if (loop.config_test) { - ircd_log(LOG_ERROR, "Configuration test passed OK"); fflush(stderr); exit(0); } if (loop.boot_function) loop.boot_function(); -#ifndef _WIN32 - fprintf(stderr, "Dynamic configuration initialized.. booting IRCd.\n"); -#endif open_debugfile(); me.local->port = 6667; /* pointless? */ - init_sys(); applymeblock(); #ifdef HAVE_SYSLOG openlog("ircd", LOG_PID | LOG_NDELAY, LOG_DAEMON); #endif - run_configuration(); - ircd_log(LOG_ERROR, "UnrealIRCd started."); + config_run(); + unreal_log(ULOG_INFO, "main", "UNREALIRCD_START", NULL, "UnrealIRCd started."); read_motd(conf_files->botmotd_file, &botmotd); read_motd(conf_files->rules_file, &rules); @@ -1250,11 +1117,10 @@ int InitUnrealIRCd(int argc, char *argv[]) * This listener will never go away */ me_hash = find_or_add(me.name); - me.serv->up = me_hash; timeofday = time(NULL); - me.local->lasttime = me.local->since = me.local->firsttime = me.serv->boottime = TStime(); - me.serv->features.protocol = UnrealProtocol; - safe_strdup(me.serv->features.software, version); + me.local->last_msg_received = me.local->fake_lag = me.local->creationtime = me.server->boottime = TStime(); + me.server->features.protocol = UnrealProtocol; + safe_strdup(me.server->features.software, version); add_to_client_hash_table(me.name, &me); add_to_id_hash_table(me.id, &me); list_add(&me.client_node, &global_server_list); @@ -1277,18 +1143,16 @@ int InitUnrealIRCd(int argc, char *argv[]) /* Background process (child) continues below... */ close_std_descriptors(); fd_fork(); - loop.ircd_forked = 1; + loop.forked = 1; } #endif #ifdef _WIN32 - loop.ircd_forked = 1; + loop.forked = 1; #endif fix_timers(); write_pidfile(); - Debug((DEBUG_NOTICE, "Server ready...")); - init_throttling(); - loop.ircd_booted = 1; + loop.booted = 1; #if defined(HAVE_SETPROCTITLE) setproctitle("%s", me.name); #elif defined(HAVE_PSTAT) @@ -1346,7 +1210,7 @@ void SocketLoop(void *dummy) */ if (dorehash) { - (void)rehash(&me, 1); + request_rehash(NULL); dorehash = 0; } if (dorestart) @@ -1355,7 +1219,8 @@ void SocketLoop(void *dummy) } if (doreloadcert) { - reinit_ssl(NULL); + unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", NULL, "Reloading all TLS related data (./unrealircd reloadtls)"); + reinit_tls(); doreloadcert = 0; } } @@ -1408,9 +1273,6 @@ static void open_debugfile(void) else # endif strlcpy(client->name, "FD2-Pipe", sizeof(client->name)); - Debug((DEBUG_FATAL, - "Debug: File <%s> Level: %d at %s", client->name, - client->local->port, myctime(time(NULL)))); } #endif } diff --git a/src/list.c b/src/list.c index 7007378..d752fb8 100644 --- a/src/list.c +++ b/src/list.c @@ -104,14 +104,14 @@ Client *make_client(Client *from, Client *servr) /* Note: all fields are already NULL/0, no need to set here */ client->direction = from ? from : client; /* 'from' of local client is self! */ - client->srvptr = servr; + client->uplink = servr; client->status = CLIENT_STATUS_UNKNOWN; INIT_LIST_HEAD(&client->client_node); INIT_LIST_HEAD(&client->client_hash); INIT_LIST_HEAD(&client->id_hash); - strcpy(client->ident, "unknown"); + strlcpy(client->ident, "unknown", sizeof(client->ident)); if (!from) { /* Local client */ @@ -123,9 +123,9 @@ Client *make_client(Client *from, Client *servr) INIT_LIST_HEAD(&client->lclient_node); INIT_LIST_HEAD(&client->special_node); - client->local->since = client->local->lasttime = - client->lastnick = client->local->firsttime = - client->local->last = TStime(); + client->local->fake_lag = client->local->last_msg_received = + client->lastnick = client->local->creationtime = + client->local->idle_since = TStime(); client->local->class = NULL; client->local->passwd = NULL; client->local->sockhost[0] = '\0'; @@ -197,7 +197,7 @@ User *make_user(Client *client) #ifdef DEBUGMODE users.inuse++; #endif - strlcpy(user->svid, "0", sizeof(user->svid)); + strlcpy(user->account, "0", sizeof(user->account)); if (client->ip) { /* initially set client->user->realhost to IP */ @@ -213,7 +213,7 @@ User *make_user(Client *client) Server *make_server(Client *client) { - Server *serv = client->serv; + Server *serv = client->server; if (!serv) { @@ -223,8 +223,7 @@ Server *make_server(Client *client) #endif *serv->by = '\0'; serv->users = 0; - serv->up = NULL; - client->serv = serv; + client->server = serv; } if (strlen(client->id) > 3) { @@ -234,7 +233,7 @@ Server *make_server(Client *client) del_from_id_hash_table(client->id, client); *client->id = '\0'; } - return client->serv; + return client->server; } /* @@ -260,6 +259,7 @@ void free_user(Client *client) } safe_free(client->user->virthost); safe_free(client->user->operlogin); + safe_free(client->user->snomask); mp_pool_release(client->user); #ifdef DEBUGMODE users.inuse--; @@ -297,8 +297,8 @@ void remove_client_from_list(Client *client) VERIFY_OPERCOUNT(client, "rmvlist"); } irccounts.clients--; - if (client->srvptr && client->srvptr->serv) - client->srvptr->serv->users--; + if (client->uplink && client->uplink->server) + client->uplink->server->users--; } if (IsUnknown(client) || IsConnecting(client) || IsHandshake(client) || IsTLSHandshake(client) @@ -313,16 +313,16 @@ void remove_client_from_list(Client *client) if (client->user) free_user(client); - if (client->serv) + if (client->server) { - safe_free(client->serv->features.usermodes); - safe_free(client->serv->features.chanmodes[0]); - safe_free(client->serv->features.chanmodes[1]); - safe_free(client->serv->features.chanmodes[2]); - safe_free(client->serv->features.chanmodes[3]); - safe_free(client->serv->features.software); - safe_free(client->serv->features.nickchars); - safe_free(client->serv); + safe_free(client->server->features.usermodes); + safe_free(client->server->features.chanmodes[0]); + safe_free(client->server->features.chanmodes[1]); + safe_free(client->server->features.chanmodes[2]); + safe_free(client->server->features.chanmodes[3]); + safe_free(client->server->features.software); + safe_free(client->server->features.nickchars); + safe_free(client->server); #ifdef DEBUGMODE servs.inuse--; #endif @@ -383,6 +383,16 @@ void free_link(Link *lp) #endif } +/** Returns the length (entry count) of a +beI list */ +int link_list_length(Link *lp) +{ + int count = 0; + + for (; lp; lp = lp->next) + count++; + return count; +} + Ban *make_ban(void) { Ban *lp; @@ -491,7 +501,7 @@ void add_ListItemPrio(ListStructPrio *new, ListStructPrio **list, int priority) /* NameList functions */ -void _add_name_list(NameList **list, char *name) +void _add_name_list(NameList **list, const char *name) { NameList *e = safe_alloc(sizeof(NameList)+strlen(name)); strcpy(e->name, name); /* safe, allocated above */ @@ -509,7 +519,7 @@ void _free_entire_name_list(NameList *n) } } -void _del_name_list(NameList **list, char *name) +void _del_name_list(NameList **list, const char *name) { NameList *e = find_name_list(*list, name); if (e) @@ -523,7 +533,7 @@ void _del_name_list(NameList **list, char *name) /** Find an entry in a NameList - case insensitive comparisson. * @ingroup ListFunctions */ -NameList *find_name_list(NameList *list, char *name) +NameList *find_name_list(NameList *list, const char *name) { NameList *e; @@ -540,7 +550,7 @@ NameList *find_name_list(NameList *list, char *name) /** Find an entry in a NameList by running match_simple() on it. * @ingroup ListFunctions */ -NameList *find_name_list_match(NameList *list, char *name) +NameList *find_name_list_match(NameList *list, const char *name) { NameList *e; @@ -554,7 +564,7 @@ NameList *find_name_list_match(NameList *list, char *name) return NULL; } -void add_nvplist(NameValuePrioList **lst, int priority, char *name, char *value) +void add_nvplist(NameValuePrioList **lst, int priority, const char *name, const char *value) { va_list vl; NameValuePrioList *e = safe_alloc(sizeof(NameValuePrioList)); @@ -564,7 +574,7 @@ void add_nvplist(NameValuePrioList **lst, int priority, char *name, char *value) AddListItemPrio(e, *lst, priority); } -NameValuePrioList *find_nvplist(NameValuePrioList *list, char *name) +NameValuePrioList *find_nvplist(NameValuePrioList *list, const char *name) { NameValuePrioList *e; @@ -578,7 +588,7 @@ NameValuePrioList *find_nvplist(NameValuePrioList *list, char *name) return NULL; } -void add_fmt_nvplist(NameValuePrioList **lst, int priority, char *name, FORMAT_STRING(const char *format), ...) +void add_fmt_nvplist(NameValuePrioList **lst, int priority, const char *name, FORMAT_STRING(const char *format), ...) { char value[512]; va_list vl; @@ -603,3 +613,37 @@ void free_nvplist(NameValuePrioList *lst) safe_free(e); } } + +#define nv_find_by_name(stru, name) do_nv_find_by_name(stru, name, ARRAY_SIZEOF((stru))) + +long do_nv_find_by_name(NameValue *table, const char *cmd, int numelements) +{ + int start = 0; + int stop = numelements-1; + int mid; + while (start <= stop) { + mid = (start+stop)/2; + + if (smycmp(cmd,table[mid].name) < 0) { + stop = mid-1; + } + else if (strcmp(cmd,table[mid].name) == 0) { + return table[mid].value; + } + else + start = mid+1; + } + return 0; +} + +#define nv_find_by_value(stru, value) do_nv_find_by_value(stru, value, ARRAY_SIZEOF((stru))) +const char *do_nv_find_by_value(NameValue *table, long value, int numelements) +{ + int i; + + for (i=0; i < numelements; i++) + if (table[i].value == value) + return table[i].name; + + return NULL; +} diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..d21dabb --- /dev/null +++ b/src/log.c @@ -0,0 +1,1871 @@ +/************************************************************************ + * IRC - Internet Relay Chat, src/api-channelmode.c + * (C) 2021 Bram Matthys (Syzop) 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 The logging API + */ + +#define UNREAL_LOGGER_CODE +#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 + +/* Variables */ +Log *logs[NUM_LOG_DESTINATIONS] = { NULL, NULL, NULL, NULL, NULL }; +Log *temp_logs[NUM_LOG_DESTINATIONS] = { NULL, NULL, NULL, NULL, NULL }; +static int snomask_num_destinations = 0; + +static char snomasks_in_use[257] = { '\0' }; +static char snomasks_in_use_testing[257] = { '\0' }; + +/* Forward declarations */ +int log_sources_match(LogSource *logsource, LogLevel loglevel, const char *subsystem, const char *event_id, int matched_already); +void do_unreal_log_internal(LogLevel loglevel, const char *subsystem, const char *event_id, Client *client, int expand_msg, const char *msg, va_list vl); +void log_blocks_switchover(void); + +/** Convert a regular string value to a JSON string. + * In UnrealIRCd, this must be used instead of json_string() + * as we may use non-UTF8 sequences. Also, this takes care + * of using json_null() if the string was NULL, which is + * usually what we want as well. + * @param s Input string + * @returns a json string value or json null value. + */ +json_t *json_string_unreal(const char *s) +{ + static char buf[8192]; + char *verified_s; + + if (s == NULL) + return json_null(); + + verified_s = unrl_utf8_make_valid(s, buf, sizeof(buf), 0); + return json_string(verified_s); +} + +#define json_string __BAD___DO__NOT__USE__JSON__STRING__PLZ + +json_t *json_timestamp(time_t v) +{ + const char *ts = timestamp_iso8601(v); + if (ts) + return json_string_unreal(ts); + return json_null(); +} + +LogType log_type_stringtoval(const char *str) +{ + if (!strcmp(str, "json")) + return LOG_TYPE_JSON; + if (!strcmp(str, "text")) + return LOG_TYPE_TEXT; + return LOG_TYPE_INVALID; +} + +const char *log_type_valtostring(LogType v) +{ + switch(v) + { + case LOG_TYPE_TEXT: + return "text"; + case LOG_TYPE_JSON: + return "json"; + default: + return NULL; + } +} + +/***** CONFIGURATION ******/ + +LogSource *add_log_source(const char *str) +{ + LogSource *ls; + char buf[256]; + char *p; + LogLevel loglevel = ULOG_INVALID; + char *subsystem = NULL; + char *event_id = NULL; + int negative = 0; + + if (*str == '!') + { + negative = 1; + strlcpy(buf, str+1, sizeof(buf)); + } else + { + strlcpy(buf, str, sizeof(buf)); + } + + p = strchr(buf, '.'); + if (p) + *p++ = '\0'; + + loglevel = log_level_stringtoval(buf); + if (loglevel == ULOG_INVALID) + { + if (isupper(*buf)) + event_id = buf; + else + subsystem = buf; + } + if (p) + { + if (isupper(*p)) + { + event_id = p; + } else + if (loglevel == ULOG_INVALID) + { + loglevel = log_level_stringtoval(p); + if ((loglevel == ULOG_INVALID) && !subsystem) + subsystem = p; + } else if (!subsystem) + { + subsystem = p; + } + } + ls = safe_alloc(sizeof(LogSource)); + ls->loglevel = loglevel; + ls->negative = negative; + if (!BadPtr(subsystem)) + strlcpy(ls->subsystem, subsystem, sizeof(ls->subsystem)); + if (!BadPtr(event_id)) + strlcpy(ls->event_id, event_id, sizeof(ls->event_id)); + + return ls; +} + +void free_log_source(LogSource *l) +{ + safe_free(l); +} + +void free_log_sources(LogSource *l) +{ + LogSource *l_next; + for (; l; l = l_next) + { + l_next = l->next; + free_log_source(l); + } +} + +int config_test_log(ConfigFile *conf, ConfigEntry *block) +{ + int errors = 0; + int any_sources = 0; + ConfigEntry *ce, *cep, *cepp; + int destinations = 0; + + for (ce = block->items; ce; ce = ce->next) + { + if (!strcmp(ce->name, "source")) + { + for (cep = ce->items; cep; cep = cep->next) + { + /* TODO: Validate the sources lightly for formatting issues */ + any_sources = 1; + } + } + if (!strcmp(ce->name, "destination")) + { + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "snomask")) + { + destinations++; + snomask_num_destinations++; + /* We need to validate the parameter here as well */ + if (!cep->value) + { + config_error_blank(cep->file->filename, cep->line_number, "set::logging::snomask"); + errors++; + } else + if ((strlen(cep->value) != 1) || !(islower(cep->value[0]) || isupper(cep->value[0]))) + { + config_error("%s:%d: snomask must be a single letter", + cep->file->filename, cep->line_number); + errors++; + } else { + strlcat(snomasks_in_use_testing, cep->value, sizeof(snomasks_in_use_testing)); + } + } else + if (!strcmp(cep->name, "channel")) + { + destinations++; + /* We need to validate the parameter here as well */ + if (!cep->value) + { + config_error_blank(cep->file->filename, cep->line_number, "set::logging::channel"); + errors++; + } else + if (!valid_channelname(cep->value)) + { + config_error("%s:%d: Invalid channel name '%s'", + cep->file->filename, cep->line_number, cep->value); + errors++; + } + } else + if (!strcmp(cep->name, "file")) + { + destinations++; + if (!cep->value) + { + config_error_blank(cep->file->filename, cep->line_number, "set::logging::file"); + errors++; + continue; + } + convert_to_absolute_path(&cep->value, LOGDIR); + for (cepp = cep->items; cepp; cepp = cepp->next) + { + if (!strcmp(cepp->name, "type")) + { + if (!cepp->value) + { + config_error_empty(cepp->file->filename, + cepp->line_number, "log", cepp->name); + errors++; + continue; + } + if (!log_type_stringtoval(cepp->value)) + { + config_error("%s:%i: unknown log type '%s'", + cepp->file->filename, cepp->line_number, + cepp->value); + errors++; + } + } else + if (!strcmp(cepp->name, "maxsize")) + { + if (!cepp->value) + { + config_error_empty(cepp->file->filename, + cepp->line_number, "log", cepp->name); + errors++; + } + } else + { + config_error_unknown(cepp->file->filename, cepp->line_number, "log::destination::file", cepp->name); + errors++; + } + } + } else + if (!strcmp(cep->name, "remote")) + { + destinations++; + } else + if (!strcmp(cep->name, "syslog")) + { + destinations++; + for (cepp = cep->items; cepp; cepp = cepp->next) + { + if (!strcmp(cepp->name, "type")) + { + if (!cepp->value) + { + config_error_empty(cepp->file->filename, + cepp->line_number, "log", cepp->name); + errors++; + continue; + } + if (!log_type_stringtoval(cepp->value)) + { + config_error("%s:%i: unknown log type '%s'", + cepp->file->filename, cepp->line_number, + cepp->value); + errors++; + } + } else + { + config_error_unknown(cepp->file->filename, cepp->line_number, "log::destination::syslog", cepp->name); + errors++; + } + } + } else + { + config_error_unknownopt(cep->file->filename, cep->line_number, "log::destination", cep->name); + errors++; + continue; + } + } + } + } + + if (!any_sources && !destinations) + { + unreal_log(ULOG_ERROR, "config", "CONFIG_OLD_LOG_BLOCK", NULL, + "$config_file:$line_number: Your log block contains no sources and no destinations.\n" + "The log block changed between UnrealIRCd 5 and UnrealIRCd 6, " + "see https://www.unrealircd.org/docs/FAQ#old-log-block on how " + "to convert it to the new syntax.", + log_data_string("config_file", block->file->filename), + log_data_integer("line_number", block->line_number)); + errors++; + return errors; + } + + if (!any_sources) + { + config_error("%s:%d: log block contains no sources. Old log block perhaps?", + block->file->filename, block->line_number); + errors++; + } + if (destinations == 0) + { + config_error("%s:%d: log block contains no destinations. Old log block perhaps?", + block->file->filename, block->line_number); + errors++; + } + if (destinations > 1) + { + config_error("%s:%d: log block contains multiple destinations. This is not support... YET!", + block->file->filename, block->line_number); + errors++; + } + return errors; +} + +int config_run_log(ConfigFile *conf, ConfigEntry *block) +{ + ConfigEntry *ce, *cep, *cepp; + LogSource *sources = NULL; + int type; + + /* If we later allow multiple destination entries later, + * then we need to 'clone' sources or work with reference counts. + */ + + /* First, gather the source... */ + for (ce = block->items; ce; ce = ce->next) + { + if (!strcmp(ce->name, "source")) + { + LogSource *s; + for (cep = ce->items; cep; cep = cep->next) + { + s = add_log_source(cep->name); + AddListItem(s, sources); + } + } + } + + /* Now deal with destinations... */ + for (ce = block->items; ce; ce = ce->next) + { + if (!strcmp(ce->name, "destination")) + { + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "snomask")) + { + Log *log = safe_alloc(sizeof(Log)); + strlcpy(log->destination, cep->value, sizeof(log->destination)); /* destination is the snomask */ + strlcat(snomasks_in_use, cep->value, sizeof(snomasks_in_use)); + log->sources = sources; + if (!strcmp(cep->value, "s")) + AddListItem(log, temp_logs[LOG_DEST_OPER]); + else + AddListItem(log, temp_logs[LOG_DEST_SNOMASK]); + } else + if (!strcmp(cep->name, "channel")) + { + Log *log = safe_alloc(sizeof(Log)); + strlcpy(log->destination, cep->value, sizeof(log->destination)); /* destination is the channel */ + log->sources = sources; + AddListItem(log, temp_logs[LOG_DEST_CHANNEL]); + } else + if (!strcmp(cep->name, "remote")) + { + Log *log = safe_alloc(sizeof(Log)); + /* destination stays empty */ + log->sources = sources; + AddListItem(log, temp_logs[LOG_DEST_REMOTE]); + } else + if (!strcmp(cep->name, "file") || !strcmp(cep->name, "syslog")) + { + Log *log; + /* First check if already exists... yeah this is a bit late + * and ideally would have been done in config_test but... + * that would have been lots of work for a (hopefully) rare case. + */ + for (log = temp_logs[LOG_DEST_DISK]; log; log = log->next) + { + if ((log->file && !strcmp(log->file, cep->value)) || + (log->filefmt && !strcmp(log->filefmt, cep->value))) + { + config_warn("%s:%d: Ignoring duplicate log block for file '%s'. " + "You cannot have multiple log blocks logging to the same file.", + cep->file->filename, cep->line_number, + cep->value); + free_log_sources(sources); + return 0; + } + } + log = safe_alloc(sizeof(Log)); + log->sources = sources; + log->logfd = -1; + log->type = LOG_TYPE_TEXT; /* default */ + if (!strcmp(cep->name, "syslog")) + safe_strdup(log->file, "syslog"); + else if (strchr(cep->value, '%')) + safe_strdup(log->filefmt, cep->value); + else + safe_strdup(log->file, cep->value); + for (cepp = cep->items; cepp; cepp = cepp->next) + { + if (!strcmp(cepp->name, "maxsize")) + { + log->maxsize = config_checkval(cepp->value,CFG_SIZE); + } + else if (!strcmp(cepp->name, "type")) + { + log->type = log_type_stringtoval(cepp->value); + } + } + AddListItem(log, temp_logs[LOG_DEST_DISK]); + } + } + } + } + + return 0; +} + + + + +/***** RUNTIME *****/ + +void json_expand_client_security_groups(json_t *parent, Client *client) +{ + SecurityGroup *s; + json_t *child = json_array(); + json_object_set_new(parent, "security-groups", child); + + /* We put known-users or unknown-users at the beginning. + * The latter is special and doesn't actually exist + * in the linked list, hence the special code here, + * and again later in the for loop to skip it. + */ + if (user_allowed_by_security_group_name(client, "known-users")) + json_array_append_new(child, json_string_unreal("known-users")); + else + json_array_append_new(child, json_string_unreal("unknown-users")); + + for (s = securitygroups; s; s = s->next) + if (strcmp(s->name, "known-users") && user_allowed_by_security_group(client, s)) + json_array_append_new(child, json_string_unreal(s->name)); +} + +void json_expand_client(json_t *j, const char *key, Client *client, int detail) +{ + char buf[BUFSIZE+1]; + json_t *child = json_object(); + json_t *user = NULL; + json_object_set_new(j, key, child); + + /* First the information that is available for ALL client types: */ + + json_object_set_new(child, "name", json_string_unreal(client->name)); + json_object_set_new(child, "id", json_string_unreal(client->id)); + + /* hostname is available for all, it just depends a bit on whether it is DNS or IP */ + if (client->user && *client->user->realhost) + json_object_set_new(child, "hostname", json_string_unreal(client->user->realhost)); + else if (client->local && *client->local->sockhost) + json_object_set_new(child, "hostname", json_string_unreal(client->local->sockhost)); + else + json_object_set_new(child, "hostname", json_string_unreal(GetIP(client))); + + /* 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)); + + /* client.details is always available: it is nick!user@host, nick@host, server@host + * server@ip, or just server. + */ + if (client->user) + { + snprintf(buf, sizeof(buf), "%s!%s@%s", client->name, client->user->username, client->user->realhost); + json_object_set_new(child, "details", json_string_unreal(buf)); + } else if (client->ip) { + if (*client->name) + snprintf(buf, sizeof(buf), "%s@%s", client->name, client->ip); + else + snprintf(buf, sizeof(buf), "[%s]", client->ip); + json_object_set_new(child, "details", json_string_unreal(buf)); + } else { + json_object_set_new(child, "details", json_string_unreal(client->name)); + } + + if (client->local && client->local->creationtime) + json_object_set_new(child, "connected_since", json_timestamp(client->local->creationtime)); + + if (client->user) + { + char buf[512]; + const char *str; + /* client.user */ + user = json_object(); + json_object_set_new(child, "user", user); + + json_object_set_new(user, "username", json_string_unreal(client->user->username)); + if (!BadPtr(client->info)) + json_object_set_new(user, "realname", json_string_unreal(client->info)); + if (client->uplink) + json_object_set_new(user, "servername", json_string_unreal(client->uplink->name)); + if (IsLoggedIn(client)) + json_object_set_new(user, "account", json_string_unreal(client->user->account)); + json_object_set_new(user, "reputation", json_integer(GetReputation(client))); + json_expand_client_security_groups(user, client); + + /* user modes and snomasks */ + get_usermode_string_r(client, buf, sizeof(buf)); + json_object_set_new(user, "modes", json_string_unreal(buf+1)); + if (client->user->snomask) + json_object_set_new(user, "snomasks", json_string_unreal(client->user->snomask)); + + /* if oper then we can possibly expand a bit more */ + str = get_operlogin(client); + if (str) + json_object_set_new(user, "operlogin", json_string_unreal(str)); + str = get_operclass(client); + if (str) + json_object_set_new(user, "operclass", json_string_unreal(str)); + } else + if (IsMe(client)) + { + json_t *server = json_object(); + json_t *features; + + /* client.server */ + json_object_set_new(child, "server", server); + + if (!BadPtr(client->info)) + json_object_set_new(server, "info", json_string_unreal(client->info)); + json_object_set_new(server, "num_users", json_integer(client->server->users)); + json_object_set_new(server, "boot_time", json_timestamp(client->server->boottime)); + } else + if (IsServer(client) && client->server) + { + /* client.server */ + + /* Whenever a server is expanded, which is rare, + * we should probably expand as much as info as possible: + */ + json_t *server = json_object(); + json_t *features; + + /* client.server */ + json_object_set_new(child, "server", server); + if (!BadPtr(client->info)) + json_object_set_new(server, "info", json_string_unreal(client->info)); + if (client->uplink) + json_object_set_new(server, "uplink", json_string_unreal(client->uplink->name)); + json_object_set_new(server, "num_users", json_integer(client->server->users)); + json_object_set_new(server, "boot_time", json_timestamp(client->server->boottime)); + json_object_set_new(server, "synced", json_boolean(client->server->flags.synced)); + + /* client.server.features */ + features = json_object(); + json_object_set_new(server, "features", features); + if (!BadPtr(client->server->features.software)) + json_object_set_new(features, "software", json_string_unreal(client->server->features.software)); + json_object_set_new(features, "protocol", json_integer(client->server->features.protocol)); + if (!BadPtr(client->server->features.usermodes)) + json_object_set_new(features, "usermodes", json_string_unreal(client->server->features.usermodes)); + if (!BadPtr(client->server->features.chanmodes[0])) + { + /* client.server.features.chanmodes (array) */ + int i; + json_t *chanmodes = json_array(); + json_object_set_new(features, "chanmodes", chanmodes); + for (i=0; i < 4; i++) + json_array_append_new(chanmodes, json_string_unreal(client->server->features.chanmodes[i])); + } + if (!BadPtr(client->server->features.nickchars)) + json_object_set_new(features, "nick_character_sets", json_string_unreal(client->server->features.nickchars)); + } +} + +void json_expand_channel(json_t *j, const char *key, Channel *channel, int detail) +{ + char mode1[512], mode2[512], modes[512]; + + json_t *child = json_object(); + json_object_set_new(j, key, child); + json_object_set_new(child, "name", json_string_unreal(channel->name)); + json_object_set_new(child, "creation_time", json_timestamp(channel->creationtime)); + json_object_set_new(child, "num_users", json_integer(channel->users)); + if (channel->topic) + { + json_object_set_new(child, "topic", json_string_unreal(channel->topic)); + json_object_set_new(child, "topic_set_by", json_string_unreal(channel->topic_nick)); + json_object_set_new(child, "topic_set_at", json_timestamp(channel->topic_time)); + } + + /* Add "mode" too */ + channel_modes(NULL, mode1, mode2, sizeof(mode1), sizeof(mode2), channel, 0); + if (*mode2) + { + snprintf(modes, sizeof(modes), "%s %s", mode1+1, mode2); + json_object_set_new(child, "modes", json_string_unreal(modes)); + } else { + json_object_set_new(child, "modes", json_string_unreal(mode1+1)); + } + + // Possibly later: If detail is set to 1 then expand more... +} + +const char *timestamp_iso8601_now(void) +{ + struct timeval t; + struct tm *tm; + time_t sec; + static char buf[64]; + + gettimeofday(&t, NULL); + sec = t.tv_sec; + tm = gmtime(&sec); + + snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec, + (int)(t.tv_usec / 1000)); + + return buf; +} + +const char *timestamp_iso8601(time_t v) +{ + struct tm *tm; + static char buf[64]; + + if (v == 0) + return NULL; + + tm = gmtime(&v); + + if (tm == NULL) + return NULL; + + snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", + tm->tm_year + 1900, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + tm->tm_sec, + 0); + + return buf; +} + +LogData *log_data_string(const char *key, const char *str) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_STRING; + safe_strdup(d->key, key); + safe_strdup(d->value.string, str); + return d; +} + +LogData *log_data_char(const char *key, const char c) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_STRING; + safe_strdup(d->key, key); + d->value.string = safe_alloc(2); + d->value.string[0] = c; + d->value.string[1] = '\0'; + return d; +} + +LogData *log_data_integer(const char *key, int64_t integer) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_INTEGER; + safe_strdup(d->key, key); + d->value.integer = integer; + return d; +} + +LogData *log_data_timestamp(const char *key, time_t ts) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_STRING; + safe_strdup(d->key, key); + safe_strdup(d->value.string, timestamp_iso8601(ts)); + return d; +} + +LogData *log_data_client(const char *key, Client *client) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_CLIENT; + safe_strdup(d->key, key); + d->value.client = client; + return d; +} + +LogData *log_data_channel(const char *key, Channel *channel) +{ + LogData *d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_CHANNEL; + safe_strdup(d->key, key); + d->value.channel = channel; + return d; +} + +LogData *log_data_source(const char *file, int line, const char *function) +{ + LogData *d = safe_alloc(sizeof(LogData)); + json_t *j; + + d->type = LOG_FIELD_OBJECT; + safe_strdup(d->key, "source"); + d->value.object = j = json_object(); + json_object_set_new(j, "file", json_string_unreal(file)); + json_object_set_new(j, "line", json_integer(line)); + json_object_set_new(j, "function", json_string_unreal(function)); + return d; +} + +LogData *log_data_socket_error(int fd) +{ + /* First, grab the error number very early here: */ +#ifndef _WIN32 + int sockerr = errno; +#else + int sockerr = WSAGetLastError(); +#endif + int v; + int len = sizeof(v); + LogData *d; + json_t *j; + +#ifdef SO_ERROR + /* Try to get the "real" error from the underlying socket. + * If we succeed then we will override "sockerr" with it. + */ + if ((fd >= 0) && !getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&v, &len) && v) + sockerr = v; +#endif + + d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_OBJECT; + safe_strdup(d->key, "socket_error"); + d->value.object = j = json_object(); + json_object_set_new(j, "error_code", json_integer(sockerr)); + json_object_set_new(j, "error_string", json_string_unreal(STRERROR(sockerr))); + return d; +} + +/** Populate log with the TLS error(s) stack */ +LogData *log_data_tls_error(void) +{ + LogData *d; + json_t *j; + json_t *error_stack; + json_t *name = NULL; + json_t *jt; + unsigned long e; + char buf[512]; + static char all_errors[8192]; + + d = safe_alloc(sizeof(LogData)); + d->type = LOG_FIELD_OBJECT; + safe_strdup(d->key, "tls_error"); + d->value.object = j = json_object(); + + error_stack = json_array(); + json_object_set_new(j, "error_stack", error_stack); + *all_errors = '\0'; + + do { + json_t *obj; + + e = ERR_get_error(); + if (e == 0) + break; + ERR_error_string_n(e, buf, sizeof(buf)); + + obj = json_object(); + json_object_set_new(obj, "code", json_integer(e)); + json_object_set_new(obj, "string", json_string_unreal(buf)); + json_array_append_new(error_stack, obj); + + if (name == NULL) + { + /* Set tls_error.name to the first error that was encountered */ + json_object_set_new(j, "name", json_string_unreal(buf)); + } + strlcat(all_errors, buf, sizeof(all_errors)); + strlcat(all_errors, "\n", sizeof(all_errors)); + } while(e); + + json_object_set_new(j, "all", json_string_unreal(all_errors)); + + return d; +} + +LogData *log_data_link_block(ConfigItem_link *link) +{ + LogData *d = safe_alloc(sizeof(LogData)); + json_t *j; + char *bind_ip; + + d->type = LOG_FIELD_OBJECT; + safe_strdup(d->key, "link_block"); + d->value.object = j = json_object(); + json_object_set_new(j, "name", json_string_unreal(link->servername)); + json_object_set_new(j, "hostname", json_string_unreal(link->outgoing.hostname)); + json_object_set_new(j, "ip", json_string_unreal(link->connect_ip)); + json_object_set_new(j, "port", json_integer(link->outgoing.port)); + + if (!link->outgoing.bind_ip && iConf.link_bindip) + bind_ip = iConf.link_bindip; + else + bind_ip = link->outgoing.bind_ip; + if (!bind_ip) + bind_ip = "*"; + json_object_set_new(j, "bind_ip", json_string_unreal(bind_ip)); + + return d; +} + +LogData *log_data_tkl(const char *key, TKL *tkl) +{ + char buf[BUFSIZE]; + LogData *d = safe_alloc(sizeof(LogData)); + json_t *j; + + d->type = LOG_FIELD_OBJECT; + safe_strdup(d->key, key); + d->value.object = j = json_object(); + + json_object_set_new(j, "type", json_string_unreal(tkl_type_config_string(tkl))); // Eg 'kline' + json_object_set_new(j, "type_string", json_string_unreal(tkl_type_string(tkl))); // Eg 'Soft K-Line' + json_object_set_new(j, "set_by", json_string_unreal(tkl->set_by)); + json_object_set_new(j, "set_at", json_timestamp(tkl->set_at)); + json_object_set_new(j, "expire_at", json_timestamp(tkl->expire_at)); + *buf = '\0'; + short_date(tkl->set_at, buf); + strlcat(buf, " GMT", sizeof(buf)); + json_object_set_new(j, "set_at_string", json_string_unreal(buf)); + if (tkl->expire_at <= 0) + { + json_object_set_new(j, "expire_at_string", json_string_unreal("Never")); + json_object_set_new(j, "duration_string", json_string_unreal("permanent")); + } else { + *buf = '\0'; + short_date(tkl->expire_at, buf); + strlcat(buf, " GMT", sizeof(buf)); + json_object_set_new(j, "expire_at_string", json_string_unreal(buf)); + json_object_set_new(j, "duration_string", json_string_unreal(pretty_time_val_r(buf, sizeof(buf), tkl->expire_at - tkl->set_at))); + } + json_object_set_new(j, "set_at_delta", json_integer(TStime() - tkl->set_at)); + if (TKLIsServerBan(tkl)) + { + json_object_set_new(j, "name", json_string_unreal(tkl_uhost(tkl, buf, sizeof(buf), 0))); + json_object_set_new(j, "reason", json_string_unreal(tkl->ptr.serverban->reason)); + } else + if (TKLIsNameBan(tkl)) + { + json_object_set_new(j, "name", json_string_unreal(tkl->ptr.nameban->name)); + json_object_set_new(j, "reason", json_string_unreal(tkl->ptr.nameban->reason)); + } else + if (TKLIsBanException(tkl)) + { + json_object_set_new(j, "name", json_string_unreal(tkl_uhost(tkl, buf, sizeof(buf), 0))); + json_object_set_new(j, "reason", json_string_unreal(tkl->ptr.banexception->reason)); + json_object_set_new(j, "exception_types", json_string_unreal(tkl->ptr.banexception->bantypes)); + } else + if (TKLIsSpamfilter(tkl)) + { + json_object_set_new(j, "name", json_string_unreal(tkl->ptr.spamfilter->match->str)); + json_object_set_new(j, "match_type", json_string_unreal(unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type))); + json_object_set_new(j, "ban_action", json_string_unreal(banact_valtostring(tkl->ptr.spamfilter->action))); + json_object_set_new(j, "spamfilter_targets", json_string_unreal(spamfilter_target_inttostring(tkl->ptr.spamfilter->target))); + json_object_set_new(j, "reason", json_string_unreal(unreal_decodespace(tkl->ptr.spamfilter->tkl_reason))); + } + + return d; +} + +void log_data_free(LogData *d) +{ + if (d->type == LOG_FIELD_STRING) + safe_free(d->value.string); + else if ((d->type == LOG_FIELD_OBJECT) && d->value.object) + json_decref(d->value.object); + + safe_free(d->key); + safe_free(d); +} + +const char *log_level_valtostring(LogLevel loglevel) +{ + switch(loglevel) + { + case ULOG_DEBUG: + return "debug"; + case ULOG_INFO: + return "info"; + case ULOG_WARNING: + return "warn"; + case ULOG_ERROR: + return "error"; + case ULOG_FATAL: + return "fatal"; + default: + return NULL; + } +} + +static NameValue log_colors_irc[] = { + { ULOG_INVALID, "\0030,01" }, + { ULOG_DEBUG, "\0030,01" }, + { ULOG_INFO, "\00303" }, + { ULOG_WARNING, "\00307" }, + { ULOG_ERROR, "\00304" }, + { ULOG_FATAL, "\00313" }, +}; + +static NameValue log_colors_terminal[] = { + { ULOG_INVALID, "\033[90m" }, + { ULOG_DEBUG, "\033[37m" }, + { ULOG_INFO, "\033[92m" }, + { ULOG_WARNING, "\033[93m" }, + { ULOG_ERROR, "\033[91m" }, + { ULOG_FATAL, "\033[95m" }, +}; +#define TERMINAL_COLOR_RESET "\033[0m" + +const char *log_level_irc_color(LogLevel loglevel) +{ + return nv_find_by_value(log_colors_irc, loglevel); +} + +const char *log_level_terminal_color(LogLevel loglevel) +{ + return nv_find_by_value(log_colors_terminal, loglevel); +} + +LogLevel log_level_stringtoval(const char *str) +{ + if (!strcmp(str, "info")) + return ULOG_INFO; + if (!strcmp(str, "warn")) + return ULOG_WARNING; + if (!strcmp(str, "error")) + return ULOG_ERROR; + if (!strcmp(str, "fatal")) + return ULOG_FATAL; + if (!strcmp(str, "debug")) + return ULOG_DEBUG; + return ULOG_INVALID; +} + +#define validvarcharacter(x) (isalnum((x)) || ((x) == '_')) +#define valideventidcharacter(x) (isupper((x)) || isdigit((x)) || ((x) == '_')) +#define validsubsystemcharacter(x) (islower((x)) || isdigit((x)) || ((x) == '_') || ((x) == '-')) + +int valid_event_id(const char *s) +{ + if (!*s) + return 0; + for (; *s; s++) + if (!valideventidcharacter(*s)) + return 0; + return 1; +} + +int valid_subsystem(const char *s) +{ + if (!*s) + return 0; + if (log_level_stringtoval(s) != ULOG_INVALID) + return 0; + for (; *s; s++) + if (!validsubsystemcharacter(*s)) + return 0; + return 1; +} + +const char *json_get_value(json_t *t) +{ + static char buf[32]; + + if (json_is_string(t)) + return json_string_value(t); + + if (json_is_integer(t)) + { + snprintf(buf, sizeof(buf), "%lld", (long long)json_integer_value(t)); + return buf; + } + + return NULL; +} + +// TODO: if in the function below we keep adding auto expanshion shit, +// like we currently have $client automatically expanding to $client.name +// and $socket_error to $socket_error.error_string, +// if this gets more than we should use some kind of array for it, +// especially for the hardcoded name shit like $socket_error. + +/** Build a string and replace $variables where needed. + * See src/modules/blacklist.c for an example. + * @param inbuf The input string + * @param outbuf The output string + * @param len The maximum size of the output string (including NUL) + * @param name Array of variables names + * @param value Array of variable values + */ +void buildlogstring(const char *inbuf, char *outbuf, size_t len, json_t *details) +{ + const char *i, *p; + char *o; + int left = len - 1; + int cnt, found; + char varname[256], *varp, *varpp; + json_t *t; + +#ifdef DEBUGMODE + if (len <= 0) + abort(); +#endif + + for (i = inbuf, o = outbuf; *i; i++) + { + if (*i == '$') + { + i++; + + /* $$ = literal $ */ + if (*i == '$') + goto literal; + + if (!validvarcharacter(*i)) + { + /* What do we do with things like '$/' ? -- treat literal */ + i--; + goto literal; + } + + /* find termination */ + for (p=i; validvarcharacter(*p) || ((*p == '.') && validvarcharacter(p[1])); p++); + + /* find variable name in list */ + strlncpy(varname, i, sizeof(varname), p - i); + varp = strchr(varname, '.'); + if (varp) + *varp = '\0'; + t = json_object_get(details, varname); + if (t) + { + const char *output = NULL; + if (varp) + { + char *varpp; + do { + varpp = strchr(varp+1, '.'); + if (varpp) + *varpp = '\0'; + /* Fetch explicit object.key */ + t = json_object_get(t, varp+1); + varp = varpp; + } while(t && varpp); + if (t) + output = json_get_value(t); + } else + if (!strcmp(varname, "socket_error")) + { + /* Fetch socket_error.error_string */ + t = json_object_get(t, "error_string"); + if (t) + output = json_get_value(t); + } else + if (json_is_object(t)) + { + /* Fetch object.name */ + t = json_object_get(t, "name"); + if (t) + output = json_get_value(t); + } else + { + output = json_get_value(t); + } + if (output) + { + strlcpy(o, output, left); + left -= strlen(output); /* may become <0 */ + if (left <= 0) + return; /* return - don't write \0 to 'o'. ensured by strlcpy already */ + o += strlen(output); /* value entirely written */ + } + } else + { + /* variable name does not exist -- treat as literal string */ + i--; + goto literal; + } + + /* value written. we're done. */ + i = p - 1; + continue; + } +literal: + if (!left) + break; + *o++ = *i; + left--; + if (!left) + break; + } + *o = '\0'; +} + +/** 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) +{ + static int last_log_file_warning = 0; + Log *l; + char timebuf[128]; + struct stat fstats; + int n; + int write_error; + long snomask; + MultiLine *m; + + snprintf(timebuf, sizeof(timebuf), "[%s] ", myctime(TStime())); + + RunHook(HOOKTYPE_LOG, loglevel, subsystem, event_id, msg, json_serialized, timebuf); + + if (!loop.forked && (loglevel > ULOG_DEBUG)) + { + for (m = msg; m; m = m->next) + { +#ifdef _WIN32 + if (show_event_id_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) + { + fprintf(stderr, "%s%s.%s%s %s[%s]%s %s\n", + log_level_terminal_color(ULOG_INVALID), subsystem, event_id, TERMINAL_COLOR_RESET, + log_level_terminal_color(loglevel), log_level_valtostring(loglevel), TERMINAL_COLOR_RESET, + m->line); + } else { + fprintf(stderr, "%s[%s]%s %s\n", + log_level_terminal_color(loglevel), log_level_valtostring(loglevel), TERMINAL_COLOR_RESET, + m->line); + } + } else { + if (show_event_id_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); + } +#endif + } + } + + /* In case of './unrealircd configtest': don't write to log file, only to stderr */ + if (loop.config_test) + return; + + for (l = logs[LOG_DEST_DISK]; l; l = l->next) + { + if (!log_sources_match(l->sources, loglevel, subsystem, event_id, 0)) + continue; + +#ifdef HAVE_SYSLOG + if (l->file && !strcasecmp(l->file, "syslog")) + { + if (l->type == LOG_TYPE_JSON) + { + syslog(LOG_INFO, "%s", json_serialized); + } else + if (l->type == LOG_TYPE_TEXT) + { + for (m = msg; m; m = m->next) + syslog(LOG_INFO, "%s.%s%s %s: %s", subsystem, event_id, m->next?"+":"", log_level_valtostring(loglevel), m->line); + } + continue; + } +#endif + + /* This deals with dynamic log file names, such as ircd.%Y-%m-%d.log */ + if (l->filefmt) + { + char *fname = unreal_strftime(l->filefmt); + if (l->file && (l->logfd != -1) && strcmp(l->file, fname)) + { + /* We are logging already and need to switch over */ + fd_close(l->logfd); + l->logfd = -1; + } + safe_strdup(l->file, fname); + } + + /* log::maxsize code */ + if (l->maxsize && (stat(l->file, &fstats) != -1) && fstats.st_size >= l->maxsize) + { + char oldlog[512]; + if (l->logfd == -1) + { + /* Try to open, so we can write the 'Max file size reached' message. */ + l->logfd = fd_fileopen(l->file, O_CREAT|O_APPEND|O_WRONLY); + } + if (l->logfd != -1) + { + if (write(l->logfd, "Max file size reached, starting new log file\n", 45) < 0) + { + /* We already handle the unable to write to log file case for normal data. + * I think we can get away with not handling this one. + */ + ; + } + fd_close(l->logfd); + } + l->logfd = -1; + + /* Rename log file to xxxxxx.old */ + snprintf(oldlog, sizeof(oldlog), "%s.old", l->file); + unlink(oldlog); /* windows rename cannot overwrite, so unlink here.. ;) */ + rename(l->file, oldlog); + } + + /* generic code for opening log if not open yet.. */ + if (l->logfd == -1) + { + l->logfd = fd_fileopen(l->file, O_CREAT|O_APPEND|O_WRONLY); + if (l->logfd == -1) + { + if (!loop.booted) + { + config_status("WARNING: Unable to write to '%s': %s", l->file, strerror(errno)); + } else { + if (last_log_file_warning + 300 < TStime()) + { + config_status("WARNING: Unable to write to '%s': %s. This warning will not re-appear for at least 5 minutes.", l->file, strerror(errno)); + last_log_file_warning = TStime(); + } + } + continue; + } + } + + /* Now actually WRITE to the log... */ + write_error = 0; + if ((l->type == LOG_TYPE_JSON) && strcmp(subsystem, "rawtraffic")) + { + n = write(l->logfd, json_serialized, strlen(json_serialized)); + if (n < strlen(json_serialized)) + { + write_error = 1; + } else { + if (write(l->logfd, "\n", 1) < 1) // FIXME: no.. we should do it this way..... and why do we use direct I/O at all? + write_error = 1; + } + } else + if (l->type == LOG_TYPE_TEXT) + { + 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... */ + ; + } + n = write(l->logfd, text_buf, strlen(text_buf)); + if (n < strlen(text_buf)) + { + write_error = 1; + break; + } + } + } + + if (write_error) + { + if (!loop.booted) + { + config_status("WARNING: Unable to write to '%s': %s", l->file, strerror(errno)); + } else { + if (last_log_file_warning + 300 < TStime()) + { + config_status("WARNING: Unable to write to '%s': %s. This warning will not re-appear for at least 5 minutes.", l->file, strerror(errno)); + last_log_file_warning = TStime(); + } + } + } + } +} + +int log_sources_match(LogSource *logsource, LogLevel loglevel, const char *subsystem, const char *event_id, int matched_already) +{ + int retval = 0; + LogSource *ls; + + // NOTE: This routine works by exclusion, so a bad struct would + // cause everything to match!! + + for (ls = logsource; ls; ls = ls->next) + { + /* First deal with all positive matchers.. */ + if (ls->negative) + continue; + if (!strcmp(ls->subsystem, "all")) + { + retval = 1; + break; + } + if (!strcmp(ls->subsystem, "nomatch") && !matched_already) + { + /* catch-all */ + retval = 1; + break; + } + if (*ls->event_id && strcmp(ls->event_id, event_id)) + continue; + if (*ls->subsystem && strcmp(ls->subsystem, subsystem)) + continue; + if ((ls->loglevel != ULOG_INVALID) && (ls->loglevel != loglevel)) + continue; + /* MATCH */ + retval = 1; + break; + } + + /* No matches? Then we can stop here */ + if (retval == 0) + return 0; + + /* There was a match, now check for exemptions, eg !operoverride */ + for (ls = logsource; ls; ls = ls->next) + { + /* Only deal with negative matches... */ + if (!ls->negative) + continue; + if (!strcmp(ls->subsystem, "nomatch") || !strcmp(ls->subsystem, "all")) + continue; /* !nomatch and !all make no sense, so just ignore it */ + if (*ls->event_id && strcmp(ls->event_id, event_id)) + continue; + if (*ls->subsystem && strcmp(ls->subsystem, subsystem)) + continue; + if ((ls->loglevel != ULOG_INVALID) && (ls->loglevel != loglevel)) + continue; + /* NEGATIVE MATCH */ + return 0; + } + + return 1; +} + +/** Convert loglevel/subsystem/event_id to a snomask. + * @returns The snomask letters (may be more than one), + * an asterisk (for all ircops), or NULL (no delivery) + */ +const char *log_to_snomask(LogLevel loglevel, const char *subsystem, const char *event_id) +{ + Log *ld; + static char snomasks[64]; + int matched = 0; + + *snomasks = '\0'; + for (ld = logs[LOG_DEST_SNOMASK]; ld; ld = ld->next) + { + if (log_sources_match(ld->sources, loglevel, subsystem, event_id, 0)) + { + strlcat(snomasks, ld->destination, sizeof(snomasks)); + matched = 1; + } + } + + if (logs[LOG_DEST_OPER] && log_sources_match(logs[LOG_DEST_OPER]->sources, loglevel, subsystem, event_id, matched)) + strlcat(snomasks, "s", sizeof(snomasks)); + + return *snomasks ? snomasks : NULL; +} + +#define COLOR_NONE "\xf" +#define COLOR_DARKGREY "\00314" +/** Do the actual writing to log files */ +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) + return; + + /* Never send these */ + if (!strcmp(subsystem, "rawtraffic")) + return; + + snomask_destinations = log_to_snomask(loglevel, subsystem, event_id); + 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; + + if (snomask_destinations) + { + char found = 0; + if (!client->user->snomask) + continue; /* None set, so will never match */ + for (p = snomask_destinations; *p; p++) + { + if (strchr(client->user->snomask, *p)) + { + found = 1; + break; + } + } + if (!found) + continue; + } + + 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 */ + } + } + + 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) +{ + Log *l; + int found = 0; + + for (l = logs[LOG_DEST_REMOTE]; l; l = l->next) + { + if (log_sources_match(l->sources, loglevel, subsystem, event_id, 0)) + { + found = 1; + break; + } + } + if (found == 0) + return; + + do_unreal_log_remote_deliver(loglevel, subsystem, event_id, msg, json_serialized); +} + +void do_unreal_log_free_args(va_list vl) +{ + LogData *d; + + while ((d = va_arg(vl, LogData *))) + { + log_data_free(d); + } +} + +static int unreal_log_recursion_trap = 0; + +/* Logging function, called by the unreal_log() macro. */ +void do_unreal_log(LogLevel loglevel, const char *subsystem, const char *event_id, + Client *client, const char *msg, ...) +{ + va_list vl; + + if (unreal_log_recursion_trap) + { + va_start(vl, msg); + do_unreal_log_free_args(vl); + va_end(vl); + return; + } + + unreal_log_recursion_trap = 1; + va_start(vl, msg); + do_unreal_log_internal(loglevel, subsystem, event_id, client, 1, msg, vl); + va_end(vl); + unreal_log_recursion_trap = 0; +} + +/* Logging function, called by the unreal_log_raw() macro. */ +void do_unreal_log_raw(LogLevel loglevel, const char *subsystem, const char *event_id, + Client *client, const char *msg, ...) +{ + va_list vl; + + if (unreal_log_recursion_trap) + { + va_start(vl, msg); + do_unreal_log_free_args(vl); + va_end(vl); + return; + } + + unreal_log_recursion_trap = 1; + va_start(vl, msg); + do_unreal_log_internal(loglevel, subsystem, event_id, client, 0, msg, vl); + va_end(vl); + unreal_log_recursion_trap = 0; +} + +void do_unreal_log_norecursioncheck(LogLevel loglevel, const char *subsystem, const char *event_id, + Client *client, const char *msg, ...) +{ + va_list vl; + + va_start(vl, msg); + do_unreal_log_internal(loglevel, subsystem, event_id, client, 1, msg, vl); + va_end(vl); +} + +void do_unreal_log_internal(LogLevel loglevel, const char *subsystem, const char *event_id, + Client *client, int expand_msg, const char *msg, va_list vl) +{ + LogData *d; + char *json_serialized; + const char *str; + json_t *j = NULL; + json_t *j_details = NULL; + json_t *t; + char msgbuf[8192]; + const char *loglevel_string = log_level_valtostring(loglevel); + MultiLine *mmsg; + Client *from_server = NULL; + + if (loglevel_string == NULL) + { + do_unreal_log_norecursioncheck(ULOG_ERROR, "log", "BUG_LOG_LOGLEVEL", NULL, + "[BUG] Next log message had an invalid log level -- corrected to ULOG_ERROR", + NULL); + loglevel = ULOG_ERROR; + loglevel_string = log_level_valtostring(loglevel); + } + if (!valid_subsystem(subsystem)) + { + do_unreal_log_norecursioncheck(ULOG_ERROR, "log", "BUG_LOG_SUBSYSTEM", NULL, + "[BUG] Next log message had an invalid subsystem -- changed to 'unknown'", + NULL); + subsystem = "unknown"; + } + if (!valid_event_id(event_id)) + { + do_unreal_log_norecursioncheck(ULOG_ERROR, "log", "BUG_LOG_EVENT_ID", NULL, + "[BUG] Next log message had an invalid event id -- changed to 'unknown'", + NULL); + event_id = "unknown"; + } + /* This one is probably temporary since it should not be a real error, actually (but often is) */ + if (expand_msg && strchr(msg, '%')) + { + do_unreal_log_norecursioncheck(ULOG_ERROR, "log", "BUG_LOG_MESSAGE_PERCENT", NULL, + "[BUG] Next log message contains a percent sign -- possibly accidental format string!", + NULL); + } + + j = json_object(); + j_details = json_object(); + + json_object_set_new(j, "timestamp", json_string_unreal(timestamp_iso8601_now())); + json_object_set_new(j, "level", json_string_unreal(loglevel_string)); + json_object_set_new(j, "subsystem", json_string_unreal(subsystem)); + json_object_set_new(j, "event_id", json_string_unreal(event_id)); + json_object_set_new(j, "log_source", json_string_unreal(*me.name ? me.name : "local")); + + /* We put all the rest in j_details because we want to enforce + * a certain ordering of the JSON output. We will merge these + * details later on. + */ + if (client) + json_expand_client(j_details, "client", client, 0); + /* Additional details (if any) */ + while ((d = va_arg(vl, LogData *))) + { + switch(d->type) + { + case LOG_FIELD_INTEGER: + json_object_set_new(j_details, d->key, json_integer(d->value.integer)); + break; + case LOG_FIELD_STRING: + if (d->value.string) + json_object_set_new(j_details, d->key, json_string_unreal(d->value.string)); + else + json_object_set_new(j_details, d->key, json_null()); + break; + case LOG_FIELD_CLIENT: + json_expand_client(j_details, d->key, d->value.client, 0); + break; + case LOG_FIELD_CHANNEL: + json_expand_channel(j_details, d->key, d->value.channel, 0); + break; + case LOG_FIELD_OBJECT: + json_object_set_new(j_details, d->key, d->value.object); + d->value.object = NULL; /* don't let log_data_free() free it */ + break; + default: +#ifdef DEBUGMODE + abort(); +#endif + break; + } + log_data_free(d); + } + + if (expand_msg) + buildlogstring(msg, msgbuf, sizeof(msgbuf), j_details); + else + strlcpy(msgbuf, msg, sizeof(msgbuf)); + + json_object_set_new(j, "msg", json_string_unreal(msgbuf)); + + /* Now merge the details into root object 'j': */ + json_object_update_missing(j, j_details); + /* Generate the JSON */ + json_serialized = json_dumps(j, JSON_COMPACT); + + /* 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 */ + 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; + do_unreal_log_opers(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! + + /* Free everything */ + safe_free(json_serialized); + safe_free_multiline(mmsg); + json_decref(j_details); + json_decref(j); +} + +void do_unreal_log_internal_from_remote(LogLevel loglevel, const char *subsystem, const char *event_id, + MultiLine *msg, const char *json_serialized, Client *from_server) +{ + if (unreal_log_recursion_trap) + return; + unreal_log_recursion_trap = 1; + + /* Call the disk loggers */ + do_unreal_log_disk(loglevel, subsystem, event_id, msg, json_serialized); + + /* And the ircops stuff */ + do_unreal_log_opers(loglevel, subsystem, event_id, msg, json_serialized, from_server); + + unreal_log_recursion_trap = 0; +} + + +void free_log_block(Log *l) +{ + Log *l_next; + LogSource *src, *src_next; + for (; l; l = l_next) + { + l_next = l->next; + if (l->logfd > 0) + { + fd_close(l->logfd); + l->logfd = -1; + } + free_log_sources(l->sources); + safe_free(l->file); + safe_free(l->filefmt); + safe_free(l); + } +} + +int log_tests(void) +{ + if (snomask_num_destinations <= 1) + { + unreal_log(ULOG_ERROR, "config", "LOG_SNOMASK_BLOCK_MISSING", NULL, + "Missing snomask logging configuration:\n" + "Please add the following line to your unrealircd.conf: " + "include \"snomasks.default.conf\";"); + return 0; + } + snomask_num_destinations = 0; + return 1; +} + +void postconf_defaults_log_block(void) +{ + Log *l; + LogSource *ls; + + /* Is there any log block to disk? Then nothing to do. */ + if (logs[LOG_DEST_DISK]) + return; + + unreal_log(ULOG_WARNING, "log", "NO_DISK_LOG_BLOCK", NULL, + "No log { } block found that logs to disk -- " + "logging everything in text format to 'ircd.log'"); + + /* Create a default log block */ + l = safe_alloc(sizeof(Log)); + l->logfd = -1; + l->type = LOG_TYPE_TEXT; /* text */ + l->maxsize = 100000000; /* maxsize 100M */ + safe_strdup(l->file, "ircd.log"); + convert_to_absolute_path(&l->file, LOGDIR); + AddListItem(l, logs[LOG_DEST_DISK]); + + /* And the source filter */ + ls = add_log_source("all"); + AppendListItem(ls, l->sources); + ls = add_log_source("!debug"); + AppendListItem(ls, l->sources); + ls = add_log_source("!join.LOCAL_CLIENT_JOIN"); + AppendListItem(ls, l->sources); + ls = add_log_source("!join.REMOTE_CLIENT_JOIN"); + AppendListItem(ls, l->sources); + ls = add_log_source("!part.LOCAL_CLIENT_PART"); + AppendListItem(ls, l->sources); + ls = add_log_source("!part.REMOTE_CLIENT_PART"); + AppendListItem(ls, l->sources); + ls = add_log_source("!kick.LOCAL_CLIENT_KICK"); + AppendListItem(ls, l->sources); + ls = add_log_source("!kick.REMOTE_CLIENT_KICK"); + AppendListItem(ls, l->sources); +} + +/* Called before CONFIG_TEST */ +void log_pre_rehash(void) +{ + *snomasks_in_use_testing = '\0'; +} + +/* Called after CONFIG_TEST right before CONFIG_RUN */ +void config_pre_run_log(void) +{ + *snomasks_in_use = '\0'; +} + +/* Called after CONFIG_RUN is complete */ +void log_blocks_switchover(void) +{ + int i; + for (i=0; i < NUM_LOG_DESTINATIONS; i++) + free_log_block(logs[i]); + memcpy(logs, temp_logs, sizeof(logs)); + memset(temp_logs, 0, sizeof(temp_logs)); +} + +/** Check if a letter is a valid snomask (that is: + * one that exists in the log block configuration). + * @param c the snomask letter to check + * @returns 1 if exists, 0 if not. + */ +int is_valid_snomask(char c) +{ + return strchr(snomasks_in_use, c) ? 1 : 0; +} + +/** Check if a letter is a valid snomask during or after CONFIG_TEST + * (the snomasks exist in the log block configuration read during config_test). + * @param c the snomask letter to check + * @returns 1 if exists, 0 if not. + */ +int is_valid_snomask_testing(char c) +{ + return strchr(snomasks_in_use_testing, c) ? 1 : 0; +} + +/** Check if a string all consists of valid snomasks during or after CONFIG_TEST + * (the snomasks exist in the log block configuration read during config_test). + * @param str the snomask string to check + * @param invalid_snomasks list of unknown snomask letters + * @returns 1 if exists, 0 if not. + */ +int is_valid_snomask_string_testing(const char *str, char **invalid_snomasks) +{ + static char invalid_snomasks_buf[256]; + + *invalid_snomasks_buf = '\0'; + for (; *str; str++) + { + if ((*str == '+') || (*str == '-')) + continue; + if (!strchr(snomasks_in_use_testing, *str)) + strlcat_letter(invalid_snomasks_buf, *str, sizeof(invalid_snomasks_buf)); + } + *invalid_snomasks = invalid_snomasks_buf; + return *invalid_snomasks_buf ? 0 : 1; +} diff --git a/src/macosx/UnrealIRCd/AppModel.swift b/src/macosx/UnrealIRCd/AppModel.swift index 7cb4570..5fff8ba 100644 --- a/src/macosx/UnrealIRCd/AppModel.swift +++ b/src/macosx/UnrealIRCd/AppModel.swift @@ -13,7 +13,7 @@ class AppModel : ChangeNotifierDelegate { var menuItem : NSStatusItem static let logoName = "logo.png" - static let helpURL = "https://www.unrealircd.org/docs/UnrealIRCd_5_documentation" + static let helpURL = "https://www.unrealircd.org/docs/UnrealIRCd_6_documentation" var daemonModel : DaemonModel var configurationModel : ConfigurationModel var windowController : NSWindowController? diff --git a/src/match.c b/src/match.c index ccc1c39..ca361b0 100644 --- a/src/match.c +++ b/src/match.c @@ -384,7 +384,7 @@ void unreal_delete_match(Match *m) safe_free(m); } -Match *unreal_create_match(MatchType type, char *str, char **error) +Match *unreal_create_match(MatchType type, const char *str, char **error) { Match *m = safe_alloc(sizeof(Match)); static char errorbuf[512]; @@ -438,7 +438,7 @@ Match *unreal_create_match(MatchType type, char *str, char **error) * @returns 1 if matched, 0 if not. * @note These (more logical) return values are opposite to the match_simple() function. */ -int unreal_match(Match *m, char *str) +int unreal_match(Match *m, const char *str) { if (m->type == MATCH_SIMPLE) { @@ -463,7 +463,7 @@ int unreal_match(Match *m, char *str) return 0; } -int unreal_match_method_strtoval(char *str) +int unreal_match_method_strtoval(const char *str) { if (!strcmp(str, "regex") || !strcmp(str, "pcre")) return MATCH_PCRE_REGEX; @@ -489,10 +489,11 @@ char *unreal_match_method_valtostr(int val) * Moved here from the censor channel and user mode module * (previously was present in both modules, code duplication) */ -int fast_badword_match(ConfigItem_badword *badword, char *line) +int fast_badword_match(ConfigItem_badword *badword, const char *line) { - char *p; + const char *p; int bwlen = strlen(badword->word); + if ((badword->type & BADW_TYPE_FAST_L) && (badword->type & BADW_TYPE_FAST_R)) return (our_strcasestr(line, badword->word) ? 1 : 0); @@ -523,21 +524,20 @@ next: * buf is used for the result and max is sizeof(buf). * Assumptions[!]: max > 0 AND max > strlen(line)+1 */ -int fast_badword_replace(ConfigItem_badword *badword, char *line, char *buf, int max) +int fast_badword_replace(ConfigItem_badword *badword, const char *line, char *buf, int max) { /* Some aliases ;P */ char *replacew = badword->replace ? badword->replace : REPLACEWORD; - char *pold = line, *pnew = buf; /* Pointers to old string and new string */ - char *poldx = line; + const char *pold = line; /* pointer to the old string */ + const char *poldx = line; + char *pnew = buf; /* pointer to the new string */ int replacen = -1; /* Only calculated if needed. w00t! saves us a few nanosecs? lol */ int searchn = -1; - char *startw, *endw; + const char *startw, *endw; /* start and end of the word */ char *c_eol = buf + max - 1; /* Cached end of (new) line */ int run = 1; int cleaned = 0; - Debug((DEBUG_NOTICE, "replacing %s -> %s in '%s'", badword->word, replacew, line)); - while(run) { pold = our_strcasestr(pold, badword->word); if (!pold) @@ -617,7 +617,7 @@ int fast_badword_replace(ConfigItem_badword *badword, char *line, char *buf, int * the loadbadwords() function. It's primary use is to filter swearing * in both private and public messages */ -char *stripbadwords(char *str, ConfigItem_badword *start_bw, int *blocked) +const char *stripbadwords(const char *str, ConfigItem_badword *start_bw, int *blocked) { static char cleanstr[4096]; char buf[4096]; @@ -692,14 +692,17 @@ char *stripbadwords(char *str, ConfigItem_badword *start_bw, int *blocked) ret = pcre2_match(this_word->pcre2_expr, ptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL); /* run the regex */ if (ret > 0) { - ircd_log(LOG_ERROR, "pcre2_get_ovector_count: %d", pcre2_get_ovector_count(md)); dd = pcre2_get_ovector_pointer(md); start = (int)dd[0]; end = (int)dd[1]; if ((start < 0) || (end < 0) || (start > strlen(ptr)) || (end > strlen(ptr)+1)) { - ircd_log(LOG_ERROR, "pcre2_match() returned an ovector with OOB start/end: %d/%d, str (%d): '%s'", - (int)start, (int)end, (int)strlen(ptr), ptr); + unreal_log(ULOG_FATAL, "main", "BUG_STRIPBADWORDS_PCRE2_MATCH_OOB", NULL, + "[BUG] pcre2_match() returned an ovector with OOB start/end: $start/$end, len $length: '$buf'", + log_data_integer("start", start), + log_data_integer("end", end), + log_data_integer("length", strlen(ptr)), + log_data_string("buf", ptr)); abort(); } m = end - start; @@ -743,10 +746,10 @@ char *stripbadwords(char *str, ConfigItem_badword *start_bw, int *blocked) * if check_broadness is 1, the function will attempt to determine * if the given regex string is too broad (i.e. matches everything) */ -char *badword_config_check_regex(char *str, int fastsupport, int check_broadness) +const char *badword_config_check_regex(const char *str, int fastsupport, int check_broadness) { int regex=0; - char *tmp; + const char *tmp; static char errorbuf[512]; if (fastsupport) @@ -787,9 +790,9 @@ char *badword_config_check_regex(char *str, int fastsupport, int check_broadness return NULL; } -int badword_config_process(ConfigItem_badword *ca, char *str) +int badword_config_process(ConfigItem_badword *ca, const char *str) { - char *tmp; + const char *tmp; short regex = 0; int ast_l = 0, ast_r = 0; diff --git a/src/misc.c b/src/misc.c index e8a1ee5..27c4f36 100644 --- a/src/misc.c +++ b/src/misc.c @@ -32,17 +32,26 @@ static void exit_one_client(Client *, MessageTag *mtags_i, const char *); -static char *months[] = { +static const char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; -static char *weekdays[] = { +static const char *weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; +static const char *short_months[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", +}; + +static const char *short_weekdays[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", +}; + typedef struct { int value; /** Unique integer value of item */ char character; /** Unique character assigned to item */ @@ -98,167 +107,8 @@ SpamfilterTargetTable spamfiltertargettable[] = { /** IRC Statistics (quite useless?) */ struct IRCStatistics ircstats; -/** Main IRCd logging function. - * @param flags One of LOG_* (eg: LOG_ERROR) - * @param format Format string - * @param ... Arguments - * @note This function is safe to call at all times. It provides - * protection against recursion. - */ -void ircd_log(int flags, FORMAT_STRING(const char *format), ...) -{ - static int last_log_file_warning = 0; - static char recursion_trap=0; - - va_list ap; - ConfigItem_log *logs; - char buf[2048], timebuf[128]; - struct stat fstats; - int written = 0; - int n; - - /* Trap infinite recursions to avoid crash if log file is unavailable, - * this will also avoid calling ircd_log from anything else called - */ - if (recursion_trap == 1) - return; - - recursion_trap = 1; - - /* NOTE: past this point you CANNOT just 'return'. - * You must set 'recursion_trap = 0;' before 'return'! - */ - - va_start(ap, format); - ircvsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); - snprintf(timebuf, sizeof(timebuf), "[%s] - ", myctime(TStime())); - - RunHook3(HOOKTYPE_LOG, flags, timebuf, buf); - strlcat(buf, "\n", sizeof(buf)); - - if (!loop.ircd_forked && (flags & LOG_ERROR)) - { -#ifdef _WIN32 - win_log("* %s", buf); -#else - fprintf(stderr, "%s", buf); -#endif - } - - /* In case of './unrealircd configtest': don't write to log file, only to stderr */ - if (loop.config_test) - { - recursion_trap = 0; - return; - } - - for (logs = conf_log; logs; logs = logs->next) - { - if (!(logs->flags & flags)) - continue; - -#ifdef HAVE_SYSLOG - if (logs->file && !strcasecmp(logs->file, "syslog")) - { - syslog(LOG_INFO, "%s", buf); - written++; - continue; - } -#endif - - /* This deals with dynamic log file names, such as ircd.%Y-%m-%d.log */ - if (logs->filefmt) - { - char *fname = unreal_strftime(logs->filefmt); - if (logs->file && (logs->logfd != -1) && strcmp(logs->file, fname)) - { - /* We are logging already and need to switch over */ - fd_close(logs->logfd); - logs->logfd = -1; - } - safe_strdup(logs->file, fname); - } - - /* log::maxsize code */ - if (logs->maxsize && (stat(logs->file, &fstats) != -1) && fstats.st_size >= logs->maxsize) - { - char oldlog[512]; - if (logs->logfd == -1) - { - /* Try to open, so we can write the 'Max file size reached' message. */ - logs->logfd = fd_fileopen(logs->file, O_CREAT|O_APPEND|O_WRONLY); - } - if (logs->logfd != -1) - { - if (write(logs->logfd, "Max file size reached, starting new log file\n", 45) < 0) - { - /* We already handle the unable to write to log file case for normal data. - * I think we can get away with not handling this one. - */ - ; - } - fd_close(logs->logfd); - } - logs->logfd = -1; - - /* Rename log file to xxxxxx.old */ - snprintf(oldlog, sizeof(oldlog), "%s.old", logs->file); - unlink(oldlog); /* windows rename cannot overwrite, so unlink here.. ;) */ - rename(logs->file, oldlog); - } - - /* generic code for opening log if not open yet.. */ - if (logs->logfd == -1) - { - logs->logfd = fd_fileopen(logs->file, O_CREAT|O_APPEND|O_WRONLY); - if (logs->logfd == -1) - { - if (!loop.ircd_booted) - { - config_status("WARNING: Unable to write to '%s': %s", logs->file, strerror(ERRNO)); - } else { - if (last_log_file_warning + 300 < TStime()) - { - config_status("WARNING: Unable to write to '%s': %s. This warning will not re-appear for at least 5 minutes.", logs->file, strerror(ERRNO)); - last_log_file_warning = TStime(); - } - } - continue; - } - } - - /* Now actually WRITE to the log... */ - if (write(logs->logfd, timebuf, strlen(timebuf)) < 0) - { - /* Let's ignore any write errors for this one. Next write() will catch it... */ - ; - } - n = write(logs->logfd, buf, strlen(buf)); - if (n == strlen(buf)) - { - written++; - } - else - { - if (!loop.ircd_booted) - { - config_status("WARNING: Unable to write to '%s': %s", logs->file, strerror(ERRNO)); - } else { - if (last_log_file_warning + 300 < TStime()) - { - config_status("WARNING: Unable to write to '%s': %s. This warning will not re-appear for at least 5 minutes.", logs->file, strerror(ERRNO)); - last_log_file_warning = TStime(); - } - } - } - } - - recursion_trap = 0; -} - /** Returns the date in rather long string */ -char *long_date(time_t clock) +const char *long_date(time_t clock) { static char buf[80], plus; struct tm *lt, *gm; @@ -300,10 +150,9 @@ char *long_date(time_t clock) * @param buf The buffer to store the string (minimum size: 128 bytes), * or NULL to use temporary static storage. */ -char *short_date(time_t ts, char *buf) +const char *short_date(time_t ts, char *buf) { struct tm *t = gmtime(&ts); - char *timestr; static char retbuf[128]; if (!buf) @@ -313,17 +162,14 @@ char *short_date(time_t ts, char *buf) if (!t) return NULL; - timestr = asctime(t); - if (!timestr) + if (!strftime(buf, 128, "%a %b %d %H:%M:%S %Y", t)) return NULL; - strlcpy(buf, timestr, 128); - stripcrlf(buf); return buf; } /** Return a string with the "pretty date" - yeah, another variant */ -char *pretty_date(time_t t) +const char *pretty_date(time_t t) { static char buf[128]; struct tm *tm; @@ -347,62 +193,53 @@ char *pretty_date(time_t t) * string marker (`\-`). returns the 'fixed' string or "*" if the string * was NULL length or a NULL pointer. */ -char *check_string(char *s) +const char *check_string(const char *s) { + static char buf[512]; static char star[2] = "*"; - char *str = s; + const char *str = s; if (BadPtr(s)) return star; for (; *s; s++) + { if (isspace(*s)) { - *s = '\0'; + /* Because this is an unlikely scenario, we have + * delayed the copy until here: + */ + strlncpy(buf, s, sizeof(buf), s - str); + str = buf; break; } + } return (BadPtr(str)) ? star : str; } /** Create a user@host based on the provided name and host */ -char *make_user_host(char *name, char *host) +char *make_user_host(const char *name, const char *host) { static char namebuf[USERLEN + HOSTLEN + 6]; - char *s = namebuf; - memset(namebuf, 0, sizeof(namebuf)); - name = check_string(name); - strlcpy(s, name, USERLEN + 1); - s += strlen(s); - *s++ = '@'; - host = check_string(host); - strlcpy(s, host, HOSTLEN + 1); - s += strlen(s); - *s = '\0'; - return (namebuf); + strlncpy(namebuf, check_string(name), sizeof(namebuf), USERLEN+1); + strlcat(namebuf, "@", sizeof(namebuf)); + strlncat(namebuf, check_string(host), sizeof(namebuf), HOSTLEN+1); + return namebuf; } /** Create a nick!user@host string based on the provided variables. * If any of the variables are NULL, it becomes * (asterisk) * This is the reentrant safe version. */ -char *make_nick_user_host_r(char *namebuf, char *nick, char *name, char *host) +char *make_nick_user_host_r(char *namebuf, size_t namebuflen, const char *nick, const char *name, const char *host) { - char *s = namebuf; - - nick = check_string(nick); - strlcpy(namebuf, nick, NICKLEN + 1); - s += strlen(s); - *s++ = '!'; - name = check_string(name); - strlcpy(s, name, USERLEN + 1); - s += strlen(s); - *s++ = '@'; - host = check_string(host); - strlcpy(s, host, HOSTLEN + 1); - s += strlen(s); - *s = '\0'; + strlncpy(namebuf, check_string(nick), namebuflen, NICKLEN+1); + strlcat(namebuf, "!", namebuflen); + strlncat(namebuf, check_string(name), namebuflen, USERLEN+1); + strlcat(namebuf, "@", namebuflen); + strlncat(namebuf, check_string(host), namebuflen, HOSTLEN+1); return namebuf; } @@ -410,18 +247,18 @@ char *make_nick_user_host_r(char *namebuf, char *nick, char *name, char *host) * If any of the variables are NULL, it becomes * (asterisk) * This version uses static storage. */ -char *make_nick_user_host(char *nick, char *name, char *host) +char *make_nick_user_host(const char *nick, const char *name, const char *host) { static char namebuf[NICKLEN + USERLEN + HOSTLEN + 24]; - return make_nick_user_host_r(namebuf, nick, name, host); + return make_nick_user_host_r(namebuf, sizeof(namebuf), nick, name, host); } /** Similar to ctime() but without a potential newline and * also takes a time_t value rather than a pointer. */ -char *myctime(time_t value) +const char *myctime(time_t value) { static char buf[28]; char *p; @@ -457,7 +294,7 @@ char *myctime(time_t value) ** to internal buffer (nbuf). *NEVER* use the returned pointer ** to modify what it points!!! */ -char *get_client_name(Client *client, int showip) +const char *get_client_name(Client *client, int showip) { static char nbuf[HOSTLEN * 2 + USERLEN + 5]; @@ -482,7 +319,7 @@ char *get_client_name(Client *client, int showip) return client->name; } -char *get_client_host(Client *client) +const char *get_client_host(Client *client) { static char nbuf[HOSTLEN * 2 + USERLEN + 5]; @@ -500,9 +337,9 @@ char *get_client_host(Client *client) /* * Set sockhost to 'host'. Skip the user@ part of 'host' if necessary. */ -void set_sockhost(Client *client, char *host) +void set_sockhost(Client *client, const char *host) { - char *s; + const char *s; if ((s = strchr(host, '@'))) s++; else @@ -516,7 +353,7 @@ int on_dccallow_list(Client *to, Client *from) Link *lp; for(lp = to->user->dccallow; lp; lp = lp->next) - if(lp->flags == DCC_LINK_ME && lp->value.client == from) + if (lp->flags == DCC_LINK_ME && lp->value.client == from) return 1; return 0; } @@ -538,11 +375,11 @@ void remove_dcc_references(Client *client) acptr = lp->value.client; for(found = 0, lpp = &(acptr->user->dccallow); *lpp; lpp=&((*lpp)->next)) { - if(lp->flags == (*lpp)->flags) + if (lp->flags == (*lpp)->flags) continue; /* match only opposite types for sanity */ - if((*lpp)->value.client == client) + if ((*lpp)->value.client == client) { - if((*lpp)->flags == DCC_LINK_ME) + if ((*lpp)->flags == DCC_LINK_ME) { sendto_one(acptr, NULL, ":%s %d %s :%s has been removed from " "your DCC allow list for signing off", @@ -556,38 +393,19 @@ void remove_dcc_references(Client *client) } } - if(!found) - sendto_realops("[BUG] remove_dcc_references: %s was in dccallowme " - "list[%d] of %s but not in dccallowrem list!", - acptr->name, lp->flags, client->name); + if (!found) + { + unreal_log(ULOG_WARNING, "main", "BUG_REMOVE_DCC_REFERENCES", acptr, + "[BUG] remove_dcc_references: $client was in dccallowme " + "list of $existing_client but not in dccallowrem list!", + log_data_client("existing_client", client)); + } free_link(lp); lp = nextlp; } } -/* - * Recursively send QUITs and SQUITs for cptr and all of it's dependent - * clients. A server needs the client QUITs if it does not support NOQUIT. - * - kaniini - */ -static void recurse_send_quits(Client *cptr, Client *client, Client *from, Client *to, - MessageTag *mtags, const char *comment, const char *splitstr) -{ - Client *acptr, *next; - - list_for_each_entry_safe(acptr, next, &global_server_list, client_node) - { - if (acptr->srvptr != client) - continue; - - recurse_send_quits(cptr, acptr, from, to, mtags, comment, splitstr); - } - - if (cptr == client && to != from && !(to->direction && (to->direction == from))) - sendto_one(to, mtags, "SQUIT %s :%s", client->name, comment); -} - /* * Remove all clients that depend on source_p; assumes all (S)QUITs have * already been sent. we make sure to exit a server's dependent clients @@ -600,7 +418,7 @@ static void recurse_remove_clients(Client *client, MessageTag *mtags, const char list_for_each_entry_safe(acptr, next, &client_list, client_node) { - if (acptr->srvptr != client) + if (acptr->uplink != client) continue; exit_one_client(acptr, mtags, comment); @@ -608,7 +426,7 @@ static void recurse_remove_clients(Client *client, MessageTag *mtags, const char list_for_each_entry_safe(acptr, next, &global_server_list, client_node) { - if (acptr->srvptr != client) + if (acptr->uplink != client) continue; recurse_remove_clients(acptr, mtags, comment); @@ -626,7 +444,10 @@ static void remove_dependents(Client *client, Client *from, MessageTag *mtags, c Client *acptr; list_for_each_entry(acptr, &global_server_list, client_node) - recurse_send_quits(client, client, from, acptr, mtags, comment, splitstr); + { + if (acptr != from && !(acptr->direction && (acptr->direction == from))) + sendto_one(acptr, mtags, "SQUIT %s :%s", client->name, comment); + } recurse_remove_clients(client, mtags, splitstr); } @@ -647,18 +468,14 @@ static void exit_one_client(Client *client, MessageTag *mtags_i, const char *com MessageTag *mtags_o = NULL; if (!MyUser(client)) - RunHook3(HOOKTYPE_REMOTE_QUIT, client, mtags_i, comment); + RunHook(HOOKTYPE_REMOTE_QUIT, client, mtags_i, comment); new_message_special(client, mtags_i, &mtags_o, ":%s QUIT", client->name); sendto_local_common_channels(client, NULL, 0, mtags_o, ":%s QUIT :%s", client->name, comment); free_message_tags(mtags_o); while ((mp = client->user->channel)) - remove_user_from_channel(client, mp->channel); - - /* Clean up invitefield */ - while ((lp = client->user->invited)) - del_invite(client, lp->value.channel); + remove_user_from_channel(client, mp->channel, 1); /* again, this is all that is needed */ /* Clean up dccallow list and (if needed) notify other clients @@ -687,8 +504,6 @@ static void exit_one_client(Client *client, MessageTag *mtags_i, const char *com } if (*client->name) del_from_client_hash_table(client->name, client); - if (IsUser(client)) - hash_check_watch(client, RPL_LOGOFF); if (remote_rehash_client == client) remote_rehash_client = NULL; /* client did a /REHASH and QUIT before rehash was complete */ remove_client_from_list(client); @@ -699,7 +514,7 @@ static void exit_one_client(Client *client, MessageTag *mtags_i, const char *com * @param recv_mtags Message tags to use as a base (if any). * @param comment The (s)quit message */ -void exit_client(Client *client, MessageTag *recv_mtags, char *comment) +void exit_client(Client *client, MessageTag *recv_mtags, const char *comment) { exit_client_ex(client, client->direction, recv_mtags, comment); } @@ -709,7 +524,24 @@ void exit_client(Client *client, MessageTag *recv_mtags, char *comment) * @param recv_mtags Message tags to use as a base (if any). * @param comment The (s)quit message */ -void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char *comment) +void exit_client_fmt(Client *client, MessageTag *recv_mtags, FORMAT_STRING(const char *pattern), ...) +{ + char comment[512]; + + va_list vl; + va_start(vl, pattern); + vsnprintf(comment, sizeof(comment), pattern, vl); + va_end(vl); + + exit_client_ex(client, client->direction, recv_mtags, comment); +} + +/** Exit this IRC client, and all the dependents (users, servers) if this is a server. + * @param client The client to exit. + * @param recv_mtags Message tags to use as a base (if any). + * @param comment The (s)quit message + */ +void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, const char *comment) { long long on_for; ConfigItem_listen *listen_conf; @@ -741,23 +573,25 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char } if (IsUser(client)) irccounts.me_clients--; - if (client->serv && client->serv->conf) + if (client->server && client->server->conf) { - client->serv->conf->refcount--; - Debug((DEBUG_ERROR, "reference count for %s (%s) is now %d", - client->name, client->serv->conf->servername, client->serv->conf->refcount)); - if (!client->serv->conf->refcount - && client->serv->conf->flag.temporary) + client->server->conf->refcount--; + if (!client->server->conf->refcount + && client->server->conf->flag.temporary) { - Debug((DEBUG_ERROR, "deleting temporary block %s", client->serv->conf->servername)); - delete_linkblock(client->serv->conf); - client->serv->conf = NULL; + delete_linkblock(client->server->conf); + client->server->conf = NULL; } } if (IsServer(client)) { irccounts.me_servers--; - ircd_log(LOG_SERVER, "SQUIT %s (%s)", client->name, comment); + if (!IsServerDisconnectLogged(client)) + { + unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, + "Lost server link to $client [$client.ip]: $reason", + log_data_string("reason", comment)); + } } free_pending_net(client); if (client->local->listener) @@ -774,24 +608,17 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char SetClosing(client); if (IsUser(client)) { - RunHook3(HOOKTYPE_LOCAL_QUIT, client, recv_mtags, comment); - sendto_connectnotice(client, 1, comment); - /* Clean out list and watch structures -Donwulff */ - hash_del_watch_list(client); - on_for = TStime() - client->local->firsttime; - if (IsHidden(client)) - ircd_log(LOG_CLIENT, "Disconnect - (%lld:%lld:%lld) %s!%s@%s [%s] [vhost: %s] (%s)", - on_for / 3600, (on_for % 3600) / 60, on_for % 60, - client->name, client->user->username, - client->user->realhost, GetIP(client), client->user->virthost, comment); - else - ircd_log(LOG_CLIENT, "Disconnect - (%lld:%lld:%lld) %s!%s@%s [%s] (%s)", - on_for / 3600, (on_for % 3600) / 60, on_for % 60, - client->name, client->user->username, client->user->realhost, GetIP(client), comment); + long connected_time = TStime() - client->local->creationtime; + RunHook(HOOKTYPE_LOCAL_QUIT, client, recv_mtags, comment); + unreal_log(ULOG_INFO, "connect", "LOCAL_CLIENT_DISCONNECT", client, + "Client exiting: $client ($client.user.username@$client.hostname) [$client.ip] ($reason)", + log_data_string("extended_client_info", get_connect_extinfo(client)), + log_data_string("reason", comment), + log_data_integer("connected_time", connected_time)); } else if (IsUnknown(client)) { - RunHook3(HOOKTYPE_UNKUSER_QUIT, client, recv_mtags, comment); + RunHook(HOOKTYPE_UNKUSER_QUIT, client, recv_mtags, comment); } if (client->local->fd >= 0 && !IsConnecting(client)) @@ -803,8 +630,14 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char } else if (IsUser(client) && !IsULine(client)) { - if (client->srvptr != &me) - sendto_fconnectnotice(client, 1, comment); + if (client->uplink != &me) + { + unreal_log(ULOG_INFO, "connect", "REMOTE_CLIENT_DISCONNECT", client, + "Client exiting: $client ($client.user.username@$client.hostname) [$client.ip] ($reason)", + log_data_string("extended_client_info", get_connect_extinfo(client)), + log_data_string("reason", comment), + log_data_string("from_server_name", client->user->server)); + } } /* @@ -816,16 +649,16 @@ void exit_client_ex(Client *client, Client *origin, MessageTag *recv_mtags, char { char splitstr[HOSTLEN + HOSTLEN + 2]; - assert(client->serv != NULL && client->srvptr != NULL); + assert(client->server != NULL && client->uplink != NULL); if (FLAT_MAP) strlcpy(splitstr, "*.net *.split", sizeof splitstr); else - ircsnprintf(splitstr, sizeof splitstr, "%s %s", client->srvptr->name, client->name); + ircsnprintf(splitstr, sizeof splitstr, "%s %s", client->uplink->name, client->name); remove_dependents(client, origin, recv_mtags, comment, splitstr); - RunHook2(HOOKTYPE_SERVER_QUIT, client, recv_mtags); + RunHook(HOOKTYPE_SERVER_QUIT, client, recv_mtags); } else if (IsUser(client) && !IsKilled(client)) { @@ -845,7 +678,7 @@ void initstats(void) } /** Verify operator count, to catch bugs introduced by flawed services */ -void verify_opercount(Client *orig, char *tag) +void verify_opercount(Client *orig, const char *tag) { int counted = 0; Client *client; @@ -858,31 +691,44 @@ void verify_opercount(Client *orig, char *tag) } if (counted == irccounts.operators) return; - snprintf(text, sizeof(text), "[BUG] operator count bug! value in /lusers is '%d', we counted '%d', " - "user='%s', userserver='%s', tag=%s. Corrected. ", - irccounts.operators, counted, orig->name, - orig->srvptr ? orig->srvptr->name : "", tag ? tag : ""); -#ifdef DEBUGMODE - sendto_realops("%s", text); -#endif - ircd_log(LOG_ERROR, "%s", text); + unreal_log(ULOG_WARNING, "main", "BUG_LUSERS_OPERS", orig, + "[BUG] Operator count bug at $where! Value in /LUSERS is $opers, " + "we counted $counted_opers, " + "triggered by $client.details on $client.user.servername", + log_data_integer("opers", irccounts.operators), + log_data_integer("counted_opers", counted), + log_data_string("where", tag)); irccounts.operators = counted; } /** Check if the specified hostname does not contain forbidden characters. - * RETURNS: - * 1 if ok, 0 if rejected. + * @param host The host name to check + * @param strict If set to 1 then we also check if the hostname + * resembles an IP address (eg contains ':') and + * some other stuff that we don't consider valid + * in actual DNS names (eg '/'). + * @returns 1 if valid, 0 if not. */ -int valid_host(char *host) +int valid_host(const char *host, int strict) { - char *p; + const char *p; + + if (!*host) + return 0; /* must at least contain something */ if (strlen(host) > HOSTLEN) return 0; /* too long hosts are invalid too */ - for (p=host; *p; p++) - if (!isalnum(*p) && (*p != '_') && (*p != '-') && (*p != '.') && (*p != ':') && (*p != '/')) - return 0; + if (strict) + { + for (p=host; *p; p++) + if (!isalnum(*p) && !strchr("_-.", *p)) + return 0; + } else { + for (p=host; *p; p++) + if (!isalnum(*p) && !strchr("_-.:/", *p)) + return 0; + } return 1; } @@ -890,7 +736,7 @@ int valid_host(char *host) /*|| BAN ACTION ROUTINES FOLLOW ||*/ /** Converts a banaction string (eg: "kill") to an integer value (eg: BAN_ACT_KILL) */ -BanAction banact_stringtoval(char *s) +BanAction banact_stringtoval(const char *s) { BanActTable *b; @@ -923,7 +769,7 @@ char banact_valtochar(BanAction val) } /** Converts a banaction value (eg: BAN_ACT_KLINE) to a string (eg: "kline") */ -char *banact_valtostring(BanAction val) +const char *banact_valtostring(BanAction val) { BanActTable *b; @@ -936,7 +782,7 @@ char *banact_valtostring(BanAction val) /*|| BAN TARGET ROUTINES FOLLOW ||*/ /** Extract target flags from string 's'. */ -int spamfilter_gettargets(char *s, Client *client) +int spamfilter_gettargets(const char *s, Client *client) { SpamfilterTargetTable *e; int flags = 0; @@ -959,7 +805,7 @@ int flags = 0; } /** Convert a string with a targetname to an integer value */ -int spamfilter_getconftargets(char *s) +int spamfilter_getconftargets(const char *s) { SpamfilterTargetTable *e; @@ -972,9 +818,9 @@ SpamfilterTargetTable *e; /** Create a string with (multiple) targets from an integer mask */ char *spamfilter_target_inttostring(int v) { -static char buf[128]; -SpamfilterTargetTable *e; -char *p = buf; + static char buf[128]; + SpamfilterTargetTable *e; + char *p = buf; for (e = &spamfiltertargettable[0]; e->value; e++) if (v & e->value) @@ -1033,7 +879,7 @@ char *unreal_encodespace(char *s) } /** This is basically only used internally by match_spamfilter()... */ -char *cmdname_by_spamftarget(int target) +const char *cmdname_by_spamftarget(int target) { SpamfilterTargetTable *e; @@ -1044,7 +890,7 @@ char *cmdname_by_spamftarget(int target) } /** Returns 1 if this is a channel from set::auto-join or set::oper-auto-join */ -int is_autojoin_chan(char *chname) +int is_autojoin_chan(const char *chname) { char buf[512]; char *p, *name; @@ -1070,40 +916,6 @@ int is_autojoin_chan(char *chname) return 0; } -/** Convert a character like 'o' to the corresponding channel flag - * like CHFL_CHANOP. - * @param c The mode character. The only valid values are: vhoaq - * @returns One of CHFL_* or 0 if an invalid mode character is specified. - */ -int char_to_channelflag(char c) -{ - if (c == 'v') - return CHFL_VOICE; - else if (c == 'h') - return CHFL_HALFOP; - else if (c == 'o') - return CHFL_CHANOP; - else if (c == 'a') - return CHFL_CHANADMIN; - else if (c == 'q') - return CHFL_CHANOWNER; - return 0; -} - -// FIXME: should detect ce_vardata) - safe_strdup(m->mask, ce->ce_vardata); + if (ce->value) + safe_strdup(m->mask, ce->value); else - safe_strdup(m->mask, ce->ce_varname); + safe_strdup(m->mask, ce->name); add_ListItem((ListStruct *)m, (ListStruct **)head); } @@ -1136,10 +948,10 @@ static void unreal_add_mask(ConfigItem_mask **head, ConfigEntry *ce) /** Add mask entries from config */ void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce) { - if (ce->ce_entries) + if (ce->items) { ConfigEntry *cep; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) unreal_add_mask(head, cep); } else { @@ -1147,29 +959,108 @@ void unreal_add_masks(ConfigItem_mask **head, ConfigEntry *ce) } } -/** Check if a client matches any of the masks in the mask list */ -int unreal_mask_match(Client *client, ConfigItem_mask *m) +/** Check if a client matches any of the masks in the mask list. + * The following rules apply: + * - If you have only negating entries, like '!abc' and '!def', then + * we assume an implicit * rule first, since that is clearly what + * the user wants. + * - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the + * implicit * is dropped and we assume you only want to match *.com, + * with the exception of irc1*.com and irc2*.com. + * - If you only have normal entries without ! then things are + * as they always are. + * @param client The client to run the mask match against + * @param mask The mask entry from the config file + * @returns 1 on match, 0 on non-match. + */ +int unreal_mask_match(Client *client, ConfigItem_mask *mask) { - for (; m; m = m->next) + int retval = 1; + ConfigItem_mask *m; + + if (!mask) + return 0; /* Empty mask block is no match */ + + /* First check normal matches (without ! prefix) */ + for (m = mask; m; m = m->next) { - /* With special support for '!' prefix (negative matching like "!192.168.*") */ - if (m->mask[0] == '!') + if (m->mask[0] != '!') { - if (!match_user(m->mask+1, client, MATCH_CHECK_REAL)) - return 1; - } else { - if (match_user(m->mask, client, MATCH_CHECK_REAL)) - return 1; + retval = 0; /* no implicit * */ + if (match_user(m->mask, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED)) + { + retval = 1; + break; + } } } - return 0; + if (retval) + { + /* We matched. Check for exceptions (with ! prefix) */ + for (m = mask; m; m = m->next) + { + if ((m->mask[0] == '!') && match_user(m->mask+1, client, MATCH_CHECK_REAL|MATCH_CHECK_EXTENDED)) + return 0; + } + } + + return retval; +} + +/** Check if a string matches any of the masks in the mask list. + * The following rules apply: + * - If you have only negating entries, like '!abc' and '!def', then + * we assume an implicit * rule first, since that is clearly what + * the user wants. + * - If you have a mix, like '*.com', '!irc1*', '!irc2*' then the + * implicit * is dropped and we assume you only want to match *.com, + * with the exception of irc1*.com and irc2*.com. + * - If you only have normal entries without ! then things are + * as they always are. + * @param name The name to run the mask matching on + * @param mask The mask entry from the config file + * @returns 1 on match, 0 on non-match. + */ +int unreal_mask_match_string(const char *name, ConfigItem_mask *mask) +{ + int retval = 1; + ConfigItem_mask *m; + + if (!mask) + return 0; /* Empty mask block is no match */ + + /* First check normal matches (without ! prefix) */ + for (m = mask; m; m = m->next) + { + if (m->mask[0] != '!') + { + retval = 0; /* no implicit * */ + if (match_simple(m->mask, name)) + { + retval = 1; + break; + } + } + } + + if (retval) + { + /* We matched. Check for exceptions (with ! prefix) */ + for (m = mask; m; m = m->next) + { + if ((m->mask[0] == '!') && match_simple(m->mask+1, name)) + return 0; + } + } + + return retval; } /** Our own strcasestr implementation because strcasestr is * often not available or is not working correctly. */ -char *our_strcasestr(char *haystack, char *needle) +char *our_strcasestr(const char *haystack, const char *needle) { int i; int nlength = strlen(needle); @@ -1182,12 +1073,12 @@ char *our_strcasestr(char *haystack, char *needle) return NULL; if (nlength <= 0) - return haystack; + return (char *)haystack; for (i = 0; i <= (hlength - nlength); i++) { if (strncasecmp (haystack + i, needle, nlength) == 0) - return haystack + i; + return (char *)(haystack + i); } return NULL; /* not found */ @@ -1203,7 +1094,7 @@ char *our_strcasestr(char *haystack, char *needle) * @param from Who added this entry * @param skip Which server(-side) to skip broadcasting this entry to. */ -int swhois_add(Client *client, char *tag, int priority, char *swhois, Client *from, Client *skip) +int swhois_add(Client *client, const char *tag, int priority, const char *swhois, Client *from, Client *skip) { SWhois *s; @@ -1237,7 +1128,7 @@ int swhois_add(Client *client, char *tag, int priority, char *swhois, Client *fr * @param skip Which server(-side) to skip broadcasting this entry to. * @note If you use swhois "*" then it will remove all swhois titles for that tag */ -int swhois_delete(Client *client, char *tag, char *swhois, Client *from, Client *skip) +int swhois_delete(Client *client, const char *tag, const char *swhois, Client *from, Client *skip) { SWhois *s, *s_next; int ret = -1; /* default to 'not found' */ @@ -1277,8 +1168,6 @@ int IsWebsocket(Client *client) return (MyConnect(client) && moddata_client(client, md).ptr) ? 1 : 0; } -extern void send_raw_direct(Client *user, FORMAT_STRING(const char *pattern), ...); - /** Generic function to inform the user he/she has been banned. * @param client The affected client. * @param bantype The ban type, such as: "K-Lined", "G-Lined" or "realname". @@ -1290,7 +1179,7 @@ extern void send_raw_direct(Client *user, FORMAT_STRING(const char *pattern), .. * * @note This function will call exit_client() appropriately. */ -void banned_client(Client *client, char *bantype, char *reason, int global, int noexit) +void banned_client(Client *client, const char *bantype, const char *reason, int global, int noexit) { char buf[512]; char *fmt = global ? iConf.reject_message_gline : iConf.reject_message_kline; @@ -1369,7 +1258,7 @@ char *mystpcpy(char *dst, const char *src) * so similar to what strlen() would have returned. * @note Caller must ensure that the buffer 'buf' is of sufficient size. */ -size_t add_sjsby(char *buf, char *setby, time_t seton) +size_t add_sjsby(char *buf, const char *setby, time_t seton) { char tbuf[32]; char *p = buf; @@ -1399,14 +1288,14 @@ size_t add_sjsby(char *buf, char *setby, time_t seton) * sendto_server(client, 0, 0, recv_mtags, ":%s SOMECOMMAND %s", client->name, buf); * @endcode */ -void concat_params(char *buf, int len, int parc, char *parv[]) +void concat_params(char *buf, int len, int parc, const char *parv[]) { int i; *buf = '\0'; for (i = 1; i < parc; i++) { - char *param = parv[i]; + const char *param = parv[i]; if (!param) break; @@ -1476,7 +1365,6 @@ void new_message(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list) * This function calls modules so they can add tags, such as: * msgid, time and account. * This special version deals in a special way with msgid in particular. - * TODO: document * The pattern and vararg create a 'signature', this is normally * identical to the message that is sent to clients (end-users). * For example ":xyz JOIN #chan". @@ -1509,7 +1397,7 @@ void parse_message_tags_default_handler(Client *client, char **str, MessageTag * * This is only used if the 'mtags' module is NOT loaded, * which would be quite unusual, but possible. */ -char *mtags_to_string_default_handler(MessageTag *m, Client *client) +const char *mtags_to_string_default_handler(MessageTag *m, Client *client) { return NULL; } @@ -1568,6 +1456,11 @@ void labeled_response_force_end_default_handler(void) { } +/** Ad default handler for if the slog module is not loaded */ +void do_unreal_log_remote_deliver_default_handler(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized) +{ +} + /** my_timegm: mktime()-like function which will use GMT/UTC. * Strangely enough there is no standard function for this. * On some *NIX OS's timegm() may be available, sometimes only @@ -1612,16 +1505,10 @@ time_t server_time_to_unix_time(const char *tbuf) time_t ret; if (!tbuf) - { - ircd_log(LOG_ERROR, "[BUG] server_time_to_unix_time() failed for NULL item. Incorrect S2S traffic?"); return 0; - } if (strlen(tbuf) < 20) - { - ircd_log(LOG_ERROR, "[BUG] server_time_to_unix_time() failed for short item '%s'", tbuf); return 0; - } memset(&tm, 0, sizeof(tm)); ret = sscanf(tbuf, "%d-%d-%dT%d:%d:%d.%dZ", @@ -1634,10 +1521,7 @@ time_t server_time_to_unix_time(const char *tbuf) &dontcare); if (ret != 7) - { - ircd_log(LOG_ERROR, "[BUG] server_time_to_unix_time() failed for '%s'", tbuf); return 0; - } tm.tm_year -= 1900; tm.tm_mon -= 1; @@ -1646,6 +1530,68 @@ time_t server_time_to_unix_time(const char *tbuf) return ret; } +/** Convert an RFC 2616 timestamp (used in HTTP headers) to UNIX time */ +time_t rfc2616_time_to_unix_time(const char *tbuf) +{ + struct tm tm; + int dontcare = 0; + time_t ret; + char month[8]; + int i; + + if (!tbuf) + return 0; + + if (strlen(tbuf) < 20) + return 0; + + memset(&tm, 0, sizeof(tm)); + *month = '\0'; + ret = sscanf(tbuf, "%*[a-zA-Z,] %d %3s %d %d:%d:%d", + &tm.tm_mday, month, &tm.tm_year, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + + if (ret < 6) + return 0; + + for (i=0; i < 12; i++) + { + if (!strcmp(short_months[i], month)) + { + tm.tm_mon = i; + break; + } + } + if (i == 12) + return 0; /* Month not found */ + if (tm.tm_year < 1900) + return 0; + + tm.tm_year -= 1900; + ret = my_timegm(&tm); + return ret; /* can still be 0 */ +} + +/** Returns an RFC 2616 timestamp (used in HTTP headers) */ +const char *rfc2616_time(time_t clock) +{ + static char buf[80], plus; + struct tm *lt, *gm; + struct tm gmbuf; + int minswest; + + if (!clock) + time(&clock); + gm = gmtime(&clock); + + snprintf(buf, sizeof(buf), + "%s, %02d %.3s %4d %02d:%02d:%02d GMT", + short_weekdays[gm->tm_wday], gm->tm_mday, short_months[gm->tm_mon], + gm->tm_year + 1900, gm->tm_hour, gm->tm_min, gm->tm_sec); + + return buf; +} + /** Write a 64 bit integer to a file. * @param fd File descriptor * @param t The value to write @@ -1735,7 +1681,7 @@ int write_data(FILE *fd, const void *buf, size_t len) * Note that 'x' can safely be NULL. * @returns 1 on success, 0 on failure. */ -int write_str(FILE *fd, char *x) +int write_str(FILE *fd, const char *x) { uint16_t len; @@ -1814,19 +1760,31 @@ void binarytohex(void *data, size_t len, char *str) str[n] = '\0'; } -/** Generates an MD5 checksum. +/** Generates an MD5 checksum - binary version. * @param mdout[out] Buffer to store result in, the result will be 16 bytes in binary * (not ascii printable!). * @param src[in] The input data used to generate the checksum. * @param n[in] Length of data. + * @deprecated The MD5 algorithm is deprecated and insecure, + * so only use this if absolutely needed. */ void DoMD5(char *mdout, const char *src, unsigned long n) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + unsigned int md_len; + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (EVP_DigestInit_ex(mdctx, md5_function, NULL) != 1) + abort(); + EVP_DigestUpdate(mdctx, src, n); + EVP_DigestFinal_ex(mdctx, mdout, &md_len); + EVP_MD_CTX_free(mdctx); +#else MD5_CTX hash; MD5_Init(&hash); MD5_Update(&hash, src, n); MD5_Final(mdout, &hash); +#endif } /** Generates an MD5 checksum - ASCII printable string (0011223344..etc..). @@ -1834,6 +1792,8 @@ void DoMD5(char *mdout, const char *src, unsigned long n) * 32 characters + nul terminator, so needs to be at least 33 characters. * @param src[in] The input data used to generate the checksum. * @param n[in] Length of data. + * @deprecated The MD5 algorithm is deprecated and insecure, + * so only use this if absolutely needed. */ char *md5hash(char *dst, const char *src, unsigned long n) { @@ -1844,6 +1804,32 @@ char *md5hash(char *dst, const char *src, unsigned long n) return dst; } +/** Generates a SHA256 checksum - binary version. + * Most people will want to use sha256hash() instead which outputs hex. + * @param dst[out] Buffer to store result in, which needs to be 32 bytes in length + * (SHA256_DIGEST_LENGTH). + * @param src[in] The input data used to generate the checksum. + * @param n[in] Length of data. + */ +void sha256hash_binary(char *dst, const char *src, unsigned long n) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + unsigned int md_len; + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (EVP_DigestInit_ex(mdctx, sha256_function, NULL) != 1) + abort(); + EVP_DigestUpdate(mdctx, src, n); + EVP_DigestFinal_ex(mdctx, dst, &md_len); + EVP_MD_CTX_free(mdctx); +#else + SHA256_CTX hash; + + SHA256_Init(&hash); + SHA256_Update(&hash, src, n); + SHA256_Final(dst, &hash); +#endif +} + /** Generates a SHA256 checksum - ASCII printable string (0011223344..etc..). * @param dst[out] Buffer to store result in, which needs to be 65 bytes minimum. * @param src[in] The input data used to generate the checksum. @@ -1851,18 +1837,15 @@ char *md5hash(char *dst, const char *src, unsigned long n) */ char *sha256hash(char *dst, const char *src, unsigned long n) { - SHA256_CTX hash; char binaryhash[SHA256_DIGEST_LENGTH]; - SHA256_Init(&hash); - SHA256_Update(&hash, src, n); - SHA256_Final(binaryhash, &hash); + sha256hash_binary(binaryhash, src, n); binarytohex(binaryhash, sizeof(binaryhash), dst); return dst; } /** Calculate the SHA256 checksum of a file */ -char *sha256sum_file(const char *fname) +const char *sha256sum_file(const char *fname) { FILE *fd; char buf[2048]; @@ -1870,22 +1853,68 @@ char *sha256sum_file(const char *fname) char binaryhash[SHA256_DIGEST_LENGTH]; static char hexhash[SHA256_DIGEST_LENGTH*2+1]; int n; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + unsigned int md_len; + EVP_MD_CTX *mdctx; + + mdctx = EVP_MD_CTX_new(); + if (EVP_DigestInit_ex(mdctx, sha256_function, NULL) != 1) + abort(); +#else + SHA256_Init(&hash); +#endif fd = fopen(fname, "rb"); if (!fd) return NULL; - SHA256_Init(&hash); while ((n = fread(buf, 1, sizeof(buf), fd)) > 0) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_DigestUpdate(mdctx, buf, n); +#else SHA256_Update(&hash, buf, n); +#endif } fclose(fd); + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_DigestFinal_ex(mdctx, binaryhash, &md_len); + EVP_MD_CTX_free(mdctx); +#else SHA256_Final(binaryhash, &hash); +#endif binarytohex(binaryhash, sizeof(binaryhash), hexhash); return hexhash; } +/** Generates a SHA1 checksum - binary version. + * @param dst[out] Buffer to store result in, which needs to be 32 bytes in length + * (SHA1_DIGEST_LENGTH). + * @param src[in] The input data used to generate the checksum. + * @param n[in] Length of data. + * @deprecated The SHA1 algorithm is deprecated and insecure, + * so only use this if absolutely needed. + */ +void sha1hash_binary(char *dst, const char *src, unsigned long n) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + unsigned int md_len; + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (EVP_DigestInit_ex(mdctx, sha1_function, NULL) != 1) + abort(); + EVP_DigestUpdate(mdctx, src, n); + EVP_DigestFinal_ex(mdctx, dst, &md_len); + EVP_MD_CTX_free(mdctx); +#else + SHA_CTX hash; + + SHA1_Init(&hash); + SHA1_Update(&hash, src, n); + SHA1_Final(dst, &hash); +#endif +} + /** Remove a suffix from a filename, eg ".c" (if it is present) */ char *filename_strip_suffix(const char *fname, const char *suffix) { @@ -1932,21 +1961,20 @@ int filename_has_suffix(const char *fname, const char *suffix) return 0; } -/** Check if the specified file exists */ -int file_exists(char *file) +/** Check if the specified file or directory exists */ +int file_exists(const char *file) { - FILE *fd; - - fd = fopen(file, "r"); - if (!fd) - return 0; - - fclose(fd); - return 1; +#ifdef _WIN32 + if (_access(file, 0) == 0) +#else + if (access(file, 0) == 0) +#endif + return 1; + return 0; } /** Get the file creation time */ -time_t get_file_time(char *fname) +time_t get_file_time(const char *fname) { struct stat st; @@ -1957,7 +1985,7 @@ time_t get_file_time(char *fname) } /** Get the size of a file */ -long get_file_size(char *fname) +long get_file_size(const char *fname) { struct stat st; @@ -1968,7 +1996,7 @@ long get_file_size(char *fname) } /** Add a line to a MultiLine list */ -void addmultiline(MultiLine **l, char *line) +void addmultiline(MultiLine **l, const char *line) { MultiLine *m = safe_alloc(sizeof(MultiLine)); safe_strdup(m->line, line); @@ -1987,8 +2015,27 @@ void freemultiline(MultiLine *l) } } +/** Convert a line regular string containing \n's to a MultiLine linked list */ +MultiLine *line2multiline(const char *str) +{ + static char buf[8192]; + char *p, *p2; + MultiLine *ml = NULL; + + strlcpy(buf, str, sizeof(buf)); + p = buf; + do { + p2 = strchr(p, '\n'); + if (p2) + *p2++ = '\0'; + addmultiline(&ml, p); + p = p2; + } while(p2 && *p2); + return ml; +} + /** Convert a sendtype to a command string */ -char *sendtype_to_cmd(SendType sendtype) +const char *sendtype_to_cmd(SendType sendtype) { if (sendtype == SEND_TYPE_PRIVMSG) return "PRIVMSG"; @@ -2005,11 +2052,11 @@ char *sendtype_to_cmd(SendType sendtype) * @param strict Whether to require UPPER+lower+digits * @returns 1 if good, 0 if not. */ -int check_password_strength(char *pass, int min_length, int strict, char **err) +int check_password_strength(const char *pass, int min_length, int strict, char **err) { - char has_lowercase=0, has_uppercase=0, has_digit=0; - char *p; static char buf[256]; + char has_lowercase=0, has_uppercase=0, has_digit=0; + const char *p; if (err) *err = NULL; @@ -2059,7 +2106,7 @@ int check_password_strength(char *pass, int min_length, int strict, char **err) return 1; } -int valid_secret_password(char *pass, char **err) +int valid_secret_password(const char *pass, char **err) { return check_password_strength(pass, 10, 1, err); } @@ -2082,6 +2129,34 @@ int running_interactively(void) #endif } +int terminal_supports_color(void) +{ +#ifndef _WIN32 + char *s; + + /* Yeah we check all of stdin, stdout, stderr, because + * or more may be redirected (bin/unrealircd >log 2>&1), + * and then we want to say no to color support. + */ + if (!isatty(0) || !isatty(1) || !isatty(2)) + return 0; + + s = getenv("TERM"); + /* Yeah this is a lazy way to detect color-capable terminals + * but it is good enough for me. + */ + if (s) + { + if (strstr(s, "color") || strstr(s, "ansi")) + return 1; + } + + return 0; +#else + return 0; +#endif +} + /** Skip whitespace (if any) */ void skip_whitespace(char **p) { @@ -2096,3 +2171,201 @@ void read_until(char **p, char *stopchars) { for (; **p && !strchr(stopchars, **p); *p = *p + 1); } + +void write_pidfile_failed(void) +{ + char *errstr = strerror(errno); + unreal_log(ULOG_WARNING, "config", "WRITE_PID_FILE_FAILED", NULL, + "Unable to write to pid file '$filename': $system_error", + log_data_string("filename", conf_files->pid_file), + log_data_string("system_error", errstr)); +} + +/** Write PID file */ +void write_pidfile(void) +{ +#ifdef IRCD_PIDFILE + int fd; + char buff[20]; + if ((fd = open(conf_files->pid_file, O_CREAT | O_WRONLY, 0600)) < 0) + { + write_pidfile_failed(); + return; + } + ircsnprintf(buff, sizeof(buff), "%5d\n", (int)getpid()); + if (write(fd, buff, strlen(buff)) < 0) + write_pidfile_failed(); + if (close(fd) < 0) + write_pidfile_failed(); +#endif +} + +/* + * Determines if the given string is a valid URL. Since libcurl + * supports telnet, ldap, and dict such strings are treated as + * invalid URLs here since we don't want them supported in + * unreal. + */ +int url_is_valid(const char *string) +{ + if (strstr(string, " ") || strstr(string, "\t")) + return 0; + + if (strstr(string, "telnet://") == string || + strstr(string, "ldap://") == string || + strstr(string, "dict://") == string) + { + return 0; + } + return (strstr(string, "://") != NULL); +} + +/** A displayable URL for in error messages and such. + * This leaves out any authentication information (user:pass) + * the URL may contain. + */ +const char *displayurl(const char *url) +{ + static char buf[512]; + char *proto, *rest; + + /* protocol://user:pass@host/etc.. */ + rest = strchr(url, '@'); + + if (!rest) + return url; /* contains no auth information */ + + rest++; /* now points to the rest (remainder) of the URL */ + + proto = strstr(url, "://"); + if (!proto || (proto > rest) || (proto == url)) + return url; /* incorrectly formatted, just show entire URL. */ + + strlncpy(buf, url, sizeof(buf), proto - url); + strlcat(buf, "://***:***@", sizeof(buf)); + strlcat(buf, rest, sizeof(buf)); + + return buf; +} + +/* + * Returns the filename portion of the URL. The returned string + * is malloc()'ed and must be freed by the caller. If the specified + * URL does not contain a filename, a '-' is allocated and returned. + */ +char *url_getfilename(const char *url) +{ + const char *c, *start; + + if ((c = strstr(url, "://"))) + c += 3; + else + c = url; + + while (*c && *c != '/') + c++; + + if (*c == '/') + { + c++; + if (!*c || *c == '?') + return raw_strdup("-"); + start = c; + while (*c && *c != '?') + c++; + if (!*c) + return raw_strdup(start); + else + return raw_strldup(start, c-start+1); + + } + return raw_strdup("-"); +} + +#ifdef _WIN32 + // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess + // mode value Checks file for + // 04 Read-only + #define R_OK 04 +#endif + +/* + * Checks whether a file can be opened for reading. + */ +int is_file_readable(const char *file, const char *dir) +{ + char *filename = strdup(file); + convert_to_absolute_path(&filename, dir); + if (access(filename, R_OK)){ + safe_free(filename); + return 0; + } + safe_free(filename); + return 1; +} + +void delletterfromstring(char *s, char letter) +{ + if (s == NULL) + return; + for (; *s; s++) + { + if (*s == letter) + { + for (; *s; s++) + *s = s[1]; + break; + } + } +} + +int sort_character_lowercase_before_uppercase(char x, char y) +{ + /* Lower before upper */ + if (islower(x) && isupper(y)) + return 1; + if (isupper(x) && islower(y)) + return 0; + /* Other than that, easy */ + return x < y ? 1 : 0; +} + +/* Helper function, mainly used by snomask code */ +void addlettertodynamicstringsorted(char **str, char letter) +{ + char *i, *o; + char *newbuf; + size_t newbuflen; + + /* NULL string? Easy! */ + if (*str == NULL) + { + *str = safe_alloc(2); + **str = letter; + return; + } + + /* Exists? Then nothing to do */ + if (strchr(*str, letter)) + return; + + /* Ok, we really need to add it */ + newbuflen = strlen(*str) + 2; + newbuf = safe_alloc(newbuflen); + for (i = *str, o = newbuf; *i; i++) + { + /* Insert before a higher letter */ + if (letter && sort_character_lowercase_before_uppercase(letter, *i)) + { + *o++ = letter; + letter = '\0'; + } + *o++ = *i; + } + /* Or maybe we should be at the final spot? */ + if (letter) + *o++ = letter; + *o = '\0'; + safe_free_raw(*str); + *str = newbuf; +} diff --git a/src/modulemanager.c b/src/modulemanager.c index 1165df0..680a888 100644 --- a/src/modulemanager.c +++ b/src/modulemanager.c @@ -44,7 +44,7 @@ int mm_valid_module_name(char *name); void free_managed_module(ManagedModule *m); -SSL_CTX *mm_init_ssl(void) +SSL_CTX *mm_init_tls(void) { SSL_CTX *ctx_client; char buf1[512], buf2[512]; @@ -101,8 +101,7 @@ int parse_url(const char *url, char **host, int *port, char **document) if (!p) return 0; - *hostbuf = '\0'; - strlncat(hostbuf, url, sizeof(hostbuf), p - url); + strlncpy(hostbuf, url, sizeof(hostbuf), p - url); strlcpy(documentbuf, p, sizeof(documentbuf)); @@ -134,7 +133,7 @@ int mm_http_request(char *url, char *fname, int follow_redirects) snprintf(hostandport, sizeof(hostandport), "%s:%d", host, port); - ctx_client = mm_init_ssl(); + ctx_client = mm_init_tls(); if (!ctx_client) { fprintf(stderr, "ERROR: TLS initalization failure (I)\n"); @@ -163,14 +162,14 @@ int mm_http_request(char *url, char *fname, int follow_redirects) if (BIO_do_connect(socket) != 1) { fprintf(stderr, "ERROR: Could not connect to %s\n", hostandport); - config_report_ssl_error(); + //config_report_ssl_error(); FIXME? goto out2; } if (BIO_do_handshake(socket) != 1) { fprintf(stderr, "ERROR: Could not connect to %s (TLS handshake failed)\n", hostandport); - config_report_ssl_error(); + //config_report_ssl_error(); FIXME? goto out2; } @@ -339,63 +338,64 @@ int parse_quoted_string(char *buf, char *dest, size_t destlen) return 1; } -#define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", m->name, (x)->ce_varlinenum); return 0; } +#undef CheckNull +#define CheckNull(x) if ((!(x)->value) || (!(*((x)->value)))) { config_error("%s:%i: missing parameter", m->name, (x)->line_number); return 0; } /** Parse a module { } line from a module (not repo!!) */ int mm_module_file_config(ManagedModule *m, ConfigEntry *ce) { ConfigEntry *cep; - if (ce->ce_vardata) + if (ce->value) { config_error("%s:%d: module { } block should not have a name.", - m->name, ce->ce_varlinenum); + m->name, ce->line_number); return 0; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "source") || - !strcmp(cep->ce_varname, "version") || - !strcmp(cep->ce_varname, "author") || - !strcmp(cep->ce_varname, "sha256sum") || - !strcmp(cep->ce_varname, "description") + if (!strcmp(cep->name, "source") || + !strcmp(cep->name, "version") || + !strcmp(cep->name, "author") || + !strcmp(cep->name, "sha256sum") || + !strcmp(cep->name, "description") ) { config_error("%s:%d: module::%s should not be in here (it only exists in repository entries)", - m->name, cep->ce_varlinenum, cep->ce_varname); + m->name, cep->line_number, cep->name); return 0; } - else if (!strcmp(cep->ce_varname, "troubleshooting")) + else if (!strcmp(cep->name, "troubleshooting")) { CheckNull(cep); - safe_strdup(m->troubleshooting, cep->ce_vardata); + safe_strdup(m->troubleshooting, cep->value); } - else if (!strcmp(cep->ce_varname, "documentation")) + else if (!strcmp(cep->name, "documentation")) { CheckNull(cep); - safe_strdup(m->documentation, cep->ce_vardata); + safe_strdup(m->documentation, cep->value); } - else if (!strcmp(cep->ce_varname, "min-unrealircd-version")) + else if (!strcmp(cep->name, "min-unrealircd-version")) { CheckNull(cep); - safe_strdup(m->min_unrealircd_version, cep->ce_vardata); + safe_strdup(m->min_unrealircd_version, cep->value); } - else if (!strcmp(cep->ce_varname, "max-unrealircd-version")) + else if (!strcmp(cep->name, "max-unrealircd-version")) { CheckNull(cep); - safe_strdup(m->max_unrealircd_version, cep->ce_vardata); + safe_strdup(m->max_unrealircd_version, cep->value); } - else if (!strcmp(cep->ce_varname, "post-install-text")) + else if (!strcmp(cep->name, "post-install-text")) { - if (cep->ce_entries) + if (cep->items) { ConfigEntry *cepp; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - addmultiline(&m->post_install_text, cepp->ce_varname); + for (cepp = cep->items; cepp; cepp = cepp->next) + addmultiline(&m->post_install_text, cepp->name); } else { CheckNull(cep); - addmultiline(&m->post_install_text, cep->ce_vardata); + addmultiline(&m->post_install_text, cep->value); } } /* unknown items are silently ignored for future compatibility */ @@ -403,19 +403,19 @@ int mm_module_file_config(ManagedModule *m, ConfigEntry *ce) if (!m->documentation) { - config_error("%s:%d: module::documentation missing", m->name, ce->ce_varlinenum); + config_error("%s:%d: module::documentation missing", m->name, ce->line_number); return 0; } if (!m->troubleshooting) { - config_error("%s:%d: module::troubleshooting missing", m->name, ce->ce_varlinenum); + config_error("%s:%d: module::troubleshooting missing", m->name, ce->line_number); return 0; } if (!m->min_unrealircd_version) { - config_error("%s:%d: module::min-unrealircd-version missing", m->name, ce->ce_varlinenum); + config_error("%s:%d: module::min-unrealircd-version missing", m->name, ce->line_number); return 0; } @@ -438,9 +438,9 @@ int mm_parse_module_file(ManagedModule *m, char *buf, unsigned int line_offset) return 0; /* eg: parse errors */ /* Parse the module { } block (only one!) */ - for (ce = cf->cf_entries; ce; ce = ce->ce_next) + for (ce = cf->items; ce; ce = ce->next) { - if (!strcmp(ce->ce_varname, "module")) + if (!strcmp(ce->name, "module")) { int n = mm_module_file_config(m, ce); config_free(cf); @@ -652,7 +652,8 @@ int mm_valid_module_name(char *name) return 1; } -#define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", repo_url, (x)->ce_varlinenum); goto fail_mm_repo_module_config; } +#undef CheckNull +#define CheckNull(x) if ((!(x)->value) || (!(*((x)->value)))) { config_error("%s:%i: missing parameter", repo_url, (x)->line_number); goto fail_mm_repo_module_config; } /** Parse a module { } line from a repository */ ManagedModule *mm_repo_module_config(char *repo_url, ConfigEntry *ce) @@ -660,84 +661,84 @@ ManagedModule *mm_repo_module_config(char *repo_url, ConfigEntry *ce) ConfigEntry *cep; ManagedModule *m = safe_alloc(sizeof(ManagedModule)); - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%d: module { } with no name", - repo_url, ce->ce_varlinenum); + repo_url, ce->line_number); goto fail_mm_repo_module_config; } - if (strncmp(ce->ce_vardata, "third/", 6)) + if (strncmp(ce->value, "third/", 6)) { config_error("%s:%d: module { } name must start with: third/", - repo_url, ce->ce_varlinenum); + repo_url, ce->line_number); goto fail_mm_repo_module_config; } - if (!mm_valid_module_name(ce->ce_vardata)) + if (!mm_valid_module_name(ce->value)) { config_error("%s:%d: module { } with illegal name: %s", - repo_url, ce->ce_varlinenum, ce->ce_vardata); + repo_url, ce->line_number, ce->value); goto fail_mm_repo_module_config; } - safe_strdup(m->name, ce->ce_vardata); + safe_strdup(m->name, ce->value); safe_strdup(m->repo_url, repo_url); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "source")) + if (!strcmp(cep->name, "source")) { CheckNull(cep); - safe_strdup(m->source, cep->ce_vardata); + safe_strdup(m->source, cep->value); } - else if (!strcmp(cep->ce_varname, "sha256sum")) + else if (!strcmp(cep->name, "sha256sum")) { CheckNull(cep); - safe_strdup(m->sha256sum, cep->ce_vardata); + safe_strdup(m->sha256sum, cep->value); } - else if (!strcmp(cep->ce_varname, "version")) + else if (!strcmp(cep->name, "version")) { CheckNull(cep); - safe_strdup(m->version, cep->ce_vardata); + safe_strdup(m->version, cep->value); } - else if (!strcmp(cep->ce_varname, "author")) + else if (!strcmp(cep->name, "author")) { CheckNull(cep); - safe_strdup(m->author, cep->ce_vardata); + safe_strdup(m->author, cep->value); } - else if (!strcmp(cep->ce_varname, "troubleshooting")) + else if (!strcmp(cep->name, "troubleshooting")) { CheckNull(cep); - safe_strdup(m->troubleshooting, cep->ce_vardata); + safe_strdup(m->troubleshooting, cep->value); } - else if (!strcmp(cep->ce_varname, "documentation")) + else if (!strcmp(cep->name, "documentation")) { CheckNull(cep); - safe_strdup(m->documentation, cep->ce_vardata); + safe_strdup(m->documentation, cep->value); } - else if (!strcmp(cep->ce_varname, "min-unrealircd-version")) + else if (!strcmp(cep->name, "min-unrealircd-version")) { CheckNull(cep); - safe_strdup(m->min_unrealircd_version, cep->ce_vardata); + safe_strdup(m->min_unrealircd_version, cep->value); } - else if (!strcmp(cep->ce_varname, "max-unrealircd-version")) + else if (!strcmp(cep->name, "max-unrealircd-version")) { CheckNull(cep); - safe_strdup(m->max_unrealircd_version, cep->ce_vardata); + safe_strdup(m->max_unrealircd_version, cep->value); } - else if (!strcmp(cep->ce_varname, "description")) + else if (!strcmp(cep->name, "description")) { CheckNull(cep); - safe_strdup(m->description, cep->ce_vardata); + safe_strdup(m->description, cep->value); } - else if (!strcmp(cep->ce_varname, "post-install-text")) + else if (!strcmp(cep->name, "post-install-text")) { - if (cep->ce_entries) + if (cep->items) { ConfigEntry *cepp; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - addmultiline(&m->post_install_text, cepp->ce_varname); + for (cepp = cep->items; cepp; cepp = cepp->next) + addmultiline(&m->post_install_text, cepp->name); } else { CheckNull(cep); - addmultiline(&m->post_install_text, cep->ce_vardata); + addmultiline(&m->post_install_text, cep->value); } } /* unknown items are silently ignored for future compatibility */ @@ -745,43 +746,43 @@ ManagedModule *mm_repo_module_config(char *repo_url, ConfigEntry *ce) if (!m->source) { - config_error("%s:%d: module::source missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::source missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } if (!m->sha256sum) { - config_error("%s:%d: module::sha256sum missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::sha256sum missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } if (!m->version) { - config_error("%s:%d: module::version missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::version missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } if (!m->author) { - config_error("%s:%d: module::author missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::author missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } if (!m->documentation) { - config_error("%s:%d: module::documentation missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::documentation missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } if (!m->troubleshooting) { - config_error("%s:%d: module::troubleshooting missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::troubleshooting missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } if (!m->min_unrealircd_version) { - config_error("%s:%d: module::min-unrealircd-version missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::min-unrealircd-version missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } /* max_unrealircd_version is optional */ if (!m->description) { - config_error("%s:%d: module::description missing", repo_url, ce->ce_varlinenum); + config_error("%s:%d: module::description missing", repo_url, ce->line_number); goto fail_mm_repo_module_config; } /* post_install_text is optional */ @@ -805,9 +806,9 @@ int mm_parse_repo_db(char *url, char *filename) if (!cf) return 0; /* eg: parse errors */ - for (ce = cf->cf_entries; ce; ce = ce->ce_next) + for (ce = cf->items; ce; ce = ce->next) { - if (!strcmp(ce->ce_varname, "module")) + if (!strcmp(ce->name, "module")) { m = mm_repo_module_config(url, ce); if (!m) @@ -957,7 +958,7 @@ int mm_get_module_status(ManagedModule *m) { FILE *fd; char fname[512]; - char *our_sha256sum; + const char *our_sha256sum; snprintf(fname, sizeof(fname), "%s/src/modules/%s.c", BUILDDIR, m->name); if (!file_exists(fname)) @@ -1142,7 +1143,7 @@ int mm_compile(ManagedModule *m, char *tmpfile, int test) { char newpath[512]; char cmd[512]; - char *basename; + const char *basename; char *p; FILE *fd; char buf[512]; @@ -1213,15 +1214,15 @@ int mm_compile(ManagedModule *m, char *tmpfile, int test) */ void mm_install_module(ManagedModule *m) { - char *basename = unreal_getfilename(m->source); + const char *basename = unreal_getfilename(m->source); char *tmpfile; - char *sha256; + const char *sha256; if (!basename) basename = "mod.c"; tmpfile = unreal_mktemp(TMPDIR, basename); - printf("Downloading %s from %s...\n", m->name, m->source); + printf("ConfigResourceing %s from %s...\n", m->name, m->source); if (!mm_http_request(m->source, tmpfile, 1)) { fprintf(stderr, "Repository %s seems to list a module file that cannot be retrieved (%s).\n", m->repo_url, m->source); @@ -1535,7 +1536,7 @@ void print_md_block(FILE *fdo, ManagedModule *m) void mm_generate_repository_usage(void) { - fprintf(stderr, "Usage: ./unrealircd module generate-repository \n"); + fprintf(stderr, "Usage: ./unrealircd module generate-repository [optional-minimum-version-filter]\n"); fprintf(stderr, "For example: ./unrealircd module generate-repository https://www.unrealircd.org/modules/ src/modules/third modules.lst\n"); } @@ -1547,6 +1548,7 @@ void mm_generate_repository(int argc, char *args[]) char *urlbasepath; char *dirname; char *outputfile; + char *minversion; char modname[128]; char fullname[512]; ManagedModule *m; @@ -1555,6 +1557,8 @@ void mm_generate_repository(int argc, char *args[]) urlbasepath = args[1]; dirname = args[2]; outputfile = args[3]; + minversion = args[4]; + if (!urlbasepath || !dirname || !outputfile) { mm_generate_repository_usage(); @@ -1587,6 +1591,7 @@ void mm_generate_repository(int argc, char *args[]) char *fname = dir->d_name; if (filename_has_suffix(fname, ".c")) { + int hide = 0; snprintf(fullname, sizeof(fullname), "%s/%s", dirname, fname); snprintf(modname, sizeof(modname), "third/%s", filename_strip_suffix(fname, ".c")); printf("Processing: %s\n", modname); @@ -1599,7 +1604,12 @@ void mm_generate_repository(int argc, char *args[]) m->sha256sum = strdup(sha256sum_file(fullname)); m->source = safe_alloc(512); snprintf(m->source, 512, "%s%s.c", urlbasepath, modname + 6); - print_md_block(fdo, m); + /* filter */ + if (minversion && m->min_unrealircd_version && strncmp(minversion, m->min_unrealircd_version, strlen(minversion))) + hide = 1; + /* /filter */ + if (!hide) + print_md_block(fdo, m); free_managed_module(m); m = NULL; } @@ -1611,7 +1621,7 @@ void mm_generate_repository(int argc, char *args[]) void mm_parse_c_file(int argc, char *args[]) { char *fullname = args[1]; - char *basename; + const char *basename; char modname[256]; ManagedModule *m; diff --git a/src/modules.c b/src/modules.c index d7de75c..208c6b5 100644 --- a/src/modules.c +++ b/src/modules.c @@ -52,7 +52,7 @@ Module *Module_make(ModuleHeader *header, #ifdef UNDERSCORE /* dlsym for OpenBSD */ -void *obsd_dlsym(void *handle, char *symbol) +void *obsd_dlsym(void *handle, const char *symbol) { size_t buflen = strlen(symbol) + 2; char *obsdsymbol = safe_alloc(buflen); @@ -69,7 +69,7 @@ void *obsd_dlsym(void *handle, char *symbol) } #endif -void deletetmp(char *path) +void deletetmp(const char *path) { #ifndef NOREMOVETMP if (!loop.config_test) @@ -88,7 +88,7 @@ void DeleteTempModules(void) { config_error("Unable to open temp directory %s: %s, please create one with the appropriate permissions", TMPDIR, strerror(errno)); - if (!loop.ircd_booted) + if (!loop.booted) exit(7); return; } @@ -131,7 +131,7 @@ void DeleteTempModules(void) #endif } -Module *Module_Find(char *name) +Module *Module_Find(const char *name) { Module *p; @@ -149,16 +149,16 @@ Module *Module_Find(char *name) } -int parse_modsys_version(char *version) +int parse_modsys_version(const char *version) { - if (!strcmp(version, "unrealircd-5")) - return 0x500000; + if (!strcmp(version, "unrealircd-6")) + return 0x600000; return 0; } void make_compiler_string(char *buf, size_t buflen, unsigned int ver) { -unsigned int maj, min, plevel; + unsigned int maj, min, plevel; if (ver == 0) { @@ -180,7 +180,7 @@ unsigned int maj, min, plevel; * something like "/home/xyz/unrealircd/modules/third/la.so * (and other tricks) */ -char *Module_TransformPath(char *path_) +const char *Module_TransformPath(const char *path_) { static char path[1024]; @@ -202,17 +202,18 @@ char *Module_TransformPath(char *path_) } /** This function is the inverse of Module_TransformPath() */ -char *Module_GetRelPath(char *fullpath) +const char *Module_GetRelPath(const char *fullpath) { static char buf[512]; char prefix[512]; - char *s = fullpath; + const char *without_prefix = fullpath; + char *s; /* Strip the prefix */ snprintf(prefix, sizeof(prefix), "%s/", MODULESDIR); if (!strncasecmp(fullpath, prefix, strlen(prefix))) - s += strlen(prefix); - strlcpy(buf, s, sizeof(buf)); + without_prefix += strlen(prefix); + strlcpy(buf, without_prefix, sizeof(buf)); /* Strip the suffix */ s = strstr(buf, MODULE_SUFFIX); @@ -225,7 +226,7 @@ char *Module_GetRelPath(char *fullpath) /** Validate a modules' ModuleHeader. * @returns Error message is returned, or NULL if everything is OK. */ -static char *validate_mod_header(char *relpath, ModuleHeader *mod_header) +static const char *validate_mod_header(const char *relpath, ModuleHeader *mod_header) { char *p; static char buf[256]; @@ -260,7 +261,7 @@ static char *validate_mod_header(char *relpath, ModuleHeader *mod_header) return NULL; /* SUCCESS */ } -int module_already_in_testing(char *relpath) +int module_already_in_testing(const char *relpath) { Module *m; for (m = Modules; m; m = m->next) @@ -277,7 +278,7 @@ int module_already_in_testing(char *relpath) /* * Returns an error if insucessful .. yes NULL is OK! */ -char *Module_Create(char *path_) +const char *Module_Create(const char *path_) { #ifdef _WIN32 HMODULE Mod; @@ -291,15 +292,14 @@ char *Module_Create(char *path_) char *Mod_Version; unsigned int *compiler_version; static char errorbuf[1024]; - char *path, *relpath, *tmppath; + const char *path, *relpath, *tmppath; ModuleHeader *mod_header = NULL; int ret = 0; - char *reterr; + const char *reterr; Module *mod = NULL, **Mod_Handle = NULL; char *expectedmodversion = our_mod_version; unsigned int expectedcompilerversion = our_compiler_version; long modsys_ver = 0; - Debug((DEBUG_DEBUG, "Attempting to load module from %s", path_)); path = Module_TransformPath(path_); @@ -447,7 +447,7 @@ char *Module_Create(char *path_) else { /* Return the error .. */ - return ((char *)irc_dlerror()); + return irc_dlerror(); } } @@ -524,9 +524,6 @@ void FreeModObj(ModuleObject *obj, Module *m) else if (obj->type == MOBJ_VERSIONFLAG) { VersionflagDel(obj->object.versionflag, m); } - else if (obj->type == MOBJ_SNOMASK) { - SnomaskDel(obj->object.snomask); - } else if (obj->type == MOBJ_UMODE) { UmodeDel(obj->object.umode); } @@ -565,7 +562,9 @@ void FreeModObj(ModuleObject *obj, Module *m) } else { - ircd_log(LOG_ERROR, "FreeModObj() called for unknown object"); + unreal_log(ULOG_FATAL, "module", "FREEMODOBJ_UNKNOWN_TYPE", NULL, + "[BUG] FreeModObj() called for unknown object (type $type)", + log_data_integer("type", obj->type)); abort(); } } @@ -655,8 +654,6 @@ int Module_free(Module *mod) for (cp = mod->children; cp; cp = cp->next) { - sendto_realops("Unloading child module %s", - cp->child->header->name); Module_Unload(cp->child->header->name); } for (objs = mod->objects; objs; objs = next) { @@ -693,7 +690,7 @@ int Module_free(Module *mod) * 1 Module unloaded * 2 Module wishes delayed unloading, has placed event */ -int Module_Unload(char *name) +int Module_Unload(const char *name) { Module *m; int (*Mod_Unload)(); @@ -734,11 +731,6 @@ void module_loadall(void) iFP fp; Module *mi, *next; - if (!loop.ircd_booted) - { - sendto_realops("Ehh, !loop.ircd_booted in module_loadall()"); - return ; - } /* Run through all modules and check for module load */ for (mi = Modules; mi; mi = next) { @@ -799,12 +791,12 @@ CMD_FUNC(cmd_module) all = 1; if (MyUser(client) && !IsOper(client) && all) - client->local->since += 7; /* Lag them up. Big list. */ + add_fake_lag(client, 7000); /* Lag them up. Big list. */ - if ((parc > 2) && (hunt_server(client, recv_mtags, ":%s MODULE %s :%s", 2, parc, parv) != HUNTED_ISME)) + if ((parc > 2) && (hunt_server(client, recv_mtags, "MODULE", 2, parc, parv) != HUNTED_ISME)) return; - if ((parc == 2) && (parv[1][0] != '-') && (hunt_server(client, recv_mtags, ":%s MODULE :%s", 1, parc, parv) != HUNTED_ISME)) + if ((parc == 2) && (parv[1][0] != '-') && (hunt_server(client, recv_mtags, "MODULE", 1, parc, parv) != HUNTED_ISME)) return; if (all) @@ -884,7 +876,7 @@ CMD_FUNC(cmd_module) sendtxtnumeric(client, "Override: %s", tmp); } -Hooktype *HooktypeFind(char *string) { +Hooktype *HooktypeFind(const char *string) { Hooktype *hooktype; for (hooktype = Hooktypes; hooktype->string ;hooktype++) { if (!strcasecmp(hooktype->string, string)) @@ -984,7 +976,7 @@ void VersionflagDel(Versionflag *vflag, Module *module) } } -Hook *HookAddMain(Module *module, int hooktype, int priority, int (*func)(), void (*vfunc)(), char *(*cfunc)()) +Hook *HookAddMain(Module *module, int hooktype, int priority, int (*func)(), void (*vfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) { Hook *p; @@ -993,8 +985,10 @@ Hook *HookAddMain(Module *module, int hooktype, int priority, int (*func)(), voi p->func.intfunc = func; if (vfunc) p->func.voidfunc = vfunc; - if (cfunc) - p->func.pcharfunc = cfunc; + if (stringfunc) + p->func.stringfunc = stringfunc; + if (conststringfunc) + p->func.conststringfunc = conststringfunc; p->type = hooktype; p->owner = module; p->priority = priority; @@ -1036,17 +1030,40 @@ Hook *HookDel(Hook *hook) return NULL; } -Callback *CallbackAddMain(Module *module, int cbtype, int (*func)(), void (*vfunc)(), char *(*cfunc)()) +static int num_callbacks(int cbtype) +{ +Callback *e; +int cnt = 0; + + for (e = Callbacks[cbtype]; e; e = e->next) + if (!e->willberemoved) + cnt++; + + return cnt; +} + +Callback *CallbackAddMain(Module *module, int cbtype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) { Callback *p; + if (num_callbacks(cbtype) > 0) + { + if (module) + module->errorcode = MODERR_EXISTS; + return NULL; + } + p = safe_alloc(sizeof(Callback)); if (func) p->func.intfunc = func; if (vfunc) p->func.voidfunc = vfunc; - if (cfunc) - p->func.pcharfunc = cfunc; + if (pvfunc) + p->func.pvoidfunc = pvfunc; + if (stringfunc) + p->func.stringfunc = stringfunc; + if (conststringfunc) + p->func.conststringfunc = conststringfunc; p->type = cbtype; p->owner = module; AddListItem(p, Callbacks[cbtype]); @@ -1086,7 +1103,7 @@ Callback *CallbackDel(Callback *cb) return NULL; } -CommandOverride *CommandOverrideAddEx(Module *module, char *name, int priority, OverrideCmdFunc function) +CommandOverride *CommandOverrideAdd(Module *module, const char *name, int priority, OverrideCmdFunc function) { RealCommand *p; CommandOverride *ovr; @@ -1108,7 +1125,7 @@ CommandOverride *CommandOverrideAddEx(Module *module, char *name, int priority, } ovr = safe_alloc(sizeof(CommandOverride)); ovr->func = function; - ovr->owner = module; /* TODO: module objects */ + ovr->owner = module; ovr->priority = priority; if (module) { @@ -1127,11 +1144,6 @@ CommandOverride *CommandOverrideAddEx(Module *module, char *name, int priority, return ovr; } -CommandOverride *CommandOverrideAdd(Module *module, char *name, OverrideCmdFunc function) -{ - return CommandOverrideAddEx(module, name, 0, function); -} - void CommandOverrideDel(CommandOverride *cmd) { DelListItem(cmd, cmd->command->overriders); @@ -1157,7 +1169,7 @@ void CommandOverrideDel(CommandOverride *cmd) safe_free(cmd); } -void CallCommandOverride(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, char *parv[]) +void CallCommandOverride(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, const char *parv[]) { if (ovr->next) ovr->next->func(ovr->next, client, mtags, parc, parv); @@ -1171,21 +1183,27 @@ EVENT(e_unload_module_delayed) int i; i = Module_Unload(name); if (i == 1) - sendto_realops("Unloaded module %s", name); + { + unreal_log(ULOG_INFO, "module", "MODULE_UNLOADING_DELAYED", NULL, + "Unloading module $module_name (was delayed earlier)", + log_data_string("module_name", name)); + } safe_free(name); extcmodes_check_for_changes(); umodes_check_for_changes(); return; } -void unload_all_modules(void) +void unload_all_modules(void) { Module *m; int (*Mod_Unload)(); for (m = Modules; m; m = m->next) { #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "Unloading %s...", m->header->name); + unreal_log(ULOG_DEBUG, "module", "MODULE_UNLOADING", NULL, + "Unloading module $module_name", + log_data_string("module_name", m->header->name)); #endif irc_dlsym(m->dll, "Mod_Unload", Mod_Unload); if (Mod_Unload) @@ -1237,18 +1255,6 @@ const char *ModuleGetErrorStr(Module *module) return module_error_str[module->errorcode]; } -static int num_callbacks(int cbtype) -{ -Callback *e; -int cnt = 0; - - for (e = Callbacks[cbtype]; e; e = e->next) - if (!e->willberemoved) - cnt++; - - return cnt; -} - /** Ensure that all required callbacks are in place and meet * all specified requirements */ @@ -1262,11 +1268,21 @@ int i; { config_error("ERROR: Multiple callbacks loaded for type %d. " "Make sure you only load 1 module of 1 type (eg: only 1 cloaking module)", - i); /* TODO: make more clear? */ + i); return -1; } } - + + if (!Callbacks[CALLBACKTYPE_CLOAK_KEY_CHECKSUM]) + { + unreal_log(ULOG_ERROR, "config", "NO_CLOAKING_MODULE", NULL, + "No cloaking module loaded, you must load 1 of these modulese:\n" + "1) cloak_sha256 - if you are a new network starting with UnrealIRCd 6\n" + "2) cloak_md5 - the old one if migrating an existing network from UnrealIRCd 3.2/4/5\n" + "3) cloak_none - if you don't want to use cloaking at all\n" + "See also https://www.unrealircd.org/docs/FAQ#choose-a-cloaking-module"); + return -1; + } return 0; } @@ -1311,7 +1327,7 @@ const char *our_dlerror(void) * @note The name is checked against the module name, * this can be a problem if two modules have the same name. */ -int is_module_loaded(char *name) +int is_module_loaded(const char *name) { Module *mi; for (mi = Modules; mi; mi = mi->next) @@ -1325,17 +1341,17 @@ int is_module_loaded(char *name) return 0; } -static char *mod_var_name(ModuleInfo *modinfo, char *varshortname) +static const char *mod_var_name(ModuleInfo *modinfo, const char *varshortname) { static char fullname[512]; snprintf(fullname, sizeof(fullname), "%s:%s", modinfo->handle->header->name, varshortname); return fullname; } -int LoadPersistentPointerX(ModuleInfo *modinfo, char *varshortname, void **var, void (*free_variable)(ModData *m)) +int LoadPersistentPointerX(ModuleInfo *modinfo, const char *varshortname, void **var, void (*free_variable)(ModData *m)) { ModDataInfo *m; - char *fullname = mod_var_name(modinfo, varshortname); + const char *fullname = mod_var_name(modinfo, varshortname); m = findmoddata_byname(fullname, MODDATATYPE_LOCAL_VARIABLE); if (m) @@ -1346,27 +1362,28 @@ int LoadPersistentPointerX(ModuleInfo *modinfo, char *varshortname, void **var, ModDataInfo mreq; memset(&mreq, 0, sizeof(mreq)); mreq.type = MODDATATYPE_LOCAL_VARIABLE; - mreq.name = fullname; + mreq.name = strdup(fullname); mreq.free = free_variable; m = ModDataAdd(modinfo->handle, mreq); moddata_local_variable(m).ptr = NULL; + safe_free(mreq.name); return 0; } } -void SavePersistentPointerX(ModuleInfo *modinfo, char *varshortname, void *var) +void SavePersistentPointerX(ModuleInfo *modinfo, const char *varshortname, void *var) { ModDataInfo *m; - char *fullname = mod_var_name(modinfo, varshortname); + const char *fullname = mod_var_name(modinfo, varshortname); m = findmoddata_byname(fullname, MODDATATYPE_LOCAL_VARIABLE); moddata_local_variable(m).ptr = var; } -int LoadPersistentIntX(ModuleInfo *modinfo, char *varshortname, int *var) +int LoadPersistentIntX(ModuleInfo *modinfo, const char *varshortname, int *var) { ModDataInfo *m; - char *fullname = mod_var_name(modinfo, varshortname); + const char *fullname = mod_var_name(modinfo, varshortname); m = findmoddata_byname(fullname, MODDATATYPE_LOCAL_VARIABLE); if (m) @@ -1377,27 +1394,28 @@ int LoadPersistentIntX(ModuleInfo *modinfo, char *varshortname, int *var) ModDataInfo mreq; memset(&mreq, 0, sizeof(mreq)); mreq.type = MODDATATYPE_LOCAL_VARIABLE; - mreq.name = fullname; + mreq.name = strdup(fullname); mreq.free = NULL; m = ModDataAdd(modinfo->handle, mreq); moddata_local_variable(m).i = 0; + safe_free(mreq.name); return 0; } } -void SavePersistentIntX(ModuleInfo *modinfo, char *varshortname, int var) +void SavePersistentIntX(ModuleInfo *modinfo, const char *varshortname, int var) { ModDataInfo *m; - char *fullname = mod_var_name(modinfo, varshortname); + const char *fullname = mod_var_name(modinfo, varshortname); m = findmoddata_byname(fullname, MODDATATYPE_LOCAL_VARIABLE); moddata_local_variable(m).i = var; } -int LoadPersistentLongX(ModuleInfo *modinfo, char *varshortname, long *var) +int LoadPersistentLongX(ModuleInfo *modinfo, const char *varshortname, long *var) { ModDataInfo *m; - char *fullname = mod_var_name(modinfo, varshortname); + const char *fullname = mod_var_name(modinfo, varshortname); m = findmoddata_byname(fullname, MODDATATYPE_LOCAL_VARIABLE); if (m) @@ -1408,18 +1426,19 @@ int LoadPersistentLongX(ModuleInfo *modinfo, char *varshortname, long *var) ModDataInfo mreq; memset(&mreq, 0, sizeof(mreq)); mreq.type = MODDATATYPE_LOCAL_VARIABLE; - mreq.name = fullname; + mreq.name = strdup(fullname); mreq.free = NULL; m = ModDataAdd(modinfo->handle, mreq); moddata_local_variable(m).l = 0; + safe_free(mreq.name); return 0; } } -void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long var) +void SavePersistentLongX(ModuleInfo *modinfo, const char *varshortname, long var) { ModDataInfo *m; - char *fullname = mod_var_name(modinfo, varshortname); + const char *fullname = mod_var_name(modinfo, varshortname); m = findmoddata_byname(fullname, MODDATATYPE_LOCAL_VARIABLE); moddata_local_variable(m).l = var; diff --git a/src/modules/Makefile.in b/src/modules/Makefile.in index cdc560f..62ee898 100644 --- a/src/modules/Makefile.in +++ b/src/modules/Makefile.in @@ -28,13 +28,14 @@ INCLUDES = ../include/channel.h \ ../include/ircsprintf.h \ ../include/license.h \ ../include/modules.h ../include/modversion.h ../include/msg.h \ - ../include/numeric.h ../include/proto.h ../include/dns.h \ + ../include/numeric.h ../include/dns.h \ ../include/resource.h ../include/setup.h \ ../include/struct.h ../include/sys.h \ - ../include/types.h ../include/url.h \ + ../include/types.h \ ../include/version.h ../include/whowas.h -R_MODULES= \ +MODULES= \ + cloak_md5.so cloak_sha256.so cloak_none.so \ sethost.so chghost.so chgident.so setname.so \ setident.so sdesc.so svsmode.so swhois.so\ svsmotd.so svsnline.so who_old.so whox.so mkpasswd.so \ @@ -49,7 +50,7 @@ R_MODULES= \ invite.so list.so time.so svskill.so sjoin.so \ pass.so userhost.so ison.so silence.so knock.so \ umode2.so squit.so protoctl.so addomotd.so \ - wallops.so admin.so globops.so locops.so \ + admin.so globops.so locops.so \ trace.so netinfo.so links.so help.so rules.so \ close.so map.so eos.so server.so stats.so \ dccdeny.so whowas.so \ @@ -71,604 +72,48 @@ R_MODULES= \ account-tag.so labeled-response.so link-security.so \ message-ids.so plaintext-policy.so server-time.so sts.so \ echo-message.so userip-tag.so userhost-tag.so \ - bot-tag.so \ - reply-tag.so typing-indicator.so \ + bot-tag.so reply-tag.so json-log-tag.so \ + typing-indicator.so \ ident_lookup.so history.so chathistory.so \ - targetfloodprot.so clienttagdeny.so + targetfloodprot.so clienttagdeny.so watch-backend.so \ + monitor.so slog.so tls_cipher.so operinfo.so \ + unreal_server_compat.so \ + extended-monitor.so geoip_csv.so \ + geoip_base.so extjwt.so \ + $(GEOIP_CLASSIC_OBJECTS) $(GEOIP_MAXMIND_OBJECTS) -MODULES=cloak.so $(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ RM=@RM@ +.SUFFIXES: +.SUFFIXES: .c .h .so + all: build build: $(MODULES) cd chanmodes; $(MAKE) all cd usermodes; $(MAKE) all - cd snomasks; $(MAKE) all cd extbans; $(MAKE) all cd third; $(MAKE) all -############################################################################# -# .so's section -############################################################################# - -chgname.so: chgname.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o chgname.so chgname.c - -kill.so: kill.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o kill.so kill.c - -lag.so: lag.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o lag.so lag.c - -message.so: message.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o message.so message.c - -oper.so: oper.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o oper.so oper.c - -pingpong.so: pingpong.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o pingpong.so pingpong.c - -quit.so: quit.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o quit.so quit.c - -sendumode.so: sendumode.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sendumode.so sendumode.c - -sqline.so: sqline.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sqline.so sqline.c - -tsctl.so: tsctl.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o tsctl.so tsctl.c - -unsqline.so: unsqline.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o unsqline.so unsqline.c - -whois.so: whois.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o whois.so whois.c - -sethost.so: sethost.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sethost.so sethost.c - -chghost.so: chghost.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o chghost.so chghost.c - -chgident.so: chgident.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o chgident.so chgident.c - -setident.so: setident.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o setident.so setident.c - -setname.so: setname.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o setname.so setname.c - -sdesc.so: sdesc.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sdesc.so sdesc.c - -svsmode.so: svsmode.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svsmode.so svsmode.c - -swhois.so: swhois.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o swhois.so swhois.c - -svsmotd.so: svsmotd.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svsmotd.so svsmotd.c - -svsnline.so: svsnline.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svsnline.so svsnline.c - -who_old.so: who_old.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o who_old.so who_old.c - -whox.so: whox.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o whox.so whox.c - -mkpasswd.so: mkpasswd.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o mkpasswd.so mkpasswd.c - -away.so: away.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o away.so away.c - -svsnoop.so: svsnoop.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svsnoop.so svsnoop.c - -svsnick.so: svsnick.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svsnick.so svsnick.c - -tkl.so: tkl.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o tkl.so tkl.c - -vhost.so: vhost.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o vhost.so vhost.c - -cycle.so: cycle.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o cycle.so cycle.c - -svsjoin.so: svsjoin.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svsjoin.so svsjoin.c - -svspart.so: svspart.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svspart.so svspart.c - -svslusers.so: svslusers.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svslusers.so svslusers.c - -svswatch.so: svswatch.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svswatch.so svswatch.c - -svssilence.so: svssilence.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svssilence.so svssilence.c - -sendsno.so: sendsno.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sendsno.so sendsno.c - -svssno.so: svssno.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svssno.so svssno.c - -sajoin.so: sajoin.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sajoin.so sajoin.c - -sapart.so: sapart.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sapart.so sapart.c - -samode.so: samode.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o samode.so samode.c - -kick.so: kick.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o kick.so kick.c - -topic.so: topic.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o topic.so topic.c - -invite.so: invite.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o invite.so invite.c - -list.so: list.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o list.so list.c - -time.so: time.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o time.so time.c - -svskill.so: svskill.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svskill.so svskill.c - -sjoin.so: sjoin.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sjoin.so sjoin.c - -pass.so: pass.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o pass.so pass.c - -userhost.so: userhost.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o userhost.so userhost.c - -ison.so: ison.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o ison.so ison.c - -silence.so: silence.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o silence.so silence.c - -knock.so: knock.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o knock.so knock.c - -umode2.so: umode2.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o umode2.so umode2.c - -squit.so: squit.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o squit.so squit.c - -protoctl.so: protoctl.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o protoctl.so protoctl.c - -addmotd.so: addmotd.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o addmotd.so addmotd.c - -addomotd.so: addomotd.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o addomotd.so addomotd.c - -wallops.so: wallops.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o wallops.so wallops.c - -admin.so: admin.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o admin.so admin.c - -globops.so: globops.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o globops.so globops.c - -locops.so: locops.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o locops.so locops.c - -trace.so: trace.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o trace.so trace.c - -netinfo.so: netinfo.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o netinfo.so netinfo.c - -links.so: links.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o links.so links.c - -help.so: help.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o help.so help.c - -rules.so: rules.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o rules.so rules.c - -close.so: close.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o close.so close.c - -map.so: map.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o map.so map.c - -eos.so: eos.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o eos.so eos.c - -server.so: server.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o server.so server.c - -stats.so: stats.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o stats.so stats.c - -dccdeny.so: dccdeny.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o dccdeny.so dccdeny.c - -whowas.so: whowas.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o whowas.so whowas.c - -connect.so: connect.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o connect.so connect.c - -dccallow.so: dccallow.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o dccallow.so dccallow.c - -userip.so: userip.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o userip.so userip.c - -nick.so: nick.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nick.so nick.c - -user.so: user.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o user.so user.c - -mode.so: mode.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o mode.so mode.c - -watch.so: watch.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o watch.so watch.c - -part.so: part.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o part.so part.c - -join.so: join.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o join.so join.c - -motd.so: motd.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o motd.so motd.c - -opermotd.so: opermotd.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o opermotd.so opermotd.c - -botmotd.so: botmotd.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o botmotd.so botmotd.c - -lusers.so: lusers.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o lusers.so lusers.c - -names.so: names.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o names.so names.c - -svsnolag.so: svsnolag.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o svsnolag.so svsnolag.c - -starttls.so: starttls.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o starttls.so starttls.c - -webredir.so: webredir.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o webredir.so webredir.c - -cap.so: cap.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o cap.so cap.c - -sasl.so: sasl.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sasl.so sasl.c - -md.so: md.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o md.so md.c - -certfp.so: certfp.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o certfp.so certfp.c - -tls_antidos.so: tls_antidos.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o tls_antidos.so tls_antidos.c - -webirc.so: webirc.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o webirc.so webirc.c - -websocket.so: websocket.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o websocket.so websocket.c - -blacklist.so: blacklist.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o blacklist.so blacklist.c - -jointhrottle.so: jointhrottle.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o jointhrottle.so jointhrottle.c - -antirandom.so: antirandom.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o antirandom.so antirandom.c - -hideserver.so: hideserver.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o hideserver.so hideserver.c - -jumpserver.so: jumpserver.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o jumpserver.so jumpserver.c - -ircops.so: ircops.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o ircops.so ircops.c - -staff.so: staff.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o staff.so staff.c - -nocodes.so: nocodes.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nocodes.so nocodes.c - -charsys.so: charsys.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o charsys.so charsys.c - -antimixedutf8.so: antimixedutf8.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o antimixedutf8.so antimixedutf8.c - -authprompt.so: authprompt.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o authprompt.so authprompt.c - -sinfo.so: sinfo.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sinfo.so sinfo.c - -reputation.so: reputation.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o reputation.so reputation.c - -connthrottle.so: connthrottle.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o connthrottle.so connthrottle.c - -history_backend_mem.so: history_backend_mem.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o history_backend_mem.so history_backend_mem.c - -history_backend_null.so: history_backend_null.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o history_backend_null.so history_backend_null.c - -tkldb.so: tkldb.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o tkldb.so tkldb.c - -channeldb.so: channeldb.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o channeldb.so channeldb.c - -restrict-commands.so: restrict-commands.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o restrict-commands.so restrict-commands.c - -rmtkl.so: rmtkl.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o rmtkl.so rmtkl.c - -message-tags.so: message-tags.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o message-tags.so message-tags.c - -batch.so: batch.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o batch.so batch.c - -account-tag.so: account-tag.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o account-tag.so account-tag.c - -labeled-response.so: labeled-response.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o labeled-response.so labeled-response.c - -link-security.so: link-security.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o link-security.so link-security.c - -message-ids.so: message-ids.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o message-ids.so message-ids.c - -plaintext-policy.so: plaintext-policy.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o plaintext-policy.so plaintext-policy.c - -server-time.so: server-time.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o server-time.so server-time.c - -sts.so: sts.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o sts.so sts.c - -echo-message.so: echo-message.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o echo-message.so echo-message.c - -userip-tag.so: userip-tag.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o userip-tag.so userip-tag.c - -userhost-tag.so: userhost-tag.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o userhost-tag.so userhost-tag.c - -bot-tag.so: bot-tag.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o bot-tag.so bot-tag.c - -reply-tag.so: reply-tag.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o reply-tag.so reply-tag.c - -typing-indicator.so: typing-indicator.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o typing-indicator.so typing-indicator.c - -require-module.so: require-module.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o require-module.so require-module.c - -account-notify.so: account-notify.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o account-notify.so account-notify.c - -ident_lookup.so: ident_lookup.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o ident_lookup.so ident_lookup.c - -history.so: history.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o history.so history.c - -chathistory.so: chathistory.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o chathistory.so chathistory.c - -targetfloodprot.so: targetfloodprot.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o targetfloodprot.so targetfloodprot.c - -clienttagdeny.so: clienttagdeny.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o clienttagdeny.so clienttagdeny.c - -############################################################################# -# capabilities -############################################################################# - -############################################################################# -# and now the remaining modules... -############################################################################# - -cloak.so: cloak.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o cloak.so cloak.c - clean: $(RM) -f *.o *.so *~ core cd chanmodes; $(MAKE) clean cd usermodes; $(MAKE) clean - cd snomasks; $(MAKE) clean cd extbans; $(MAKE) clean cd third; $(MAKE) clean + +# Generic *.so rule: +%.so: %.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ + -o $@ $< + +# geoip_classic requires extra library +geoip_classic.so: geoip_classic.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) $(GEOIP_CLASSIC_CFLAGS) -DDYNAMIC_LINKING \ + -o geoip_classic.so geoip_classic.c @LDFLAGS_PRIVATELIBS@ $(GEOIP_CLASSIC_LIBS) + +# geoip_maxmind requires another extra library +geoip_maxmind.so: geoip_maxmind.c $(INCLUDES) + $(CC) $(CFLAGS) $(MODULEFLAGS) $(LIBMAXMINDDB_CFLAGS) -DDYNAMIC_LINKING \ + -o geoip_maxmind.so geoip_maxmind.c @LDFLAGS_PRIVATELIBS@ $(LIBMAXMINDDB_LIBS) diff --git a/src/modules/account-notify.c b/src/modules/account-notify.c index b2e9c3b..9bd4100 100644 --- a/src/modules/account-notify.c +++ b/src/modules/account-notify.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "account-notify CAP", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ @@ -70,7 +70,7 @@ int account_notify_account_login(Client *client, MessageTag *recv_mtags) CAP_ACCOUNT_NOTIFY, mtags, ":%s ACCOUNT %s", client->name, - !isdigit(*client->user->svid) ? client->user->svid : "*"); + IsLoggedIn(client) ? client->user->account : "*"); free_message_tags(mtags); return 0; } diff --git a/src/modules/account-tag.c b/src/modules/account-tag.c index 523da07..b23578a 100644 --- a/src/modules/account-tag.c +++ b/src/modules/account-tag.c @@ -28,14 +28,14 @@ ModuleHeader MOD_HEADER "5.0", "account-tag CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ long CAP_ACCOUNT_TAG = 0L; -int account_tag_mtag_is_ok(Client *client, char *name, char *value); -void mtag_add_account(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int account_tag_mtag_is_ok(Client *client, const char *name, const char *value); +void mtag_add_account(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -75,7 +75,7 @@ MOD_UNLOAD() * syntax. * We simply allow account-tag ONLY from servers and with any syntax. */ -int account_tag_mtag_is_ok(Client *client, char *name, char *value) +int account_tag_mtag_is_ok(Client *client, const char *name, const char *value) { if (IsServer(client)) return 1; @@ -83,15 +83,15 @@ int account_tag_mtag_is_ok(Client *client, char *name, char *value) return 0; } -void mtag_add_account(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_account(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m; - if (client && client->user && (*client->user->svid != '*') && !isdigit(*client->user->svid)) + if (IsLoggedIn(client)) { m = safe_alloc(sizeof(MessageTag)); safe_strdup(m->name, "account"); - safe_strdup(m->value, client->user->svid); + safe_strdup(m->value, client->user->account); AddListItem(m, *mtag_list); } diff --git a/src/modules/addmotd.c b/src/modules/addmotd.c index 51d5b6f..6ba7f95 100644 --- a/src/modules/addmotd.c +++ b/src/modules/addmotd.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /addmotd", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -60,7 +60,7 @@ MOD_UNLOAD() CMD_FUNC(cmd_addmotd) { FILE *conf; - char *text; + const char *text; text = parc > 1 ? parv[1] : NULL; diff --git a/src/modules/addomotd.c b/src/modules/addomotd.c index ea0f1e8..140d044 100644 --- a/src/modules/addomotd.c +++ b/src/modules/addomotd.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /addomotd", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -60,7 +60,7 @@ MOD_UNLOAD() CMD_FUNC(cmd_addomotd) { FILE *conf; - char *text; + const char *text; text = parc > 1 ? parv[1] : NULL; diff --git a/src/modules/admin.c b/src/modules/admin.c index 6b79c39..064dd9b 100644 --- a/src/modules/admin.c +++ b/src/modules/admin.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /admin", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -61,7 +61,7 @@ CMD_FUNC(cmd_admin) if (IsUser(client)) { - if (hunt_server(client, recv_mtags, ":%s ADMIN :%s", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "ADMIN", 1, parc, parv) != HUNTED_ISME) return; } diff --git a/src/modules/antimixedutf8.c b/src/modules/antimixedutf8.c index 731ef4b..8d7eabb 100644 --- a/src/modules/antimixedutf8.c +++ b/src/modules/antimixedutf8.c @@ -48,7 +48,7 @@ ModuleHeader MOD_HEADER "1.0", "Mixed UTF8 character filter (look-alike character spam) - by Syzop", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; struct { @@ -99,22 +99,22 @@ int detect_script(const char *t) else if ((t[0] == 0xd3) && (t[1] >= 0x80) && (t[1] <= 0xbf)) return SCRIPT_CYRILLIC; - if((t[0] == 0xe4) && (t[1] >= 0xb8) && (t[1] <= 0xbf)) + if ((t[0] == 0xe4) && (t[1] >= 0xb8) && (t[1] <= 0xbf)) return SCRIPT_CJK; else if ((t[0] >= 0xe5) && (t[0] <= 0xe9) && (t[1] >= 0x80) && (t[1] <= 0xbf)) return SCRIPT_CJK; - if((t[0] == 0xea) && (t[1] >= 0xb0) && (t[1] <= 0xbf)) + if ((t[0] == 0xea) && (t[1] >= 0xb0) && (t[1] <= 0xbf)) return SCRIPT_HANGUL; else if ((t[0] >= 0xeb) && (t[0] <= 0xec) && (t[1] >= 0x80) && (t[1] <= 0xbf)) return SCRIPT_HANGUL; else if ((t[0] == 0xed) && (t[1] >= 0x80) && (t[1] <= 0x9f)) return SCRIPT_HANGUL; - if((t[0] == 0xe1) && (t[1] >= 0x90) && (t[1] <= 0x99)) + if ((t[0] == 0xe1) && (t[1] >= 0x90) && (t[1] <= 0x99)) return SCRIPT_CANADIAN; - if((t[0] == 0xe0) && (t[1] >= 0xb0) && (t[1] <= 0xb1)) + if ((t[0] == 0xe0) && (t[1] >= 0xb0) && (t[1] <= 0xb1)) return SCRIPT_TELUGU; if ((t[0] >= 'a') && (t[0] <= 'z')) @@ -206,12 +206,9 @@ CMD_OVERRIDE_FUNC(override_msg) score = lookalikespam_score(StripControlCodes(parv[2])); if ((score >= cfg.score) && !find_tkl_exception(TKL_ANTIMIXEDUTF8, client)) { - if (cfg.ban_action == BAN_ACT_KILL) - { - sendto_realops("[antimixedutf8] Killed connection from %s (score %d)", - GetIP(client), score); - } /* no else here!! */ - + unreal_log(ULOG_INFO, "antimixedutf8", "ANTIMIXEDUTF8_HIT", client, + "[antimixedutf8] Client $client.details hit score $score -- taking action", + log_data_integer("score", score)); if ((cfg.ban_action == BAN_ACT_BLOCK) || ((cfg.ban_action == BAN_ACT_SOFT_BLOCK) && !IsLoggedIn(client))) { @@ -246,10 +243,10 @@ MOD_INIT() MOD_LOAD() { - if (!CommandOverrideAdd(modinfo->handle, "PRIVMSG", override_msg)) + if (!CommandOverrideAdd(modinfo->handle, "PRIVMSG", 0, override_msg)) return MOD_FAILED; - if (!CommandOverrideAdd(modinfo->handle, "NOTICE", override_msg)) + if (!CommandOverrideAdd(modinfo->handle, "NOTICE", 0, override_msg)) return MOD_FAILED; return MOD_SUCCESS; @@ -286,45 +283,45 @@ int antimixedutf8_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *er return 0; /* We are only interrested in set::antimixedutf8... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "antimixedutf8")) + if (!ce || !ce->name || strcmp(ce->name, "antimixedutf8")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata) + if (!cep->value) { config_error("%s:%i: set::antimixedutf8::%s with no value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } else - if (!strcmp(cep->ce_varname, "score")) + if (!strcmp(cep->name, "score")) { - int v = atoi(cep->ce_vardata); + int v = atoi(cep->value); if ((v < 1) || (v > 99)) { config_error("%s:%i: set::antimixedutf8::score: must be between 1 - 99 (got: %d)", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, v); + cep->file->filename, cep->line_number, v); errors++; } } else - if (!strcmp(cep->ce_varname, "ban-action")) + if (!strcmp(cep->name, "ban-action")) { - if (!banact_stringtoval(cep->ce_vardata)) + if (!banact_stringtoval(cep->value)) { config_error("%s:%i: set::antimixedutf8::ban-action: unknown action '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } } else - if (!strcmp(cep->ce_varname, "ban-reason")) + if (!strcmp(cep->name, "ban-reason")) { } else - if (!strcmp(cep->ce_varname, "ban-time")) + if (!strcmp(cep->name, "ban-time")) { } else { config_error("%s:%i: unknown directive set::antimixedutf8::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } } @@ -340,26 +337,26 @@ int antimixedutf8_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::antimixedutf8... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "antimixedutf8")) + if (!ce || !ce->name || strcmp(ce->name, "antimixedutf8")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "score")) + if (!strcmp(cep->name, "score")) { - cfg.score = atoi(cep->ce_vardata); + cfg.score = atoi(cep->value); } else - if (!strcmp(cep->ce_varname, "ban-action")) + if (!strcmp(cep->name, "ban-action")) { - cfg.ban_action = banact_stringtoval(cep->ce_vardata); + cfg.ban_action = banact_stringtoval(cep->value); } else - if (!strcmp(cep->ce_varname, "ban-reason")) + if (!strcmp(cep->name, "ban-reason")) { - safe_strdup(cfg.ban_reason, cep->ce_vardata); + safe_strdup(cfg.ban_reason, cep->value); } else - if (!strcmp(cep->ce_varname, "ban-time")) + if (!strcmp(cep->name, "ban-time")) { - cfg.ban_time = config_checkval(cep->ce_vardata, CFG_TIME); + cfg.ban_time = config_checkval(cep->value, CFG_TIME); } } return 1; diff --git a/src/modules/antirandom.c b/src/modules/antirandom.c index 8522f36..9c30703 100644 --- a/src/modules/antirandom.c +++ b/src/modules/antirandom.c @@ -22,24 +22,13 @@ #include "unrealircd.h" -/* You can change this '//#undef' into '#define' if you want to see quite - * a flood for every user that connects (and on-load if cfg.fullstatus_on_load). - * Obviously only recommended for testing, use with care! - */ -#undef DEBUGMODE - -/** Change this 'undef' to 'define' to get performance information. - * This really only meant for debugging purposes. - */ -#undef TIMING - ModuleHeader MOD_HEADER = { "antirandom", "1.4", "Detect and ban users with random names", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #ifndef MAX @@ -515,7 +504,6 @@ struct { long ban_time; int convert_to_lowercase; int show_failedconnects; - int fullstatus_on_load; ConfigItem_mask *except_hosts; int except_webirc; } cfg; @@ -553,19 +541,14 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, antirandom_config_run); /* Some default values: */ - cfg.fullstatus_on_load = 1; cfg.convert_to_lowercase = 1; cfg.except_webirc = 1; return MOD_SUCCESS; } -void check_all_users(void); - MOD_LOAD() { - if (cfg.fullstatus_on_load) - check_all_users(); return MOD_SUCCESS; } @@ -592,67 +575,64 @@ int antirandom_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; /* We are only interrested in set::antirandom... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "antirandom")) + if (!ce || !ce->name || strcmp(ce->name, "antirandom")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "except-hosts")) + if (!strcmp(cep->name, "except-hosts")) { } else - if (!strcmp(cep->ce_varname, "except-webirc")) + if (!strcmp(cep->name, "except-webirc")) { /* This should normally be UNDER the generic 'set::antirandom::%s with no value' * stuff but I put it here because people may think it's a hostlist and then * the error can be a tad confusing. -- Syzop */ - if (!cep->ce_vardata) + if (!cep->value) { config_error("%s:%i: set::antirandom::except-webirc should be 'yes' or 'no'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } } else - if (!cep->ce_vardata) + if (!cep->value) { config_error("%s:%i: set::antirandom::%s with no value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } else - if (!strcmp(cep->ce_varname, "threshold")) + if (!strcmp(cep->name, "threshold")) { req.threshold = 1; } else - if (!strcmp(cep->ce_varname, "ban-action")) + if (!strcmp(cep->name, "ban-action")) { - if (!banact_stringtoval(cep->ce_vardata)) + if (!banact_stringtoval(cep->value)) { config_error("%s:%i: set::antirandom::ban-action: unknown action '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } else req.ban_action = 1; } else - if (!strcmp(cep->ce_varname, "ban-reason")) + if (!strcmp(cep->name, "ban-reason")) { req.ban_reason = 1; } else - if (!strcmp(cep->ce_varname, "ban-time")) + if (!strcmp(cep->name, "ban-time")) { req.ban_time = 1; } else - if (!strcmp(cep->ce_varname, "convert-to-lowercase")) + if (!strcmp(cep->name, "convert-to-lowercase")) { } else - if (!strcmp(cep->ce_varname, "fullstatus-on-load")) - { - } else - if (!strcmp(cep->ce_varname, "show-failedconnects")) + if (!strcmp(cep->name, "show-failedconnects")) { } else { config_error("%s:%i: unknown directive set::antirandom::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } } @@ -668,47 +648,43 @@ int antirandom_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::antirandom... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "antirandom")) + if (!ce || !ce->name || strcmp(ce->name, "antirandom")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "except-hosts")) + if (!strcmp(cep->name, "except-hosts")) { - for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) + for (cep2 = cep->items; cep2; cep2 = cep2->next) unreal_add_masks(&cfg.except_hosts, cep2); } else - if (!strcmp(cep->ce_varname, "except-webirc")) + if (!strcmp(cep->name, "except-webirc")) { - cfg.except_webirc = config_checkval(cep->ce_vardata, CFG_YESNO); + cfg.except_webirc = config_checkval(cep->value, CFG_YESNO); } else - if (!strcmp(cep->ce_varname, "threshold")) + if (!strcmp(cep->name, "threshold")) { - cfg.threshold = atoi(cep->ce_vardata); + cfg.threshold = atoi(cep->value); } else - if (!strcmp(cep->ce_varname, "ban-action")) + if (!strcmp(cep->name, "ban-action")) { - cfg.ban_action = banact_stringtoval(cep->ce_vardata); + cfg.ban_action = banact_stringtoval(cep->value); } else - if (!strcmp(cep->ce_varname, "ban-reason")) + if (!strcmp(cep->name, "ban-reason")) { - safe_strdup(cfg.ban_reason, cep->ce_vardata); + safe_strdup(cfg.ban_reason, cep->value); } else - if (!strcmp(cep->ce_varname, "ban-time")) + if (!strcmp(cep->name, "ban-time")) { - cfg.ban_time = config_checkval(cep->ce_vardata, CFG_TIME); + cfg.ban_time = config_checkval(cep->value, CFG_TIME); } else - if (!strcmp(cep->ce_varname, "convert-to-lowercase")) + if (!strcmp(cep->name, "convert-to-lowercase")) { - cfg.convert_to_lowercase = config_checkval(cep->ce_vardata, CFG_YESNO); + cfg.convert_to_lowercase = config_checkval(cep->value, CFG_YESNO); } - if (!strcmp(cep->ce_varname, "show-failedconnects")) + if (!strcmp(cep->name, "show-failedconnects")) { - cfg.show_failedconnects = config_checkval(cep->ce_vardata, CFG_YESNO); - } else - if (!strcmp(cep->ce_varname, "fullstatus-on-load")) - { - cfg.fullstatus_on_load = config_checkval(cep->ce_vardata, CFG_YESNO); + cfg.show_failedconnects = config_checkval(cep->value, CFG_YESNO); } } return 1; @@ -820,24 +796,15 @@ static int internal_getscore(char *str) if (digits >= 5) { - score += 5 + (digits - 5); -#ifdef DEBUGMODE - sendto_ops_and_log("score@'%s': MATCH for digits check", str); -#endif + score += digits; } if (vowels >= 4) { - score += 4 + (vowels - 4); -#ifdef DEBUGMODE - sendto_ops_and_log("score@'%s': MATCH for vowels check", str); -#endif + score += vowels; } if (consonants >= 4) { - score += 4 + (consonants - 4); -#ifdef DEBUGMODE - sendto_ops_and_log("score@'%s': MATCH for consonants check", str); -#endif + score += consonants; } for (t=triples; t; t=t->next) @@ -846,10 +813,6 @@ static int internal_getscore(char *str) if ((t->two[0] == s[0]) && (t->two[1] == s[1]) && s[2] && strchr(t->rest, s[2])) { score++; /* OK */ -#ifdef DEBUGMODE - sendto_ops_and_log("score@'%s': MATCH for '%s[%s]' %c/%c/%c", str, t->two, t->rest, - s[0], s[1], s[2]); -#endif } } @@ -868,11 +831,6 @@ static int get_spam_score(Client *client) char *gecos = client->info; char nbuf[NICKLEN+1], ubuf[USERLEN+1], rbuf[REALLEN+1]; int nscore, uscore, gscore, score; -#ifdef TIMING - struct timeval tv_alpha, tv_beta; - - gettimeofday(&tv_alpha, NULL); -#endif if (cfg.convert_to_lowercase) { @@ -889,46 +847,9 @@ static int get_spam_score(Client *client) gscore = internal_getscore(gecos); score = nscore + uscore + gscore; -#ifdef TIMING - gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "AntiRandom Timing: %ld microseconds", - ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)); -#endif -#ifdef DEBUGMODE - sendto_ops_and_log("got score: %d/%d/%d = %d", - nscore, uscore, gscore, score); -#endif - return score; } -void check_all_users(void) -{ - Client *client; - int matches=0, score; - - list_for_each_entry(client, &lclient_list, lclient_node) - { - if (IsUser(client)) - { - if (is_exempt(client)) - continue; - - score = get_spam_score(client); - if (score > cfg.threshold) - { - if (!matches) - sendto_realops("[antirandom] Full status report follows:"); - sendto_realops("%d points: %s!%s@%s:%s", - score, client->name, client->user->username, client->user->realhost, client->info); - matches++; - } - } - } - if (matches) - sendto_realops("[antirandom] %d match%s", matches, matches == 1 ? "" : "es"); -} - int antirandom_preconnect(Client *client) { int score; @@ -941,13 +862,17 @@ int antirandom_preconnect(Client *client) { if (cfg.ban_action == BAN_ACT_WARN) { - sendto_ops_and_log("[antirandom] would have denied access to user with score %d: %s!%s@%s:%s", - score, client->name, client->user->username, client->user->realhost, client->info); + unreal_log(ULOG_INFO, "antirandom", "ANTIRANDOM_DENIED_USER", client, + "[antirandom] would have denied access to user with score $score: $client:$client.info", + log_data_integer("score", score)); return HOOK_CONTINUE; } if (cfg.show_failedconnects) - sendto_ops_and_log("[antirandom] denied access to user with score %d: %s!%s@%s:%s", - score, client->name, client->user->username, client->user->realhost, client->info); + { + unreal_log(ULOG_INFO, "antirandom", "ANTIRANDOM_DENIED_USER", client, + "[antirandom] denied access to user with score $score: $client:$client.info", + log_data_integer("score", score)); + } place_host_ban(client, cfg.ban_action, cfg.ban_reason, cfg.ban_time); return HOOK_DENY; } @@ -972,7 +897,7 @@ static int is_exempt(Client *client) /* WEBIRC gateway and exempt? */ if (cfg.except_webirc) { - char *val = moddata_client_get(client, "webirc"); + const char *val = moddata_client_get(client, "webirc"); if (val && (atoi(val)>0)) return 1; } diff --git a/src/modules/authprompt.c b/src/modules/authprompt.c index 3a46d96..da0d496 100644 --- a/src/modules/authprompt.c +++ b/src/modules/authprompt.c @@ -25,7 +25,7 @@ ModuleHeader MOD_HEADER "1.0", "SASL authentication for clients that don't support SASL", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /** Configuration settings */ @@ -33,6 +33,7 @@ struct { int enabled; MultiLine *message; MultiLine *fail_message; + MultiLine *unconfirmed_message; } cfg; /** User struct */ @@ -50,10 +51,9 @@ static void init_config(void); static void config_postdefaults(void); int authprompt_config_test(ConfigFile *, ConfigEntry *, int, int *); int authprompt_config_run(ConfigFile *, ConfigEntry *, int); -int authprompt_require_sasl(Client *client, char *reason); -int authprompt_sasl_continuation(Client *client, char *buf); +int authprompt_sasl_continuation(Client *client, const char *buf); int authprompt_sasl_result(Client *client, int success); -int authprompt_place_host_ban(Client *client, int action, char *reason, long duration); +int authprompt_place_host_ban(Client *client, int action, const char *reason, long duration); int authprompt_find_tkline_match(Client *client, TKL *tk); int authprompt_pre_connect(Client *client); CMD_FUNC(cmd_auth); @@ -89,7 +89,6 @@ MOD_INIT() init_config(); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, authprompt_config_run); - HookAdd(modinfo->handle, HOOKTYPE_REQUIRE_SASL, 0, authprompt_require_sasl); HookAdd(modinfo->handle, HOOKTYPE_SASL_CONTINUATION, 0, authprompt_sasl_continuation); HookAdd(modinfo->handle, HOOKTYPE_SASL_RESULT, 0, authprompt_sasl_result); HookAdd(modinfo->handle, HOOKTYPE_PLACE_HOST_BAN, 0, authprompt_place_host_ban); @@ -133,12 +132,18 @@ static void config_postdefaults(void) { addmultiline(&cfg.fail_message, "Authentication failed."); } + if (!cfg.unconfirmed_message) + { + addmultiline(&cfg.unconfirmed_message, "You are trying to use an unconfirmed services account."); + addmultiline(&cfg.unconfirmed_message, "This services account can only be used after it has been activated/confirmed."); + } } static void free_config(void) { freemultiline(cfg.message); freemultiline(cfg.fail_message); + freemultiline(cfg.unconfirmed_message); memset(&cfg, 0, sizeof(cfg)); /* needed! */ } @@ -151,29 +156,32 @@ int authprompt_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; /* We are only interrested in set::authentication-prompt... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "authentication-prompt")) + if (!ce || !ce->name || strcmp(ce->name, "authentication-prompt")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata) + if (!cep->value) { config_error("%s:%i: set::authentication-prompt::%s with no value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } else - if (!strcmp(cep->ce_varname, "enabled")) + if (!strcmp(cep->name, "enabled")) { } else - if (!strcmp(cep->ce_varname, "message")) + if (!strcmp(cep->name, "message")) { } else - if (!strcmp(cep->ce_varname, "fail-message")) + if (!strcmp(cep->name, "fail-message")) + { + } else + if (!strcmp(cep->name, "unconfirmed-message")) { } else { config_error("%s:%i: unknown directive set::authentication-prompt::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } } @@ -189,22 +197,26 @@ int authprompt_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::authentication-prompt... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "authentication-prompt")) + if (!ce || !ce->name || strcmp(ce->name, "authentication-prompt")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "enabled")) + if (!strcmp(cep->name, "enabled")) { - cfg.enabled = config_checkval(cep->ce_vardata, CFG_YESNO); + cfg.enabled = config_checkval(cep->value, CFG_YESNO); } else - if (!strcmp(cep->ce_varname, "message")) + if (!strcmp(cep->name, "message")) { - addmultiline(&cfg.message, cep->ce_vardata); + addmultiline(&cfg.message, cep->value); } else - if (!strcmp(cep->ce_varname, "fail-message")) + if (!strcmp(cep->name, "fail-message")) { - addmultiline(&cfg.fail_message, cep->ce_vardata); + addmultiline(&cfg.fail_message, cep->value); + } else + if (!strcmp(cep->name, "unconfirmed-message")) + { + addmultiline(&cfg.unconfirmed_message, cep->value); } } return 1; @@ -257,7 +269,7 @@ char *make_authbuf(const char *username, const char *password) int size; size = strlen(username) + 1 + strlen(username) + 1 + strlen(password); - if (size >= sizeof(inbuf)) + if (size >= sizeof(inbuf)-1) return NULL; /* too long */ /* Because size limits are already checked above, we can cut some corners here: */ @@ -281,7 +293,7 @@ void send_first_auth(Client *client) { Client *sasl_server; char *addr = BadPtr(client->ip) ? "0" : client->ip; - char *certfp = moddata_client_get(client, "certfp"); + const char *certfp = moddata_client_get(client, "certfp"); sasl_server = find_client(SASL_SERVER, NULL); if (!sasl_server) { @@ -365,25 +377,8 @@ void authprompt_send_auth_required_message(Client *client) sendnotice_multiline(client, cfg.message); } -int authprompt_require_sasl(Client *client, char *reason) -{ - /* If the client did SASL then we (authprompt) will not kick in */ - if (HasCapability(client, "sasl")) - return 0; - - authprompt_tag_as_auth_required(client); - - /* Display the require authentication::reason */ - if (reason && strcmp(reason, "-") && strcmp(reason, "*")) - sendnotice(client, "%s", reason); - - authprompt_send_auth_required_message(client); - - return 1; -} - /* Called upon "place a host ban on this user" (eg: spamfilter, blacklist, ..) */ -int authprompt_place_host_ban(Client *client, int action, char *reason, long duration) +int authprompt_place_host_ban(Client *client, int action, const char *reason, long duration) { /* If it's a soft-xx action and the user is not logged in * and the user is not yet online, then we will handle this user. @@ -437,7 +432,7 @@ int authprompt_pre_connect(Client *client) return HOOK_CONTINUE; /* no action taken, proceed normally */ } -int authprompt_sasl_continuation(Client *client, char *buf) +int authprompt_sasl_continuation(Client *client, const char *buf) { /* If it's not for us (eg: user is doing real SASL) then return 0. */ if (!SEUSER(client) || !SEUSER(client)->authmsg) @@ -468,10 +463,16 @@ int authprompt_sasl_result(Client *client, int success) return 1; } + if (client->user && !IsLoggedIn(client)) + { + sendnotice_multiline(client, cfg.unconfirmed_message); + return 1; + } + /* Authentication was a success */ if (*client->name && client->user && *client->user->username && IsNotSpoof(client)) { - register_user(client, client->name, client->user->username, NULL, NULL, NULL); + register_user(client); /* User MAY be killed now. But since we 'return 1' below, it's safe */ } diff --git a/src/modules/away.c b/src/modules/away.c index a760c83..2d02a48 100644 --- a/src/modules/away.c +++ b/src/modules/away.c @@ -25,6 +25,9 @@ #include "unrealircd.h" CMD_FUNC(cmd_away); +int away_join(Client *client, Channel *channel, MessageTag *mtags); + +long CAP_AWAY_NOTIFY = 0L; #define MSG_AWAY "AWAY" @@ -34,12 +37,19 @@ ModuleHeader MOD_HEADER "5.0", "command /away", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() { + ClientCapabilityInfo c; + memset(&c, 0, sizeof(c)); + c.name = "away-notify"; + ClientCapabilityAdd(modinfo->handle, &c, &CAP_AWAY_NOTIFY); CommandAdd(modinfo->handle, MSG_AWAY, cmd_away, 1, CMD_USER); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, away_join); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_JOIN, 0, away_join); + MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; } @@ -54,17 +64,44 @@ MOD_UNLOAD() return MOD_SUCCESS; } +int away_join(Client *client, Channel *channel, MessageTag *mtags) +{ + Member *lp; + Client *acptr; + int invisible = invisible_user_in_channel(client, channel); + for (lp = channel->members; lp; lp = lp->next) + { + acptr = lp->client; + + if (!MyConnect(acptr)) + continue; /* only locally connected clients */ + + if (invisible && !check_channel_access_member(lp, "hoaq") && (client != acptr)) + continue; /* skip non-ops if requested to (used for mode +D), but always send to 'client' */ + + if (client->user->away && HasCapabilityFast(acptr, CAP_AWAY_NOTIFY)) + { + MessageTag *mtags_away = NULL; + new_message(client, NULL, &mtags_away); + sendto_one(acptr, mtags_away, ":%s!%s@%s AWAY :%s", + client->name, client->user->username, GetHost(client), client->user->away); + free_message_tags(mtags_away); + } + } + return 0; +} + /** Mark client as AWAY or mark them as back (in case of empty reason) */ CMD_FUNC(cmd_away) { - char *new_reason = parv[1]; + char reason[512]; int n, already_as_away = 0; MessageTag *mtags = NULL; if (IsServer(client)) return; - if (parc < 2 || !*new_reason) + if (parc < 2 || BadPtr(parv[1])) { /* Marking as not away */ if (client->user->away) @@ -73,10 +110,9 @@ CMD_FUNC(cmd_away) new_message(client, recv_mtags, &mtags); sendto_server(client, 0, 0, mtags, ":%s AWAY", client->name); - hash_check_watch(client, RPL_NOTAWAY); - sendto_local_common_channels(client, client, ClientCapabilityBit("away-notify"), mtags, + sendto_local_common_channels(client, client, CAP_AWAY_NOTIFY, mtags, ":%s AWAY", client->name); - RunHook3(HOOKTYPE_AWAY, client, mtags, NULL); + RunHook(HOOKTYPE_AWAY, client, mtags, NULL, 0); free_message_tags(mtags); } @@ -85,8 +121,11 @@ CMD_FUNC(cmd_away) return; } + /* Obey set::away-length */ + strlncpy(reason, parv[1], sizeof(reason), iConf.away_length); + /* Check spamfilters */ - if (MyUser(client) && match_spamfilter(client, new_reason, SPAMF_AWAY, "AWAY", NULL, 0, NULL)) + if (MyUser(client) && match_spamfilter(client, reason, SPAMF_AWAY, "AWAY", NULL, 0, NULL)) return; /* Check away-flood */ @@ -98,21 +137,17 @@ CMD_FUNC(cmd_away) return; } - /* Obey set::away-length */ - if (strlen(new_reason) > iConf.away_length) - new_reason[iConf.away_length] = '\0'; - /* Check if the new away reason is the same as the current reason - if so then return (no change) */ - if ((client->user->away) && !strcmp(client->user->away, new_reason)) + if ((client->user->away) && !strcmp(client->user->away, reason)) return; /* All tests passed. Now marking as away (or still away but changing the away reason) */ - client->user->lastaway = TStime(); + client->user->away_since = TStime(); new_message(client, recv_mtags, &mtags); - sendto_server(client, 0, 0, mtags, ":%s AWAY :%s", client->id, new_reason); + sendto_server(client, 0, 0, mtags, ":%s AWAY :%s", client->id, reason); if (client->user->away) { @@ -120,18 +155,16 @@ CMD_FUNC(cmd_away) already_as_away = 1; } - safe_strdup(client->user->away, new_reason); + safe_strdup(client->user->away, reason); if (MyConnect(client)) sendnumeric(client, RPL_NOWAWAY); - hash_check_watch(client, already_as_away ? RPL_REAWAY : RPL_GONEAWAY); - sendto_local_common_channels(client, client, - ClientCapabilityBit("away-notify"), mtags, + CAP_AWAY_NOTIFY, mtags, ":%s AWAY :%s", client->name, client->user->away); - RunHook3(HOOKTYPE_AWAY, client, mtags, client->user->away); + RunHook(HOOKTYPE_AWAY, client, mtags, client->user->away, already_as_away); free_message_tags(mtags); diff --git a/src/modules/batch.c b/src/modules/batch.c index e1ca1a8..692680c 100644 --- a/src/modules/batch.c +++ b/src/modules/batch.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "5.0", "Batch CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ @@ -37,7 +37,7 @@ CMD_FUNC(cmd_batch); /* Variables */ long CAP_BATCH = 0L; -int batch_mtag_is_ok(Client *client, char *name, char *value); +int batch_mtag_is_ok(Client *client, const char *name, const char *value); MOD_INIT() { @@ -111,7 +111,7 @@ CMD_FUNC(cmd_batch) * syntax. * We simply allow batch ONLY from servers and with any syntax. */ -int batch_mtag_is_ok(Client *client, char *name, char *value) +int batch_mtag_is_ok(Client *client, const char *name, const char *value) { if (IsServer(client)) return 1; diff --git a/src/modules/blacklist.c b/src/modules/blacklist.c index 7cc99cd..696b8fa 100644 --- a/src/modules/blacklist.c +++ b/src/modules/blacklist.c @@ -26,7 +26,7 @@ ModuleHeader MOD_HEADER "5.0", "Check connecting users against DNS Blacklists", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* In this module and the config syntax I tried to 'abstract' things @@ -83,6 +83,9 @@ struct BLUser { long save_tkltime; char *save_opernotice; char *save_reason; + char *save_blacklist; + char *save_blacklist_dns_name; + int save_blacklist_dns_reply; }; /* Global variables */ @@ -96,7 +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_quit(Client *client, MessageTag *mtags, char *comment); +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); int blacklist_start_check(Client *client); @@ -109,13 +112,11 @@ void blacklist_free_bluser_if_able(BLUser *bl); #define SetBLUser(x, y) do { moddata_client(x, blacklist_md).ptr = y; } while(0) #define BLUSER(x) ((BLUser *)moddata_client(x, blacklist_md).ptr) -long SNO_BLACKLIST = 0L; - MOD_TEST() { HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, blacklist_config_test); - CallbackAddEx(modinfo->handle, CALLBACKTYPE_BLACKLIST_CHECK, blacklist_start_check); + CallbackAdd(modinfo->handle, CALLBACKTYPE_BLACKLIST_CHECK, blacklist_start_check); return MOD_SUCCESS; } @@ -150,8 +151,6 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, 0, blacklist_rehash_complete); HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, blacklist_quit); - SnomaskAdd(modinfo->handle, 'b', umode_allow_opers, &SNO_BLACKLIST); - return MOD_SUCCESS; } @@ -251,192 +250,192 @@ int blacklist_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (!ce) return 0; - if (strcmp(ce->ce_varname, "blacklist")) + if (strcmp(ce->name, "blacklist")) return 0; /* not interested in non-blacklist stuff.. */ - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: blacklist block without name (use: blacklist somename { })", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); *errs = 1; return -1; } /* Now actually go parse the blacklist { } block */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "dns")) + if (!strcmp(cep->name, "dns")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "reply")) + if (!strcmp(cepp->name, "reply")) { if (has_dns_reply) { /* this is an error (not a warning) */ config_error("%s:%i: blacklist block may contain only one blacklist::dns::reply item. " "You can specify multiple replies by using: reply { 1; 2; 4; };", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; continue; } - if (!cepp->ce_vardata && !cepp->ce_entries) + if (!cepp->value && !cepp->items) { - config_error_blank(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, "blacklist::dns::reply"); + config_error_blank(cepp->file->filename, cepp->line_number, "blacklist::dns::reply"); errors++; continue; } has_dns_reply = 1; /* we have a reply. now whether it's actually valid is another story.. */ - if (cepp->ce_vardata && cepp->ce_entries) + if (cepp->value && cepp->items) { config_error("%s:%i: blacklist::dns::reply must be either using format 'reply 1;' or " "'reply { 1; 2; 4; }; but not both formats at the same time.", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; continue; } - if (cepp->ce_vardata) + if (cepp->value) { - if (atoi(cepp->ce_vardata) <= 0) + if (atoi(cepp->value) <= 0) { config_error("%s:%i: blacklist::dns::reply must be >0", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; continue; } } - if (cepp->ce_entries) + if (cepp->items) { - for (ceppp = cepp->ce_entries; ceppp; ceppp=ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp=ceppp->next) { - if (atoi(ceppp->ce_varname) <= 0) + if (atoi(ceppp->name) <= 0) { config_error("%s:%i: all items in blacklist::dns::reply must be >0", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; } } } } else - if (!cepp->ce_vardata) + if (!cepp->value) { - config_error_empty(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, - "blacklist::dns", cepp->ce_varname); + config_error_empty(cepp->file->filename, cepp->line_number, + "blacklist::dns", cepp->name); errors++; continue; } else - if (!strcmp(cepp->ce_varname, "name")) + if (!strcmp(cepp->name, "name")) { if (has_dns_name) { - config_warn_duplicate(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "blacklist::dns::name"); + config_warn_duplicate(cepp->file->filename, + cepp->line_number, "blacklist::dns::name"); } has_dns_name = 1; } else - if (!strcmp(cepp->ce_varname, "type")) + if (!strcmp(cepp->name, "type")) { if (has_dns_type) { - config_warn_duplicate(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "blacklist::dns::type"); + config_warn_duplicate(cepp->file->filename, + cepp->line_number, "blacklist::dns::type"); } has_dns_type = 1; - if (!strcmp(cepp->ce_vardata, "record")) + if (!strcmp(cepp->value, "record")) ; - else if (!strcmp(cepp->ce_vardata, "bitmask")) + else if (!strcmp(cepp->value, "bitmask")) ; else { config_error("%s:%i: unknown blacklist::dns::type '%s', must be either 'record' or 'bitmask'", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_vardata); + cepp->file->filename, cepp->line_number, cepp->value); errors++; } } } } else - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "blacklist", cep->ce_varname); + config_error_empty(cep->file->filename, cep->line_number, + "blacklist", cep->name); errors++; continue; } - else if (!strcmp(cep->ce_varname, "action")) + else if (!strcmp(cep->name, "action")) { if (has_action) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "blacklist::action"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "blacklist::action"); continue; } has_action = 1; - if (!banact_stringtoval(cep->ce_vardata)) + if (!banact_stringtoval(cep->value)) { config_error("%s:%i: blacklist::action has unknown action type '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } } - else if (!strcmp(cep->ce_varname, "ban-time")) + else if (!strcmp(cep->name, "ban-time")) { if (has_ban_time) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "blacklist::ban-time"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "blacklist::ban-time"); continue; } has_ban_time = 1; } else - if (!strcmp(cep->ce_varname, "reason")) + if (!strcmp(cep->name, "reason")) { if (has_reason) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "blacklist::reason"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "blacklist::reason"); continue; } has_reason = 1; } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "blacklist", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "blacklist", cep->name); errors++; } } if (!has_action) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "blacklist::action"); errors++; } if (!has_reason) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "blacklist::reason"); errors++; } if (!has_dns_name) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "blacklist::dns::name"); errors++; } if (!has_dns_type) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "blacklist::dns::type"); errors++; } if (!has_dns_reply) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "blacklist::dns::reply"); errors++; } @@ -453,12 +452,12 @@ int blacklist_config_run(ConfigFile *cf, ConfigEntry *ce, int type) if (type != CONFIG_MAIN) return 0; - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "blacklist")) + if (!ce || !ce->name || strcmp(ce->name, "blacklist")) return 0; /* not interested */ d = safe_alloc(sizeof(Blacklist)); - safe_strdup(d->name, ce->ce_vardata); - /* set some defaults. TODO: use set::blacklist or something ? */ + safe_strdup(d->name, ce->value); + /* set some defaults */ d->action = BAN_ACT_KILL; safe_strdup(d->reason, "Your IP is on a DNS Blacklist"); d->ban_time = 3600; @@ -468,28 +467,28 @@ int blacklist_config_run(ConfigFile *cf, ConfigEntry *ce, int type) d->backend = safe_alloc(sizeof(BlacklistBackend)); d->backend->dns = safe_alloc(sizeof(DNSBL)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "dns")) + if (!strcmp(cep->name, "dns")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "reply")) + if (!strcmp(cepp->name, "reply")) { - if (cepp->ce_vardata) + if (cepp->value) { /* single reply */ d->backend->dns->reply = safe_alloc(sizeof(int)*2); - d->backend->dns->reply[0] = atoi(cepp->ce_vardata); + d->backend->dns->reply[0] = atoi(cepp->value); d->backend->dns->reply[1] = 0; } else - if (cepp->ce_entries) + if (cepp->items) { /* (potentially) multiple reply values */ int cnt = 0; - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - if (ceppp->ce_varname) + if (ceppp->name) cnt++; } @@ -499,37 +498,37 @@ int blacklist_config_run(ConfigFile *cf, ConfigEntry *ce, int type) d->backend->dns->reply = safe_alloc(sizeof(int)*(cnt+1)); cnt = 0; - for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) + for (ceppp = cepp->items; ceppp; ceppp = ceppp->next) { - d->backend->dns->reply[cnt++] = atoi(ceppp->ce_varname); + d->backend->dns->reply[cnt++] = atoi(ceppp->name); } d->backend->dns->reply[cnt] = 0; } } else - if (!strcmp(cepp->ce_varname, "type")) + if (!strcmp(cepp->name, "type")) { - if (!strcmp(cepp->ce_vardata, "record")) + if (!strcmp(cepp->value, "record")) d->backend->dns->type = DNSBL_RECORD; - else if (!strcmp(cepp->ce_vardata, "bitmask")) + else if (!strcmp(cepp->value, "bitmask")) d->backend->dns->type = DNSBL_BITMASK; } else - if (!strcmp(cepp->ce_varname, "name")) + if (!strcmp(cepp->name, "name")) { - safe_strdup(d->backend->dns->name, cepp->ce_vardata); + safe_strdup(d->backend->dns->name, cepp->value); } } } - else if (!strcmp(cep->ce_varname, "action")) + else if (!strcmp(cep->name, "action")) { - d->action = banact_stringtoval(cep->ce_vardata); + d->action = banact_stringtoval(cep->value); } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { - safe_strdup(d->reason, cep->ce_vardata); + safe_strdup(d->reason, cep->value); } - else if (!strcmp(cep->ce_varname, "ban-time")) + else if (!strcmp(cep->name, "ban-time")) { - d->ban_time = config_checkval(cep->ce_vardata, CFG_TIME); + d->ban_time = config_checkval(cep->value, CFG_TIME); } } @@ -644,7 +643,7 @@ void blacklist_cancel(BLUser *bl) bl->client = NULL; } -int blacklist_quit(Client *client, MessageTag *mtags, char *comment) +int blacklist_quit(Client *client, MessageTag *mtags, const char *comment) { if (BLUSER(client)) blacklist_cancel(BLUSER(client)); @@ -725,10 +724,17 @@ int blacklist_parse_reply(struct hostent *he, int entry) * from blacklist_preconnect() for softbans that need to be delayed * as to give the user the opportunity to do SASL Authentication. */ -int blacklist_action(Client *client, char *opernotice, BanAction ban_action, char *ban_reason, long ban_time) +int blacklist_action(Client *client, char *opernotice, BanAction ban_action, char *ban_reason, long ban_time, + char *blacklist, char *blacklist_dns_name, int blacklist_dns_reply) { - sendto_snomask(SNO_BLACKLIST, "%s", opernotice); - ircd_log(LOG_KILL, "%s", opernotice); + unreal_log_raw(ULOG_INFO, "blacklist", "BLACKLIST_HIT", client, + opernotice, + log_data_string("blacklist_name", blacklist), + log_data_string("blacklist_dns_name", blacklist_dns_name), + log_data_integer("blacklist_dns_reply", blacklist_dns_reply), + log_data_string("ban_action", banact_valtostring(ban_action)), + log_data_string("ban_reason", ban_reason), + log_data_integer("ban_time", ban_time)); if (ban_action == BAN_ACT_WARN) return 0; return place_host_ban(client, ban_action, ban_reason, ban_time); @@ -766,9 +772,12 @@ void blacklist_hit(Client *client, Blacklist *bl, int reply) blu->save_tkltime = bl->ban_time; safe_strdup(blu->save_opernotice, opernotice); safe_strdup(blu->save_reason, banbuf); + safe_strdup(blu->save_blacklist, bl->name); + safe_strdup(blu->save_blacklist_dns_name, bl->backend->dns->name); + blu->save_blacklist_dns_reply = reply; } else { /* Otherwise, execute the action immediately */ - blacklist_action(client, opernotice, bl->action, banbuf, bl->ban_time); + blacklist_action(client, opernotice, bl->action, banbuf, bl->ban_time, bl->name, bl->backend->dns->name, reply); } } @@ -841,7 +850,10 @@ int blacklist_preconnect(Client *client) if (IsLoggedIn(client)) return HOOK_CONTINUE; /* yup, so the softban does not apply. */ - if (blacklist_action(client, blu->save_opernotice, blu->save_action, blu->save_reason, blu->save_tkltime)) + if (blacklist_action(client, blu->save_opernotice, blu->save_action, blu->save_reason, blu->save_tkltime, + blu->save_blacklist, blu->save_blacklist_dns_name, blu->save_blacklist_dns_reply)) + { return HOOK_DENY; + } return HOOK_CONTINUE; /* exempt */ } diff --git a/src/modules/bot-tag.c b/src/modules/bot-tag.c index ed566ef..2a300c3 100644 --- a/src/modules/bot-tag.c +++ b/src/modules/bot-tag.c @@ -33,11 +33,11 @@ ModuleHeader MOD_HEADER "5.0", "bot message tag", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -int bottag_mtag_is_ok(Client *client, char *name, char *value); -void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int bottag_mtag_is_ok(Client *client, const char *name, const char *value); +void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -68,7 +68,7 @@ MOD_UNLOAD() /** This function verifies if the client sending the mtag is permitted to do so. */ -int bottag_mtag_is_ok(Client *client, char *name, char *value) +int bottag_mtag_is_ok(Client *client, const char *name, const char *value) { if (IsServer(client) && (value == NULL)) return 1; /* OK */ @@ -76,7 +76,7 @@ int bottag_mtag_is_ok(Client *client, char *name, char *value) return 0; } -void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_bottag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m; diff --git a/src/modules/botmotd.c b/src/modules/botmotd.c index a36248b..b5dcde2 100644 --- a/src/modules/botmotd.c +++ b/src/modules/botmotd.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /botmotd", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -60,7 +60,7 @@ CMD_FUNC(cmd_botmotd) MOTDLine *motdline; ConfigItem_tld *tld; - if (hunt_server(client, recv_mtags, ":%s BOTMOTD :%s", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "BOTMOTD", 1, parc, parv) != HUNTED_ISME) return; if (!IsUser(client)) diff --git a/src/modules/cap.c b/src/modules/cap.c index 7b886a3..08ffe5e 100644 --- a/src/modules/cap.c +++ b/src/modules/cap.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /cap", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ @@ -43,13 +43,7 @@ int cap_never_visible(Client *client); /* Variables */ long CAP_IN_PROGRESS = 0L; -long CAP_ACCOUNT_NOTIFY = 0L; -long CAP_AWAY_NOTIFY = 0L; -long CAP_MULTI_PREFIX = 0L; -long CAP_USERHOST_IN_NAMES = 0L; long CAP_NOTIFY = 0L; -long CAP_CHGHOST = 0L; -long CAP_EXTENDED_JOIN = 0L; MOD_INIT() { @@ -66,34 +60,10 @@ MOD_INIT() c.visible = cap_never_visible; ClientCapabilityAdd(modinfo->handle, &c, &CAP_IN_PROGRESS); - memset(&c, 0, sizeof(c)); - c.name = "account-notify"; - ClientCapabilityAdd(modinfo->handle, &c, &CAP_ACCOUNT_NOTIFY); - - memset(&c, 0, sizeof(c)); - c.name = "away-notify"; - ClientCapabilityAdd(modinfo->handle, &c, &CAP_AWAY_NOTIFY); - - memset(&c, 0, sizeof(c)); - c.name = "multi-prefix"; - ClientCapabilityAdd(modinfo->handle, &c, &CAP_MULTI_PREFIX); - - memset(&c, 0, sizeof(c)); - c.name = "userhost-in-names"; - ClientCapabilityAdd(modinfo->handle, &c, &CAP_USERHOST_IN_NAMES); - memset(&c, 0, sizeof(c)); c.name = "cap-notify"; ClientCapabilityAdd(modinfo->handle, &c, &CAP_NOTIFY); - memset(&c, 0, sizeof(c)); - c.name = "chghost"; - ClientCapabilityAdd(modinfo->handle, &c, &CAP_CHGHOST); - - memset(&c, 0, sizeof(c)); - c.name = "extended-join"; - ClientCapabilityAdd(modinfo->handle, &c, &CAP_EXTENDED_JOIN); - HookAdd(modinfo->handle, HOOKTYPE_IS_HANDSHAKE_FINISHED, 0, cap_is_handshake_finished); return MOD_SUCCESS; @@ -139,17 +109,17 @@ static ClientCapability *clicap_find(Client *client, const char *data, int *nega return NULL; } - if(*p == '-') + if (*p == '-') { *negate = 1; p++; /* someone sent a '-' without a parameter.. */ - if(*p == '\0') + if (*p == '\0') return NULL; } - if((s = strchr(p, ' '))) + if ((s = strchr(p, ' '))) *s++ = '\0'; cap = ClientCapabilityFind(p, client); @@ -190,7 +160,7 @@ static void clicap_generate(Client *client, const char *subcmd, int flags) for (cap = clicaps; cap; cap = cap->next) { char name[256]; - char *param; + const char *param; if (cap->visible && !cap->visible(client)) continue; /* hidden */ @@ -240,7 +210,7 @@ static void cap_end(Client *client, const char *arg) ClearCapabilityFast(client, CAP_IN_PROGRESS); if (*client->name && client->user && *client->user->username && IsNotSpoof(client)) - register_user(client, client->name, client->user->username, NULL, NULL, NULL); + register_user(client); } static void cap_list(Client *client, const char *arg) @@ -386,8 +356,8 @@ CMD_FUNC(cmd_cap) * Only add a 1 second fake lag penalty if this is the XXth command. * This will speed up connections considerably. */ - if (client->local->receiveM > 15) - client->local->since++; + if (client->local->traffic.messages_received > 15) + add_fake_lag(client, 1000); if (DISABLE_CAP) { @@ -406,7 +376,7 @@ CMD_FUNC(cmd_cap) return; } - if(!(cmd = bsearch(parv[1], clicap_cmdtable, + if (!(cmd = bsearch(parv[1], clicap_cmdtable, sizeof(clicap_cmdtable) / sizeof(struct clicap_cmd), sizeof(struct clicap_cmd), (bqcmp) clicap_cmd_search))) { diff --git a/src/modules/certfp.c b/src/modules/certfp.c index 1ec8b9a..c389b9e 100644 --- a/src/modules/certfp.c +++ b/src/modules/certfp.c @@ -1,6 +1,6 @@ /* * Certificate Fingerprint Module - * This grabs the SHA256 fingerprint of the SSL/TLS client certificate + * This grabs the SHA256 fingerprint of the TLS client certificate * the user is using, shares it with the other servers (and rest of * UnrealIRCd) and shows it in /WHOIS etc. * @@ -17,24 +17,22 @@ ModuleHeader MOD_HEADER "5.0", "Certificate fingerprint", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ void certfp_free(ModData *m); -char *certfp_serialize(ModData *m); -void certfp_unserialize(char *str, ModData *m); +const char *certfp_serialize(ModData *m); +void certfp_unserialize(const char *str, ModData *m); int certfp_handshake(Client *client); int certfp_connect(Client *client); -int certfp_whois(Client *client, Client *target); +int certfp_whois(Client *client, Client *target, NameValuePrioList **list); ModDataInfo *certfp_md; /* Module Data structure which we acquire */ -#define WHOISCERTFP_STRING ":%s 276 %s %s :has client certificate fingerprint %s" - MOD_INIT() { -ModDataInfo mreq; + ModDataInfo mreq; MARK_AS_OFFICIAL_MODULE(modinfo); @@ -43,7 +41,7 @@ ModDataInfo mreq; mreq.free = certfp_free; mreq.serialize = certfp_serialize; mreq.unserialize = certfp_unserialize; - mreq.sync = 1; + mreq.sync = MODDATA_SYNC_EARLY; mreq.type = MODDATATYPE_CLIENT; certfp_md = ModDataAdd(modinfo->handle, mreq); if (!certfp_md) @@ -121,7 +119,7 @@ int certfp_connect(Client *client) { if (IsSecure(client)) { - char *fp = moddata_client_get(client, "certfp"); + const char *fp = moddata_client_get(client, "certfp"); if (fp && !iConf.no_connect_tls_info) sendnotice(client, "*** Your TLS certificate fingerprint is %s", fp); @@ -130,12 +128,17 @@ int certfp_connect(Client *client) return 0; } -int certfp_whois(Client *client, Client *target) +int certfp_whois(Client *client, Client *target, NameValuePrioList **list) { - char *fp = moddata_client_get(target, "certfp"); - - if (fp) - sendnumeric(client, RPL_WHOISCERTFP, target->name, fp); + const char *fp = moddata_client_get(target, "certfp"); + char buf[512]; + + if (!fp) + return 0; + + if (whois_get_policy(client, target, "certfp") == WHOIS_CONFIG_DETAILS_FULL) + add_nvplist_numeric(list, 0, "certfp", client, RPL_WHOISCERTFP, target->name, fp); + return 0; } @@ -144,14 +147,14 @@ void certfp_free(ModData *m) safe_free(m->str); } -char *certfp_serialize(ModData *m) +const char *certfp_serialize(ModData *m) { if (!m->str) return NULL; return m->str; } -void certfp_unserialize(char *str, ModData *m) +void certfp_unserialize(const char *str, ModData *m) { safe_strdup(m->str, str); } diff --git a/src/modules/chanmodes/Makefile.in b/src/modules/chanmodes/Makefile.in index a493260..0544b9c 100644 --- a/src/modules/chanmodes/Makefile.in +++ b/src/modules/chanmodes/Makefile.in @@ -25,14 +25,17 @@ INCLUDES = ../../include/channel.h \ ../../include/ircsprintf.h \ ../../include/license.h \ ../../include/modules.h ../../include/modversion.h ../../include/msg.h \ - ../../include/numeric.h ../../include/proto.h ../../include/dns.h \ + ../../include/numeric.h ../../include/dns.h \ ../../include/resource.h ../../include/setup.h \ ../../include/struct.h ../../include/sys.h \ - ../../include/types.h ../../include/url.h \ + ../../include/types.h \ ../../include/version.h ../../include/whowas.h R_MODULES= \ - nocolor.so stripcolor.so issecure.so permanent.so floodprot.so \ + chanowner.so chanadmin.so chanop.so halfop.so voice.so \ + key.so limit.so inviteonly.so secret.so private.so \ + moderated.so noexternalmsgs.so topiclimit.so \ + nocolor.so stripcolor.so isregistered.so issecure.so permanent.so floodprot.so \ noctcp.so link.so censor.so delayjoin.so noknock.so noinvite.so operonly.so \ nonotice.so regonly.so nonickchange.so nokick.so regonlyspeak.so \ secureonly.so history.so @@ -41,6 +44,9 @@ MODULES=$(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ RM=@RM@ +.SUFFIXES: +.SUFFIXES: .c .h .so + all: build build: $(MODULES) @@ -48,82 +54,6 @@ build: $(MODULES) clean: $(RM) -f *.o *.so *~ core -############################################################################# -# .so's section -############################################################################# - -issecure.so: issecure.c $(INCLUDES) +%.so: %.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o issecure.so issecure.c - -nocolor.so: nocolor.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nocolor.so nocolor.c - -stripcolor.so: stripcolor.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o stripcolor.so stripcolor.c - -permanent.so: permanent.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o permanent.so permanent.c - -floodprot.so: floodprot.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o floodprot.so floodprot.c - -noctcp.so: noctcp.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o noctcp.so noctcp.c - -link.so: link.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o link.so link.c - -censor.so: censor.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o censor.so censor.c - -delayjoin.so: delayjoin.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o delayjoin.so delayjoin.c - -noknock.so: noknock.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o noknock.so noknock.c - -noinvite.so: noinvite.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o noinvite.so noinvite.c - -operonly.so: operonly.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o operonly.so operonly.c - -nonotice.so: nonotice.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nonotice.so nonotice.c - -regonly.so: regonly.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o regonly.so regonly.c - -nonickchange.so: nonickchange.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nonickchange.so nonickchange.c - -nokick.so: nokick.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nokick.so nokick.c - -regonlyspeak.so: regonlyspeak.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o regonlyspeak.so regonlyspeak.c - -secureonly.so: secureonly.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o secureonly.so secureonly.c - -history.so: history.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o history.so history.c + -o $@ $< diff --git a/src/modules/chanmodes/censor.c b/src/modules/chanmodes/censor.c index 767d21f..5ae21a8 100644 --- a/src/modules/chanmodes/censor.c +++ b/src/modules/chanmodes/censor.c @@ -12,18 +12,18 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +G", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTMODE_CENSOR = 0L; -#define IsCensored(x) ((x)->mode.extmode & EXTMODE_CENSOR) +#define IsCensored(x) ((x)->mode.mode & EXTMODE_CENSOR) -int censor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -char *censor_pre_local_part(Client *client, Channel *channel, char *text); -char *censor_pre_local_quit(Client *client, char *text); -int censor_stats_badwords_channel(Client *client, char *para); +int censor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +const char *censor_pre_local_part(Client *client, Channel *channel, const char *text); +const char *censor_pre_local_quit(Client *client, const char *text); +int censor_stats_badwords_channel(Client *client, const char *para); int censor_config_test(ConfigFile *, ConfigEntry *, int, int *); int censor_config_run(ConfigFile *, ConfigEntry *, int); @@ -49,12 +49,12 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 0; req.is_ok = extcmode_default_requirechop; - req.flag = 'G'; + req.letter = 'G'; CmodeAdd(modinfo->handle, req, &EXTMODE_CENSOR); HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, censor_can_send_to_channel); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, censor_pre_local_part); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, censor_pre_local_quit); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, censor_pre_local_part); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, censor_pre_local_quit); HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, censor_stats_badwords_channel); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, censor_config_run); return MOD_SUCCESS; @@ -88,99 +88,99 @@ int censor_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type != CONFIG_MAIN) return 0; - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "badword")) + if (!ce || !ce->name || strcmp(ce->name, "badword")) return 0; /* not interested */ - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: badword without type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - else if (strcmp(ce->ce_vardata, "channel") && - strcmp(ce->ce_vardata, "quit") && strcmp(ce->ce_vardata, "all")) { + else if (strcmp(ce->value, "channel") && + strcmp(ce->value, "quit") && strcmp(ce->value, "all")) { /* config_error("%s:%i: badword with unknown type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); -- can't do that.. */ + ce->file->filename, ce->line_number); -- can't do that.. */ return 0; /* unhandled */ } - if (!strcmp(ce->ce_vardata, "quit")) + if (!strcmp(ce->value, "quit")) { config_error("%s:%i: badword quit has been removed. We just use the bad words from " "badword channel { } instead.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 0; /* pretend unhandled.. ok not just pretend.. ;) */ } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "badword")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "word")) + if (!strcmp(cep->name, "word")) { - char *errbuf; + const char *errbuf; if (has_word) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "badword::word"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "badword::word"); continue; } has_word = 1; - if ((errbuf = badword_config_check_regex(cep->ce_vardata,1,1))) + if ((errbuf = badword_config_check_regex(cep->value,1,1))) { config_error("%s:%i: badword::%s contains an invalid regex: %s", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - cep->ce_varname, errbuf); + cep->file->filename, + cep->line_number, + cep->name, errbuf); errors++; } } - else if (!strcmp(cep->ce_varname, "replace")) + else if (!strcmp(cep->name, "replace")) { if (has_replace) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "badword::replace"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "badword::replace"); continue; } has_replace = 1; } - else if (!strcmp(cep->ce_varname, "action")) + else if (!strcmp(cep->name, "action")) { if (has_action) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "badword::action"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "badword::action"); continue; } has_action = 1; - if (!strcmp(cep->ce_vardata, "replace")) + if (!strcmp(cep->value, "replace")) action = 'r'; - else if (!strcmp(cep->ce_vardata, "block")) + else if (!strcmp(cep->value, "block")) action = 'b'; else { config_error("%s:%d: Unknown badword::action '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata); + cep->file->filename, cep->line_number, + cep->value); errors++; } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "badword", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "badword", cep->name); errors++; } } if (!has_word) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "badword::word"); errors++; } @@ -189,7 +189,7 @@ int censor_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (has_replace && action == 'b') { config_error("%s:%i: badword::action is block but badword::replace exists", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } } @@ -207,39 +207,39 @@ int censor_config_run(ConfigFile *cf, ConfigEntry *ce, int type) if (type != CONFIG_MAIN) return 0; - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "badword")) + if (!ce || !ce->name || strcmp(ce->name, "badword")) return 0; /* not interested */ - if (strcmp(ce->ce_vardata, "channel") && strcmp(ce->ce_vardata, "all")) + if (strcmp(ce->value, "channel") && strcmp(ce->value, "all")) return 0; /* not for us */ ca = safe_alloc(sizeof(ConfigItem_badword)); ca->action = BADWORD_REPLACE; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "action")) + if (!strcmp(cep->name, "action")) { - if (!strcmp(cep->ce_vardata, "block")) + if (!strcmp(cep->value, "block")) { ca->action = BADWORD_BLOCK; } } - else if (!strcmp(cep->ce_varname, "replace")) + else if (!strcmp(cep->name, "replace")) { - safe_strdup(ca->replace, cep->ce_vardata); + safe_strdup(ca->replace, cep->value); } else - if (!strcmp(cep->ce_varname, "word")) + if (!strcmp(cep->name, "word")) { word = cep; } } - badword_config_process(ca, word->ce_vardata); + badword_config_process(ca, word->value); - if (!strcmp(ce->ce_vardata, "channel")) + if (!strcmp(ce->value, "channel")) AddListItem(ca, conf_badword_channel); - else if (!strcmp(ce->ce_vardata, "all")) + else if (!strcmp(ce->value, "all")) { AddListItem(ca, conf_badword_channel); return 0; /* pretend we didn't see it, so other modules can handle 'all' as well */ @@ -248,12 +248,12 @@ int censor_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 1; } -char *stripbadwords_channel(char *str, int *blocked) +const char *stripbadwords_channel(const char *str, int *blocked) { return stripbadwords(str, conf_badword_channel, blocked); } -int censor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int censor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { int blocked; Hook *h; @@ -281,7 +281,7 @@ int censor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, return HOOK_CONTINUE; } -char *censor_pre_local_part(Client *client, Channel *channel, char *text) +const char *censor_pre_local_part(Client *client, Channel *channel, const char *text) { int blocked; @@ -306,7 +306,7 @@ static int IsAnyChannelCensored(Client *client) return 0; } -char *censor_pre_local_quit(Client *client, char *text) +const char *censor_pre_local_quit(Client *client, const char *text) { int blocked = 0; @@ -319,7 +319,7 @@ char *censor_pre_local_quit(Client *client, char *text) return blocked ? NULL : text; } -int censor_stats_badwords_channel(Client *client, char *para) +int censor_stats_badwords_channel(Client *client, const char *para) { ConfigItem_badword *words; diff --git a/src/modules/chanmodes/chanadmin.c b/src/modules/chanmodes/chanadmin.c new file mode 100644 index 0000000..358557d --- /dev/null +++ b/src/modules/chanmodes/chanadmin.c @@ -0,0 +1,88 @@ +/* + * Channel Mode +a + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "chanmodes/chanadmin", + "6.0", + "Channel Mode +a", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +int cmode_chanadmin_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); + +MOD_INIT() +{ + CmodeInfo creq; + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&creq, 0, sizeof(creq)); + creq.paracount = 1; + creq.is_ok = cmode_chanadmin_is_ok; + creq.letter = 'a'; + creq.prefix = '&'; + creq.sjoin_prefix = '~'; /* yeah i know, totally not confusing! */ + creq.rank = RANK_CHANADMIN; + creq.unset_with_param = 1; + creq.type = CMODE_MEMBER; + CmodeAdd(modinfo->handle, creq, NULL); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int cmode_chanadmin_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) + { + Client *target = find_user(param, NULL); + + if ((what == MODE_DEL) && (client == target)) + { + /* User may always remove their own modes */ + return EX_ALLOW; + } + if (check_channel_access(client, channel, "q")) + { + /* only +q people may do +a/-a */ + return EX_ALLOW; + } + if (type == EXCHK_ACCESS_ERR) + sendnumeric(client, ERR_CHANOWNPRIVNEEDED, channel->name); + return EX_DENY; + } + + /* fallthrough -- should not be used */ + return EX_DENY; +} diff --git a/src/modules/chanmodes/chanop.c b/src/modules/chanmodes/chanop.c new file mode 100644 index 0000000..3a08f32 --- /dev/null +++ b/src/modules/chanmodes/chanop.c @@ -0,0 +1,78 @@ +/* + * Channel Mode +o + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "chanmodes/chanop", + "6.0", + "Channel Mode +o", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +int cmode_chanop_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); + +MOD_INIT() +{ + CmodeInfo creq; + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&creq, 0, sizeof(creq)); + creq.paracount = 1; + creq.is_ok = cmode_chanop_is_ok; + creq.letter = 'o'; + creq.prefix = '@'; + creq.sjoin_prefix = '@'; + creq.rank = RANK_CHANOP; + creq.unset_with_param = 1; + creq.type = CMODE_MEMBER; + CmodeAdd(modinfo->handle, creq, NULL); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int cmode_chanop_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) + { + if (check_channel_access(client, channel, "oaq")) + return EX_ALLOW; + if (type == EXCHK_ACCESS_ERR) + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); + return EX_DENY; + } + + /* fallthrough -- should not be used */ + return EX_DENY; +} diff --git a/src/modules/chanmodes/chanowner.c b/src/modules/chanmodes/chanowner.c new file mode 100644 index 0000000..f449d99 --- /dev/null +++ b/src/modules/chanmodes/chanowner.c @@ -0,0 +1,88 @@ +/* + * Channel Mode +q + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "chanmodes/chanowner", + "6.0", + "Channel Mode +q", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +int cmode_chanowner_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); + +MOD_INIT() +{ + CmodeInfo creq; + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&creq, 0, sizeof(creq)); + creq.paracount = 1; + creq.is_ok = cmode_chanowner_is_ok; + creq.letter = 'q'; + creq.prefix = '~'; + creq.sjoin_prefix = '*'; + creq.rank = RANK_CHANOWNER; + creq.unset_with_param = 1; + creq.type = CMODE_MEMBER; + CmodeAdd(modinfo->handle, creq, NULL); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int cmode_chanowner_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) + { + Client *target = find_user(param, NULL); + + if ((what == MODE_DEL) && (client == target)) + { + /* User may always remove their own modes */ + return EX_ALLOW; + } + if (check_channel_access(client, channel, "q")) + { + /* only +q people may do +q/-q */ + return EX_ALLOW; + } + if (type == EXCHK_ACCESS_ERR) + sendnumeric(client, ERR_CHANOWNPRIVNEEDED, channel->name); + return EX_DENY; + } + + /* fallthrough -- should not be used */ + return EX_DENY; +} diff --git a/src/modules/chanmodes/delayjoin.c b/src/modules/chanmodes/delayjoin.c index 0eedb31..95fc07b 100644 --- a/src/modules/chanmodes/delayjoin.c +++ b/src/modules/chanmodes/delayjoin.c @@ -11,7 +11,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "delayed join (+D,+d)", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #define MOD_DATA_STR "delayjoin" @@ -26,13 +26,14 @@ static Cmode_t EXTMODE_POST_DELAYED; int visible_in_channel(Client *client, Channel *channel); int moded_check_part(Client *client, Channel *channel); int moded_join(Client *client, Channel *channel); -int moded_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); -int deny_all(Client *client, Channel *channel, char mode, char *para, int checkt, int what); +int moded_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment); +int moded_quit(Client *client, MessageTag *mtags, const char *comment); +int delayjoin_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what); int moded_chanmode(Client *client, Channel *channel, - MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); -int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, char *text, SendType sendtype); -char *moded_serialize(ModData *m); -void moded_unserialize(char *str, ModData *m); + MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode); +int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, const char *text, SendType sendtype); +const char *moded_serialize(ModData *m); +void moded_unserialize(const char *str, ModData *m); MOD_INIT() { @@ -45,13 +46,14 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 0; req.is_ok = extcmode_default_requirechop; - req.flag = 'D'; + req.letter = 'D'; CmodeDelayed = CmodeAdd(modinfo->handle, req, &EXTMODE_DELAYED); memset(&req, 0, sizeof(req)); req.paracount = 0; - req.is_ok = deny_all; - req.flag = 'd'; + req.is_ok = delayjoin_is_ok; + req.letter = 'd'; + req.local = 1; CmodePostDelayed = CmodeAdd(modinfo->handle, req, &EXTMODE_POST_DELAYED); memset(&mreq, 0, sizeof(mreq)); @@ -76,6 +78,8 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_JOIN_DATA, 0, moded_join); HookAdd(modinfo->handle, HOOKTYPE_LOCAL_PART, 0, moded_part); HookAdd(modinfo->handle, HOOKTYPE_REMOTE_PART, 0, moded_part); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, moded_quit); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, moded_quit); HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CHANMODE, 0, moded_chanmode); HookAdd(modinfo->handle, HOOKTYPE_PRE_REMOTE_CHANMODE, 0, moded_chanmode); HookAdd(modinfo->handle, HOOKTYPE_PRE_CHANMSG, 0, moded_prechanmsg); @@ -97,10 +101,10 @@ void set_post_delayed(Channel *channel) { MessageTag *mtags = NULL; - channel->mode.extmode |= EXTMODE_POST_DELAYED; + channel->mode.mode |= EXTMODE_POST_DELAYED; new_message(&me, NULL, &mtags); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +d", me.name, channel->chname); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +d", me.name, channel->name); free_message_tags(mtags); } @@ -108,10 +112,10 @@ void clear_post_delayed(Channel *channel) { MessageTag *mtags = NULL; - channel->mode.extmode &= ~EXTMODE_POST_DELAYED; + channel->mode.mode &= ~EXTMODE_POST_DELAYED; new_message(&me, NULL, &mtags); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -d", me.name, channel->chname); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -d", me.name, channel->name); free_message_tags(mtags); } @@ -153,14 +157,14 @@ bool channel_has_invisible_users(Channel *channel) bool channel_is_post_delayed(Channel *channel) { - if (channel->mode.extmode & EXTMODE_POST_DELAYED) + if (channel->mode.mode & EXTMODE_POST_DELAYED) return true; return false; } bool channel_is_delayed(Channel *channel) { - if (channel->mode.extmode & EXTMODE_DELAYED) + if (channel->mode.mode & EXTMODE_DELAYED) return true; return false; } @@ -196,7 +200,7 @@ void clear_user_invisible(Channel *channel, Client *client) } } - if (should_clear && (channel->mode.extmode & EXTMODE_POST_DELAYED)) + if (should_clear && (channel->mode.mode & EXTMODE_POST_DELAYED)) { clear_post_delayed(channel); } @@ -213,18 +217,18 @@ void clear_user_invisible_announce(Channel *channel, Client *client, MessageTag clear_user_invisible(channel, client); ircsnprintf(joinbuf, sizeof(joinbuf), ":%s!%s@%s JOIN %s", - client->name, client->user->username, GetHost(client), channel->chname); + client->name, client->user->username, GetHost(client), channel->name); ircsnprintf(exjoinbuf, sizeof(exjoinbuf), ":%s!%s@%s JOIN %s %s :%s", - client->name, client->user->username, GetHost(client), channel->chname, - !isdigit(*client->user->svid) ? client->user->svid : "*", + client->name, client->user->username, GetHost(client), channel->name, + IsLoggedIn(client) ? client->user->account : "*", client->info); - new_message_special(client, recv_mtags, &mtags, ":%s JOIN %s", client->name, channel->chname); + new_message_special(client, recv_mtags, &mtags, ":%s JOIN %s", client->name, channel->name); for (i = channel->members; i; i = i->next) { Client *acptr = i->client; - if (!is_skochanop(acptr, channel) && acptr != client && MyConnect(acptr)) + if (!check_channel_access(acptr, channel, "hoaq") && acptr != client && MyConnect(acptr)) { if (HasCapabilityFast(acptr, CAP_EXTENDED_JOIN)) sendto_one(acptr, mtags, "%s", exjoinbuf); @@ -252,7 +256,7 @@ void set_user_invisible(Channel *channel, Client *client) } -int deny_all(Client *client, Channel *channel, char mode, char *para, int checkt, int what) +int delayjoin_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) { return EX_ALWAYS_DENY; } @@ -272,7 +276,7 @@ int moded_join(Client *client, Channel *channel) return 0; } -int moded_part(Client *client, Channel *channel, MessageTag *mtags, char *comment) +int moded_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment) { if (channel_is_delayed(channel) || channel_is_post_delayed(channel)) clear_user_invisible(channel, client); @@ -280,7 +284,23 @@ int moded_part(Client *client, Channel *channel, MessageTag *mtags, char *commen return 0; } -int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, char *modebuf, char *parabuf, time_t sendts, int samode) +int moded_quit(Client *client, MessageTag *mtags, const char *comment) +{ + Membership *membership; + Channel *channel; + + for (membership = client->user->channel; membership; membership=membership->next) + { + channel = membership->channel; + /* Identical to moded_part() */ + if (channel_is_delayed(channel) || channel_is_post_delayed(channel)) + clear_user_invisible(channel, client); + } + + return 0; +} + +int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode) { long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join"); @@ -317,16 +337,16 @@ int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, cha if (moded_user_invisible(i->client, channel)) { MessageTag *mtags = NULL; - new_message_special(i->client, recv_mtags, &mtags, ":%s JOIN %s", i->client->name, channel->chname); + new_message_special(i->client, recv_mtags, &mtags, ":%s JOIN %s", i->client->name, channel->name); if (HasCapabilityFast(user, CAP_EXTENDED_JOIN)) { sendto_one(user, mtags, ":%s!%s@%s JOIN %s %s :%s", i->client->name, i->client->user->username, GetHost(i->client), - channel->chname, - !isdigit(*i->client->user->svid) ? i->client->user->svid : "*", + channel->name, + IsLoggedIn(i->client) ? i->client->user->account : "*", i->client->info); } else { - sendto_one(user, mtags, ":%s!%s@%s JOIN :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->chname); + sendto_one(user, mtags, ":%s!%s@%s JOIN :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name); } free_message_tags(mtags); } @@ -354,8 +374,8 @@ int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, cha if (moded_user_invisible(i->client, channel)) { MessageTag *mtags = NULL; - new_message_special(i->client, recv_mtags, &mtags, ":%s PART %s", i->client->name, channel->chname); - sendto_one(user, mtags, ":%s!%s@%s PART :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->chname); + new_message_special(i->client, recv_mtags, &mtags, ":%s PART %s", i->client->name, channel->name); + sendto_one(user, mtags, ":%s!%s@%s PART :%s", i->client->name, i->client->user->username, GetHost(i->client), channel->name); free_message_tags(mtags); } } @@ -367,7 +387,7 @@ int moded_chanmode(Client *client, Channel *channel, MessageTag *recv_mtags, cha return 0; } -int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, char *text, SendType sendtype) +int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, const char *text, SendType sendtype) { if ((channel_is_delayed(channel) || channel_is_post_delayed(channel)) && (moded_user_invisible(client, channel))) clear_user_invisible_announce(channel, client, mtags); @@ -375,12 +395,12 @@ int moded_prechanmsg(Client *client, Channel *channel, MessageTag *mtags, char * return 0; } -char *moded_serialize(ModData *m) +const char *moded_serialize(ModData *m) { return m->i ? "1" : "0"; } -void moded_unserialize(char *str, ModData *m) +void moded_unserialize(const char *str, ModData *m) { m->i = atoi(str); } diff --git a/src/modules/chanmodes/floodprot.c b/src/modules/chanmodes/floodprot.c index a3aa3ef..148a652 100644 --- a/src/modules/chanmodes/floodprot.c +++ b/src/modules/chanmodes/floodprot.c @@ -25,17 +25,18 @@ ModuleHeader MOD_HEADER "5.0", "Channel Mode +f", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -#define CHFLD_CTCP 0 /* c */ -#define CHFLD_JOIN 1 /* j */ -#define CHFLD_KNOCK 2 /* k */ -#define CHFLD_MSG 3 /* m */ -#define CHFLD_NICK 4 /* n */ -#define CHFLD_TEXT 5 /* t */ -#define CHFLD_REPEAT 6 /* r */ - +typedef enum Flood { + CHFLD_CTCP = 0, + CHFLD_JOIN = 1, + CHFLD_KNOCK = 2, + CHFLD_MSG = 3, + CHFLD_NICK = 4, + CHFLD_TEXT = 5, + CHFLD_REPEAT = 6, +} Flood; #define NUMFLD 7 /* 7 flood types */ /** Configuration settings */ @@ -47,7 +48,7 @@ struct { typedef struct FloodType { char letter; - int index; + Flood index; char *description; char default_action; char *actions; @@ -61,7 +62,7 @@ FloodType floodtypes[] = { { 'c', CHFLD_CTCP, "CTCPflood", 'C', "mM", 0, }, { 'j', CHFLD_JOIN, "joinflood", 'i', "R", 0, }, { 'k', CHFLD_KNOCK, "knockflood", 'K', "", 0, }, - { 'm', CHFLD_MSG, "msg/noticeflood", 'm', "M", 0, }, + { 'm', CHFLD_MSG, "msg/noticeflood", 'm', "M", 0, }, { 'n', CHFLD_NICK, "nickflood", 'N', "", 0, }, { 't', CHFLD_TEXT, "msg/noticeflood", '\0', "bd", 1, }, { 'r', CHFLD_REPEAT, "repeating", '\0', "bd", 1, }, @@ -113,7 +114,7 @@ static int timedban_available = 0; /**< Set to 1 if extbans/timedban module is l RemoveChannelModeTimer *removechannelmodetimer_list = NULL; char *floodprot_msghash_key = NULL; -#define IsFloodLimit(x) ((x)->mode.extmode & EXTMODE_FLOODLIMIT) +#define IsFloodLimit(x) ((x)->mode.mode & EXTMODE_FLOODLIMIT) /* Forward declarations */ static void init_config(void); @@ -128,24 +129,24 @@ static int do_floodprot(Channel *channel, Client *client, int what); char *channel_modef_string(ChannelFloodProtection *x, char *str); void do_floodprot_action(Channel *channel, int what); void floodprottimer_add(Channel *channel, char mflag, time_t when); -uint64_t gen_floodprot_msghash(char *text); -int cmodef_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what); -void *cmodef_put_param(void *r_in, char *param); -char *cmodef_get_param(void *r_in); -char *cmodef_conv_param(char *param_in, Client *client, Channel *channel); +uint64_t gen_floodprot_msghash(const char *text); +int cmodef_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); +void *cmodef_put_param(void *r_in, const char *param); +const char *cmodef_get_param(void *r_in); +const char *cmodef_conv_param(const char *param_in, Client *client, Channel *channel); void cmodef_free_param(void *r); void *cmodef_dup_struct(void *r_in); int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx); -int floodprot_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); +int floodprot_join(Client *client, Channel *channel, MessageTag *mtags); EVENT(modef_event); int cmodef_channel_destroy(Channel *channel, int *should_destroy); -int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); -int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment); -int floodprot_nickchange(Client *client, MessageTag *mtags, char *oldnick); +int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype); +int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment); +int floodprot_nickchange(Client *client, MessageTag *mtags, const char *oldnick); int floodprot_chanmode_del(Channel *channel, int m); void memberflood_free(ModData *md); -int floodprot_stats(Client *client, char *flag); +int floodprot_stats(Client *client, const char *flag); void floodprot_free_removechannelmodetimer_list(ModData *m); void floodprot_free_msghash_key(ModData *m); @@ -165,7 +166,7 @@ MOD_INIT() memset(&creq, 0, sizeof(creq)); creq.paracount = 1; creq.is_ok = cmodef_is_ok; - creq.flag = 'f'; + creq.letter = 'f'; creq.unset_with_param = 1; /* ah yeah, +f is special! */ creq.put_param = cmodef_put_param; creq.get_param = cmodef_get_param; @@ -244,53 +245,53 @@ int floodprot_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type != CONFIG_SET) return 0; - if (!strcmp(ce->ce_varname, "modef-default-unsettime")) + if (!strcmp(ce->name, "modef-default-unsettime")) { - if (!ce->ce_vardata) + if (!ce->value) { - config_error_empty(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "set", ce->ce_varname); + config_error_empty(ce->file->filename, ce->line_number, + "set", ce->name); errors++; } else { - int v = atoi(ce->ce_vardata); + int v = atoi(ce->value); if ((v <= 0) || (v > 255)) { config_error("%s:%i: set::modef-default-unsettime: value '%d' out of range (should be 1-255)", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, v); + ce->file->filename, ce->line_number, v); errors++; } } } else - if (!strcmp(ce->ce_varname, "modef-max-unsettime")) + if (!strcmp(ce->name, "modef-max-unsettime")) { - if (!ce->ce_vardata) + if (!ce->value) { - config_error_empty(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "set", ce->ce_varname); + config_error_empty(ce->file->filename, ce->line_number, + "set", ce->name); errors++; } else { - int v = atoi(ce->ce_vardata); + int v = atoi(ce->value); if ((v <= 0) || (v > 255)) { config_error("%s:%i: set::modef-max-unsettime: value '%d' out of range (should be 1-255)", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, v); + ce->file->filename, ce->line_number, v); errors++; } } } else - if (!strcmp(ce->ce_varname, "modef-boot-delay")) + if (!strcmp(ce->name, "modef-boot-delay")) { - if (!ce->ce_vardata) + if (!ce->value) { - config_error_empty(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "set", ce->ce_varname); + config_error_empty(ce->file->filename, ce->line_number, + "set", ce->name); errors++; } else { - long v = config_checkval(ce->ce_vardata, CFG_TIME); + long v = config_checkval(ce->value, CFG_TIME); if ((v < 0) || (v > 600)) { config_error("%s:%i: set::modef-boot-delay: value '%ld' out of range (should be 0-600)", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, v); + ce->file->filename, ce->line_number, v); errors++; } } @@ -309,12 +310,12 @@ int floodprot_config_run(ConfigFile *cf, ConfigEntry *ce, int type) if (type != CONFIG_SET) return 0; - if (!strcmp(ce->ce_varname, "modef-default-unsettime")) - cfg.modef_default_unsettime = (unsigned char)atoi(ce->ce_vardata); - else if (!strcmp(ce->ce_varname, "modef-max-unsettime")) - cfg.modef_max_unsettime = (unsigned char)atoi(ce->ce_vardata); - else if (!strcmp(ce->ce_varname, "modef-boot-delay")) - cfg.modef_boot_delay = config_checkval(ce->ce_vardata, CFG_TIME); + if (!strcmp(ce->name, "modef-default-unsettime")) + cfg.modef_default_unsettime = (unsigned char)atoi(ce->value); + else if (!strcmp(ce->name, "modef-max-unsettime")) + cfg.modef_max_unsettime = (unsigned char)atoi(ce->value); + else if (!strcmp(ce->name, "modef-boot-delay")) + cfg.modef_boot_delay = config_checkval(ce->value, CFG_TIME); else return 0; /* not handled by us */ @@ -331,7 +332,7 @@ FloodType *find_floodprot_by_letter(char c) return NULL; } -FloodType *find_floodprot_by_index(int index) +FloodType *find_floodprot_by_index(Flood index) { int i; for (i=0; i < ARRAY_SIZEOF(floodtypes); i++) @@ -341,11 +342,11 @@ FloodType *find_floodprot_by_index(int index) return NULL; } -int cmodef_is_ok(Client *client, Channel *channel, char mode, char *param, int type, int what) +int cmodef_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) { if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) { - if (IsUser(client) && is_chan_op(client, channel)) + if (IsUser(client) && check_channel_access(client, channel, "oaq")) return EX_ALLOW; if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ sendnumeric(client, ERR_NOTFORHALFOPS, 'f'); @@ -359,7 +360,7 @@ int cmodef_is_ok(Client *client, Channel *channel, char mode, char *param, int t unsigned short warnings = 0, breakit; unsigned char r; FloodType *floodtype; - int index; + Flood index; memset(&newf, 0, sizeof(newf)); @@ -467,7 +468,7 @@ invalidsyntax: return EX_DENY; } -void *cmodef_put_param(void *fld_in, char *param) +void *cmodef_put_param(void *fld_in, const char *param) { ChannelFloodProtection *fld = (ChannelFloodProtection *)fld_in; char xbuf[256], c, a, *p, *p2, *x = xbuf+1; @@ -475,7 +476,7 @@ void *cmodef_put_param(void *fld_in, char *param) unsigned short breakit; unsigned char r; FloodType *floodtype; - int index; + Flood index; strlcpy(xbuf, param, sizeof(xbuf)); @@ -581,7 +582,7 @@ fail_cmodef_put_param: return fld; /* FAIL */ } -char *cmodef_get_param(void *r_in) +const char *cmodef_get_param(void *r_in) { ChannelFloodProtection *r = (ChannelFloodProtection *)r_in; static char retbuf[512]; @@ -596,7 +597,7 @@ char *cmodef_get_param(void *r_in) /** Convert parameter to something proper. * NOTE: client may be NULL if called for e.g. set::modes-on-join */ -char *cmodef_conv_param(char *param_in, Client *client, Channel *channel) +const char *cmodef_conv_param(const char *param_in, Client *client, Channel *channel) { static char retbuf[256]; char param[256]; @@ -607,7 +608,7 @@ char *cmodef_conv_param(char *param_in, Client *client, Channel *channel) unsigned short breakit; unsigned char r; FloodType *floodtype; - int index; + Flood index; memset(&newf, 0, sizeof(newf)); @@ -736,7 +737,7 @@ int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx) return EXSJ_MERGE; } -int floodprot_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]) +int floodprot_join(Client *client, Channel *channel, MessageTag *mtags) { /* I'll explain this only once: * 1. if channel is +f @@ -749,8 +750,8 @@ int floodprot_join(Client *client, Channel *channel, MessageTag *mtags, char *pa * from all servers. */ if (IsFloodLimit(channel) && - (MyUser(client) || client->srvptr->serv->flags.synced) && - (client->srvptr->serv->boottime && (TStime() - client->srvptr->serv->boottime >= MODEF_BOOT_DELAY)) && + (MyUser(client) || client->uplink->server->flags.synced) && + (client->uplink->server->boottime && (TStime() - client->uplink->server->boottime >= MODEF_BOOT_DELAY)) && !IsULine(client)) { do_floodprot(channel, client, CHFLD_JOIN); @@ -818,7 +819,7 @@ char *channel_modef_string(ChannelFloodProtection *x, char *retbuf) return retbuf; } -int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { Membership *mb; ChannelFloodProtection *chp; @@ -834,7 +835,7 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership * if (sendtype == SEND_TYPE_TAGMSG) return 0; // TODO: some TAGMSG specific limit? (1 of 2) - if (ValidatePermissionsForPath("channel:override:flood",client,NULL,channel,NULL) || !IsFloodLimit(channel) || is_skochanop(client, channel)) + if (ValidatePermissionsForPath("channel:override:flood",client,NULL,channel,NULL) || !IsFloodLimit(channel) || check_channel_access(client, channel, "hoaq")) return HOOK_CONTINUE; if (!(mb = find_membership_link(client->user->channel, channel))) @@ -921,16 +922,21 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership * { /* Ban the user */ if (timedban_available && (chp->remove_after[flood_type] > 0)) - snprintf(mask, sizeof(mask), "~t:%d:*!*@%s", chp->remove_after[flood_type], GetHost(client)); - else + { + if (iConf.named_extended_bans) + snprintf(mask, sizeof(mask), "~time:%d:*!*@%s", chp->remove_after[flood_type], GetHost(client)); + else + snprintf(mask, sizeof(mask), "~t:%d:*!*@%s", chp->remove_after[flood_type], GetHost(client)); + } else { snprintf(mask, sizeof(mask), "*!*@%s", GetHost(client)); + } if (add_listmode(&channel->banlist, &me, channel, mask) == 0) { mtags = NULL; new_message(&me, NULL, &mtags); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +b %s 0", me.id, channel->chname, mask); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +b %s 0", me.id, channel->name, mask); sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s +b %s", me.name, channel->chname, mask); + ":%s MODE %s +b %s", me.name, channel->name, mask); free_message_tags(mtags); } /* else.. ban list is full */ } @@ -942,9 +948,9 @@ int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership * return HOOK_CONTINUE; } -int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype) +int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype) { - if (!IsFloodLimit(channel) || is_skochanop(client, channel) || IsULine(client)) + if (!IsFloodLimit(channel) || check_channel_access(client, channel, "hoaq") || IsULine(client)) return 0; if (sendtype == SEND_TYPE_TAGMSG) @@ -960,14 +966,14 @@ int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, int return 0; } -int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, char *comment) +int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment) { if (IsFloodLimit(channel) && !IsULine(client)) do_floodprot(channel, client, CHFLD_KNOCK); return 0; } -int floodprot_nickchange(Client *client, MessageTag *mtags, char *oldnick) +int floodprot_nickchange(Client *client, MessageTag *mtags, const char *oldnick) { Membership *mp; @@ -977,8 +983,7 @@ int floodprot_nickchange(Client *client, MessageTag *mtags, char *oldnick) for (mp = client->user->channel; mp; mp = mp->next) { Channel *channel = mp->channel; - if (channel && IsFloodLimit(channel) && - !(mp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANADMIN))) + if (channel && IsFloodLimit(channel) && !check_channel_access_membership(mp, "vhoaq")) { do_floodprot(channel, client, CHFLD_NICK); } @@ -1080,8 +1085,10 @@ void floodprottimer_add(Channel *channel, char mflag, time_t when) { if (strlen(chp->timers_running)+1 >= sizeof(chp->timers_running)) { - sendto_realops_and_log("floodprottimer_add: too many timers running for %s (%s)!!!", - channel->chname, chp->timers_running); + unreal_log(ULOG_WARNING, "flood", "BUG_FLOODPROTTIMER_ADD", NULL, + "[BUG] floodprottimer_add: too many timers running for $channel ($timers_running)", + log_data_channel("channel", channel), + log_data_string("timers_running", chp->timers_running)); return; } strccat(chp->timers_running, mflag); /* bounds already checked ^^ */ @@ -1137,39 +1144,24 @@ EVENT(modef_event) if (e->when <= now) { /* Remove chanmode... */ - long mode = 0; - Cmode_t extmode = 0; -#ifdef NEWFLDDBG - sendto_realops("modef_event: chan %s mode -%c EXPIRED", e->channel->chname, e->m); -#endif - mode = get_mode_bitbychar(e->m); - if (mode == 0) - extmode = get_extmode_bitbychar(e->m); + Cmode_t extmode = get_extmode_bitbychar(e->m); - if ((mode && (e->channel->mode.mode & mode)) || - (extmode && (e->channel->mode.extmode & extmode))) + if (extmode && (e->channel->mode.mode & extmode)) { MessageTag *mtags = NULL; new_message(&me, NULL, &mtags); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s -%c 0", me.id, e->channel->chname, e->m); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s -%c 0", me.id, e->channel->name, e->m); sendto_channel(e->channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -%c", - me.name, e->channel->chname, e->m); + me.name, e->channel->name, e->m); free_message_tags(mtags); - - e->channel->mode.mode &= ~mode; - e->channel->mode.extmode &= ~extmode; + e->channel->mode.mode &= ~extmode; } /* And delete... */ DelListItem(e, removechannelmodetimer_list); safe_free(e); - } else { -#ifdef NEWFLDDBG - sendto_realops("modef_event: chan %s mode -%c about %d seconds", - e->channel->chname, e->m, e->when - now); -#endif } } } @@ -1220,7 +1212,6 @@ int do_floodprot(Channel *channel, Client *client, int what) void do_floodprot_action(Channel *channel, int what) { char m; - int mode = 0; Cmode_t extmode = 0; ChannelFloodProtection *chp = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f'); FloodType *floodtype = find_floodprot_by_index(what); @@ -1240,15 +1231,11 @@ void do_floodprot_action(Channel *channel, int what) if (chp->action[what] == 'd') return; - mode = get_mode_bitbychar(m); - if (mode == 0) - extmode = get_extmode_bitbychar(m); - - if (!mode && !extmode) + extmode = get_extmode_bitbychar(m); + if (!extmode) return; - if (!(mode && (channel->mode.mode & mode)) && - !(extmode && (channel->mode.extmode & extmode))) + if (!(extmode && (channel->mode.mode & extmode))) { char comment[512], target[CHANNELLEN + 8]; MessageTag *mtags; @@ -1258,8 +1245,8 @@ void do_floodprot_action(Channel *channel, int what) new_message(&me, NULL, &mtags); ircsnprintf(comment, sizeof(comment), "*** Channel %s detected (limit is %d per %d seconds), setting mode +%c", text, chp->limit[what], chp->per, m); - ircsnprintf(target, sizeof(target), "%%%s", channel->chname); - sendto_channel(channel, &me, NULL, PREFIX_HALFOP|PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER, + ircsnprintf(target, sizeof(target), "%%%s", channel->name); + sendto_channel(channel, &me, NULL, "ho", 0, SEND_ALL, mtags, ":%s NOTICE %s :%s", me.name, target, comment); free_message_tags(mtags); @@ -1267,13 +1254,12 @@ void do_floodprot_action(Channel *channel, int what) /* Then the MODE broadcast */ mtags = NULL; new_message(&me, NULL, &mtags); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +%c 0", me.id, channel->chname, m); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +%c", me.name, channel->chname, m); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +%c 0", me.id, channel->name, m); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +%c", me.name, channel->name, m); free_message_tags(mtags); /* Actually set the mode internally */ - channel->mode.mode |= mode; - channel->mode.extmode |= extmode; + channel->mode.mode |= extmode; /* Add remove-chanmode timer */ if (chp->remove_after[what]) @@ -1286,7 +1272,7 @@ void do_floodprot_action(Channel *channel, int what) } } -uint64_t gen_floodprot_msghash(char *text) +uint64_t gen_floodprot_msghash(const char *text) { int i; int is_ctcp, is_action; @@ -1312,10 +1298,10 @@ uint64_t gen_floodprot_msghash(char *text) if (is_ctcp || is_action) { // Remove the \001 chars around the message - if((len = strlen(plaintext)) && plaintext[len - 1] == '\001') + if ((len = strlen(plaintext)) && plaintext[len - 1] == '\001') plaintext[len - 1] = '\0'; plaintext++; - if(is_action) + if (is_action) plaintext += 7; } @@ -1345,7 +1331,7 @@ void memberflood_free(ModData *md) safe_free(md->ptr); } -int floodprot_stats(Client *client, char *flag) +int floodprot_stats(Client *client, const char *flag) { if (*flag != 'S') return 0; diff --git a/src/modules/chanmodes/halfop.c b/src/modules/chanmodes/halfop.c new file mode 100644 index 0000000..2e2a6ba --- /dev/null +++ b/src/modules/chanmodes/halfop.c @@ -0,0 +1,88 @@ +/* + * Channel Mode +h + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "chanmodes/halfop", + "6.0", + "Channel Mode +h", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +int cmode_halfop_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); + +MOD_INIT() +{ + CmodeInfo creq; + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&creq, 0, sizeof(creq)); + creq.paracount = 1; + creq.is_ok = cmode_halfop_is_ok; + creq.letter = 'h'; + creq.prefix = '%'; + creq.sjoin_prefix = '%'; + creq.rank = RANK_HALFOP; + creq.unset_with_param = 1; + creq.type = CMODE_MEMBER; + CmodeAdd(modinfo->handle, creq, NULL); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int cmode_halfop_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) + { + Client *target = find_user(param, NULL); + + if ((what == MODE_DEL) && (target == client)) + { + /* User may always remove their own modes */ + return EX_ALLOW; + } + if ((what == MODE_ADD) && check_channel_access(client, channel, "hoaq")) + { + /* Permitted for +hoaq */ + return EX_ALLOW; + } + if (type == EXCHK_ACCESS_ERR) + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); + return EX_DENY; + } + + /* fallthrough -- should not be used */ + return EX_DENY; +} diff --git a/src/modules/chanmodes/history.c b/src/modules/chanmodes/history.c index 124a25e..9cdc73e 100644 --- a/src/modules/chanmodes/history.c +++ b/src/modules/chanmodes/history.c @@ -11,7 +11,7 @@ ModuleHeader MOD_HEADER "1.0", "Channel Mode +H", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; typedef struct ConfigHistoryExt ConfigHistoryExt; @@ -37,25 +37,25 @@ Cmode_t EXTMODE_HISTORY = 0L; static cfgstruct cfg; static cfgstruct test; -#define HistoryEnabled(channel) (channel->mode.extmode & EXTMODE_HISTORY) +#define HistoryEnabled(channel) (channel->mode.mode & EXTMODE_HISTORY) /* Forward declarations */ static void init_config(cfgstruct *cfg); int history_config_test(ConfigFile *, ConfigEntry *, int, int *); int history_config_posttest(int *); int history_config_run(ConfigFile *, ConfigEntry *, int); -int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode); +int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel); static int compare_history_modes(HistoryChanMode *a, HistoryChanMode *b); -int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what); -void *history_chanmode_put_param(void *r_in, char *param); -char *history_chanmode_get_param(void *r_in); -char *history_chanmode_conv_param(char *param, Client *client, Channel *channel); +int history_chanmode_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); +void *history_chanmode_put_param(void *r_in, const char *param); +const char *history_chanmode_get_param(void *r_in); +const char *history_chanmode_conv_param(const char *param, Client *client, Channel *channel); void history_chanmode_free_param(void *r); void *history_chanmode_dup_struct(void *r_in); int history_chanmode_sjoin_check(Channel *channel, void *ourx, void *theirx); int history_channel_destroy(Channel *channel, int *should_destroy); -int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); -int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); +int history_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype); +int history_join(Client *client, Channel *channel, MessageTag *mtags); CMD_OVERRIDE_FUNC(override_mode); MOD_TEST() @@ -77,7 +77,7 @@ MOD_INIT() memset(&creq, 0, sizeof(creq)); creq.paracount = 1; creq.is_ok = history_chanmode_is_ok; - creq.flag = 'H'; + creq.letter = 'H'; creq.put_param = history_chanmode_put_param; creq.get_param = history_chanmode_get_param; creq.conv_param = history_chanmode_conv_param; @@ -99,11 +99,11 @@ MOD_INIT() MOD_LOAD() { - CommandOverrideAdd(modinfo->handle, "MODE", override_mode); - CommandOverrideAdd(modinfo->handle, "SVSMODE", override_mode); - CommandOverrideAdd(modinfo->handle, "SVS2MODE", override_mode); - CommandOverrideAdd(modinfo->handle, "SAMODE", override_mode); - CommandOverrideAdd(modinfo->handle, "SJOIN", override_mode); + CommandOverrideAdd(modinfo->handle, "MODE", 0, override_mode); + CommandOverrideAdd(modinfo->handle, "SVSMODE", 0, override_mode); + CommandOverrideAdd(modinfo->handle, "SVS2MODE", 0, override_mode); + CommandOverrideAdd(modinfo->handle, "SAMODE", 0, override_mode); + CommandOverrideAdd(modinfo->handle, "SJOIN", 0, override_mode); return MOD_SUCCESS; } @@ -124,8 +124,6 @@ static void init_config(cfgstruct *cfg) cfg->max_storage_per_channel_registered.time = 86400*31; } -#define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } - int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { int errors = 0; @@ -134,140 +132,140 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) long on_join_time=0L, maximum_storage_time_registered=0L, maximum_storage_time_unregistered=0L; /* We only care about set::history */ - if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history")) + if ((type != CONFIG_SET) || strcmp(ce->name, "history")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "channel")) + if (!strcmp(cep->name, "channel")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "playback-on-join")) + if (!strcmp(cepp->name, "playback-on-join")) { - for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = cepp->items; cep4; cep4 = cep4->next) { - if (!strcmp(cep4->ce_varname, "lines")) + if (!strcmp(cep4->name, "lines")) { int v; CheckNull(cep4); - v = atoi(cep4->ce_vardata); + v = atoi(cep4->value); if ((v < 0) || (v > 1000)) { config_error("%s:%i: set::history::channel::playback-on-join::lines must be between 0 and 1000. " "Recommended values are 10-50. Got: %d.", - cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum, v); + cep4->file->filename, cep4->line_number, v); errors++; continue; } test.playback_on_join.lines = v; } else - if (!strcmp(cep4->ce_varname, "time")) + if (!strcmp(cep4->name, "time")) { long v; CheckNull(cep4); - v = config_checkval(cep4->ce_vardata, CFG_TIME); + v = config_checkval(cep4->value, CFG_TIME); if (v < 0) { config_error("%s:%i: set::history::channel::playback-on-join::time must be zero or more.", - cep4->ce_fileptr->cf_filename, cep4->ce_varlinenum); + cep4->file->filename, cep4->line_number); errors++; continue; } test.playback_on_join.time = v; } else { - config_error_unknown(cep4->ce_fileptr->cf_filename, - cep4->ce_varlinenum, "set::history::channel::playback-on-join", cep4->ce_varname); + config_error_unknown(cep4->file->filename, + cep4->line_number, "set::history::channel::playback-on-join", cep4->name); errors++; } } } else - if (!strcmp(cepp->ce_varname, "max-storage-per-channel")) + if (!strcmp(cepp->name, "max-storage-per-channel")) { - for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = cepp->items; cep4; cep4 = cep4->next) { - if (!strcmp(cep4->ce_varname, "registered")) + if (!strcmp(cep4->name, "registered")) { - for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next) + for (cep5 = cep4->items; cep5; cep5 = cep5->next) { - if (!strcmp(cep5->ce_varname, "lines")) + if (!strcmp(cep5->name, "lines")) { int v; CheckNull(cep5); - v = atoi(cep5->ce_vardata); + v = atoi(cep5->value); if (v < 1) { config_error("%s:%i: set::history::channel::max-storage-per-channel::registered::lines must be a positive number.", - cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum); + cep5->file->filename, cep5->line_number); errors++; continue; } test.max_storage_per_channel_registered.lines = v; } else - if (!strcmp(cep5->ce_varname, "time")) + if (!strcmp(cep5->name, "time")) { long v; CheckNull(cep5); - v = config_checkval(cep5->ce_vardata, CFG_TIME); + v = config_checkval(cep5->value, CFG_TIME); if (v < 1) { config_error("%s:%i: set::history::channel::max-storage-per-channel::registered::time must be a positive number.", - cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum); + cep5->file->filename, cep5->line_number); errors++; continue; } test.max_storage_per_channel_registered.time = v; } else { - config_error_unknown(cep5->ce_fileptr->cf_filename, - cep5->ce_varlinenum, "set::history::channel::max-storage-per-channel::registered", cep5->ce_varname); + config_error_unknown(cep5->file->filename, + cep5->line_number, "set::history::channel::max-storage-per-channel::registered", cep5->name); errors++; } } } else - if (!strcmp(cep4->ce_varname, "unregistered")) + if (!strcmp(cep4->name, "unregistered")) { - for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next) + for (cep5 = cep4->items; cep5; cep5 = cep5->next) { - if (!strcmp(cep5->ce_varname, "lines")) + if (!strcmp(cep5->name, "lines")) { int v; CheckNull(cep5); - v = atoi(cep5->ce_vardata); + v = atoi(cep5->value); if (v < 1) { config_error("%s:%i: set::history::channel::max-storage-per-channel::unregistered::lines must be a positive number.", - cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum); + cep5->file->filename, cep5->line_number); errors++; continue; } test.max_storage_per_channel_unregistered.lines = v; } else - if (!strcmp(cep5->ce_varname, "time")) + if (!strcmp(cep5->name, "time")) { long v; CheckNull(cep5); - v = config_checkval(cep5->ce_vardata, CFG_TIME); + v = config_checkval(cep5->value, CFG_TIME); if (v < 1) { config_error("%s:%i: set::history::channel::max-storage-per-channel::unregistered::time must be a positive number.", - cep5->ce_fileptr->cf_filename, cep5->ce_varlinenum); + cep5->file->filename, cep5->line_number); errors++; continue; } test.max_storage_per_channel_unregistered.time = v; } else { - config_error_unknown(cep5->ce_fileptr->cf_filename, - cep5->ce_varlinenum, "set::history::channel::max-storage-per-channel::unregistered", cep5->ce_varname); + config_error_unknown(cep5->file->filename, + cep5->line_number, "set::history::channel::max-storage-per-channel::unregistered", cep5->name); errors++; } } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "set::history::max-storage-per-channel", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "set::history::max-storage-per-channel", cep->name); errors++; } } @@ -304,15 +302,15 @@ int history_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) } if (!used) { - config_error_unknown(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "set::history::channel", cepp->ce_varname); + config_error_unknown(cepp->file->filename, + cepp->line_number, "set::history::channel", cepp->name); errors++; } } } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "set::history", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "set::history", cep->name); errors++; } } @@ -337,58 +335,58 @@ int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type) { ConfigEntry *cep, *cepp, *cep4, *cep5; - if ((type != CONFIG_SET) || strcmp(ce->ce_varname, "history")) + if ((type != CONFIG_SET) || strcmp(ce->name, "history")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "channel")) + if (!strcmp(cep->name, "channel")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "playback-on-join")) + if (!strcmp(cepp->name, "playback-on-join")) { - for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = cepp->items; cep4; cep4 = cep4->next) { - if (!strcmp(cep4->ce_varname, "lines")) + if (!strcmp(cep4->name, "lines")) { - cfg.playback_on_join.lines = atoi(cep4->ce_vardata); + cfg.playback_on_join.lines = atoi(cep4->value); } else - if (!strcmp(cep4->ce_varname, "time")) + if (!strcmp(cep4->name, "time")) { - cfg.playback_on_join.time = config_checkval(cep4->ce_vardata, CFG_TIME); + cfg.playback_on_join.time = config_checkval(cep4->value, CFG_TIME); } } } else - if (!strcmp(cepp->ce_varname, "max-storage-per-channel")) + if (!strcmp(cepp->name, "max-storage-per-channel")) { - for (cep4 = cepp->ce_entries; cep4; cep4 = cep4->ce_next) + for (cep4 = cepp->items; cep4; cep4 = cep4->next) { - if (!strcmp(cep4->ce_varname, "registered")) + if (!strcmp(cep4->name, "registered")) { - for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next) + for (cep5 = cep4->items; cep5; cep5 = cep5->next) { - if (!strcmp(cep5->ce_varname, "lines")) + if (!strcmp(cep5->name, "lines")) { - cfg.max_storage_per_channel_registered.lines = atoi(cep5->ce_vardata); + cfg.max_storage_per_channel_registered.lines = atoi(cep5->value); } else - if (!strcmp(cep5->ce_varname, "time")) + if (!strcmp(cep5->name, "time")) { - cfg.max_storage_per_channel_registered.time = config_checkval(cep5->ce_vardata, CFG_TIME); + cfg.max_storage_per_channel_registered.time = config_checkval(cep5->value, CFG_TIME); } } } else - if (!strcmp(cep4->ce_varname, "unregistered")) + if (!strcmp(cep4->name, "unregistered")) { - for (cep5 = cep4->ce_entries; cep5; cep5 = cep5->ce_next) + for (cep5 = cep4->items; cep5; cep5 = cep5->next) { - if (!strcmp(cep5->ce_varname, "lines")) + if (!strcmp(cep5->name, "lines")) { - cfg.max_storage_per_channel_unregistered.lines = atoi(cep5->ce_vardata); + cfg.max_storage_per_channel_unregistered.lines = atoi(cep5->value); } else - if (!strcmp(cep5->ce_varname, "time")) + if (!strcmp(cep5->name, "time")) { - cfg.max_storage_per_channel_unregistered.time = config_checkval(cep5->ce_vardata, CFG_TIME); + cfg.max_storage_per_channel_unregistered.time = config_checkval(cep5->value, CFG_TIME); } } } @@ -415,7 +413,7 @@ int history_config_run(ConfigFile *cf, ConfigEntry *ce, int type) * @param lines: The number of lines (the X in +H X:Y) * @param t: The time value (the Y in +H X:Y) */ -int history_parse_chanmode(Channel *channel, char *param, int *lines, long *t) +int history_parse_chanmode(Channel *channel, const char *param, int *lines, long *t) { char buf[64], *p, *q; char contains_non_digit = 0; @@ -479,11 +477,11 @@ int history_parse_chanmode(Channel *channel, char *param, int *lines, long *t) * Does the user have rights to add/remove this channel mode? * Is the supplied mode parameter ok? */ -int history_chanmode_is_ok(Client *client, Channel *channel, char mode, char *param, int type, int what) +int history_chanmode_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) { if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) { - if (IsUser(client) && is_chan_op(client, channel)) + if (IsUser(client) && check_channel_access(client, channel, "oaq")) return EX_ALLOW; if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ sendnumeric(client, ERR_NOTFORHALFOPS, 'H'); @@ -529,7 +527,7 @@ static void history_chanmode_helper(char *buf, size_t bufsize, int lines, long t /** Convert channel parameter to something proper. * NOTE: client may be NULL if called for e.g. set::modes-playback-on-join */ -char *history_chanmode_conv_param(char *param, Client *client, Channel *channel) +const char *history_chanmode_conv_param(const char *param, Client *client, Channel *channel) { static char buf[64]; int lines = 0; @@ -543,7 +541,7 @@ char *history_chanmode_conv_param(char *param, Client *client, Channel *channel) } /** Store the +H x:y channel mode */ -void *history_chanmode_put_param(void *mode_in, char *param) +void *history_chanmode_put_param(void *mode_in, const char *param) { HistoryChanMode *h = (HistoryChanMode *)mode_in; int lines = 0; @@ -565,7 +563,7 @@ void *history_chanmode_put_param(void *mode_in, char *param) } /** Retrieve the +H settings (the X:Y string) */ -char *history_chanmode_get_param(void *h_in) +const char *history_chanmode_get_param(void *h_in) { HistoryChanMode *h = (HistoryChanMode *)h_in; static char buf[64]; @@ -612,7 +610,7 @@ int history_chanmode_sjoin_check(Channel *channel, void *ourx, void *theirx) } /** On channel mode change, communicate the +H limits to the history backend layer */ -int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode) +int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel) { HistoryChanMode *settings; @@ -623,9 +621,9 @@ int history_chanmode_change(Client *client, Channel *channel, MessageTag *mtags, /* If so, grab the settings, and communicate them */ settings = (HistoryChanMode *)GETPARASTRUCT(channel, 'H'); if (settings) - history_set_limit(channel->chname, settings->max_lines, settings->max_time); + history_set_limit(channel->name, settings->max_lines, settings->max_time); else - history_destroy(channel->chname); + history_destroy(channel->name); return 0; } @@ -636,12 +634,12 @@ int history_channel_destroy(Channel *channel, int *should_destroy) if (*should_destroy == 0) return 0; /* channel will not be destroyed */ - history_destroy(channel->chname); + history_destroy(channel->name); return 0; } -int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype) +int history_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype) { char buf[512]; char source[64]; @@ -672,15 +670,15 @@ int history_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, snprintf(buf, sizeof(buf), ":%s %s %s :%s", source, sendtype_to_cmd(sendtype), - channel->chname, + channel->name, text); - history_add(channel->chname, mtags, buf); + history_add(channel->name, mtags, buf); return 0; } -int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]) +int history_join(Client *client, Channel *channel, MessageTag *mtags) { /* Only for +H channels */ if (!HistoryEnabled(channel) || !cfg.playback_on_join.lines || !cfg.playback_on_join.time) @@ -689,7 +687,7 @@ int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv /* No history-on-join for clients that implement CHATHISTORY, * they will pull history themselves if they need it. */ - if (HasCapability(client, "draft/chathistory") || HasCapability(client, "chathistory")) + if (HasCapability(client, "draft/chathistory") /*|| HasCapability(client, "chathistory")*/) return 0; if (MyUser(client) && can_receive_history(client)) @@ -700,7 +698,7 @@ int history_join(Client *client, Channel *channel, MessageTag *mtags, char *parv filter.cmd = HFC_SIMPLE; filter.last_lines = cfg.playback_on_join.lines; filter.last_seconds = cfg.playback_on_join.time; - r = history_request(channel->chname, &filter); + r = history_request(channel->name, &filter); if (r) { history_send_result(client, r); @@ -724,10 +722,10 @@ CMD_OVERRIDE_FUNC(override_mode) * means: we are the server that services are linked to. */ if ((IsServer(client) && client->local) || - (IsUser(client) && client->srvptr && client->srvptr->local)) + (IsUser(client) && client->uplink && client->uplink->local)) { /* Now check if the channel is currently +r */ - if ((parc >= 2) && !BadPtr(parv[1]) && ((channel = find_channel(parv[1], NULL))) && + if ((parc >= 2) && !BadPtr(parv[1]) && ((channel = find_channel(parv[1]))) && has_channel_mode(channel, 'r')) { had_r = 1; @@ -744,7 +742,7 @@ CMD_OVERRIDE_FUNC(override_mode) * then... */ if (had_r && - ((channel = find_channel(parv[1], NULL))) && + ((channel = find_channel(parv[1]))) && !has_channel_mode(channel, 'r') && HistoryEnabled(channel)) { @@ -770,7 +768,9 @@ CMD_OVERRIDE_FUNC(override_mode) if (changed) { MessageTag *mtags = NULL; - char *params = history_chanmode_get_param(settings); + const char *params = history_chanmode_get_param(settings); + char modebuf[BUFSIZE], parabuf[BUFSIZE]; + int destroy_channel = 0; if (!params) return; /* Weird */ @@ -782,13 +782,13 @@ CMD_OVERRIDE_FUNC(override_mode) sendto_channel(channel, &me, &me, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", - me.name, channel->chname, modebuf, parabuf); + me.name, channel->name, modebuf, parabuf); sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s %lld", - me.id, channel->chname, modebuf, parabuf, + me.id, channel->name, modebuf, parabuf, (long long)channel->creationtime); /* Activate this hook just like cmd_mode.c */ - RunHook7(HOOKTYPE_REMOTE_CHANMODE, &me, channel, mtags, modebuf, parabuf, 0, 0); + RunHook(HOOKTYPE_REMOTE_CHANMODE, &me, channel, mtags, modebuf, parabuf, 0, 0, &destroy_channel); free_message_tags(mtags); diff --git a/src/modules/chanmodes/inviteonly.c b/src/modules/chanmodes/inviteonly.c new file mode 100644 index 0000000..d7548f9 --- /dev/null +++ b/src/modules/chanmodes/inviteonly.c @@ -0,0 +1,77 @@ +/* + * Channel Mode +i + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + + +ModuleHeader MOD_HEADER + = { + "chanmodes/inviteonly", + "6.0", + "Channel Mode +i", + "UnrealIRCd Team", + "unrealircd-6", + }; + +Cmode_t EXTCMODE_INVITE_ONLY; + +#define IsInviteOnly(channel) (channel->mode.mode & EXTCMODE_INVITE_ONLY) + +int inviteonly_can_join(Client *client, Channel *channel, const char *key, char **errmsg); + +MOD_INIT() +{ + CmodeInfo req; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&req, 0, sizeof(req)); + req.paracount = 0; + req.letter = 'i'; + req.is_ok = extcmode_default_requirehalfop; + CmodeAdd(modinfo->handle, req, &EXTCMODE_INVITE_ONLY); + + HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, inviteonly_can_join); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int inviteonly_can_join (Client *client, Channel *channel, const char *key, char **errmsg) +{ + if (IsInviteOnly(channel)) + { + if (is_invited(client, channel)) + return 0; + if (find_invex(channel, client)) + return 0; + *errmsg = STR_ERR_INVITEONLYCHAN; + return ERR_INVITEONLYCHAN; + } + return 0; +} diff --git a/src/modules/chanmodes/isregistered.c b/src/modules/chanmodes/isregistered.c new file mode 100644 index 0000000..4d8675f --- /dev/null +++ b/src/modules/chanmodes/isregistered.c @@ -0,0 +1,72 @@ +/* + * Channel Mode +r + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + + +ModuleHeader MOD_HEADER + = { + "chanmodes/isregistered", + "6.0", + "Channel Mode +r", + "UnrealIRCd Team", + "unrealircd-6", + }; + +Cmode_t EXTCMODE_REGISTERED; + +#define IsRegisteredChannel(channel) (channel->mode.mode & EXTCMODE_REGISTERED) + +int isregistered_chanmode_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what); + +MOD_INIT() +{ + CmodeInfo req; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&req, 0, sizeof(req)); + req.paracount = 0; + req.letter = 'r'; + req.is_ok = isregistered_chanmode_is_ok; + CmodeAdd(modinfo->handle, req, &EXTCMODE_REGISTERED); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int isregistered_chanmode_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if (!IsServer(client) && !IsULine(client)) + { + if (type == EXCHK_ACCESS_ERR) + sendnumeric(client, ERR_ONLYSERVERSCANCHANGE, channel->name); + return EX_ALWAYS_DENY; + } + return EX_ALLOW; +} diff --git a/src/modules/chanmodes/issecure.c b/src/modules/chanmodes/issecure.c index 56320ab..07839a8 100644 --- a/src/modules/chanmodes/issecure.c +++ b/src/modules/chanmodes/issecure.c @@ -3,7 +3,7 @@ * (C) Copyright 2010-.. Bram Matthys (Syzop) and the UnrealIRCd team * * This module will indicate if a channel is secure, and if so will set +Z. - * Secure is defined as: all users on the channel are connected through SSL/TLS + * Secure is defined as: all users on the channel are connected through TLS * Additionally, the channel has to be +z (only allow secure users to join). * Suggested on http://bugs.unrealircd.org/view.php?id=3720 * Thanks go to fez for pushing us for some kind of method to indicate @@ -34,21 +34,21 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +Z", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_ISSECURE; -#define IsSecureChanIndicated(channel) (channel->mode.extmode & EXTCMODE_ISSECURE) +#define IsSecureChanIndicated(channel) (channel->mode.mode & EXTCMODE_ISSECURE) int IsSecureJoin(Channel *channel); -int modeZ_is_ok(Client *client, Channel *channel, char mode, char *para, int checkt, int what); -int issecure_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); -int issecure_part(Client *client, Channel *channel, MessageTag *mtags, char *comment); -int issecure_quit(Client *client, MessageTag *mtags, char *comment); -int issecure_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment); +int modeZ_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what); +int issecure_join(Client *client, Channel *channel, MessageTag *mtags); +int issecure_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment); +int issecure_quit(Client *client, MessageTag *mtags, const char *comment); +int issecure_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, const char *comment); int issecure_chanmode(Client *client, Channel *channel, MessageTag *mtags, - char *modebuf, char *parabuf, time_t sendts, int samode); + const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel); MOD_TEST() @@ -64,7 +64,7 @@ CmodeInfo req; memset(&req, 0, sizeof(req)); req.paracount = 0; req.is_ok = modeZ_is_ok; - req.flag = 'Z'; + req.letter = 'Z'; req.local = 1; /* local channel mode */ CmodeAdd(modinfo->handle, req, &EXTCMODE_ISSECURE); @@ -108,7 +108,7 @@ int IsSecureJoin(Channel *channel) return i; } -int modeZ_is_ok(Client *client, Channel *channel, char mode, char *para, int checkt, int what) +int modeZ_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) { /* Reject any attempt to set or unset our mode. Even to IRCOps */ return EX_ALWAYS_DENY; @@ -143,17 +143,17 @@ void issecure_unset(Channel *channel, Client *client, MessageTag *recv_mtags, in if (notice) { mtags = NULL; - new_message_special(&me, recv_mtags, &mtags, "NOTICE %s :setting -Z", channel->chname); + new_message_special(&me, recv_mtags, &mtags, "NOTICE %s :setting -Z", channel->name); sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s NOTICE %s :User '%s' joined and is not connected through SSL/TLS, setting channel -Z (insecure)", - me.id, channel->chname, client->name); + ":%s NOTICE %s :User '%s' joined and is not connected through TLS, setting channel -Z (insecure)", + me.id, channel->name, client->name); free_message_tags(mtags); } - channel->mode.extmode &= ~EXTCMODE_ISSECURE; + channel->mode.mode &= ~EXTCMODE_ISSECURE; mtags = NULL; - new_message_special(&me, recv_mtags, &mtags, "MODE %s -Z", channel->chname); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -Z", me.name, channel->chname); + new_message_special(&me, recv_mtags, &mtags, "MODE %s -Z", channel->name); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s -Z", me.name, channel->name); free_message_tags(mtags); } @@ -168,31 +168,31 @@ void issecure_set(Channel *channel, Client *client, MessageTag *recv_mtags, int MessageTag *mtags; mtags = NULL; - new_message_special(&me, recv_mtags, &mtags, "NOTICE %s :setting +Z", channel->chname); + new_message_special(&me, recv_mtags, &mtags, "NOTICE %s :setting +Z", channel->name); if (notice && client) { /* note that we have to skip 'client', since when this call is being made * he is still considered a member of this channel. */ sendto_channel(channel, &me, client, 0, 0, SEND_LOCAL, NULL, - ":%s NOTICE %s :Now all users in the channel are connected through SSL/TLS, setting channel +Z (secure)", - me.name, channel->chname); + ":%s NOTICE %s :Now all users in the channel are connected through TLS, setting channel +Z (secure)", + me.name, channel->name); } else if (notice) { /* note the missing word 'now' in next line */ sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, NULL, - ":%s NOTICE %s :All users in the channel are connected through SSL/TLS, setting channel +Z (secure)", - me.name, channel->chname); + ":%s NOTICE %s :All users in the channel are connected through TLS, setting channel +Z (secure)", + me.name, channel->name); } free_message_tags(mtags); - channel->mode.extmode |= EXTCMODE_ISSECURE; + channel->mode.mode |= EXTCMODE_ISSECURE; mtags = NULL; - new_message_special(&me, recv_mtags, &mtags, "MODE %s +Z", channel->chname); + new_message_special(&me, recv_mtags, &mtags, "MODE %s +Z", channel->name); sendto_channel(channel, &me, client, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +Z", - me.name, channel->chname); + me.name, channel->name); free_message_tags(mtags); } @@ -200,7 +200,7 @@ void issecure_set(Channel *channel, Client *client, MessageTag *recv_mtags, int * so while they can be written shorter, they would only take longer to execute! */ -int issecure_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]) +int issecure_join(Client *client, Channel *channel, MessageTag *mtags) { /* Check only if chan already +zZ and the user joining is insecure (no need to count) */ if (IsSecureJoin(channel) && IsSecureChanIndicated(channel) && !IsSecureConnect(client) && !IsULine(client)) @@ -213,7 +213,7 @@ int issecure_join(Client *client, Channel *channel, MessageTag *mtags, char *par return 0; } -int issecure_part(Client *client, Channel *channel, MessageTag *mtags, char *comment) +int issecure_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment) { /* Only care if chan is +z-Z and the user leaving is insecure, then count */ if (IsSecureJoin(channel) && !IsSecureChanIndicated(channel) && !IsSecureConnect(client) && @@ -222,7 +222,7 @@ int issecure_part(Client *client, Channel *channel, MessageTag *mtags, char *com return 0; } -int issecure_quit(Client *client, MessageTag *mtags, char *comment) +int issecure_quit(Client *client, MessageTag *mtags, const char *comment) { Membership *membership; Channel *channel; @@ -238,7 +238,7 @@ Channel *channel; return 0; } -int issecure_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, char *comment) +int issecure_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, const char *comment) { /* Identical to part&quit, except we care about 'victim' and not 'client' */ if (IsSecureJoin(channel) && !IsSecureChanIndicated(channel) && @@ -248,7 +248,7 @@ int issecure_kick(Client *client, Client *victim, Channel *channel, MessageTag * } int issecure_chanmode(Client *client, Channel *channel, MessageTag *mtags, - char *modebuf, char *parabuf, time_t sendts, int samode) + const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel) { if (!strchr(modebuf, 'z')) return 0; /* don't care */ diff --git a/src/modules/chanmodes/key.c b/src/modules/chanmodes/key.c new file mode 100644 index 0000000..e0753cc --- /dev/null +++ b/src/modules/chanmodes/key.c @@ -0,0 +1,232 @@ +/* + * Channel Mode +k + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "chanmodes/key", + "6.0", + "Channel Mode +k", + "UnrealIRCd Team", + "unrealircd-6", + }; + +typedef struct ChannelKey ChannelKey; +struct ChannelKey { + char key[KEYLEN+1]; +}; + +/* Global variables */ +ModDataInfo *mdkey = NULL; +Cmode_t EXTMODE_KEY = 0L; + +#define IsKey(x) ((x)->mode.mode & EXTMODE_KEY) + +/* Forward declarations */ +int key_can_join(Client *client, Channel *channel, const char *key, char **errmsg); +int cmode_key_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); +void *cmode_key_put_param(void *r_in, const char *param); +const char *cmode_key_get_param(void *r_in); +const char *cmode_key_conv_param(const char *param_in, Client *client, Channel *channel); +void cmode_key_free_param(void *r); +void *cmode_key_dup_struct(void *r_in); +int cmode_key_sjoin_check(Channel *channel, void *ourx, void *theirx); +int is_valid_key(const char *key); +void transform_channel_key(const char *i, char *o, int n); + +MOD_INIT() +{ + CmodeInfo creq; + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&creq, 0, sizeof(creq)); + creq.paracount = 1; + creq.is_ok = cmode_key_is_ok; + creq.letter = 'k'; + creq.unset_with_param = 1; /* yeah... +k is like this */ + creq.put_param = cmode_key_put_param; + creq.get_param = cmode_key_get_param; + creq.conv_param = cmode_key_conv_param; + creq.free_param = cmode_key_free_param; + creq.dup_struct = cmode_key_dup_struct; + creq.sjoin_check = cmode_key_sjoin_check; + CmodeAdd(modinfo->handle, creq, &EXTMODE_KEY); + + HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, key_can_join); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +/** Can the user join the channel? */ +int key_can_join(Client *client, Channel *channel, const char *key, char **errmsg) +{ + ChannelKey *r = (ChannelKey *)GETPARASTRUCT(channel, 'k'); + + /* Is the channel +k? */ + if (r && *r->key) + { + if (key && !strcmp(r->key, key)) + return 0; + *errmsg = STR_ERR_BADCHANNELKEY; + return ERR_BADCHANNELKEY; + } + + return 0; +} + +int cmode_key_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) + { + /* Permitted for +hoaq */ + if (IsUser(client) && check_channel_access(client, channel, "hoaq")) + return EX_ALLOW; + return EX_DENY; + } else + if (type == EXCHK_PARAM) + { + if (!is_valid_key(param)) + { + sendnumeric(client, ERR_INVALIDMODEPARAM, + channel->name, 'k', "*", "Channel key contains forbidden characters or is too long"); + return EX_DENY; + } + return EX_ALLOW; + } + + /* fallthrough -- should not be used */ + return EX_DENY; +} + +void *cmode_key_put_param(void *k_in, const char *param) +{ + ChannelKey *fld = (ChannelKey *)k_in; + + if (!fld) + fld = safe_alloc(sizeof(ChannelKey)); + + transform_channel_key(param, fld->key, sizeof(fld->key)); + + return fld; +} + +const char *cmode_key_get_param(void *r_in) +{ + ChannelKey *r = (ChannelKey *)r_in; + static char retbuf[KEYLEN+1]; + + if (!r) + return NULL; + + strlcpy(retbuf, r->key, sizeof(retbuf)); + return retbuf; +} + +const char *cmode_key_conv_param(const char *param, Client *client, Channel *channel) +{ + static char retbuf[KEYLEN+1]; + + transform_channel_key(param, retbuf, sizeof(retbuf)); + + if (!*retbuf) + return NULL; /* entire key was invalid */ + + return retbuf; +} + +void cmode_key_free_param(void *r) +{ + safe_free(r); +} + +void *cmode_key_dup_struct(void *r_in) +{ + ChannelKey *r = (ChannelKey *)r_in; + ChannelKey *w = safe_alloc(sizeof(ChannelKey)); + + memcpy(w, r, sizeof(ChannelKey)); + + return (void *)w; +} + +int cmode_key_sjoin_check(Channel *channel, void *ourx, void *theirx) +{ + ChannelKey *our = (ChannelKey *)ourx; + ChannelKey *their = (ChannelKey *)theirx; + int i; + int r; + + r = strcmp(our->key, their->key); + if (r == 0) + return EXSJ_SAME; + else if (r > 0) + return EXSJ_WEWON; + else + return EXSJ_THEYWON; +} + +int valid_key_char(char c) +{ + if (strchr(" :,", c)) + return 0; + if (c <= 32) + return 0; + return 1; +} + +#define BADKEYCHARS " :," +int is_valid_key(const char *key) +{ + const char *p; + + if (strlen(key) > KEYLEN) + return 0; + for (p = key; *p; p++) + if (!valid_key_char(*p)) + return 0; + return 1; +} + +void transform_channel_key(const char *i, char *o, int n) +{ + n--; /* reserve one for final nul byte */ + + for (; *i; i++) + { + if (!valid_key_char(*i)) + break; + if (n <= 0) + break; + *o++ = *i; + n--; + } + *o = '\0'; +} diff --git a/src/modules/chanmodes/limit.c b/src/modules/chanmodes/limit.c new file mode 100644 index 0000000..761d1ea --- /dev/null +++ b/src/modules/chanmodes/limit.c @@ -0,0 +1,198 @@ +/* + * Channel Mode +l + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "chanmodes/limit", + "6.0", + "Channel Mode +l", + "UnrealIRCd Team", + "unrealircd-6", + }; + +typedef struct ChannelLimit ChannelLimit; +struct ChannelLimit { + int limit; +}; + +/* Global variables */ +ModDataInfo *mdlimit = NULL; +Cmode_t EXTMODE_LIMIT = 0L; + +#define IsLimit(x) ((x)->mode.mode & EXTMODE_LIMIT) + +/* Just for buffers, nothing else */ +#define LIMITLEN 32 + +/* Forward declarations */ +int limit_can_join(Client *client, Channel *channel, const char *key, char **errmsg); +int cmode_limit_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); +void *cmode_limit_put_param(void *r_in, const char *param); +const char *cmode_limit_get_param(void *r_in); +const char *cmode_limit_conv_param(const char *param_in, Client *client, Channel *channel); +void cmode_limit_free_param(void *r); +void *cmode_limit_dup_struct(void *r_in); +int cmode_limit_sjoin_check(Channel *channel, void *ourx, void *theirx); +int transform_channel_limit(const char *param); + +MOD_INIT() +{ + CmodeInfo creq; + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&creq, 0, sizeof(creq)); + creq.paracount = 1; + creq.is_ok = cmode_limit_is_ok; + creq.letter = 'l'; + creq.put_param = cmode_limit_put_param; + creq.get_param = cmode_limit_get_param; + creq.conv_param = cmode_limit_conv_param; + creq.free_param = cmode_limit_free_param; + creq.dup_struct = cmode_limit_dup_struct; + creq.sjoin_check = cmode_limit_sjoin_check; + CmodeAdd(modinfo->handle, creq, &EXTMODE_LIMIT); + + HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, limit_can_join); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +/** Can the user join the channel? */ +int limit_can_join(Client *client, Channel *channel, const char *key, char **errmsg) +{ + ChannelLimit *r = (ChannelLimit *)GETPARASTRUCT(channel, 'l'); + + /* Is the channel +l? */ + if (r && r->limit && (channel->users >= r->limit)) + { + Hook *h; + for (h = Hooks[HOOKTYPE_CAN_JOIN_LIMITEXCEEDED]; h; h = h->next) + { + int i = (*(h->func.intfunc))(client,channel,key,errmsg); + if (i != 0) + return i; + } + *errmsg = STR_ERR_CHANNELISFULL; + return ERR_CHANNELISFULL; + } + + return 0; +} + +int cmode_limit_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) + { + /* Permitted for +hoaq */ + if (IsUser(client) && check_channel_access(client, channel, "hoaq")) + return EX_ALLOW; + return EX_DENY; + } else + if (type == EXCHK_PARAM) + { + /* Actually any value is valid, we just morph it */ + return EX_ALLOW; + } + + /* fallthrough -- should not be used */ + return EX_DENY; +} + +void *cmode_limit_put_param(void *k_in, const char *param) +{ + ChannelLimit *fld = (ChannelLimit *)k_in; + + if (!fld) + fld = safe_alloc(sizeof(ChannelLimit)); + + fld->limit = transform_channel_limit(param); + + return fld; +} + +const char *cmode_limit_get_param(void *r_in) +{ + ChannelLimit *r = (ChannelLimit *)r_in; + static char retbuf[32]; + + if (!r) + return NULL; + + snprintf(retbuf, sizeof(retbuf), "%d", r->limit); + return retbuf; +} + +const char *cmode_limit_conv_param(const char *param, Client *client, Channel *channel) +{ + static char retbuf[32]; + int v = transform_channel_limit(param); + snprintf(retbuf, sizeof(retbuf), "%d", v); + return retbuf; +} + +void cmode_limit_free_param(void *r) +{ + safe_free(r); +} + +void *cmode_limit_dup_struct(void *r_in) +{ + ChannelLimit *r = (ChannelLimit *)r_in; + ChannelLimit *w = safe_alloc(sizeof(ChannelLimit)); + + memcpy(w, r, sizeof(ChannelLimit)); + + return (void *)w; +} + +int cmode_limit_sjoin_check(Channel *channel, void *ourx, void *theirx) +{ + ChannelLimit *our = (ChannelLimit *)ourx; + ChannelLimit *their = (ChannelLimit *)theirx; + + if (our->limit == their->limit) + return EXSJ_SAME; + else if (our->limit > their->limit) + return EXSJ_WEWON; + else + return EXSJ_THEYWON; +} + +int transform_channel_limit(const char *param) +{ + int v = atoi(param); + if (v <= 0) + v = 1; /* setting +l with a negative number makes no sense */ + if (v > 1000000) + v = 1000000; /* some kind of limit, 1 million (mrah...) */ + return v; +} diff --git a/src/modules/chanmodes/link.c b/src/modules/chanmodes/link.c index 3be4a43..9a5b5b8 100644 --- a/src/modules/chanmodes/link.c +++ b/src/modules/chanmodes/link.c @@ -26,7 +26,7 @@ ModuleHeader MOD_HEADER = { "5.0", "Channel Mode +L", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTMODE_LINK = 0L; @@ -39,26 +39,25 @@ typedef enum { LINKTYPE_BAN = 1, // +b LINKTYPE_INVITE = 2, // +i LINKTYPE_OPER = 3, // +O - LINKTYPE_SSL = 4, // +z + LINKTYPE_SECURE = 4, // +z LINKTYPE_REG = 5, // +R LINKTYPE_LIMIT = 6, // +l LINKTYPE_BADKEY = 7, // +k } linkType; -int cmodeL_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what); -void *cmodeL_put_param(void *r_in, char *param); -char *cmodeL_get_param(void *r_in); -char *cmodeL_conv_param(char *param_in, Client *client, Channel *channel); +int cmodeL_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); +void *cmodeL_put_param(void *r_in, const char *param); +const char *cmodeL_get_param(void *r_in); +const char *cmodeL_conv_param(const char *param_in, Client *client, Channel *channel); void cmodeL_free_param(void *r); void *cmodeL_dup_struct(void *r_in); int cmodeL_sjoin_check(Channel *channel, void *ourx, void *theirx); -int extban_link_syntax(Client *client, int checkt, char *reason); -int extban_link_is_ok(Client *client, Channel *channel, char *param, int checkt, int what, int what2); -char *extban_link_conv_param(char *param); -int extban_link_is_banned(Client *client, Channel *channel, char *ban, int type, char **msg, char **errmsg); -int link_doforward(Client *client, Channel *channel, char *linked, linkType linktype); -int link_pre_localjoin_cb(Client *client, Channel *channel, char *parv[]); +int extban_link_syntax(Client *client, int checkt, const char *reason); +int extban_link_is_ok(BanContext *b); +const char *extban_link_conv_param(BanContext *b, Extban *extban); +int link_doforward(Client *client, Channel *channel, const char *linked, linkType linktype); +int link_pre_localjoin_cb(Client *client, Channel *channel, const char *key); MOD_INIT() { @@ -70,7 +69,7 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 1; req.is_ok = cmodeL_is_ok; - req.flag = 'L'; + req.letter = 'L'; req.unset_with_param = 1; /* Oh yeah, we are special! */ req.put_param = cmodeL_put_param; req.get_param = cmodeL_get_param; @@ -81,10 +80,10 @@ MOD_INIT() CmodeAdd(modinfo->handle, req, &EXTMODE_LINK); memset(&req_extban, 0, sizeof(ExtbanInfo)); - req_extban.flag = 'f'; + req_extban.letter = 'f'; + req_extban.name = "forward"; req_extban.is_ok = extban_link_is_ok; req_extban.conv_param = extban_link_conv_param; - req_extban.is_banned = extban_link_is_banned; req_extban.options = EXTBOPT_ACTMODIFIER; if (!ExtbanAdd(modinfo->handle, req_extban)) { @@ -107,14 +106,14 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int cmodeL_is_ok(Client *client, Channel *channel, char mode, char *para, int type, int what) +int cmodeL_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what) { if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) { - if (IsUser(client) && is_chan_op(client, channel)) + if (IsUser(client) && check_channel_access(client, channel, "oaq")) return EX_ALLOW; if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ - sendnumeric(client, ERR_NOTFORHALFOPS, 'H'); + sendnumeric(client, ERR_NOTFORHALFOPS, 'L'); return EX_DENY; } else if (type == EXCHK_PARAM) @@ -129,7 +128,7 @@ int cmodeL_is_ok(Client *client, Channel *channel, char mode, char *para, int ty return EX_DENY; } - if (find_channel(para, NULL) == channel) + if (find_channel(para) == channel) { if (MyUser(client)) sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'L', @@ -143,7 +142,7 @@ int cmodeL_is_ok(Client *client, Channel *channel, char mode, char *para, int ty return EX_DENY; } -void *cmodeL_put_param(void *r_in, char *param) +void *cmodeL_put_param(void *r_in, const char *param) { aModeLEntry *r = (aModeLEntry *)r_in; @@ -156,7 +155,7 @@ void *cmodeL_put_param(void *r_in, char *param) return (void *)r; } -char *cmodeL_get_param(void *r_in) +const char *cmodeL_get_param(void *r_in) { aModeLEntry *r = (aModeLEntry *)r_in; static char retbuf[CHANNELLEN+1]; @@ -171,10 +170,8 @@ char *cmodeL_get_param(void *r_in) /** Convert parameter to something proper. * NOTE: client may be NULL */ -char *cmodeL_conv_param(char *param, Client *client, Channel *channel) +const char *cmodeL_conv_param(const char *param, Client *client, Channel *channel) { - char *p; - if (!valid_channelname(param)) return NULL; @@ -207,7 +204,7 @@ int cmodeL_sjoin_check(Channel *channel, void *ourx, void *theirx) return EXSJ_THEYWON; } -int extban_link_syntax(Client *client, int checkt, char *reason) +int extban_link_syntax(Client *client, int checkt, const char *reason) { if (MyUser(client) && (checkt == EXBCHK_PARAM)) { @@ -222,52 +219,50 @@ int extban_link_syntax(Client *client, int checkt, char *reason) return 0; // Reject ban } -int extban_link_is_ok(Client *client, Channel *channel, char *param, int checkt, int what, int what2) +int extban_link_is_ok(BanContext *b) { - char paramtmp[MAX_EB_LEN + 1]; - char tmpmask[MAX_EB_LEN + 1]; + static char paramtmp[MAX_EB_LEN + 1]; char *matchby; // Matching method, such as 'n!u@h' char *chan; // Always permit deletion - if (what == MODE_DEL) + if (b->what == MODE_DEL) return 1; - if (what2 != EXBTYPE_BAN) + if (b->ban_type != EXBTYPE_BAN) { - if (checkt == EXBCHK_PARAM) - sendnotice(client, "Ban type ~f only works with bans (+b) and not with exceptions or invex (+e/+I)"); + if (b->is_ok_check == EXBCHK_PARAM) + sendnotice(b->client, "Ban type ~f only works with bans (+b) and not with exceptions or invex (+e/+I)"); return 0; // Reject } - strlcpy(paramtmp, param + 3, sizeof(paramtmp)); // Work on a size-truncated copy + strlcpy(paramtmp, b->banstr, sizeof(paramtmp)); // Work on a size-truncated copy chan = paramtmp; matchby = strchr(paramtmp, ':'); if (!matchby || !matchby[1]) - return extban_link_syntax(client, checkt, "Invalid syntax"); + return extban_link_syntax(b->client, b->is_ok_check, "Invalid syntax"); *matchby++ = '\0'; - if (*chan != '#' || strchr(param, ',')) - return extban_link_syntax(client, checkt, "Invalid channel"); + if (*chan != '#' || strchr(b->banstr, ',')) + return extban_link_syntax(b->client, b->is_ok_check, "Invalid channel"); - // Possibly stack multiple extbans, this is a little convoluted due to extban API limitations - snprintf(tmpmask, sizeof(tmpmask), "~?:%s", matchby); - if (extban_is_ok_nuh_extban(client, channel, tmpmask, checkt, what, what2) == 0) - return extban_link_syntax(client, checkt, "Invalid matcher"); + b->banstr = matchby; + if (extban_is_ok_nuh_extban(b) == 0) + return extban_link_syntax(b->client, b->is_ok_check, "Invalid matcher"); return 1; // Is ok } -char *extban_link_conv_param(char *param) +const char *extban_link_conv_param(BanContext *b, Extban *extban) { static char retbuf[MAX_EB_LEN + 1]; char paramtmp[MAX_EB_LEN + 1]; char tmpmask[MAX_EB_LEN + 1]; char *matchby; // Matching method, such as 'n!u@h' - char *newmask; // Cleaned matching method, such as 'n!u@h' + const char *newmask; // Cleaned matching method, such as 'n!u@h' char *chan; - strlcpy(paramtmp, param + 3, sizeof(paramtmp)); // Work on a size-truncated copy + strlcpy(paramtmp, b->banstr, sizeof(paramtmp)); // Work on a size-truncated copy chan = paramtmp; matchby = strchr(paramtmp, ':'); if (!matchby || !matchby[1]) @@ -277,26 +272,20 @@ char *extban_link_conv_param(char *param) if (!valid_channelname(chan)) return NULL; - // Possibly stack multiple extbans, this is a little convoluted due to extban API limitations - snprintf(tmpmask, sizeof(tmpmask), "~?:%s", matchby); - newmask = extban_conv_param_nuh_or_extban(tmpmask); - if (!newmask || (strlen(newmask) <= 3)) + b->banstr = matchby; + newmask = extban_conv_param_nuh_or_extban(b, extban); + if (BadPtr(newmask)) return NULL; - snprintf(retbuf, sizeof(retbuf), "~f:%s:%s", chan, newmask + 3); + snprintf(retbuf, sizeof(retbuf), "%s:%s", chan, newmask); return retbuf; } -int extban_link_is_banned(Client *client, Channel *channel, char *ban, int type, char **msg, char **errmsg) -{ - // We don't actually ban here because we have to extract the channel name in PRE_LOCAL_JOIN anyways - return 0; -} - -int link_doforward(Client *client, Channel *channel, char *linked, linkType type) +int link_doforward(Client *client, Channel *channel, const char *linked, linkType type) { + char linked_channel_buffer[CHANNELLEN+1]; char desc[64]; - char *parv[3]; + const char *parv[3]; switch (type) { @@ -312,8 +301,8 @@ int link_doforward(Client *client, Channel *channel, char *linked, linkType type strncpy(desc, "channel is oper only", sizeof(desc)); break; - case LINKTYPE_SSL: - strncpy(desc, "channel requires SSL", sizeof(desc)); + case LINKTYPE_SECURE: + strncpy(desc, "channel requires a secure connection", sizeof(desc)); break; case LINKTYPE_REG: @@ -335,23 +324,24 @@ int link_doforward(Client *client, Channel *channel, char *linked, linkType type sendto_one(client, NULL, ":%s %d %s %s %s :[Link] Cannot join channel %s (%s) -- transferring you to %s", - me.name, ERR_LINKCHANNEL, client->name, channel->chname, linked, - channel->chname, desc, linked); + me.name, ERR_LINKCHANNEL, client->name, channel->name, linked, + channel->name, desc, linked); + + strlcpy(linked_channel_buffer, linked, sizeof(linked_channel_buffer)); parv[0] = client->name; - parv[1] = linked; + parv[1] = linked_channel_buffer; parv[2] = NULL; + do_join(client, 2, parv); + return HOOK_DENY; // Original channel join = ignored } -int link_pre_localjoin_cb(Client *client, Channel *channel, char *parv[]) +int link_pre_localjoin_cb(Client *client, Channel *channel, const char *key) { - char *linked; + const char *linked; int canjoin; - Ban *ban; - char bantmp[MAX_EB_LEN + 1]; - char *banchan; - char *banmask; + char *error = NULL; // User might already be on this channel, let's also exclude any possible services bots early if (IsULine(client) || find_membership_link(client->user->channel, channel)) @@ -361,12 +351,26 @@ int link_pre_localjoin_cb(Client *client, Channel *channel, char *parv[]) // only /INVITE from chanop bypasses: if (!is_invited(client, channel)) { - for(ban = channel->banlist; ban; ban = ban->next) + Ban *ban; + BanContext *b = safe_alloc(sizeof(BanContext)); + char bantmp[MAX_EB_LEN + 1]; + char *banchan; + char *banmask; + + b->client = client; + b->channel = channel; + b->ban_check_types = BANCHK_JOIN; + + for (ban = channel->banlist; ban; ban = ban->next) { if (!strncmp(ban->banstr, "~f:", 3)) { strlcpy(bantmp, ban->banstr + 3, sizeof(bantmp)); } else + if (!strncmp(ban->banstr, "~forward:", 9)) + { + strlcpy(bantmp, ban->banstr + 9, sizeof(bantmp)); + } else if (!strncmp(ban->banstr, "~t:", 3)) { /* A timed ban, but is it for us? Need to parse a little: @@ -376,6 +380,28 @@ int link_pre_localjoin_cb(Client *client, Channel *channel, char *parv[]) if (p && !strncmp(p, ":~f:", 4)) { strlcpy(bantmp, p + 4, sizeof(bantmp)); + } else + if (p && !strncmp(p, ":~forward:", 10)) + { + strlcpy(bantmp, p + 10, sizeof(bantmp)); + } else { + /* Not for us - some other ~t ban */ + continue; + } + } else + if (!strncmp(ban->banstr, "~time:", 6)) + { + /* A timed ban, but is it for us? Need to parse a little: + * ~t:dddd:~f:... + */ + char *p = strchr(ban->banstr + 6, ':'); + if (p && !strncmp(p, ":~f:", 4)) + { + strlcpy(bantmp, p + 4, sizeof(bantmp)); + } else + if (p && !strncmp(p, ":~forward:", 10)) + { + strlcpy(bantmp, p + 10, sizeof(bantmp)); } else { /* Not for us - some other ~t ban */ continue; @@ -391,26 +417,32 @@ int link_pre_localjoin_cb(Client *client, Channel *channel, char *parv[]) continue; *banmask++ = '\0'; - if (ban_check_mask(client, channel, banmask, BANCHK_JOIN, NULL, NULL, 0)) + b->banstr = banmask; + if (ban_check_mask(b)) + { + safe_free(b); return link_doforward(client, channel, banchan, LINKTYPE_BAN); + } } + + safe_free(b); } // Either +L is not set, or it is set but the parameter isn't stored somehow - if (!(channel->mode.extmode & EXTMODE_LINK) || !(linked = cm_getparameter(channel, 'L'))) + if (!(channel->mode.mode & EXTMODE_LINK) || !(linked = cm_getparameter(channel, 'L'))) return HOOK_CONTINUE; // can_join() actually returns 0 if we *can* join a channel, so we don't need to bother checking any further conditions - if (!(canjoin = can_join(client, channel, parv[2], parv))) + if (!(canjoin = can_join(client, channel, key, &error))) return HOOK_CONTINUE; // Oper only channel if (has_channel_mode(channel, 'O') && !IsOper(client)) return link_doforward(client, channel, linked, LINKTYPE_OPER); - // SSL/TLS connected users only + // TLS connected users only if (has_channel_mode(channel, 'z') && !IsSecureConnect(client)) - return link_doforward(client, channel, linked, LINKTYPE_SSL); + return link_doforward(client, channel, linked, LINKTYPE_SECURE); // Registered/identified users only if (has_channel_mode(channel, 'R') && !IsRegNick(client)) diff --git a/src/modules/chanmodes/moderated.c b/src/modules/chanmodes/moderated.c new file mode 100644 index 0000000..6a00d4a --- /dev/null +++ b/src/modules/chanmodes/moderated.c @@ -0,0 +1,117 @@ +/* + * Channel Mode +m + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + + +ModuleHeader MOD_HEADER + = { + "chanmodes/moderated", + "6.0", + "Channel Mode +m", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Global variables */ +Cmode_t EXTCMODE_MODERATED; + +/* Forward declarations */ +int moderated_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +const char *moderated_pre_local_part(Client *client, Channel *channel, const char *text); +int moderated_can_set_topic(Client *client, Channel *channel, const char *topic, const char **errmsg); + +/* Macros */ +#define IsModerated(channel) (channel->mode.mode & EXTCMODE_MODERATED) + +MOD_INIT() +{ + CmodeInfo req; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&req, 0, sizeof(req)); + req.paracount = 0; + req.letter = 'm'; + req.is_ok = extcmode_default_requirehalfop; + CmodeAdd(modinfo->handle, req, &EXTCMODE_MODERATED); + + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, moderated_can_send_to_channel); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, moderated_pre_local_part); + HookAdd(modinfo->handle, HOOKTYPE_CAN_SET_TOPIC, 0, moderated_can_set_topic); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int moderated_can_send_to_channel(Client *client, Channel *channel, Membership *m, const char **msg, const char **errmsg, SendType sendtype) +{ + if (IsModerated(channel) && (!m || !check_channel_access_membership(m, "vhoaq")) && + !op_can_override("channel:override:message:moderated",client,channel,NULL)) + { + Hook *h; + for (h = Hooks[HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION]; h; h = h->next) + { + int i = (*(h->func.intfunc))(client, channel, BYPASS_CHANMSG_MODERATED); + if (i == HOOK_ALLOW) + return HOOK_CONTINUE; /* bypass +m restriction */ + if (i != HOOK_CONTINUE) + break; + } + + *errmsg = "You need voice (+v)"; + return HOOK_DENY; /* BLOCK message */ + } + + return HOOK_CONTINUE; +} + +/** Remove PART reason too if the channel is +m, -t, and user not +vhoaq */ +const char *moderated_pre_local_part(Client *client, Channel *channel, const char *text) +{ + if (IsModerated(channel) && !check_channel_access(client, channel, "v") && !check_channel_access(client, channel, "h")) + return NULL; + return text; +} + +int moderated_can_set_topic(Client *client, Channel *channel, const char *topic, const char **errmsg) +{ + static char errmsg_buf[NICKLEN+256]; + + /* Channel is +m but user is not +vhoaq: reject the topic change */ + if (has_channel_mode(channel, 'm') && !check_channel_access(client, channel, "vhoaq")) + { + char buf[512]; + snprintf(buf, sizeof(buf), "Voice (+v) or higher is required in order to change the topic on %s (channel is +m)", channel->name); + buildnumeric(errmsg_buf, sizeof(errmsg_buf), client, ERR_CANNOTDOCOMMAND, "TOPIC", buf); + *errmsg = errmsg_buf; + return EX_DENY; + } + + return EX_ALLOW; +} diff --git a/src/modules/chanmodes/nocolor.c b/src/modules/chanmodes/nocolor.c index 6f2dab5..9544368 100644 --- a/src/modules/chanmodes/nocolor.c +++ b/src/modules/chanmodes/nocolor.c @@ -27,16 +27,16 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +c", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_NOCOLOR; -#define IsNoColor(channel) (channel->mode.extmode & EXTCMODE_NOCOLOR) +#define IsNoColor(channel) (channel->mode.mode & EXTCMODE_NOCOLOR) -int nocolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -char *nocolor_prelocalpart(Client *client, Channel *channel, char *comment); -char *nocolor_prelocalquit(Client *client, char *comment); +int nocolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +const char *nocolor_prelocalpart(Client *client, Channel *channel, const char *comment); +const char *nocolor_prelocalquit(Client *client, const char *comment); MOD_TEST() { @@ -50,14 +50,14 @@ CmodeInfo req; /* Channel mode */ memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'c'; + req.letter = 'c'; req.is_ok = extcmode_default_requirechop; CmodeAdd(modinfo->handle, req, &EXTCMODE_NOCOLOR); HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, nocolor_can_send_to_channel); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, nocolor_prelocalpart); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT_CHAN, 0, nocolor_prelocalpart); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, nocolor_prelocalquit); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, nocolor_prelocalpart); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT_CHAN, 0, nocolor_prelocalpart); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, nocolor_prelocalquit); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -73,7 +73,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static int IsUsingColor(char *s) +static int IsUsingColor(const char *s) { if (!s) return 0; @@ -85,7 +85,7 @@ static int IsUsingColor(char *s) return 0; } -int nocolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int nocolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { Hook *h; int i; @@ -108,7 +108,7 @@ int nocolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp return HOOK_CONTINUE; } -char *nocolor_prelocalpart(Client *client, Channel *channel, char *comment) +const char *nocolor_prelocalpart(Client *client, Channel *channel, const char *comment) { if (!comment) return NULL; @@ -130,7 +130,7 @@ static int IsAnyChannelNoColor(Client *client) return 0; } -char *nocolor_prelocalquit(Client *client, char *comment) +const char *nocolor_prelocalquit(Client *client, const char *comment) { if (!comment) return NULL; diff --git a/src/modules/chanmodes/noctcp.c b/src/modules/chanmodes/noctcp.c index 058f20a..3ce69db 100644 --- a/src/modules/chanmodes/noctcp.c +++ b/src/modules/chanmodes/noctcp.c @@ -27,14 +27,14 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +C", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_NOCTCP; -#define IsNoCTCP(channel) (channel->mode.extmode & EXTCMODE_NOCTCP) +#define IsNoCTCP(channel) (channel->mode.mode & EXTCMODE_NOCTCP) -int noctcp_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); +int noctcp_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); MOD_TEST() { @@ -47,7 +47,7 @@ CmodeInfo req; memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'C'; + req.letter = 'C'; req.is_ok = extcmode_default_requirehalfop; CmodeAdd(modinfo->handle, req, &EXTCMODE_NOCTCP); @@ -67,7 +67,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static int IsACTCP(char *s) +static int IsACTCP(const char *s) { if (!s) return 0; @@ -78,7 +78,7 @@ static int IsACTCP(char *s) return 0; } -int noctcp_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int noctcp_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { if (IsNoCTCP(channel) && IsACTCP(*msg)) { diff --git a/src/modules/chanmodes/noexternalmsgs.c b/src/modules/chanmodes/noexternalmsgs.c new file mode 100644 index 0000000..1de63ad --- /dev/null +++ b/src/modules/chanmodes/noexternalmsgs.c @@ -0,0 +1,89 @@ +/* + * Channel Mode +n + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + + +ModuleHeader MOD_HEADER + = { + "chanmodes/noexternalmsgs", + "6.0", + "Channel Mode +n", + "UnrealIRCd Team", + "unrealircd-6", + }; + +Cmode_t EXTCMODE_NO_EXTERNAL_MESSAGES; + +#define IsNoExternalMessages(channel) (channel->mode.mode & EXTCMODE_NO_EXTERNAL_MESSAGES) + +int noexternalmsgs_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); + +MOD_INIT() +{ + CmodeInfo req; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&req, 0, sizeof(req)); + req.paracount = 0; + req.letter = 'n'; + req.is_ok = extcmode_default_requirehalfop; + CmodeAdd(modinfo->handle, req, &EXTCMODE_NO_EXTERNAL_MESSAGES); + + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, noexternalmsgs_can_send_to_channel); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int noexternalmsgs_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) +{ + if (IsNoExternalMessages(channel) && !IsMember(client,channel)) + { + /* Channel does not accept external messages (+n). + * Reject, unless HOOKTYPE_CAN_BYPASS_NO_EXTERNAL_MSGS tells otherwise. + */ + Hook *h; + int i; + + for (h = Hooks[HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION]; h; h = h->next) + { + i = (*(h->func.intfunc))(client, channel, BYPASS_CHANMSG_EXTERNAL); + if (i == HOOK_ALLOW) + return HOOK_CONTINUE; /* bypass +n restriction */ + if (i != HOOK_CONTINUE) + break; + } + + *errmsg = "No external channel messages"; + return HOOK_DENY; /* BLOCK message */ + } + + return HOOK_CONTINUE; +} diff --git a/src/modules/chanmodes/noinvite.c b/src/modules/chanmodes/noinvite.c index d6874aa..2d81825 100644 --- a/src/modules/chanmodes/noinvite.c +++ b/src/modules/chanmodes/noinvite.c @@ -27,14 +27,14 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +V", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_NOINVITE; -#define IsNoInvite(channel) (channel->mode.extmode & EXTCMODE_NOINVITE) +#define IsNoInvite(channel) (channel->mode.mode & EXTCMODE_NOINVITE) -int noinvite_pre_knock(Client *client, Channel *channel); +int noinvite_pre_knock(Client *client, Channel *channel, const char **reason); int noinvite_pre_invite(Client *client, Client *target, Channel *channel, int *override); MOD_TEST() @@ -48,7 +48,7 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'V'; + req.letter = 'V'; req.is_ok = extcmode_default_requirechop; CmodeAdd(modinfo->handle, req, &EXTCMODE_NOINVITE); @@ -70,12 +70,12 @@ MOD_UNLOAD() } -int noinvite_pre_knock(Client *client, Channel *channel) +int noinvite_pre_knock(Client *client, Channel *channel, const char **reason) { if (MyUser(client) && IsNoInvite(channel)) { - sendnumeric(client, ERR_CANNOTKNOCK, - channel->chname, "The channel does not allow invites (+V)"); + sendnumeric(client, ERR_CANNOTKNOCK, channel->name, + "The channel does not allow invites (+V)"); return HOOK_DENY; } @@ -90,7 +90,7 @@ int noinvite_pre_invite(Client *client, Client *target, Channel *channel, int *o { *override = 1; } else { - sendnumeric(client, ERR_NOINVITE, channel->chname); + sendnumeric(client, ERR_NOINVITE, channel->name); return HOOK_DENY; } } diff --git a/src/modules/chanmodes/nokick.c b/src/modules/chanmodes/nokick.c index 899d7c9..56cb115 100644 --- a/src/modules/chanmodes/nokick.c +++ b/src/modules/chanmodes/nokick.c @@ -26,14 +26,14 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +Q", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_NOKICK; -#define IsNoKick(channel) (channel->mode.extmode & EXTCMODE_NOKICK) +#define IsNoKick(channel) (channel->mode.mode & EXTCMODE_NOKICK) -int nokick_check (Client *client, Client *who, Channel *channel, char *comment, long client_flags, long who_flags, char **reject_reason); +int nokick_check (Client *client, Client *target, Channel *channel, const char *comment, const char *client_member_modes, const char *target_member_modes, const char **reject_reason); MOD_TEST() { @@ -46,7 +46,7 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'Q'; + req.letter = 'Q'; req.is_ok = extcmode_default_requirechop; CmodeAdd(modinfo->handle, req, &EXTCMODE_NOKICK); @@ -67,7 +67,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int nokick_check (Client *client, Client *who, Channel *channel, char *comment, long client_flags, long who_flags, char **reject_reason) +int nokick_check (Client *client, Client *target, Channel *channel, const char *comment, const char *client_member_modes, const char *target_member_modes, const char **reject_reason) { static char errmsg[256]; diff --git a/src/modules/chanmodes/noknock.c b/src/modules/chanmodes/noknock.c index d5b36a4..ac5f1f6 100644 --- a/src/modules/chanmodes/noknock.c +++ b/src/modules/chanmodes/noknock.c @@ -25,15 +25,15 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +K", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_NOKNOCK; -#define IsNoKnock(channel) (channel->mode.extmode & EXTCMODE_NOKNOCK) +#define IsNoKnock(channel) (channel->mode.mode & EXTCMODE_NOKNOCK) -int noknock_check (Client *client, Channel *channel); -int noknock_mode_allow(Client *client, Channel *channel, char mode, char *para, int checkt, int what); +int noknock_check_knock(Client *client, Channel *channel, const char **reason); +int noknock_mode_allow(Client *client, Channel *channel, char mode, const char *para, int checkt, int what); int noknock_mode_del (Channel *channel, int modeChar); MOD_TEST() @@ -47,11 +47,11 @@ CmodeInfo req; memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'K'; + req.letter = 'K'; req.is_ok = noknock_mode_allow; CmodeAdd(modinfo->handle, req, &EXTCMODE_NOKNOCK); - HookAdd(modinfo->handle, HOOKTYPE_PRE_KNOCK, 0, noknock_check); + HookAdd(modinfo->handle, HOOKTYPE_PRE_KNOCK, 0, noknock_check_knock); HookAdd(modinfo->handle, HOOKTYPE_MODECHAR_DEL, 0, noknock_mode_del); @@ -70,11 +70,11 @@ MOD_UNLOAD() } -int noknock_check (Client *client, Channel *channel) +int noknock_check_knock (Client *client, Channel *channel, const char **reason) { if (MyUser(client) && IsNoKnock(channel)) { - sendnumeric(client, ERR_CANNOTKNOCK, channel->chname, "No knocks are allowed! (+K)"); + sendnumeric(client, ERR_CANNOTKNOCK, channel->name, "No knocks are allowed! (+K)"); return HOOK_DENY; } @@ -85,14 +85,14 @@ int noknock_mode_del (Channel *channel, int modeChar) { // Remove noknock when we're removing invite only if (modeChar == 'i') - channel->mode.extmode &= ~EXTCMODE_NOKNOCK; + channel->mode.mode &= ~EXTCMODE_NOKNOCK; return 0; } -int noknock_mode_allow(Client *client, Channel *channel, char mode, char *para, int checkt, int what) +int noknock_mode_allow(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) { - if (!(channel->mode.mode & MODE_INVITEONLY)) + if (!has_channel_mode(channel, 'i')) { if (checkt == EXCHK_ACCESS_ERR) { diff --git a/src/modules/chanmodes/nonickchange.c b/src/modules/chanmodes/nonickchange.c index 1d28e38..88a411c 100644 --- a/src/modules/chanmodes/nonickchange.c +++ b/src/modules/chanmodes/nonickchange.c @@ -26,12 +26,12 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +N", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_NONICKCHANGE; -#define IsNoNickChange(channel) (channel->mode.extmode & EXTCMODE_NONICKCHANGE) +#define IsNoNickChange(channel) (channel->mode.mode & EXTCMODE_NONICKCHANGE) int nonickchange_check (Client *client, Channel *channel); @@ -46,7 +46,7 @@ CmodeInfo req; memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'N'; + req.letter = 'N'; req.is_ok = extcmode_default_requirehalfop; CmodeAdd(modinfo->handle, req, &EXTCMODE_NONICKCHANGE); @@ -71,7 +71,7 @@ int nonickchange_check (Client *client, Channel *channel) { if (!IsOper(client) && !IsULine(client) && IsNoNickChange(channel) - && !is_chan_op(client, channel)) + && !check_channel_access(client, channel, "oaq")) { return HOOK_DENY; } diff --git a/src/modules/chanmodes/nonotice.c b/src/modules/chanmodes/nonotice.c index 0d60023..7c63b35 100644 --- a/src/modules/chanmodes/nonotice.c +++ b/src/modules/chanmodes/nonotice.c @@ -25,14 +25,14 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +T", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_NONOTICE; -#define IsNoNotice(channel) (channel->mode.extmode & EXTCMODE_NONOTICE) +#define IsNoNotice(channel) (channel->mode.mode & EXTCMODE_NONOTICE) -int nonotice_check_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); +int nonotice_check_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); MOD_TEST() { @@ -45,7 +45,7 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'T'; + req.letter = 'T'; req.is_ok = extcmode_default_requirechop; CmodeAdd(modinfo->handle, req, &EXTCMODE_NONOTICE); @@ -65,14 +65,14 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int nonotice_check_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int nonotice_check_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { Hook *h; int i; if ((sendtype == SEND_TYPE_NOTICE) && IsNoNotice(channel) && - (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_CHANOWNER | CHFL_CHANADMIN)))) + !check_channel_access_membership(lp, "oaq")) { for (h = Hooks[HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION]; h; h = h->next) { diff --git a/src/modules/chanmodes/operonly.c b/src/modules/chanmodes/operonly.c index 4b959c0..3c4b4f7 100644 --- a/src/modules/chanmodes/operonly.c +++ b/src/modules/chanmodes/operonly.c @@ -27,15 +27,15 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +O", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_OPERONLY; -int operonly_require_oper(Client *client, Channel *channel, char mode, char *para, int checkt, int what); -int operonly_check (Client *client, Channel *channel, char *key, char *parv[]); -int operonly_topic_allow (Client *client, Channel *channel); -int operonly_check_ban(Client *client, Channel *channel); +int operonly_require_oper(Client *client, Channel *channel, char mode, const char *para, int checkt, int what); +int operonly_can_join(Client *client, Channel *channel, const char *key, char **errmsg); +int operonly_view_topic_outside_channel(Client *client, Channel *channel); +int operonly_oper_invite_ban(Client *client, Channel *channel); MOD_TEST() { @@ -48,13 +48,13 @@ CmodeInfo req; memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'O'; + req.letter = 'O'; req.is_ok = operonly_require_oper; CmodeAdd(modinfo->handle, req, &EXTCMODE_OPERONLY); - HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, operonly_check); - HookAdd(modinfo->handle, HOOKTYPE_OPER_INVITE_BAN, 0, operonly_check_ban); - HookAdd(modinfo->handle, HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL, 0, operonly_topic_allow); + HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, operonly_can_join); + HookAdd(modinfo->handle, HOOKTYPE_OPER_INVITE_BAN, 0, operonly_oper_invite_ban); + HookAdd(modinfo->handle, HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL, 0, operonly_view_topic_outside_channel); MARK_AS_OFFICIAL_MODULE(modinfo); @@ -71,31 +71,34 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int operonly_check (Client *client, Channel *channel, char *key, char *parv[]) +int operonly_can_join(Client *client, Channel *channel, const char *key, char **errmsg) { - if ((channel->mode.extmode & EXTCMODE_OPERONLY) && !ValidatePermissionsForPath("channel:operonly:join",client,NULL,channel,NULL)) + if ((channel->mode.mode & EXTCMODE_OPERONLY) && !ValidatePermissionsForPath("channel:operonly:join",client,NULL,channel,NULL)) + { + *errmsg = STR_ERR_OPERONLY; return ERR_OPERONLY; + } return 0; } -int operonly_check_ban(Client *client, Channel *channel) +int operonly_oper_invite_ban(Client *client, Channel *channel) { - if ((channel->mode.extmode & EXTCMODE_OPERONLY) && + if ((channel->mode.mode & EXTCMODE_OPERONLY) && !ValidatePermissionsForPath("channel:operonly:ban",client,NULL,NULL,NULL)) return HOOK_DENY; return HOOK_CONTINUE; } -int operonly_topic_allow (Client *client, Channel *channel) +int operonly_view_topic_outside_channel(Client *client, Channel *channel) { - if (channel->mode.extmode & EXTCMODE_OPERONLY && !ValidatePermissionsForPath("channel:operonly:topic",client,NULL,channel,NULL)) + if (channel->mode.mode & EXTCMODE_OPERONLY && !ValidatePermissionsForPath("channel:operonly:topic",client,NULL,channel,NULL)) return HOOK_DENY; return HOOK_CONTINUE; } -int operonly_require_oper(Client *client, Channel *channel, char mode, char *para, int checkt, int what) +int operonly_require_oper(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) { if (!MyUser(client) || ValidatePermissionsForPath("channel:operonly:set",client,NULL,channel,NULL)) return EX_ALLOW; diff --git a/src/modules/chanmodes/permanent.c b/src/modules/chanmodes/permanent.c index bccc443..e52c9f7 100644 --- a/src/modules/chanmodes/permanent.c +++ b/src/modules/chanmodes/permanent.c @@ -25,20 +25,20 @@ ModuleHeader MOD_HEADER "4.2", "Permanent channel mode (+P)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; static Cmode_t EXTMODE_PERMANENT = 0L; static int permanent_channel_destroy(Channel *channel, int *should_destroy) { - if (channel->mode.extmode & EXTMODE_PERMANENT) + if (channel->mode.mode & EXTMODE_PERMANENT) *should_destroy = 0; return 0; } -static int permanent_is_ok(Client *client, Channel *channel, char mode, char *para, int checkt, int what) +static int permanent_is_ok(Client *client, Channel *channel, char mode, const char *para, int checkt, int what) { if (!IsOper(client)) { @@ -51,14 +51,17 @@ static int permanent_is_ok(Client *client, Channel *channel, char mode, char *pa return EX_ALLOW; } -int permanent_chanmode(Client *client, Channel *channel, MessageTag *mtags, char *modebuf, char *parabuf, time_t sendts, int samode) +int permanent_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel) { if (samode == -1) return 0; /* SJOIN server-sync, already has its own way of destroying the channel */ /* Destroy the channel if it was set '(SA)MODE #chan -P' with nobody in it (#4442) */ - if (!(channel->mode.extmode & EXTMODE_PERMANENT) && (channel->users <= 0)) + if (!(channel->mode.mode & EXTMODE_PERMANENT) && (channel->users <= 0)) + { sub1_from_channel(channel); + *destroy_channel = 1; + } return 0; } @@ -72,7 +75,7 @@ CmodeInfo req; memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'P'; + req.letter = 'P'; req.is_ok = permanent_is_ok; CmodeAdd(modinfo->handle, req, &EXTMODE_PERMANENT); diff --git a/src/modules/snomasks/dccreject.c b/src/modules/chanmodes/private.c similarity index 52% rename from src/modules/snomasks/dccreject.c rename to src/modules/chanmodes/private.c index 50bccdd..d292050 100644 --- a/src/modules/snomasks/dccreject.c +++ b/src/modules/chanmodes/private.c @@ -1,6 +1,6 @@ /* - * Show DCC SEND rejection notices (Snomask +D) - * (C) Copyright 2000-.. Bram Matthys (Syzop) and the UnrealIRCd team + * Channel Mode +p + * (C) Copyright 2021 Syzop and the UnrealIRCd team * * 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 @@ -19,34 +19,36 @@ #include "unrealircd.h" -/* Module header */ + ModuleHeader MOD_HEADER = { - "snomasks/dccreject", - "4.2", - "Snomask +D", + "chanmodes/private", + "6.0", + "Channel Mode +p", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -/* Global variables */ -long SNO_DCCREJECT = 0L; +Cmode_t EXTCMODE_PRIVATE; -/* Forward declarations */ -int dccreject_dcc_denied(Client *client, char *target, char *realfile, char *displayfile, ConfigItem_deny_dcc *dccdeny); +#define IsPrivate(channel) (channel->mode.mode & EXTCMODE_PRIVATE) -MOD_TEST() -{ - return MOD_SUCCESS; -} +int private_modechar_add(Channel *channel, int modechar); MOD_INIT() { - SnomaskAdd(modinfo->handle, 'D', umode_allow_opers, &SNO_DCCREJECT); - - HookAdd(modinfo->handle, HOOKTYPE_DCC_DENIED, 0, dccreject_dcc_denied); - + CmodeInfo req; + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&req, 0, sizeof(req)); + req.paracount = 0; + req.letter = 'p'; + req.is_ok = extcmode_default_requirehalfop; + CmodeAdd(modinfo->handle, req, &EXTCMODE_PRIVATE); + + HookAdd(modinfo->handle, HOOKTYPE_MODECHAR_ADD, 0, private_modechar_add); + return MOD_SUCCESS; } @@ -60,11 +62,12 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int dccreject_dcc_denied(Client *client, char *target, char *realfile, char *displayfile, ConfigItem_deny_dcc *dccdeny) +/** This clears channel mode +p when +s gets set */ +int private_modechar_add(Channel *channel, int modechar) { - sendto_snomask_global(SNO_DCCREJECT, - "%s tried to send forbidden file %s (%s) to %s (is blocked now)", - client->name, displayfile, dccdeny->reason, target); - + if (modechar == 's') + { + channel->mode.mode &= ~EXTCMODE_PRIVATE; + } return 0; } diff --git a/src/modules/chanmodes/regonly.c b/src/modules/chanmodes/regonly.c index 8b1b559..b35100c 100644 --- a/src/modules/chanmodes/regonly.c +++ b/src/modules/chanmodes/regonly.c @@ -26,14 +26,14 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +R", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_REGONLY; -#define IsRegOnly(channel) (channel->mode.extmode & EXTCMODE_REGONLY) +#define IsRegOnly(channel) (channel->mode.mode & EXTCMODE_REGONLY) -int regonly_check (Client *client, Channel *channel, char *key, char *parv[]); +int regonly_check(Client *client, Channel *channel, const char *key, char **errmsg); MOD_TEST() @@ -47,7 +47,7 @@ CmodeInfo req; memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'R'; + req.letter = 'R'; req.is_ok = extcmode_default_requirehalfop; CmodeAdd(modinfo->handle, req, &EXTCMODE_REGONLY); @@ -68,10 +68,13 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int regonly_check (Client *client, Channel *channel, char *key, char *parv[]) +int regonly_check (Client *client, Channel *channel, const char *key, char **errmsg) { if (IsRegOnly(channel) && !IsLoggedIn(client)) + { + *errmsg = STR_ERR_NEEDREGGEDNICK; return ERR_NEEDREGGEDNICK; + } return 0; } diff --git a/src/modules/chanmodes/regonlyspeak.c b/src/modules/chanmodes/regonlyspeak.c index 33e2c5b..656dd06 100644 --- a/src/modules/chanmodes/regonlyspeak.c +++ b/src/modules/chanmodes/regonlyspeak.c @@ -26,16 +26,16 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +M", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_REGONLYSPEAK; static char errMsg[2048]; -#define IsRegOnlySpeak(channel) (channel->mode.extmode & EXTCMODE_REGONLYSPEAK) +#define IsRegOnlySpeak(channel) (channel->mode.mode & EXTCMODE_REGONLYSPEAK) -int regonlyspeak_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -char *regonlyspeak_part_message (Client *client, Channel *channel, char *comment); +int regonlyspeak_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +const char *regonlyspeak_part_message (Client *client, Channel *channel, const char *comment); MOD_TEST() { @@ -48,12 +48,12 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'M'; + req.letter = 'M'; req.is_ok = extcmode_default_requirehalfop; CmodeAdd(modinfo->handle, req, &EXTCMODE_REGONLYSPEAK); HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, regonlyspeak_can_send_to_channel); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, regonlyspeak_part_message); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, regonlyspeak_part_message); MARK_AS_OFFICIAL_MODULE(modinfo); @@ -70,7 +70,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -char *regonlyspeak_part_message (Client *client, Channel *channel, char *comment) +const char *regonlyspeak_part_message (Client *client, Channel *channel, const char *comment) { if (!comment) return NULL; @@ -81,15 +81,15 @@ char *regonlyspeak_part_message (Client *client, Channel *channel, char *comment return comment; } -int regonlyspeak_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int regonlyspeak_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { Hook *h; int i; - if (IsRegOnlySpeak(channel) && !op_can_override("channel:override:message:regonlyspeak",client,channel,NULL) && !IsLoggedIn(client) && - (!lp - || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER | - CHFL_HALFOP | CHFL_CHANADMIN)))) + if (IsRegOnlySpeak(channel) && + !op_can_override("channel:override:message:regonlyspeak",client,channel,NULL) && + !IsLoggedIn(client) && + !check_channel_access_membership(lp, "vhoaq")) { for (h = Hooks[HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION]; h; h = h->next) { diff --git a/src/modules/chanmodes/secret.c b/src/modules/chanmodes/secret.c new file mode 100644 index 0000000..0487a29 --- /dev/null +++ b/src/modules/chanmodes/secret.c @@ -0,0 +1,73 @@ +/* + * Channel Mode +s + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + + +ModuleHeader MOD_HEADER + = { + "chanmodes/secret", + "6.0", + "Channel Mode +s", + "UnrealIRCd Team", + "unrealircd-6", + }; + +Cmode_t EXTCMODE_SECRET; + +#define IsSecret(channel) (channel->mode.mode & EXTCMODE_SECRET) + +int secret_modechar_add(Channel *channel, int modechar); + +MOD_INIT() +{ + CmodeInfo req; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&req, 0, sizeof(req)); + req.paracount = 0; + req.letter = 's'; + req.is_ok = extcmode_default_requirehalfop; + CmodeAdd(modinfo->handle, req, &EXTCMODE_SECRET); + + HookAdd(modinfo->handle, HOOKTYPE_MODECHAR_ADD, 0, secret_modechar_add); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +/** This clears channel mode +s when +p gets set */ +int secret_modechar_add(Channel *channel, int modechar) +{ + if (modechar == 'p') + { + channel->mode.mode &= ~EXTCMODE_SECRET; + } + return 0; +} diff --git a/src/modules/chanmodes/secureonly.c b/src/modules/chanmodes/secureonly.c index 4bd3dcb..084997b 100644 --- a/src/modules/chanmodes/secureonly.c +++ b/src/modules/chanmodes/secureonly.c @@ -25,18 +25,18 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +z", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_SECUREONLY; -#define IsSecureOnly(channel) (channel->mode.extmode & EXTCMODE_SECUREONLY) +#define IsSecureOnly(channel) (channel->mode.mode & EXTCMODE_SECUREONLY) -int secureonly_check_join(Client *client, Channel *channel, char *key, char *parv[]); +int secureonly_check_join(Client *client, Channel *channel, const char *key, char **errmsg); int secureonly_channel_sync (Channel *channel, int merge, int removetheirs, int nomode); int secureonly_check_secure(Channel *channel); int secureonly_check_sajoin(Client *target, Channel *channel, Client *requester); -int secureonly_specialcheck(Client *client, Channel *channel, char *parv[]); +int secureonly_pre_local_join(Client *client, Channel *channel, const char *key); MOD_TEST() { @@ -49,11 +49,11 @@ MOD_INIT() memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'z'; + req.letter = 'z'; req.is_ok = extcmode_default_requirechop; CmodeAdd(modinfo->handle, req, &EXTCMODE_SECUREONLY); - HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_JOIN, 0, secureonly_specialcheck); + HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_JOIN, 0, secureonly_pre_local_join); HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, secureonly_check_join); HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_SYNCED, 0, secureonly_channel_sync); HookAdd(modinfo->handle, HOOKTYPE_IS_CHANNEL_SECURE, 0, secureonly_check_secure); @@ -95,39 +95,39 @@ static int secureonly_kick_insecure_users(Channel *channel) client = member->client; if (MyUser(client) && !IsSecureConnect(client) && !IsULine(client)) { - int prefix = 0; + char *prefix = NULL; MessageTag *mtags = NULL; if (invisible_user_in_channel(client, channel)) { /* Send only to chanops */ - prefix = CHFL_HALFOP|CHFL_CHANOP|CHFL_CHANOWNER|CHFL_CHANADMIN; + prefix = "ho"; } new_message(&me, NULL, &mtags); - RunHook6(HOOKTYPE_LOCAL_KICK, &me, &me, client, channel, mtags, comment); + RunHook(HOOKTYPE_LOCAL_KICK, &me, &me, client, channel, mtags, comment); sendto_channel(channel, &me, client, prefix, 0, SEND_LOCAL, mtags, ":%s KICK %s %s :%s", - me.name, channel->chname, client->name, comment); + me.name, channel->name, client->name, comment); - sendto_prefix_one(client, &me, mtags, ":%s KICK %s %s :%s", me.name, channel->chname, client->name, comment); + sendto_prefix_one(client, &me, mtags, ":%s KICK %s %s :%s", me.name, channel->name, client->name, comment); - sendto_server(NULL, 0, 0, mtags, ":%s KICK %s %s :%s", me.id, channel->chname, client->id, comment); + sendto_server(NULL, 0, 0, mtags, ":%s KICK %s %s :%s", me.id, channel->name, client->id, comment); free_message_tags(mtags); - if (remove_user_from_channel(client, channel) == 1) + if (remove_user_from_channel(client, channel, 0) == 1) return 1; /* channel was destroyed */ } } return 0; } -int secureonly_check_join(Client *client, Channel *channel, char *key, char *parv[]) +int secureonly_check_join(Client *client, Channel *channel, const char *key, char **errmsg) { Link *lp; @@ -141,6 +141,7 @@ int secureonly_check_join(Client *client, Channel *channel, char *key, char *par if (is_invited(client, channel)) return HOOK_CONTINUE; } + *errmsg = STR_ERR_SECUREONLYCHAN; return ERR_SECUREONLYCHAN; } return 0; @@ -167,8 +168,8 @@ int secureonly_check_sajoin(Client *target, Channel *channel, Client *requester) { if (IsSecureOnly(channel) && !IsSecure(target)) { - sendnotice(requester, "You cannot SAJOIN %s to %s because the channel is +z and the user is not connected via SSL/TLS", - target->name, channel->chname); + sendnotice(requester, "You cannot SAJOIN %s to %s because the channel is +z and the user is not connected via TLS", + target->name, channel->name); return HOOK_DENY; } @@ -178,11 +179,11 @@ int secureonly_check_sajoin(Client *target, Channel *channel, Client *requester) /* Special check for +z in set::modes-on-join. Needs to be done early. * Perhaps one day this will be properly handled in the core so this can be removed. */ -int secureonly_specialcheck(Client *client, Channel *channel, char *parv[]) +int secureonly_pre_local_join(Client *client, Channel *channel, const char *key) { - if ((channel->users == 0) && (iConf.modes_on_join.extmodes & EXTCMODE_SECUREONLY) && !IsSecure(client) && !IsOper(client)) + if ((channel->users == 0) && (MODES_ON_JOIN & EXTCMODE_SECUREONLY) && !IsSecure(client) && !IsOper(client)) { - sendnumeric(client, ERR_SECUREONLYCHAN, channel->chname); + sendnumeric(client, ERR_SECUREONLYCHAN, channel->name); return HOOK_DENY; } return HOOK_CONTINUE; diff --git a/src/modules/chanmodes/stripcolor.c b/src/modules/chanmodes/stripcolor.c index 11f9f31..c172381 100644 --- a/src/modules/chanmodes/stripcolor.c +++ b/src/modules/chanmodes/stripcolor.c @@ -27,16 +27,16 @@ ModuleHeader MOD_HEADER "4.2", "Channel Mode +S", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; Cmode_t EXTCMODE_STRIPCOLOR; -#define IsStripColor(channel) (channel->mode.extmode & EXTCMODE_STRIPCOLOR) +#define IsStripColor(channel) (channel->mode.mode & EXTCMODE_STRIPCOLOR) -int stripcolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -char *stripcolor_prelocalpart(Client *client, Channel *channel, char *comment); -char *stripcolor_prelocalquit(Client *client, char *comment); +int stripcolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +const char *stripcolor_prelocalpart(Client *client, Channel *channel, const char *comment); +const char *stripcolor_prelocalquit(Client *client, const char *comment); MOD_TEST() { @@ -50,14 +50,14 @@ CmodeInfo req; /* Channel mode */ memset(&req, 0, sizeof(req)); req.paracount = 0; - req.flag = 'S'; + req.letter = 'S'; req.is_ok = extcmode_default_requirechop; CmodeAdd(modinfo->handle, req, &EXTCMODE_STRIPCOLOR); HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, stripcolor_can_send_to_channel); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, stripcolor_prelocalpart); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT_CHAN, 0, stripcolor_prelocalpart); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, stripcolor_prelocalquit); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_PART, 0, stripcolor_prelocalpart); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT_CHAN, 0, stripcolor_prelocalpart); + HookAddConstString(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, stripcolor_prelocalquit); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -73,7 +73,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int stripcolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int stripcolor_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { Hook *h; int i; @@ -95,7 +95,7 @@ int stripcolor_can_send_to_channel(Client *client, Channel *channel, Membership return HOOK_CONTINUE; } -char *stripcolor_prelocalpart(Client *client, Channel *channel, char *comment) +const char *stripcolor_prelocalpart(Client *client, Channel *channel, const char *comment) { if (!comment) return NULL; @@ -118,7 +118,7 @@ static int IsAnyChannelStripColor(Client *client) } -char *stripcolor_prelocalquit(Client *client, char *comment) +const char *stripcolor_prelocalquit(Client *client, const char *comment) { if (!comment) return NULL; diff --git a/src/modules/chanmodes/topiclimit.c b/src/modules/chanmodes/topiclimit.c new file mode 100644 index 0000000..f16583a --- /dev/null +++ b/src/modules/chanmodes/topiclimit.c @@ -0,0 +1,82 @@ +/* + * Channel Mode +t + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + + +ModuleHeader MOD_HEADER + = { + "chanmodes/topiclimit", + "6.0", + "Channel Mode +t", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Global variables */ +Cmode_t EXTCMODE_TOPIC_LIMIT; + +/* Forward declarations */ +int topiclimit_can_set_topic(Client *client, Channel *channel, const char *topic, const char **errmsg); + +/* Macros */ +#define IsTopicLimit(channel) (channel->mode.mode & EXTCMODE_TOPIC_LIMIT) + +MOD_INIT() +{ + CmodeInfo req; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&req, 0, sizeof(req)); + req.paracount = 0; + req.letter = 't'; + req.is_ok = extcmode_default_requirehalfop; + CmodeAdd(modinfo->handle, req, &EXTCMODE_TOPIC_LIMIT); + + HookAdd(modinfo->handle, HOOKTYPE_CAN_SET_TOPIC, 0, topiclimit_can_set_topic); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int topiclimit_can_set_topic(Client *client, Channel *channel, const char *topic, const char **errmsg) +{ + static char errmsg_buf[NICKLEN+256]; + + if (has_channel_mode(channel, 't') && + !check_channel_access(client, channel, "hoaq") && + !IsULine(client) && + !IsServer(client)) + { + buildnumeric(errmsg_buf, sizeof(errmsg_buf), client, ERR_CHANOPRIVSNEEDED, channel->name); + *errmsg = errmsg_buf; + return EX_DENY; + } + + return EX_ALLOW; +} diff --git a/src/modules/chanmodes/voice.c b/src/modules/chanmodes/voice.c new file mode 100644 index 0000000..7233c53 --- /dev/null +++ b/src/modules/chanmodes/voice.c @@ -0,0 +1,88 @@ +/* + * Channel Mode +v + * (C) Copyright 2021 Syzop and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "chanmodes/voice", + "6.0", + "Channel Mode +v", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +int cmode_voice_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); + +MOD_INIT() +{ + CmodeInfo creq; + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&creq, 0, sizeof(creq)); + creq.paracount = 1; + creq.is_ok = cmode_voice_is_ok; + creq.letter = 'v'; + creq.prefix = '+'; + creq.sjoin_prefix = '+'; + creq.rank = RANK_VOICE; + creq.unset_with_param = 1; + creq.type = CMODE_MEMBER; + CmodeAdd(modinfo->handle, creq, NULL); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int cmode_voice_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) +{ + if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) + { + Client *target = find_user(param, NULL); + + if ((what == MODE_DEL) && (target == client)) + { + /* User may always remove their own modes */ + return EX_ALLOW; + } + if (check_channel_access(client, channel, "hoaq")) + { + /* Permitted for +hoaq */ + return EX_ALLOW; + } + if (type == EXCHK_ACCESS_ERR) + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); + return EX_DENY; + } + + /* fallthrough -- should not be used */ + return EX_DENY; +} diff --git a/src/modules/channeldb.c b/src/modules/channeldb.c index 7df6ddf..9c4f0c2 100644 --- a/src/modules/channeldb.c +++ b/src/modules/channeldb.c @@ -11,7 +11,7 @@ ModuleHeader MOD_HEADER = { "1.0", "Stores and retrieves channel settings for persistent (+P) channels", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Database version */ @@ -27,15 +27,14 @@ ModuleHeader MOD_HEADER = { #define MAGIC_CHANNEL_START 0x11111111 #define MAGIC_CHANNEL_END 0x22222222 -#ifdef DEBUGMODE - #define BENCHMARK -#endif +// #undef BENCHMARK #define WARN_WRITE_ERROR(fname) \ do { \ - sendto_realops_and_log("[channeldb] Error writing to temporary database file " \ - "'%s': %s (DATABASE NOT SAVED)", \ - fname, unrealdb_get_error_string()); \ + unreal_log(ULOG_ERROR, "channeldb", "CHANNELDB_FILE_WRITE_ERROR", NULL, \ + "[channeldb] Error writing to temporary database file $filename: $system_error", \ + log_data_string("filename", fname), \ + log_data_string("system_error", unrealdb_get_error_string())); \ } while(0) #define W_SAFE(x) \ @@ -72,7 +71,6 @@ EVENT(write_channeldb_evt); int write_channeldb(void); int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel); int read_channeldb(void); -static void set_channel_mode(Channel *channel, char *modes, char *parameters); /* Global variables */ static uint32_t channeldb_version = CHANNELDB_VERSION; @@ -132,7 +130,7 @@ MOD_LOAD() MOD_UNLOAD() { - if (loop.ircd_terminating) + if (loop.terminating) write_channeldb(); freecfg(&test); freecfg(&cfg); @@ -168,34 +166,34 @@ int channeldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type != CONFIG_SET) return 0; - if (!ce || strcmp(ce->ce_varname, "channeldb")) + if (!ce || strcmp(ce->name, "channeldb")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata) + if (!cep->value) { - config_error("%s:%i: blank set::channeldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: blank set::channeldb::%s without value", cep->file->filename, cep->line_number, cep->name); errors++; } else - if (!strcmp(cep->ce_varname, "database")) + if (!strcmp(cep->name, "database")) { - convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); - safe_strdup(test.database, cep->ce_vardata); + convert_to_absolute_path(&cep->value, PERMDATADIR); + safe_strdup(test.database, cep->value); } else - if (!strcmp(cep->ce_varname, "db-secret")) + if (!strcmp(cep->name, "db-secret")) { - char *err; - if ((err = unrealdb_test_secret(cep->ce_vardata))) + const char *err; + if ((err = unrealdb_test_secret(cep->value))) { - config_error("%s:%i: set::channeldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err); + config_error("%s:%i: set::channeldb::db-secret: %s", cep->file->filename, cep->line_number, err); errors++; continue; } - safe_strdup(test.db_secret, cep->ce_vardata); + safe_strdup(test.db_secret, cep->value); } else { - config_error("%s:%i: unknown directive set::channeldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: unknown directive set::channeldb::%s", cep->file->filename, cep->line_number, cep->name); errors++; } } @@ -227,15 +225,15 @@ int channeldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type) if (type != CONFIG_SET) return 0; - if (!ce || strcmp(ce->ce_varname, "channeldb")) + if (!ce || strcmp(ce->name, "channeldb")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "database")) - safe_strdup(cfg.database, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "db-secret")) - safe_strdup(cfg.db_secret, cep->ce_vardata); + if (!strcmp(cep->name, "database")) + safe_strdup(cfg.database, cep->value); + else if (!strcmp(cep->name, "db-secret")) + safe_strdup(cfg.db_secret, cep->value); } return 1; } @@ -300,7 +298,7 @@ int write_channeldb(void) #endif if (rename(tmpfname, cfg.database) < 0) { - sendto_realops_and_log("[channeldb] Error renaming '%s' to '%s': %s (DATABASE NOT SAVED)", tmpfname, cfg.database, strerror(errno)); + config_error("[channeldb] Error renaming '%s' to '%s': %s (DATABASE NOT SAVED)", tmpfname, cfg.database, strerror(errno)); return 0; } #ifdef BENCHMARK @@ -333,9 +331,11 @@ int write_listmode(UnrealDB *db, const char *tmpfname, Ban *lst) int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel) { + char modebuf[BUFSIZE], parabuf[BUFSIZE]; + W_SAFE(unrealdb_write_int32(db, MAGIC_CHANNEL_START)); /* Channel name */ - W_SAFE(unrealdb_write_str(db, channel->chname)); + W_SAFE(unrealdb_write_str(db, channel->name)); /* Channel creation time */ W_SAFE(unrealdb_write_int64(db, channel->creationtime)); /* Topic (topic, setby, seton) */ @@ -343,7 +343,7 @@ int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel) W_SAFE(unrealdb_write_str(db, channel->topic_nick)); W_SAFE(unrealdb_write_int64(db, channel->topic_time)); /* Basic channel modes (eg: +sntkl key 55) */ - channel_modes(&me, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel); + channel_modes(&me, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel, 1); W_SAFE(unrealdb_write_str(db, modebuf)); W_SAFE(unrealdb_write_str(db, parabuf)); /* Mode lock */ @@ -506,8 +506,11 @@ int read_channeldb(void) R_SAFE(unrealdb_read_str(db, &modes2)); R_SAFE(unrealdb_read_str(db, &mode_lock)); /* If we got this far, we can create/initialize the channel with the above */ - channel = get_channel(&me, chname, CREATE); - channel->creationtime = creationtime; + channel = make_channel(chname); + if (IsInvalidChannelTS(creationtime)) + channel->creationtime = TStime(); + else + channel->creationtime = creationtime; safe_strdup(channel->topic, topic); safe_strdup(channel->topic_nick, topic_nick); channel->topic_time = topic_time; @@ -529,37 +532,14 @@ int read_channeldb(void) unrealdb_close(db); if (added) - sendto_realops_and_log("[channeldb] Added %d persistent channels (+P)", added); + config_status("[channeldb] Added %d persistent channels (+P)", added); #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "[channeldb] Benchmark: LOAD DB: %ld microseconds", - ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)); + unreal_log(ULOG_DEBUG, "channeldb", "CHANNELDB_BENCHMARK", NULL, + "[channeldb] Benchmark: LOAD DB: $time_msec microseconds", + log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); #endif return 1; } #undef FreeChannelEntry #undef R_SAFE - -static void set_channel_mode(Channel *channel, char *modes, char *parameters) -{ - char buf[512]; - char *p, *param; - int myparc = 1, i; - char *myparv[64]; - - memset(&myparv, 0, sizeof(myparv)); - myparv[0] = raw_strdup(modes); - - strlcpy(buf, parameters, sizeof(buf)); - for (param = strtoken(&p, buf, " "); param; param = strtoken(&p, NULL, " ")) - myparv[myparc++] = raw_strdup(param); - myparv[myparc] = NULL; - - SetULine(&me); // hack for crash.. set ulined so no access checks. - do_mode(channel, &me, NULL, myparc, myparv, 0, 0); - ClearULine(&me); // and clear it again.. - - for (i = 0; i < myparc; i++) - safe_free(myparv[i]); -} -// FIXME: move above function to m_mode and make efunc, available for all modules anyway diff --git a/src/modules/charsys.c b/src/modules/charsys.c index f0b1430..e20e13c 100644 --- a/src/modules/charsys.c +++ b/src/modules/charsys.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "Character System (set::allowed-nickchars)", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* NOTE: it is guaranteed that char is unsigned by compiling options @@ -73,6 +73,7 @@ char langsinuse[4096]; #define LANGAV_CYRILLIC_UTF8 0x008000 /* UTF8: cyrillic script */ #define LANGAV_GREEK_UTF8 0x010000 /* UTF8: greek script */ #define LANGAV_HEBREW_UTF8 0x020000 /* UTF8: hebrew script */ +#define LANGAV_ARABIC_UTF8 0x040000 /* UTF8: arabic script */ typedef struct LangList LangList; struct LangList { @@ -83,7 +84,7 @@ struct LangList /* MUST be alphabetized (first column) */ static LangList langlist[] = { -/* { "arabic", "ara", LANGAV_ASCII|LANGAV_ISO8859_6 }, -- TODO: check if this has issues first! */ + { "arabic-utf8", "ara-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_ARABIC_UTF8 }, { "belarussian-utf8", "blr-utf8", LANGAV_ASCII|LANGAV_UTF8|LANGAV_CYRILLIC_UTF8 }, { "belarussian-w1251", "blr", LANGAV_ASCII|LANGAV_W1251 }, { "catalan", "cat", LANGAV_ASCII|LANGAV_LATIN1 }, @@ -189,7 +190,7 @@ MOD_TEST() MARK_AS_OFFICIAL_MODULE(modinfo); EfunctionAdd(modinfo->handle, EFUNC_DO_NICK_NAME, _do_nick_name); EfunctionAdd(modinfo->handle, EFUNC_DO_REMOTE_NICK_NAME, _do_remote_nick_name); - EfunctionAddPChar(modinfo->handle, EFUNC_CHARSYS_GET_CURRENT_LANGUAGES, _charsys_get_current_languages); + EfunctionAddString(modinfo->handle, EFUNC_CHARSYS_GET_CURRENT_LANGUAGES, _charsys_get_current_languages); charsys_reset(); charsys_reset_pretest(); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, charsys_config_test); @@ -226,26 +227,26 @@ int charsys_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; /* We are only interrested in set::allowed-nickchars... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "allowed-nickchars")) + if (!ce || !ce->name || strcmp(ce->name, "allowed-nickchars")) return 0; - if (ce->ce_vardata) + if (ce->value) { config_error("%s:%i: set::allowed-nickchars: please use 'allowed-nickchars { name; };' " "and not 'allowed-nickchars name;'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); /* Give up immediately. Don't bother the user with any other errors. */ errors++; *errs = errors; return -1; } - for (cep = ce->ce_entries; cep; cep=cep->ce_next) + for (cep = ce->items; cep; cep=cep->next) { - if (!charsys_test_language(cep->ce_varname)) + if (!charsys_test_language(cep->name)) { config_error("%s:%i: set::allowed-nickchars: Unknown (sub)language '%s'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, cep->ce_varname); + ce->file->filename, ce->line_number, cep->name); errors++; } } @@ -262,11 +263,11 @@ int charsys_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::allowed-nickchars... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "allowed-nickchars")) + if (!ce || !ce->name || strcmp(ce->name, "allowed-nickchars")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - charsys_add_language(cep->ce_varname); + for (cep = ce->items; cep; cep = cep->next) + charsys_add_language(cep->name); return 1; } @@ -313,6 +314,8 @@ int charsys_config_posttest(int *errs) x++; if (x > 1) { +#if 0 +// I don't think this should be hard error, right? Some combinations may be problematic, but not all. if (langav & LANGAV_LATIN_UTF8) { config_error("ERROR: set::allowed-nickchars: you cannot combine 'latin-utf8' with any other character set"); @@ -333,8 +336,13 @@ int charsys_config_posttest(int *errs) config_error("ERROR: set::allowed-nickchars: you cannot combine 'hebrew-utf8' with any other character set"); errors++; } - config_status("WARNING: set::allowed-nickchars: " - "Mixing of charsets (eg: latin1+latin2) can cause display problems"); + if (langav & LANGAV_ARABIC_UTF8) + { + config_error("ERROR: set::allowed-nickchars: you cannot combine 'arabic-utf8' with any other character set"); + errors++; + } +#endif + config_status("WARNING: set::allowed-nickchars: Mixing of charsets (eg: latin1+latin2) may cause display problems"); } *errs = errors; @@ -877,13 +885,6 @@ void charsys_add_language(char *name) charsys_addmultibyterange(0xc3, 0xc3, 0xba, 0xba); charsys_addmultibyterange(0xc3, 0xc3, 0xbd, 0xbe); } -/* if (latin1 || !strcmp(name, "arabic")) -- Since when is arabic considered latin(1)??? oh man... - { - char bytes[] = { 0xa0, 0xa4, 0xac, 0xad, 0xbb, 0xbf, 0x00 }; - charsys_addallowed(bytes); - charsys_addallowed_range(0xc1, 0xda); - charsys_addallowed_range(0xe0, 0xf2); - } */ /* [LATIN2] and rest of [LATIN-UTF8] */ /* actually hungarian is a special case, include it in both w1250 and latin2 ;p */ @@ -1181,6 +1182,19 @@ void charsys_add_language(char *name) charsys_addmultibyterange(0xc5, 0xc5, 0xaa, 0xab); charsys_addmultibyterange(0xc5, 0xc5, 0xbd, 0xbe); } + + /* [ARABIC] */ + if (latin_utf8 || !strcmp(name, "arabic-utf8")) + { + /* Supplied by Sensiva */ + /*charsys_addallowed("اأإآءبتثجحخدذرزسشصضطظعغفقكلمنهؤةويىئ");*/ + /*- From U+0621 to U+063A (Regex: [\u0621-\u063A])*/ + /* 0xd8a1 - 0xd8ba */ + charsys_addmultibyterange(0xd8, 0xd8, 0xa1, 0xba); + /*- From U+0641 to U+064A (Regex: [\u0641-\u064A])*/ + /* 0xd981 - 0xd98a */ + charsys_addmultibyterange(0xd9, 0xd9, 0x81, 0x8a); + } } /** This displays all the nick characters that are permitted */ @@ -1250,6 +1264,8 @@ char *charsys_group(int v) return "Greek script"; if (v & LANGAV_HEBREW_UTF8) return "Hebrew script"; + if (v & LANGAV_ARABIC_UTF8) + return "Arabic script"; return "Other"; } diff --git a/src/modules/chathistory.c b/src/modules/chathistory.c index dba2d5f..d13c32d 100644 --- a/src/modules/chathistory.c +++ b/src/modules/chathistory.c @@ -13,7 +13,15 @@ ModuleHeader MOD_HEADER "1.0", "IRCv3 CHATHISTORY command", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", +}; + +/* Structs */ +typedef struct ChatHistoryTarget ChatHistoryTarget; +struct ChatHistoryTarget { + ChatHistoryTarget *prev, *next; + char *datetime; + char *object; }; /* Forward declarations */ @@ -22,7 +30,6 @@ CMD_FUNC(cmd_chathistory); /* Global variables */ long CAP_CHATHISTORY = 0L; -/* TODO: consider moving to config file */ #define CHATHISTORY_LIMIT 50 MOD_INIT() @@ -49,13 +56,18 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int chathistory_token(char *str, char *token, char **store) +int chathistory_token(const char *str, char *token, char **store) { - char *p = strchr(str, '='); + char request[BUFSIZE]; + char *p; + + strlcpy(request, str, sizeof(request)); + + p = strchr(request, '='); if (!p) return 0; *p = '\0'; // frag - if (!strcmp(str, token)) + if (!strcmp(request, token)) { *p = '='; // restore *store = strdup(p + 1); // can be \0 @@ -65,15 +77,68 @@ int chathistory_token(char *str, char *token, char **store) return 0; } -static int chathistory_targets_send_line(Client *client, HistoryResult *r, char *batchid) +static void add_chathistory_target_list(ChatHistoryTarget *new, ChatHistoryTarget **list) +{ + ChatHistoryTarget *x, *last = NULL; + + if (!*list) + { + /* We are the only item. Easy. */ + *list = new; + return; + } + + for (x = *list; x; x = x->next) + { + last = x; + if (strcmp(new->datetime, x->datetime) >= 0) + break; + } + + if (x) + { + if (x->prev) + { + /* We will insert ourselves just before this item */ + new->prev = x->prev; + new->next = x; + x->prev->next = new; + x->prev = new; + } else { + /* We are the new head */ + *list = new; + new->next = x; + x->prev = new; + } + } else + { + /* We are the last item */ + last->next = new; + new->prev = last; + } +} + +static void add_chathistory_target(ChatHistoryTarget **list, HistoryResult *r) +{ + MessageTag *m; + time_t ts; + char *datetime; + ChatHistoryTarget *e; + + if (!r->log || !((m = find_mtag(r->log->mtags, "time"))) || !m->value) + return; + datetime = m->value; + + e = safe_alloc(sizeof(ChatHistoryTarget)); + safe_strdup(e->datetime, datetime); + safe_strdup(e->object, r->object); + add_chathistory_target_list(e, list); +} + +static void chathistory_targets_send_line(Client *client, ChatHistoryTarget *r, char *batchid) { MessageTag *mtags = NULL; MessageTag *m; - char *ts; - - if (!r->log || !((m = find_mtag(r->log->mtags, "time"))) || !m->value) - return 0; - ts = m->value; if (!BadPtr(batchid)) { @@ -83,12 +148,10 @@ static int chathistory_targets_send_line(Client *client, HistoryResult *r, char } sendto_one(client, mtags, ":%s CHATHISTORY TARGETS %s %s", - me.name, r->object, ts); + me.name, r->object, r->datetime); if (mtags) free_message_tags(mtags); - - return 1; } void chathistory_targets(Client *client, HistoryFilter *filter, int limit) @@ -97,14 +160,9 @@ void chathistory_targets(Client *client, HistoryFilter *filter, int limit) HistoryResult *r; char batch[BATCHLEN+1]; int sent = 0; + ChatHistoryTarget *targets = NULL, *targets_next; - batch[0] = '\0'; - if (HasCapability(client, "batch")) - { - /* Start a new batch */ - generate_batch_id(batch); - sendto_one(client, NULL, ":%s BATCH +%s draft/chathistory-targets", me.name, batch); - } + /* 1. Grab all information we need */ filter->cmd = HFC_BEFORE; if (strcmp(filter->timestamp_a, filter->timestamp_b) < 0) @@ -119,14 +177,32 @@ void chathistory_targets(Client *client, HistoryFilter *filter, int limit) for (mp = client->user->channel; mp; mp = mp->next) { Channel *channel = mp->channel; - r = history_request(channel->chname, filter); - if (r->log && chathistory_targets_send_line(client, r, batch)) + r = history_request(channel->name, filter); + if (r) { - if (++sent >= limit) - break; /* We are done */ + add_chathistory_target(&targets, r); + free_history_result(r); } - free_history_result(r); - r = NULL; + } + + /* 2. Now send it to the client */ + + batch[0] = '\0'; + if (HasCapability(client, "batch")) + { + /* Start a new batch */ + generate_batch_id(batch); + sendto_one(client, NULL, ":%s BATCH +%s draft/chathistory-targets", me.name, batch); + } + + for (; targets; targets = targets_next) + { + targets_next = targets->next; + if (++sent < limit) + chathistory_targets_send_line(client, targets, batch); + safe_free(targets->datetime); + safe_free(targets->object); + safe_free(targets); } /* End of batch */ @@ -160,7 +236,7 @@ CMD_FUNC(cmd_chathistory) return; } - if (!strcmp(parv[1], "TARGETS")) + if (!strcasecmp(parv[1], "TARGETS")) { Membership *mp; int limit; @@ -185,18 +261,39 @@ CMD_FUNC(cmd_chathistory) goto end; } - channel = find_channel(parv[2], NULL); - if (!channel || !IsMember(client, channel) || !has_channel_mode(channel, 'H')) + channel = find_channel(parv[2]); + if (!channel) { - sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_TARGET %s %s :Messages could not be retrieved", + sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_TARGET %s %s :Messages could not be retrieved, not an existing channel", me.name, parv[1], parv[2]); return; } + if (!IsMember(client, channel)) + { + sendto_one(client, NULL, ":%s FAIL CHATHISTORY INVALID_TARGET %s %s :Messages could not be retrieved, you are not a member", + me.name, parv[1], parv[2]); + return; + } + + /* Channel is not +H? Send empty response/batch (as per IRCv3 discussion) */ + if (!has_channel_mode(channel, 'H')) + { + if (HasCapability(client, "batch")) + { + char batch[BATCHLEN+1]; + + generate_batch_id(batch); + sendto_one(client, NULL, ":%s BATCH +%s chathistory %s", me.name, batch, channel->name); + sendto_one(client, NULL, ":%s BATCH -%s", me.name, batch); + } + return; + } + filter = safe_alloc(sizeof(HistoryFilter)); /* Below this point, instead of 'return', use 'goto end', which takes care of the freeing of 'filter' and 'history' */ - if (!strcmp(parv[1], "BEFORE")) + if (!strcasecmp(parv[1], "BEFORE")) { filter->cmd = HFC_BEFORE; if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) && @@ -208,7 +305,7 @@ CMD_FUNC(cmd_chathistory) } filter->limit = atoi(parv[4]); } else - if (!strcmp(parv[1], "AFTER")) + if (!strcasecmp(parv[1], "AFTER")) { filter->cmd = HFC_AFTER; if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) && @@ -220,7 +317,7 @@ CMD_FUNC(cmd_chathistory) } filter->limit = atoi(parv[4]); } else - if (!strcmp(parv[1], "LATEST")) + if (!strcasecmp(parv[1], "LATEST")) { filter->cmd = HFC_LATEST; if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) && @@ -233,7 +330,7 @@ CMD_FUNC(cmd_chathistory) } filter->limit = atoi(parv[4]); } else - if (!strcmp(parv[1], "AROUND")) + if (!strcasecmp(parv[1], "AROUND")) { filter->cmd = HFC_AROUND; if (!chathistory_token(parv[3], "timestamp", &filter->timestamp_a) && @@ -245,7 +342,7 @@ CMD_FUNC(cmd_chathistory) } filter->limit = atoi(parv[4]); } else - if (!strcmp(parv[1], "BETWEEN")) + if (!strcasecmp(parv[1], "BETWEEN")) { filter->cmd = HFC_BETWEEN; if (BadPtr(parv[5])) @@ -283,7 +380,7 @@ CMD_FUNC(cmd_chathistory) if (filter->limit > CHATHISTORY_LIMIT) filter->limit = CHATHISTORY_LIMIT; - if ((r = history_request(channel->chname, filter))) + if ((r = history_request(channel->name, filter))) history_send_result(client, r); end: diff --git a/src/modules/chghost.c b/src/modules/chghost.c index 8d3e3f0..a52c883 100644 --- a/src/modules/chghost.c +++ b/src/modules/chghost.c @@ -25,6 +25,10 @@ #define MSG_CHGHOST "CHGHOST" CMD_FUNC(cmd_chghost); +void _userhost_save_current(Client *client); +void _userhost_changed(Client *client); + +long CAP_CHGHOST = 0L; ModuleHeader MOD_HEADER = { @@ -32,13 +36,28 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "/chghost", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; +MOD_TEST() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + EfunctionAddVoid(modinfo->handle, EFUNC_USERHOST_SAVE_CURRENT, _userhost_save_current); + EfunctionAddVoid(modinfo->handle, EFUNC_USERHOST_CHANGED, _userhost_changed); + return MOD_SUCCESS; +} + MOD_INIT() { + ClientCapabilityInfo c; + CommandAdd(modinfo->handle, MSG_CHGHOST, cmd_chghost, MAXPARA, CMD_USER|CMD_SERVER); MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&c, 0, sizeof(c)); + c.name = "chghost"; + ClientCapabilityAdd(modinfo->handle, &c, &CAP_CHGHOST); + return MOD_SUCCESS; } @@ -53,6 +72,177 @@ MOD_UNLOAD() return MOD_SUCCESS; } + +static char remember_nick[NICKLEN+1]; +static char remember_user[USERLEN+1]; +static char remember_host[HOSTLEN+1]; + +/** Save current nick/user/host. Used later by userhost_changed(). */ +void _userhost_save_current(Client *client) +{ + strlcpy(remember_nick, client->name, sizeof(remember_nick)); + strlcpy(remember_user, client->user->username, sizeof(remember_user)); + strlcpy(remember_host, GetHost(client), sizeof(remember_host)); +} + +/** User/Host changed for user. + * Note that userhost_save_current() needs to be called before this + * to save the old username/hostname. + * This userhost_changed() function deals with notifying local clients + * about the user/host change by sending PART+JOIN+MODE if + * set::allow-userhost-change force-rejoin is in use, + * and it wills end "CAP chghost" to such capable clients. + * It will also deal with bumping fakelag for the user since a user/host + * change is costly, doesn't matter if it was self-induced or not. + * + * Please call this function for any user/host change by doing: + * userhost_save_current(client); + * << change username or hostname here >> + * userhost_changed(client); + */ +void _userhost_changed(Client *client) +{ + Membership *channels; + Member *lp; + Client *acptr; + int impact = 0; + char buf[512]; + long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join"); + + if (strcmp(remember_nick, client->name)) + { + unreal_log(ULOG_ERROR, "main", "BUG_USERHOST_CHANGED", client, + "[BUG] userhost_changed() was called but without calling userhost_save_current() first! Affected user: $client\n" + "Please report above bug on https://bugs.unrealircd.org/"); + return; /* We cannot safely process this request anymore */ + } + + /* It's perfectly acceptable to call us even if the userhost didn't change. */ + if (!strcmp(remember_user, client->user->username) && !strcmp(remember_host, GetHost(client))) + return; /* Nothing to do */ + + /* Most of the work is only necessary for set::allow-userhost-change force-rejoin */ + if (UHOST_ALLOWED == UHALLOW_REJOIN) + { + /* Walk through all channels of this user.. */ + for (channels = client->user->channel; channels; channels = channels->next) + { + Channel *channel = channels->channel; + char *modes; + char partbuf[512]; /* PART */ + char joinbuf[512]; /* JOIN */ + char exjoinbuf[512]; /* JOIN (for CAP extended-join) */ + char modebuf[512]; /* MODE (if any) */ + int chanops_only = invisible_user_in_channel(client, channel); + + modebuf[0] = '\0'; + + /* If the user is banned, don't send any rejoins, it would only be annoying */ + if (is_banned(client, channel, BANCHK_JOIN, NULL, NULL)) + continue; + + /* Prepare buffers for PART, JOIN, MODE */ + ircsnprintf(partbuf, sizeof(partbuf), ":%s!%s@%s PART %s :%s", + remember_nick, remember_user, remember_host, + channel->name, + "Changing host"); + + ircsnprintf(joinbuf, sizeof(joinbuf), ":%s!%s@%s JOIN %s", + client->name, client->user->username, GetHost(client), channel->name); + + ircsnprintf(exjoinbuf, sizeof(exjoinbuf), ":%s!%s@%s JOIN %s %s :%s", + client->name, client->user->username, GetHost(client), channel->name, + IsLoggedIn(client) ? client->user->account : "*", + client->info); + + modes = get_chmodes_for_user(client, channels->member_modes); + if (!BadPtr(modes)) + ircsnprintf(modebuf, sizeof(modebuf), ":%s MODE %s %s", me.name, channel->name, modes); + + for (lp = channel->members; lp; lp = lp->next) + { + acptr = lp->client; + + if (acptr == client) + continue; /* skip self */ + + if (!MyConnect(acptr)) + continue; /* only locally connected clients */ + + if (chanops_only && !check_channel_access_member(lp, "hoaq")) + continue; /* skip non-ops if requested to (used for mode +D) */ + + if (HasCapabilityFast(acptr, CAP_CHGHOST)) + continue; /* we notify 'CAP chghost' users in a different way, so don't send it here. */ + + impact++; + + /* FIXME: if a client does not have the "chghost" cap then + * here we will not generate a proper new message, probably + * needs to be fixed... I skipped doing it for now. + */ + sendto_one(acptr, NULL, "%s", partbuf); + + if (HasCapabilityFast(acptr, CAP_EXTENDED_JOIN)) + sendto_one(acptr, NULL, "%s", exjoinbuf); + else + sendto_one(acptr, NULL, "%s", joinbuf); + + if (*modebuf) + sendto_one(acptr, NULL, "%s", modebuf); + } + } + } + + /* Now deal with "CAP chghost" clients. + * This only needs to be sent one per "common channel". + * This would normally call sendto_common_channels_local_butone() but the user already + * has the new user/host.. so we do it here.. + */ + ircsnprintf(buf, sizeof(buf), ":%s!%s@%s CHGHOST %s %s", + remember_nick, remember_user, remember_host, + client->user->username, + GetHost(client)); + current_serial++; + for (channels = client->user->channel; channels; channels = channels->next) + { + for (lp = channels->channel->members; lp; lp = lp->next) + { + acptr = lp->client; + if (MyUser(acptr) && HasCapabilityFast(acptr, CAP_CHGHOST) && + (acptr->local->serial != current_serial) && (client != acptr)) + { + /* FIXME: send mtag */ + sendto_one(acptr, NULL, "%s", buf); + acptr->local->serial = current_serial; + } + } + } + + RunHook(HOOKTYPE_USERHOST_CHANGED, client, remember_user, remember_host); + + if (MyUser(client)) + { + /* We take the liberty of sending the CHGHOST to the impacted user as + * well. This makes things easy for client coders. + * (Note that this cannot be merged with the for loop from 15 lines up + * since the user may not be in any channels) + */ + if (HasCapabilityFast(client, CAP_CHGHOST)) + sendto_one(client, NULL, "%s", buf); + + /* A userhost change always generates the following network traffic: + * server to server traffic, CAP "chghost" notifications, and + * possibly PART+JOIN+MODE if force-rejoin had work to do. + * We give the user a penalty so they don't flood... + */ + if (impact) + add_fake_lag(client, 7000); /* Resulted in rejoins and such. */ + else + add_fake_lag(client, 4000); /* No rejoins */ + } +} + /* * cmd_chghost - 12/07/1999 (two months after I made SETIDENT) - Stskeeps * :prefix CHGHOST @@ -83,7 +273,7 @@ CMD_FUNC(cmd_chghost) return; } - if (!valid_host(parv[2])) + if (!valid_host(parv[2], 0)) { sendnotice(client, "*** /ChgHost Error: A hostname may contain a-z, A-Z, 0-9, '-' & '.' - Please only use them"); return; @@ -95,7 +285,7 @@ CMD_FUNC(cmd_chghost) return; } - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); return; @@ -135,14 +325,11 @@ CMD_FUNC(cmd_chghost) if (!IsULine(client)) { - sendto_snomask(SNO_EYES, - "%s changed the virtual hostname of %s (%s@%s) to be %s", - client->name, target->name, target->user->username, - target->user->realhost, parv[2]); - /* Logging added by XeRXeS */ - ircd_log(LOG_CHGCMDS, - "CHGHOST: %s changed the virtual hostname of %s (%s@%s) to be %s", - client->name, target->name, target->user->username, target->user->realhost, parv[2]); + unreal_log(ULOG_INFO, "chgcmds", "CHGHOST_COMMAND", client, + "CHGHOST: $client changed the virtual hostname of $target.details to be $new_hostname", + log_data_string("change_type", "hostname"), + log_data_client("target", target), + log_data_string("new_hostname", parv[2])); } target->umodes |= UMODE_HIDE; diff --git a/src/modules/chgident.c b/src/modules/chgident.c index 923ec39..155c894 100644 --- a/src/modules/chgident.c +++ b/src/modules/chgident.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "/chgident", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -63,7 +63,7 @@ MOD_UNLOAD() CMD_FUNC(cmd_chgident) { Client *target; - char *s; + const char *s; int legalident = 1; if (!ValidatePermissionsForPath("client:set:ident",client,NULL,NULL,NULL)) @@ -102,7 +102,7 @@ CMD_FUNC(cmd_chgident) return; } - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); return; @@ -134,15 +134,11 @@ CMD_FUNC(cmd_chgident) } if (!IsULine(client)) { - sendto_snomask(SNO_EYES, - "%s changed the virtual ident of %s (%s@%s) to be %s", - client->name, target->name, target->user->username, - GetHost(target), parv[2]); - /* Logging ability added by XeRXeS */ - ircd_log(LOG_CHGCMDS, - "CHGIDENT: %s changed the virtual ident of %s (%s@%s) to be %s", - client->name, target->name, target->user->username, - GetHost(target), parv[2]); + unreal_log(ULOG_INFO, "chgcmds", "CHGIDENT_COMMAND", client, + "CHGIDENT: $client changed the username of $target.details to be $new_username", + log_data_string("change_type", "username"), + log_data_client("target", target), + log_data_string("new_username", parv[2])); } sendto_server(client, 0, 0, NULL, ":%s CHGIDENT %s %s", diff --git a/src/modules/chgname.c b/src/modules/chgname.c index 2866117..ed6b089 100644 --- a/src/modules/chgname.c +++ b/src/modules/chgname.c @@ -31,7 +31,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /chgname", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; @@ -88,7 +88,7 @@ CMD_FUNC(cmd_chgname) return; } - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); return; @@ -97,15 +97,11 @@ CMD_FUNC(cmd_chgname) /* Let's log this first */ if (!IsULine(client)) { - sendto_snomask(SNO_EYES, - "%s changed the GECOS of %s (%s@%s) to be %s", - client->name, target->name, target->user->username, - GetHost(target), parv[2]); - /* Logging ability added by XeRXeS */ - ircd_log(LOG_CHGCMDS, - "CHGNAME: %s changed the GECOS of %s (%s@%s) to be %s", - client->name, target->name, target->user->username, - GetHost(target), parv[2]); + unreal_log(ULOG_INFO, "chgcmds", "CHGNAME_COMMAND", client, + "CHGNAME: $client changed the realname of $target.details to be $new_realname", + log_data_string("change_type", "realname"), + log_data_client("target", target), + log_data_string("new_realname", parv[2])); } /* set the realname to make ban checking work */ diff --git a/src/modules/clienttagdeny.c b/src/modules/clienttagdeny.c index 2552d49..4add58a 100644 --- a/src/modules/clienttagdeny.c +++ b/src/modules/clienttagdeny.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER = { "5.0", "Informs clients about supported client tags", "k4be", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT(){ @@ -62,7 +62,7 @@ char *ct_isupport_param(void){ strlcpy(buf, "*", sizeof(buf)); for (m = mtaghandlers; m; m = m->next) { - if(!m->unloaded && m->name[0] == '+'){ + if (!m->unloaded && m->name[0] == '+'){ strlcat(buf, ",-", sizeof(buf)); strlcat(buf, m->name+1, sizeof(buf)); } diff --git a/src/modules/cloak.c b/src/modules/cloak_md5.c similarity index 84% rename from src/modules/cloak.c rename to src/modules/cloak_md5.c index a28a53b..74df253 100644 --- a/src/modules/cloak.c +++ b/src/modules/cloak_md5.c @@ -1,6 +1,6 @@ /* - * IRC - Internet Relay Chat, src/modules/cloak.c - * (C) 2004 The UnrealIRCd Team + * IRC - Internet Relay Chat, src/modules/cloak_md5.c + * (C) 2004-2017 Bram Matthys and The UnrealIRCd Team * * See file AUTHORS in IRC package for additional names of * the programmers. @@ -46,28 +46,26 @@ static char *hidehost_ipv6(char *host); static char *hidehost_normalhost(char *host); static inline unsigned int downsample(char *i); -Callback *cloak = NULL, *cloak_csum = NULL; - ModuleHeader MOD_HEADER = { - "cloak", + "cloak_md5", "1.0", - "Official cloaking module (md5)", + "Old cloaking module (MD5)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_TEST() { - cloak = CallbackAddPCharEx(modinfo->handle, CALLBACKTYPE_CLOAK_EX, hidehost); - if (!cloak) + if (!CallbackAddString(modinfo->handle, CALLBACKTYPE_CLOAK_KEY_CHECKSUM, cloakcsum)) { - config_error("cloak: Error while trying to install cloaking callback!"); + unreal_log(ULOG_ERROR, "config", "CLOAK_MODULE_DUPLICATE", NULL, + "cloak_md5: Error while trying to install callback.\n" + "Maybe you have multiple cloaking modules loaded? You can only load one!"); return MOD_FAILED; } - cloak_csum = CallbackAddPCharEx(modinfo->handle, CALLBACKTYPE_CLOAKKEYCSUM, cloakcsum); - if (!cloak_csum) + if (!CallbackAddString(modinfo->handle, CALLBACKTYPE_CLOAK_EX, hidehost)) { - config_error("cloak: Error while trying to install cloaking checksum callback!"); + config_error("cloak_md5: Error while trying to install cloaking callback!"); return MOD_FAILED; } HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, cloak_config_test); @@ -125,19 +123,19 @@ int cloak_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type == CONFIG_SET) { /* set::cloak-method */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "cloak-method")) + if (!ce || !ce->name || strcmp(ce->name, "cloak-method")) return 0; - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: set::cloak-method: no method specified. The only supported methods are: 'ip' and 'host'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } else - if (strcmp(ce->ce_vardata, "ip") && strcmp(ce->ce_vardata, "host")) + if (strcmp(ce->value, "ip") && strcmp(ce->value, "host")) { config_error("%s:%i: set::cloak-method: unknown method '%s'. The only supported methods are: 'ip' and 'host'", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, ce->ce_vardata); + ce->file->filename, ce->line_number, ce->value); errors++; } @@ -149,41 +147,40 @@ int cloak_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; nokeys = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { keycnt++; - /* TODO: check randomness */ - if (check_badrandomness(cep->ce_varname)) + if (check_badrandomness(cep->name)) { config_error("%s:%i: set::cloak-keys: (key %d) Keys should be mixed a-zA-Z0-9, " - "like \"a2JO6fh3Q6w4oN3s7\"", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, keycnt); + "like \"a2JO6fh3Q6w4oN3s7\"", cep->file->filename, cep->line_number, keycnt); errors++; } - if (strlen(cep->ce_varname) < 5) + if (strlen(cep->name) < 5) { config_error("%s:%i: set::cloak-keys: (key %d) Each key should be at least 5 characters", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, keycnt); + cep->file->filename, cep->line_number, keycnt); errors++; } - if (strlen(cep->ce_varname) > 100) + if (strlen(cep->name) > 100) { config_error("%s:%i: set::cloak-keys: (key %d) Each key should be less than 100 characters", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, keycnt); + cep->file->filename, cep->line_number, keycnt); errors++; } if (keycnt < 4) - keys[keycnt-1] = cep->ce_varname; + keys[keycnt-1] = cep->name; } if (keycnt != 3) { config_error("%s:%i: set::cloak-keys: we want 3 values, not %i!", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, keycnt); + ce->file->filename, ce->line_number, keycnt); errors++; } if ((keycnt == 3) && (!strcmp(keys[0], keys[1]) || !strcmp(keys[1], keys[2]))) { config_error("%s:%i: set::cloak-keys: All your 3 keys should be RANDOM, they should not be equal", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } *errs = errors; @@ -212,10 +209,10 @@ char buf[512], result[16]; if (type == CONFIG_SET) { /* set::cloak-method */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "cloak-method")) + if (!ce || !ce->name || strcmp(ce->name, "cloak-method")) return 0; - if (!strcmp(ce->ce_vardata, "ip")) + if (!strcmp(ce->value, "ip")) CLOAK_IP_ONLY = 1; return 0; @@ -225,12 +222,12 @@ char buf[512], result[16]; return 0; /* config test should ensure this goes fine... */ - cep = ce->ce_entries; - safe_strdup(cloak_key1, cep->ce_varname); - cep = cep->ce_next; - safe_strdup(cloak_key2, cep->ce_varname); - cep = cep->ce_next; - safe_strdup(cloak_key3, cep->ce_varname); + cep = ce->items; + safe_strdup(cloak_key1, cep->name); + cep = cep->next; + safe_strdup(cloak_key2, cep->name); + cep = cep->next; + safe_strdup(cloak_key3, cep->name); /* Calculate checksum */ ircsnprintf(buf, sizeof(buf), "%s:%s:%s", KEY1, KEY2, KEY3); @@ -412,14 +409,14 @@ unsigned int alpha, n; { unsigned int len; p++; - ircsnprintf(result, sizeof(result), "%s-%X.", hidden_host, alpha); + ircsnprintf(result, sizeof(result), "%s-%X.", CLOAK_PREFIX, alpha); len = strlen(result) + strlen(p); if (len <= HOSTLEN) strlcat(result, p, sizeof(result)); else strlcat(result, p + (len - HOSTLEN), sizeof(result)); } else - ircsnprintf(result, sizeof(result), "%s-%X", hidden_host, alpha); + ircsnprintf(result, sizeof(result), "%s-%X", CLOAK_PREFIX, alpha); return result; } diff --git a/src/modules/cloak_none.c b/src/modules/cloak_none.c new file mode 100644 index 0000000..4cb627d --- /dev/null +++ b/src/modules/cloak_none.c @@ -0,0 +1,87 @@ +/* + * IRC - Internet Relay Chat, src/modules/cloak_none.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +char *cloakcsum(); +int cloak_config_test(ConfigFile *, ConfigEntry *, int, int *); + +ModuleHeader MOD_HEADER = { + "cloak_none", + "1.0", + "Cloaking module that does nothing", + "UnrealIRCd Team", + "unrealircd-6", +}; + +MOD_TEST() +{ + if (!CallbackAddString(modinfo->handle, CALLBACKTYPE_CLOAK_KEY_CHECKSUM, cloakcsum)) + { + unreal_log(ULOG_ERROR, "config", "CLOAK_MODULE_DUPLICATE", NULL, + "cloak_none: Error while trying to install callback.\n" + "Maybe you have multiple cloaking modules loaded? You can only load one!"); + return MOD_FAILED; + } + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, cloak_config_test); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int cloak_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + int errors = 0; + + if (type != CONFIG_CLOAKKEYS) + return 0; + + if (ce->items) + { + config_error("%s:%i: The cloaking module 'cloak_none' is loaded (no cloaking) but " + "you also have set::cloak-keys set. Either delete your cloak keys, " + "or switch to a real cloaking module.", + ce->file->filename, ce->line_number); + errors++; + } + *errs = errors; + return errors ? -1 : 1; +} + +char *cloakcsum() +{ + return "NONE"; +} diff --git a/src/modules/cloak_sha256.c b/src/modules/cloak_sha256.c new file mode 100644 index 0000000..3200a86 --- /dev/null +++ b/src/modules/cloak_sha256.c @@ -0,0 +1,411 @@ +/* + * IRC - Internet Relay Chat, src/modules/cloak_sha256.c + * (C) 2004-2021 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. + */ + +#include "unrealircd.h" + +static char *cloak_key1 = NULL, *cloak_key2 = NULL, *cloak_key3 = NULL; +static char cloak_checksum[64]; +static int nokeys = 1; + +int CLOAK_IP_ONLY = 0; + +#undef KEY1 +#undef KEY2 +#undef KEY3 +#define KEY1 cloak_key1 +#define KEY2 cloak_key2 +#define KEY3 cloak_key3 + +#define SHA256_HASH_SIZE (256/8) + +char *hidehost(Client *client, char *host); +char *cloakcsum(); +int cloak_config_test(ConfigFile *, ConfigEntry *, int, int *); +int cloak_config_run(ConfigFile *, ConfigEntry *, int); +int cloak_config_posttest(int *); + +static char *hidehost_ipv4(char *host); +static char *hidehost_ipv6(char *host); +static char *hidehost_normalhost(char *host); +static inline unsigned int downsample(char *i); + +ModuleHeader MOD_HEADER = { + "cloak_sha256", + "1.0", + "Cloaking module (SHA256)", + "UnrealIRCd Team", + "unrealircd-6", +}; + +MOD_TEST() +{ + if (!CallbackAddString(modinfo->handle, CALLBACKTYPE_CLOAK_KEY_CHECKSUM, cloakcsum)) + { + unreal_log(ULOG_ERROR, "config", "CLOAK_MODULE_DUPLICATE", NULL, + "cloak_sha256: Error while trying to install callback.\n" + "Maybe you have multiple cloaking modules loaded? You can only load one!"); + return MOD_FAILED; + } + if (!CallbackAddString(modinfo->handle, CALLBACKTYPE_CLOAK_EX, hidehost)) + { + config_error("cloak_sha256: Error while trying to install cloaking callback!"); + return MOD_FAILED; + } + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, cloak_config_test); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, cloak_config_posttest); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, cloak_config_run); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + if (cloak_key1) + { + safe_free(cloak_key1); + safe_free(cloak_key2); + safe_free(cloak_key3); + } + return MOD_SUCCESS; +} + +static int check_badrandomness(char *key) +{ + char gotlowcase=0, gotupcase=0, gotdigit=0; + char *p; + + for (p=key; *p; p++) + { + if (islower(*p)) + gotlowcase = 1; + else if (isupper(*p)) + gotupcase = 1; + else if (isdigit(*p)) + gotdigit = 1; + } + + if (gotlowcase && gotupcase && gotdigit) + return 0; + + return 1; +} + + +int cloak_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + ConfigEntry *cep; + int keycnt = 0, errors = 0; + char *keys[3]; + + if (type == CONFIG_SET) + { + /* set::cloak-method */ + if (!ce || !ce->name || strcmp(ce->name, "cloak-method")) + return 0; + + if (!ce->value) + { + config_error("%s:%i: set::cloak-method: no method specified. The only supported methods are: 'ip' and 'host'", + ce->file->filename, ce->line_number); + errors++; + } else + if (strcmp(ce->value, "ip") && strcmp(ce->value, "host")) + { + config_error("%s:%i: set::cloak-method: unknown method '%s'. The only supported methods are: 'ip' and 'host'", + ce->file->filename, ce->line_number, ce->value); + errors++; + } + + *errs = errors; + return errors ? -1 : 1; + } + + if (type != CONFIG_CLOAKKEYS) + return 0; + + nokeys = 0; + for (cep = ce->items; cep; cep = cep->next) + { + keycnt++; + if (check_badrandomness(cep->name)) + { + config_error("%s:%i: set::cloak-keys: (key %d) Keys should be mixed a-zA-Z0-9, " + "like \"a2JO6fh3Q6w4oN3s7\"", cep->file->filename, cep->line_number, keycnt); + errors++; + } + if (strlen(cep->name) < 80) + { + config_error("%s:%i: set::cloak-keys: (key %d) Each key should be at least 80 characters", + cep->file->filename, cep->line_number, keycnt); + errors++; + } + if (strlen(cep->name) > 1000) + { + config_error("%s:%i: set::cloak-keys: (key %d) Each key should be less than 1000 characters", + cep->file->filename, cep->line_number, keycnt); + errors++; + } + if (keycnt < 4) + keys[keycnt-1] = cep->name; + } + if (keycnt != 3) + { + config_error("%s:%i: set::cloak-keys: we want 3 values, not %i!", + ce->file->filename, ce->line_number, keycnt); + errors++; + } + if ((keycnt == 3) && (!strcmp(keys[0], keys[1]) || !strcmp(keys[1], keys[2]))) + { + config_error("%s:%i: set::cloak-keys: All your 3 keys should be RANDOM, they should not be equal", + ce->file->filename, ce->line_number); + errors++; + } + *errs = errors; + return errors ? -1 : 1; +} + +int cloak_config_posttest(int *errs) +{ + int errors = 0; + + if (nokeys) + { + config_error("set::cloak-keys missing!"); + errors++; + } + + *errs = errors; + return errors ? -1 : 1; +} + +int cloak_config_run(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep; + char buf[4096]; + char result[128]; + + if (type == CONFIG_SET) + { + /* set::cloak-method */ + if (!ce || !ce->name || strcmp(ce->name, "cloak-method")) + return 0; + + if (!strcmp(ce->value, "ip")) + CLOAK_IP_ONLY = 1; + + return 0; + } + + if (type != CONFIG_CLOAKKEYS) + return 0; + + /* config test should ensure this goes fine... */ + cep = ce->items; + safe_strdup(cloak_key1, cep->name); + cep = cep->next; + safe_strdup(cloak_key2, cep->name); + cep = cep->next; + safe_strdup(cloak_key3, cep->name); + + /* Calculate checksum */ + ircsnprintf(buf, sizeof(buf), "%s:%s:%s", KEY1, KEY2, KEY3); + ircsnprintf(cloak_checksum, sizeof(cloak_checksum), "SHA256:%s", sha256hash(result, buf, strlen(buf))); + return 1; +} + +char *hidehost(Client *client, char *host) +{ + char *p; + int host_type; + + if (CLOAK_IP_ONLY) + host = GetIP(client); + + host_type = is_valid_ip(host); + + if (host_type == 4) + return hidehost_ipv4(host); + else if (host_type == 6) + return hidehost_ipv6(host); + else + return hidehost_normalhost(host); +} + +char *cloakcsum() +{ + return cloak_checksum; +} + +/** Downsamples a 256 bit result to 32 bits (SHA256 -> unsigned int) */ +static inline unsigned int downsample(char *i) +{ + char r[4]; + + r[0] = i[0] ^ i[1] ^ i[2] ^ i[3] ^ i[4] ^ i[5] ^ i[6] ^ i[7]; + r[1] = i[8] ^ i[9] ^ i[10] ^ i[11] ^ i[12] ^ i[13] ^ i[14] ^ i[15]; + r[2] = i[16] ^ i[17] ^ i[18] ^ i[19] ^ i[20] ^ i[21] ^ i[22] ^ i[23]; + r[3] = i[24] ^ i[25] ^ i[26] ^ i[27] ^ i[28] ^ i[29] ^ i[30] ^ i[31]; + + return ( ((unsigned int)r[0] << 24) + + ((unsigned int)r[1] << 16) + + ((unsigned int)r[2] << 8) + + (unsigned int)r[3]); +} + +static char *hidehost_ipv4(char *host) +{ + unsigned int a, b, c, d; + static char buf[512], res[512], res2[512], result[128]; + unsigned long n; + unsigned int alpha, beta, gamma; + + /* + * Output: ALPHA.BETA.GAMMA.IP + * ALPHA is unique for a.b.c.d + * BETA is unique for a.b.c.* + * GAMMA is unique for a.b.* + * We cloak like this: + * ALPHA = downsample(sha256(sha256("KEY2:A.B.C.D:KEY3")+"KEY1")); + * BETA = downsample(sha256(sha256("KEY3:A.B.C:KEY1")+"KEY2")); + * GAMMA = downsample(sha256(sha256("KEY1:A.B:KEY2")+"KEY3")); + */ + sscanf(host, "%u.%u.%u.%u", &a, &b, &c, &d); + + /* ALPHA... */ + ircsnprintf(buf, sizeof(buf), "%s:%s:%s", KEY2, host, KEY3); + sha256hash_binary(res, buf, strlen(buf)); + strlcpy(res+SHA256_HASH_SIZE, KEY1, sizeof(res)-SHA256_HASH_SIZE); /* first bytes are filled, append our key.. */ + n = strlen(res+SHA256_HASH_SIZE) + SHA256_HASH_SIZE; + sha256hash_binary(res2, res, n); + alpha = downsample(res2); + + /* BETA... */ + ircsnprintf(buf, sizeof(buf), "%s:%d.%d.%d:%s", KEY3, a, b, c, KEY1); + sha256hash_binary(res, buf, strlen(buf)); + strlcpy(res+SHA256_HASH_SIZE, KEY2, sizeof(res)-SHA256_HASH_SIZE); /* first bytes are filled, append our key.. */ + n = strlen(res+SHA256_HASH_SIZE) + SHA256_HASH_SIZE; + sha256hash_binary(res2, res, n); + beta = downsample(res2); + + /* GAMMA... */ + ircsnprintf(buf, sizeof(buf), "%s:%d.%d:%s", KEY1, a, b, KEY2); + sha256hash_binary(res, buf, strlen(buf)); + strlcpy(res+SHA256_HASH_SIZE, KEY3, sizeof(res)-SHA256_HASH_SIZE); /* first bytes are filled, append our key.. */ + n = strlen(res+SHA256_HASH_SIZE) + SHA256_HASH_SIZE; + sha256hash_binary(res2, res, n); + gamma = downsample(res2); + + ircsnprintf(result, sizeof(result), "%X.%X.%X.IP", alpha, beta, gamma); + return result; +} + +static char *hidehost_ipv6(char *host) +{ + unsigned int a, b, c, d, e, f, g, h; + static char buf[512], res[512], res2[512], result[128]; + unsigned long n; + unsigned int alpha, beta, gamma; + + /* + * Output: ALPHA:BETA:GAMMA:IP + * ALPHA is unique for a:b:c:d:e:f:g:h + * BETA is unique for a:b:c:d:e:f:g + * GAMMA is unique for a:b:c:d + * We cloak like this: + * ALPHA = downsample(sha256(sha256("KEY2:a:b:c:d:e:f:g:h:KEY3")+"KEY1")); + * BETA = downsample(sha256(sha256("KEY3:a:b:c:d:e:f:g:KEY1")+"KEY2")); + * GAMMA = downsample(sha256(sha256("KEY1:a:b:c:d:KEY2")+"KEY3")); + */ + sscanf(host, "%x:%x:%x:%x:%x:%x:%x:%x", + &a, &b, &c, &d, &e, &f, &g, &h); + + /* ALPHA... */ + ircsnprintf(buf, sizeof(buf), "%s:%s:%s", KEY2, host, KEY3); + sha256hash_binary(res, buf, strlen(buf)); + strlcpy(res+SHA256_HASH_SIZE, KEY1, sizeof(res)-SHA256_HASH_SIZE); /* first bytes are filled, append our key.. */ + n = strlen(res+SHA256_HASH_SIZE) + SHA256_HASH_SIZE; + sha256hash_binary(res2, res, n); + alpha = downsample(res2); + + /* BETA... */ + ircsnprintf(buf, sizeof(buf), "%s:%x:%x:%x:%x:%x:%x:%x:%s", KEY3, a, b, c, d, e, f, g, KEY1); + sha256hash_binary(res, buf, strlen(buf)); + strlcpy(res+SHA256_HASH_SIZE, KEY2, sizeof(res)-SHA256_HASH_SIZE); /* first bytes are filled, append our key.. */ + n = strlen(res+SHA256_HASH_SIZE) + SHA256_HASH_SIZE; + sha256hash_binary(res2, res, n); + beta = downsample(res2); + + /* GAMMA... */ + ircsnprintf(buf, sizeof(buf), "%s:%x:%x:%x:%x:%s", KEY1, a, b, c, d, KEY2); + sha256hash_binary(res, buf, strlen(buf)); + strlcpy(res+SHA256_HASH_SIZE, KEY3, sizeof(res)-SHA256_HASH_SIZE); /* first bytes are filled, append our key.. */ + n = strlen(res+SHA256_HASH_SIZE) + SHA256_HASH_SIZE; + sha256hash_binary(res2, res, n); + gamma = downsample(res2); + + ircsnprintf(result, sizeof(result), "%X:%X:%X:IP", alpha, beta, gamma); + return result; +} + +static char *hidehost_normalhost(char *host) +{ + char *p; + static char buf[512], res[512], res2[512], result[HOSTLEN+1]; + unsigned int alpha, n; + + ircsnprintf(buf, sizeof(buf), "%s:%s:%s", KEY1, host, KEY2); + sha256hash_binary(res, buf, strlen(buf)); + strlcpy(res+SHA256_HASH_SIZE, KEY3, sizeof(res)-SHA256_HASH_SIZE); /* first bytes are filled, append our key.. */ + n = strlen(res+SHA256_HASH_SIZE) + SHA256_HASH_SIZE; + sha256hash_binary(res2, res, n); + alpha = downsample(res2); + + for (p = host; *p; p++) + if (*p == '.') + if (isalpha(*(p + 1))) + break; + + if (*p) + { + unsigned int len; + p++; + ircsnprintf(result, sizeof(result), "%s-%X.", CLOAK_PREFIX, alpha); + len = strlen(result) + strlen(p); + if (len <= HOSTLEN) + strlcat(result, p, sizeof(result)); + else + strlcat(result, p + (len - HOSTLEN), sizeof(result)); + } else + ircsnprintf(result, sizeof(result), "%s-%X", CLOAK_PREFIX, alpha); + + return result; +} diff --git a/src/modules/close.c b/src/modules/close.c index e9f988e..a732990 100644 --- a/src/modules/close.c +++ b/src/modules/close.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /close", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -75,7 +75,8 @@ CMD_FUNC(cmd_close) } sendnumeric(client, RPL_CLOSEEND, closed); - sendto_realops("%s!%s@%s closed %d unknown connections", client->name, - client->user->username, GetHost(client), closed); + unreal_log(ULOG_INFO, "close", "CLOSED_CONNECTIONS", client, + "$client.details closed $num_closed unknown connections", + log_data_integer("num_closed", closed)); irccounts.unknown = 0; } diff --git a/src/modules/connect.c b/src/modules/connect.c index e5c47bd..59cff3c 100644 --- a/src/modules/connect.c +++ b/src/modules/connect.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /connect", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -76,7 +76,7 @@ CMD_FUNC(cmd_connect) sendnumeric(client, ERR_NOPRIVILEGES); return; } - if (hunt_server(client, recv_mtags, ":%s CONNECT %s %s :%s", 3, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "CONNECT", 3, parc, parv) != HUNTED_ISME) return; if (parc < 2 || *parv[1] == '\0') @@ -117,7 +117,7 @@ CMD_FUNC(cmd_connect) /* Evaluate deny link */ for (deny = conf_deny_link; deny; deny = deny->next) { - if (deny->flag.type == CRULE_ALL && match_simple(deny->mask, aconf->servername) + if (deny->flag.type == CRULE_ALL && unreal_mask_match_string(aconf->servername, deny->mask) && crule_eval(deny->rule)) { sendnotice(client, "*** Connect: Disallowed by connection rule"); @@ -134,22 +134,5 @@ CMD_FUNC(cmd_connect) get_client_name(client, FALSE)); } - switch (retval = connect_server(aconf, client, NULL)) - { - case 0: - sendnotice(client, "*** Trying to activate link with server %s[%s]...", - aconf->servername, aconf->outgoing.hostname); - break; - case -1: - sendnotice(client, "*** Couldn't connect to %s[%s]", - aconf->servername, aconf->outgoing.hostname); - break; - case -2: - sendnotice(client, "*** Resolving hostname '%s'...", - aconf->outgoing.hostname); - break; - default: - sendnotice(client, "*** Connection to %s[%s] failed: %s", - aconf->servername, aconf->outgoing.hostname, STRERROR(retval)); - } + connect_server(aconf, client, NULL); } diff --git a/src/modules/connthrottle.c b/src/modules/connthrottle.c index 608e07c..5b35e2a 100644 --- a/src/modules/connthrottle.c +++ b/src/modules/connthrottle.c @@ -19,7 +19,7 @@ ModuleHeader MOD_HEADER CONNTHROTTLE_VERSION, "Connection throttler - by Syzop", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; typedef struct { @@ -146,9 +146,6 @@ int ct_config_posttest(int *errs) return errors ? -1 : 1; } -#ifndef CheckNull - #define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } -#endif /** Test the set::connthrottle configuration */ int ct_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { @@ -159,113 +156,113 @@ int ct_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; /* We are only interrested in set::connthrottle.. */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "connthrottle")) + if (!ce || !ce->name || strcmp(ce->name, "connthrottle")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "known-users")) + if (!strcmp(cep->name, "known-users")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "minimum-reputation-score")) + if (!strcmp(cepp->name, "minimum-reputation-score")) { - int cnt = atoi(cepp->ce_vardata); + int cnt = atoi(cepp->value); if (cnt < 1) { config_error("%s:%i: set::connthrottle::known-users::minimum-reputation-score should be at least 1", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; continue; } } else - if (!strcmp(cepp->ce_varname, "sasl-bypass")) + if (!strcmp(cepp->name, "sasl-bypass")) { } else - if (!strcmp(cepp->ce_varname, "webirc-bypass")) + if (!strcmp(cepp->name, "webirc-bypass")) { } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, - "set::connthrottle::known-users", cepp->ce_varname); + config_error_unknown(cepp->file->filename, cepp->line_number, + "set::connthrottle::known-users", cepp->name); errors++; } } } else - if (!strcmp(cep->ce_varname, "new-users")) + if (!strcmp(cep->name, "new-users")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "local-throttle")) + if (!strcmp(cepp->name, "local-throttle")) { int cnt, period; - if (!config_parse_flood(cepp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(cepp->value, &cnt, &period) || (cnt < 1) || (cnt > 2000000000) || (period > 2000000000)) { config_error("%s:%i: set::connthrottle::new-users::local-throttle error. " "Syntax is : (eg 6:60), " "and count and period should be non-zero.", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; continue; } } else - if (!strcmp(cepp->ce_varname, "global-throttle")) + if (!strcmp(cepp->name, "global-throttle")) { int cnt, period; - if (!config_parse_flood(cepp->ce_vardata, &cnt, &period) || + if (!config_parse_flood(cepp->value, &cnt, &period) || (cnt < 1) || (cnt > 2000000000) || (period > 2000000000)) { config_error("%s:%i: set::connthrottle::new-users::global-throttle error. " "Syntax is : (eg 6:60), " "and count and period should be non-zero.", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; continue; } } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, - "set::connthrottle::new-users", cepp->ce_varname); + config_error_unknown(cepp->file->filename, cepp->line_number, + "set::connthrottle::new-users", cepp->name); errors++; } } } else - if (!strcmp(cep->ce_varname, "disabled-when")) + if (!strcmp(cep->name, "disabled-when")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { CheckNull(cepp); - if (!strcmp(cepp->ce_varname, "start-delay")) + if (!strcmp(cepp->name, "start-delay")) { - int cnt = config_checkval(cepp->ce_vardata, CFG_TIME); + int cnt = config_checkval(cepp->value, CFG_TIME); if ((cnt < 0) || (cnt > 3600)) { config_error("%s:%i: set::connthrottle::disabled-when::start-delay should be in range 0-3600", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum); + cepp->file->filename, cepp->line_number); errors++; continue; } } else - if (!strcmp(cepp->ce_varname, "reputation-gathering")) + if (!strcmp(cepp->name, "reputation-gathering")) { } else { - config_error_unknown(cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, - "set::connthrottle::disabled-when", cepp->ce_varname); + config_error_unknown(cepp->file->filename, cepp->line_number, + "set::connthrottle::disabled-when", cepp->name); errors++; } } } else - if (!strcmp(cep->ce_varname, "reason")) + if (!strcmp(cep->name, "reason")) { CheckNull(cep); } else { config_error("%s:%i: unknown directive set::connthrottle::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; continue; } @@ -284,48 +281,48 @@ int ct_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::connthrottle.. */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "connthrottle")) + if (!ce || !ce->name || strcmp(ce->name, "connthrottle")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "known-users")) + if (!strcmp(cep->name, "known-users")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "minimum-reputation-score")) - cfg.minimum_reputation_score = atoi(cepp->ce_vardata); - else if (!strcmp(cepp->ce_varname, "sasl-bypass")) - cfg.sasl_bypass = config_checkval(cepp->ce_vardata, CFG_YESNO); - else if (!strcmp(cepp->ce_varname, "webirc-bypass")) - cfg.webirc_bypass = config_checkval(cepp->ce_vardata, CFG_YESNO); + if (!strcmp(cepp->name, "minimum-reputation-score")) + cfg.minimum_reputation_score = atoi(cepp->value); + else if (!strcmp(cepp->name, "sasl-bypass")) + cfg.sasl_bypass = config_checkval(cepp->value, CFG_YESNO); + else if (!strcmp(cepp->name, "webirc-bypass")) + cfg.webirc_bypass = config_checkval(cepp->value, CFG_YESNO); } } else - if (!strcmp(cep->ce_varname, "new-users")) + if (!strcmp(cep->name, "new-users")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "local-throttle")) - config_parse_flood(cepp->ce_vardata, &cfg.local.count, &cfg.local.period); - else if (!strcmp(cepp->ce_varname, "global-throttle")) - config_parse_flood(cepp->ce_vardata, &cfg.global.count, &cfg.global.period); + if (!strcmp(cepp->name, "local-throttle")) + config_parse_flood(cepp->value, &cfg.local.count, &cfg.local.period); + else if (!strcmp(cepp->name, "global-throttle")) + config_parse_flood(cepp->value, &cfg.global.count, &cfg.global.period); } } else - if (!strcmp(cep->ce_varname, "disabled-when")) + if (!strcmp(cep->name, "disabled-when")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcmp(cepp->ce_varname, "start-delay")) - cfg.start_delay = config_checkval(cepp->ce_vardata, CFG_TIME); - else if (!strcmp(cepp->ce_varname, "reputation-gathering")) - cfg.reputation_gathering = config_checkval(cepp->ce_vardata, CFG_TIME); + if (!strcmp(cepp->name, "start-delay")) + cfg.start_delay = config_checkval(cepp->value, CFG_TIME); + else if (!strcmp(cepp->name, "reputation-gathering")) + cfg.reputation_gathering = config_checkval(cepp->value, CFG_TIME); } } else - if (!strcmp(cep->ce_varname, "reason")) + if (!strcmp(cep->name, "reason")) { safe_free(cfg.reason); - cfg.reason = safe_alloc(strlen(cep->ce_vardata)+16); - sprintf(cfg.reason, "Throttled: %s", cep->ce_vardata); + cfg.reason = safe_alloc(strlen(cep->value)+16); + sprintf(cfg.reason, "Throttled: %s", cep->value); } } return 1; @@ -360,16 +357,18 @@ EVENT(connthrottle_evt) if (ucounter->rejected_clients) { - snprintf(buf, sizeof(buf), - "[ConnThrottle] Stats for this server past 60 secs: Connections rejected: %d. Accepted: %d known user(s), %d SASL, %d WEBIRC and %d new user(s).", - ucounter->rejected_clients, - ucounter->allowed_score, - ucounter->allowed_sasl, - ucounter->allowed_webirc, - ucounter->allowed_other); - - sendto_realops("%s", buf); - ircd_log(LOG_ERROR, "%s", buf); + unreal_log(ULOG_INFO, "connthrottle", "CONNTHROTLE_REPORT", NULL, + "ConnThrottle] Stats for this server past 60 secs: " + "Connections rejected: $num_rejected. " + "Accepted: $num_accepted_known_users known user(s), " + "$num_accepted_sasl SASL, " + "$num_accepted_webirc WEBIRC and " + "$num_accepted_unknown_users new user(s).", + log_data_integer("num_rejected", ucounter->rejected_clients), + log_data_integer("num_accepted_known_users", ucounter->allowed_score), + log_data_integer("num_accepted_sasl", ucounter->allowed_sasl), + log_data_integer("num_accepted_webirc", ucounter->allowed_webirc), + log_data_integer("num_accepted_unknown_users", ucounter->allowed_other)); } /* Reset stats for next message */ @@ -391,7 +390,7 @@ int ct_pre_lconnect(Client *client) int throttle=0; int score; - if (me.local->firsttime + cfg.start_delay > TStime()) + if (me.local->creationtime + cfg.start_delay > TStime()) return HOOK_CONTINUE; /* no throttle: start delay */ if (ucounter->disabled) @@ -436,10 +435,10 @@ int ct_pre_lconnect(Client *client) /* We send the LARGE banner if throttling was activated */ if (!ucounter->throttling_previous_minute && !ucounter->throttling_banner_displayed) { - ircd_log(LOG_ERROR, "[ConnThrottle] Connection throttling has been ACTIVATED due to a HIGH CONNECTION RATE."); - sendto_realops("[ConnThrottle] Connection throttling has been ACTIVATED due to a HIGH CONNECTION RATE."); - sendto_realops("[ConnThrottle] Users with IP addresses that have not been seen before will be rejected above the set connection rate. Known users can still get in."); - sendto_realops("[ConnThrottle] For more information see https://www.unrealircd.org/docs/ConnThrottle"); + unreal_log(ULOG_WARNING, "connthrottle", "CONNTHROTLE_ACTIVATED", NULL, + "[ConnThrottle] Connection throttling has been ACTIVATED due to a HIGH CONNECTION RATE.\n" + "Users with IP addresses that have not been seen before will be rejected above the set connection rate. Known users can still get in.\n" + "or more information see https://www.unrealircd.org/docs/ConnThrottle"); ucounter->throttling_banner_displayed = 1; } exit_client(client, NULL, cfg.reason); @@ -478,7 +477,7 @@ int ct_lconnect(Client *client) { int score; - if (me.local->firsttime + cfg.start_delay > TStime()) + if (me.local->creationtime + cfg.start_delay > TStime()) return 0; /* no throttle: start delay */ if (ucounter->disabled) @@ -521,7 +520,7 @@ int ct_rconnect(Client *client) { int score; - if (client->srvptr && !IsSynched(client->srvptr)) + if (client->uplink && !IsSynched(client->uplink)) return 0; /* Netmerge: skip */ if (IsULine(client)) @@ -533,8 +532,8 @@ int ct_rconnect(Client *client) * set::disabled-when::start-delay restriction on remote * servers as well. */ - if (client->srvptr && client->srvptr->serv && client->srvptr->serv->boottime && - (TStime() - client->srvptr->serv->boottime < cfg.start_delay)) + if (client->uplink && client->uplink->server && client->uplink->server->boottime && + (TStime() - client->uplink->server->boottime < cfg.start_delay)) { return 0; } @@ -584,10 +583,10 @@ CMD_FUNC(ct_throttle) { sendnotice(client, "Module DISABLED because the 'reputation' module has not gathered enough data yet (set::connthrottle::disabled-when::reputation-gathering)."); } else - if (me.local->firsttime + cfg.start_delay > TStime()) + if (me.local->creationtime + cfg.start_delay > TStime()) { sendnotice(client, "Module DISABLED due to start-delay (set::connthrottle::disabled-when::start-delay), will be enabled in %lld second(s).", - (long long)((me.local->firsttime + cfg.start_delay) - TStime())); + (long long)((me.local->creationtime + cfg.start_delay) - TStime())); } else { sendnotice(client, "Module ENABLED"); @@ -602,8 +601,8 @@ CMD_FUNC(ct_throttle) return; } ucounter->disabled = 1; - sendto_realops("[connthrottle] %s (%s@%s) DISABLED the connthrottle module.", - client->name, client->user->username, client->user->realhost); + unreal_log(ULOG_WARNING, "connthrottle", "CONNTHROTLE_MODULE_DISABLED", client, + "[ConnThrottle] $client.details DISABLED the connthrottle module."); } else if (!strcasecmp(parv[1], "ON")) { @@ -612,15 +611,15 @@ CMD_FUNC(ct_throttle) sendnotice(client, "Already ON"); return; } - sendto_realops("[connthrottle] %s (%s@%s) ENABLED the connthrottle module.", - client->name, client->user->username, client->user->realhost); + unreal_log(ULOG_WARNING, "connthrottle", "CONNTHROTLE_MODULE_ENABLED", client, + "[ConnThrottle] $client.details ENABLED the connthrottle module."); ucounter->disabled = 0; } else if (!strcasecmp(parv[1], "RESET")) { memset(ucounter, 0, sizeof(UCounter)); - sendto_realops("[connthrottle] %s (%s@%s) did a RESET on the stats/counters!!", - client->name, client->user->username, client->user->realhost); + unreal_log(ULOG_WARNING, "connthrottle", "CONNTHROTLE_RESET", client, + "[ConnThrottle] $client.details did a RESET on the statistics/counters."); } else { sendnotice(client, "Unknown option '%s'", parv[1]); diff --git a/src/modules/cycle.c b/src/modules/cycle.c index 8953609..f1af457 100644 --- a/src/modules/cycle.c +++ b/src/modules/cycle.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /cycle", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -62,6 +62,7 @@ MOD_UNLOAD() CMD_FUNC(cmd_cycle) { char channels[BUFSIZE]; + const char *parx[3]; int n; if (parc < 2) @@ -75,7 +76,8 @@ CMD_FUNC(cmd_cycle) return; /* Then JOIN the user again... */ - parv[1] = channels; - parv[2] = NULL; - do_cmd(client, recv_mtags, "JOIN", 2, parv); + parx[0] = NULL; + parx[1] = channels; + parx[2] = NULL; + do_cmd(client, recv_mtags, "JOIN", 2, parx); } diff --git a/src/modules/dccallow.c b/src/modules/dccallow.c index ffeffd4..ce1ad49 100644 --- a/src/modules/dccallow.c +++ b/src/modules/dccallow.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /dccallow", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -59,6 +59,7 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_dccallow) { + char request[BUFSIZE]; Link *lp; char *p, *s; Client *friend; @@ -92,7 +93,8 @@ CMD_FUNC(cmd_dccallow) return; } - for (p = NULL, s = strtoken(&p, parv[1], ", "); s; s = strtoken(&p, NULL, ", ")) + strlcpy(request, parv[1], sizeof(request)); + for (p = NULL, s = strtoken(&p, request, ", "); s; s = strtoken(&p, NULL, ", ")) { if (MyUser(client) && (++ntargets > maxtargets)) { @@ -105,7 +107,7 @@ CMD_FUNC(cmd_dccallow) if (!*++s) continue; - friend = find_person(s, NULL); + friend = find_user(s, NULL); if (friend == client) continue; @@ -123,7 +125,7 @@ CMD_FUNC(cmd_dccallow) if (!*++s) continue; - friend = find_person(s, NULL); + friend = find_user(s, NULL); if (friend == client) continue; if (!friend) @@ -243,8 +245,11 @@ int del_dccallow(Client *client, Client *optr) } } if (!found) - sendto_realops("[BUG!] %s was in dccallowme list of %s but not in dccallowrem list!", - optr->name, client->name); + { + unreal_log(ULOG_WARNING, "dccallow", "BUG_DCCALLOW", client, + "[BUG] DCCALLOW list for $client did not contain $target", + log_data_client("target", optr)); + } sendnumeric(client, RPL_DCCSTATUS, optr->name, "removed from"); diff --git a/src/modules/dccdeny.c b/src/modules/dccdeny.c index d27a9f7..0fda03f 100644 --- a/src/modules/dccdeny.c +++ b/src/modules/dccdeny.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "5.0", "command /dccdeny", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ @@ -40,24 +40,26 @@ int dccdeny_configtest_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int * int dccdeny_configtest_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int dccdeny_configrun_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type); int dccdeny_configrun_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type); -int dccdeny_stats(Client *client, char *para); +int dccdeny_stats(Client *client, const char *para); +int dccdeny_dcc_denied(Client *client, const char *target, const char *realfile, const char *displayfile, ConfigItem_deny_dcc *dccdeny); CMD_FUNC(cmd_dccdeny); CMD_FUNC(cmd_undccdeny); CMD_FUNC(cmd_svsfline); -int dccdeny_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); -int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); +int dccdeny_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); +int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); int dccdeny_server_sync(Client *client); -static ConfigItem_deny_dcc *dcc_isforbidden(Client *client, char *filename); -static ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, char *filename); -static void DCCdeny_add(char *filename, char *reason, int type, int type2); +static ConfigItem_deny_dcc *dcc_isforbidden(Client *client, const char *filename); +static ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, const char *filename); +static void DCCdeny_add(const char *filename, const char *reason, int type, int type2); static void DCCdeny_del(ConfigItem_deny_dcc *deny); static void dcc_wipe_services(void); -static char *get_dcc_filename(const char *text); -static int can_dcc(Client *client, char *target, Client *targetcli, char *filename, char **errmsg); -static int can_dcc_soft(Client *from, Client *to, char *filename, char **errmsg); +static const char *get_dcc_filename(const char *text); +static int can_dcc(Client *client, const char *target, Client *targetcli, const char *filename, const char **errmsg); +static int can_dcc_soft(Client *from, Client *to, const char *filename, const char **errmsg); static void free_dcc_config_blocks(void); void dccdeny_unload_free_all_conf_deny_dcc(ModData *m); void dccdeny_unload_free_all_conf_allow_dcc(ModData *m); +ConfigItem_deny_dcc *find_deny_dcc(const char *name); MOD_TEST() { @@ -80,6 +82,7 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, dccdeny_can_send_to_user); HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, dccdeny_can_send_to_channel); HookAdd(modinfo->handle, HOOKTYPE_SERVER_SYNC, 0, dccdeny_server_sync); + HookAdd(modinfo->handle, HOOKTYPE_DCC_DENIED, 0, dccdeny_dcc_denied); return MOD_SUCCESS; } @@ -104,62 +107,62 @@ int dccdeny_configtest_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int * char has_filename = 0, has_reason = 0, has_soft = 0; /* We are only interested in deny dcc { } */ - if ((type != CONFIG_DENY) || strcmp(ce->ce_vardata, "dcc")) + if ((type != CONFIG_DENY) || strcmp(ce->value, "dcc")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "deny dcc")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "filename")) + if (!strcmp(cep->name, "filename")) { if (has_filename) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny dcc::filename"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny dcc::filename"); continue; } has_filename = 1; } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { if (has_reason) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny dcc::reason"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny dcc::reason"); continue; } has_reason = 1; } - else if (!strcmp(cep->ce_varname, "soft")) + else if (!strcmp(cep->name, "soft")) { if (has_soft) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny dcc::soft"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "deny dcc::soft"); continue; } has_soft = 1; } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "deny dcc", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "deny dcc", cep->name); errors++; } } if (!has_filename) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny dcc::filename"); errors++; } if (!has_reason) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "deny dcc::reason"); errors++; } @@ -174,46 +177,46 @@ int dccdeny_configtest_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type, int int errors = 0, has_filename = 0, has_soft = 0; /* We are only interested in allow dcc { } */ - if ((type != CONFIG_ALLOW) || strcmp(ce->ce_vardata, "dcc")) + if ((type != CONFIG_ALLOW) || strcmp(ce->value, "dcc")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "allow dcc")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "filename")) + if (!strcmp(cep->name, "filename")) { if (has_filename) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow dcc::filename"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow dcc::filename"); continue; } has_filename = 1; } - else if (!strcmp(cep->ce_varname, "soft")) + else if (!strcmp(cep->name, "soft")) { if (has_soft) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "allow dcc::soft"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "allow dcc::soft"); continue; } has_soft = 1; } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "allow dcc", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "allow dcc", cep->name); errors++; } } if (!has_filename) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "allow dcc::filename"); errors++; } @@ -228,23 +231,23 @@ int dccdeny_configrun_deny_dcc(ConfigFile *cf, ConfigEntry *ce, int type) ConfigEntry *cep; /* We are only interested in deny dcc { } */ - if ((type != CONFIG_DENY) || strcmp(ce->ce_vardata, "dcc")) + if ((type != CONFIG_DENY) || strcmp(ce->value, "dcc")) return 0; deny = safe_alloc(sizeof(ConfigItem_deny_dcc)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "filename")) + if (!strcmp(cep->name, "filename")) { - safe_strdup(deny->filename, cep->ce_vardata); + safe_strdup(deny->filename, cep->value); } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { - safe_strdup(deny->reason, cep->ce_vardata); + safe_strdup(deny->reason, cep->value); } - else if (!strcmp(cep->ce_varname, "soft")) + else if (!strcmp(cep->name, "soft")) { - int x = config_checkval(cep->ce_vardata,CFG_YESNO); + int x = config_checkval(cep->value,CFG_YESNO); if (x == 1) deny->flag.type = DCCDENY_SOFT; } @@ -266,18 +269,18 @@ int dccdeny_configrun_allow_dcc(ConfigFile *cf, ConfigEntry *ce, int type) ConfigEntry *cep; /* We are only interested in allow dcc { } */ - if ((type != CONFIG_ALLOW) || strcmp(ce->ce_vardata, "dcc")) + if ((type != CONFIG_ALLOW) || strcmp(ce->value, "dcc")) return 0; allow = safe_alloc(sizeof(ConfigItem_allow_dcc)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "filename")) - safe_strdup(allow->filename, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "soft")) + if (!strcmp(cep->name, "filename")) + safe_strdup(allow->filename, cep->value); + else if (!strcmp(cep->name, "soft")) { - int x = config_checkval(cep->ce_vardata,CFG_YESNO); + int x = config_checkval(cep->value,CFG_YESNO); if (x) allow->flag.type = DCCDENY_SOFT; } @@ -371,8 +374,10 @@ CMD_FUNC(cmd_dccdeny) if (!find_deny_dcc(parv[1])) { - sendto_ops("%s added a temp dccdeny for %s (%s)", client->name, - parv[1], parv[2]); + unreal_log(ULOG_INFO, "dccdeny", "DCCDENY_ADD", client, + "[dccdeny] $client added a temporary DCCDENY for $file ($reason)", + log_data_string("file", parv[1]), + log_data_string("reason", parv[2])); DCCdeny_add(parv[1], parv[2], DCCDENY_HARD, CONF_BAN_TYPE_TEMPORARY); return; } else @@ -405,7 +410,10 @@ CMD_FUNC(cmd_undccdeny) if ((d = find_deny_dcc(parv[1])) && d->flag.type2 == CONF_BAN_TYPE_TEMPORARY) { - sendto_ops("%s removed a temp dccdeny for %s", client->name, parv[1]); + unreal_log(ULOG_INFO, "dccdeny", "DCCDENY_DEL", client, + "[dccdeny] $client removed a temporary DCCDENY for $file ($reason)", + log_data_string("file", d->filename), + log_data_string("reason", d->reason)); DCCdeny_del(d); return; } else @@ -489,11 +497,11 @@ int dccdeny_server_sync(Client *client) } /** Check if a DCC should be blocked (user-to-user) */ -int dccdeny_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int dccdeny_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { if (**text == '\001') { - char *filename = get_dcc_filename(*text); + const char *filename = get_dcc_filename(*text); if (filename) { if (MyUser(client) && !can_dcc(client, target->name, target, filename, errmsg)) @@ -507,15 +515,15 @@ int dccdeny_can_send_to_user(Client *client, Client *target, char **text, char * } /** Check if a DCC should be blocked (user-to-channel, unusual) */ -int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { static char errbuf[512]; if (MyUser(client) && (**msg == '\001')) { - char *err = NULL; - char *filename = get_dcc_filename(*msg); - if (filename && !can_dcc(client, channel->chname, NULL, filename, &err)) + const char *err = NULL; + const char *filename = get_dcc_filename(*msg); + if (filename && !can_dcc(client, channel->name, NULL, filename, &err)) { if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE)) { @@ -541,10 +549,11 @@ int dccdeny_can_send_to_channel(Client *client, Channel *channel, Membership *lp * This is to protect a bit against tricks like 'flood-it-off-the-buffer' * and color 1,1 etc... */ -static char *dcc_displayfile(char *f) +static const char *dcc_displayfile(const char *f) { static char buf[512]; - char *i, *o = buf; + const char *i; + char *o = buf; size_t n = strlen(f); if (n < 300) @@ -575,7 +584,7 @@ static char *dcc_displayfile(char *f) return buf; } -static char *get_dcc_filename(const char *text) +static const char *get_dcc_filename(const char *text) { static char filename[BUFSIZE+1]; char *end; @@ -617,7 +626,7 @@ static char *get_dcc_filename(const char *text) * @param text The entire message * @returns 1 if DCC SEND allowed, 0 if rejected */ -static int can_dcc(Client *client, char *target, Client *targetcli, char *filename, char **errmsg) +static int can_dcc(Client *client, const char *target, Client *targetcli, const char *filename, const char **errmsg) { ConfigItem_deny_dcc *fl; static char errbuf[256]; @@ -644,9 +653,9 @@ static int can_dcc(Client *client, char *target, Client *targetcli, char *filena if ((fl = dcc_isforbidden(client, filename))) { - char *displayfile = dcc_displayfile(filename); + const char *displayfile = dcc_displayfile(filename); - RunHook5(HOOKTYPE_DCC_DENIED, client, target, filename, displayfile, fl); + RunHook(HOOKTYPE_DCC_DENIED, client, target, filename, displayfile, fl); ircsnprintf(errbuf, sizeof(errbuf), "Cannot DCC SEND file: %s", fl->reason); *errmsg = errbuf; @@ -675,10 +684,10 @@ static int can_dcc(Client *client, char *target, Client *targetcli, char *filena * 1: allowed * 0: block */ -static int can_dcc_soft(Client *from, Client *to, char *filename, char **errmsg) +static int can_dcc_soft(Client *from, Client *to, const char *filename, const char **errmsg) { ConfigItem_deny_dcc *fl; - char *displayfile; + const char *displayfile; static char errbuf[256]; /* User (IRCOp) may bypass send restrictions */ @@ -718,7 +727,7 @@ static int can_dcc_soft(Client *from, Client *to, char *filename, char **errmsg) } /** Checks if the dcc is blacklisted. */ -static ConfigItem_deny_dcc *dcc_isforbidden(Client *client, char *filename) +static ConfigItem_deny_dcc *dcc_isforbidden(Client *client, const char *filename) { ConfigItem_deny_dcc *d; ConfigItem_allow_dcc *a; @@ -743,7 +752,7 @@ static ConfigItem_deny_dcc *dcc_isforbidden(Client *client, char *filename) } /** checks if the dcc is discouraged ('soft bans'). */ -static ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, char *filename) +static ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, const char *filename) { ConfigItem_deny_dcc *d; ConfigItem_allow_dcc *a; @@ -767,7 +776,7 @@ static ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, char *filename) return NULL; } -static void DCCdeny_add(char *filename, char *reason, int type, int type2) +static void DCCdeny_add(const char *filename, const char *reason, int type, int type2) { ConfigItem_deny_dcc *deny = NULL; @@ -787,7 +796,7 @@ static void DCCdeny_del(ConfigItem_deny_dcc *deny) safe_free(deny); } -ConfigItem_deny_dcc *find_deny_dcc(char *name) +ConfigItem_deny_dcc *find_deny_dcc(const char *name) { ConfigItem_deny_dcc *e; @@ -820,7 +829,7 @@ static void dcc_wipe_services(void) } -int dccdeny_stats(Client *client, char *para) +int dccdeny_stats(Client *client, const char *para) { ConfigItem_deny_dcc *denytmp; ConfigItem_allow_dcc *allowtmp; @@ -860,3 +869,13 @@ int dccdeny_stats(Client *client, char *para) } return 1; } + +int dccdeny_dcc_denied(Client *client, const char *target, const char *realfile, const char *displayfile, ConfigItem_deny_dcc *dccdeny) +{ + unreal_log(ULOG_INFO, "dcc", "DCC_REJECTED", client, + "$client.details tried to send forbidden file $filename ($ban_reason) to $target (is blocked now)", + log_data_string("filename", displayfile), + log_data_string("ban_reason", dccdeny->reason), + log_data_string("target", target)); + return 0; +} diff --git a/src/modules/echo-message.c b/src/modules/echo-message.c index 90f83b5..97a5997 100644 --- a/src/modules/echo-message.c +++ b/src/modules/echo-message.c @@ -28,15 +28,15 @@ ModuleHeader MOD_HEADER "5.0", "Batch CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ long CAP_ECHO_MESSAGE = 0L; /* Forward declarations */ -int em_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype); -int em_usermsg(Client *client, Client *to, MessageTag *mtags, char *text, SendType sendtype); +int em_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype); +int em_usermsg(Client *client, Client *to, MessageTag *mtags, const char *text, SendType sendtype); MOD_INIT() { @@ -64,7 +64,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int em_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, SendType sendtype) +int em_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype) { if (MyUser(client) && HasCapabilityFast(client, CAP_ECHO_MESSAGE)) { @@ -85,7 +85,7 @@ int em_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char return 0; } -int em_usermsg(Client *client, Client *to, MessageTag *mtags, char *text, SendType sendtype) +int em_usermsg(Client *client, Client *to, MessageTag *mtags, const char *text, SendType sendtype) { if (MyUser(client) && HasCapabilityFast(client, CAP_ECHO_MESSAGE)) { diff --git a/src/modules/eos.c b/src/modules/eos.c index 157c9f0..c3e0707 100644 --- a/src/modules/eos.c +++ b/src/modules/eos.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /eos", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -63,12 +63,8 @@ CMD_FUNC(cmd_eos) { if (!IsServer(client)) return; - client->serv->flags.synced = 1; + client->server->flags.synced = 1; /* pass it on ^_- */ -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[EOSDBG] cmd_eos: got sync from %s (path:%s)", client->name, client->direction->name); - ircd_log(LOG_ERROR, "[EOSDBG] cmd_eos: broadcasting it back to everyone except route from %s", client->direction->name); -#endif sendto_server(client, 0, 0, NULL, ":%s EOS", client->id); RunHook(HOOKTYPE_SERVER_SYNCED, client); diff --git a/src/modules/extbans/Makefile.in b/src/modules/extbans/Makefile.in index b286cc3..26ea7cf 100644 --- a/src/modules/extbans/Makefile.in +++ b/src/modules/extbans/Makefile.in @@ -25,21 +25,25 @@ INCLUDES = ../../include/channel.h \ ../../include/ircsprintf.h \ ../../include/license.h \ ../../include/modules.h ../../include/modversion.h ../../include/msg.h \ - ../../include/numeric.h ../../include/proto.h ../../include/dns.h \ + ../../include/numeric.h ../../include/dns.h \ ../../include/resource.h ../../include/setup.h \ ../../include/struct.h ../../include/sys.h \ - ../../include/types.h ../../include/url.h \ + ../../include/types.h \ ../../include/version.h ../../include/whowas.h R_MODULES= \ join.so quiet.so nickchange.so inchannel.so realname.so \ account.so operclass.so certfp.so textban.so msgbypass.so \ - timedban.so partmsg.so securitygroup.so + timedban.so partmsg.so securitygroup.so \ + country.so MODULES=$(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ RM=@RM@ +.SUFFIXES: +.SUFFIXES: .c .h .so + all: build build: $(MODULES) @@ -47,62 +51,6 @@ build: $(MODULES) clean: $(RM) -f *.o *.so *~ core -############################################################################# -# .so's section -############################################################################# - -skel.so: skel.c $(INCLUDES) +%.so: %.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o skel.so skel.c - -join.so: join.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o join.so join.c - -quiet.so: quiet.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o quiet.so quiet.c - -nickchange.so: nickchange.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nickchange.so nickchange.c - -inchannel.so: inchannel.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o inchannel.so inchannel.c - -realname.so: realname.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o realname.so realname.c - -account.so: account.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o account.so account.c - -operclass.so: operclass.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o operclass.so operclass.c - -certfp.so: certfp.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o certfp.so certfp.c - -textban.so: textban.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o textban.so textban.c - -msgbypass.so: msgbypass.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o msgbypass.so msgbypass.c - -timedban.so: timedban.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o timedban.so timedban.c - -partmsg.so: partmsg.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o partmsg.so partmsg.c - -securitygroup.so: securitygroup.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o securitygroup.so securitygroup.c + -o $@ $< diff --git a/src/modules/extbans/account.c b/src/modules/extbans/account.c index 3e4877d..d28b0e8 100644 --- a/src/modules/extbans/account.c +++ b/src/modules/extbans/account.c @@ -24,22 +24,25 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~a - Ban/exempt by services account name", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -char *extban_account_conv_param(char *para); -int extban_account_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +const char *extban_account_conv_param(BanContext *b, Extban *extban); +int extban_account_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'a'; + memset(&req, 0, sizeof(req)); + req.letter = 'a'; + req.name = "account"; req.is_ok = NULL; req.conv_param = extban_account_conv_param; req.is_banned = extban_account_is_banned; + req.is_banned_events = BANCHK_ALL|BANCHK_TKL; req.options = EXTBOPT_INVEX|EXTBOPT_TKL; if (!ExtbanAdd(modinfo->handle, req)) { @@ -65,27 +68,34 @@ MOD_UNLOAD() } /** Account bans */ -char *extban_account_conv_param(char *para) +const char *extban_account_conv_param(BanContext *b, Extban *extban) { char *mask, *acc; static char retbuf[NICKLEN + 4]; - strlcpy(retbuf, para, sizeof(retbuf)); /* truncate */ + strlcpy(retbuf, b->banstr, sizeof(retbuf)); /* truncate */ - acc = retbuf+3; + acc = retbuf; if (!*acc) return NULL; /* don't allow "~a:" */ - if (!strcmp(acc, "0")) - return NULL; /* ~a:0 would mean ban all non-regged, but we already have +R for that. */ return retbuf; } -int extban_account_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_account_is_banned(BanContext *b) { - char *ban = banin+3; + /* ~a:0 is special and matches all unauthenticated users */ + if (!strcmp(b->banstr, "0") && !IsLoggedIn(b->client)) + return 1; - if (!strcasecmp(ban, client->user->svid)) + /* ~a:* matches all authenticated users + * (Yes this special code is needed because account + * is 0 or * for unauthenticated users) + */ + if (!strcmp(b->banstr, "*") && IsLoggedIn(b->client)) + return 1; + + if (match_simple(b->banstr, b->client->user->account)) return 1; return 0; diff --git a/src/modules/extbans/certfp.c b/src/modules/extbans/certfp.c index c3f4103..8bb1102 100644 --- a/src/modules/extbans/certfp.c +++ b/src/modules/extbans/certfp.c @@ -24,23 +24,26 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~S - Ban/exempt by SHA256 TLS certificate fingerprint", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -int extban_certfp_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2); -char *extban_certfp_conv_param(char *para); -int extban_certfp_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +int extban_certfp_is_ok(BanContext *b); +const char *extban_certfp_conv_param(BanContext *b, Extban *extban); +int extban_certfp_is_banned(BanContext *b); /* Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'S'; + memset(&req, 0, sizeof(req)); + req.letter = 'S'; + req.name = "certfp"; req.is_ok = extban_certfp_is_ok; req.conv_param = extban_certfp_conv_param; req.is_banned = extban_certfp_is_banned; + req.is_banned_events = BANCHK_ALL|BANCHK_TKL; req.options = EXTBOPT_INVEX|EXTBOPT_TKL; if (!ExtbanAdd(modinfo->handle, req)) { @@ -74,18 +77,18 @@ int extban_certfp_usage(Client *client) return EX_DENY; } -int extban_certfp_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2) +int extban_certfp_is_ok(BanContext *b) { - if (checkt == EXCHK_PARAM) + if (b->is_ok_check == EXCHK_PARAM) { - char *p; + const char *p; - if (strlen(para) != 3 + CERT_FP_LEN) - return extban_certfp_usage(client); + if (strlen(b->banstr) != CERT_FP_LEN) + return extban_certfp_usage(b->client); - for (p = para + 3; *p; p++) + for (p = b->banstr; *p; p++) if (!isxdigit(*p)) - return extban_certfp_usage(client); + return extban_certfp_usage(b->client); return EX_ALLOW; } @@ -93,14 +96,14 @@ int extban_certfp_is_ok(Client *client, Channel *channel, char *para, int checkt } /* Obtain targeted certfp from the ban string */ -char *extban_certfp_conv_param(char *para) +const char *extban_certfp_conv_param(BanContext *b, Extban *extban) { static char retbuf[EVP_MAX_MD_SIZE * 2 + 1]; char *p; - strlcpy(retbuf, para, sizeof(retbuf)); + strlcpy(retbuf, b->banstr, sizeof(retbuf)); - for (p = retbuf+3; *p; p++) + for (p = retbuf; *p; p++) { *p = tolower(*p); } @@ -108,17 +111,14 @@ char *extban_certfp_conv_param(char *para) return retbuf; } -int extban_certfp_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_certfp_is_banned(BanContext *b) { - char *ban = banin+3; - char *fp; - - fp = moddata_client_get(client, "certfp"); + const char *fp = moddata_client_get(b->client, "certfp"); if (!fp) return 0; /* not using TLS */ - if (!strcmp(ban, fp)) + if (!strcmp(b->banstr, fp)) return 1; return 0; diff --git a/src/modules/extbans/country.c b/src/modules/extbans/country.c new file mode 100644 index 0000000..012bfe9 --- /dev/null +++ b/src/modules/extbans/country.c @@ -0,0 +1,124 @@ +/* + * Extended ban to ban/exempt by country/geoip info (+b ~country:UK) + * (C) Copyright 2021 The UnrealIRCd Team + * + * 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. + */ +#include "unrealircd.h" + +ModuleHeader MOD_HEADER += { + "extbans/country", + "6.0", + "ExtBan ~country - Ban/exempt by country (geoip)", + "UnrealIRCd Team", + "unrealircd-6", +}; + +/* Forward declarations */ +int extban_country_is_ok(BanContext *b); +const char *extban_country_conv_param(BanContext *b, Extban *extban); +int extban_country_is_banned(BanContext *b); + +/* Called upon module init */ +MOD_INIT() +{ + ExtbanInfo req; + + memset(&req, 0, sizeof(req)); + req.letter = 'C'; + req.name = "country"; + req.is_ok = extban_country_is_ok; + req.conv_param = extban_country_conv_param; + req.is_banned = extban_country_is_banned; + req.is_banned_events = BANCHK_ALL|BANCHK_TKL; + req.options = EXTBOPT_INVEX|EXTBOPT_TKL; + if (!ExtbanAdd(modinfo->handle, req)) + { + config_error("could not register extended ban type"); + return MOD_FAILED; + } + + MARK_AS_OFFICIAL_MODULE(modinfo); + + return MOD_SUCCESS; +} + +/* Called upon module load */ +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +/* Called upon unload */ +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int extban_country_usage(Client *client) +{ + sendnotice(client, "ERROR: ExtBan ~country expects a two letter country code, or * to ban unknown countries. " + "For example: +b ~country:UK"); + return EX_DENY; +} + +int extban_country_is_ok(BanContext *b) +{ + if (b->is_ok_check == EXCHK_PARAM) + { + const char *p; + + if (!strcmp(b->banstr, "*")) + return EX_ALLOW; + + if ((strlen(b->banstr) != 2)) + return extban_country_usage(b->client); + + for (p = b->banstr; *p; p++) + if (!isalpha(*p)) + return extban_country_usage(b->client); + + return EX_ALLOW; + } + return EX_ALLOW; +} + +/* Obtain targeted country from the ban string */ +const char *extban_country_conv_param(BanContext *b, Extban *extban) +{ + static char retbuf[EVP_MAX_MD_SIZE * 2 + 1]; + char *p; + + strlcpy(retbuf, b->banstr, sizeof(retbuf)); + + for (p = retbuf; *p; p++) + *p = toupper(*p); + + return retbuf; +} + +int extban_country_is_banned(BanContext *b) +{ + GeoIPResult *geo = geoip_client(b->client); + char *country; + + country = geo ? geo->country_code : "*"; + + if (!strcmp(b->banstr, country)) + return 1; + + return 0; +} diff --git a/src/modules/extbans/inchannel.c b/src/modules/extbans/inchannel.c index e8755f4..5d5a3ac 100644 --- a/src/modules/extbans/inchannel.c +++ b/src/modules/extbans/inchannel.c @@ -24,23 +24,26 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~c - banned when in specified channel", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -int extban_inchannel_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2); -char *extban_inchannel_conv_param(char *para); -int extban_inchannel_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +int extban_inchannel_is_ok(BanContext *b); +const char *extban_inchannel_conv_param(BanContext *b, Extban *extban); +int extban_inchannel_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'c'; + memset(&req, 0, sizeof(req)); + req.letter = 'c'; + req.name = "channel"; req.is_ok = extban_inchannel_is_ok; req.conv_param = extban_inchannel_conv_param; req.is_banned = extban_inchannel_is_banned; + req.is_banned_events = BANCHK_ALL|BANCHK_TKL; req.options = EXTBOPT_INVEX; /* for +I too */ if (!ExtbanAdd(modinfo->handle, req)) { @@ -65,13 +68,13 @@ MOD_UNLOAD() return MOD_SUCCESS; } -char *extban_inchannel_conv_param(char *para) +const char *extban_inchannel_conv_param(BanContext *b, Extban *extban) { static char retbuf[CHANNELLEN+6]; char *chan, *p, symbol='\0'; - strlcpy(retbuf, para, sizeof(retbuf)); - chan = retbuf+3; + strlcpy(retbuf, b->banstr, sizeof(retbuf)); + chan = retbuf; if ((*chan == '+') || (*chan == '%') || (*chan == '%') || (*chan == '@') || (*chan == '&') || (*chan == '~')) @@ -92,51 +95,53 @@ char *extban_inchannel_conv_param(char *para) } /* The only purpose of this function is a temporary workaround to prevent a desync.. pfff */ -int extban_inchannel_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2) +int extban_inchannel_is_ok(BanContext *b) { - char *p; + const char *p = b->banstr; - if ((checkt == EXBCHK_PARAM) && MyUser(client) && (what == MODE_ADD) && (strlen(para) > 3)) + if ((b->is_ok_check == EXBCHK_PARAM) && MyUser(b->client) && (b->what == MODE_ADD) && (strlen(b->banstr) > 3)) { - p = para + 3; if ((*p == '+') || (*p == '%') || (*p == '%') || (*p == '@') || (*p == '&') || (*p == '~')) p++; if (*p != '#') { - sendnotice(client, "Please use a # in the channelname (eg: ~c:#*blah*)"); + sendnotice(b->client, "Please use a # in the channelname (eg: ~c:#*blah*)"); return 0; } } return 1; } -static int extban_inchannel_compareflags(char symbol, int flags) +static int extban_inchannel_compareflags(char symbol, const char *member_modes) { - int require=0; + const char *required_modes = NULL; if (symbol == '+') - require = CHFL_VOICE|CHFL_HALFOP|CHFL_CHANOP|CHFL_CHANADMIN|CHFL_CHANOWNER; + required_modes = "vhoaq"; else if (symbol == '%') - require = CHFL_HALFOP|CHFL_CHANOP|CHFL_CHANADMIN|CHFL_CHANOWNER; + required_modes = "hoaq"; else if (symbol == '@') - require = CHFL_CHANOP|CHFL_CHANADMIN|CHFL_CHANOWNER; + required_modes = "oaq"; else if (symbol == '&') - require = CHFL_CHANADMIN|CHFL_CHANOWNER; + required_modes = "aq"; else if (symbol == '~') - require = CHFL_CHANOWNER; + required_modes = "q"; + else + return 0; /* unknown prefix character */ - if (flags & require) + if (check_channel_access_string(member_modes, required_modes)) return 1; return 0; } -int extban_inchannel_is_banned(Client *client, Channel *channel, char *ban, int type, char **msg, char **errmsg) +int extban_inchannel_is_banned(BanContext *b) { Membership *lp; - char *p = ban+3, symbol = '\0'; + const char *p = b->banstr; + char symbol = '\0'; if (*p != '#') { @@ -144,14 +149,14 @@ int extban_inchannel_is_banned(Client *client, Channel *channel, char *ban, int p++; } - for (lp = client->user->channel; lp; lp = lp->next) + for (lp = b->client->user->channel; lp; lp = lp->next) { - if (match_esc(p, lp->channel->chname)) + if (match_esc(p, lp->channel->name)) { /* Channel matched, check symbol if needed (+/%/@/etc) */ if (symbol) { - if (extban_inchannel_compareflags(symbol, lp->flags)) + if (extban_inchannel_compareflags(symbol, lp->member_modes)) return 1; } else return 1; diff --git a/src/modules/extbans/join.c b/src/modules/extbans/join.c index 050b43e..d2ca1ce 100644 --- a/src/modules/extbans/join.c +++ b/src/modules/extbans/join.c @@ -24,21 +24,24 @@ ModuleHeader MOD_HEADER "4.2", "Extban ~j - prevent join only", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -int extban_modej_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +int extban_modej_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'j'; + memset(&req, 0, sizeof(req)); + req.letter = 'j'; + req.name = "join"; req.is_ok = extban_is_ok_nuh_extban; req.conv_param = extban_conv_param_nuh_or_extban; req.is_banned = extban_modej_is_banned; + req.is_banned_events = BANCHK_JOIN; req.options = EXTBOPT_ACTMODIFIER; if (!ExtbanAdd(modinfo->handle, req)) { @@ -64,15 +67,7 @@ MOD_UNLOAD() } /** This ban that affects JOINs only */ -int extban_modej_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_modej_is_banned(BanContext *b) { - char *sub_ban; - - if (type != BANCHK_JOIN) - return 0; - - sub_ban = banin + 3; - - return ban_check_mask(client, channel, sub_ban, type, msg, errmsg, 0); + return ban_check_mask(b); } - diff --git a/src/modules/extbans/msgbypass.c b/src/modules/extbans/msgbypass.c index 167bf64..ef0b6fc 100644 --- a/src/modules/extbans/msgbypass.c +++ b/src/modules/extbans/msgbypass.c @@ -24,24 +24,24 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~m - bypass +m/+n/+c/+S/+T (msgbypass)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -int extban_msgbypass_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); int msgbypass_can_bypass(Client *client, Channel *channel, BypassChannelMessageRestrictionType bypass_type); -int msgbypass_extban_is_ok(Client *client, Channel* channel, char *para, int checkt, int what, int what2); -char *msgbypass_extban_conv_param(char *para); +int msgbypass_extban_is_ok(BanContext *b); +const char *msgbypass_extban_conv_param(BanContext *b, Extban *extban); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'm'; + memset(&req, 0, sizeof(req)); + req.letter = 'm'; + req.name = "msgbypass"; req.is_ok = msgbypass_extban_is_ok; req.conv_param = msgbypass_extban_conv_param; - req.is_banned = extban_msgbypass_is_banned; req.options = EXTBOPT_ACTMODIFIER; if (!ExtbanAdd(modinfo->handle, req)) { @@ -67,42 +67,50 @@ MOD_UNLOAD() return MOD_SUCCESS; } -/** Is the user banned? No, never by us anyway. */ -int extban_msgbypass_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) -{ - return 0; /* not banned by us */ -} - /** Can the user bypass restrictions? */ int msgbypass_can_bypass(Client *client, Channel *channel, BypassChannelMessageRestrictionType bypass_type) { Ban *ban; char *p; - + BanContext *b = safe_alloc(sizeof(BanContext)); + + b->client = client; + b->channel = channel; + b->ban_check_types = BANCHK_MSG; + for (ban = channel->exlist; ban; ban=ban->next) { + char *type; + char *matchby; + if (!strncmp(ban->banstr, "~m:", 3)) + type = ban->banstr + 3; + else if (!strncmp(ban->banstr, "~msgbypass:", 11)) + type = ban->banstr + 11; + else + continue; + + if (((bypass_type == BYPASS_CHANMSG_EXTERNAL) && !strncmp(type, "external:", 9)) || + ((bypass_type == BYPASS_CHANMSG_MODERATED) && !strncmp(type, "moderated:", 10)) || + ((bypass_type == BYPASS_CHANMSG_COLOR) && !strncmp(type, "color:", 6)) || + ((bypass_type == BYPASS_CHANMSG_CENSOR) && !strncmp(type, "censor:", 7)) || + ((bypass_type == BYPASS_CHANMSG_NOTICE) && !strncmp(type, "notice:", 7))) { - char *type = ban->banstr + 3; - char *matchby; + matchby = strchr(type, ':'); + if (!matchby) + continue; + matchby++; - if (((bypass_type == BYPASS_CHANMSG_EXTERNAL) && !strncmp(type, "external:", 9)) || - ((bypass_type == BYPASS_CHANMSG_MODERATED) && !strncmp(type, "moderated:", 10)) || - ((bypass_type == BYPASS_CHANMSG_COLOR) && !strncmp(type, "color:", 6)) || - ((bypass_type == BYPASS_CHANMSG_CENSOR) && !strncmp(type, "censor:", 7)) || - ((bypass_type == BYPASS_CHANMSG_NOTICE) && !strncmp(type, "notice:", 7))) + b->banstr = matchby; + if (ban_check_mask(b)) { - matchby = strchr(type, ':'); - if (!matchby) - continue; - matchby++; - - if (ban_check_mask(client, channel, matchby, BANCHK_MSG, NULL, NULL, 0)) - return HOOK_ALLOW; /* Yes, user may bypass */ + safe_free(b); + return HOOK_ALLOW; /* Yes, user may bypass */ } } } + safe_free(b); return HOOK_CONTINUE; /* No, may NOT bypass. */ } @@ -121,16 +129,16 @@ int msgbypass_extban_type_ok(char *type) } #define MAX_LENGTH 128 -char *msgbypass_extban_conv_param(char *para_in) +const char *msgbypass_extban_conv_param(BanContext *b, Extban *extban) { static char retbuf[MAX_LENGTH+1]; char para[MAX_LENGTH+1]; char tmpmask[MAX_LENGTH+1]; char *type; /**< Type, such as 'external' */ char *matchby; /**< Matching method, such as 'n!u@h' */ - char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ + const char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ - strlcpy(para, para_in+3, sizeof(para)); /* work on a copy (and truncate it) */ + strlcpy(para, b->banstr, sizeof(para)); /* work on a copy (and truncate it) */ /* ~m:type:n!u@h for direct matching * ~m:type:~x:.... when calling another bantype @@ -145,16 +153,13 @@ char *msgbypass_extban_conv_param(char *para_in) if (!msgbypass_extban_type_ok(type)) return NULL; - /* This is quite silly, we have to create a fake extban here due to - * the current API of extban_conv_param_nuh and extban_conv_param_nuh_or_extban - * expecting the full banmask rather than the portion that actually matters. - */ - snprintf(tmpmask, sizeof(tmpmask), "~?:%s", matchby); - newmask = extban_conv_param_nuh_or_extban(tmpmask); - if (!newmask || (strlen(newmask) <= 3)) + b->banstr = matchby; + newmask = extban_conv_param_nuh_or_extban(b, extban); + if (BadPtr(newmask)) return NULL; - snprintf(retbuf, sizeof(retbuf), "~m:%s:%s", type, newmask+3); + //snprintf(retbuf, sizeof(retbuf), "~m:%s:%s", type, newmask); + snprintf(retbuf, sizeof(retbuf), "%s:%s", type, newmask); return retbuf; } @@ -171,26 +176,25 @@ int msgbypass_extban_syntax(Client *client, int checkt, char *reason) return 0; /* FAIL: ban rejected */ } -int msgbypass_extban_is_ok(Client *client, Channel* channel, char *para_in, int checkt, int what, int what2) +int msgbypass_extban_is_ok(BanContext *b) { - char para[MAX_LENGTH+1]; - char tmpmask[MAX_LENGTH+1]; + static char para[MAX_LENGTH+1]; char *type; /**< Type, such as 'external' */ char *matchby; /**< Matching method, such as 'n!u@h' */ char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ /* Always permit deletion */ - if (what == MODE_DEL) + if (b->what == MODE_DEL) return 1; - if (what2 != EXBTYPE_EXCEPT) + if (b->ban_type != EXBTYPE_EXCEPT) { - if (checkt == EXBCHK_PARAM) - sendnotice(client, "Ban type ~m only works with exceptions (+e) and not with bans or invex (+b/+I)"); + if (b->is_ok_check == EXBCHK_PARAM) + sendnotice(b->client, "Ban type ~m only works with exceptions (+e) and not with bans or invex (+b/+I)"); return 0; /* reject */ } - strlcpy(para, para_in+3, sizeof(para)); /* work on a copy (and truncate it) */ + strlcpy(para, b->banstr, sizeof(para)); /* work on a copy (and truncate it) */ /* ~m:type:n!u@h for direct matching * ~m:type:~x:.... when calling another bantype @@ -199,24 +203,20 @@ int msgbypass_extban_is_ok(Client *client, Channel* channel, char *para_in, int type = para; matchby = strchr(para, ':'); if (!matchby || !matchby[1]) - return msgbypass_extban_syntax(client, checkt, "Invalid syntax"); + return msgbypass_extban_syntax(b->client, b->is_ok_check, "Invalid syntax"); *matchby++ = '\0'; if (!msgbypass_extban_type_ok(type)) - return msgbypass_extban_syntax(client, checkt, "Unknown type"); + return msgbypass_extban_syntax(b->client, b->is_ok_check, "Unknown type"); - /* This is quite silly, we have to create a fake extban here due to - * the current API of extban_conv_param_nuh and extban_conv_param_nuh_or_extban - * expecting the full banmask rather than the portion that actually matters. - */ - snprintf(tmpmask, sizeof(tmpmask), "~?:%s", matchby); - if (extban_is_ok_nuh_extban(client, channel, tmpmask, checkt, what, what2) == 0) + b->banstr = matchby; + if (extban_is_ok_nuh_extban(b) == 0) { /* This could be anything ranging from: * invalid n!u@h syntax, unknown (sub)extbantype, * disabled extban type in conf, too much recursion, etc. */ - return msgbypass_extban_syntax(client, checkt, "Invalid matcher"); + return msgbypass_extban_syntax(b->client, b->is_ok_check, "Invalid matcher"); } return 1; /* OK */ diff --git a/src/modules/extbans/nickchange.c b/src/modules/extbans/nickchange.c index 164fbc0..bc8769f 100644 --- a/src/modules/extbans/nickchange.c +++ b/src/modules/extbans/nickchange.c @@ -24,21 +24,24 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~n - prevent nick-changes only", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -int extban_nickchange_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +int extban_nickchange_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'n'; + memset(&req, 0, sizeof(req)); + req.letter = 'n'; + req.name = "nickchange"; req.is_ok = extban_is_ok_nuh_extban; req.conv_param = extban_conv_param_nuh_or_extban; req.is_banned = extban_nickchange_is_banned; + req.is_banned_events = BANCHK_NICK; req.options = EXTBOPT_ACTMODIFIER; if (!ExtbanAdd(modinfo->handle, req)) { @@ -64,18 +67,10 @@ MOD_UNLOAD() } /** This ban that affects nick-changes only */ -int extban_nickchange_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_nickchange_is_banned(BanContext *b) { - char *sub_ban; - - if (type != BANCHK_NICK) + if (check_channel_access(b->client, b->channel, "v")) return 0; - if (has_voice(client, channel)) - return 0; - - sub_ban = banin + 3; - - return ban_check_mask(client, channel, sub_ban, type, msg, errmsg, 0); + return ban_check_mask(b); } - diff --git a/src/modules/extbans/operclass.c b/src/modules/extbans/operclass.c index b5469cc..5b6bc2a 100644 --- a/src/modules/extbans/operclass.c +++ b/src/modules/extbans/operclass.c @@ -24,22 +24,25 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~O - Ban/exempt operclass", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -char *extban_operclass_conv_param(char *para); -int extban_operclass_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +const char *extban_operclass_conv_param(BanContext *b, Extban *extban); +int extban_operclass_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'O'; + memset(&req, 0, sizeof(req)); + req.letter = 'O'; + req.name = "operclass"; req.is_ok = NULL; req.conv_param = extban_operclass_conv_param; req.is_banned = extban_operclass_is_banned; + req.is_banned_events = BANCHK_ALL; req.options = EXTBOPT_INVEX; if (!ExtbanAdd(modinfo->handle, req)) { @@ -67,15 +70,15 @@ MOD_UNLOAD() #define OPERCLASSLEN 64 -char *extban_operclass_conv_param(char *para) +const char *extban_operclass_conv_param(BanContext *b, Extban *extban) { static char retbuf[OPERCLASSLEN + 4]; char *p; - strlcpy(retbuf, para, sizeof(retbuf)); + strlcpy(retbuf, b->banstr, sizeof(retbuf)); /* allow alpha, numeric, -, _, * and ? wildcards */ - for (p = retbuf+3; *p; p++) + for (p = retbuf; *p; p++) if (!strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_?*", *p)) *p = '\0'; @@ -85,18 +88,12 @@ char *extban_operclass_conv_param(char *para) return retbuf; } -int extban_operclass_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_operclass_is_banned(BanContext *b) { - char *ban = banin+3; - - if (MyUser(client) && IsOper(client)) + if (MyUser(b->client) && IsOper(b->client)) { - char *operclass = NULL; - ConfigItem_oper *oper = find_oper(client->user->operlogin); - if (oper && oper->operclass) - operclass = oper->operclass; - - if (operclass && match_simple(ban, operclass)) + const char *operclass = get_operclass(b->client); + if (operclass && match_simple(b->banstr, operclass)) return 1; } diff --git a/src/modules/extbans/partmsg.c b/src/modules/extbans/partmsg.c index 10c3651..a6402d5 100644 --- a/src/modules/extbans/partmsg.c +++ b/src/modules/extbans/partmsg.c @@ -25,20 +25,23 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~p - Ban/exempt Part/Quit message", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -int extban_partmsg_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +int extban_partmsg_is_banned(BanContext *b); MOD_INIT() { ExtbanInfo req; - req.flag = 'p'; + memset(&req, 0, sizeof(req)); + req.letter = 'p'; + req.name = "partmsg"; req.is_ok = extban_is_ok_nuh_extban; req.conv_param = extban_conv_param_nuh_or_extban; req.options = EXTBOPT_ACTMODIFIER; req.is_banned = extban_partmsg_is_banned; + req.is_banned_events = BANCHK_LEAVE_MSG; if (!ExtbanAdd(modinfo->handle, req)) { config_error("could not register extended ban type"); @@ -62,10 +65,10 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int extban_partmsg_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_partmsg_is_banned(BanContext *b) { - if (type == BANCHK_LEAVE_MSG) - *msg = NULL; + b->msg = NULL; + // Uh.. there is no attempt to match.... anything.......? return 0; } diff --git a/src/modules/extbans/quiet.c b/src/modules/extbans/quiet.c index 2f7d787..5d3dfdf 100644 --- a/src/modules/extbans/quiet.c +++ b/src/modules/extbans/quiet.c @@ -24,21 +24,24 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~q - prevent messages only (quiet)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -int extban_quiet_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +int extban_quiet_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'q'; + memset(&req, 0, sizeof(req)); + req.letter = 'q'; + req.name = "quiet"; req.is_ok = extban_is_ok_nuh_extban; req.conv_param = extban_conv_param_nuh_or_extban; req.is_banned = extban_quiet_is_banned; + req.is_banned_events = BANCHK_MSG; req.options = EXTBOPT_ACTMODIFIER; if (!ExtbanAdd(modinfo->handle, req)) { @@ -64,14 +67,7 @@ MOD_UNLOAD() } /** This ban that affects messages/notices only */ -int extban_quiet_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_quiet_is_banned(BanContext *b) { - char *sub_ban; - - if (type != BANCHK_MSG) - return 0; - - sub_ban = banin + 3; - - return ban_check_mask(client, channel, sub_ban, type, msg, errmsg, 0); + return ban_check_mask(b); } diff --git a/src/modules/extbans/realname.c b/src/modules/extbans/realname.c index 8181716..074948e 100644 --- a/src/modules/extbans/realname.c +++ b/src/modules/extbans/realname.c @@ -24,22 +24,25 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~r - Ban based on realname/gecos field", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -char *extban_realname_conv_param(char *para); -int extban_realname_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +const char *extban_realname_conv_param(BanContext *b, Extban *extban); +int extban_realname_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - req.flag = 'r'; + memset(&req, 0, sizeof(req)); + req.letter = 'r'; + req.name = "realname"; req.is_ok = NULL; 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; if (!ExtbanAdd(modinfo->handle, req)) { @@ -65,32 +68,31 @@ MOD_UNLOAD() } /** Realname bans - conv_param */ -char *extban_realname_conv_param(char *para) +const char *extban_realname_conv_param(BanContext *b, Extban *extban) { static char retbuf[REALLEN + 8]; char *mask; - strlcpy(retbuf, para, sizeof(retbuf)); + strlcpy(retbuf, b->banstr, sizeof(retbuf)); - mask = retbuf+3; + mask = retbuf; if (!*mask) return NULL; /* don't allow "~r:" */ - if (strlen(mask) > REALLEN + 3) - mask[REALLEN + 3] = '\0'; + if (strlen(mask) > REALLEN) + mask[REALLEN] = '\0'; + /* Prevent otherwise confusing extban relationship */ if (*mask == '~') - *mask = '?'; /* Is this good? No ;) */ + *mask = '?'; return retbuf; } -int extban_realname_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_realname_is_banned(BanContext *b) { - char *ban = banin+3; - - if (match_esc(ban, client->info)) + if (match_esc(b->banstr, b->client->info)) return 1; return 0; diff --git a/src/modules/extbans/securitygroup.c b/src/modules/extbans/securitygroup.c index e45782e..ac412f2 100644 --- a/src/modules/extbans/securitygroup.c +++ b/src/modules/extbans/securitygroup.c @@ -24,23 +24,26 @@ ModuleHeader MOD_HEADER "4.2", "ExtBan ~G - Ban based on security-group", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -char *extban_securitygroup_conv_param(char *para); -int extban_securitygroup_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2); -int extban_securitygroup_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg); +const char *extban_securitygroup_conv_param(BanContext *b, Extban *extban); +int extban_securitygroup_is_ok(BanContext *b); +int extban_securitygroup_is_banned(BanContext *b); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; - - req.flag = 'G'; + + memset(&req, 0, sizeof(req)); + req.letter = 'G'; + req.name = "security-group"; req.conv_param = extban_securitygroup_conv_param; req.is_ok = extban_securitygroup_is_ok; req.is_banned = extban_securitygroup_is_banned; + req.is_banned_events = BANCHK_ALL|BANCHK_TKL; req.options = EXTBOPT_INVEX|EXTBOPT_TKL; if (!ExtbanAdd(modinfo->handle, req)) { @@ -68,12 +71,8 @@ MOD_UNLOAD() /* Helper function for extban_securitygroup_is_ok() and extban_securitygroup_conv_param() * to do ban validation. */ -int extban_securitygroup_generic(char *para, int strict) +int extban_securitygroup_generic(char *mask, int strict) { - char *mask; - - mask = para+3; - /* ! at the start means negative match */ if (*mask == '!') mask++; @@ -91,27 +90,24 @@ int extban_securitygroup_generic(char *para, int strict) if (!*mask) return 0; /* don't allow "~G:" nor "~G:!" */ - if (strlen(mask) > SECURITYGROUPLEN + 3) - mask[SECURITYGROUPLEN + 3] = '\0'; - return 1; } -int extban_securitygroup_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2) +int extban_securitygroup_is_ok(BanContext *b) { - if (MyUser(client) && (what == MODE_ADD) && (checkt == EXBCHK_PARAM)) + if (MyUser(b->client) && (b->what == MODE_ADD) && (b->is_ok_check == EXBCHK_PARAM)) { char banbuf[SECURITYGROUPLEN+8]; - strlcpy(banbuf, para, sizeof(banbuf)); + strlcpy(banbuf, b->banstr, sizeof(banbuf)); if (!extban_securitygroup_generic(banbuf, 1)) { SecurityGroup *s; - sendnotice(client, "ERROR: Unknown security-group '%s'. Syntax: +b ~G:securitygroup or +b ~G:!securitygroup", para+3); - sendnotice(client, "Available security groups:"); + sendnotice(b->client, "ERROR: Unknown security-group '%s'. Syntax: +b ~G:securitygroup or +b ~G:!securitygroup", b->banstr); + sendnotice(b->client, "Available security groups:"); for (s = securitygroups; s; s = s->next) - sendnotice(client, "%s", s->name); - sendnotice(client, "unknown-users"); - sendnotice(client, "End of security group list."); + sendnotice(b->client, "%s", s->name); + sendnotice(b->client, "unknown-users"); + sendnotice(b->client, "End of security group list."); return 0; } } @@ -119,11 +115,11 @@ int extban_securitygroup_is_ok(Client *client, Channel *channel, char *para, int } /** Security group extban - conv_param */ -char *extban_securitygroup_conv_param(char *para) +const char *extban_securitygroup_conv_param(BanContext *b, Extban *extban) { static char retbuf[SECURITYGROUPLEN + 8]; - strlcpy(retbuf, para, sizeof(retbuf)); + strlcpy(retbuf, b->banstr, sizeof(retbuf)); if (!extban_securitygroup_generic(retbuf, 0)) return NULL; @@ -131,11 +127,9 @@ char *extban_securitygroup_conv_param(char *para) } /** Is the user banned by ~G:something ? */ -int extban_securitygroup_is_banned(Client *client, Channel *channel, char *banin, int type, char **msg, char **errmsg) +int extban_securitygroup_is_banned(BanContext *b) { - char *ban = banin+3; - - if (*ban == '!') - return !user_allowed_by_security_group_name(client, ban+1); - return user_allowed_by_security_group_name(client, ban); + if (*b->banstr == '!') + return !user_allowed_by_security_group_name(b->client, b->banstr+1); + return user_allowed_by_security_group_name(b->client, b->banstr); } diff --git a/src/modules/extbans/textban.c b/src/modules/extbans/textban.c index 297f0ca..cc241f5 100644 --- a/src/modules/extbans/textban.c +++ b/src/modules/extbans/textban.c @@ -61,30 +61,20 @@ /** Which censor replace word to use when CENSORFEATURE is enabled. */ #define CENSORWORD "" -/** Benchmark mode. - * Should never be used on production servers. - * Mainly meant for debugging/profiling purposes for myself, but if you - * have a test server and are curious about the speed of this module, - * then you can enable it of course ;). - */ -#undef BENCHMARK - - ModuleHeader MOD_HEADER = { "extbans/textban", "2.2", "ExtBan ~T (textban) by Syzop", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -char *extban_modeT_conv_param(char *para_in); -int textban_check_ban(Client *client, Channel *channel, char *ban, char **msg, char **errmsg); -int textban_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -int extban_modeT_is_banned(Client *client, Channel *channel, char *ban, int type, char **msg, char **errmsg); -int extban_modeT_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2); +const char *extban_modeT_conv_param(BanContext *b, Extban *extban); +int textban_check_ban(Client *client, Channel *channel, const char *ban, const char **msg, const char **errmsg); +int textban_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +int extban_modeT_is_ok(BanContext *b); void parse_word(const char *s, char **word, int *type); MOD_INIT() @@ -94,10 +84,10 @@ MOD_INIT() MARK_AS_OFFICIAL_MODULE(modinfo); memset(&req, 0, sizeof(ExtbanInfo)); - req.flag = 'T'; + req.letter = 'T'; + req.name = "text"; req.options = EXTBOPT_NOSTACKCHILD; /* disallow things like ~n:~T, as we only affect text. */ req.conv_param = extban_modeT_conv_param; - req.is_banned = extban_modeT_is_banned; req.is_ok = extban_modeT_is_ok; if (!ExtbanAdd(modinfo->handle, req)) @@ -262,21 +252,21 @@ unsigned int counttextbans(Channel *channel) } -int extban_modeT_is_ok(Client *client, Channel *channel, char *para, int checkt, int what, int what2) +int extban_modeT_is_ok(BanContext *b) { int n; - if ((what == MODE_ADD) && (what2 == EXBTYPE_EXCEPT) && MyUser(client)) + if ((b->what == MODE_ADD) && (b->ban_type == EXBTYPE_EXCEPT) && MyUser(b->client)) return 0; /* except is not supported */ /* We check the # of bans in the channel, may not exceed MAX_EXTBANT_PER_CHAN */ - if ((what == MODE_ADD) && (checkt == EXBCHK_PARAM) && - MyUser(client) && !IsOper(client) && - ((n = counttextbans(channel)) >= MAX_EXTBANT_PER_CHAN)) + if ((b->what == MODE_ADD) && (b->is_ok_check == EXBCHK_PARAM) && + MyUser(b->client) && !IsOper(b->client) && + ((n = counttextbans(b->channel)) >= MAX_EXTBANT_PER_CHAN)) { /* We check the # of bans in the channel, may not exceed MAX_EXTBANT_PER_CHAN */ - sendnumeric(client, ERR_BANLISTFULL, channel->chname, para); - sendnotice(client, "Too many textbans for this channel"); + sendnumeric(b->client, ERR_BANLISTFULL, b->channel->name, b->banstr); // FIXME: wants b->full_banstr here + sendnotice(b->client, "Too many textbans for this channel"); return 0; } return 1; @@ -298,7 +288,7 @@ char *conv_pattern_asterisks(const char *pattern) } /** Ban callbacks */ -char *extban_modeT_conv_param(char *para_in) +const char *extban_modeT_conv_param(BanContext *b, Extban *extban) { static char retbuf[MAX_LENGTH+1]; char para[MAX_LENGTH+1], *action, *text, *p; @@ -307,7 +297,7 @@ char *extban_modeT_conv_param(char *para_in) int ap = 0; #endif - strlcpy(para, para_in+3, sizeof(para)); /* work on a copy (and truncate it) */ + strlcpy(para, b->banstr, sizeof(para)); /* work on a copy (and truncate it) */ /* ~T:: * ~T:user@host:: if UHOSTFEATURE is enabled @@ -380,26 +370,20 @@ char *extban_modeT_conv_param(char *para_in) /* Rebuild the string.. can be cut off if too long. */ #ifdef UHOSTFEATURE - snprintf(retbuf, sizeof(retbuf), "~T:%s:%s:%s", uhost, action, text); + snprintf(retbuf, sizeof(retbuf), "%s:%s:%s", uhost, action, text); #else - snprintf(retbuf, sizeof(retbuf), "~T:%s:%s", action, text); + snprintf(retbuf, sizeof(retbuf), "%s:%s", action, text); #endif return retbuf; } -/** This is the regular "is banned?" routine. We can't use this as we need to be called for voiced users as well */ -int extban_modeT_is_banned(Client *client, Channel *channel, char *ban, int checktype, char **msg, char **errmsg) -{ - return 0; -} - /** Check for text bans (censor and block) */ -int textban_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int textban_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { Ban *ban; /* +h/+o/+a/+q users bypass textbans */ - if (is_skochanop(client, channel)) + if (check_channel_access(client, channel, "hoaq")) return HOOK_CONTINUE; /* IRCOps with these privileges bypass textbans too */ @@ -409,21 +393,29 @@ int textban_can_send_to_channel(Client *client, Channel *channel, Membership *lp /* Now we have to manually walk the banlist and check if things match */ for (ban = channel->banlist; ban; ban=ban->next) { - if (!strncmp(ban->banstr, "~T:", 3)) + char *banstr = ban->banstr; + + /* Pretend time does not exist... */ + if (!strncmp(banstr, "~t:", 3)) { - /* ~T ban */ - if (textban_check_ban(client, channel, ban->banstr, msg, errmsg)) + banstr = strchr(banstr+3, ':'); + if (!banstr) + continue; + banstr++; + } + else if (!strncmp(banstr, "~time:", 6)) + { + banstr = strchr(banstr+6, ':'); + if (!banstr) + continue; + banstr++; + } + + if (!strncmp(banstr, "~T:", 3) || !strncmp(banstr, "~text:", 6)) + { + /* text ban */ + if (textban_check_ban(client, channel, banstr, msg, errmsg)) return HOOK_DENY; - } else - if (!strncmp(ban->banstr, "~t:", 3)) - { - /* Stacked ~t:xx:~T ban (timed text ban) */ - char *p = strchr(ban->banstr+3, ':'); - if (p && !strncmp(p+1, "~T:", 3)) - { - if (textban_check_ban(client, channel, p+1, msg, errmsg)) - return HOOK_DENY; - } } } @@ -431,23 +423,18 @@ int textban_can_send_to_channel(Client *client, Channel *channel, Membership *lp } -int textban_check_ban(Client *client, Channel *channel, char *ban, char **msg, char **errmsg) +int textban_check_ban(Client *client, Channel *channel, const char *ban, const char **msg, const char **errmsg) { static char retbuf[512]; char filtered[512]; /* temp input buffer */ long fl; int cleaned=0; - char *p; + const char *p; #ifdef UHOSTFEATURE char buf[512], uhost[USERLEN + HOSTLEN + 16]; #endif char tmp[1024], *word; int type; -#ifdef BENCHMARK - struct timeval tv_alpha, tv_beta; - - gettimeofday(&tv_alpha, NULL); -#endif /* We can only filter on non-NULL text of course */ if ((msg == NULL) || (*msg == NULL)) @@ -460,7 +447,10 @@ int textban_check_ban(Client *client, Channel *channel, char *ban, char **msg, c #endif strlcpy(filtered, StripControlCodes(*msg), sizeof(filtered)); - p = ban + 3; + p = strchr(ban, ':'); + if (!p) + return 0; /* "impossible" */ + p++; #ifdef UHOSTFEATURE /* First.. deal with userhost... */ strcpy(buf, p); @@ -496,13 +486,6 @@ int textban_check_ban(Client *client, Channel *channel, char *ban, char **msg, c #endif } -#ifdef BENCHMARK - gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "TextBan Timing: %ld microseconds (%s / %s / %d)", - ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec), - client->name, channel->chname, strlen(*msg)); -#endif - if (cleaned) { /* check for null string */ diff --git a/src/modules/extbans/timedban.c b/src/modules/extbans/timedban.c index 1b5f130..5ec418c 100644 --- a/src/modules/extbans/timedban.c +++ b/src/modules/extbans/timedban.c @@ -51,13 +51,13 @@ ModuleHeader MOD_HEADER "1.0", "ExtBan ~t: automatically removed timed bans", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -char *timedban_extban_conv_param(char *para_in); -int timedban_extban_is_ok(Client *client, Channel* channel, char *para_in, int checkt, int what, int what2); -int timedban_is_banned(Client *client, Channel *channel, char *ban, int chktype, char **msg, char **errmsg); +const char *timedban_extban_conv_param(BanContext *b, Extban *extban); +int timedban_extban_is_ok(BanContext *b); +int timedban_is_banned(BanContext *b); void add_send_mode_param(Channel *channel, Client *from, char what, char mode, char *param); char *timedban_chanmsg(Client *, Client *, Channel *, char *, int); @@ -70,18 +70,20 @@ MOD_TEST() MOD_INIT() { -ExtbanInfo extban; + ExtbanInfo extban; MARK_AS_OFFICIAL_MODULE(modinfo); memset(&extban, 0, sizeof(ExtbanInfo)); - extban.flag = 't'; + 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; extban.is_banned = timedban_is_banned; + extban.is_banned_events = BANCHK_ALL; if (!ExtbanAdd(modinfo->handle, extban)) { @@ -106,17 +108,18 @@ MOD_UNLOAD() /** Generic helper for our conv_param extban function. * Mostly copied from clean_ban_mask() + * FIXME: Figure out why we have this one at all and not use conv_param? ;) */ -char *generic_clean_ban_mask(char *mask) +const char *generic_clean_ban_mask(BanContext *b, Extban *extban) { char *cp, *x; char *user; char *host; - Extban *p; static char maskbuf[512]; + char *mask; /* Work on a copy */ - strlcpy(maskbuf, mask, sizeof(maskbuf)); + strlcpy(maskbuf, b->banstr, sizeof(maskbuf)); mask = maskbuf; cp = strchr(mask, ' '); @@ -136,11 +139,22 @@ char *generic_clean_ban_mask(char *mask) /* Extended ban? */ if (is_extended_ban(mask)) { - p = findmod_by_bantype(mask[1]); - if (!p) + const char *nextbanstr; + Extban *extban = findmod_by_bantype(mask, &nextbanstr); + if (!extban) return NULL; /* reject unknown extban */ - if (p->conv_param) - return p->conv_param(mask); + if (extban->conv_param) + { + const char *ret; + static char retbuf[512]; + BanContext *newb = safe_alloc(sizeof(BanContext)); + newb->banstr = nextbanstr; + newb->conv_options = b->conv_options; + ret = extban->conv_param(newb, extban); + ret = prefix_with_extban(ret, newb, extban, retbuf, sizeof(retbuf)); + safe_free(newb); + return ret; + } /* else, do some basic sanity checks and cut it off at 80 bytes */ if ((mask[1] != ':') || (mask[2] == '\0')) return NULL; /* require a ":" after extban type */ @@ -169,7 +183,7 @@ char *generic_clean_ban_mask(char *mask) } /** Convert ban to an acceptable format (or return NULL to fully reject it) */ -char *timedban_extban_conv_param(char *para_in) +const char *timedban_extban_conv_param(BanContext *b, Extban *extban) { static char retbuf[MAX_LENGTH+1]; char para[MAX_LENGTH+1]; @@ -177,13 +191,13 @@ char *timedban_extban_conv_param(char *para_in) char *durationstr; /**< Duration, such as '5' */ int duration; char *matchby; /**< Matching method, such as 'n!u@h' */ - char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ + const char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ static int timedban_extban_conv_param_recursion = 0; if (timedban_extban_conv_param_recursion) return NULL; /* reject: recursion detected! */ - strlcpy(para, para_in+3, sizeof(para)); /* work on a copy (and truncate it) */ + strlcpy(para, b->banstr, sizeof(para)); /* work on a copy (and truncate it) */ /* ~t:duration:n!u@h for direct matching * ~t:duration:~x:.... when calling another bantype @@ -203,12 +217,14 @@ char *timedban_extban_conv_param(char *para_in) strlcpy(tmpmask, matchby, sizeof(tmpmask)); timedban_extban_conv_param_recursion++; //newmask = extban_conv_param_nuh_or_extban(tmpmask); - newmask = generic_clean_ban_mask(tmpmask); + b->banstr = matchby; // this was previously 'tmpmask' but then it's a copy-copy-copy.. :D + newmask = generic_clean_ban_mask(b, extban); timedban_extban_conv_param_recursion--; if (!newmask || (strlen(newmask) <= 1)) return NULL; - snprintf(retbuf, sizeof(retbuf), "~t:%d:%s", duration, newmask); + //snprintf(retbuf, sizeof(retbuf), "~t:%d:%s", duration, newmask); + snprintf(retbuf, sizeof(retbuf), "%d:%s", duration, newmask); return retbuf; } @@ -226,54 +242,50 @@ int timedban_extban_syntax(Client *client, int checkt, char *reason) } /** Generic helper for sub-bans, used by our "is this ban ok?" function */ -int generic_ban_is_ok(Client *client, Channel *channel, char *mask, int checkt, int what, int what2) +int generic_ban_is_ok(BanContext *b) { - if ((mask[0] == '~') && MyUser(client)) + if ((b->banstr[0] == '~') && MyUser(b->client)) { - Extban *p; + Extban *extban; + const char *nextbanstr; /* This portion is copied from clean_ban_mask() */ - if (is_extended_ban(mask) && MyUser(client)) + if (is_extended_ban(b->banstr) && MyUser(b->client)) { - if (RESTRICT_EXTENDEDBANS && !ValidatePermissionsForPath("immune:restrict-extendedbans",client,NULL,NULL,NULL)) + if (RESTRICT_EXTENDEDBANS && !ValidatePermissionsForPath("immune:restrict-extendedbans",b->client,NULL,NULL,NULL)) { if (!strcmp(RESTRICT_EXTENDEDBANS, "*")) { - if (checkt == EXBCHK_ACCESS_ERR) - sendnotice(client, "Setting/removing of extended bans has been disabled"); + if (b->is_ok_check == EXBCHK_ACCESS_ERR) + sendnotice(b->client, "Setting/removing of extended bans has been disabled"); return 0; /* REJECT */ } - if (strchr(RESTRICT_EXTENDEDBANS, mask[1])) + if (strchr(RESTRICT_EXTENDEDBANS, b->banstr[1])) { - if (checkt == EXBCHK_ACCESS_ERR) - sendnotice(client, "Setting/removing of extended bantypes '%s' has been disabled", RESTRICT_EXTENDEDBANS); + if (b->is_ok_check == EXBCHK_ACCESS_ERR) + sendnotice(b->client, "Setting/removing of extended bantypes '%s' has been disabled", RESTRICT_EXTENDEDBANS); return 0; /* REJECT */ } } /* And next is inspired by cmd_mode */ - p = findmod_by_bantype(mask[1]); - if (checkt == EXBCHK_ACCESS) + extban = findmod_by_bantype(b->banstr, &nextbanstr); + if (extban && extban->is_ok) { - if (p && p->is_ok && !p->is_ok(client, channel, mask, EXBCHK_ACCESS, what, what2) && - !ValidatePermissionsForPath("channel:override:mode:extban",client,NULL,channel,NULL)) + b->banstr = nextbanstr; + if ((b->is_ok_check == EXBCHK_ACCESS) || (b->is_ok_check == EXBCHK_ACCESS_ERR)) { - return 0; /* REJECT */ - } - } else - if (checkt == EXBCHK_ACCESS_ERR) - { - if (p && p->is_ok && !p->is_ok(client, channel, mask, EXBCHK_ACCESS, what, what2) && - !ValidatePermissionsForPath("channel:override:mode:extban",client,NULL,channel,NULL)) + if (!extban->is_ok(b) && + !ValidatePermissionsForPath("channel:override:mode:extban",b->client,NULL,b->channel,NULL)) + { + return 0; /* REJECT */ + } + } else + if (b->is_ok_check == EXBCHK_PARAM) { - p->is_ok(client, channel, mask, EXBCHK_ACCESS_ERR, what, what2); - return 0; /* REJECT */ - } - } else - if (checkt == EXBCHK_PARAM) - { - if (p && p->is_ok && !p->is_ok(client, channel, mask, EXBCHK_PARAM, what, what2)) - { - return 0; /* REJECT */ + if (!extban->is_ok(b)) + { + return 0; /* REJECT */ + } } } } @@ -288,7 +300,7 @@ int generic_ban_is_ok(Client *client, Channel *channel, char *mask, int checkt, } /** Validate ban ("is this ban ok?") */ -int timedban_extban_is_ok(Client *client, Channel* channel, char *para_in, int checkt, int what, int what2) +int timedban_extban_is_ok(BanContext *b) { char para[MAX_LENGTH+1]; char tmpmask[MAX_LENGTH+1]; @@ -300,13 +312,13 @@ int timedban_extban_is_ok(Client *client, Channel* channel, char *para_in, int c int res; /* Always permit deletion */ - if (what == MODE_DEL) + if (b->what == MODE_DEL) return 1; if (timedban_extban_is_ok_recursion) return 0; /* Recursion detected (~t:1:~t:....) */ - strlcpy(para, para_in+3, sizeof(para)); /* work on a copy (and truncate it) */ + strlcpy(para, b->banstr, sizeof(para)); /* work on a copy (and truncate it) */ /* ~t:duration:n!u@h for direct matching * ~t:duration:~x:.... when calling another bantype @@ -315,18 +327,19 @@ int timedban_extban_is_ok(Client *client, Channel* channel, char *para_in, int c durationstr = para; matchby = strchr(para, ':'); if (!matchby || !matchby[1]) - return timedban_extban_syntax(client, checkt, "Invalid syntax"); + return timedban_extban_syntax(b->client, b->is_ok_check, "Invalid syntax"); *matchby++ = '\0'; duration = atoi(durationstr); if ((duration <= 0) || (duration > TIMEDBAN_MAX_TIME)) - return timedban_extban_syntax(client, checkt, "Invalid duration time"); + return timedban_extban_syntax(b->client, b->is_ok_check, "Invalid duration time"); strlcpy(tmpmask, matchby, sizeof(tmpmask)); timedban_extban_is_ok_recursion++; - //res = extban_is_ok_nuh_extban(client, channel, tmpmask, checkt, what, what2); - res = generic_ban_is_ok(client, channel, tmpmask, checkt, what, what2); + //res = extban_is_ok_nuh_extban(b->client, b->channel, tmpmask, b->is_ok_check, b->what, b->ban_type); + b->banstr = tmpmask; + res = generic_ban_is_ok(b); timedban_extban_is_ok_recursion--; if (res == 0) { @@ -334,41 +347,48 @@ int timedban_extban_is_ok(Client *client, Channel* channel, char *para_in, int c * invalid n!u@h syntax, unknown (sub)extbantype, * disabled extban type in conf, too much recursion, etc. */ - return timedban_extban_syntax(client, checkt, "Invalid matcher"); + return timedban_extban_syntax(b->client, b->is_ok_check, "Invalid matcher"); } return 1; /* OK */ } /** Check if the user is currently banned */ -int timedban_is_banned(Client *client, Channel *channel, char *ban, int chktype, char **msg, char **errmsg) +int timedban_is_banned(BanContext *b) { - if (strncmp(ban, "~t:", 3)) - return 0; /* not for us */ - ban = strchr(ban+3, ':'); /* skip time argument */ - if (!ban) + b->banstr = strchr(b->banstr, ':'); /* skip time argument */ + if (!b->banstr) return 0; /* invalid fmt */ - ban++; + b->banstr++; /* skip over final semicolon */ - return ban_check_mask(client, channel, ban, chktype, msg, errmsg, 0); + return ban_check_mask(b); } -/** Helper to check if the ban has been expired */ +/** Helper to check if the ban has been expired. + */ int timedban_has_ban_expired(Ban *ban) { char *banstr = ban->banstr; - char *p; + char *p1, *p2; int t; time_t expire_on; - if (strncmp(banstr, "~t:", 3)) + /* The caller has only performed a very light check (string starting + * with ~t, in the interest of performance), so we don't know yet if + * it REALLY is a timed ban. We check that first here... + */ + if (!strncmp(banstr, "~t:", 3)) + p1 = banstr + 3; + else if (!strncmp(banstr, "~time:", 6)) + p1 = banstr + 6; + else return 0; /* not for us */ - p = strchr(banstr+3, ':'); /* skip time argument */ - if (!p) + p2 = strchr(p1+1, ':'); /* skip time argument */ + if (!p2) return 0; /* invalid fmt */ - *p = '\0'; /* danger.. must restore!! */ - t = atoi(banstr+3); - *p = ':'; /* restored.. */ + *p2 = '\0'; /* danger.. must restore!! */ + t = atoi(p1); + *p2 = ':'; /* restored.. */ expire_on = ban->when + (t * 60) - TIMEDBAN_TIMER_DELTA; @@ -398,14 +418,14 @@ EVENT(timedban_timeout) * is too costly. So we stick with this. It should be * good enough. Alternative would be some channel->id value. */ - if (((unsigned int)channel->chname[1] % TIMEDBAN_TIMER_ITERATION_SPLIT) != current_iteration) + if (((unsigned int)channel->name[1] % TIMEDBAN_TIMER_ITERATION_SPLIT) != current_iteration) continue; /* not this time, maybe next */ *mbuf = *pbuf = '\0'; for (ban = channel->banlist; ban; ban=nextban) { nextban = ban->next; - if (!strncmp(ban->banstr, "~t:", 3) && timedban_has_ban_expired(ban)) + if (!strncmp(ban->banstr, "~t", 2) && timedban_has_ban_expired(ban)) { add_send_mode_param(channel, &me, '-', 'b', ban->banstr); del_listmode(&channel->banlist, channel, ban->banstr); @@ -414,7 +434,7 @@ EVENT(timedban_timeout) for (ban = channel->exlist; ban; ban=nextban) { nextban = ban->next; - if (!strncmp(ban->banstr, "~t:", 3) && timedban_has_ban_expired(ban)) + if (!strncmp(ban->banstr, "~t", 2) && timedban_has_ban_expired(ban)) { add_send_mode_param(channel, &me, '-', 'e', ban->banstr); del_listmode(&channel->exlist, channel, ban->banstr); @@ -423,7 +443,7 @@ EVENT(timedban_timeout) for (ban = channel->invexlist; ban; ban=nextban) { nextban = ban->next; - if (!strncmp(ban->banstr, "~t:", 3) && timedban_has_ban_expired(ban)) + if (!strncmp(ban->banstr, "~t", 2) && timedban_has_ban_expired(ban)) { add_send_mode_param(channel, &me, '-', 'I', ban->banstr); del_listmode(&channel->invexlist, channel, ban->banstr); @@ -433,8 +453,8 @@ EVENT(timedban_timeout) { MessageTag *mtags = NULL; new_message(&me, NULL, &mtags); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", me.name, channel->chname, mbuf, pbuf); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s 0", me.id, channel->chname, mbuf, pbuf); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", me.name, channel->name, mbuf, pbuf); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s 0", me.id, channel->name, mbuf, pbuf); free_message_tags(mtags); *pbuf = 0; } @@ -484,8 +504,8 @@ void add_send_mode_param(Channel *channel, Client *from, char what, char mode, c MessageTag *mtags = NULL; new_message(&me, NULL, &mtags); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", me.name, channel->chname, mbuf, pbuf); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s 0", me.id, channel->chname, mbuf, pbuf); + sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", me.name, channel->name, mbuf, pbuf); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s 0", me.id, channel->name, mbuf, pbuf); free_message_tags(mtags); send = 0; *pbuf = 0; diff --git a/src/modules/extended-monitor.c b/src/modules/extended-monitor.c new file mode 100644 index 0000000..34c9f1f --- /dev/null +++ b/src/modules/extended-monitor.c @@ -0,0 +1,153 @@ +/* + * IRC - Internet Relay Chat, src/modules/extended-monitor.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +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_notification(Client *client, Watch *watch, Link *lp, int event); + +ModuleHeader MOD_HEADER + = { + "extended-monitor", + "5.0", + "extended functionality for /monitor", + "UnrealIRCd Team", + "unrealircd-6", + }; + +MOD_INIT() +{ + ClientCapabilityInfo cap; + ClientCapability *c; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + ModDataInfo mreq; + + memset(&cap, 0, sizeof(cap)); + cap.name = "draft/extended-monitor"; + c = ClientCapabilityAdd(modinfo->handle, &cap, &CAP_EXTENDED_MONITOR); + if (!c) + { + config_error("[%s] Failed to request extended-monitor cap: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); + return MOD_FAILED; + } + + 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); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int extended_monitor_away(Client *client, MessageTag *mtags, const char *reason, int already_as_away) +{ + if (reason) + watch_check(client, WATCH_EVENT_AWAY, extended_monitor_notification); + else + watch_check(client, WATCH_EVENT_NOTAWAY, extended_monitor_notification); + + return 0; +} + +int extended_monitor_account_login(Client *client, MessageTag *mtags) +{ + if (IsLoggedIn(client)) + watch_check(client, WATCH_EVENT_LOGGEDIN, extended_monitor_notification); + else + watch_check(client, WATCH_EVENT_LOGGEDOUT, extended_monitor_notification); + + return 0; +} + +int extended_monitor_userhost_changed(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) +{ + watch_check(client, WATCH_EVENT_REALNAME, extended_monitor_notification); + return 0; +} + +int extended_monitor_notification(Client *client, Watch *watch, Link *lp, int event) +{ + if (!(lp->flags & WATCH_FLAG_TYPE_MONITOR)) + return 0; + + if (!HasCapabilityFast(lp->value.client, CAP_EXTENDED_MONITOR)) + return 0; /* this client does not support our notifications */ + + if (has_common_channels(client, lp->value.client)) + return 0; /* will be notified anyway */ + + switch (event) + { + case WATCH_EVENT_AWAY: + if (HasCapability(lp->value.client, "away-notify")) + sendto_prefix_one(lp->value.client, client, NULL, ":%s AWAY :%s", client->name, client->user->away); + break; + case WATCH_EVENT_NOTAWAY: + if (HasCapability(lp->value.client, "away-notify")) + sendto_prefix_one(lp->value.client, client, NULL, ":%s AWAY", client->name); + break; + case WATCH_EVENT_LOGGEDIN: + if (HasCapability(lp->value.client, "account-notify")) + sendto_prefix_one(lp->value.client, client, NULL, ":%s ACCOUNT :%s", client->name, client->user->account); + break; + case WATCH_EVENT_LOGGEDOUT: + if (HasCapability(lp->value.client, "account-notify")) + sendto_prefix_one(lp->value.client, client, NULL, ":%s ACCOUNT :*", client->name); + break; + case WATCH_EVENT_USERHOST: + if (HasCapability(lp->value.client, "chghost")) + sendto_prefix_one(lp->value.client, client, NULL, ":%s CHGHOST %s %s", client->name, client->user->username, GetHost(client)); + break; + case WATCH_EVENT_REALNAME: + if (HasCapability(lp->value.client, "setname")) + sendto_prefix_one(lp->value.client, client, NULL, ":%s SETNAME :%s", client->name, client->info); + break; + default: + break; + } + + return 0; +} + diff --git a/src/modules/extjwt.c b/src/modules/extjwt.c new file mode 100644 index 0000000..0d9d63c --- /dev/null +++ b/src/modules/extjwt.c @@ -0,0 +1,1151 @@ +/* + * IRC - Internet Relay Chat, src/modules/extjwt.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +#if defined(__GNUC__) +/* Temporarily ignore these for this entire file. FIXME later when updating the code for OpenSSL 3: */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +/* internal definitions */ + +#define MSG_EXTJWT "EXTJWT" +#define MYCONF "extjwt" + +#undef NEW_ISUPPORT /* enable this for https://github.com/ircv3/ircv3-specifications/pull/341#issuecomment-617038799 */ + +#define EXTJWT_METHOD_NOT_SET 0 +#define EXTJWT_METHOD_HS256 1 +#define EXTJWT_METHOD_HS384 2 +#define EXTJWT_METHOD_HS512 3 +#define EXTJWT_METHOD_RS256 4 +#define EXTJWT_METHOD_RS384 5 +#define EXTJWT_METHOD_RS512 6 +#define EXTJWT_METHOD_ES256 7 +#define EXTJWT_METHOD_ES384 8 +#define EXTJWT_METHOD_ES512 9 +#define EXTJWT_METHOD_NONE 10 + +#define NEEDS_KEY(x) (x>=EXTJWT_METHOD_RS256 && x<=EXTJWT_METHOD_ES512) + +#define URL_LENGTH 4096 +#define MODES_SIZE 41 /* about 10 mode chars */ +#define TS_LENGTH 19 /* 64-bit integer */ +#define MAX_TOKEN_CHUNK (510-sizeof(extjwt_message_pattern)-HOSTLEN-CHANNELLEN) + +/* OpenSSL 1.0.x compatibility */ + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) +void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} +#endif + +/* struct definitions */ + +struct extjwt_config { + time_t exp_delay; + char *secret; + int method; + char *vfy; +}; + +struct jwt_service { + char *name; + struct extjwt_config *cfg; + struct jwt_service *next; +}; + +/* function declarations */ + +CMD_FUNC(cmd_extjwt); +char *extjwt_make_payload(Client *client, Channel *channel, struct extjwt_config *config); +char *extjwt_generate_token(const char *payload, struct extjwt_config *config); +void b64url(char *b64); +unsigned char *extjwt_hmac_extjwt_hash(int method, const void *key, int keylen, const unsigned char *data, int datalen, unsigned int* resultlen); +unsigned char *extjwt_sha_pem_extjwt_hash(int method, const void *key, int keylen, const unsigned char *data, int datalen, unsigned int* resultlen); +unsigned char *extjwt_hash(int method, const void *key, int keylen, const unsigned char *data, int datalen, unsigned int* resultlen); +char *extjwt_gen_header(int method); +int extjwt_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +int extjwt_configrun(ConfigFile *cf, ConfigEntry *ce, int type); +int extjwt_configposttest(int *errs); +void extjwt_free_services(struct jwt_service **services); +struct jwt_service *find_jwt_service(struct jwt_service *services, const char *name); +int extjwt_valid_integer_string(const char *in, int min, int max); +char *extjwt_test_key(const char *file, int method); +char *extjwt_read_file_contents(const char *file, int absolute, int *size); +int EXTJWT_METHOD_from_string(const char *in); +#ifdef NEW_ISUPPORT +char *extjwt_isupport_param(void); +#endif + +/* string constants */ + +const char extjwt_message_pattern[] = ":%s EXTJWT %s %s %s%s"; + +/* global structs */ + +ModuleHeader MOD_HEADER = { + "extjwt", + "6.0", + "Command /EXTJWT (web service authorization)", + "UnrealIRCd Team", + "unrealircd-6" +}; + +struct { + int have_secret; + int have_key; + int have_method; + int have_expire; + int have_vfy; + char *key_filename; +} cfg_state; + +struct extjwt_config cfg; +struct jwt_service *jwt_services; + +MOD_TEST() +{ + memset(&cfg_state, 0, sizeof(cfg_state)); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, extjwt_configtest); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, extjwt_configposttest); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + CommandAdd(modinfo->handle, MSG_EXTJWT, cmd_extjwt, 2, CMD_USER); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, extjwt_configrun); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + struct jwt_service *service = jwt_services; +#ifdef NEW_ISUPPORT + ISupportAdd(modinfo->handle, "EXTJWT", extjwt_isupport_param()); +#else + ISupportAdd(modinfo->handle, "EXTJWT", "1"); +#endif + while (service) + { /* copy default exp to all services not having one specified */ + if (service->cfg->exp_delay == 0) + service->cfg->exp_delay = cfg.exp_delay; + service = service->next; + } + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + extjwt_free_services(&jwt_services); + return MOD_SUCCESS; +} + +#ifdef NEW_ISUPPORT +char *extjwt_isupport_param(void) +{ + struct jwt_service *services = jwt_services; + int count = 0; + static char buf[500]; + strlcpy(buf, "V:1", sizeof(buf)); + while (services) + { + strlcat(buf, count?",":"&S:", sizeof(buf)); + strlcat(buf, services->name, sizeof(buf)); + count++; + services = services->next; + } + return buf; +} +#endif + +void extjwt_free_services(struct jwt_service **services){ + struct jwt_service *ss, *next; + ss = *services; + while (ss) + { + next = ss->next; + safe_free(ss->name); + if (ss->cfg) + safe_free(ss->cfg->secret); + safe_free(ss->cfg); + safe_free(ss); + ss = next; + } + *services = NULL; +} + +struct jwt_service *find_jwt_service(struct jwt_service *services, const char *name) +{ + if (!name) + return NULL; + while (services) + { + if (services->name && !strcmp(services->name, name)) + return services; + services = services->next; + } + return NULL; +} + +int extjwt_valid_integer_string(const char *in, int min, int max) +{ + int i, val; + if (BadPtr(in)) + return 0; + for (i=0; in[i]; i++){ + if (!isdigit(in[i])) + return 0; + } + val = atoi(in); + if (val < min || val > max) + return 0; + return 1; +} + +int vfy_url_is_valid(const char *string) +{ + if (strstr(string, "http://") == string || strstr(string, "https://") == string) + { + if (strstr(string, "%s")) + return 1; + } + return 0; +} + +char *extjwt_test_key(const char *file, int method) +{ /* returns NULL when valid */ + int fsize; + char *fcontent = NULL; + char *retval = NULL; + BIO *bufkey = NULL; + EVP_PKEY *pkey = NULL; + int type, pkey_type; + do { + switch (method) + { + case EXTJWT_METHOD_RS256: case EXTJWT_METHOD_RS384: case EXTJWT_METHOD_RS512: + type = EVP_PKEY_RSA; + break; + case EXTJWT_METHOD_ES256: case EXTJWT_METHOD_ES384: case EXTJWT_METHOD_ES512: + type = EVP_PKEY_EC; + break; + default: + retval = "Internal error (invalid type)"; + return retval; + } + fcontent = extjwt_read_file_contents(file, 0, &fsize); + if (!fcontent) + { + retval = "Cannot open file"; + break; + } + if (fsize == 0) + { + retval = "File is empty"; + break; + } + if (!(bufkey = BIO_new_mem_buf(fcontent, fsize))) + { + retval = "Unknown error"; + break; + } + if (!(pkey = PEM_read_bio_PrivateKey(bufkey, NULL, NULL, NULL))) + { + retval = "Key is invalid"; + break; + } + pkey_type = EVP_PKEY_id(pkey); + if (type != pkey_type) + { + retval = "Key does not match method"; + break; + } + } while (0); + safe_free(fcontent); + if (bufkey) + BIO_free(bufkey); + if (pkey) + EVP_PKEY_free(pkey); + return retval; +} + +int EXTJWT_METHOD_from_string(const char *in) +{ + if (!strcmp(in, "HS256")) + return EXTJWT_METHOD_HS256; + if (!strcmp(in, "HS384")) + return EXTJWT_METHOD_HS384; + if (!strcmp(in, "HS512")) + return EXTJWT_METHOD_HS512; + if (!strcmp(in, "RS256")) + return EXTJWT_METHOD_RS256; + if (!strcmp(in, "RS384")) + return EXTJWT_METHOD_RS384; + if (!strcmp(in, "RS512")) + return EXTJWT_METHOD_RS512; + if (!strcmp(in, "ES256")) + return EXTJWT_METHOD_ES256; + if (!strcmp(in, "ES384")) + return EXTJWT_METHOD_ES384; + if (!strcmp(in, "ES512")) + return EXTJWT_METHOD_ES512; + if (!strcmp(in, "NONE")) + return EXTJWT_METHOD_NONE; + return EXTJWT_METHOD_NOT_SET; +} + +/* Configuration is described in conf/modules.optional.conf */ + +int extjwt_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + int errors = 0; + ConfigEntry *cep, *cep2; + int i; + struct jwt_service *services = NULL; + struct jwt_service **ss = &services; /* list for checking whether service names repeat */ + int have_ssecret, have_smethod, have_svfy, have_scert; + unsigned int sfilename_line_number = 0; + char *sfilename = NULL; + + if (type != CONFIG_MAIN) + return 0; + + if (!ce || strcmp(ce->name, MYCONF)) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!cep->value) + { + config_error("%s:%i: blank %s::%s without value", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + continue; + } + if (!strcmp(cep->name, "method")) + { + if (cfg_state.have_method) + { + config_error("%s:%i: duplicate %s::%s item", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + continue; + } + cfg_state.have_method = EXTJWT_METHOD_from_string(cep->value); + if (cfg_state.have_method == EXTJWT_METHOD_NOT_SET) + { + config_error("%s:%i: invalid value %s::%s \"%s\" (check docs for allowed options)", cep->file->filename, cep->line_number, MYCONF, cep->name, cep->value); + errors++; + } + continue; + } + if (!strcmp(cep->name, "expire-after")) + { + if (!extjwt_valid_integer_string(cep->value, 1, 9999)) + { + config_error("%s:%i: %s::%s must be an integer between 1 and 9999 (seconds)", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + } + continue; + } + if (!strcmp(cep->name, "secret")) + { + if (cfg_state.have_secret) + { + config_error("%s:%i: duplicate %s::%s item", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + continue; + } + cfg_state.have_secret = 1; + if (strlen(cep->value) < 4) + { + config_error("%s:%i: Secret specified in %s::%s is too short!", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + } + continue; + } + if (!strcmp(cep->name, "key")) + { + if (cfg_state.have_key) + { + config_error("%s:%i: duplicate %s::%s item", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + continue; + } + if (!is_file_readable(cep->value, CONFDIR)) + { + config_error("%s:%i: Cannot open file \"%s\" specified in %s::%s for reading", cep->file->filename, cep->line_number, cep->value, MYCONF, cep->name); + errors++; + } + safe_strdup(cfg_state.key_filename, cep->value); + cfg_state.have_key = 1; + continue; + } + if (!strcmp(cep->name, "verify-url")) + { + if (cfg_state.have_vfy) + { + config_error("%s:%i: duplicate %s:%s item", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + continue; + } + cfg_state.have_vfy = 1; + if (!vfy_url_is_valid(cep->value)) + { + config_error("%s:%i: Optional URL specified in %s::%s is invalid!", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + continue; + } + if (strlen(cep->value) > URL_LENGTH) + { + config_error("%s:%i: Optional URL specified in %s::%s is too long!", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + } + continue; + } + if (!strcmp(cep->name, "service")) + { + have_ssecret = 0; + have_smethod = 0; + have_svfy = 0; + have_scert = 0; + if (strchr(cep->value, ' ') || strchr(cep->value, ',')) + { + config_error("%s:%i: Invalid %s::%s name (contains spaces or commas)", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + continue; + } + if (find_jwt_service(services, cep->value)) + { + config_error("%s:%i: Duplicate %s::%s name \"%s\"", cep->file->filename, cep->line_number, MYCONF, cep->name, cep->value); + errors++; + continue; + } + *ss = safe_alloc(sizeof(struct jwt_service)); /* store the new name for further checking */ + safe_strdup((*ss)->name, cep->value); + ss = &(*ss)->next; + for (cep2 = cep->items; cep2; cep2 = cep2->next) + { + if (!cep2->name || !cep2->value || !cep2->value[0]) + { + config_error("%s:%i: blank/incomplete %s::service entry", cep2->file->filename, cep2->line_number, MYCONF); + errors++; + continue; + } + + if (!strcmp(cep2->name, "method")) + { + if (have_smethod) + { + config_error("%s:%i: duplicate %s::service::%s item", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + continue; + } + have_smethod = EXTJWT_METHOD_from_string(cep2->value); + if (have_smethod == EXTJWT_METHOD_NOT_SET || have_smethod == EXTJWT_METHOD_NONE) + { + config_error("%s:%i: invalid value of optional %s::service::%s \"%s\" (check docs for allowed options)", cep2->file->filename, cep2->line_number, MYCONF, cep2->name, cep2->value); + errors++; + } + continue; + } + + if (!strcmp(cep2->name, "secret")) + { + if (have_ssecret) + { + config_error("%s:%i: duplicate %s::service::%s item", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + continue; + } + have_ssecret = 1; + if (strlen(cep2->value) < 4) /* TODO maybe a better check? */ + { + config_error("%s:%i: Secret specified in %s::service::%s is too short!", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + } + continue; + } + + if (!strcmp(cep2->name, "key")) + { + if (have_scert) + { + config_error("%s:%i: duplicate %s::service::%s item", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + continue; + } + if (!is_file_readable(cep2->value, CONFDIR)) + { + config_error("%s:%i: Cannot open file \"%s\" specified in %s::service::%s for reading", cep2->file->filename, cep2->line_number, cep2->value, MYCONF, cep2->name); + errors++; + } + have_scert = 1; + safe_strdup(sfilename, cep2->value); + sfilename_line_number = cep2->line_number; + continue; + } + + if (!strcmp(cep2->name, "expire-after")) + { + if (!extjwt_valid_integer_string(cep2->value, 1, 9999)) + { + config_error("%s:%i: %s::%s must be an integer between 1 and 9999 (seconds)", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + } + continue; + } + + if (!strcmp(cep2->name, "verify-url")) + { + if (have_svfy) + { + config_error("%s:%i: duplicate %s::service::%s item", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + continue; + } + have_svfy = 1; + if (!vfy_url_is_valid(cep2->value)) + { + config_error("%s:%i: Optional URL specified in %s::service::%s is invalid!", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + continue; + } + if (strlen(cep2->value) > URL_LENGTH) + { + config_error("%s:%i: Optional URL specified in %s::service::%s is too long!", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + } + continue; + } + + config_error("%s:%i: invalid %s::service attribute %s (must be one of: name, secret, expire-after)", cep2->file->filename, cep2->line_number, MYCONF, cep2->name); + errors++; + } + if (!have_smethod) + { + config_error("%s:%i: invalid %s::service entry (no %s::service::method specfied)", cep->file->filename, cep->line_number, MYCONF, MYCONF); + errors++; + continue; + } + if (have_ssecret && NEEDS_KEY(have_smethod)) + { + config_error("%s:%i: invalid %s::service entry (this method needs %s::service::key and not %s::service::secret option)", cep->file->filename, cep->line_number, MYCONF, MYCONF, MYCONF); + errors++; + continue; + } + if (have_scert && !NEEDS_KEY(have_smethod)) + { + config_error("%s:%i: invalid %s::service entry (this method needs %s::service::secret and not %s::service::key option)", cep->file->filename, cep->line_number, MYCONF, MYCONF, MYCONF); + errors++; + continue; + } + if (!have_ssecret && !NEEDS_KEY(have_smethod)) + { + config_error("%s:%i: invalid %s::service entry (must contain %s::service::secret option)", cep->file->filename, cep->line_number, MYCONF, MYCONF); + errors++; + continue; + } + if (!have_scert && NEEDS_KEY(have_smethod)) { + config_error("%s:%i: invalid %s::service entry (must contain %s::service::key option)", cep->file->filename, cep->line_number, MYCONF, MYCONF); + errors++; + continue; + } + if (NEEDS_KEY(have_smethod) && have_scert) + { + char *keyerr; + keyerr = extjwt_test_key(sfilename, have_smethod); + if (keyerr) + { + config_error("%s:%i: Invalid key file specified for %s::key: %s", cep->file->filename, sfilename_line_number, MYCONF, keyerr); + errors++; + } + } + continue; + } + config_error("%s:%i: unknown directive %s::%s", cep->file->filename, cep->line_number, MYCONF, cep->name); + errors++; + } + *errs = errors; + extjwt_free_services(&services); + if (errors) + safe_free(cfg_state.key_filename); + safe_free(sfilename); + return errors ? -1 : 1; +} + +int extjwt_configposttest(int *errs) +{ + int errors = 0; + if (cfg_state.have_method == EXTJWT_METHOD_NOT_SET) + { + config_error("No %s::method specfied!", MYCONF); + errors++; + } else + { + if (cfg_state.have_method != EXTJWT_METHOD_NONE && !NEEDS_KEY(cfg_state.have_method) && !cfg_state.have_secret) + { + config_error("No %s::secret specfied as required by requested method!", MYCONF); + errors++; + } + if ((cfg_state.have_method == EXTJWT_METHOD_NONE || NEEDS_KEY(cfg_state.have_method)) && cfg_state.have_secret) + { + config_error("A %s::secret specfied but it should not be when using requested method!", MYCONF); + errors++; + } + if (NEEDS_KEY(cfg_state.have_method) && !cfg_state.have_key) + { + config_error("No %s::key specfied as required by requested method!", MYCONF); + errors++; + } + if (!NEEDS_KEY(cfg_state.have_method) && cfg_state.have_key) + { + config_error("A %s::key specfied but it should not be when using requested method!", MYCONF); + errors++; + } + if (NEEDS_KEY(cfg_state.have_method) && cfg_state.have_key && cfg_state.key_filename) + { + char *keyerr; + + keyerr = extjwt_test_key(cfg_state.key_filename, cfg_state.have_method); + if (keyerr) + { + config_error("Invalid key file specified for %s::key: %s", MYCONF, keyerr); + errors++; + } + } + } + safe_free(cfg_state.key_filename); + if (errors) + { + *errs = errors; + return -1; + } + /* setting defaults, FIXME this may behave incorrectly if there's another module failing POSTTEST */ + if (!cfg_state.have_expire) + cfg.exp_delay = 30; + /* prepare service list to load new data */ + extjwt_free_services(&jwt_services); + return 1; +} + +int extjwt_configrun(ConfigFile *cf, ConfigEntry *ce, int type) +{ /* actually use the new configuration data */ + ConfigEntry *cep, *cep2; + struct jwt_service **ss = &jwt_services; + if (*ss) + ss = &((*ss)->next); + + if (type != CONFIG_MAIN) + return 0; + + if (!ce || strcmp(ce->name, MYCONF)) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "method")) + { + cfg.method = EXTJWT_METHOD_from_string(cep->value); + continue; + } + if (!strcmp(cep->name, "expire-after")) + { + cfg.exp_delay = atoi(cep->value); + continue; + } + if (!strcmp(cep->name, "secret")) + { + cfg.secret = strdup(cep->value); + continue; + } + if (!strcmp(cep->name, "key")) + { + cfg.secret = extjwt_read_file_contents(cep->value, 0, NULL); + continue; + } + if (!strcmp(cep->name, "verify-url")) + { + cfg.vfy = strdup(cep->value); + continue; + } + if (!strcmp(cep->name, "service")) + { /* nested block */ + *ss = safe_alloc(sizeof(struct jwt_service)); + (*ss)->cfg = safe_alloc(sizeof(struct extjwt_config)); + safe_strdup((*ss)->name, cep->value); /* copy the service name */ + for (cep2 = cep->items; cep2; cep2 = cep2->next) + { + if (!strcmp(cep2->name, "method")) + { + (*ss)->cfg->method = EXTJWT_METHOD_from_string(cep2->value); + continue; + } + if (!strcmp(cep2->name, "expire-after")) + { + (*ss)->cfg->exp_delay = atoi(cep2->value); + continue; + } + if (!strcmp(cep2->name, "secret")) + { + (*ss)->cfg->secret = strdup(cep2->value); + continue; + } + if (!strcmp(cep2->name, "key")) + { + (*ss)->cfg->secret = extjwt_read_file_contents(cep2->value, 0, NULL); + continue; + } + if (!strcmp(cep2->name, "verify-url")) + { + (*ss)->cfg->vfy = strdup(cep2->value); + continue; + } + } + ss = &((*ss)->next); + } + } + return 1; +} + +char *extjwt_read_file_contents(const char *file, int absolute, int *size) +{ + FILE *f = NULL; + int fsize; + char *filename = NULL; + char *buf = NULL; + do + { + safe_strdup(filename, file); + if (!absolute) + convert_to_absolute_path(&filename, CONFDIR); + f = fopen(filename, "rb"); + if (!f) + break; + fseek(f, 0, SEEK_END); + fsize = ftell(f); + fseek(f, 0, SEEK_SET); + buf = safe_alloc(fsize + 1); + fsize = fread(buf, 1, fsize, f); + buf[fsize] = '\0'; + if (size) + *size = fsize; + fclose(f); + } while (0); + safe_free(filename); + if (!buf && size) + *size = 0; + return buf; +} + +CMD_FUNC(cmd_extjwt) +{ + Channel *channel; + char *payload; + char *token, *full_token; + struct jwt_service *service = NULL; + struct extjwt_config *config; + int last = 0; + char message[MAX_TOKEN_CHUNK+1]; + if (parc < 2 || BadPtr(parv[1])) + { + sendnumeric(client, ERR_NEEDMOREPARAMS, MSG_EXTJWT); + return; + } + if (parv[1][0] == '*' && parv[1][1] == '\0') + { + channel = NULL; /* not linked to a channel */ + } else + { + channel = find_channel(parv[1]); + if (!channel) + { + sendnumeric(client, ERR_NOSUCHNICK, parv[1]); + return; + } + } + if (parc > 2 && !BadPtr(parv[2])) + { + service = find_jwt_service(jwt_services, parv[2]); + if (!service) + { + sendto_one(client, NULL, ":%s FAIL %s NO_SUCH_SERVICE :No such service", me.name, MSG_EXTJWT); + return; + } + } + if (service){ + config = service->cfg; /* service config */ + } else { + config = &cfg; /* default config */ + } + if (!(payload = extjwt_make_payload(client, channel, config)) || !(full_token = extjwt_generate_token(payload, config))) + { + sendto_one(client, NULL, ":%s FAIL %s UNKNOWN_ERROR :Failed to generate token", me.name, MSG_EXTJWT); + return; + } + safe_free(payload); + token = full_token; + do + { + if (strlen(token) <= MAX_TOKEN_CHUNK) + { /* the remaining data (or whole token) will fit a single irc message */ + last = 1; + strcpy(message, token); + } else + { /* send a chunk and shift buffer */ + strlcpy(message, token, MAX_TOKEN_CHUNK+1); + token += MAX_TOKEN_CHUNK; + } + sendto_one(client, NULL, extjwt_message_pattern, me.name, parv[1], "*", last?"":"* ", message); + } while (!last); + safe_free(full_token); +} + +char *extjwt_make_payload(Client *client, Channel *channel, struct extjwt_config *config) +{ + Membership *lp; + json_t *payload = NULL; + json_t *modes = NULL; + json_t *umodes = NULL; + char *modestring; + char singlemode[2] = { '\0' }; + char *result; + + if (!IsUser(client)) + return NULL; + + payload = json_object(); + modes = json_array(); + umodes = json_array(); + + json_object_set_new(payload, "exp", json_integer(TStime()+config->exp_delay)); + json_object_set_new(payload, "iss", json_string_unreal(me.name)); + json_object_set_new(payload, "sub", json_string_unreal(client->name)); + json_object_set_new(payload, "account", json_string_unreal(IsLoggedIn(client)?client->user->account:"")); + + if (config->vfy) /* also add the URL */ + json_object_set_new(payload, "vfy", json_string_unreal(config->vfy)); + + if (IsOper(client)) /* add "o" ircop flag */ + json_array_append_new(umodes, json_string("o")); + json_object_set_new(payload, "umodes", umodes); + + if (channel) + { /* fill in channel information and user flags */ + lp = find_membership_link(client->user->channel, channel); + if (lp) + { + modestring = lp->member_modes; + while (*modestring) + { + singlemode[0] = *modestring; + json_array_append_new(modes, json_string(singlemode)); + modestring++; + } + } + json_object_set_new(payload, "channel", json_string_unreal(channel->name)); + json_object_set_new(payload, "joined", json_integer(lp?1:0)); + json_object_set_new(payload, "cmodes", modes); + } + result = json_dumps(payload, JSON_COMPACT); + json_decref(modes); + json_decref(umodes); + json_decref(payload); + return result; +} + +void b64url(char *b64) +{ /* convert base64 to base64-url */ + while (*b64) + { + if (*b64 == '+') + *b64 = '-'; + if (*b64 == '/') + *b64 = '_'; + if (*b64 == '=') + { + *b64 = '\0'; + return; + } + b64++; + } +} + +unsigned char *extjwt_hash(int method, const void *key, int keylen, const unsigned char *data, int datalen, unsigned int* resultlen) +{ + switch(method) + { + case EXTJWT_METHOD_HS256: case EXTJWT_METHOD_HS384: case EXTJWT_METHOD_HS512: + return extjwt_hmac_extjwt_hash(method, key, keylen, data, datalen, resultlen); + case EXTJWT_METHOD_RS256: case EXTJWT_METHOD_RS384: case EXTJWT_METHOD_RS512: case EXTJWT_METHOD_ES256: case EXTJWT_METHOD_ES384: case EXTJWT_METHOD_ES512: + return extjwt_sha_pem_extjwt_hash(method, key, keylen, data, datalen, resultlen); + } + return NULL; +} + +unsigned char* extjwt_sha_pem_extjwt_hash(int method, const void *key, int keylen, const unsigned char *data, int datalen, unsigned int* resultlen) +{ + EVP_MD_CTX *mdctx = NULL; + ECDSA_SIG *ec_sig = NULL; + const BIGNUM *ec_sig_r = NULL; + const BIGNUM *ec_sig_s = NULL; + BIO *bufkey = NULL; + const EVP_MD *alg; + int type; + EVP_PKEY *pkey = NULL; + int pkey_type; + unsigned char *sig = NULL; + int ret = 0; + size_t slen; + char *retval = NULL; + char *output = NULL; + char *sig_ptr; + + do + { + switch (method) + { + case EXTJWT_METHOD_RS256: + alg = EVP_sha256(); + type = EVP_PKEY_RSA; + break; + case EXTJWT_METHOD_RS384: + alg = EVP_sha384(); + type = EVP_PKEY_RSA; + break; + case EXTJWT_METHOD_RS512: + alg = EVP_sha512(); + type = EVP_PKEY_RSA; + break; + case EXTJWT_METHOD_ES256: + alg = EVP_sha256(); + type = EVP_PKEY_EC; + break; + case EXTJWT_METHOD_ES384: + alg = EVP_sha384(); + type = EVP_PKEY_EC; + break; + case EXTJWT_METHOD_ES512: + alg = EVP_sha512(); + type = EVP_PKEY_EC; + break; + default: + return NULL; + } + +#if (OPENSSL_VERSION_NUMBER < 0x10100003L) /* https://github.com/openssl/openssl/commit/8ab31975bacb9c907261088937d3aa4102e3af84 */ + if (!(bufkey = BIO_new_mem_buf((void *)key, keylen))) + break; /* out of memory */ +#else + if (!(bufkey = BIO_new_mem_buf(key, keylen))) + break; /* out of memory */ +#endif + if (!(pkey = PEM_read_bio_PrivateKey(bufkey, NULL, NULL, NULL))) + break; /* invalid key? */ + pkey_type = EVP_PKEY_id(pkey); + if (type != pkey_type) + break; /* invalid key type */ + if (!(mdctx = EVP_MD_CTX_create())) + break; /* out of memory */ + if (EVP_DigestSignInit(mdctx, NULL, alg, NULL, pkey) != 1) + break; /* initialize error */ + if (EVP_DigestSignUpdate(mdctx, data, datalen) != 1) + break; /* signing error */ + if (EVP_DigestSignFinal(mdctx, NULL, &slen) != 1) /* get required buffer length */ + break; + sig = safe_alloc(slen); + if (EVP_DigestSignFinal(mdctx, sig, &slen) != 1) + break; + if (pkey_type != EVP_PKEY_EC) + { + *resultlen = slen; + output = safe_alloc(slen); + memcpy(output, sig, slen); + retval = output; + } else + { + unsigned int degree, bn_len, r_len, s_len, buf_len; + unsigned char *raw_buf = NULL; + EC_KEY *ec_key; + if (!(ec_key = EVP_PKEY_get1_EC_KEY(pkey))) + break; /* out of memory */ + degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec_key)); + EC_KEY_free(ec_key); + sig_ptr = sig; + if (!(ec_sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&sig_ptr, slen))) + break; /* out of memory */ + ECDSA_SIG_get0(ec_sig, &ec_sig_r, &ec_sig_s); + r_len = BN_num_bytes(ec_sig_r); + s_len = BN_num_bytes(ec_sig_s); + bn_len = (degree+7)/8; + if (r_len>bn_len || s_len > bn_len) + break; + buf_len = bn_len*2; + raw_buf = safe_alloc(buf_len); + BN_bn2bin(ec_sig_r, raw_buf+bn_len-r_len); + BN_bn2bin(ec_sig_s, raw_buf+buf_len-s_len); + output = safe_alloc(buf_len); + *resultlen = buf_len; + memcpy(output, raw_buf, buf_len); + retval = output; + safe_free(raw_buf); + } + } while (0); + + if (bufkey) + BIO_free(bufkey); + if (pkey) + EVP_PKEY_free(pkey); + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + if (ec_sig) + ECDSA_SIG_free(ec_sig); + safe_free(sig); + return retval; +} + +unsigned char* extjwt_hmac_extjwt_hash(int method, const void *key, int keylen, const unsigned char *data, int datalen, unsigned int* resultlen) +{ + const EVP_MD* typ; + char *hmac = safe_alloc(EVP_MAX_MD_SIZE); + switch (method) + { + default: + case EXTJWT_METHOD_HS256: + typ = EVP_sha256(); + break; + case EXTJWT_METHOD_HS384: + typ = EVP_sha384(); + break; + case EXTJWT_METHOD_HS512: + typ = EVP_sha512(); + break; + } + if (HMAC(typ, key, keylen, data, datalen, hmac, resultlen)) + { /* openssl call */ + return hmac; + } else { + safe_free(hmac); + return NULL; + } +} + +char *extjwt_gen_header(int method) +{ /* returns header json */ + json_t *header = NULL; + json_t *alg; + char *result; + + header = json_object(); + json_object_set_new(header, "typ", json_string("JWT")); + + switch (method) + { + default: + case EXTJWT_METHOD_HS256: + alg = json_string("HS256"); + break; + case EXTJWT_METHOD_HS384: + alg = json_string("HS384"); + break; + case EXTJWT_METHOD_HS512: + alg = json_string("HS512"); + break; + case EXTJWT_METHOD_RS256: + alg = json_string("RS256"); + break; + case EXTJWT_METHOD_RS384: + alg = json_string("RS384"); + break; + case EXTJWT_METHOD_RS512: + alg = json_string("RS512"); + break; + case EXTJWT_METHOD_ES256: + alg = json_string("ES256"); + break; + case EXTJWT_METHOD_ES384: + alg = json_string("ES384"); + break; + case EXTJWT_METHOD_ES512: + alg = json_string("ES512"); + break; + case EXTJWT_METHOD_NONE: + alg = json_string("none"); + break; + } + json_object_set_new(header, "alg", alg); + result = json_dumps(header, JSON_COMPACT); + json_decref(header); + return result; +} + +char *extjwt_generate_token(const char *payload, struct extjwt_config *config) +{ + char *header = extjwt_gen_header(config->method); + size_t b64header_size = strlen(header)*4/3 + 8; // base64 has 4/3 overhead + size_t b64payload_size = strlen(payload)*4/3 + 8; + size_t b64sig_size = 4096*4/3 + 8; + size_t b64data_size = b64header_size + b64payload_size + b64sig_size + 4; + char *b64header = safe_alloc(b64header_size); + char *b64payload = safe_alloc(b64payload_size); + char *b64sig = safe_alloc(b64sig_size); + char *b64data = safe_alloc(b64data_size); + unsigned int extjwt_hashsize; + char *extjwt_hash_val = NULL; + char *retval = NULL; + b64_encode(header, strlen(header), b64header, b64header_size); + b64_encode(payload, strlen(payload), b64payload, b64payload_size); + b64url(b64header); + b64url(b64payload); + snprintf(b64data, b64data_size, "%s.%s", b64header, b64payload); // generate first part of the token + if (config->method != EXTJWT_METHOD_NONE) + { + extjwt_hash_val = extjwt_hash(config->method, config->secret, strlen(config->secret), b64data, strlen(b64data), &extjwt_hashsize); // calculate the signature extjwt_hash + if (extjwt_hash_val) + { + b64_encode(extjwt_hash_val, extjwt_hashsize, b64sig, b64sig_size); + b64url(b64sig); + strlcat(b64data, ".", b64data_size); // append signature extjwt_hash to token + strlcat(b64data, b64sig, b64data_size); + retval = b64data; + } + } else + { + retval = b64data; + } + safe_free(header); + safe_free(b64header); + safe_free(b64payload); + safe_free(b64sig); + safe_free(extjwt_hash_val); + + if (retval != b64data) + safe_free(b64data); + + return retval; +} diff --git a/src/modules/geoip_base.c b/src/modules/geoip_base.c new file mode 100644 index 0000000..3244e1f --- /dev/null +++ b/src/modules/geoip_base.c @@ -0,0 +1,326 @@ +/* + * GEOIP Base module, needed for all geoip functions + * as this stores the geo information in ModData. + * (C) Copyright 2021-.. Syzop and The UnrealIRCd Team + * License: GPLv2 + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "geoip_base", + "5.0", + "Base module for geoip", + "UnrealIRCd Team", + "unrealircd-6", + }; + +struct geoip_base_config_s { + int check_on_load; +}; + +/* Forward declarations */ +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_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); +int geoip_base_configrun(ConfigFile *cf, ConfigEntry *ce, int type); +EVENT(geoip_base_set_existing_users_evt); +CMD_FUNC(cmd_geoip); + +ModDataInfo *geoip_md; /* Module Data structure which we acquire */ +struct geoip_base_config_s geoip_base_config; + +/* We can use GEOIPDATA() and GEOIPDATARAW() for fast access. + * People wanting to get this information from outside this module + * should use geoip_client(client) ! + */ + +#define GEOIPDATARAW(x) (moddata_client((x), geoip_md).ptr) +#define GEOIPDATA(x) ((GeoIPResult *)moddata_client((x), geoip_md).ptr) + +int geoip_base_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + ConfigEntry *cep; + int errors = 0; + int i; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "check-on-load")) + { + CheckNull(cep); + continue; + } + config_warn("%s:%i: unknown item geoip::%s", cep->file->filename, cep->line_number, cep->name); + } + + *errs = errors; + return errors ? -1 : 1; +} + +int geoip_base_configrun(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "check-on-load")) + geoip_base_config.check_on_load = config_checkval(cep->value, CFG_YESNO); + } + return 1; +} + +MOD_TEST() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_base_configtest); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&mreq, 0, sizeof(mreq)); + mreq.name = "geoip"; + mreq.free = geoip_base_free; + mreq.serialize = geoip_base_serialize; + mreq.unserialize = geoip_base_unserialize; + mreq.sync = MODDATA_SYNC_EARLY; + mreq.type = MODDATATYPE_CLIENT; + geoip_md = ModDataAdd(modinfo->handle, mreq); + if (!geoip_md) + abort(); + + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_base_configrun); + HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, geoip_base_handshake); + 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); + + /* set defaults */ + geoip_base_config.check_on_load = 1; + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + /* add info for all users upon module loading if enabled, but delay it a bit for data provider module to load */ + if (geoip_base_config.check_on_load) + { + EventAdd(modinfo->handle, "geoip_base_set_existing_users", geoip_base_set_existing_users_evt, NULL, 1000, 1); + } + return MOD_SUCCESS; +} + + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int geoip_base_handshake(Client *client) +{ + if (!client->ip) + return 0; + GeoIPResult *res = geoip_lookup(client->ip); + + if (!res) + return 0; + + if (GEOIPDATA(client)) + { + free_geoip_result(GEOIPDATA(client)); + GEOIPDATARAW(client) = NULL; + } + GEOIPDATARAW(client) = res; + return 0; +} + +void geoip_base_free(ModData *m) +{ + if (m->ptr) + { + free_geoip_result((GeoIPResult *)m->ptr); + m->ptr = NULL; + } +} + +const char *geoip_base_serialize(ModData *m) +{ + static char buf[512]; + GeoIPResult *geo; + + if (!m->ptr) + return NULL; + + geo = m->ptr; + snprintf(buf, sizeof(buf), "cc=%s|cd=%s", + geo->country_code, + geo->country_name); + + return buf; +} + +void geoip_base_unserialize(const char *str, ModData *m) +{ + char buf[512], *p=NULL, *varname, *value; + char *country_name = NULL; + char *country_code = NULL; + GeoIPResult *res; + + if (m->ptr == NULL) + { + free_geoip_result((GeoIPResult *)m->ptr); + m->ptr = NULL; + } + if (str == NULL) + return; + + strlcpy(buf, str, sizeof(buf)); + for (varname = strtoken(&p, buf, "|"); varname; varname = strtoken(&p, NULL, "|")) + { + value = strchr(varname, '='); + if (!value) + continue; + *value++ = '\0'; + if (!strcmp(varname, "cc")) + country_code = value; + else if (!strcmp(varname, "cd")) + country_name = value; + } + + if (!country_code || !country_name) + return; /* does not meet minimum criteria */ + + res = safe_alloc(sizeof(GeoIPResult)); + safe_strdup(res->country_name, country_name); + safe_strdup(res->country_code, country_code); + m->ptr = res; +} + +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); + } +} + +int geoip_connect_extinfo(Client *client, NameValuePrioList **list) +{ + GeoIPResult *geo = GEOIPDATA(client); + if (geo) + add_nvplist(list, 0, "country", geo->country_code); + return 0; +} + +int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list) +{ + GeoIPResult *geo; + char buf[512]; + int policy = whois_get_policy(client, target, "geo"); + + if (policy == WHOIS_CONFIG_DETAILS_NONE) + return 0; + + geo = GEOIPDATA(target); + if (!geo) + return 0; + + // we only have country atm, but if we add city then city goes in 'full' and + // country goes in 'limited' + // if policy == WHOIS_CONFIG_DETAILS_LIMITED ... + add_nvplist_numeric_fmt(list, 0, "geo", client, RPL_WHOISCOUNTRY, + "%s %s :is connecting from %s", + target->name, + geo->country_code, + geo->country_name); + return 0; +} + +CMD_FUNC(cmd_geoip) +{ + const char *ip = NULL; + Client *target; + GeoIPResult *res; + + if (!IsOper(client)) + { + sendnumeric(client, ERR_NOPRIVILEGES); + return; + } + + if ((parc < 2) || BadPtr(parv[1])) + { + /* Maybe some report */ + return; + } + + if (strchr(parv[1], '.') || strchr(parv[1], ':')) + { + ip = parv[1]; + } else { + target = find_user(parv[1], NULL); + if (!target) + { + sendnumeric(client, ERR_NOSUCHNICK, parv[1]); + return; + } + ip = target->ip; + if (!ip) + { + sendnotice(client, "User %s has no known IP address", client->name); // (eg: services bot) + return; + } + } + + res = geoip_lookup(ip); + + sendnotice(client, "*** GEOIP information for IP %s ***", ip); + if (!res) + { + sendnotice(client, "- No information available"); + return; + } else { + if (res->country_code) + sendnotice(client, "- Country code: %s", res->country_code); + if (res->country_name) + sendnotice(client, "- Country name: %s", res->country_name); + } + + free_geoip_result(res); + + sendnotice(client, "*** End of information ***"); +} diff --git a/src/modules/geoip_classic.c b/src/modules/geoip_classic.c new file mode 100644 index 0000000..c7299fc --- /dev/null +++ b/src/modules/geoip_classic.c @@ -0,0 +1,297 @@ +/* GEOIP Classic module + * (C) Copyright 2021 Bram Matthys and the UnrealIRCd team + * License: GPLv2 + */ + +#include "unrealircd.h" +#include + +ModuleHeader MOD_HEADER + = { + "geoip_classic", + "5.0", + "GEOIP using classic databases", + "UnrealIRCd Team", + "unrealircd-6", + }; + +struct geoip_classic_config_s { + char *v4_db_file; + char *v6_db_file; +/* for config reading only */ + int have_config; + int have_ipv4_database; + int have_ipv6_database; +}; + +/* Variables */ + +struct geoip_classic_config_s geoip_classic_config; +GeoIP *gi4 = NULL; +GeoIP *gi6 = NULL; + +/* Forward declarations */ +int geoip_classic_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +int geoip_classic_configposttest(int *errs); +int geoip_classic_configrun(ConfigFile *cf, ConfigEntry *ce, int type); +void geoip_classic_free(void); +GeoIPResult *geoip_lookup_classic(char *ip); + +int geoip_classic_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + ConfigEntry *cep; + int errors = 0; + int i; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip-classic")) + return 0; + + geoip_classic_config.have_config = 1; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "ipv4-database")) + { + if (geoip_classic_config.have_ipv4_database) + { + config_error("%s:%i: duplicate item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name); + continue; + } + if (!is_file_readable(cep->value, PERMDATADIR)) + { + config_error("%s:%i: set::geoip-classic::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); + errors++; + continue; + } + geoip_classic_config.have_ipv4_database = 1; + continue; + } + if (!strcmp(cep->name, "ipv6-database")) + { + if (geoip_classic_config.have_ipv6_database) + { + config_error("%s:%i: duplicate item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name); + continue; + } + if (!is_file_readable(cep->value, PERMDATADIR)) + { + config_error("%s:%i: set::geoip-classic::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); + errors++; + continue; + } + geoip_classic_config.have_ipv6_database = 1; + continue; + } + config_warn("%s:%i: unknown item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name); + } + + *errs = errors; + return errors ? -1 : 1; +} + +int geoip_classic_configposttest(int *errs) +{ + int errors = 0; + if (geoip_classic_config.have_config) + { + if (!geoip_classic_config.have_ipv4_database && !geoip_classic_config.have_ipv6_database) + { + config_error("geoip_classic: no database files specified! Remove set::geoip-classic to use defaults"); + errors++; + } + } else + { + safe_strdup(geoip_classic_config.v4_db_file, "GeoIP.dat"); + safe_strdup(geoip_classic_config.v6_db_file, "GeoIPv6.dat"); + + if (is_file_readable(geoip_classic_config.v4_db_file, PERMDATADIR)) + { + geoip_classic_config.have_ipv4_database = 1; + } else + { + config_warn("[geoip_classic] cannot open IPv4 database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_classic_config.v4_db_file, strerror(errno)); + safe_free(geoip_classic_config.v4_db_file); + } + if (is_file_readable(geoip_classic_config.v6_db_file, PERMDATADIR)) + { + geoip_classic_config.have_ipv6_database = 1; + } else + { + config_warn("[geoip_classic] cannot open IPv6 database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_classic_config.v6_db_file, strerror(errno)); + safe_free(geoip_classic_config.v6_db_file); + } + if (!geoip_classic_config.have_ipv4_database && !geoip_classic_config.have_ipv6_database) + { + config_error("[geoip_classic] couldn't read any database! Either put these in %s location " + "or specify another in set::geoip-classic config block", PERMDATADIR); + errors++; + } + } + + *errs = errors; + return errors ? -1 : 1; +} + +int geoip_classic_configrun(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip-classic")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "ipv4-database") && geoip_classic_config.have_ipv4_database) + safe_strdup(geoip_classic_config.v4_db_file, cep->value); + if (!strcmp(cep->name, "ipv6-database") && geoip_classic_config.have_ipv6_database) + safe_strdup(geoip_classic_config.v6_db_file, cep->value); + } + return 1; +} + +MOD_TEST() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + if (!CallbackAddPVoid(modinfo->handle, CALLBACKTYPE_GEOIP_LOOKUP, TO_PVOIDFUNC(geoip_lookup_classic))) + { + unreal_log(ULOG_ERROR, "geoip_classic", "GEOIP_ADD_CALLBACK_FAILED", NULL, + "geoip_classic: Could not install GEOIP_LOOKUP callback. " + "Most likely another geoip module is already loaded. " + "You can only load one!"); + return MOD_FAILED; + } + + geoip_classic_config.have_config = 0; + geoip_classic_config.have_ipv4_database = 0; + geoip_classic_config.have_ipv6_database = 0; + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_classic_configtest); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_classic_configposttest); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + geoip_classic_free(); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_classic_configrun); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + int found_good_file = 0; + + if (geoip_classic_config.v4_db_file) + { + convert_to_absolute_path(&geoip_classic_config.v4_db_file, PERMDATADIR); + gi4 = GeoIP_open(geoip_classic_config.v4_db_file, GEOIP_STANDARD | GEOIP_CHECK_CACHE | GEOIP_SILENCE); + if (gi4) + { + found_good_file = 1; + } else + { + int save_err = errno; + unreal_log(ULOG_WARNING, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL, + "[IPv4] Could not open '$filename': $system_error", + log_data_string("filename", geoip_classic_config.v4_db_file), + log_data_string("system_error", strerror(save_err))); + } + } + if (geoip_classic_config.v6_db_file) + { + convert_to_absolute_path(&geoip_classic_config.v6_db_file, PERMDATADIR); + gi6 = GeoIP_open(geoip_classic_config.v6_db_file, GEOIP_STANDARD | GEOIP_CHECK_CACHE | GEOIP_SILENCE); + if (gi6) + { + found_good_file = 1; + } else + { + int save_err = errno; + unreal_log(ULOG_WARNING, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL, + "[IPv6] Could not open '$filename': $system_error", + log_data_string("filename", geoip_classic_config.v6_db_file), + log_data_string("system_error", strerror(save_err))); + } + convert_to_absolute_path(&geoip_classic_config.v6_db_file, PERMDATADIR); + } + + if (!found_good_file) + { + unreal_log(ULOG_ERROR, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL, + "could not open any database!"); + return MOD_FAILED; + } + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + geoip_classic_free(); + return MOD_SUCCESS; +} + +void geoip_classic_free(void) +{ + if (gi4) + GeoIP_delete(gi4); + if (gi6) + GeoIP_delete(gi6); + gi4 = NULL; + gi6 = NULL; + safe_free(geoip_classic_config.v4_db_file); + safe_free(geoip_classic_config.v6_db_file); +} + +GeoIPResult *geoip_lookup_classic(char *ip) +{ + static char buf[256]; + const char *country_code, *country_name; + GeoIPLookup gl; + GeoIP *gi; + int geoid; + GeoIPResult *r; + + if (!ip) + return NULL; + + if (strchr(ip, ':')) + { + if (!gi6) + return NULL; + geoid = GeoIP_id_by_addr_v6_gl(gi6, ip, &gl); + gi = gi6; + } else + { + if (!gi4 || !strcmp(ip, "255.255.255.255")) + return NULL; + geoid = GeoIP_id_by_addr_gl(gi4, ip, &gl); + gi = gi4; + } + + if (geoid == 0) + return NULL; + + country_code = GeoIP_code_by_id(geoid); + country_name = GeoIP_country_name_by_id(gi, geoid); + + if (!country_code || !country_name) + return NULL; + + r = safe_alloc(sizeof(GeoIPResult)); + safe_strdup(r->country_code, country_code); + safe_strdup(r->country_name, country_name); + return r; +} + diff --git a/src/modules/geoip_csv.c b/src/modules/geoip_csv.c new file mode 100644 index 0000000..19ec91e --- /dev/null +++ b/src/modules/geoip_csv.c @@ -0,0 +1,838 @@ +/* + * IRC - Internet Relay Chat, src/modules/geoip_csv.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "geoip_csv", + "5.0", + "GEOIP using csv data files", + "UnrealIRCd Team", + "unrealircd-6", + }; + +struct geoip_csv_config_s { + char *v4_db_file; + char *v6_db_file; + char *countries_db_file; +/* for config reading only */ + int have_config; + int have_ipv4_database; + int have_ipv6_database; + int have_countries; +}; + +struct geoip_csv_ip_range { + uint32_t addr; + uint32_t mask; + int geoid; + struct geoip_csv_ip_range *next; +}; + +struct geoip_csv_ip6_range { + uint16_t addr[8]; + uint16_t mask[8]; + int geoid; + struct geoip_csv_ip6_range *next; +}; + +struct geoip_csv_country { + char code[10]; + char name[100]; + char continent[25]; + int id; + struct geoip_csv_country *next; +}; + +/* Variables */ +struct geoip_csv_config_s geoip_csv_config; +struct geoip_csv_ip_range *geoip_csv_ip_range_list[256]; // we are keeping a separate list for each possible first octet to speed up searching +struct geoip_csv_ip6_range *geoip_csv_ip6_range_list = NULL; // for ipv6 there would be too many separate lists so just use a single one +struct geoip_csv_country *geoip_csv_country_list = NULL; + +/* Forward declarations */ +static void geoip_csv_free_ipv4(void); +static void geoip_csv_free_ipv6(void); +static void geoip_csv_free_ipv6(void); +static void geoip_csv_free_countries(void); +static void geoip_csv_free(void); +static int geoip_csv_read_ipv4(char *file); +static int geoip_csv_ip6_convert(char *ip, uint16_t out[8]); +static int geoip_csv_read_ipv6(char *file); +static int geoip_csv_read_countries(char *file); +static struct geoip_csv_country *geoip_csv_get_country(int id); +static int geoip_csv_get_v4_geoid(char *iip); +static int geoip_csv_get_v6_geoid(char *iip); +int geoip_csv_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +int geoip_csv_configposttest(int *errs); +int geoip_csv_configrun(ConfigFile *cf, ConfigEntry *ce, int type); +void geoip_csv_free(void); +GeoIPResult *geoip_lookup_csv(char *ip); + +int geoip_csv_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + ConfigEntry *cep; + int errors = 0; + int i; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip-csv")) + return 0; + + geoip_csv_config.have_config = 1; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "ipv4-blocks-file")) + { + if (geoip_csv_config.have_ipv4_database) + { + config_error("%s:%i: duplicate item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); + continue; + } + if (!is_file_readable(cep->value, PERMDATADIR)) + { + config_error("%s:%i: set::geoip-csv::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); + errors++; + continue; + } + geoip_csv_config.have_ipv4_database = 1; + continue; + } + if (!strcmp(cep->name, "ipv6-blocks-file")) + { + if (geoip_csv_config.have_ipv6_database) + { + config_error("%s:%i: duplicate item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); + continue; + } + if (!is_file_readable(cep->value, PERMDATADIR)) + { + config_error("%s:%i: set::geoip-csv::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); + errors++; + continue; + } + geoip_csv_config.have_ipv6_database = 1; + continue; + } + if (!strcmp(cep->name, "countries-file")) + { + if (geoip_csv_config.have_countries) + { + config_error("%s:%i: duplicate item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); + continue; + } + if (!is_file_readable(cep->value, PERMDATADIR)) + { + config_error("%s:%i: set::geoip-csv::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); + errors++; + continue; + } + geoip_csv_config.have_countries = 1; + continue; + } + config_warn("%s:%i: unknown item set::geoip-csv::%s", cep->file->filename, cep->line_number, cep->name); + } + + *errs = errors; + return errors ? -1 : 1; +} + +int geoip_csv_configposttest(int *errs) +{ + int errors = 0; + if (geoip_csv_config.have_config) + { + if (!geoip_csv_config.have_countries) + { + config_error("[geoip_csv] no countries file specified! Remove set::geoip-csv to use defaults"); + errors++; + } + if (!geoip_csv_config.have_ipv4_database && !geoip_csv_config.have_ipv6_database) + { + config_error("[geoip_csv] no database files specified! Remove set::geoip-csv to use defaults"); + errors++; + } + } else + { + safe_strdup(geoip_csv_config.v4_db_file, "GeoLite2-Country-Blocks-IPv4.csv"); + safe_strdup(geoip_csv_config.v6_db_file, "GeoLite2-Country-Blocks-IPv6.csv"); + safe_strdup(geoip_csv_config.countries_db_file, "GeoLite2-Country-Locations-en.csv"); + + if (is_file_readable(geoip_csv_config.v4_db_file, PERMDATADIR)) + { + geoip_csv_config.have_ipv4_database = 1; + } else + { + config_warn("[geoip_csv] cannot open IPv4 blocks file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_csv_config.v4_db_file, strerror(errno)); + safe_free(geoip_csv_config.v4_db_file); + } + if (is_file_readable(geoip_csv_config.v6_db_file, PERMDATADIR)) + { + geoip_csv_config.have_ipv6_database = 1; + } else + { + config_warn("[geoip_csv] cannot open IPv6 blocks file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_csv_config.v6_db_file, strerror(errno)); + safe_free(geoip_csv_config.v6_db_file); + } + if (!is_file_readable(geoip_csv_config.countries_db_file, PERMDATADIR)) + { + config_error("[geoip_csv] cannot open countries file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_csv_config.countries_db_file, strerror(errno)); + safe_free(geoip_csv_config.countries_db_file); + errors++; + } + if (!geoip_csv_config.have_ipv4_database && !geoip_csv_config.have_ipv6_database) + { + config_error("[geoip_csv] couldn't read any blocks file! Either put these in %s location " + "or specify another in set::geoip-csv config block", PERMDATADIR); + errors++; + } + } + + *errs = errors; + return errors ? -1 : 1; +} + +int geoip_csv_configrun(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip-csv")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "ipv4-blocks-file") && geoip_csv_config.have_ipv4_database) + safe_strdup(geoip_csv_config.v4_db_file, cep->value); + if (!strcmp(cep->name, "ipv6-blocks-file") && geoip_csv_config.have_ipv6_database) + safe_strdup(geoip_csv_config.v6_db_file, cep->value); + if (!strcmp(cep->name, "countries-file")) + safe_strdup(geoip_csv_config.countries_db_file, cep->value); + } + return 1; +} + +MOD_TEST() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + if (!CallbackAddPVoid(modinfo->handle, CALLBACKTYPE_GEOIP_LOOKUP, TO_PVOIDFUNC(geoip_lookup_csv))) + { + unreal_log(ULOG_ERROR, "geoip_csv", "GEOIP_ADD_CALLBACK_FAILED", NULL, + "geoip_csv: Could not install GEOIP_LOOKUP callback. " + "Most likely another geoip module is already loaded. " + "You can only load one!"); + return MOD_FAILED; + } + + geoip_csv_config.have_config = 0; + geoip_csv_config.have_ipv4_database = 0; + geoip_csv_config.have_ipv6_database = 0; + geoip_csv_config.have_countries = 0; + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_csv_configtest); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_csv_configposttest); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + geoip_csv_free(); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_csv_configrun); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + int found_good_file = 0; + + if (geoip_csv_config.v4_db_file) + { + convert_to_absolute_path(&geoip_csv_config.v4_db_file, PERMDATADIR); + if (!geoip_csv_read_ipv4(geoip_csv_config.v4_db_file)) + { + found_good_file = 1; + } + } + if (geoip_csv_config.v6_db_file) + { + convert_to_absolute_path(&geoip_csv_config.v6_db_file, PERMDATADIR); + if (!geoip_csv_read_ipv6(geoip_csv_config.v6_db_file)) + { + found_good_file = 1; + } + } + if (!geoip_csv_config.countries_db_file) + { + unreal_log(ULOG_DEBUG, "geoip_csv", "GEOIP_NO_COUNTRIES", NULL, + "[BUG] No countries file specified"); + geoip_csv_free(); + return MOD_FAILED; + } + convert_to_absolute_path(&geoip_csv_config.countries_db_file, PERMDATADIR); + if (geoip_csv_read_countries(geoip_csv_config.countries_db_file)) + { + unreal_log(ULOG_ERROR, "geoip_csv", "GEOIP_CANNOT_OPEN_DB", NULL, + "could not open required countries file!"); + geoip_csv_free(); + return MOD_FAILED; + } + + if (!found_good_file) + { + unreal_log(ULOG_ERROR, "geoip_csv", "GEOIP_CANNOT_OPEN_DB", NULL, + "could not open any database!"); + geoip_csv_free(); + return MOD_FAILED; + } + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + geoip_csv_free(); + return MOD_SUCCESS; +} + +static void geoip_csv_free_ipv4(void) +{ + struct geoip_csv_ip_range *ptr, *oldptr; + int i; + for (i=0; i<256; i++) + { + ptr = geoip_csv_ip_range_list[i]; + geoip_csv_ip_range_list[i] = NULL; + while (ptr) + { + oldptr = ptr; + ptr = ptr->next; + safe_free(oldptr); + } + } +} + +static void geoip_csv_free_ipv6(void) +{ + struct geoip_csv_ip6_range *ptr, *oldptr; + ptr = geoip_csv_ip6_range_list; + geoip_csv_ip6_range_list = NULL; + while (ptr) + { + oldptr = ptr; + ptr = ptr->next; + safe_free(oldptr); + } +} + +static void geoip_csv_free_countries(void) +{ + struct geoip_csv_country *ptr, *oldptr; + ptr = geoip_csv_country_list; + geoip_csv_country_list = NULL; + while (ptr) + { + oldptr = ptr; + ptr = ptr->next; + safe_free(oldptr); + } +} + +static void geoip_csv_free(void) +{ + geoip_csv_free_ipv4(); + geoip_csv_free_ipv6(); + geoip_csv_free_countries(); +} + +/* reading data from files */ + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#define BUFLEN 8191 + +static int geoip_csv_read_ipv4(char *file) +{ + FILE *u; + char buf[BUFLEN+1]; + int cidr, geoid; + char ip[24]; + char netmask[24]; + uint32_t addr; + uint32_t mask; + struct geoip_csv_ip_range *curr[256]; + struct geoip_csv_ip_range *ptr; + memset(curr, 0, sizeof(curr)); + int i; + char *filename = NULL; + + safe_strdup(filename, file); + convert_to_absolute_path(&filename, CONFDIR); + u = fopen(filename, "r"); + safe_free(filename); + if (!u) + { + config_warn("[geoip_csv] Cannot open IPv4 ranges list file"); + return 1; + } + + if (!fgets(buf, BUFLEN, u)) + { + config_warn("[geoip_csv] IPv4 list file is empty"); + fclose(u); + return 1; + } + buf[BUFLEN] = '\0'; + while (fscanf(u, "%23[^/\n]/%d,%" STR(BUFLEN) "[^\n]\n", ip, &cidr, buf) == 3) + { + if (sscanf(buf, "%d,", &geoid) != 1) + { + /* missing geoid: can happen with valid files */ + continue; + } + + if (cidr < 1 || cidr > 32) + { + config_warn("[geoip_csv] Invalid CIDR found! IP=%s CIDR=%d! Bad CSV file?", ip, cidr); + continue; + } + + if (inet_pton(AF_INET, ip, &addr) < 1) + { + config_warn("[geoip_csv] Invalid IP found! \"%s\" Bad CSV file?", ip); + continue; + } + addr = htonl(addr); + + mask = 0; + while (cidr) + { /* calculate netmask */ + mask >>= 1; + mask |= (1<<31); + cidr--; + } + + i=0; + do + { /* multiple iterations in case CIDR is <8 and we have multiple first octets matching */ + uint8_t index = addr>>24; + if (!curr[index]) + { + geoip_csv_ip_range_list[index] = safe_alloc(sizeof(struct geoip_csv_ip_range)); + curr[index] = geoip_csv_ip_range_list[index]; + } else + { + curr[index]->next = safe_alloc(sizeof(struct geoip_csv_ip_range)); + curr[index] = curr[index]->next; + } + ptr = curr[index]; + ptr->next = NULL; + ptr->addr = addr; + ptr->mask = mask; + ptr->geoid = geoid; + i++; + index++; + } while (i<=((~mask)>>24)); + } + fclose(u); + return 0; +} + +static int geoip_csv_ip6_convert(char *ip, uint16_t out[8]) +{ /* convert text to binary form */ + uint16_t tmp[8]; + int i; + if (inet_pton(AF_INET6, ip, out) < 1) + return 0; + for (i=0; i<8; i++) + { + out[i] = htons(out[i]); + } + return 1; +} + +#define IPV6_STRING_SIZE 40 + +static int geoip_csv_read_ipv6(char *file) +{ + FILE *u; + char buf[BUFLEN+1]; + char *bptr, *optr; + int cidr, geoid; + char ip[IPV6_STRING_SIZE]; + uint16_t addr[8]; + uint16_t mask[8]; + struct geoip_csv_ip6_range *curr = NULL; + struct geoip_csv_ip6_range *ptr; + int error; + int length; + char *filename = NULL; + + safe_strdup(filename, file); + convert_to_absolute_path(&filename, CONFDIR); + u = fopen(filename, "r"); + safe_free(filename); + if (!u) + { + config_warn("[geoip_csv] Cannot open IPv6 ranges list file"); + return 1; + } + if (!fgets(buf, BUFLEN, u)) + { + config_warn("[geoip_csv] IPv6 list file is empty"); + fclose(u); + return 1; + } + while (fgets(buf, BUFLEN, u)) + { + error = 0; + bptr = buf; + optr = ip; + length = 0; + while (*bptr != '/') + { + if (!*bptr) + { + error = 1; + break; + } + if (++length >= IPV6_STRING_SIZE) + { + ip[IPV6_STRING_SIZE-1] = '\0'; + config_warn("[geoip_csv] Too long IPv6 address found, starts with %s. Bad CSV file?", ip); + error = 1; + break; + } + *optr++ = *bptr++; + } + if (error) + continue; + *optr = '\0'; + bptr++; + if (!geoip_csv_ip6_convert(ip, addr)) + { + config_warn("[geoip_csv] Invalid IP found! \"%s\" Bad CSV file?", ip); + continue; + } + sscanf(bptr, "%d,%d,", &cidr, &geoid); + if (cidr < 1 || cidr > 128) + { + config_warn("[geoip_csv] Invalid CIDR found! CIDR=%d Bad CSV file?", cidr); + continue; + } + + memset(mask, 0, 16); + + int mask_bit = 0; + while (cidr) + { /* calculate netmask */ + mask[mask_bit/16] |= 1<<(15-(mask_bit%16)); + mask_bit++; + cidr--; + } + + if (!curr) + { + geoip_csv_ip6_range_list = safe_alloc(sizeof(struct geoip_csv_ip6_range)); + curr = geoip_csv_ip6_range_list; + } else + { + curr->next = safe_alloc(sizeof(struct geoip_csv_ip6_range)); + curr = curr->next; + } + ptr = curr; + ptr->next = NULL; + memcpy(ptr->addr, addr, 16); + memcpy(ptr->mask, mask, 16); + ptr->geoid = geoid; + } + fclose(u); + return 0; +} + +/* CSV fields; no STATE_GEONAME_ID because of using %d in fscanf */ +#define STATE_LOCALE_CODE 0 +#define STATE_CONTINENT_CODE 1 +#define STATE_CONTINENT_NAME 2 +#define STATE_COUNTRY_ISO_CODE 3 +#define STATE_COUNTRY_NAME 4 +#define STATE_IS_IN_EU 5 + +#define MEMBER_SIZE(type,member) sizeof(((type *)0)->member) + +static int geoip_csv_read_countries(char *file) +{ + FILE *u; + char code[MEMBER_SIZE(struct geoip_csv_country, code)]; + char continent[MEMBER_SIZE(struct geoip_csv_country, continent)]; + char name[MEMBER_SIZE(struct geoip_csv_country, name)]; + char buf[BUFLEN+1]; + int state; + int id; + struct geoip_csv_country *curr = NULL; + char *filename = NULL; + + safe_strdup(filename, file); + convert_to_absolute_path(&filename, CONFDIR); + u = fopen(filename, "r"); + safe_free(filename); + if (!u) + { + config_warn("[geoip_csv] Cannot open countries list file"); + return 1; + } + + if (!fgets(buf, BUFLEN, u)) + { + config_warn("[geoip_csv] Countries list file is empty"); + fclose(u); + return 1; + } + while (fscanf(u, "%d,%" STR(BUFLEN) "[^\n]", &id, buf) == 2) + { /* getting country ID integer and all other data in string */ + char *ptr = buf; + char *codeptr = code; + char *contptr = continent; + char *nptr = name; + int quote_open = 0; + int length = 0; + state = STATE_LOCALE_CODE; + while (*ptr) + { + switch (state) + { + case STATE_CONTINENT_NAME: + if (*ptr == ',') + goto next_line; /* no continent? */ + if (length >= MEMBER_SIZE(struct geoip_csv_country, continent)) + { + *contptr = '\0'; + config_warn("[geoip_csv] Too long continent name found: `%s`. If you are sure your countries file is correct, please file a bug report.", continent); + goto next_line; + } + *contptr = *ptr; /* scan for continent name */ + contptr++; + length++; + break; + case STATE_COUNTRY_ISO_CODE: + if (*ptr == ',') /* country code is empty */ + goto next_line; /* -- that means only the continent is specified - we ignore it completely */ + if (length >= MEMBER_SIZE(struct geoip_csv_country, code)) + { + *codeptr = '\0'; + config_warn("[geoip_csv] Too long country code found: `%s`. If you are sure your countries file is correct, please file a bug report.", code); + goto next_line; + } + *codeptr = *ptr; // scan for country code (DE, PL, US etc) + codeptr++; + length++; + break; + case STATE_COUNTRY_NAME: + goto read_country_name; + default: + break; // ignore this field and wait for next one + } + ptr++; + if (*ptr == ',') + { + length = 0; + ptr++; + state++; + } + } + read_country_name: + *codeptr = '\0'; + *contptr = '\0'; + length = 0; + while (*ptr) + { + switch (*ptr) + { + case '"': + quote_open = !quote_open; + ptr++; + continue; + case ',': + if (!quote_open) + goto end_country_name; /* we reached the end of current CSV field */ + /* fall through */ + default: + *nptr++ = *ptr++; + if (length >= MEMBER_SIZE(struct geoip_csv_country, name)) + { + *nptr = '\0'; + config_warn("[geoip_csv] Too long country name found: `%s`. If you are sure your countries file is correct, please file a bug report.", name); + goto next_line; + } + break; // scan for country name + } + } + end_country_name: + *nptr = '\0'; + if (geoip_csv_country_list) + { + curr->next = safe_alloc(sizeof(struct geoip_csv_country)); + curr = curr->next; + } else + { + geoip_csv_country_list = safe_alloc(sizeof(struct geoip_csv_country)); + curr = geoip_csv_country_list; + } + curr->next = NULL; + strcpy(curr->code, code); + strcpy(curr->name, name); + strcpy(curr->continent, continent); + curr->id = id; + next_line: continue; + } + fclose(u); + return 0; +} + +static struct geoip_csv_country *geoip_csv_get_country(int id) +{ + struct geoip_csv_country *curr = geoip_csv_country_list; + if (!curr) + return NULL; + int found = 0; + for (;curr;curr = curr->next) + { + if (curr->id == id) + { + found = 1; + break; + } + } + if (found) + return curr; + return NULL; +} + +static int geoip_csv_get_v4_geoid(char *iip) +{ + uint32_t addr, tmp_addr; + struct geoip_csv_ip_range *curr; + int i; + int found = 0; + if (inet_pton(AF_INET, iip, &addr) < 1) + { + unreal_log(ULOG_WARNING, "geoip_csv", "UNSUPPORTED_IP", NULL, "Invalid or unsupported client IP $ip", log_data_string("ip", iip)); + return 0; + } + addr = htonl(addr); + curr = geoip_csv_ip_range_list[addr>>24]; + if (curr) + { + i = 0; + for (;curr;curr = curr->next) + { + tmp_addr = addr; + tmp_addr &= curr->mask; /* mask the address to filter out net prefix only */ + if (tmp_addr == curr->addr) + { /* ... and match it to the loaded data */ + found = 1; + break; + } + i++; + } + } + if (found) + return curr->geoid; + return 0; +} + +static int geoip_csv_get_v6_geoid(char *iip) +{ + uint16_t addr[8]; + struct geoip_csv_ip6_range *curr; + int i; + int found = 0; + + if (!geoip_csv_ip6_convert(iip, addr)) + { + unreal_log(ULOG_WARNING, "geoip_csv", "UNSUPPORTED_IP", NULL, "Invalid or unsupported client IP $ip", log_data_string("ip", iip)); + return 0; + } + curr = geoip_csv_ip6_range_list; + if (curr) + { + for (;curr;curr = curr->next) + { + found = 1; + for (i=0; i<8; i++) + { + if (curr->addr[i] != (addr[i] & curr->mask[i])) + { /* compare net address to loaded data */ + found = 0; + break; + } + } + if(found) + break; + } + } + if (found) + return curr->geoid; + return 0; +} + +GeoIPResult *geoip_lookup_csv(char *ip) +{ + int geoid; + struct geoip_csv_country *country; + GeoIPResult *r; + + if (!ip) + return NULL; + + if (strchr(ip, ':')) + { + geoid = geoip_csv_get_v6_geoid(ip); + } else + { + geoid = geoip_csv_get_v4_geoid(ip); + } + + if (geoid == 0) + return NULL; + + country = geoip_csv_get_country(geoid); + + if (!country) + return NULL; + + r = safe_alloc(sizeof(GeoIPResult)); + safe_strdup(r->country_code, country->code); + safe_strdup(r->country_name, country->name); + return r; +} + diff --git a/src/modules/geoip_maxmind.c b/src/modules/geoip_maxmind.c new file mode 100644 index 0000000..06581f3 --- /dev/null +++ b/src/modules/geoip_maxmind.c @@ -0,0 +1,239 @@ +/* GEOIP maxmind module + * (C) Copyright 2021 Bram Matthys and the UnrealIRCd team + * License: GPLv2 + */ + +#include "unrealircd.h" +#include + +ModuleHeader MOD_HEADER + = { + "geoip_maxmind", + "5.0", + "GEOIP using maxmind databases", + "UnrealIRCd Team", + "unrealircd-6", + }; + +struct geoip_maxmind_config_s { + char *db_file; +/* for config reading only */ + int have_config; + int have_database; +}; + +/* Variables */ + +struct geoip_maxmind_config_s geoip_maxmind_config; +MMDB_s mmdb; + +/* Forward declarations */ +int geoip_maxmind_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +int geoip_maxmind_configposttest(int *errs); +int geoip_maxmind_configrun(ConfigFile *cf, ConfigEntry *ce, int type); +void geoip_maxmind_free(void); +GeoIPResult *geoip_lookup_maxmind(char *ip); + +int geoip_maxmind_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + ConfigEntry *cep; + int errors = 0; + int i; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip-maxmind")) + return 0; + + geoip_maxmind_config.have_config = 1; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "database")) + { + if (geoip_maxmind_config.have_database) + { + config_error("%s:%i: duplicate item set::geoip-maxmind::%s", cep->file->filename, cep->line_number, cep->name); + continue; + } + if (!is_file_readable(cep->value, PERMDATADIR)) + { + config_error("%s:%i: set::geoip-maxmind::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); + errors++; + continue; + } + geoip_maxmind_config.have_database = 1; + continue; + } + config_warn("%s:%i: unknown item set::geoip-maxmind::%s", cep->file->filename, cep->line_number, cep->name); + } + + *errs = errors; + return errors ? -1 : 1; +} + +int geoip_maxmind_configposttest(int *errs) +{ + int errors = 0; + if (geoip_maxmind_config.have_config) + { + if (!geoip_maxmind_config.have_database) + { + config_error("geoip_maxmind: no database file specified! Remove set::geoip-maxmind to use defaults"); + errors++; + } + } else + { + safe_strdup(geoip_maxmind_config.db_file, "GeoLite2-Country.mmdb"); + + if (is_file_readable(geoip_maxmind_config.db_file, PERMDATADIR)) + { + geoip_maxmind_config.have_database = 1; + } else + { + config_error("[geoip_maxmind] cannot open database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_maxmind_config.db_file, strerror(errno)); + safe_free(geoip_maxmind_config.db_file); + errors++; + } + } + + *errs = errors; + return errors ? -1 : 1; +} + +int geoip_maxmind_configrun(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name) + return 0; + + if (strcmp(ce->name, "geoip-maxmind")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "database") && geoip_maxmind_config.have_database) + safe_strdup(geoip_maxmind_config.db_file, cep->value); + } + return 1; +} + +MOD_TEST() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + if (!CallbackAddPVoid(modinfo->handle, CALLBACKTYPE_GEOIP_LOOKUP, TO_PVOIDFUNC(geoip_lookup_maxmind))) + { + unreal_log(ULOG_ERROR, "geoip_maxmind", "GEOIP_ADD_CALLBACK_FAILED", NULL, + "geoip_maxmind: Could not install GEOIP_LOOKUP callback. " + "Most likely another geoip module is already loaded. " + "You can only load one!"); + return MOD_FAILED; + } + + geoip_maxmind_config.have_config = 0; + geoip_maxmind_config.have_database = 0; + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_maxmind_configtest); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_maxmind_configposttest); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_maxmind_configrun); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + geoip_maxmind_free(); + convert_to_absolute_path(&geoip_maxmind_config.db_file, PERMDATADIR); + + int status = MMDB_open(geoip_maxmind_config.db_file, MMDB_MODE_MMAP, &mmdb); + + if (status != MMDB_SUCCESS) { + int save_err = errno; + unreal_log(ULOG_WARNING, "geoip_maxmind", "GEOIP_CANNOT_OPEN_DB", NULL, + "Could not open '$filename' - $maxmind_error; IO error: $io_error", + log_data_string("filename", geoip_maxmind_config.db_file), + log_data_string("maxmind_error", MMDB_strerror(status)), + log_data_string("io_error", (status == MMDB_IO_ERROR)?strerror(save_err):"none")); + return MOD_FAILED; + } + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + geoip_maxmind_free(); + return MOD_SUCCESS; +} + +void geoip_maxmind_free(void) +{ + MMDB_close(&mmdb); +} + +GeoIPResult *geoip_lookup_maxmind(char *ip) +{ + int gai_error, mmdb_error, status; + MMDB_lookup_result_s result; + MMDB_entry_data_s country_code; + MMDB_entry_data_s country_name; + char *country_code_str, *country_name_str; + GeoIPResult *r; + + if (!ip) + return NULL; + + result = MMDB_lookup_string(&mmdb, ip, &gai_error, &mmdb_error); + if (gai_error) + { + unreal_log(ULOG_DEBUG, "geoip_maxmind", "GEOIP_DB_ERROR", NULL, + "libmaxminddb: getaddrinfo error for $ip: $error", + log_data_string("ip", ip), + log_data_string("error", gai_strerror(gai_error))); + return NULL; + } + + if (mmdb_error != MMDB_SUCCESS) + { + unreal_log(ULOG_DEBUG, "geoip_maxmind", "GEOIP_DB_ERROR", NULL, + "libmaxminddb: library error for $ip: $error", + log_data_string("ip", ip), + log_data_string("error", MMDB_strerror(mmdb_error))); + return NULL; + } + + if (!result.found_entry) /* no result */ + return NULL; + + status = MMDB_get_value(&result.entry, &country_code, "country", "iso_code", NULL); + if (status != MMDB_SUCCESS || !country_code.has_data || country_code.type != MMDB_DATA_TYPE_UTF8_STRING) + return NULL; + status = MMDB_get_value(&result.entry, &country_name, "country", "names", "en", NULL); + if (status != MMDB_SUCCESS || !country_name.has_data || country_name.type != MMDB_DATA_TYPE_UTF8_STRING) + return NULL; + + /* these results are not null-terminated */ + country_code_str = safe_alloc(country_code.data_size + 1); + country_name_str = safe_alloc(country_name.data_size + 1); + memcpy(country_code_str, country_code.utf8_string, country_code.data_size); + country_code_str[country_code.data_size] = '\0'; + memcpy(country_name_str, country_name.utf8_string, country_name.data_size); + country_name_str[country_name.data_size] = '\0'; + + r = safe_alloc(sizeof(GeoIPResult)); + r->country_code = country_code_str; + r->country_name = country_name_str; + return r; +} + diff --git a/src/modules/globops.c b/src/modules/globops.c index 8afcb1f..284e62c 100644 --- a/src/modules/globops.c +++ b/src/modules/globops.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /globops", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -57,9 +57,7 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_globops) { - char *message; - - message = parc > 1 ? parv[1] : NULL; + const char *message = parc > 1 ? parv[1] : NULL; if (BadPtr(message)) { diff --git a/src/modules/help.c b/src/modules/help.c index 65f2976..7d0e496 100644 --- a/src/modules/help.c +++ b/src/modules/help.c @@ -33,7 +33,7 @@ ModuleHeader MOD_HEADER "5.0", "command /help", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -57,7 +57,7 @@ MOD_UNLOAD() #define HDR(str) sendto_one(client, NULL, ":%s 290 %s :%s", me.name, client->name, str); #define SND(str) sendto_one(client, NULL, ":%s 292 %s :%s", me.name, client->name, str); -ConfigItem_help *find_Help(char *command) +ConfigItem_help *find_Help(const char *command) { ConfigItem_help *help; @@ -80,7 +80,7 @@ ConfigItem_help *find_Help(char *command) return NULL; } -void parse_help(Client *client, char *name, char *help) +void parse_help(Client *client, const char *help) { ConfigItem_help *helpitem; MOTDLine *text; @@ -109,7 +109,7 @@ void parse_help(Client *client, char *name, char *help) SND(" We're sorry, we don't have help available for the command you requested."); SND(" -"); sendto_one(client, NULL, ":%s 292 %s : ***** Go to %s if you have any further questions *****", - me.name, client->name, helpchan); + me.name, client->name, HELP_CHANNEL); SND(" -"); return; } @@ -131,7 +131,7 @@ void parse_help(Client *client, char *name, char *help) */ CMD_FUNC(cmd_help) { - char *helptopic; + const char *helptopic; if (!MyUser(client)) return; /* never remote */ @@ -141,5 +141,5 @@ CMD_FUNC(cmd_help) if (helptopic && (*helptopic == '?')) helptopic++; - parse_help(client, client->name, BadPtr(helptopic) ? NULL : helptopic); + parse_help(client, BadPtr(helptopic) ? NULL : helptopic); } diff --git a/src/modules/hideserver.c b/src/modules/hideserver.c index e0231a1..4e22bca 100644 --- a/src/modules/hideserver.c +++ b/src/modules/hideserver.c @@ -49,7 +49,7 @@ ModuleHeader MOD_HEADER "5.0", "Hide servers from /MAP & /LINKS", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; static void InitConf() @@ -95,10 +95,10 @@ MOD_INIT() MOD_LOAD() { - if (!CommandOverrideAdd(MyMod, "MAP", override_map)) + if (!CommandOverrideAdd(MyMod, "MAP", 0, override_map)) return MOD_FAILED; - if (!CommandOverrideAdd(MyMod, "LINKS", override_links)) + if (!CommandOverrideAdd(MyMod, "LINKS", 0, override_links)) return MOD_FAILED; return MOD_SUCCESS; @@ -118,35 +118,35 @@ static int cb_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type == CONFIG_MAIN) { - if (!strcmp(ce->ce_varname, "hideserver")) + if (!strcmp(ce->name, "hideserver")) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "hide")) + if (!strcmp(cep->name, "hide")) { /* No checking needed */ } - else if (!cep->ce_vardata) + else if (!cep->value) { config_error("%s:%i: %s::%s without value", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - ce->ce_varname, cep->ce_varname); + cep->file->filename, + cep->line_number, + ce->name, cep->name); errors++; continue; } - else if (!strcmp(cep->ce_varname, "disable-map")) + else if (!strcmp(cep->name, "disable-map")) ; - else if (!strcmp(cep->ce_varname, "disable-links")) + else if (!strcmp(cep->name, "disable-links")) ; - else if (!strcmp(cep->ce_varname, "map-deny-message")) + else if (!strcmp(cep->name, "map-deny-message")) ; - else if (!strcmp(cep->ce_varname, "links-deny-message")) + else if (!strcmp(cep->name, "links-deny-message")) ; else { config_error("%s:%i: unknown directive hideserver::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } } @@ -165,31 +165,31 @@ static int cb_conf(ConfigFile *cf, ConfigEntry *ce, int type) if (type == CONFIG_MAIN) { - if (!strcmp(ce->ce_varname, "hideserver")) + if (!strcmp(ce->name, "hideserver")) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "disable-map")) - Settings.disable_map = config_checkval(cep->ce_vardata, CFG_YESNO); - else if (!strcmp(cep->ce_varname, "disable-links")) - Settings.disable_links = config_checkval(cep->ce_vardata, CFG_YESNO); - else if (!strcmp(cep->ce_varname, "map-deny-message")) + if (!strcmp(cep->name, "disable-map")) + Settings.disable_map = config_checkval(cep->value, CFG_YESNO); + else if (!strcmp(cep->name, "disable-links")) + Settings.disable_links = config_checkval(cep->value, CFG_YESNO); + else if (!strcmp(cep->name, "map-deny-message")) { - safe_strdup(Settings.map_deny_message, cep->ce_vardata); + safe_strdup(Settings.map_deny_message, cep->value); } - else if (!strcmp(cep->ce_varname, "links-deny-message")) + else if (!strcmp(cep->name, "links-deny-message")) { - safe_strdup(Settings.links_deny_message, cep->ce_vardata); + safe_strdup(Settings.links_deny_message, cep->value); } - else if (!strcmp(cep->ce_varname, "hide")) + else if (!strcmp(cep->name, "hide")) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!strcasecmp(cepp->ce_varname, me.name)) + if (!strcasecmp(cepp->name, me.name)) continue; ca = safe_alloc(sizeof(ConfigItem_ulines)); - safe_strdup(ca->servername, cepp->ce_varname); + safe_strdup(ca->servername, cepp->name); AddListItem(ca, HiddenServers); } } @@ -231,7 +231,7 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng else { sendnumeric(client, RPL_MAP, prompt, - length, server->name, server->serv->users, IsOper(client) ? server->id : ""); + length, server->name, server->server->users, IsOper(client) ? server->id : ""); cnt = 0; } @@ -248,7 +248,7 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng list_for_each_entry(acptr, &global_server_list, client_node) { - if (acptr->srvptr != server || + if (acptr->uplink != server || (IsULine(acptr) && HIDE_ULINES && !ValidatePermissionsForPath("server:info:map:ulines",client,NULL,NULL,NULL))) continue; if (FindHiddenServer(acptr->name)) @@ -263,7 +263,7 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng continue; if (FindHiddenServer(acptr->name)) break; - if (acptr->srvptr != server) + if (acptr->uplink != server) continue; if (!IsMap(acptr)) continue; @@ -284,7 +284,7 @@ void dump_flat_map(Client *client, Client *server, int length) hide_ulines = (HIDE_ULINES && !ValidatePermissionsForPath("server:info:map:ulines",client,NULL,NULL,NULL)) ? 1 : 0; - sendnumeric(client, RPL_MAP, "", length, server->name, server->serv->users, ""); + sendnumeric(client, RPL_MAP, "", length, server->name, server->server->users, ""); list_for_each_entry(acptr, &global_server_list, client_node) { @@ -304,7 +304,7 @@ 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->serv->users, ""); + sendnumeric(client, RPL_MAP, buf, length-2, acptr->name, acptr->server->users, ""); } } @@ -388,7 +388,7 @@ CMD_OVERRIDE_FUNC(override_links) sendnumeric(client, RPL_LINKS, acptr->name, me.name, 1, (acptr->info[0] ? acptr->info : "(Unknown Location)")); else - sendnumeric(client, RPL_LINKS, acptr->name, acptr->serv->up, + sendnumeric(client, RPL_LINKS, acptr->name, acptr->uplink->name, acptr->hopcount, (acptr->info[0] ? acptr->info : "(Unknown Location)")); } diff --git a/src/modules/history.c b/src/modules/history.c index e8311e0..d99823e 100644 --- a/src/modules/history.c +++ b/src/modules/history.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", "Simple history command for end-users", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #define HISTORY_LINES_DEFAULT 100 @@ -76,13 +76,16 @@ CMD_FUNC(cmd_history) Channel *channel; int lines = HISTORY_LINES_DEFAULT; + if (!MyUser(client)) + return; + if ((parc < 2) || BadPtr(parv[1])) { history_usage(client); return; } - channel = find_channel(parv[1], NULL); + channel = find_channel(parv[1]); if (!channel) { sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]); @@ -91,13 +94,13 @@ CMD_FUNC(cmd_history) if (!IsMember(client, channel)) { - sendnumeric(client, ERR_NOTONCHANNEL, channel->chname); + sendnumeric(client, ERR_NOTONCHANNEL, channel->name); return; } if (!has_channel_mode(channel, 'H')) { - sendnotice(client, "Channel %s does not have channel mode +H set", channel->chname); + sendnotice(client, "Channel %s does not have channel mode +H set", channel->name); return; } @@ -125,7 +128,7 @@ CMD_FUNC(cmd_history) filter.cmd = HFC_SIMPLE; filter.last_lines = lines; - if ((r = history_request(channel->chname, &filter))) + if ((r = history_request(channel->name, &filter))) { history_send_result(client, r); free_history_result(r); diff --git a/src/modules/history_backend_mem.c b/src/modules/history_backend_mem.c index 897da65..9fd5117 100644 --- a/src/modules/history_backend_mem.c +++ b/src/modules/history_backend_mem.c @@ -17,7 +17,7 @@ ModuleHeader MOD_HEADER "2.0", "History backend: memory", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Defines */ @@ -97,16 +97,16 @@ static void freecfg(struct cfgstruct *cfg); static void hbm_init_hashes(ModuleInfo *m); static void init_history_storage(ModuleInfo *modinfo); int hbm_modechar_del(Channel *channel, int modechar); -int hbm_history_add(char *object, MessageTag *mtags, char *line); +int hbm_history_add(const char *object, MessageTag *mtags, const char *line); int hbm_history_cleanup(HistoryLogObject *h); -HistoryResult *hbm_history_request(char *object, HistoryFilter *filter); -int hbm_history_destroy(char *object); -int hbm_history_set_limit(char *object, int max_lines, long max_time); +HistoryResult *hbm_history_request(const char *object, HistoryFilter *filter); +int hbm_history_destroy(const char *object); +int hbm_history_set_limit(const char *object, int max_lines, long max_time); EVENT(history_mem_clean); EVENT(history_mem_init); static int hbm_read_masterdb(void); static void hbm_read_dbs(void); -static int hbm_read_db(char *fname); +static int hbm_read_db(const char *fname); static int hbm_write_masterdb(void); static int hbm_write_db(HistoryLogObject *h); static void hbm_delete_db(HistoryLogObject *h); @@ -200,7 +200,7 @@ EVENT(history_mem_init) MOD_UNLOAD() { - if (loop.ircd_terminating) + if (loop.terminating) hbm_flush(); freecfg(&test); freecfg(&cfg); @@ -235,6 +235,7 @@ static void setcfg(struct cfgstruct *cfg) static void freecfg(struct cfgstruct *cfg) { + safe_free(cfg->masterdb); safe_free(cfg->directory); safe_free(cfg->db_secret); } @@ -264,40 +265,40 @@ int hbm_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { int errors = 0; - if ((type != CONFIG_SET_HISTORY_CHANNEL) || !ce || !ce->ce_varname) + if ((type != CONFIG_SET_HISTORY_CHANNEL) || !ce || !ce->name) return 0; - if (!strcmp(ce->ce_varname, "persist")) + if (!strcmp(ce->name, "persist")) { - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: missing parameter", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } else { - test.persist = config_checkval(ce->ce_vardata, CFG_YESNO); + test.persist = config_checkval(ce->value, CFG_YESNO); } } else - if (!strcmp(ce->ce_varname, "db-secret")) + if (!strcmp(ce->name, "db-secret")) { - char *err; - if ((err = unrealdb_test_secret(ce->ce_vardata))) + const char *err; + if ((err = unrealdb_test_secret(ce->value))) { - config_error("%s:%i: set::history::channel::db-secret: %s", ce->ce_fileptr->cf_filename, ce->ce_varlinenum, err); + config_error("%s:%i: set::history::channel::db-secret: %s", ce->file->filename, ce->line_number, err); errors++; } - safe_strdup(test.db_secret, ce->ce_vardata); + safe_strdup(test.db_secret, ce->value); } else - if (!strcmp(ce->ce_varname, "directory")) // or "path" ? + if (!strcmp(ce->name, "directory")) // or "path" ? { - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: missing parameter", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } else { - safe_strdup(test.directory, ce->ce_vardata); + safe_strdup(test.directory, ce->value); hbm_set_masterdb_filename(&test); } } else @@ -322,7 +323,7 @@ int hbm_config_posttest(int *errs) } else if (!test.db_secret && test.persist) { - config_error("set::history::channel::db-secret needs to be set."); // TODO: REFER TO FAQ OR OTHER ENTRY!!!! + config_error("set::history::channel::db-secret needs to be set."); errors++; } else if (test.db_secret && test.persist) @@ -367,22 +368,22 @@ hbm_config_posttest_end: /** Configure ourselves based on the set::history::channel settings */ int hbm_config_run(ConfigFile *cf, ConfigEntry *ce, int type) { - if ((type != CONFIG_SET_HISTORY_CHANNEL) || !ce || !ce->ce_varname) + if ((type != CONFIG_SET_HISTORY_CHANNEL) || !ce || !ce->name) return 0; - if (!strcmp(ce->ce_varname, "persist")) + if (!strcmp(ce->name, "persist")) { - cfg.persist = config_checkval(ce->ce_vardata, CFG_YESNO); + cfg.persist = config_checkval(ce->value, CFG_YESNO); } else - if (!strcmp(ce->ce_varname, "directory")) // or "path" ? + if (!strcmp(ce->name, "directory")) // or "path" ? { - safe_strdup(cfg.directory, ce->ce_vardata); + safe_strdup(cfg.directory, ce->value); convert_to_absolute_path(&cfg.directory, PERMDATADIR); hbm_set_masterdb_filename(&cfg); } else - if (!strcmp(ce->ce_varname, "db-secret")) + if (!strcmp(ce->name, "db-secret")) { - safe_strdup(cfg.db_secret, ce->ce_vardata); + safe_strdup(cfg.db_secret, ce->value); } else { return 0; /* unknown option to us, let another module handle it */ @@ -403,7 +404,7 @@ int hbm_rehash_complete(void) return 0; } -char *history_storage_capability_parameter(Client *client) +const char *history_storage_capability_parameter(Client *client) { static char buf[128]; @@ -426,12 +427,12 @@ static void init_history_storage(ModuleInfo *modinfo) ClientCapabilityAdd(modinfo->handle, &cap, NULL); } -uint64_t hbm_hash(char *object) +uint64_t hbm_hash(const char *object) { return siphash_nocase(object, siphashkey_history_backend_mem) % HISTORY_BACKEND_MEM_HASH_TABLE_SIZE; } -HistoryLogObject *hbm_find_object(char *object) +HistoryLogObject *hbm_find_object(const char *object) { int hashv = hbm_hash(object); HistoryLogObject *h; @@ -444,7 +445,7 @@ HistoryLogObject *hbm_find_object(char *object) return NULL; } -HistoryLogObject *hbm_find_or_add_object(char *object) +HistoryLogObject *hbm_find_or_add_object(const char *object) { int hashv = hbm_hash(object); HistoryLogObject *h; @@ -480,7 +481,7 @@ int hbm_modechar_del(Channel *channel, int modechar) if (!cfg.persist) return 0; - if ((modechar == 'P') && ((h = hbm_find_object(channel->chname)))) + if ((modechar == 'P') && ((h = hbm_find_object(channel->name)))) { /* Channel went from +P to -P and also has channel history: delete the history file */ hbm_delete_db(h); @@ -538,7 +539,7 @@ void hbm_duplicate_mtags(HistoryLogLine *l, MessageTag *m) } /** Add a line to a history object */ -void hbm_history_add_line(HistoryLogObject *h, MessageTag *mtags, char *line) +void hbm_history_add_line(HistoryLogObject *h, MessageTag *mtags, const char *line) { HistoryLogLine *l = safe_alloc(sizeof(HistoryLogLine) + strlen(line)); strcpy(l->line, line); /* safe, see memory allocation above ^ */ @@ -589,12 +590,14 @@ void hbm_history_del_line(HistoryLogObject *h, HistoryLogLine *l) } /** Add history entry */ -int hbm_history_add(char *object, MessageTag *mtags, char *line) +int hbm_history_add(const char *object, MessageTag *mtags, const char *line) { HistoryLogObject *h = hbm_find_or_add_object(object); if (!h->max_lines) { - sendto_realops("hbm_history_add() for '%s', which has no limit", h->name); + unreal_log(ULOG_WARNING, "history", "BUG_HISTORY_ADD_NO_LIMIT", NULL, + "[BUG] hbm_history_add() called for $object, which has no limit set", + log_data_string("object", h->name)); #ifdef DEBUGMODE abort(); #else @@ -955,7 +958,7 @@ static int hbm_return_between(HistoryResult *r, HistoryLogObject *h, HistoryFilt return 0; } -HistoryResult *hbm_history_request(char *object, HistoryFilter *filter) +HistoryResult *hbm_history_request(const char *object, HistoryFilter *filter) { HistoryResult *r; HistoryLogObject *h = hbm_find_object(object); @@ -1050,7 +1053,7 @@ int hbm_history_cleanup(HistoryLogObject *h) return 1; } -int hbm_history_destroy(char *object) +int hbm_history_destroy(const char *object) { HistoryLogObject *h = hbm_find_object(object); HistoryLogLine *l, *l_next; @@ -1075,7 +1078,7 @@ int hbm_history_destroy(char *object) } /** Set new limit on history object */ -int hbm_history_set_limit(char *object, int max_lines, long max_time) +int hbm_history_set_limit(const char *object, int max_lines, long max_time) { HistoryLogObject *h = hbm_find_or_add_object(object); h->max_lines = max_lines; @@ -1104,8 +1107,6 @@ static int hbm_read_masterdb(void) { /* Database does not exist. Could be first boot */ config_warn("[history] No database present at '%s', will start a new one", test.masterdb); - // TODO: maybe check for condition where 'master.db' does not exist but - // there are other .db files. if (!hbm_write_masterdb()) return 0; /* fatal error */ return 1; @@ -1125,10 +1126,10 @@ static int hbm_read_masterdb(void) !unrealdb_read_str(db, &prehash) || !unrealdb_read_str(db, &posthash)) { - safe_free(prehash); - safe_free(posthash); config_error("[history] Read error from database file '%s': %s", test.masterdb, unrealdb_get_error_string()); + safe_free(prehash); + safe_free(posthash); unrealdb_close(db); return 0; } @@ -1138,14 +1139,24 @@ static int hbm_read_masterdb(void) { config_error("[history] Read error from database file '%s': unexpected values encountered", test.masterdb); + safe_free(prehash); + safe_free(posthash); return 0; } /* Now, safely switch over.. */ - safe_free(hbm_prehash); - safe_free(hbm_posthash); - hbm_prehash = prehash; - hbm_posthash = posthash; + if (hbm_prehash && !strcmp(hbm_prehash, prehash) && hbm_posthash && !strcmp(hbm_posthash, posthash)) + { + /* Identical sets */ + safe_free(prehash); + safe_free(posthash); + } else { + /* Diffferent */ + safe_free(hbm_prehash); + safe_free(hbm_posthash); + hbm_prehash = prehash; + hbm_posthash = posthash; + } return 1; } @@ -1275,7 +1286,7 @@ static void hbm_read_dbs(void) /** Read a channel history db file */ -static int hbm_read_db(char *fname) +static int hbm_read_db(const char *fname) { UnrealDB *db = NULL; // header @@ -1475,7 +1486,7 @@ EVENT(history_mem_clean) } while(loopcnt++ < HISTORY_CLEAN_PER_LOOP); } -char *hbm_history_filename(HistoryLogObject *h) +const char *hbm_history_filename(HistoryLogObject *h) { static char fname[512]; char oname[OBJECTLEN+1]; @@ -1495,9 +1506,10 @@ char *hbm_history_filename(HistoryLogObject *h) #define WARN_WRITE_ERROR(fname) \ do { \ - sendto_realops_and_log("[history] Error writing to temporary database file " \ - "'%s': %s (DATABASE NOT SAVED)", \ - fname, unrealdb_get_error_string()); \ + unreal_log(ULOG_ERROR, "history", "HISTORYDB_FILE_WRITE_ERROR", NULL, \ + "[historydb] Error writing to temporary database file $filename: $system_error", \ + log_data_string("filename", fname), \ + log_data_string("system_error", unrealdb_get_error_string())); \ } while(0) #define W_SAFE(x) \ @@ -1515,7 +1527,7 @@ char *hbm_history_filename(HistoryLogObject *h) static int hbm_write_db(HistoryLogObject *h) { UnrealDB *db; - char *realfname; + const char *realfname; char tmpfname[512]; HistoryLogLine *l; MessageTag *m; @@ -1524,7 +1536,7 @@ static int hbm_write_db(HistoryLogObject *h) if (!cfg.db_secret) abort(); - channel = find_channel(h->name, NULL); + channel = find_channel(h->name); if (!channel || !has_channel_mode(channel, 'P')) return 1; /* Don't save this channel, pretend success */ @@ -1575,7 +1587,7 @@ static int hbm_write_db(HistoryLogObject *h) #endif if (rename(tmpfname, realfname) < 0) { - sendto_realops_and_log("[history] Error renaming '%s' to '%s': %s (HISTORY NOT SAVED)", + config_error("[history] Error renaming '%s' to '%s': %s (HISTORY NOT SAVED)", tmpfname, realfname, strerror(errno)); return 0; } @@ -1588,7 +1600,7 @@ static int hbm_write_db(HistoryLogObject *h) static void hbm_delete_db(HistoryLogObject *h) { UnrealDB *db; - char *fname; + const char *fname; if (!cfg.persist || !hbm_prehash || !hbm_posthash) { #ifdef DEBUGMODE diff --git a/src/modules/history_backend_null.c b/src/modules/history_backend_null.c index 2df4513..7136ce2 100644 --- a/src/modules/history_backend_null.c +++ b/src/modules/history_backend_null.c @@ -16,14 +16,14 @@ ModuleHeader MOD_HEADER "2.0", "History backend: null/none", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -int hbn_history_set_limit(char *object, int max_lines, long max_time); -int hbn_history_add(char *object, MessageTag *mtags, char *line); -HistoryResult *hbn_history_request(char *object, HistoryFilter *filter); -int hbn_history_destroy(char *object); +int hbn_history_set_limit(const char *object, int max_lines, long max_time); +int hbn_history_add(const char *object, MessageTag *mtags, const char *line); +HistoryResult *hbn_history_request(const char *object, HistoryFilter *filter); +int hbn_history_destroy(const char *object); MOD_INIT() { @@ -53,22 +53,22 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int hbn_history_add(char *object, MessageTag *mtags, char *line) +int hbn_history_add(const char *object, MessageTag *mtags, const char *line) { return 1; } -HistoryResult *hbn_history_request(char *object, HistoryFilter *filter) +HistoryResult *hbn_history_request(const char *object, HistoryFilter *filter) { return NULL; } -int hbn_history_set_limit(char *object, int max_lines, long max_time) +int hbn_history_set_limit(const char *object, int max_lines, long max_time) { return 1; } -int hbn_history_destroy(char *object) +int hbn_history_destroy(const char *object) { return 1; } diff --git a/src/modules/ident_lookup.c b/src/modules/ident_lookup.c index 93ec0ed..c729bcc 100644 --- a/src/modules/ident_lookup.c +++ b/src/modules/ident_lookup.c @@ -10,7 +10,7 @@ ModuleHeader MOD_HEADER "1.0", "Ident lookups (RFC1413)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ @@ -43,7 +43,6 @@ MOD_UNLOAD() static void ident_lookup_failed(Client *client) { - Debug((DEBUG_NOTICE, "ident_lookup_failed() for %p", client)); ircstats.is_abad++; if (client->local->authfd != -1) { @@ -68,12 +67,12 @@ static EVENT(check_ident_timeout) if (IsIdentLookupSent(client)) { /* set::ident::connect-timeout */ - if ((TStime() - client->local->firsttime) > IDENT_CONNECT_TIMEOUT) + if ((TStime() - client->local->creationtime) > IDENT_CONNECT_TIMEOUT) ident_lookup_failed(client); } else { /* set::ident::read-timeout */ - if ((TStime() - client->local->firsttime) > IDENT_READ_TIMEOUT) + if ((TStime() - client->local->creationtime) > IDENT_READ_TIMEOUT) ident_lookup_failed(client); } } @@ -93,7 +92,8 @@ static int ident_lookup_connect(Client *client) } if (++OpenFiles >= maxclients+1) { - sendto_ops("Can't allocate fd, too many connections."); + unreal_log(ULOG_FATAL, "io", "IDENT_ERROR_MAXCLIENTS", client, + "Cannot do ident connection for $client.details: All connections in use"); fd_close(client->local->authfd); --OpenFiles; client->local->authfd = -1; diff --git a/src/modules/invite.c b/src/modules/invite.c index 906f5cc..feb99e3 100644 --- a/src/modules/invite.c +++ b/src/modules/invite.c @@ -22,9 +22,27 @@ #include "unrealircd.h" +#define MSG_INVITE "INVITE" + +#define CLIENT_INVITES(client) (moddata_local_client(client, userInvitesMD).ptr) +#define CHANNEL_INVITES(channel) (moddata_channel(channel, channelInvitesMD).ptr) + +ModDataInfo *userInvitesMD; +ModDataInfo *channelInvitesMD; +long CAP_INVITE_NOTIFY = 0L; +int invite_always_notify = 0; + CMD_FUNC(cmd_invite); -#define MSG_INVITE "INVITE" +void invite_free(ModData *md); +int invite_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +int invite_config_run(ConfigFile *cf, ConfigEntry *ce, int type); +void add_invite(Client *from, Client *to, Channel *channel, MessageTag *mtags); +void del_invite(Client *client, Channel *channel); +static int invite_channel_destroy(Channel *channel, int *should_destroy); +int invite_user_quit(Client *client, MessageTag *mtags, const char *comment); +int invite_user_join(Client *client, Channel *channel, MessageTag *mtags); +int invite_is_invited(Client *client, Channel *channel, int *invited); ModuleHeader MOD_HEADER = { @@ -32,13 +50,63 @@ ModuleHeader MOD_HEADER "5.0", "command /invite", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; +MOD_TEST() +{ + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, invite_config_test); + return MOD_SUCCESS; +} + MOD_INIT() { - CommandAdd(modinfo->handle, MSG_INVITE, cmd_invite, MAXPARA, CMD_USER); + ClientCapabilityInfo cap; + ClientCapability *c; + ModDataInfo mreq; + MARK_AS_OFFICIAL_MODULE(modinfo); + + CommandAdd(modinfo->handle, MSG_INVITE, cmd_invite, MAXPARA, CMD_USER|CMD_SERVER); + + memset(&cap, 0, sizeof(cap)); + cap.name = "invite-notify"; + c = ClientCapabilityAdd(modinfo->handle, &cap, &CAP_INVITE_NOTIFY); + if (!c) + { + config_error("[%s] Failed to request invite-notify cap: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); + return MOD_FAILED; + } + + memset(&mreq, 0 , sizeof(mreq)); + mreq.type = MODDATATYPE_LOCAL_CLIENT; + mreq.name = "invite", + mreq.free = invite_free; + userInvitesMD = ModDataAdd(modinfo->handle, mreq); + if (!userInvitesMD) + { + config_error("[%s] Failed to request user invite moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); + return MOD_FAILED; + } + + memset(&mreq, 0 , sizeof(mreq)); + mreq.type = MODDATATYPE_CHANNEL; + mreq.name = "invite", + mreq.free = invite_free; + channelInvitesMD = ModDataAdd(modinfo->handle, mreq); + if (!channelInvitesMD) + { + config_error("[%s] Failed to request channel invite moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); + return MOD_FAILED; + } + + invite_always_notify = 0; /* the default */ + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, invite_config_run); + HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_DESTROY, 1000000, invite_channel_destroy); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, invite_user_quit); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, invite_user_join); + HookAdd(modinfo->handle, HOOKTYPE_IS_INVITED, 0, invite_is_invited); + return MOD_SUCCESS; } @@ -52,62 +120,252 @@ MOD_UNLOAD() return MOD_SUCCESS; } +void invite_free(ModData *md) +{ + Link **inv, *tmp; + + if (!md->ptr) + return; // was not set + + for (inv = (Link **)md->ptr; (tmp = *inv); inv = &tmp->next) + { + *inv = tmp->next; + free_link(tmp); + } +} + +int invite_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + int errors = 0; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name || strcmp(ce->name, "normal-user-invite-notification")) + return 0; + + if (!ce->value) + { + config_error_empty(ce->file->filename, ce->line_number, "set", ce->name); + errors++; + } + + *errs = errors; + return errors ? -1 : 1; +} + +int invite_config_run(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep; + + if (type != CONFIG_SET) + return 0; + + if (!ce || !ce->name || strcmp(ce->name, "normal-user-invite-notification")) + return 0; + + invite_always_notify = config_checkval(ce->value, CFG_YESNO); + + return 1; +} + +static int invite_channel_destroy(Channel *channel, int *should_destroy) +{ + Link *lp; + while ((lp = CHANNEL_INVITES(channel))) + del_invite(lp->value.client, channel); + return 0; +} + +int invite_user_quit(Client *client, MessageTag *mtags, const char *comment) +{ + Link *lp; + /* Clean up invitefield */ + while ((lp = CLIENT_INVITES(client))) + del_invite(client, lp->value.channel); + return 0; +} + +int invite_user_join(Client *client, Channel *channel, MessageTag *mtags) +{ + del_invite(client, channel); + return 0; +} + /* Send the user their list of active invites */ void send_invite_list(Client *client) { Link *inv; - for (inv = client->user->invited; inv; inv = inv->next) + for (inv = CLIENT_INVITES(client); inv; inv = inv->next) { sendnumeric(client, RPL_INVITELIST, - inv->value.channel->chname); + inv->value.channel->name); } sendnumeric(client, RPL_ENDOFINVITELIST); } +int invite_is_invited(Client *client, Channel *channel, int *invited) +{ + Link *lp; + + if (!MyConnect(client)) + return 0; // not handling invite lists for remote clients + + for (lp = CLIENT_INVITES(client); lp; lp = lp->next) + if (lp->value.channel == channel) + { + *invited = 1; + return 0; + } + return 0; +} + +void invite_process(Client *client, Client *target, Channel *channel, MessageTag *recv_mtags, int override) +{ + MessageTag *mtags = NULL; + + new_message(client, recv_mtags, &mtags); + + /* broadcast to other servers */ + sendto_server(client, 0, 0, mtags, ":%s INVITE %s %s %d", client->id, target->id, channel->name, override); + + /* send chanops notifications */ + if (IsUser(client) && (check_channel_access(client, channel, "oaq") + || IsULine(client) + || ValidatePermissionsForPath("channel:override:invite:self",client,NULL,channel,NULL) + || invite_always_notify + )) + { + if (override == 1) + { + sendto_channel(channel, &me, NULL, "o", + 0, SEND_LOCAL, mtags, + ":%s NOTICE @%s :OperOverride -- %s invited him/herself into the channel.", + me.name, channel->name, client->name); + } + if (override == 0) + { + sendto_channel(channel, &me, NULL, "o", + CAP_INVITE_NOTIFY | CAP_INVERT, SEND_LOCAL, mtags, + ":%s NOTICE @%s :%s invited %s into the channel.", + me.name, channel->name, client->name, target->name); + } + /* always send IRCv3 invite-notify if possible */ + sendto_channel(channel, client, NULL, "o", + CAP_INVITE_NOTIFY, SEND_LOCAL, mtags, + ":%s INVITE %s %s", + client->name, target->name, channel->name); + } + + /* add to list and notify the person who got invited */ + if (MyConnect(target)) + { + if (IsUser(client) && (check_channel_access(client, channel, "oaq") + || IsULine(client) + || ValidatePermissionsForPath("channel:override:invite:self",client,NULL,channel,NULL) + )) + { + add_invite(client, target, channel, mtags); + } + + if (!is_silenced(client, target)) + { + sendto_prefix_one(target, client, mtags, ":%s INVITE %s :%s", client->name, + target->name, channel->name); + } + } + free_message_tags(mtags); +} + +void invite_operoverride_msg(Client *client, Channel *channel, char *override_mode, char *override_mode_text) +{ + unreal_log(ULOG_INFO, "operoverride", "OPEROVERRIDE_INVITE", client, + "OperOverride: $client.details invited him/herself into $channel (Overriding $override_mode_text)", + log_data_string("override_type", "join"), + log_data_string("override_mode", override_mode), + log_data_string("override_mode_text", override_mode_text), + log_data_channel("channel", channel)); +} + /* ** cmd_invite ** parv[1] - user to invite -** parv[2] - channel number +** parv[2] - channel name +** parv[3] - override (S2S only) */ CMD_FUNC(cmd_invite) { - Client *target; - Channel *channel; + Client *target = NULL; + Channel *channel = NULL; int override = 0; int i = 0; + int params_ok = 0; Hook *h; + if (parc >= 3 && *parv[1] != '\0') + { + params_ok = 1; + target = find_user(parv[1], NULL); + channel = find_channel(parv[2]); + } + + if (!MyConnect(client)) + /*** remote invite ***/ + { + if (!params_ok) + return; + /* the client or channel may be already gone */ + if (!target) + { + sendnumeric(client, ERR_NOSUCHNICK, parv[1]); + return; + } + if (!channel) + { + sendnumeric(client, ERR_NOSUCHCHANNEL, parv[2]); + return; + } + if (parc >= 4 && !BadPtr(parv[3])) + { + override = atoi(parv[3]); + } + + /* no further checks */ + + invite_process(client, target, channel, recv_mtags, override); + return; + } + + /*** local invite ***/ + + /* the client requested own invite list */ if (parc == 1) { send_invite_list(client); return; } - - if (parc < 3 || *parv[1] == '\0') + + /* notify user about bad parameters */ + if (!params_ok) { sendnumeric(client, ERR_NEEDMOREPARAMS, "INVITE"); return; } - if (!(target = find_person(parv[1], NULL))) + if (!target) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); return; } - if (MyConnect(client) && !valid_channelname(parv[2])) - { - sendnumeric(client, ERR_NOSUCHCHANNEL, parv[2]); - return; - } - - if (!(channel = find_channel(parv[2], NULL))) + if (!channel) { sendnumeric(client, ERR_NOSUCHCHANNEL, parv[2]); return; } + /* proceed with the command */ for (h = Hooks[HOOKTYPE_PRE_INVITE]; h; h = h->next) { i = (*(h->func.intfunc))(client,target,channel,&override); @@ -134,15 +392,15 @@ CMD_FUNC(cmd_invite) return; } - if (channel->mode.mode & MODE_INVITEONLY) + if (has_channel_mode(channel, 'i')) { - if (!is_chan_op(client, channel) && !IsULine(client)) + if (!check_channel_access(client, channel, "oaq") && !IsULine(client)) { if (ValidatePermissionsForPath("channel:override:invite:invite-only",client,NULL,channel,NULL) && client == target) { override = 1; } else { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); return; } } @@ -152,100 +410,52 @@ CMD_FUNC(cmd_invite) { override = 1; } else { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); return; } } } if (SPAMFILTER_VIRUSCHANDENY && SPAMFILTER_VIRUSCHAN && - !strcasecmp(channel->chname, SPAMFILTER_VIRUSCHAN) && - !is_chan_op(client, channel) && !ValidatePermissionsForPath("immune:server-ban:viruschan",client,NULL,NULL,NULL)) + !strcasecmp(channel->name, SPAMFILTER_VIRUSCHAN) && + !check_channel_access(client, channel, "oaq") && !ValidatePermissionsForPath("immune:server-ban:viruschan",client,NULL,NULL,NULL)) { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); return; } - if (MyUser(client)) + if (target_limit_exceeded(client, target, target->name)) + return; + + if (!ValidatePermissionsForPath("immune:invite-flood",client,NULL,NULL,NULL) && + flood_limit_exceeded(client, FLD_INVITE)) { - if (target_limit_exceeded(client, target, target->name)) - return; - - if (!ValidatePermissionsForPath("immune:invite-flood",client,NULL,NULL,NULL) && - flood_limit_exceeded(client, FLD_INVITE)) - { - sendnumeric(client, RPL_TRYAGAIN, "INVITE"); - return; - } - - if (!override) - { - sendnumeric(client, RPL_INVITING, target->name, channel->chname); - if (target->user->away) - { - sendnumeric(client, RPL_AWAY, target->name, target->user->away); - } - } + sendnumeric(client, RPL_TRYAGAIN, "INVITE"); + return; } - /* Send OperOverride messages */ - if (override && MyConnect(target)) + if (!override) { + sendnumeric(client, RPL_INVITING, target->name, channel->name); + if (target->user->away) + { + sendnumeric(client, RPL_AWAY, target->name, target->user->away); + } + } + else + { + /* Send OperOverride messages */ + char override_what = '\0'; if (is_banned(client, channel, BANCHK_JOIN, NULL, NULL)) - { - sendto_snomask_global(SNO_EYES, - "*** OperOverride -- %s (%s@%s) invited him/herself into %s (overriding +b).", - client->name, client->user->username, client->user->realhost, channel->chname); - - /* Logging implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) invited him/herself into %s (Overriding Ban).", - client->name, client->user->username, client->user->realhost, channel->chname); - - } - else if (channel->mode.mode & MODE_INVITEONLY) - { - sendto_snomask_global(SNO_EYES, - "*** OperOverride -- %s (%s@%s) invited him/herself into %s (overriding +i).", - client->name, client->user->username, client->user->realhost, channel->chname); - - /* Logging implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) invited him/herself into %s (Overriding Invite Only)", - client->name, client->user->username, client->user->realhost, channel->chname); - - } - else if (channel->mode.limit) - { - sendto_snomask_global(SNO_EYES, - "*** OperOverride -- %s (%s@%s) invited him/herself into %s (overriding +l).", - client->name, client->user->username, client->user->realhost, channel->chname); - - /* Logging implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) invited him/herself into %s (Overriding Limit)", - client->name, client->user->username, client->user->realhost, channel->chname); - - } - - else if (*channel->mode.key) - { - sendto_snomask_global(SNO_EYES, - "*** OperOverride -- %s (%s@%s) invited him/herself into %s (overriding +k).", - client->name, client->user->username, client->user->realhost, channel->chname); - - /* Logging implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) invited him/herself into %s (Overriding Key)", - client->name, client->user->username, client->user->realhost, channel->chname); - - } + invite_operoverride_msg(client, channel, "b", "ban"); + else if (has_channel_mode(channel, 'i')) + invite_operoverride_msg(client, channel, "i", "invite only"); + else if (has_channel_mode(channel, 'l')) + invite_operoverride_msg(client, channel, "l", "user limit"); + else if (has_channel_mode(channel, 'k')) + invite_operoverride_msg(client, channel, "k", "key"); else if (has_channel_mode(channel, 'z')) - { - sendto_snomask_global(SNO_EYES, - "*** OperOverride -- %s (%s@%s) invited him/herself into %s (overriding +z).", - client->name, client->user->username, client->user->realhost, channel->chname); - - /* Logging implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) invited him/herself into %s (Overriding SSL/TLS-Only)", - client->name, client->user->username, client->user->realhost, channel->chname); - } + invite_operoverride_msg(client, channel, "z", "secure only"); #ifdef OPEROVERRIDE_VERIFY else if (channel->mode.mode & MODE_SECRET || channel->mode.mode & MODE_PRIVATE) override = -1; @@ -254,40 +464,79 @@ CMD_FUNC(cmd_invite) return; } - if (MyConnect(target)) - { - if (IsUser(client) - && (is_chan_op(client, channel) - || IsULine(client) - || ValidatePermissionsForPath("channel:override:invite:self",client,NULL,channel,NULL) - )) - { - MessageTag *mtags = NULL; - - new_message(&me, NULL, &mtags); - if (override == 1) - { - sendto_channel(channel, &me, NULL, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER, - 0, SEND_ALL, mtags, - ":%s NOTICE @%s :OperOverride -- %s invited him/herself into the channel.", - me.name, channel->chname, client->name); - } else - if (override == 0) - { - sendto_channel(channel, &me, NULL, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER, - 0, SEND_ALL, mtags, - ":%s NOTICE @%s :%s invited %s into the channel.", - me.name, channel->chname, client->name, target->name); - } - add_invite(client, target, channel, mtags); - free_message_tags(mtags); - } - } - - /* Notify the person who got invited */ - if (!is_silenced(client, target)) - { - sendto_prefix_one(target, client, NULL, ":%s INVITE %s :%s", client->name, - target->name, channel->chname); - } + /* allowed to proceed */ + invite_process(client, target, channel, recv_mtags, override); } + +/** Register an invite from someone to a channel - so they can bypass +i etc. + * @param from The person sending the invite + * @param to The person who is invited to join + * @param channel The channel + * @param mtags Message tags associated with this INVITE command + */ +void add_invite(Client *from, Client *to, Channel *channel, MessageTag *mtags) +{ + Link *inv, *tmp; + + del_invite(to, channel); + /* If too many invite entries then delete the oldest one */ + if (link_list_length(CLIENT_INVITES(to)) >= MAXCHANNELSPERUSER) + { + for (tmp = CLIENT_INVITES(to); tmp->next; tmp = tmp->next) + ; + del_invite(to, tmp->value.channel); + + } + /* We get pissy over too many invites per channel as well now, + * since otherwise mass-inviters could take up some major + * resources -Donwulff + */ + if (link_list_length(CHANNEL_INVITES(channel)) >= MAXCHANNELSPERUSER) + { + for (tmp = CHANNEL_INVITES(channel); tmp->next; tmp = tmp->next) + ; + del_invite(tmp->value.client, channel); + } + /* + * add client to the beginning of the channel invite list + */ + inv = make_link(); + inv->value.client = to; + inv->next = CHANNEL_INVITES(channel); + CHANNEL_INVITES(channel) = inv; + /* + * add channel to the beginning of the client invite list + */ + inv = make_link(); + inv->value.channel = channel; + inv->next = CLIENT_INVITES(to); + CLIENT_INVITES(to) = inv; + + RunHook(HOOKTYPE_INVITE, from, to, channel, mtags); +} + +/** Delete a previous invite of someone to a channel. + * @param client The client who was invited + * @param channel The channel to which the person was invited + */ +void del_invite(Client *client, Channel *channel) +{ + Link **inv, *tmp; + + for (inv = (Link **)&CHANNEL_INVITES(channel); (tmp = *inv); inv = &tmp->next) + if (tmp->value.client == client) + { + *inv = tmp->next; + free_link(tmp); + break; + } + + for (inv = (Link **)&CLIENT_INVITES(client); (tmp = *inv); inv = &tmp->next) + if (tmp->value.channel == channel) + { + *inv = tmp->next; + free_link(tmp); + break; + } +} + diff --git a/src/modules/ircops.c b/src/modules/ircops.c index 352208a..4b71eb0 100644 --- a/src/modules/ircops.c +++ b/src/modules/ircops.c @@ -31,7 +31,7 @@ ModuleHeader MOD_HEADER "3.71", "/IRCOPS command that lists IRC Operators", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() diff --git a/src/modules/ison.c b/src/modules/ison.c index 69ce42d..928810c 100644 --- a/src/modules/ison.c +++ b/src/modules/ison.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /ison", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -66,6 +66,7 @@ static char buf[BUFSIZE]; CMD_FUNC(cmd_ison) { + char request[BUFSIZE]; char namebuf[USERLEN + HOSTLEN + 4]; Client *acptr; char *s, *user; @@ -82,11 +83,12 @@ CMD_FUNC(cmd_ison) ircsnprintf(buf, sizeof(buf), ":%s %d %s :", me.name, RPL_ISON, client->name); - for (s = strtoken(&p, parv[1], " "); s; s = strtoken(&p, NULL, " ")) + strlcpy(request, parv[1], sizeof(request)); + for (s = strtoken(&p, request, " "); s; s = strtoken(&p, NULL, " ")) { if ((user = strchr(s, '!'))) *user++ = '\0'; - if ((acptr = find_person(s, NULL))) + if ((acptr = find_user(s, NULL))) { if (user) { diff --git a/src/modules/join.c b/src/modules/join.c index 334256e..37610b8 100644 --- a/src/modules/join.c +++ b/src/modules/join.c @@ -24,12 +24,12 @@ /* Forward declarations */ CMD_FUNC(cmd_join); -void _join_channel(Channel *channel, Client *client, MessageTag *mtags, int flags); -void _do_join(Client *client, int parc, char *parv[]); -int _can_join(Client *client, Channel *channel, char *key, char *parv[]); -void _userhost_save_current(Client *client); -void _userhost_changed(Client *client); +void _join_channel(Channel *channel, Client *client, MessageTag *mtags, const char *member_modes); +void _do_join(Client *client, int parc, const char *parv[]); +int _can_join(Client *client, Channel *channel, const char *key, char **errmsg); void _send_join_to_local_users(Client *client, Channel *channel, MessageTag *mtags); +char *_get_chmodes_for_user(Client *client, const char *flags); +void send_cannot_join_error(Client *client, int numeric, char *fmtstr, char *channel_name); /* Externs */ extern MODVAR int spamf_ugly_vchanoverride; @@ -37,6 +37,7 @@ extern int find_invex(Channel *channel, Client *client); /* Local vars */ static int bouncedtimes = 0; +long CAP_EXTENDED_JOIN = 0L; /* Macros */ #define MAXBOUNCE 5 /** Most sensible */ @@ -48,7 +49,7 @@ ModuleHeader MOD_HEADER "5.0", "command /join", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_TEST() @@ -57,15 +58,19 @@ MOD_TEST() EfunctionAddVoid(modinfo->handle, EFUNC_JOIN_CHANNEL, _join_channel); EfunctionAddVoid(modinfo->handle, EFUNC_DO_JOIN, _do_join); EfunctionAdd(modinfo->handle, EFUNC_CAN_JOIN, _can_join); - EfunctionAddVoid(modinfo->handle, EFUNC_USERHOST_SAVE_CURRENT, _userhost_save_current); - EfunctionAddVoid(modinfo->handle, EFUNC_USERHOST_CHANGED, _userhost_changed); EfunctionAddVoid(modinfo->handle, EFUNC_SEND_JOIN_TO_LOCAL_USERS, _send_join_to_local_users); + EfunctionAddPVoid(modinfo->handle, EFUNC_GET_CHMODES_FOR_USER, TO_PVOIDFUNC(_get_chmodes_for_user)); return MOD_SUCCESS; } MOD_INIT() { + ClientCapabilityInfo c; + memset(&c, 0, sizeof(c)); + c.name = "extended-join"; + ClientCapabilityAdd(modinfo->handle, &c, &CAP_EXTENDED_JOIN); + CommandAdd(modinfo->handle, MSG_JOIN, cmd_join, MAXPARA, CMD_USER); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -87,7 +92,7 @@ MOD_UNLOAD() * (eg: bans at the end), so don't change it unless you have a good reason * to do so -- Syzop. */ -int _can_join(Client *client, Channel *channel, char *key, char *parv[]) +int _can_join(Client *client, Channel *channel, const char *key, char **errmsg) { Link *lp; Ban *banned; @@ -96,7 +101,7 @@ int _can_join(Client *client, Channel *channel, char *key, char *parv[]) for (h = Hooks[HOOKTYPE_CAN_JOIN]; h; h = h->next) { - i = (*(h->func.intfunc))(client,channel,key,parv); + i = (*(h->func.intfunc))(client,channel,key, errmsg); if (i != 0) return i; } @@ -111,47 +116,32 @@ int _can_join(Client *client, Channel *channel, char *key, char *parv[]) /* See if we can evade this ban */ banned = is_banned(client, channel, BANCHK_JOIN, NULL, NULL); if (banned && j == HOOK_DENY) - return (ERR_BANNEDFROMCHAN); + { + *errmsg = STR_ERR_BANNEDFROMCHAN; + return ERR_BANNEDFROMCHAN; + } if (is_invited(client, channel)) - return 0; /* allowed */ + return 0; /* allowed to walk through all the other modes */ - if (channel->users >= channel->mode.limit) - { - /* Hmmm.. don't really like this.. and not at this place */ - - for (h = Hooks[HOOKTYPE_CAN_JOIN_LIMITEXCEEDED]; h; h = h->next) - { - i = (*(h->func.intfunc))(client,channel,key,parv); - if (i != 0) - return i; - } - - /* We later check again for this limit (in case +L was not set) */ - } - - - if (*channel->mode.key && (BadPtr(key) || strcmp(channel->mode.key, key))) - return (ERR_BADCHANNELKEY); - - if ((channel->mode.mode & MODE_INVITEONLY) && !find_invex(channel, client)) - return (ERR_INVITEONLYCHAN); - - if ((channel->mode.limit && channel->users >= channel->mode.limit)) - return (ERR_CHANNELISFULL); - - if (banned) - return (ERR_BANNEDFROMCHAN); + if (banned) + { + *errmsg = STR_ERR_BANNEDFROMCHAN; + return ERR_BANNEDFROMCHAN; + } #ifndef NO_OPEROVERRIDE #ifdef OPEROVERRIDE_VERIFY - if (ValidatePermissionsForPath("channel:override:privsecret",client,NULL,channel,NULL) && (channel->mode.mode & MODE_SECRET || - channel->mode.mode & MODE_PRIVATE) && !is_autojoin_chan(channel->chname)) - return (ERR_OPERSPVERIFY); + if (ValidatePermissionsForPath("channel:override:privsecret",client,NULL,channel,NULL) && (channel->mode.mode & MODE_SECRET || + channel->mode.mode & MODE_PRIVATE) && !is_autojoin_chan(channel->name)) + { + *errmsg = STR_ERR_OPERSPVERIFY; + return (ERR_OPERSPVERIFY); + } #endif #endif - return 0; + return 0; } /* @@ -170,7 +160,12 @@ CMD_FUNC(cmd_join) int r; if (bouncedtimes) - sendto_realops("join: bouncedtimes=%d??? [please report at https://bugs.unrealircd.org/]", bouncedtimes); + { + unreal_log(ULOG_ERROR, "join", "BUG_JOIN_BOUNCEDTIMES", NULL, + "[BUG] join: bouncedtimes is not initialized to zero ($bounced_times)!! " + "Please report at https://bugs.unrealircd.org/", + log_data_integer("bounced_times", bouncedtimes)); + } bouncedtimes = 0; if (IsServer(client)) @@ -190,15 +185,13 @@ void _send_join_to_local_users(Client *client, Channel *channel, MessageTag *mta Client *acptr; char joinbuf[512]; char exjoinbuf[512]; - long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join"); - long CAP_AWAY_NOTIFY = ClientCapabilityBit("away-notify"); ircsnprintf(joinbuf, sizeof(joinbuf), ":%s!%s@%s JOIN :%s", - client->name, client->user->username, GetHost(client), channel->chname); + client->name, client->user->username, GetHost(client), channel->name); ircsnprintf(exjoinbuf, sizeof(exjoinbuf), ":%s!%s@%s JOIN %s %s :%s", - client->name, client->user->username, GetHost(client), channel->chname, - !isdigit(*client->user->svid) ? client->user->svid : "*", + client->name, client->user->username, GetHost(client), channel->name, + IsLoggedIn(client) ? client->user->account : "*", client->info); for (lp = channel->members; lp; lp = lp->next) @@ -208,46 +201,37 @@ void _send_join_to_local_users(Client *client, Channel *channel, MessageTag *mta if (!MyConnect(acptr)) continue; /* only locally connected clients */ - if (chanops_only && !(lp->flags & (CHFL_HALFOP|CHFL_CHANOP|CHFL_CHANOWNER|CHFL_CHANADMIN)) && (client != acptr)) + if (chanops_only && !check_channel_access_member(lp, "hoaq") && (client != acptr)) continue; /* skip non-ops if requested to (used for mode +D), but always send to 'client' */ if (HasCapabilityFast(acptr, CAP_EXTENDED_JOIN)) sendto_one(acptr, mtags, "%s", exjoinbuf); else sendto_one(acptr, mtags, "%s", joinbuf); - - if (client->user->away && HasCapabilityFast(acptr, CAP_AWAY_NOTIFY)) - { - MessageTag *mtags_away = NULL; - new_message(client, NULL, &mtags_away); - sendto_one(acptr, mtags_away, ":%s!%s@%s AWAY :%s", - client->name, client->user->username, GetHost(client), client->user->away); - free_message_tags(mtags_away); - } } } /* Routine that actually makes a user join the channel * this does no actual checking (banned, etc.) it just adds the user */ -void _join_channel(Channel *channel, Client *client, MessageTag *recv_mtags, int flags) +void _join_channel(Channel *channel, Client *client, MessageTag *recv_mtags, const char *member_modes) { MessageTag *mtags = NULL; /** Message tags to send to local users (sender is :user) */ MessageTag *mtags_sjoin = NULL; /* Message tags to send to remote servers for SJOIN (sender is :me.id) */ - char *parv[] = { 0, 0 }; + const char *parv[3]; /* Same way as in SJOIN */ - new_message_special(client, recv_mtags, &mtags, ":%s JOIN %s", client->name, channel->chname); + new_message_special(client, recv_mtags, &mtags, ":%s JOIN %s", client->name, channel->name); new_message(&me, recv_mtags, &mtags_sjoin); - add_user_to_channel(channel, client, flags); + add_user_to_channel(channel, client, member_modes); send_join_to_local_users(client, channel, mtags); sendto_server(client, 0, 0, mtags_sjoin, ":%s SJOIN %lld %s :%s%s ", me.id, (long long)channel->creationtime, - channel->chname, chfl_to_sjoin_symbol(flags), client->id); + channel->name, modes_to_sjoin_prefix(member_modes), client->id); if (MyUser(client)) { @@ -260,56 +244,63 @@ void _join_channel(Channel *channel, Client *client, MessageTag *recv_mtags, int { channel->creationtime = TStime(); sendto_server(client, 0, 0, NULL, ":%s MODE %s + %lld", - me.id, channel->chname, (long long)channel->creationtime); + me.id, channel->name, (long long)channel->creationtime); } - del_invite(client, channel); if (channel->topic) { - sendnumeric(client, RPL_TOPIC, channel->chname, channel->topic); - sendnumeric(client, RPL_TOPICWHOTIME, channel->chname, channel->topic_nick, - channel->topic_time); + sendnumeric(client, RPL_TOPIC, channel->name, channel->topic); + sendnumeric(client, RPL_TOPICWHOTIME, channel->name, channel->topic_nick, (long long)channel->topic_time); } /* Set default channel modes (set::modes-on-join). * Set only if it's the 1st user and only if no other modes have been set * already (eg: +P, permanent). */ - if ((channel->users == 1) && !channel->mode.mode && !channel->mode.extmode && - (MODES_ON_JOIN || iConf.modes_on_join.extmodes)) + if ((channel->users == 1) && !channel->mode.mode && MODES_ON_JOIN) { - int i; MessageTag *mtags_mode = NULL; - - channel->mode.extmode = iConf.modes_on_join.extmodes; - /* Param fun */ - for (i = 0; i <= Channelmode_highest; i++) - { - if (!Channelmode_Table[i].flag || !Channelmode_Table[i].paracount) - continue; - if (channel->mode.extmode & Channelmode_Table[i].mode) - cm_putparameter(channel, Channelmode_Table[i].flag, iConf.modes_on_join.extparams[i]); - } + Cmode *cm; + char modebuf[BUFSIZE], parabuf[BUFSIZE]; channel->mode.mode = MODES_ON_JOIN; + /* Param fun */ + for (cm=channelmodes; cm; cm = cm->next) + { + if (!cm->letter || !cm->paracount) + continue; + if (channel->mode.mode & cm->mode) + cm_putparameter(channel, cm->letter, iConf.modes_on_join.extparams[cm->letter]); + } + *modebuf = *parabuf = 0; - channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel); + channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel, 0); /* This should probably be in the SJOIN stuff */ - new_message_special(&me, recv_mtags, &mtags_mode, ":%s MODE %s %s %s", me.name, channel->chname, modebuf, parabuf); + new_message_special(&me, recv_mtags, &mtags_mode, ":%s MODE %s %s %s", me.name, channel->name, modebuf, parabuf); sendto_server(NULL, 0, 0, mtags_mode, ":%s MODE %s %s %s %lld", - me.id, channel->chname, modebuf, parabuf, (long long)channel->creationtime); - sendto_one(client, mtags_mode, ":%s MODE %s %s %s", me.name, channel->chname, modebuf, parabuf); + me.id, channel->name, modebuf, parabuf, (long long)channel->creationtime); + sendto_one(client, mtags_mode, ":%s MODE %s %s %s", me.name, channel->name, modebuf, parabuf); free_message_tags(mtags_mode); } - parv[0] = client->name; - parv[1] = channel->chname; + parv[0] = NULL; + parv[1] = channel->name; + parv[2] = NULL; do_cmd(client, NULL, "NAMES", 2, parv); - RunHook4(HOOKTYPE_LOCAL_JOIN, client, channel, mtags, parv); + unreal_log(ULOG_INFO, "join", "LOCAL_CLIENT_JOIN", client, + "User $client joined $channel", + log_data_channel("channel", channel), + log_data_string("modes", member_modes)); + + RunHook(HOOKTYPE_LOCAL_JOIN, client, channel, mtags); } else { - RunHook4(HOOKTYPE_REMOTE_JOIN, client, channel, mtags, parv); + unreal_log(ULOG_INFO, "join", "REMOTE_CLIENT_JOIN", client, + "User $client joined $channel", + log_data_channel("channel", channel), + log_data_string("modes", member_modes)); + RunHook(HOOKTYPE_REMOTE_JOIN, client, channel, mtags); } free_message_tags(mtags); @@ -323,18 +314,21 @@ void _join_channel(Channel *channel, Client *client, MessageTag *recv_mtags, int * increased every time we enter this loop and decreased anytime we leave the * loop. So be carefull not to use a simple 'return' after bouncedtimes++. -- Syzop */ -void _do_join(Client *client, int parc, char *parv[]) +void _do_join(Client *client, int parc, const char *parv[]) { + char request[BUFSIZE]; + char request_key[BUFSIZE]; char jbuf[BUFSIZE], jbuf2[BUFSIZE]; - char *orig_parv1; + const char *orig_parv1; Membership *lp; Channel *channel; char *name, *key = NULL; - int i, flags = 0, ishold; + int i, ishold; char *p = NULL, *p2 = NULL; TKL *tklban; int ntargets = 0; int maxtargets = max_targets_for_command("JOIN"); + const char *member_modes = ""; #define RET() do { bouncedtimes--; parv[1] = orig_parv1; return; } while(0) @@ -343,6 +337,11 @@ void _do_join(Client *client, int parc, char *parv[]) sendnumeric(client, ERR_NEEDMOREPARAMS, "JOIN"); return; } + + /* For our tests we need super accurate time for JOINs or they mail fail. */ + gettimeofday(&timeofday_tv, NULL); + timeofday = timeofday_tv.tv_sec; + bouncedtimes++; orig_parv1 = parv[1]; /* don't use 'return;' but 'RET();' from here ;p */ @@ -359,9 +358,8 @@ void _do_join(Client *client, int parc, char *parv[]) ** Rebuild list of channels joined to be the actual result of the ** JOIN. Note that "JOIN 0" is the destructive problem. */ - for (i = 0, name = strtoken(&p, parv[1], ","); - name; - i++, name = strtoken(&p, NULL, ",")) + strlcpy(request, parv[1], sizeof(request)); + for (i = 0, name = strtoken(&p, request, ","); name; i++, name = strtoken(&p, NULL, ",")) { if (MyUser(client) && (++ntargets > maxtargets)) { @@ -370,7 +368,7 @@ void _do_join(Client *client, int parc, char *parv[]) } if (*name == '0' && !atoi(name)) { - /* UnrealIRCd 5: we only support "JOIN 0", + /* UnrealIRCd 5+: we only support "JOIN 0", * "JOIN 0,#somechan" etc... so only at the beginning. * We do not support it half-way like "JOIN #a,0,#b" * since that doesn't make sense, unless you are flooding... @@ -384,7 +382,7 @@ void _do_join(Client *client, int parc, char *parv[]) if (MyConnect(client) && !valid_channelname(name)) { send_invalid_channelname(client, name); - if (IsOper(client) && find_channel(name, NULL)) + if (IsOper(client) && find_channel(name)) { /* Give IRCOps a bit more information */ sendnotice(client, "Channel '%s' is unjoinable because it contains illegal characters. " @@ -415,7 +413,10 @@ void _do_join(Client *client, int parc, char *parv[]) p = NULL; if (parv[2]) - key = strtoken(&p2, parv[2], ","); + { + strlcpy(request_key, parv[2], sizeof(request_key)); + key = strtoken(&p2, request_key, ","); + } parv[2] = NULL; /* for cmd_names call later, parv[parc] must == NULL */ for (name = strtoken(&p, jbuf, ","); @@ -442,13 +443,13 @@ void _do_join(Client *client, int parc, char *parv[]) sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, ":%s PART %s :%s", - client->name, channel->chname, "Left all channels"); - sendto_server(client, 0, 0, mtags, ":%s PART %s :Left all channels", client->name, channel->chname); + client->name, channel->name, "Left all channels"); + sendto_server(client, 0, 0, mtags, ":%s PART %s :Left all channels", client->name, channel->name); if (MyConnect(client)) - RunHook4(HOOKTYPE_LOCAL_PART, client, channel, mtags, "Left all channels"); + RunHook(HOOKTYPE_LOCAL_PART, client, channel, mtags, "Left all channels"); - remove_user_from_channel(client, channel); + remove_user_from_channel(client, channel, 0); free_message_tags(mtags); } continue; @@ -456,19 +457,7 @@ void _do_join(Client *client, int parc, char *parv[]) if (MyConnect(client)) { - /* - ** local client is first to enter previously nonexistant - ** channel so make them (rightfully) the Channel - ** Operator. - */ - /* Where did this come from? Potvin ? --Stskeeps - flags = (ChannelExists(name)) ? CHFL_DEOPPED : - CHFL_CHANOWNER; - - */ - - flags = - (ChannelExists(name)) ? CHFL_DEOPPED : LEVEL_ON_JOIN; + member_modes = (ChannelExists(name)) ? "" : LEVEL_ON_JOIN; if (!ValidatePermissionsForPath("immune:maxchannelsperuser",client,NULL,NULL,NULL)) /* opers can join unlimited chans */ if (client->user->joined >= MAXCHANNELSPERUSER) @@ -486,8 +475,9 @@ void _do_join(Client *client, int parc, char *parv[]) { if (d->warn) { - sendto_snomask(SNO_EYES, "*** %s tried to join forbidden channel %s", - get_client_name(client, 1), name); + unreal_log(ULOG_INFO, "join", "JOIN_DENIED_FORBIDDEN_CHANNEL", client, + "Client $client.details tried to join forbidden channel $channel", + log_data_string("channel", name)); } if (d->reason) sendnumeric(client, ERR_FORBIDDENCHANNEL, name, d->reason); @@ -514,7 +504,7 @@ void _do_join(Client *client, int parc, char *parv[]) !strcasecmp(name, SPAMFILTER_VIRUSCHAN) && !ValidatePermissionsForPath("immune:server-ban:viruschan",client,NULL,NULL,NULL) && !spamf_ugly_vchanoverride) { - Channel *channel = find_channel(name, NULL); + Channel *channel = find_channel(name); if (!channel || !is_invited(client, channel)) { @@ -525,7 +515,7 @@ void _do_join(Client *client, int parc, char *parv[]) } } - channel = get_channel(client, name, CREATE); + channel = make_channel(name); if (channel && (lp = find_membership_link(client->user->channel, channel))) continue; @@ -534,23 +524,14 @@ void _do_join(Client *client, int parc, char *parv[]) i = HOOK_CONTINUE; if (!MyConnect(client)) - flags = CHFL_DEOPPED; + member_modes = ""; else { Hook *h; + char *errmsg = NULL; for (h = Hooks[HOOKTYPE_PRE_LOCAL_JOIN]; h; h = h->next) { - /* Note: this is just a hack not to break the ABI but still be - * able to fix https://bugs.unrealircd.org/view.php?id=5644 - * In the future we should just drop the parv/parx argument - * and use key as an argument instead. - */ - char *parx[4]; - parx[0] = NULL; - parx[1] = name; - parx[2] = key; - parx[3] = NULL; - i = (*(h->func.intfunc))(client,channel,parx); + i = (*(h->func.intfunc))(client,channel,key); if (i == HOOK_DENY || i == HOOK_ALLOW) break; } @@ -564,12 +545,10 @@ void _do_join(Client *client, int parc, char *parv[]) } /* If they are allowed, don't check can_join */ if (i != HOOK_ALLOW && - (i = can_join(client, channel, key, parv))) + (i = can_join(client, channel, key, &errmsg))) { if (i != -1) - { - sendnumeric(client, i, name); - } + send_cannot_join_error(client, i, errmsg, name); continue; } } @@ -585,43 +564,42 @@ void _do_join(Client *client, int parc, char *parv[]) * and so on, each with their own unique msgid and such. */ new_message(client, NULL, &mtags); - join_channel(channel, client, mtags, flags); + join_channel(channel, client, mtags, member_modes); free_message_tags(mtags); } RET(); #undef RET } +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +void send_cannot_join_error(Client *client, int numeric, char *fmtstr, char *channel_name) +{ + // TODO: add single %s validation ! + sendnumericfmt(client, numeric, fmtstr, channel_name); +} +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + /* Additional channel-related functions. I've put it here instead * of the core so it could be upgraded on the fly should it be necessary. */ -char *get_chmodes_for_user(Client *client, int flags) +char *_get_chmodes_for_user(Client *client, const char *member_flags) { static char modebuf[512]; /* returned */ char flagbuf[8]; /* For holding "vhoaq" */ - char *p = flagbuf; char parabuf[512]; int n, i; - if (!flags) + if (BadPtr(member_flags)) return ""; - if (flags & MODE_CHANOWNER) - *p++ = 'q'; - if (flags & MODE_CHANADMIN) - *p++ = 'a'; - if (flags & MODE_CHANOP) - *p++ = 'o'; - if (flags & MODE_VOICE) - *p++ = 'v'; - if (flags & MODE_HALFOP) - *p++ = 'h'; - *p = '\0'; - parabuf[0] = '\0'; - - n = strlen(flagbuf); + n = strlen(member_flags); if (n) { for (i=0; i < n; i++) @@ -631,182 +609,9 @@ char *get_chmodes_for_user(Client *client, int flags) strlcat(parabuf, " ", sizeof(parabuf)); } /* And we have our mode line! */ - snprintf(modebuf, sizeof(modebuf), "+%s %s", flagbuf, parabuf); + snprintf(modebuf, sizeof(modebuf), "+%s %s", member_flags, parabuf); return modebuf; } return ""; } - -static char remember_nick[NICKLEN+1]; -static char remember_user[USERLEN+1]; -static char remember_host[HOSTLEN+1]; - -/** Save current nick/user/host. Used later by userhost_changed(). */ -void _userhost_save_current(Client *client) -{ - strlcpy(remember_nick, client->name, sizeof(remember_nick)); - strlcpy(remember_user, client->user->username, sizeof(remember_user)); - strlcpy(remember_host, GetHost(client), sizeof(remember_host)); -} - -/** User/Host changed for user. - * Note that userhost_save_current() needs to be called before this - * to save the old username/hostname. - * This userhost_changed() function deals with notifying local clients - * about the user/host change by sending PART+JOIN+MODE if - * set::allow-userhost-change force-rejoin is in use, - * and it wills end "CAP chghost" to such capable clients. - * It will also deal with bumping fakelag for the user since a user/host - * change is costly, doesn't matter if it was self-induced or not. - * - * Please call this function for any user/host change by doing: - * userhost_save_current(client); - * << change username or hostname here >> - * userhost_changed(client); - */ -void _userhost_changed(Client *client) -{ - Membership *channels; - Member *lp; - Client *acptr; - int impact = 0; - char buf[512]; - long CAP_EXTENDED_JOIN = ClientCapabilityBit("extended-join"); - long CAP_CHGHOST = ClientCapabilityBit("chghost"); - - if (strcmp(remember_nick, client->name)) - { - ircd_log(LOG_ERROR, "[BUG] userhost_changed() was called but without calling userhost_save_current() first! Affected user: %s", - client->name); - ircd_log(LOG_ERROR, "Please report above bug on https://bugs.unrealircd.org/"); - sendto_realops("[BUG] userhost_changed() was called but without calling userhost_save_current() first! Affected user: %s", - client->name); - sendto_realops("Please report above bug on https://bugs.unrealircd.org/"); - return; /* We cannot safely process this request anymore */ - } - - /* It's perfectly acceptable to call us even if the userhost didn't change. */ - if (!strcmp(remember_user, client->user->username) && !strcmp(remember_host, GetHost(client))) - return; /* Nothing to do */ - - /* Most of the work is only necessary for set::allow-userhost-change force-rejoin */ - if (UHOST_ALLOWED == UHALLOW_REJOIN) - { - /* Walk through all channels of this user.. */ - for (channels = client->user->channel; channels; channels = channels->next) - { - Channel *channel = channels->channel; - int flags = channels->flags; - char *modes; - char partbuf[512]; /* PART */ - char joinbuf[512]; /* JOIN */ - char exjoinbuf[512]; /* JOIN (for CAP extended-join) */ - char modebuf[512]; /* MODE (if any) */ - int chanops_only = invisible_user_in_channel(client, channel); - - modebuf[0] = '\0'; - - /* If the user is banned, don't send any rejoins, it would only be annoying */ - if (is_banned(client, channel, BANCHK_JOIN, NULL, NULL)) - continue; - - /* Prepare buffers for PART, JOIN, MODE */ - ircsnprintf(partbuf, sizeof(partbuf), ":%s!%s@%s PART %s :%s", - remember_nick, remember_user, remember_host, - channel->chname, - "Changing host"); - - ircsnprintf(joinbuf, sizeof(joinbuf), ":%s!%s@%s JOIN %s", - client->name, client->user->username, GetHost(client), channel->chname); - - ircsnprintf(exjoinbuf, sizeof(exjoinbuf), ":%s!%s@%s JOIN %s %s :%s", - client->name, client->user->username, GetHost(client), channel->chname, - !isdigit(*client->user->svid) ? client->user->svid : "*", - client->info); - - modes = get_chmodes_for_user(client, flags); - if (!BadPtr(modes)) - ircsnprintf(modebuf, sizeof(modebuf), ":%s MODE %s %s", me.name, channel->chname, modes); - - for (lp = channel->members; lp; lp = lp->next) - { - acptr = lp->client; - - if (acptr == client) - continue; /* skip self */ - - if (!MyConnect(acptr)) - continue; /* only locally connected clients */ - - if (chanops_only && !(lp->flags & (CHFL_CHANOP|CHFL_CHANOWNER|CHFL_CHANADMIN))) - continue; /* skip non-ops if requested to (used for mode +D) */ - - if (HasCapabilityFast(acptr, CAP_CHGHOST)) - continue; /* we notify 'CAP chghost' users in a different way, so don't send it here. */ - - impact++; - - /* FIXME: if a client does not have the "chghost" cap then - * here we will not generate a proper new message, probably - * needs to be fixed... I skipped doing it for now. - */ - sendto_one(acptr, NULL, "%s", partbuf); - - if (HasCapabilityFast(acptr, CAP_EXTENDED_JOIN)) - sendto_one(acptr, NULL, "%s", exjoinbuf); - else - sendto_one(acptr, NULL, "%s", joinbuf); - - if (*modebuf) - sendto_one(acptr, NULL, "%s", modebuf); - } - } - } - - /* Now deal with "CAP chghost" clients. - * This only needs to be sent one per "common channel". - * This would normally call sendto_common_channels_local_butone() but the user already - * has the new user/host.. so we do it here.. - */ - ircsnprintf(buf, sizeof(buf), ":%s!%s@%s CHGHOST %s %s", - remember_nick, remember_user, remember_host, - client->user->username, - GetHost(client)); - current_serial++; - for (channels = client->user->channel; channels; channels = channels->next) - { - for (lp = channels->channel->members; lp; lp = lp->next) - { - acptr = lp->client; - if (MyUser(acptr) && HasCapabilityFast(acptr, CAP_CHGHOST) && - (acptr->local->serial != current_serial) && (client != acptr)) - { - /* FIXME: send mtag */ - sendto_one(acptr, NULL, "%s", buf); - acptr->local->serial = current_serial; - } - } - } - - if (MyUser(client)) - { - /* We take the liberty of sending the CHGHOST to the impacted user as - * well. This makes things easy for client coders. - * (Note that this cannot be merged with the for loop from 15 lines up - * since the user may not be in any channels) - */ - if (HasCapabilityFast(client, CAP_CHGHOST)) - sendto_one(client, NULL, "%s", buf); - - /* A userhost change always generates the following network traffic: - * server to server traffic, CAP "chghost" notifications, and - * possibly PART+JOIN+MODE if force-rejoin had work to do. - * We give the user a penalty so they don't flood... - */ - if (impact) - client->local->since += 7; /* Resulted in rejoins and such. */ - else - client->local->since += 4; /* No rejoins */ - } -} diff --git a/src/modules/jointhrottle.c b/src/modules/jointhrottle.c index 3247406..58a2404 100644 --- a/src/modules/jointhrottle.c +++ b/src/modules/jointhrottle.c @@ -29,7 +29,7 @@ ModuleHeader MOD_HEADER "5.0", "Join flood protection (set::anti-flood::join-flood)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; ModuleInfo *ModInfo = NULL; @@ -40,15 +40,15 @@ typedef struct JoinFlood JoinFlood; struct JoinFlood { JoinFlood *prev, *next; - char chname[CHANNELLEN+1]; + char name[CHANNELLEN+1]; time_t firstjoin; unsigned short numjoins; }; /* Forward declarations */ void jointhrottle_md_free(ModData *m); -int jointhrottle_can_join(Client *client, Channel *channel, char *key, char *parv[]); -int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]); +int jointhrottle_can_join(Client *client, Channel *channel, const char *key, char **errmsg); +int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags); static int isjthrottled(Client *client, Channel *channel); static void jointhrottle_increase_usercounter(Client *client, Channel *channel); EVENT(jointhrottle_cleanup_structs); @@ -104,7 +104,7 @@ static int isjthrottled(Client *client, Channel *channel) /* Grab user<->chan entry.. */ for (e = moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next) - if (!strcasecmp(e->chname, channel->chname)) + if (!strcasecmp(e->name, channel->name)) break; if (!e) @@ -129,7 +129,7 @@ static void jointhrottle_increase_usercounter(Client *client, Channel *channel) /* Grab user<->chan entry.. */ for (e = moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next) - if (!strcasecmp(e->chname, channel->chname)) + if (!strcasecmp(e->name, channel->name)) break; if (!e) @@ -149,15 +149,18 @@ static void jointhrottle_increase_usercounter(Client *client, Channel *channel) } } -int jointhrottle_can_join(Client *client, Channel *channel, char *key, char *parv[]) +int jointhrottle_can_join(Client *client, Channel *channel, const char *key, char **errmsg) { if (!ValidatePermissionsForPath("immune:join-flood",client,NULL,channel,NULL) && isjthrottled(client, channel)) + { + *errmsg = STR_ERR_TOOMANYJOINS; return ERR_TOOMANYJOINS; + } return 0; } -int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags, char *parv[]) +int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags) { jointhrottle_increase_usercounter(client, channel); return 0; @@ -175,12 +178,12 @@ JoinFlood *jointhrottle_addentry(Client *client, Channel *channel) abort(); for (e=moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next) - if (!strcasecmp(e->chname, channel->chname)) + if (!strcasecmp(e->name, channel->name)) abort(); /* already exists -- should never happen */ #endif e = safe_alloc(sizeof(JoinFlood)); - strlcpy(e->chname, channel->chname, sizeof(e->chname)); + strlcpy(e->name, channel->name, sizeof(e->name)); /* Insert our new entry as (new) head */ if (moddata_local_client(client, jointhrottle_md).ptr) @@ -211,11 +214,6 @@ EVENT(jointhrottle_cleanup_structs) if (jf->firstjoin + iConf.floodsettings->period[FLD_JOIN] > TStime()) continue; /* still valid entry */ -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "jointhrottle_cleanup_structs(): freeing %s/%s (%ld[%ld], %ld)", - client->name, jf->chname, jf->firstjoin, (long)(TStime() - jf->firstjoin), - iConf.floodsettings->period[FLD_JOIN]); -#endif if (moddata_local_client(client, jointhrottle_md).ptr == jf) { /* change head */ diff --git a/src/modules/json-log-tag.c b/src/modules/json-log-tag.c new file mode 100644 index 0000000..5354ef1 --- /dev/null +++ b/src/modules/json-log-tag.c @@ -0,0 +1,90 @@ +/* + * IRC - Internet Relay Chat, src/modules/monitor.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "json-log-tag", + "5.0", + "unrealircd.org/json-log tag for S2S and ircops", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Variables */ +long CAP_JSON_LOG = 0L; + +/* Forward declarations */ +int json_log_mtag_is_ok(Client *client, const char *name, const char *value); +int json_log_mtag_should_send_to_client(Client *target); + +MOD_INIT() +{ + ClientCapabilityInfo cap; + ClientCapability *c; + MessageTagHandlerInfo mtag; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&cap, 0, sizeof(cap)); + cap.name = "unrealircd.org/json-log"; + c = ClientCapabilityAdd(modinfo->handle, &cap, &CAP_JSON_LOG); + + memset(&mtag, 0, sizeof(mtag)); + mtag.name = "unrealircd.org/json-log"; + mtag.is_ok = json_log_mtag_is_ok; + mtag.should_send_to_client = json_log_mtag_should_send_to_client; + mtag.clicap_handler = c; + MessageTagHandlerAdd(modinfo->handle, &mtag); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +/** This function verifies if the client sending + * We simply allow from servers without any syntax checking. + */ +int json_log_mtag_is_ok(Client *client, const char *name, const char *value) +{ + if (IsServer(client) || IsMe(client)) + return 1; + + return 0; +} + +/** Outgoing filter for this message tag */ +int json_log_mtag_should_send_to_client(Client *target) +{ + if (IsServer(target) || (target->local && IsOper(target) && HasCapabilityFast(target, CAP_JSON_LOG))) + return 1; + return 0; +} diff --git a/src/modules/jumpserver.c b/src/modules/jumpserver.c index 8d11c53..7743624 100644 --- a/src/modules/jumpserver.c +++ b/src/modules/jumpserver.c @@ -25,12 +25,9 @@ ModuleHeader MOD_HEADER "1.1", "/jumpserver command", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -/* Defines */ -#define MSG_JUMPSERVER "JUMPSERVER" - /* Forward declarations */ CMD_FUNC(cmd_jumpserver); int jumpserver_preconnect(Client *); @@ -43,8 +40,8 @@ struct JSS char *reason; char *server; int port; - char *ssl_server; - int ssl_port; + char *tls_server; + int tls_port; }; JSS *jss=NULL; /**< JumpServer Status. NULL=disabled. */ @@ -53,7 +50,7 @@ MOD_INIT() { MARK_AS_OFFICIAL_MODULE(modinfo); LoadPersistentPointer(modinfo, jss, jumpserver_free_jss); - CommandAdd(modinfo->handle, MSG_JUMPSERVER, cmd_jumpserver, 3, CMD_USER); + CommandAdd(modinfo->handle, "JUMPSERVER", cmd_jumpserver, 3, CMD_USER); HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0, jumpserver_preconnect); return MOD_SUCCESS; } @@ -71,8 +68,8 @@ MOD_UNLOAD() static void do_jumpserver_exit_client(Client *client) { - if (IsSecure(client) && jss->ssl_server) - sendnumeric(client, RPL_REDIR, jss->ssl_server, jss->ssl_port); + if (IsSecure(client) && jss->tls_server) + sendnumeric(client, RPL_REDIR, jss->tls_server, jss->tls_port); else sendnumeric(client, RPL_REDIR, jss->server, jss->port); exit_client(client, NULL, jss->reason); @@ -91,8 +88,9 @@ static void redirect_all_clients(void) count++; } } - sendto_realops("JUMPSERVER: Redirected %d client%s", - count, count == 1 ? "" : "s"); /* Language fun... ;p */ + unreal_log(ULOG_INFO, "jumpserver", "JUMPSERVER_REPORT", NULL, + "[jumpserver] Redirected $num_clients client(s)", + log_data_integer("num_clients", count)); } int jumpserver_preconnect(Client *client) @@ -111,7 +109,7 @@ void free_jss(void) { safe_free(jss->server); safe_free(jss->reason); - safe_free(jss->ssl_server); + safe_free(jss->tls_server); safe_free(jss); jss = NULL; } @@ -124,8 +122,10 @@ void jumpserver_free_jss(ModData *m) CMD_FUNC(cmd_jumpserver) { - char *serv, *sslserv=NULL, *reason, *p; + char *serv, *tlsserv=NULL, *p; + const char *reason; int all=0, port=6667, sslport=6697; + char request[BUFSIZE]; char logbuf[512]; if (!IsOper(client)) @@ -136,9 +136,9 @@ CMD_FUNC(cmd_jumpserver) if ((parc < 2) || BadPtr(parv[1])) { - if (jss && jss->ssl_server) - sendnotice(client, "JumpServer is \002ENABLED\002 to %s:%d (SSL/TLS: %s:%d) with reason '%s'", - jss->server, jss->port, jss->ssl_server, jss->ssl_port, jss->reason); + if (jss && jss->tls_server) + sendnotice(client, "JumpServer is \002ENABLED\002 to %s:%d (TLS: %s:%d) with reason '%s'", + jss->server, jss->port, jss->tls_server, jss->tls_port, jss->reason); else if (jss) sendnotice(client, "JumpServer is \002ENABLED\002 to %s:%d with reason '%s'", @@ -156,10 +156,8 @@ CMD_FUNC(cmd_jumpserver) return; } free_jss(); - snprintf(logbuf, sizeof(logbuf), "%s (%s@%s) turned JUMPSERVER OFF", - client->name, client->user->username, client->user->realhost); - sendto_realops("%s", logbuf); - ircd_log(LOG_ERROR, "%s", logbuf); + unreal_log(ULOG_INFO, "jumpserver", "JUMPSERVER_DISABLED", client, + "[jumpserver] $client.details turned jumpserver OFF"); return; } @@ -167,7 +165,7 @@ CMD_FUNC(cmd_jumpserver) { /* Waah, pretty verbose usage info ;) */ sendnotice(client, "Use: /JUMPSERVER [:port] "); - sendnotice(client, " Or: /JUMPSERVER [:port]/[:port] "); + sendnotice(client, " Or: /JUMPSERVER [:port]/[:port] "); sendnotice(client, "if 'NEW' is chosen then only new (incoming) connections will be redirected"); sendnotice(client, "if 'ALL' is chosen then all clients except opers will be redirected immediately (+incoming connections)"); sendnotice(client, "Example: /JUMPSERVER irc2.test.net NEW This server will be upgraded, please use irc2.test.net for now"); @@ -177,19 +175,16 @@ CMD_FUNC(cmd_jumpserver) return; } - /* Parsing code follows... - * The parsing of the SSL stuff is still done even on non-SSL, - * but it's simply not used/applied :). - * Reason for this is to reduce non-SSL/SSL inconsistency issues. - */ + /* Parsing code follows... */ - serv = parv[1]; + strlcpy(request, parv[1], sizeof(request)); + serv = request; p = strchr(serv, '/'); if (p) { *p = '\0'; - sslserv = p+1; + tlsserv = p+1; } p = strchr(serv, ':'); @@ -203,21 +198,21 @@ CMD_FUNC(cmd_jumpserver) return; } } - if (sslserv) + if (tlsserv) { - p = strchr(sslserv, ':'); + p = strchr(tlsserv, ':'); if (p) { *p++ = '\0'; sslport = atoi(p); if ((sslport < 1) || (sslport > 65535)) { - sendnotice(client, "Invalid SSL/TLS serverport specified (%d)", sslport); + sendnotice(client, "Invalid TLS serverport specified (%d)", sslport); return; } } - if (!*sslserv) - sslserv = NULL; + if (!*tlsserv) + tlsserv = NULL; } if (!strcasecmp(parv[2], "new")) all = 0; @@ -239,27 +234,37 @@ CMD_FUNC(cmd_jumpserver) /* Set it */ safe_strdup(jss->server, serv); jss->port = port; - if (sslserv) + if (tlsserv) { - safe_strdup(jss->ssl_server, sslserv); - jss->ssl_port = sslport; + safe_strdup(jss->tls_server, tlsserv); + jss->tls_port = sslport; } safe_strdup(jss->reason, reason); /* Broadcast/log */ - if (sslserv) - snprintf(logbuf, sizeof(logbuf), "%s (%s@%s) added JUMPSERVER redirect for %s to %s:%d [SSL/TLS: %s:%d] with reason '%s'", - client->name, client->user->username, client->user->realhost, - all ? "ALL CLIENTS" : "all new clients", - jss->server, jss->port, jss->ssl_server, jss->ssl_port, jss->reason); - else - snprintf(logbuf, sizeof(logbuf), "%s (%s@%s) added JUMPSERVER redirect for %s to %s:%d with reason '%s'", - client->name, client->user->username, client->user->realhost, - all ? "ALL CLIENTS" : "all new clients", - jss->server, jss->port, jss->reason); - - sendto_realops("%s", logbuf); - ircd_log(LOG_ERROR, "%s", logbuf); + if (tlsserv) + { + unreal_log(ULOG_INFO, "jumpserver", "JUMPSERVER_ENABLED", client, + "[jumpserver] $client.details turned jumpserver ON for $jumpserver_who " + "to $jumpserver_server:$jumpserver_port " + "[TLS: $jumpserver_tls_server:$jumpserver_tls_port] " + "($reason)", + log_data_string("jumpserver_who", all ? "ALL CLIENTS" : "all new clients"), + log_data_string("jumpserver_server", jss->server), + log_data_integer("jumpserver_port", jss->port), + log_data_string("jumpserver_tls_server", jss->tls_server), + log_data_integer("jumpserver_tls_port", jss->tls_port), + log_data_string("reason", jss->reason)); + } else { + unreal_log(ULOG_INFO, "jumpserver", "JUMPSERVER_ENABLED", client, + "[jumpserver] $client.details turned jumpserver ON for $jumpserver_who " + "to $jumpserver_server:$jumpserver_port " + "($reason)", + log_data_string("jumpserver_who", all ? "ALL CLIENTS" : "all new clients"), + log_data_string("jumpserver_server", jss->server), + log_data_integer("jumpserver_port", jss->port), + log_data_string("reason", jss->reason)); + } if (all) redirect_all_clients(); diff --git a/src/modules/kick.c b/src/modules/kick.c index 8545f9b..f5a2b09 100644 --- a/src/modules/kick.c +++ b/src/modules/kick.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "5.0", "command /kick", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ @@ -59,6 +59,16 @@ MOD_UNLOAD() return MOD_SUCCESS; } +void kick_operoverride_msg(Client *client, Channel *channel, Client *target, char *reason) +{ + unreal_log(ULOG_INFO, "operoverride", "OPEROVERRIDE_KICK", client, + "OperOverride: $client.details kicked $target from $channel ($reason)", + log_data_string("override_type", "kick"), + log_data_string("reason", reason), + log_data_client("target", target), + log_data_channel("channel", channel)); +} + /** Kick a user from a channel. * @param initial_mtags Message tags associated with this KICK (can be NULL) * @param channel The channel where the KICK should happen @@ -84,37 +94,37 @@ void _kick_user(MessageTag *initial_mtags, Channel *channel, Client *client, Cli new_message(client, NULL, &initial_mtags); } - new_message_special(client, initial_mtags, &mtags, ":%s KICK %s %s", client->name, channel->chname, victim->name); + new_message_special(client, initial_mtags, &mtags, ":%s KICK %s %s", client->name, channel->name, victim->name); /* The same message is actually sent at 5 places below (though max 4 at most) */ if (MyUser(client)) - RunHook5(HOOKTYPE_LOCAL_KICK, client, victim, channel, mtags, comment); + RunHook(HOOKTYPE_LOCAL_KICK, client, victim, channel, mtags, comment); else - RunHook5(HOOKTYPE_REMOTE_KICK, client, victim, channel, mtags, comment); + RunHook(HOOKTYPE_REMOTE_KICK, client, victim, channel, mtags, comment); if (invisible_user_in_channel(victim, channel)) { /* Send it only to chanops & victim */ sendto_channel(channel, client, victim, - PREFIX_HALFOP|PREFIX_OP|PREFIX_OWNER|PREFIX_ADMIN, 0, + "h", 0, SEND_LOCAL, mtags, ":%s KICK %s %s :%s", - client->name, channel->chname, victim->name, comment); + client->name, channel->name, victim->name, comment); if (MyUser(victim)) { sendto_prefix_one(victim, client, mtags, ":%s KICK %s %s :%s", - client->name, channel->chname, victim->name, comment); + client->name, channel->name, victim->name, comment); } } else { /* NORMAL */ sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, ":%s KICK %s %s :%s", - client->name, channel->chname, victim->name, comment); + client->name, channel->name, victim->name, comment); } sendto_server(client, 0, 0, mtags, ":%s KICK %s %s :%s", - client->id, channel->chname, victim->id, comment); + client->id, channel->name, victim->id, comment); free_message_tags(mtags); if (initial_mtags_generated) @@ -123,34 +133,44 @@ void _kick_user(MessageTag *initial_mtags, Channel *channel, Client *client, Cli initial_mtags = NULL; } - remove_user_from_channel(victim, channel); + if (MyUser(victim)) + { + unreal_log(ULOG_INFO, "kick", "LOCAL_CLIENT_KICK", victim, + "User $client kicked from $channel", + log_data_channel("channel", channel)); + } else { + unreal_log(ULOG_INFO, "kick", "REMOTE_CLIENT_KICK", victim, + "User $client kicked from $channel", + log_data_channel("channel", channel)); + } + + remove_user_from_channel(victim, channel, 1); } /* ** cmd_kick -** parv[1] = channel -** parv[2] = client to kick +** parv[1] = channel (single channel) +** parv[2] = client to kick (comma separated) ** parv[3] = kick comment */ -#ifdef PREFIX_AQ -#define CHFL_ISOP (CHFL_CHANOWNER|CHFL_CHANADMIN|CHFL_CHANOP) -#else -#define CHFL_ISOP (CHFL_CHANOP) -#endif - CMD_FUNC(cmd_kick) { - Client *who; + Client *target; Channel *channel; int chasing = 0; - char *comment, *name, *p = NULL, *user, *p2 = NULL, *badkick; + char *p = NULL, *user, *p2 = NULL, *badkick; + char comment[MAXKICKLEN+1]; Membership *lp; Hook *h; int ret; int ntargets = 0; int maxtargets = max_targets_for_command("KICK"); MessageTag *mtags; + char request[BUFSIZE]; + char request_chans[BUFSIZE]; + const char *client_member_modes = NULL; + const char *target_member_modes; if (parc < 3 || *parv[1] == '\0') { @@ -158,216 +178,192 @@ CMD_FUNC(cmd_kick) return; } - comment = (BadPtr(parv[3])) ? client->name : parv[3]; + if (BadPtr(parv[3])) + strlcpy(comment, client->name, sizeof(comment)); + else + strlncpy(comment, parv[3], sizeof(comment), iConf.kick_length); - if (!BadPtr(parv[3]) && (strlen(comment) > iConf.kick_length)) - comment[iConf.kick_length] = '\0'; - - for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) + strlcpy(request_chans, parv[1], sizeof(request_chans)); + p = strchr(request_chans, ','); + if (p) + *p = '\0'; + channel = find_channel(request_chans); + if (!channel) { - long client_flags = 0; - channel = get_channel(client, name, !CREATE); - if (!channel) + sendnumeric(client, ERR_NOSUCHCHANNEL, request_chans); + return; + } + + /* Store "client" access flags */ + if (IsUser(client)) + client_member_modes = get_channel_access(client, channel); + if (MyUser(client) && !IsULine(client) && + !op_can_override("channel:override:kick:no-ops",client,channel,NULL) && + !check_channel_access(client, channel, "hoaq")) + { + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); + return; + } + + strlcpy(request, parv[2], sizeof(request)); + for (user = strtoken(&p2, request, ","); user; user = strtoken(&p2, NULL, ",")) + { + if (MyUser(client) && (++ntargets > maxtargets)) { - sendnumeric(client, ERR_NOSUCHCHANNEL, name); - continue; + sendnumeric(client, ERR_TOOMANYTARGETS, user, maxtargets, "KICK"); + break; } - /* Store "client" access flags */ - if (IsUser(client)) - client_flags = get_access(client, channel); - if (MyUser(client) && !IsULine(client) && !op_can_override("channel:override:kick:no-ops",client,channel,NULL) - && !(client_flags & CHFL_ISOP) && !(client_flags & CHFL_HALFOP)) + + if (!(target = find_chasing(client, user, &chasing))) + continue; /* No such user left! */ + + if (!target->user) + continue; /* non-user */ + + lp = find_membership_link(target->user->channel, channel); + if (!lp) { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); + if (MyUser(client)) + sendnumeric(client, ERR_USERNOTINCHANNEL, user, request_chans); continue; } - for (; (user = strtoken(&p2, parv[2], ",")); parv[2] = NULL) - { - long who_flags; + if (IsULine(client) || IsServer(client)) + goto attack; - if (MyUser(client) && (++ntargets > maxtargets)) + /* Note for coders regarding oper override: + * always let a remote kick (=from a user on another server) through or + * else we will get desynced. In short this means all the denying should + * always contain a && MyUser(client) and at the end + * a remote kick should always be allowed (pass through). -- Syzop + */ + + /* Store "target" access flags */ + target_member_modes = get_channel_access(target, channel); + + badkick = NULL; + ret = EX_ALLOW; + for (h = Hooks[HOOKTYPE_CAN_KICK]; h; h = h->next) { + int n = (*(h->func.intfunc))(client, target, channel, comment, client_member_modes, target_member_modes, &badkick); + + if (n == EX_DENY) + ret = n; + else if (n == EX_ALWAYS_DENY) { - sendnumeric(client, ERR_TOOMANYTARGETS, user, maxtargets, "KICK"); + ret = n; break; } + } - if (!(who = find_chasing(client, user, &chasing))) - continue; /* No such user left! */ + if (ret == EX_ALWAYS_DENY) + { + if (MyUser(client) && badkick) + sendto_one(client, NULL, "%s", badkick); /* send error, if any */ - if (!who->user) - continue; /* non-user */ + if (MyUser(client)) + continue; /* reject the kick (note: we never block remote kicks) */ + } - lp = find_membership_link(who->user->channel, channel); - if (!lp) - { - if (MyUser(client)) - sendnumeric(client, ERR_USERNOTINCHANNEL, user, name); - continue; - } - - if (IsULine(client) || IsServer(client)) - goto attack; - - /* Note for coders regarding oper override: - * always let a remote kick (=from a user on another server) through or - * else we will get desynced. In short this means all the denying should - * always contain a && MyUser(client) and at the end - * a remote kick should always be allowed (pass through). -- Syzop - */ - - /* Store "who" access flags */ - who_flags = get_access(who, channel); - - badkick = NULL; - ret = EX_ALLOW; - for (h = Hooks[HOOKTYPE_CAN_KICK]; h; h = h->next) { - int n = (*(h->func.intfunc))(client, who, channel, comment, client_flags, who_flags, &badkick); - - if (n == EX_DENY) - ret = n; - else if (n == EX_ALWAYS_DENY) - { - ret = n; - break; - } - } - - if (ret == EX_ALWAYS_DENY) + if (ret == EX_DENY) + { + /* If set it means 'not allowed to kick'.. now check if (s)he can override that.. */ + if (op_can_override("channel:override:kick:no-ops",client,channel,NULL)) { + kick_operoverride_msg(client, channel, target, comment); + goto attack; /* all other checks don't matter anymore (and could cause double msgs) */ + } else { + /* Not an oper overriding */ if (MyUser(client) && badkick) sendto_one(client, NULL, "%s", badkick); /* send error, if any */ - if (MyUser(client)) - continue; /* reject the kick (note: we never block remote kicks) */ + continue; /* reject the kick */ } + } - if (ret == EX_DENY) + // FIXME: Most, maybe even all, of these must be moved to HOOKTYPE_CAN_KICK checks in the corresponding halfop/chanop/chanadmin/chanowner modules :) + // !!!! FIXME + + /* we are neither +o nor +h, OR.. + * we are +h but target is +o, OR... + * we are +h and target is +h + */ + if (op_can_override("channel:override:kick:no-ops",client,channel,NULL)) + { + if ((!check_channel_access_string(client_member_modes, "o") && !check_channel_access_string(client_member_modes, "h")) || + (check_channel_access_string(client_member_modes, "h") && check_channel_access_string(target_member_modes, "h")) || + (check_channel_access_string(client_member_modes, "h") && check_channel_access_string(target_member_modes, "o"))) { - /* If set it means 'not allowed to kick'.. now check if (s)he can override that.. */ - if (op_can_override("channel:override:kick:no-ops",client,channel,NULL)) - { - sendto_snomask(SNO_EYES, - "*** OperOverride -- %s (%s@%s) KICK %s %s (%s)", - client->name, client->user->username, client->user->realhost, - channel->chname, who->name, comment); - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) KICK %s %s (%s)", - client->name, client->user->username, client->user->realhost, - channel->chname, who->name, comment); - goto attack; /* all other checks don't matter anymore (and could cause double msgs) */ - } else { - /* Not an oper overriding */ - if (MyUser(client) && badkick) - sendto_one(client, NULL, "%s", badkick); /* send error, if any */ + kick_operoverride_msg(client, channel, target, comment); + goto attack; + } /* is_chan_op */ - continue; /* reject the kick */ - } - } + } - /* we are neither +o nor +h, OR.. - * we are +h but victim is +o, OR... - * we are +h and victim is +h - */ - if (op_can_override("channel:override:kick:no-ops",client,channel,NULL)) + /* target is +a/+q, and we are not +q? */ + if (check_channel_access_string(target_member_modes, "qa") && !check_channel_access_string(client_member_modes, "q")) + { + if (client == target) + goto attack; /* kicking self == ok */ + if (op_can_override("channel:override:kick:owner",client,channel,NULL)) /* (and f*ck local ops) */ { - if ((!(client_flags & CHFL_ISOP) && !(client_flags & CHFL_HALFOP)) || - ((client_flags & CHFL_HALFOP) && (who_flags & CHFL_ISOP)) || - ((client_flags & CHFL_HALFOP) && (who_flags & CHFL_HALFOP))) - { - sendto_snomask(SNO_EYES, - "*** OperOverride -- %s (%s@%s) KICK %s %s (%s)", - client->name, client->user->username, client->user->realhost, - channel->chname, who->name, comment); - - /* Logging Implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) KICK %s %s (%s)", - client->name, client->user->username, client->user->realhost, - channel->chname, who->name, comment); - - goto attack; - } /* is_chan_op */ - + /* IRCop kicking owner/prot */ + kick_operoverride_msg(client, channel, target, comment); + goto attack; } - - /* victim is +a or +q, we are not +q */ - if ((who_flags & (CHFL_CHANOWNER|CHFL_CHANADMIN)) - && !(client_flags & CHFL_CHANOWNER)) { - if (client == who) - goto attack; /* kicking self == ok */ - if (op_can_override("channel:override:kick:owner",client,channel,NULL)) /* (and f*ck local ops) */ - { /* IRCop kicking owner/prot */ - sendto_snomask(SNO_EYES, - "*** OperOverride -- %s (%s@%s) KICK %s %s (%s)", - client->name, client->user->username, client->user->realhost, - channel->chname, who->name, comment); - - /* Logging Implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) KICK %s %s (%s)", - client->name, client->user->username, client->user->realhost, - channel->chname, who->name, comment); - - goto attack; - } - else if (!IsULine(client) && (who != client) && MyUser(client)) - { - char errbuf[NICKLEN+25]; - if (who_flags & CHFL_CHANOWNER) - ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel owner", - who->name); - else - ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel admin", - who->name); - sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK", - errbuf); - goto deny; - } /* chanadmin/chanowner */ - } - - /* victim is +o, we are +h [operoverride is already taken care of 2 blocks above] */ - if ((who_flags & CHFL_ISOP) && (client_flags & CHFL_HALFOP) - && !(client_flags & CHFL_ISOP) && !IsULine(client) && MyUser(client)) + else if (!IsULine(client) && (target != client) && MyUser(client)) { - char errbuf[NICKLEN+30]; - ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel operator", who->name); - sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK", - errbuf); + char errbuf[NICKLEN+25]; + if (check_channel_access_string(target_member_modes, "q")) + ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel owner", target->name); + else + ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel admin", target->name); + sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK", errbuf); goto deny; } + } - /* victim is +h, we are +h [operoverride is already taken care of 3 blocks above] */ - if ((who_flags & CHFL_HALFOP) && (client_flags & CHFL_HALFOP) - && !(client_flags & CHFL_ISOP) && MyUser(client)) - { - char errbuf[NICKLEN+15]; - ircsnprintf(errbuf, sizeof(errbuf), "%s is a halfop", who->name); - sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK", - errbuf); - goto deny; - } /* halfop */ + /* target is +o, we are +h [operoverride is already taken care of 2 blocks above] */ + if (check_channel_access_string(target_member_modes, "h") && check_channel_access_string(client_member_modes, "h") + && !check_channel_access_string(client_member_modes, "o") && !IsULine(client) && MyUser(client)) + { + char errbuf[NICKLEN+30]; + ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel operator", target->name); + sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK", + errbuf); + goto deny; + } - /* allowed (either coz access granted or a remote kick), so attack! */ - goto attack; + /* target is +h, we are +h [operoverride is already taken care of 3 blocks above] */ + if (check_channel_access_string(target_member_modes, "o") && check_channel_access_string(client_member_modes, "h") + && !check_channel_access_string(client_member_modes, "o") && MyUser(client)) + { + char errbuf[NICKLEN+15]; + ircsnprintf(errbuf, sizeof(errbuf), "%s is a halfop", target->name); + sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK", + errbuf); + goto deny; + } /* halfop */ - deny: - continue; + /* allowed (either coz access granted or a remote kick), so attack! */ + goto attack; - attack: - if (MyConnect(client)) { - int breakit = 0; - Hook *h; - for (h = Hooks[HOOKTYPE_PRE_LOCAL_KICK]; h; h = h->next) { - if((*(h->func.intfunc))(client,who,channel,comment) > 0) { - breakit = 1; - break; - } + deny: + continue; + + attack: + if (MyConnect(client)) { + int breakit = 0; + Hook *h; + for (h = Hooks[HOOKTYPE_PRE_LOCAL_KICK]; h; h = h->next) { + if ((*(h->func.intfunc))(client,target,channel,comment) > 0) { + breakit = 1; + break; } - if (breakit) - continue; } + if (breakit) + continue; + } - kick_user(recv_mtags, channel, client, who, comment); - } /* loop on parv[2] */ - if (MyUser(client)) - break; - } /* loop on parv[1] */ + kick_user(recv_mtags, channel, client, target, comment); + } } diff --git a/src/modules/kill.c b/src/modules/kill.c index 67c53c3..2a62a75 100644 --- a/src/modules/kill.c +++ b/src/modules/kill.c @@ -29,7 +29,7 @@ ModuleHeader MOD_HEADER "5.0", "command /kill", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -56,7 +56,9 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_kill) { - char *targetlist, *reason; + char targetlist[BUFSIZE]; + char reason[BUFSIZE]; + char *str; char *nick, *save = NULL; Client *target; Hook *h; @@ -69,20 +71,18 @@ CMD_FUNC(cmd_kill) return; } - targetlist = parv[1]; - reason = parv[2]; - if (!IsServer(client->direction) && !ValidatePermissionsForPath("kill:global",client,NULL,NULL,NULL) && !ValidatePermissionsForPath("kill:local",client,NULL,NULL,NULL)) { sendnumeric(client, ERR_NOPRIVILEGES); return; } - if (strlen(reason) > iConf.quit_length) - reason[iConf.quit_length] = '\0'; - if (MyUser(client)) - targetlist = canonize(targetlist); + strlcpy(targetlist, canonize(parv[1]), sizeof(targetlist)); + else + strlcpy(targetlist, parv[1], sizeof(targetlist)); + + strlncpy(reason, parv[2], sizeof(reason), iConf.quit_length); for (nick = strtoken(&save, targetlist, ","); nick; nick = strtoken(&save, NULL, ",")) { @@ -94,7 +94,7 @@ CMD_FUNC(cmd_kill) break; } - target = find_person(nick, NULL); + target = find_user(nick, NULL); /* If a local user issued the /KILL then we will "chase" the user. * In other words: we'll check the history for recently changed nicks. @@ -138,16 +138,10 @@ CMD_FUNC(cmd_kill) /* From here on, the kill is probably going to be successful. */ - sendto_snomask(SNO_KILLS, - "*** Received KILL message for %s (%s@%s) from %s: %s", - target->name, target->user->username, GetHost(target), - client->name, - reason); - - ircd_log(LOG_KILL, "KILL (%s) by %s (%s)", - make_nick_user_host(target->name, target->user->username, GetHost(target)), - client->name, - reason); + unreal_log(ULOG_INFO, "kill", "KILL_COMMAND", client, + "Client killed: $target.details [by: $client] ($reason)", + log_data_client("target", target), + log_data_string("reason", reason)); new_message(client, recv_mtags, &mtags); @@ -177,7 +171,7 @@ CMD_FUNC(cmd_kill) } if (MyUser(client)) - RunHook3(HOOKTYPE_LOCAL_KILL, client, target, reason); + RunHook(HOOKTYPE_LOCAL_KILL, client, target, reason); ircsnprintf(buf2, sizeof(buf2), "Killed by %s (%s)", client->name, reason); exit_client(target, mtags, buf2); diff --git a/src/modules/knock.c b/src/modules/knock.c index d2197af..16d2a7f 100644 --- a/src/modules/knock.c +++ b/src/modules/knock.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /knock", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -78,7 +78,7 @@ CMD_FUNC(cmd_knock) Hook *h; int i = 0; MessageTag *mtags = NULL; - char *reason; + const char *reason; if (IsServer(client)) return; @@ -97,7 +97,7 @@ CMD_FUNC(cmd_knock) return; } - if (!(channel = find_channel(parv[1], NULL))) + if (!(channel = find_channel(parv[1]))) { sendnumeric(client, ERR_CANNOTKNOCK, parv[1], "Channel does not exist!"); return; @@ -106,25 +106,25 @@ CMD_FUNC(cmd_knock) /* IsMember bugfix by codemastr */ if (IsMember(client, channel) == 1) { - sendnumeric(client, ERR_CANNOTKNOCK, channel->chname, "You're already there!"); + sendnumeric(client, ERR_CANNOTKNOCK, channel->name, "You're already there!"); return; } - if (!(channel->mode.mode & MODE_INVITEONLY)) + if (!has_channel_mode(channel, 'i')) { - sendnumeric(client, ERR_CANNOTKNOCK, channel->chname, "Channel is not invite only!"); + sendnumeric(client, ERR_CANNOTKNOCK, channel->name, "Channel is not invite only!"); return; } if (is_banned(client, channel, BANCHK_JOIN, NULL, NULL)) { - sendnumeric(client, ERR_CANNOTKNOCK, channel->chname, "You're banned!"); + sendnumeric(client, ERR_CANNOTKNOCK, channel->name, "You're banned!"); return; } for (h = Hooks[HOOKTYPE_PRE_KNOCK]; h; h = h->next) { - i = (*(h->func.intfunc))(client,channel); + i = (*(h->func.intfunc))(client, channel, &reason); if (i == HOOK_DENY || i == HOOK_ALLOW) break; } @@ -142,19 +142,19 @@ CMD_FUNC(cmd_knock) new_message(&me, NULL, &mtags); - sendto_channel(channel, &me, NULL, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER, + sendto_channel(channel, &me, NULL, "o", 0, SEND_LOCAL, mtags, ":%s NOTICE @%s :[Knock] by %s!%s@%s (%s)", - me.name, channel->chname, + me.name, channel->name, client->name, client->user->username, GetHost(client), reason); - sendto_server(client, 0, 0, mtags, ":%s KNOCK %s :%s", client->id, channel->chname, reason); + sendto_server(client, 0, 0, mtags, ":%s KNOCK %s :%s", client->id, channel->name, reason); if (MyUser(client)) - sendnotice(client, "Knocked on %s", channel->chname); + sendnotice(client, "Knocked on %s", channel->name); - RunHook4(HOOKTYPE_KNOCK, client, channel, mtags, parv[2]); + RunHook(HOOKTYPE_KNOCK, client, channel, mtags, parv[2]); free_message_tags(mtags); } diff --git a/src/modules/labeled-response.c b/src/modules/labeled-response.c index 56e9183..5027fe0 100644 --- a/src/modules/labeled-response.c +++ b/src/modules/labeled-response.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "5.0", "Labeled response CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Data structures */ @@ -43,8 +43,8 @@ struct LabeledResponseContext { }; /* Forward declarations */ -int lr_pre_command(Client *from, MessageTag *mtags, char *buf); -int lr_post_command(Client *from, MessageTag *mtags, char *buf); +int lr_pre_command(Client *from, MessageTag *mtags, const char *buf); +int lr_post_command(Client *from, MessageTag *mtags, const char *buf); int lr_close_connection(Client *client); int lr_packet(Client *from, Client *to, Client *intended_to, char **msg, int *len); void *_labeled_response_save_context(void); @@ -61,7 +61,7 @@ static long CAP_LABELED_RESPONSE = 0L; static char packet[8192]; -int labeled_response_mtag_is_ok(Client *client, char *name, char *value); +int labeled_response_mtag_is_ok(Client *client, const char *name, const char *value); MOD_TEST() { @@ -111,7 +111,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int lr_pre_command(Client *from, MessageTag *mtags, char *buf) +int lr_pre_command(Client *from, MessageTag *mtags, const char *buf) { memset(¤tcmd, 0, sizeof(currentcmd)); labeled_response_inhibit = labeled_response_inhibit_end = labeled_response_force = 0; @@ -156,7 +156,7 @@ char *gen_start_batch(void) return buf; } -int lr_post_command(Client *from, MessageTag *mtags, char *buf) +int lr_post_command(Client *from, MessageTag *mtags, const char *buf) { /* ** IMPORTANT ** * Take care NOT to return here, use 'goto done' instead @@ -336,7 +336,7 @@ int lr_packet(Client *from, Client *to, Client *intended_to, char **msg, int *le /** This function verifies if the client sending the * tag is permitted to do so and uses a permitted syntax. */ -int labeled_response_mtag_is_ok(Client *client, char *name, char *value) +int labeled_response_mtag_is_ok(Client *client, const char *name, const char *value) { if (BadPtr(value)) return 0; diff --git a/src/modules/lag.c b/src/modules/lag.c index b2c1b59..9018303 100644 --- a/src/modules/lag.c +++ b/src/modules/lag.c @@ -31,7 +31,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /lag", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -78,7 +78,7 @@ CMD_FUNC(cmd_lag) return; } - if (hunt_server(client, recv_mtags, ":%s LAG :%s", 1, parc, parv) == HUNTED_NOSUCH) + if (hunt_server(client, recv_mtags, "LAG", 1, parc, parv) == HUNTED_NOSUCH) return; sendnotice(client, "Lag reply -- %s %s %lld", me.name, parv[1], (long long)TStime()); diff --git a/src/modules/link-security.c b/src/modules/link-security.c index d4e6341..76e8bed 100644 --- a/src/modules/link-security.c +++ b/src/modules/link-security.c @@ -29,14 +29,14 @@ ModuleHeader MOD_HEADER "5.0", "Link Security CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ -char *link_security_md_serialize(ModData *m); -void link_security_md_unserialize(char *str, ModData *m); +const char *link_security_md_serialize(ModData *m); +void link_security_md_unserialize(const char *str, ModData *m); EVENT(checklinksec); -char *link_security_capability_parameter(Client *client); +const char *link_security_capability_parameter(Client *client); CMD_FUNC(cmd_linksecurity); /* Global variables */ @@ -59,6 +59,7 @@ MOD_INIT() mreq.serialize = link_security_md_serialize; mreq.unserialize = link_security_md_unserialize; mreq.sync = 1; + mreq.self_write = 1; link_security_md = ModDataAdd(modinfo->handle, mreq); if (!link_security_md) { @@ -97,7 +98,7 @@ MOD_UNLOAD() */ #define LNKSECMAGIC 100 -char *link_security_md_serialize(ModData *m) +const char *link_security_md_serialize(ModData *m) { static char buf[32]; if (m->i == 0) @@ -106,7 +107,7 @@ char *link_security_md_serialize(ModData *m) return buf; } -void link_security_md_unserialize(char *str, ModData *m) +void link_security_md_unserialize(const char *str, ModData *m) { m->i = atoi(str) + LNKSECMAGIC; } @@ -118,9 +119,9 @@ int certificate_verification_active(Client *client) { ConfigItem_link *conf; - if (!client->serv || !client->serv->conf) + if (!client->server || !client->server->conf) return 0; /* wtf? */ - conf = client->serv->conf; + conf = client->server->conf; if (conf->verify_certificate) return 1; /* yes, verify-certificate is 'yes' */ @@ -140,7 +141,7 @@ int certificate_verification_active(Client *client) /** Calculate our (local) link-security level. * This means stepping through the list of directly linked - * servers and determining if they are linked via SSL and + * servers and determining if they are linked via TLS and * certificate verification is active. * @returns value from 0 to 2. */ @@ -154,7 +155,7 @@ int our_link_security(void) if (IsLocalhost(client)) continue; /* server connected via localhost */ if (!IsSecure(client)) - return 0; /* Any non-SSL server (which is not localhost) results in level 0. */ + return 0; /* Any non-TLS server (which is not localhost) results in level 0. */ if (!certificate_verification_active(client)) level = 1; /* downgrade to level 1 */ } @@ -177,7 +178,6 @@ EVENT(checklinksec) int last_local_link_security = local_link_security; int last_global_link_security = global_link_security; Client *client; - char *s; int v; int warning_sent = 0; @@ -193,7 +193,7 @@ EVENT(checklinksec) global_link_security = 2; list_for_each_entry(client, &global_server_list, client_node) { - s = moddata_client_get(client, "link-security"); + const char *s = moddata_client_get(client, "link-security"); if (s) { v = atoi(s); @@ -209,15 +209,19 @@ EVENT(checklinksec) if (local_link_security < last_local_link_security) { - sendto_realops("Local link-security downgraded from level %d to %d due to just linked in server(s)", - last_local_link_security, local_link_security); + unreal_log(ULOG_INFO, "link-security", "LOCAL_LINK_SECURITY_DOWNGRADED", NULL, + "Local link-security downgraded from level $previous_level to $new_level due to just linked in server(s)", + log_data_integer("previous_level", last_local_link_security), + log_data_integer("new_level", local_link_security)); warning_sent = 1; } if (global_link_security < last_global_link_security) { - sendto_realops("Global link-security downgraded from level %d to %d due to just linked in server(s)", - last_global_link_security, global_link_security); + unreal_log(ULOG_INFO, "link-security", "GLOBAL_LINK_SECURITY_DOWNGRADED", NULL, + "Global link-security downgraded from level $previous_level to $new_level due to just linked in server(s)", + log_data_integer("previous_level", last_global_link_security), + log_data_integer("new_level", global_link_security)); warning_sent = 1; } @@ -225,12 +229,14 @@ EVENT(checklinksec) if (warning_sent) { - sendto_realops("Effective (network-wide) link-security is: level %d", effective_link_security); - sendto_realops("More information about this can be found at https://www.unrealircd.org/docs/Link_security"); + unreal_log(ULOG_INFO, "link-security", "EFFECTIVE_LINK_SECURITY_REPORT", NULL, + "Effective (network-wide) link-security is now: level $effective_link_security\n" + "More information about this can be found at https://www.unrealircd.org/docs/Link_security", + log_data_integer("effective_link_security", effective_link_security)); } } -char *link_security_capability_parameter(Client *client) +const char *link_security_capability_parameter(Client *client) { return valtostr(effective_link_security); } @@ -239,8 +245,6 @@ char *link_security_capability_parameter(Client *client) CMD_FUNC(cmd_linksecurity) { Client *acptr; - char *s; - int v; if (!IsOper(client)) { @@ -253,15 +257,11 @@ CMD_FUNC(cmd_linksecurity) sendtxtnumeric(client, "= By server ="); list_for_each_entry(acptr, &global_server_list, client_node) { - v = -1; - s = moddata_client_get(acptr, "link-security"); + const char *s = moddata_client_get(acptr, "link-security"); if (s) - { - v = atoi(s); - sendtxtnumeric(client, "%s: level %d", acptr->name, v); - } else { + sendtxtnumeric(client, "%s: level %d", acptr->name, atoi(s)); + else sendtxtnumeric(client, "%s: level UNKNOWN", acptr->name); - } } sendtxtnumeric(client, "-"); @@ -271,9 +271,9 @@ CMD_FUNC(cmd_linksecurity) sendtxtnumeric(client, "= Legend ="); sendtxtnumeric(client, "Higher level means better link security"); sendtxtnumeric(client, "Level UNKNOWN: Not an UnrealIRCd server (eg: services) or an old version (<4.0.14)"); - sendtxtnumeric(client, "Level 0: One or more servers linked insecurely (not using SSL/TLS)"); - sendtxtnumeric(client, "Level 1: Servers are linked with SSL/TLS but at least one of them is not verifying certificates"); - sendtxtnumeric(client, "Level 2: Servers linked with SSL/TLS and certificates are properly verified"); + sendtxtnumeric(client, "Level 0: One or more servers linked insecurely (not using TLS)"); + sendtxtnumeric(client, "Level 1: Servers are linked with TLS but at least one of them is not verifying certificates"); + sendtxtnumeric(client, "Level 2: Servers linked with TLS and certificates are properly verified"); sendtxtnumeric(client, "-"); sendtxtnumeric(client, "= More information ="); sendtxtnumeric(client, "To understand more about link security and how to improve your level"); diff --git a/src/modules/links.c b/src/modules/links.c index 6036cc7..041bd97 100644 --- a/src/modules/links.c +++ b/src/modules/links.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /links", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -69,7 +69,7 @@ CMD_FUNC(cmd_links) sendnumeric(client, RPL_LINKS, acptr->name, me.name, 1, (acptr->info[0] ? acptr->info : "(Unknown Location)")); else - sendnumeric(client, RPL_LINKS, acptr->name, acptr->serv->up, + sendnumeric(client, RPL_LINKS, acptr->name, acptr->uplink ? acptr->uplink->name : me.name, acptr->hopcount, (acptr->info[0] ? acptr->info : "(Unknown Location)")); } diff --git a/src/modules/list.c b/src/modules/list.c index ab4bdea..f7ece82 100644 --- a/src/modules/list.c +++ b/src/modules/list.c @@ -33,7 +33,7 @@ ModuleHeader MOD_HEADER "5.0", "command /list", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; typedef struct ChannelListOptions ChannelListOptions; @@ -54,6 +54,7 @@ struct ChannelListOptions { /* Global variables */ ModDataInfo *list_md = NULL; +char modebuf[BUFSIZE], parabuf[BUFSIZE]; /* Macros */ #define CHANNELLISTOPTIONS(x) ((ChannelListOptions *)moddata_local_client(x, list_md).ptr) @@ -125,6 +126,7 @@ CMD_FUNC(cmd_list) NameList *nolist = NULL; int ntargets = 0; int maxtargets = max_targets_for_command("LIST"); + char request[BUFSIZE]; static char *usage[] = { " Usage: /LIST ", @@ -187,8 +189,8 @@ CMD_FUNC(cmd_list) usermin = 0; /* Minimum of 0 */ usermax = -1; /* No maximum */ - for (name = strtoken(&p, parv[1], ","); name && !error; - name = strtoken(&p, NULL, ",")) + strlcpy(request, parv[1], sizeof(request)); + for (name = strtoken(&p, request, ","); name && !error; name = strtoken(&p, NULL, ",")) { if (MyUser(client) && (++ntargets > maxtargets)) { @@ -269,21 +271,17 @@ CMD_FUNC(cmd_list) } else /* Just a normal channel */ { - channel = find_channel(name, NULL); + channel = find_channel(name); if (channel && (ShowChannel(client, channel) || ValidatePermissionsForPath("channel:see:list:secret",client,NULL,channel,NULL))) { -#ifdef LIST_SHOW_MODES modebuf[0] = '['; - channel_modes(client, modebuf+1, parabuf, sizeof(modebuf)-1, sizeof(parabuf), channel); + 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); -#endif sendnumeric(client, RPL_LIST, name, channel->users, -#ifdef LIST_SHOW_MODES modebuf, -#endif (channel->topic ? channel->topic : "")); } @@ -343,13 +341,11 @@ int send_list(Client *client) ConfigItem_offchans *x; for (x = conf_offchans; x; x = x->next) { - if (find_channel(x->chname, NULL)) + if (find_channel(x->name)) continue; /* exists, >0 users.. will be sent later */ - sendnumeric(client, RPL_LIST, x->chname, + sendnumeric(client, RPL_LIST, x->name, 0, -#ifdef LIST_SHOW_MODES "", -#endif x->topic ? x->topic : ""); } } @@ -366,11 +362,11 @@ int send_list(Client *client) continue; /* set::hide-list { deny-channel } */ - if (!IsOper(client) && iConf.hide_list && find_channel_allowed(client, channel->chname)) + if (!IsOper(client) && iConf.hide_list && find_channel_allowed(client, channel->name)) continue; /* Similarly, hide unjoinable channels for non-ircops since it would be confusing */ - if (!IsOper(client) && !valid_channelname(channel->chname)) + if (!IsOper(client) && !valid_channelname(channel->name)) continue; /* Much more readable like this -- codemastr */ @@ -394,39 +390,33 @@ int send_list(Client *client) continue; /* Must not be on nolist (if it exists) */ - if (lopt->nolist && find_name_list_match(lopt->nolist, channel->chname)) + if (lopt->nolist && find_name_list_match(lopt->nolist, channel->name)) continue; /* Must be on yeslist (if it exists) */ - if (lopt->yeslist && !find_name_list_match(lopt->yeslist, channel->chname)) + if (lopt->yeslist && !find_name_list_match(lopt->yeslist, channel->name)) continue; } -#ifdef LIST_SHOW_MODES modebuf[0] = '['; - channel_modes(client, modebuf+1, parabuf, sizeof(modebuf)-1, sizeof(parabuf), channel); + 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); -#endif if (!ValidatePermissionsForPath("channel:see:list:secret",client,NULL,channel,NULL)) sendnumeric(client, RPL_LIST, ShowChannel(client, - channel) ? channel->chname : + channel) ? channel->name : "*", channel->users, -#ifdef LIST_SHOW_MODES ShowChannel(client, channel) ? modebuf : "", -#endif ShowChannel(client, channel) ? (channel->topic ? channel->topic : "") : ""); else - sendnumeric(client, RPL_LIST, channel->chname, + sendnumeric(client, RPL_LIST, channel->name, channel->users, -#ifdef LIST_SHOW_MODES modebuf, -#endif (channel->topic ? channel->topic : "")); numsend--; } diff --git a/src/modules/locops.c b/src/modules/locops.c index 5437385..1943d87 100644 --- a/src/modules/locops.c +++ b/src/modules/locops.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /locops", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -58,9 +58,7 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_locops) { - char *message; - - message = parc > 1 ? parv[1] : NULL; + const char *message = parc > 1 ? parv[1] : NULL; if (BadPtr(message)) { diff --git a/src/modules/lusers.c b/src/modules/lusers.c index 4059eb4..3f07805 100644 --- a/src/modules/lusers.c +++ b/src/modules/lusers.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /lusers", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -59,7 +59,7 @@ CMD_FUNC(cmd_lusers) { char flatmap; - if (hunt_server(client, recv_mtags, ":%s LUSERS :%s", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "LUSERS", 1, parc, parv) != HUNTED_ISME) return; flatmap = (FLAT_MAP && !ValidatePermissionsForPath("server:info:lusers",client,NULL,NULL,NULL)) ? 1 : 0; @@ -83,12 +83,14 @@ char flatmap; sendnumeric(client, RPL_LUSERME, irccounts.me_clients, flatmap ? 0 : irccounts.me_servers); sendnumeric(client, RPL_LOCALUSERS, irccounts.me_clients, irccounts.me_max, irccounts.me_clients, irccounts.me_max); sendnumeric(client, RPL_GLOBALUSERS, irccounts.clients, irccounts.global_max, irccounts.clients, irccounts.global_max); - if ((irccounts.me_clients + irccounts.me_servers) > max_connection_count) + if (irccounts.me_clients > max_connection_count) { - max_connection_count = - irccounts.me_clients + irccounts.me_servers; + max_connection_count = irccounts.me_clients; if (max_connection_count % 10 == 0) /* only send on even tens */ - sendto_ops("New record on this server: %d connections (%d clients)", - max_connection_count, irccounts.me_clients); + { + unreal_log(ULOG_INFO, "client", "NEW_USER_RECORD", NULL, + "New record on this server: $num_users connections", + log_data_integer("num_users", max_connection_count)); + } } } diff --git a/src/modules/map.c b/src/modules/map.c index a0c8aa4..1f57e9e 100644 --- a/src/modules/map.c +++ b/src/modules/map.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /map", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -71,7 +71,7 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng else { sendnumeric(client, RPL_MAP, prompt, - length, server->name, server->serv->users, IsOper(client) ? server->id : ""); + length, server->name, server->server->users, IsOper(client) ? server->id : ""); cnt = 0; } @@ -88,7 +88,7 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng list_for_each_entry(acptr, &global_server_list, client_node) { - if (acptr->srvptr != server || + if (acptr->uplink != server || (IsULine(acptr) && HIDE_ULINES && !ValidatePermissionsForPath("server:info:map:ulines",client,NULL,NULL,NULL))) continue; SetMap(acptr); @@ -99,7 +99,7 @@ static void dump_map(Client *client, Client *server, char *mask, int prompt_leng { if (IsULine(acptr) && HIDE_ULINES && !ValidatePermissionsForPath("server:info:map:ulines",client,NULL,NULL,NULL)) continue; - if (acptr->srvptr != server) + if (acptr->uplink != server) continue; if (!IsMap(acptr)) continue; @@ -120,7 +120,7 @@ void dump_flat_map(Client *client, Client *server, int length) hide_ulines = (HIDE_ULINES && !ValidatePermissionsForPath("server:info:map:ulines",client,NULL,NULL,NULL)) ? 1 : 0; - sendnumeric(client, RPL_MAP, "", length, server->name, server->serv->users, ""); + sendnumeric(client, RPL_MAP, "", length, server->name, server->server->users, ""); list_for_each_entry(acptr, &global_server_list, client_node) { @@ -136,7 +136,7 @@ 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->serv->users, ""); + sendnumeric(client, RPL_MAP, buf, length-2, acptr->name, acptr->server->users, ""); } } diff --git a/src/modules/md.c b/src/modules/md.c index 485e8a4..ba735b9 100644 --- a/src/modules/md.c +++ b/src/modules/md.c @@ -14,7 +14,7 @@ ModuleHeader MOD_HEADER "5.0", "command /MD (S2S only)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; CMD_FUNC(cmd_md); @@ -23,11 +23,13 @@ void _broadcast_md_channel(ModDataInfo *mdi, Channel *channel, ModData *md); void _broadcast_md_member(ModDataInfo *mdi, Channel *channel, Member *m, ModData *md); void _broadcast_md_membership(ModDataInfo *mdi, Client *client, Membership *m, ModData *md); void _broadcast_md_globalvar(ModDataInfo *mdi, ModData *md); -void _broadcast_md_client_cmd(Client *except, Client *sender, Client *client, char *varname, char *value); -void _broadcast_md_channel_cmd(Client *except, Client *sender, Channel *channel, char *varname, char *value); -void _broadcast_md_member_cmd(Client *except, Client *sender, Channel *channel, Client *client, char *varname, char *value); -void _broadcast_md_membership_cmd(Client *except, Client *sender, Client *client, Channel *channel, char *varname, char *value); -void _broadcast_md_globalvar_cmd(Client *except, Client *sender, char *varname, char *value); +void _broadcast_md_client_cmd(Client *except, Client *sender, Client *client, const char *varname, const char *value); +void _broadcast_md_channel_cmd(Client *except, Client *sender, Channel *channel, const char *varname, const char *value); +void _broadcast_md_member_cmd(Client *except, Client *sender, Channel *channel, Client *client, const char *varname, const char *value); +void _broadcast_md_membership_cmd(Client *except, Client *sender, Client *client, Channel *channel, const char *varname, const char *value); +void _broadcast_md_globalvar_cmd(Client *except, Client *sender, const char *varname, const char *value); +void _moddata_add_s2s_mtags(Client *client, MessageTag **mtags); +void _moddata_extract_s2s_mtags(Client *client, MessageTag *mtags); void _send_moddata_client(Client *srv, Client *client); void _send_moddata_channel(Client *srv, Channel *channel); void _send_moddata_members(Client *srv); @@ -48,6 +50,8 @@ MOD_TEST() EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_MEMBER_CMD, _broadcast_md_member_cmd); EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_MEMBERSHIP_CMD, _broadcast_md_membership_cmd); EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_GLOBALVAR_CMD, _broadcast_md_globalvar_cmd); + EfunctionAddVoid(modinfo->handle, EFUNC_MODDATA_ADD_S2S_MTAGS, _moddata_add_s2s_mtags); + EfunctionAddVoid(modinfo->handle, EFUNC_MODDATA_EXTRACT_S2S_MTAGS, _moddata_extract_s2s_mtags); EfunctionAddVoid(modinfo->handle, EFUNC_SEND_MODDATA_CLIENT, _send_moddata_client); EfunctionAddVoid(modinfo->handle, EFUNC_SEND_MODDATA_CHANNEL, _send_moddata_channel); EfunctionAddVoid(modinfo->handle, EFUNC_SEND_MODDATA_MEMBERS, _send_moddata_members); @@ -72,6 +76,25 @@ MOD_UNLOAD() return MOD_SUCCESS; } +/** Check if client may write to this MD object */ +int md_access_check(Client *client, ModDataInfo *md, Client *target) +{ + if ((client == target) && md->self_write) + return 1; + + if (MyConnect(target) && !md->remote_write) + { + unreal_log(ULOG_WARNING, "md", "REMOTE_MD_WRITE_DENIED", client, + "Remote server $client tried to write moddata $moddata_name " + "of a client from ours ($target.name) -- attempt BLOCKED", + log_data_string("moddata_name", md->name), + log_data_client("target", target)); + return 0; + } + + return 1; +} + /** Set ModData command. * Syntax: MD * Example: MD client Syzop sslfp 123456789 @@ -88,7 +111,7 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_md) { - char *type, *objname, *varname, *value; + const char *type, *objname, *varname, *value; ModDataInfo *md; if (!IsServer(client) || (parc < 4) || BadPtr(parv[3])) @@ -105,6 +128,10 @@ CMD_FUNC(cmd_md) md = findmoddata_byname(varname, MODDATATYPE_CLIENT); if (!md || !md->unserialize || !target) return; + + if (!md_access_check(client, md, target)) + return; + if (value) md->unserialize(value, &moddata_client(target, md)); else @@ -118,7 +145,7 @@ CMD_FUNC(cmd_md) } else if (!strcmp(type, "channel")) { - Channel *channel = find_channel(objname, NULL); + Channel *channel = find_channel(objname); md = findmoddata_byname(varname, MODDATATYPE_CHANNEL); if (!md || !md->unserialize || !channel) return; @@ -146,11 +173,11 @@ CMD_FUNC(cmd_md) return; *p++ = '\0'; - channel = find_channel(objname, NULL); + channel = find_channel(objname); if (!channel) return; - target = find_person(p, NULL); + target = find_user(p, NULL); if (!target) return; @@ -162,6 +189,9 @@ CMD_FUNC(cmd_md) if (!md || !md->unserialize) return; + if (!md_access_check(client, md, target)) + return; + if (value) md->unserialize(value, &moddata_member(m, md)); else @@ -186,11 +216,11 @@ CMD_FUNC(cmd_md) return; *p++ = '\0'; - target = find_person(objname, NULL); + target = find_user(objname, NULL); if (!target) return; - channel = find_channel(p, NULL); + channel = find_channel(p); if (!channel) return; @@ -202,6 +232,9 @@ CMD_FUNC(cmd_md) if (!md || !md->unserialize) return; + if (!md_access_check(client, md, target)) + return; + if (value) md->unserialize(value, &moddata_membership(m, md)); else @@ -232,7 +265,7 @@ CMD_FUNC(cmd_md) } } -void _broadcast_md_client_cmd(Client *except, Client *sender, Client *client, char *varname, char *value) +void _broadcast_md_client_cmd(Client *except, Client *sender, Client *client, const char *varname, const char *value) { if (value) { @@ -246,45 +279,45 @@ void _broadcast_md_client_cmd(Client *except, Client *sender, Client *client, ch } } -void _broadcast_md_channel_cmd(Client *except, Client *sender, Channel *channel, char *varname, char *value) +void _broadcast_md_channel_cmd(Client *except, Client *sender, Channel *channel, const char *varname, const char *value) { if (value) sendto_server(except, 0, 0, NULL, ":%s MD %s %s %s :%s", - sender->id, "channel", channel->chname, varname, value); + sender->id, "channel", channel->name, varname, value); else sendto_server(except, 0, 0, NULL, ":%s MD %s %s %s", - sender->id, "channel", channel->chname, varname); + sender->id, "channel", channel->name, varname); } -void _broadcast_md_member_cmd(Client *except, Client *sender, Channel *channel, Client *client, char *varname, char *value) +void _broadcast_md_member_cmd(Client *except, Client *sender, Channel *channel, Client *client, const char *varname, const char *value) { if (value) { sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s :%s", - sender->id, "member", channel->chname, client->id, varname, value); + sender->id, "member", channel->name, client->id, varname, value); } else { sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s", - sender->id, "member", channel->chname, client->id, varname); + sender->id, "member", channel->name, client->id, varname); } } -void _broadcast_md_membership_cmd(Client *except, Client *sender, Client *client, Channel *channel, char *varname, char *value) +void _broadcast_md_membership_cmd(Client *except, Client *sender, Client *client, Channel *channel, const char *varname, const char *value) { if (value) { sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s :%s", - sender->id, "membership", client->id, channel->chname, varname, value); + sender->id, "membership", client->id, channel->name, varname, value); } else { sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s", - sender->id, "membership", client->id, channel->chname, varname); + sender->id, "membership", client->id, channel->name, varname); } } -void _broadcast_md_globalvar_cmd(Client *except, Client *sender, char *varname, char *value) +void _broadcast_md_globalvar_cmd(Client *except, Client *sender, const char *varname, const char *value) { if (value) { @@ -306,35 +339,35 @@ void _broadcast_md_globalvar_cmd(Client *except, Client *sender, char *varname, void _broadcast_md_client(ModDataInfo *mdi, Client *client, ModData *md) { - char *value = md ? mdi->serialize(md) : NULL; + const char *value = md ? mdi->serialize(md) : NULL; broadcast_md_client_cmd(NULL, &me, client, mdi->name, value); } void _broadcast_md_channel(ModDataInfo *mdi, Channel *channel, ModData *md) { - char *value = md ? mdi->serialize(md) : NULL; + const char *value = md ? mdi->serialize(md) : NULL; broadcast_md_channel_cmd(NULL, &me, channel, mdi->name, value); } void _broadcast_md_member(ModDataInfo *mdi, Channel *channel, Member *m, ModData *md) { - char *value = md ? mdi->serialize(md) : NULL; + const char *value = md ? mdi->serialize(md) : NULL; broadcast_md_member_cmd(NULL, &me, channel, m->client, mdi->name, value); } void _broadcast_md_membership(ModDataInfo *mdi, Client *client, Membership *m, ModData *md) { - char *value = md ? mdi->serialize(md) : NULL; + const char *value = md ? mdi->serialize(md) : NULL; broadcast_md_membership_cmd(NULL, &me, client, m->channel, mdi->name, value); } void _broadcast_md_globalvar(ModDataInfo *mdi, ModData *md) { - char *value = md ? mdi->serialize(md) : NULL; + const char *value = md ? mdi->serialize(md) : NULL; broadcast_md_globalvar_cmd(NULL, &me, mdi->name, value); } @@ -348,7 +381,7 @@ void _send_moddata_client(Client *srv, Client *client) { if ((mdi->type == MODDATATYPE_CLIENT) && mdi->sync && mdi->serialize) { - char *value = mdi->serialize(&moddata_client(client, mdi)); + const char *value = mdi->serialize(&moddata_client(client, mdi)); if (value) sendto_one(srv, NULL, ":%s MD %s %s %s :%s", me.id, "client", client->id, mdi->name, value); @@ -356,6 +389,62 @@ void _send_moddata_client(Client *srv, Client *client) } } +/** Enhance the command with moddata message tags, so we can send + * traffic like @s2s-md/certfp=xxxxx UID .... + */ +void _moddata_add_s2s_mtags(Client *client, MessageTag **mtags_list) +{ + ModDataInfo *mdi; + char name[128]; + + for (mdi = MDInfo; mdi; mdi = mdi->next) + { + if ((mdi->type == MODDATATYPE_CLIENT) && (mdi->sync == MODDATA_SYNC_EARLY) && mdi->serialize) + { + MessageTag *m; + const char *value = mdi->serialize(&moddata_client(client, mdi)); + if (!value) + continue; + snprintf(name, sizeof(name), "s2s-md/%s", mdi->name); + + m = safe_alloc(sizeof(MessageTag)); + safe_strdup(m->name, name); + safe_strdup(m->value, value); + AddListItem(m, *mtags_list); + } + } +} + +/** Extract the s2s-md/ tags again from an incoming command, + * eg @s2s-md/certfp=xxxxx UID .... + */ +void _moddata_extract_s2s_mtags(Client *client, MessageTag *mtags) +{ + MessageTag *m; + ModDataInfo *md; + + for (m = mtags; m; m = m->next) + { + if (!strncmp(m->name, "s2s-md/", 7)) + { + char *varname = m->name + 7; + char *value = m->value; + + if (!value) + continue; + + md = findmoddata_byname(varname, MODDATATYPE_CLIENT); + if (!md || !md->unserialize) + continue; + + if (!md_access_check(client, md, client)) + return; + + md->unserialize(value, &moddata_client(client, md)); + } + } +} + /** Send all moddata attached to channel 'channel' to remote server 'srv' (if the module wants this), called by SJOIN */ void _send_moddata_channel(Client *srv, Channel *channel) { @@ -365,10 +454,10 @@ void _send_moddata_channel(Client *srv, Channel *channel) { if ((mdi->type == MODDATATYPE_CHANNEL) && mdi->sync && mdi->serialize) { - char *value = mdi->serialize(&moddata_channel(channel, mdi)); + const char *value = mdi->serialize(&moddata_channel(channel, mdi)); if (value) sendto_one(srv, NULL, ":%s MD %s %s %s :%s", - me.id, "channel", channel->chname, mdi->name, value); + me.id, "channel", channel->name, mdi->name, value); } } } @@ -392,10 +481,10 @@ void _send_moddata_members(Client *srv) { if ((mdi->type == MODDATATYPE_MEMBER) && mdi->sync && mdi->serialize) { - char *value = mdi->serialize(&moddata_member(m, mdi)); + const char *value = mdi->serialize(&moddata_member(m, mdi)); if (value) sendto_one(srv, NULL, ":%s MD %s %s:%s %s :%s", - me.id, "member", channel->chname, client->id, mdi->name, value); + me.id, "member", channel->name, client->id, mdi->name, value); } } } @@ -416,10 +505,10 @@ void _send_moddata_members(Client *srv) { if ((mdi->type == MODDATATYPE_MEMBERSHIP) && mdi->sync && mdi->serialize) { - char *value = mdi->serialize(&moddata_membership(m, mdi)); + const char *value = mdi->serialize(&moddata_membership(m, mdi)); if (value) sendto_one(srv, NULL, ":%s MD %s %s:%s %s :%s", - me.id, "membership", client->id, m->channel->chname, mdi->name, value); + me.id, "membership", client->id, m->channel->name, mdi->name, value); } } } diff --git a/src/modules/mdex.c b/src/modules/mdex.c deleted file mode 100644 index 26388fb..0000000 --- a/src/modules/mdex.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Example module for ModData usage - * NEVER LOAD THIS ON A LIVE SERVER!! - * - * (C) Copyright 2014 Bram Matthys and the UnrealIRCd team - * License: GPLv2 - */ - -#include "unrealircd.h" - -CMD_FUNC(cmd_mdex); - -ModuleHeader MOD_HEADER - = { - "mdex", - "5.0", - "Command /MDEX", - "UnrealIRCd Team", - "unrealircd-5", - }; - -ModDataInfo *mdex_cli = NULL, *mdex_chan = NULL, *mdex_member = NULL, *mdex_membership = NULL; -void mdex_free(ModData *m); -char *mdex_serialize(ModData *m); -void mdex_unserialize(char *str, ModData *m); - -MOD_INIT() -{ -ModDataInfo mreq; - - memset(&mreq, 0, sizeof(mreq)); - mreq.name = "mdex"; - mreq.free = mdex_free; - mreq.serialize = mdex_serialize; - mreq.unserialize = mdex_unserialize; - mreq.sync = 1; - mreq.type = MODDATATYPE_CLIENT; - mdex_cli = ModDataAdd(modinfo->handle, mreq); - if (!mdex_cli) - abort(); - mreq.type = MODDATATYPE_CHANNEL; - mdex_chan = ModDataAdd(modinfo->handle, mreq); - if (!mdex_cli) - abort(); - mreq.type = MODDATATYPE_MEMBER; - mdex_member = ModDataAdd(modinfo->handle, mreq); - if (!mdex_cli) - abort(); - mreq.type = MODDATATYPE_MEMBERSHIP; - mdex_membership = ModDataAdd(modinfo->handle, mreq); - if (!mdex_cli) - abort(); - - CommandAdd(modinfo->handle, "MDEX", cmd_mdex, MAXPARA, CMD_USER); - - return MOD_SUCCESS; -} - -MOD_LOAD() -{ - return MOD_SUCCESS; -} - - -MOD_UNLOAD() -{ - return MOD_SUCCESS; -} - -CMD_FUNC(cmd_mdex) -{ - char *action, *type, *objname, *varname, *value; - ModDataInfo *md; - - if (!IsOper(client) || (parc < 5) || BadPtr(parv[4])) - return 0; - - action = parv[1]; /* get / set */ - type = parv[2]; - objname = parv[3]; -#ifdef DEBUGMODE - varname = parv[4]; -#else - varname = "mdex"; -#endif - value = parv[5]; /* may be NULL */ - - if (!strcmp(action, "set")) - { - if (!strcmp(type, "client")) - { - Client *target = find_client(objname, NULL); - md = findmoddata_byname(varname, MODDATATYPE_CLIENT); - if (!md || !md->unserialize || !md->free || !target) - return 0; - if (value) - md->unserialize(value, &moddata_client(target, md)); - else - { - md->free(&moddata_client(target, md)); - memset(&moddata_client(target, md), 0, sizeof(ModData)); - } - broadcast_md_client(md, target, &moddata_client(target, md)); - } else - if (!strcmp(type, "channel")) - { - Channel *channel = find_channel(objname, NULL); - md = findmoddata_byname(varname, MODDATATYPE_CHANNEL); - if (!md || !md->unserialize || !md->free || !channel) - return 0; - if (value) - md->unserialize(value, &moddata_channel(channel, md)); - else - { - md->free(&moddata_channel(channel, md)); - memset(&moddata_channel(channel, md), 0, sizeof(ModData)); - } - broadcast_md_channel(md, channel, &moddata_channel(channel, md)); - } else - if (!strcmp(type, "member")) - { - Client *target; - Channel *channel; - Member *m; - char *p; - - /* for member the object name is like '#channel/Syzop' */ - p = strchr(objname, ':'); - if (!p) - return 0; - *p++ = '\0'; - - channel = find_channel(objname, NULL); - if (!channel) - return 0; - - target = find_person(p, NULL); - if (!target) - return 0; - - m = find_member_link(channel->members, target); - if (!m) - return 0; - - md = findmoddata_byname(varname, MODDATATYPE_MEMBER); - if (!md || !md->unserialize || !md->free) - return 0; - - if (value) - md->unserialize(value, &moddata_member(m, md)); - else - { - md->free(&moddata_member(m, md)); - memset(&moddata_member(m, md), 0, sizeof(ModData)); - } - broadcast_md_member(md, channel, m, &moddata_member(m, md)); - } else - if (!strcmp(type, "membership")) - { - Client *target; - Channel *channel; - Membership *m; - char *p; - - /* for membership the object name is like 'Syzop/#channel' */ - p = strchr(objname, ':'); - if (!p) - return 0; - *p++ = '\0'; - - target = find_person(objname, NULL); - if (!target) - return 0; - - channel = find_channel(p, NULL); - if (!channel) - return 0; - - m = find_membership_link(target->user->channel, channel); - if (!m) - return 0; - - md = findmoddata_byname(varname, MODDATATYPE_MEMBERSHIP); - if (!md || !md->unserialize || !md->free) - return 0; - - if (value) - md->unserialize(value, &moddata_membership(m, md)); - else - { - md->free(&moddata_membership(m, md)); - memset(&moddata_membership(m, md), 0, sizeof(ModData)); - } - broadcast_md_membership(md, target, m, &moddata_membership(m, md)); - } - } else - if (!strcmp(action, "get")) - { - if (!strcmp(type, "client")) - { - Client *target = find_client(objname, NULL); - char *str; - - md = findmoddata_byname(varname, MODDATATYPE_CLIENT); - if (!md || !md->serialize || !target) - return 0; - str = md->serialize(&moddata_client(target, md)); - if (str) - sendnotice(client, "Value: %s", str ? str : ""); - else - sendnotice(client, "No value set"); - } else - if (!strcmp(type, "channel")) - { - Channel *channel = find_channel(objname, NULL); - char *str; - - md = findmoddata_byname(varname, MODDATATYPE_CHANNEL); - if (!md || !md->serialize || !channel) - return 0; - str = md->serialize(&moddata_channel(channel, md)); - if (str) - sendnotice(client, "Value: %s", str ? str : ""); - else - sendnotice(client, "No value set"); - } else - if (!strcmp(type, "member")) - { - Client *target; - Channel *channel; - Member *m; - char *p, *str; - - /* for member the object name is like '#channel/Syzop' */ - p = strchr(objname, ':'); - if (!p) - return 0; - *p++ = '\0'; - - channel = find_channel(objname, NULL); - if (!channel) - return 0; - - target = find_person(p, NULL); - if (!target) - return 0; - - m = find_member_link(channel->members, target); - if (!m) - return 0; - - md = findmoddata_byname(varname, MODDATATYPE_MEMBER); - if (!md || !md->serialize) - return 0; - - str = md->serialize(&moddata_member(m, md)); - if (str) - sendnotice(client, "Value: %s", str ? str : ""); - else - sendnotice(client, "No value set"); - } else - if (!strcmp(type, "membership")) - { - Client *target; - Channel *channel; - Membership *m; - char *p, *str; - - /* for membership the object name is like 'Syzop/#channel' */ - p = strchr(objname, ':'); - if (!p) - return 0; - *p++ = '\0'; - - target = find_person(objname, NULL); - if (!target) - return 0; - - channel = find_channel(p, NULL); - if (!channel) - return 0; - - m = find_membership_link(target->user->channel, channel); - if (!m) - return 0; - - md = findmoddata_byname(varname, MODDATATYPE_MEMBERSHIP); - if (!md || !md->serialize) - return 0; - - str = md->serialize(&moddata_membership(m, md)); - if (str) - sendnotice(client, "Value: %s", str ? str : ""); - else - sendnotice(client, "No value set"); - } - } - - return 0; -} - -void mdex_free(ModData *m) -{ - safe_free(m->str); -} - -char *mdex_serialize(ModData *m) -{ - if (!m->str) - return NULL; - return m->str; -} - -void mdex_unserialize(char *str, ModData *m) -{ - safe_strdup(m->str, str); -} diff --git a/src/modules/message-ids.c b/src/modules/message-ids.c index 61959f3..ec44b8c 100644 --- a/src/modules/message-ids.c +++ b/src/modules/message-ids.c @@ -28,14 +28,14 @@ ModuleHeader MOD_HEADER "5.0", "msgid CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ long CAP_ACCOUNT_TAG = 0L; -int msgid_mtag_is_ok(Client *client, char *name, char *value); -void mtag_add_or_inherit_msgid(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int msgid_mtag_is_ok(Client *client, const char *name, const char *value); +void mtag_add_or_inherit_msgid(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -69,7 +69,7 @@ MOD_UNLOAD() * syntax. * We simply allow msgid ONLY from servers and with any syntax. */ -int msgid_mtag_is_ok(Client *client, char *name, char *value) +int msgid_mtag_is_ok(Client *client, const char *name, const char *value) { if (IsServer(client) && !BadPtr(value)) return 1; @@ -103,7 +103,7 @@ MessageTag *mtag_generate_msgid(void) } -void mtag_add_or_inherit_msgid(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_or_inherit_msgid(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m = find_mtag(recv_mtags, "msgid"); if (m) @@ -146,9 +146,7 @@ void mtag_add_or_inherit_msgid(Client *sender, MessageTag *recv_mtags, MessageTa char newbuf[256]; memset(&binaryhash, 0, sizeof(binaryhash)); memset(&b64hash, 0, sizeof(b64hash)); - SHA256_Init(&hash); - SHA256_Update(&hash, signature, strlen(signature)); - SHA256_Final(binaryhash, &hash); + sha256hash_binary(binaryhash, signature, strlen(signature)); b64_encode(binaryhash, sizeof(binaryhash)/2, b64hash, sizeof(b64hash)); b64hash[22] = '\0'; /* cut off at '=' */ snprintf(newbuf, sizeof(newbuf), "%s-%s", prefix, b64hash); diff --git a/src/modules/message-tags.c b/src/modules/message-tags.c index 1eab0fd..ae20a8e 100644 --- a/src/modules/message-tags.c +++ b/src/modules/message-tags.c @@ -28,18 +28,18 @@ ModuleHeader MOD_HEADER "5.0", "Message tags CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; long CAP_MESSAGE_TAGS = 0L; -char *_mtags_to_string(MessageTag *m, Client *client); +const char *_mtags_to_string(MessageTag *m, Client *client); void _parse_message_tags(Client *client, char **str, MessageTag **mtag_list); MOD_TEST() { MARK_AS_OFFICIAL_MODULE(modinfo); - EfunctionAddPChar(modinfo->handle, EFUNC_MTAGS_TO_STRING, _mtags_to_string); + EfunctionAddConstString(modinfo->handle, EFUNC_MTAGS_TO_STRING, _mtags_to_string); EfunctionAddVoid(modinfo->handle, EFUNC_PARSE_MESSAGE_TAGS, _parse_message_tags); return 0; @@ -148,7 +148,13 @@ int message_tag_ok(Client *client, char *name, char *value) m = MessageTagHandlerFind(name); if (!m) + { + /* Permit unknown message tags from trusted servers */ + if (IsServer(client) || !MyConnect(client)) + return 1; + return 0; + } if (m->is_ok(client, name, value)) return 1; @@ -198,7 +204,7 @@ void _parse_message_tags(Client *client, char **str, MessageTag **mtag_list) m = safe_alloc(sizeof(MessageTag)); safe_strdup(m->name, name); /* Both NULL and empty become NULL: */ - if (BadPtr(value)) + if (!*value) m->value = NULL; else /* a real value... */ safe_strdup(m->value, value); @@ -227,7 +233,7 @@ int client_accepts_tag(const char *token, Client *client) return 0; /* Maybe there is an outgoing filter in effect (usually not) */ - if (m->can_send && !m->can_send(client)) + if (m->should_send_to_client && !m->should_send_to_client(client)) return 0; /* If the client has indicated 'message-tags' support then we can @@ -257,10 +263,10 @@ int client_accepts_tag(const char *token, Client *client) * Taking into account the restrictions that 'client' may have. * @returns A string (static buffer) or NULL if no tags at all (!) */ -char *_mtags_to_string(MessageTag *m, Client *client) +const char *_mtags_to_string(MessageTag *m, Client *client) { static char buf[4096], name[8192], value[8192]; - char tbuf[512]; + static char tbuf[4094]; if (!m) return NULL; diff --git a/src/modules/message.c b/src/modules/message.c index db8684b..279df2c 100644 --- a/src/modules/message.c +++ b/src/modules/message.c @@ -21,15 +21,15 @@ #include "unrealircd.h" /* Forward declarations */ -char *_StripColors(unsigned char *text); -char *_StripControlCodes(unsigned char *text); -int ban_version(Client *client, char *text); +const char *_StripColors(const char *text); +const char *_StripControlCodes(const char *text); +int ban_version(Client *client, const char *text); CMD_FUNC(cmd_private); CMD_FUNC(cmd_notice); CMD_FUNC(cmd_tagmsg); -void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], SendType sendtype); -int _can_send_to_channel(Client *client, Channel *channel, char **msgtext, char **errmsg, SendType sendtype); -int can_send_to_user(Client *client, Client *target, char **msgtext, char **errmsg, SendType sendtype); +void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype); +int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype); +int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype); /* Variables */ long CAP_MESSAGE_TAGS = 0; /**< Looked up at MOD_LOAD, may stay 0 if message-tags support is absent */ @@ -40,14 +40,14 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "private message and notice", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_TEST() { MARK_AS_OFFICIAL_MODULE(modinfo); - EfunctionAddPChar(modinfo->handle, EFUNC_STRIPCOLORS, _StripColors); - EfunctionAddPChar(modinfo->handle, EFUNC_STRIPCONTROLCODES, _StripControlCodes); + EfunctionAddConstString(modinfo->handle, EFUNC_STRIPCOLORS, _StripColors); + EfunctionAddConstString(modinfo->handle, EFUNC_STRIPCONTROLCODES, _StripControlCodes); EfunctionAdd(modinfo->handle, EFUNC_CAN_SEND_TO_CHANNEL, _can_send_to_channel); return MOD_SUCCESS; } @@ -85,7 +85,7 @@ MOD_UNLOAD() * text: Pointer to a pointer to a text [in, out] * cmd: Pointer to a pointer which contains the command to use [in, out] */ -int can_send_to_user(Client *client, Client *target, char **msgtext, char **errmsg, SendType sendtype) +int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype) { int ret; Hook *h; @@ -111,7 +111,7 @@ int can_send_to_user(Client *client, Client *target, char **msgtext, char **errm if (is_silenced(client, target)) { - RunHook3(HOOKTYPE_SILENCED, client, target, sendtype); + RunHook(HOOKTYPE_SILENCED, client, target, sendtype); /* Silently discarded, no error message */ return 0; } @@ -120,7 +120,7 @@ int can_send_to_user(Client *client, Client *target, char **msgtext, char **errm if (MyUser(client)) { int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG); - char *cmd = sendtype_to_cmd(sendtype); + const char *cmd = sendtype_to_cmd(sendtype); if (match_spamfilter(client, *msgtext, spamtype, cmd, target->name, 0, NULL)) return 0; @@ -134,7 +134,9 @@ int can_send_to_user(Client *client, Client *target, char **msgtext, char **errm { if (!*errmsg) { - ircd_log(LOG_ERROR, "Module %s did not set errmsg!!!", h->owner->header->name); + unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_USER_NO_ERRMSG", client, + "[BUG] Module $module did not set errmsg!!!", + log_data_string("module", h->owner->header->name)); abort(); } return 0; @@ -151,85 +153,12 @@ int can_send_to_user(Client *client, Client *target, char **msgtext, char **errm return 1; } -#ifdef PREFIX_AQ - #define PREFIX_REST (PREFIX_ADMIN|PREFIX_OWNER) -#else - #define PREFIX_REST (0) -#endif - -/** Convert a string of prefixes (like "+%@") to values (like PREFIX_VOICE|PREFIX_HALFOP|PREFIX_OP). - * @param str The string containing the prefixes and the channel name. - * @param end The position of the hashmark (#) - * @returns A value of PREFIX_*, potentially OR'ed if there are multiple values. - */ -int prefix_string_to_values(char *str, char *end) -{ - char *p; - int prefix = 0; - - for (p = str; p != end; p++) - { - switch (*p) - { - case '+': - prefix |= PREFIX_VOICE | PREFIX_HALFOP | PREFIX_OP | PREFIX_REST; - break; - case '%': - prefix |= PREFIX_HALFOP | PREFIX_OP | PREFIX_REST; - break; - case '@': - prefix |= PREFIX_OP | PREFIX_REST; - break; -#ifdef PREFIX_AQ - case '&': - prefix |= PREFIX_ADMIN | PREFIX_OWNER; - break; - case '~': - prefix |= PREFIX_OWNER; - break; -#else - case '&': - prefix |= PREFIX_OP | PREFIX_REST; - break; - case '~': - prefix |= PREFIX_OP | PREFIX_REST; - break; -#endif - default: - break; /* ignore it :P */ - } - } - return prefix; -} - -/** Find out the lowest prefix to use, so @&~#chan becomes @#chan. - * @param prefix One or more of PREFIX_* values (OR'ed) - * @returns A single character - * @note prefix must be >0, so must contain at least one PREFIX_xx value! - */ -char prefix_values_to_char(int prefix) -{ - if (prefix & PREFIX_VOICE) - return '+'; - if (prefix & PREFIX_HALFOP) - return '%'; - if (prefix & PREFIX_OP) - return '@'; -#ifdef PREFIX_AQ - if (prefix & PREFIX_ADMIN) - return '&'; - if (prefix & PREFIX_OWNER) - return '~'; -#endif - abort(); -} - /** Check if user is allowed to send to a prefix (eg: @#channel). * @param client The client (sender) * @param channel The target channel - * @param prefix The prefix mask (eg: PREFIX_CHANOP) + * @param mode The member mode to send to (eg: 'o') */ -int can_send_to_prefix(Client *client, Channel *channel, int prefix) +int can_send_to_member_mode(Client *client, Channel *channel, char mode) { Membership *lp; @@ -242,18 +171,20 @@ int can_send_to_prefix(Client *client, Channel *channel, int prefix) * Need at least voice (+) in order to send to +,% or @ * Need at least ops (@) in order to send to & or ~ */ - if (!lp || !(lp->flags & (CHFL_VOICE|CHFL_HALFOP|CHFL_CHANOP|CHFL_CHANOWNER|CHFL_CHANADMIN))) + if (!lp || !check_channel_access_membership(lp, "vhoaq")) { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); return 0; } +#if 0 if (!(prefix & PREFIX_OP) && ((prefix & PREFIX_OWNER) || (prefix & PREFIX_ADMIN)) && - !(lp->flags & (CHFL_CHANOP|CHFL_CHANOWNER|CHFL_CHANADMIN))) + !check_channel_access_membership(lp, "oaq")) { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); return 0; } +#endif return 1; } @@ -270,16 +201,16 @@ int has_client_mtags(MessageTag *mtags) /* General message handler to users and channels. Used by PRIVMSG, NOTICE, etc. */ -void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], SendType sendtype) +void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype) { Client *target; Channel *channel; - char *targetstr, *p, *p2, *pc, *text, *errmsg; - int prefix = 0; - char pfixchan[CHANNELLEN + 4]; + char targets[BUFSIZE]; + char *targetstr, *p, *p2, *pc; + const char *text, *errmsg; int ret; int ntargets = 0; - char *cmd = sendtype_to_cmd(sendtype); + const char *cmd = sendtype_to_cmd(sendtype); int maxtargets = max_targets_for_command(cmd); Hook *h; MessageTag *mtags; @@ -306,7 +237,8 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], if (MyConnect(client)) parv[1] = (char *)canonize(parv[1]); - for (p = NULL, targetstr = strtoken(&p, parv[1], ","); targetstr; targetstr = strtoken(&p, NULL, ",")) + strlcpy(targets, parv[1], sizeof(targets)); + for (p = NULL, targetstr = strtoken(&p, targets, ","); targetstr; targetstr = strtoken(&p, NULL, ",")) { if (MyUser(client) && (++ntargets > maxtargets)) { @@ -331,29 +263,44 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], } p2 = strchr(targetstr, '#'); - prefix = 0; /* Message to channel */ - if (p2 && (channel = find_channel(p2, NULL))) + if (p2 && (channel = find_channel(p2))) { - prefix = prefix_string_to_values(targetstr, p2); - if (prefix) + char pfixchan[CHANNELLEN + 4]; + int replaced = 0; + char member_modes_tmp[2]; + char *member_modes = NULL; + if (p2 - targetstr > 0) + { + /* There is (posssibly) a prefix involved... */ + char prefix_tmp[32]; + char prefix; + strlncpy(prefix_tmp, targetstr, sizeof(prefix_tmp), p2 - targetstr); + prefix = lowest_ranking_prefix(prefix_tmp); + if (prefix) + { + /* Rewrite the target. Eg: @&~#chan becomes @#chan */ + snprintf(pfixchan, sizeof(pfixchan), "%c%s", prefix, channel->name); + targetstr = pfixchan; + replaced = 1; + /* And set 'member_modes' */ + member_modes_tmp[0] = prefix_to_mode(prefix); + member_modes_tmp[1] = '\0'; + member_modes = member_modes_tmp; + /* Oh, and some access check */ + if (MyUser(client) && !can_send_to_member_mode(client, channel, *member_modes)) + continue; + } + } + if (!replaced) { - if (MyUser(client) && !can_send_to_prefix(client, channel, prefix)) - continue; - /* Now find out the lowest prefix and rewrite the target. - * Eg: @&~#chan becomes @#chan - */ - pfixchan[0] = prefix_values_to_char(prefix); - strlcpy(pfixchan+1, channel->chname, sizeof(pfixchan)-1); - targetstr = pfixchan; - } else { /* Replace target so the privmsg always goes to the "official" channel name */ - strlcpy(pfixchan, channel->chname, sizeof(pfixchan)); + strlcpy(pfixchan, channel->name, sizeof(pfixchan)); targetstr = pfixchan; } - if (IsVirus(client) && strcasecmp(channel->chname, SPAMFILTER_VIRUSCHAN)) + if (IsVirus(client) && strcasecmp(channel->name, SPAMFILTER_VIRUSCHAN)) { sendnotice(client, "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN); continue; @@ -372,7 +319,7 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], if (IsDead(client)) return; if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE) && errmsg) - sendnumeric(client, ERR_CANNOTSENDTOCHAN, channel->chname, errmsg, p2); + sendnumeric(client, ERR_CANNOTSENDTOCHAN, channel->name, errmsg, p2); continue; /* skip delivery to this target */ } } @@ -389,13 +336,13 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], { int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG); - if (match_spamfilter(client, text, spamtype, cmd, channel->chname, 0, NULL)) + if (match_spamfilter(client, text, spamtype, cmd, channel->name, 0, NULL)) return; } new_message(client, recv_mtags, &mtags); - RunHook5(HOOKTYPE_PRE_CHANMSG, client, channel, mtags, text, sendtype); + RunHook(HOOKTYPE_PRE_CHANMSG, client, channel, mtags, text, sendtype); if (!text) { @@ -407,7 +354,7 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], { /* PRIVMSG or NOTICE */ sendto_channel(channel, client, client->direction, - prefix, 0, sendflags, mtags, + member_modes, 0, sendflags, mtags, ":%s %s %s :%s", client->name, cmd, targetstr, text); } else { @@ -422,12 +369,12 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], continue; } sendto_channel(channel, client, client->direction, - prefix, CAP_MESSAGE_TAGS, sendflags, mtags, + member_modes, CAP_MESSAGE_TAGS, sendflags, mtags, ":%s TAGMSG %s", client->name, targetstr); } - RunHook8(HOOKTYPE_CHANMSG, client, channel, sendflags, prefix, targetstr, mtags, text, sendtype); + RunHook(HOOKTYPE_CHANMSG, client, channel, sendflags, member_modes, targetstr, mtags, text, sendtype); free_message_tags(mtags); @@ -469,7 +416,7 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], target = hash_find_nickatserver(targetstr, NULL); if (target) { - char *errmsg = NULL; + const char *errmsg = NULL; text = parv[2]; if (!can_send_to_user(client, target, &text, &errmsg, sendtype)) { @@ -520,7 +467,7 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], } } labeled_response_inhibit = 0; - RunHook5(HOOKTYPE_USERMSG, client, target, mtags, text, sendtype); + RunHook(HOOKTYPE_USERMSG, client, target, mtags, text, sendtype); free_message_tags(mtags); continue; } @@ -581,11 +528,12 @@ CMD_FUNC(cmd_tagmsg) * RGB color stripping support added -- codemastr */ -char *_StripColors(unsigned char *text) +const char *_StripColors(const char *text) { int i = 0, len = strlen(text), save_len=0; - char nc = 0, col = 0, rgb = 0, *save_text=NULL; - static unsigned char new_str[4096]; + char nc = 0, col = 0, rgb = 0; + const char *save_text=NULL; + static char new_str[4096]; while (len > 0) { @@ -648,10 +596,11 @@ char *_StripColors(unsigned char *text) } /* strip color, bold, underline, and reverse codes from a string */ -char *_StripControlCodes(unsigned char *text) +const char *_StripControlCodes(const char *text) { int i = 0, len = strlen(text), save_len=0; - char nc = 0, col = 0, rgb = 0, *save_text=NULL; + char nc = 0, col = 0, rgb = 0; + const char *save_text=NULL; static unsigned char new_str[4096]; while (len > 0) { @@ -744,19 +693,21 @@ char *_StripControlCodes(unsigned char *text) } /** Check ban version { } blocks, returns 1 if banned and 0 if not. */ -int ban_version(Client *client, char *text) +int ban_version(Client *client, const char *text) { int len; ConfigItem_ban *ban; + char ctcp_reply[BUFSIZE]; - len = strlen(text); + strlcpy(ctcp_reply, text, sizeof(ctcp_reply)); + len = strlen(ctcp_reply); if (!len) return 0; + + if (ctcp_reply[len-1] == '\1') + ctcp_reply[len-1] = '\0'; /* remove CTCP REPLY terminator (ASCII 1) */ - if (text[len-1] == '\1') - text[len-1] = '\0'; /* remove CTCP REPLY terminator (ASCII 1) */ - - if ((ban = find_ban(NULL, text, CONF_BAN_VERSION))) + if ((ban = find_ban(NULL, ctcp_reply, CONF_BAN_VERSION))) { if (IsSoftBanAction(ban->action) && IsLoggedIn(client)) return 0; /* soft ban does not apply to us, we are logged in */ @@ -780,7 +731,7 @@ int ban_version(Client *client, char *text) * @returns Returns 1 if the user is allowed to send, otherwise 0. * (note that this behavior was reversed in UnrealIRCd versions <5.x. */ -int _can_send_to_channel(Client *client, Channel *channel, char **msgtext, char **errmsg, SendType sendtype) +int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype) { Membership *lp; int member, i = 0; @@ -793,45 +744,7 @@ int _can_send_to_channel(Client *client, Channel *channel, char **msgtext, char member = IsMember(client, channel); - if (channel->mode.mode & MODE_NOPRIVMSGS && !member) - { - /* Channel does not accept external messages (+n). - * Reject, unless HOOKTYPE_CAN_BYPASS_NO_EXTERNAL_MSGS tells otherwise. - */ - for (h = Hooks[HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION]; h; h = h->next) - { - i = (*(h->func.intfunc))(client, channel, BYPASS_CHANMSG_EXTERNAL); - if (i != HOOK_CONTINUE) - break; - } - if (i != HOOK_ALLOW) - { - *errmsg = "No external channel messages"; - return 0; - } - } - lp = find_membership_link(client->user->channel, channel); - if (channel->mode.mode & MODE_MODERATED && - !op_can_override("channel:override:message:moderated",client,channel,NULL) && - (!lp /* FIXME: UGLY */ - || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER | CHFL_HALFOP | CHFL_CHANADMIN)))) - { - /* Channel is moderated (+m). - * Reject, unless HOOKTYPE_CAN_BYPASS_MODERATED tells otherwise. - */ - for (h = Hooks[HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION]; h; h = h->next) - { - i = (*(h->func.intfunc))(client, channel, BYPASS_CHANMSG_MODERATED); - if (i != HOOK_CONTINUE) - break; - } - if (i != HOOK_ALLOW) - { - *errmsg = "You need voice (+v)"; - return 0; - } - } /* Modules can plug in as well */ for (h = Hooks[HOOKTYPE_CAN_SEND_TO_CHANNEL]; h; h = h->next) @@ -841,7 +754,9 @@ int _can_send_to_channel(Client *client, Channel *channel, char **msgtext, char { if (!*errmsg) { - ircd_log(LOG_ERROR, "Module %s did not set errmsg!!!", h->owner->header->name); + unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_CHANNEL_NO_ERRMSG", client, + "[BUG] Module $module did not set errmsg!!!", + log_data_string("module", h->owner->header->name)); abort(); } break; @@ -873,10 +788,10 @@ int _can_send_to_channel(Client *client, Channel *channel, char **msgtext, char if (op_can_override("channel:override:message:ban",client,channel,NULL)) return 1; - if ((!lp - || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE | CHFL_CHANOWNER | - CHFL_HALFOP | CHFL_CHANADMIN))) && MyUser(client) - && is_banned(client, channel, BANCHK_MSG, msgtext, errmsg)) + /* If local client is banned and not +vhoaq... */ + if (MyUser(client) && + !check_channel_access_membership(lp, "vhoaq") && + is_banned(client, channel, BANCHK_MSG, msgtext, errmsg)) { /* Modules can set 'errmsg', otherwise we default to this: */ if (!*errmsg) diff --git a/src/modules/mkpasswd.c b/src/modules/mkpasswd.c index bde0127..ca750c6 100644 --- a/src/modules/mkpasswd.c +++ b/src/modules/mkpasswd.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", "command /mkpasswd", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -60,8 +60,8 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_mkpasswd) { - short type; - char *result = NULL; + short type; + const char *result = NULL; if (!MKPASSWD_FOR_EVERYONE && !IsOper(client)) { @@ -73,9 +73,9 @@ CMD_FUNC(cmd_mkpasswd) /* Non-opers /mkpasswd usage: lag them up, and send a notice to eyes snomask. * This notice is always sent, even in case of bad usage/bad auth methods/etc. */ - client->local->since += 7; - sendto_snomask(SNO_EYES, "*** /mkpasswd used by %s (%s@%s)", - client->name, client->user->username, GetHost(client)); + add_fake_lag(client, 7000); + unreal_log(ULOG_INFO, "mkpasswd", "MKPASSWD_COMMAND", client, + "mkpasswd command used by $client.details"); } if ((parc < 3) || BadPtr(parv[2])) diff --git a/src/modules/mode.c b/src/modules/mode.c index f269ce0..bb3a175 100644 --- a/src/modules/mode.c +++ b/src/modules/mode.c @@ -1,6 +1,6 @@ /* * IRC - Internet Relay Chat, src/modules/mode.c - * (C) 2005 The UnrealIRCd Team + * (C) 2005-.. The UnrealIRCd Team * * See file AUTHORS in IRC package for additional names of * the programmers. @@ -22,54 +22,53 @@ #include "unrealircd.h" -/* Forward declarations */ -CMD_FUNC(cmd_mode); -CMD_FUNC(cmd_mlock); -void _do_mode(Channel *channel, Client *client, MessageTag *recv_mtags, int parc, char *parv[], time_t sendts, int samode); -void _set_mode(Channel *channel, Client *client, int parc, char *parv[], u_int *pcount, - char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce); -CMD_FUNC(_cmd_umode); - -/* local: */ -static void bounce_mode(Channel *, Client *, int, char **); -int do_mode_char(Channel *channel, long modetype, char modechar, char *param, - u_int what, Client *client, - u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char bounce, long my_access); -int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what, - Client *client, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], - char bounce); -void make_mode_str(Channel *channel, long oldm, Cmode_t oldem, long oldl, int pcount, - char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, - size_t mode_buf_size, size_t para_buf_size, char bounce); - -static void mode_cutoff(char *s); -static void mode_cutoff2(Client *client, Channel *channel, int *parc_out, char *parv[]); - -static int samode_in_progress = 0; - -#define MSG_MODE "MODE" - ModuleHeader MOD_HEADER = { "mode", "5.0", "command /mode", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; +/* Forward declarations */ +int list_mode_request(Client *client, Channel *channel, const char *req); +CMD_FUNC(cmd_mode); +CMD_FUNC(cmd_mlock); +void _do_mode(Channel *channel, Client *client, MessageTag *recv_mtags, int parc, const char *parv[], time_t sendts, int samode); +MultiLineMode *_set_mode(Channel *channel, Client *client, int parc, const char *parv[], u_int *pcount, + char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]); +void _set_channel_mode(Channel *channel, char *modes, char *parameters); +CMD_FUNC(_cmd_umode); + +/* local: */ +int do_mode_char(Channel *channel, long modetype, char modechar, const char *param, + u_int what, Client *client, + u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]); +int do_extmode_char(Channel *channel, Cmode *handler, const char *param, u_int what, + Client *client, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]); +void do_mode_char_member_mode_new(Channel *channel, Cmode *handler, const char *param, u_int what, + Client *client, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]); +MultiLineMode *make_mode_str(Client *client, Channel *channel, Cmode_t oldem, int pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]); + +static char *mode_cutoff(const char *s); +void mode_operoverride_msg(Client *client, Channel *channel, char *modebuf, char *parabuf); + +static int samode_in_progress = 0; + MOD_TEST() { MARK_AS_OFFICIAL_MODULE(modinfo); EfunctionAddVoid(modinfo->handle, EFUNC_DO_MODE, _do_mode); - EfunctionAddVoid(modinfo->handle, EFUNC_SET_MODE, _set_mode); + EfunctionAddPVoid(modinfo->handle, EFUNC_SET_MODE, TO_PVOIDFUNC(_set_mode)); EfunctionAddVoid(modinfo->handle, EFUNC_CMD_UMODE, _cmd_umode); + EfunctionAddVoid(modinfo->handle, EFUNC_SET_CHANNEL_MODE, _set_channel_mode); return MOD_SUCCESS; } MOD_INIT() { - CommandAdd(modinfo->handle, MSG_MODE, cmd_mode, MAXPARA, CMD_USER|CMD_SERVER); + CommandAdd(modinfo->handle, "MODE", cmd_mode, MAXPARA, CMD_USER|CMD_SERVER); CommandAdd(modinfo->handle, MSG_MLOCK, cmd_mlock, MAXPARA, CMD_SERVER); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -105,7 +104,7 @@ CMD_FUNC(cmd_mode) { if (*parv[1] == '#') { - channel = find_channel(parv[1], NULL); + channel = find_channel(parv[1]); if (!channel) { cmd_umode(client, recv_mtags, parc, parv); @@ -130,102 +129,33 @@ CMD_FUNC(cmd_mode) if (parc < 3) { + char modebuf[BUFSIZE], parabuf[BUFSIZE]; *modebuf = *parabuf = '\0'; modebuf[1] = '\0'; - channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel); - sendnumeric(client, RPL_CHANNELMODEIS, channel->chname, modebuf, parabuf); - sendnumeric(client, RPL_CREATIONTIME, channel->chname, channel->creationtime); + channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel, 0); + sendnumeric(client, RPL_CHANNELMODEIS, channel->name, modebuf, parabuf); + sendnumeric(client, RPL_CREATIONTIME, channel->name, (long long)channel->creationtime); return; } - if (MyUser(client)) - { - /* Deal with information requests from local users, such as: - * MODE #chan b Show the ban list - * MODE #chan e Show the ban exemption list - * MODE #chan I Show the invite exception list - * MODE #chan q Show list of channel owners - * MODE #chan a Show list of channel admins - */ - if (strstr(parv[2], "b") && BadPtr(parv[3])) - { - if (!IsMember(client, channel) && !ValidatePermissionsForPath("channel:see:mode:remotebanlist",client,NULL,channel,NULL)) - return; - /* send ban list */ - for (ban = channel->banlist; ban; ban = ban->next) - sendnumeric(client, RPL_BANLIST, channel->chname, ban->banstr, ban->who, ban->when); - sendnumeric(client, RPL_ENDOFBANLIST, channel->chname); - return; - } - - if (strstr(parv[2], "e") && BadPtr(parv[3])) - { - if (!IsMember(client, channel) && !ValidatePermissionsForPath("channel:see:mode:remotebanlist",client,NULL,channel,NULL)) - return; - /* send exban list */ - for (ban = channel->exlist; ban; ban = ban->next) - sendnumeric(client, RPL_EXLIST, channel->chname, ban->banstr, ban->who, ban->when); - sendnumeric(client, RPL_ENDOFEXLIST, channel->chname); - return; - } - - if (strstr(parv[2], "I") && BadPtr(parv[3])) - { - if (!IsMember(client, channel) && !ValidatePermissionsForPath("channel:see:mode:remoteinvexlist",client,NULL,channel,NULL)) - return; - for (ban = channel->invexlist; ban; ban = ban->next) - sendnumeric(client, RPL_INVEXLIST, channel->chname, ban->banstr, ban->who, ban->when); - sendnumeric(client, RPL_ENDOFINVEXLIST, channel->chname); - return; - } - - if (strstr(parv[2], "q") && BadPtr(parv[3])) - { - Member *member; - - if (!IsMember(client, channel) && !ValidatePermissionsForPath("channel:see:mode:remoteownerlist",client,NULL,channel,NULL)) - return; - - for (member = channel->members; member; member = member->next) - { - if (is_chanowner(member->client, channel)) - sendnumeric(client, RPL_QLIST, channel->chname, member->client->name); - } - sendnumeric(client, RPL_ENDOFQLIST, channel->chname); - return; - } - - if (strstr(parv[2], "a") && BadPtr(parv[3])) - { - Member *member; - - if (!IsMember(client, channel) && !ValidatePermissionsForPath("channel:see:mode:remoteownerlist",client,NULL,channel,NULL)) - return; - - for (member = channel->members; member; member = member->next) - { - if (is_chanadmin(member->client, channel)) - sendnumeric(client, RPL_ALIST, channel->chname, member->client->name); - } - sendnumeric(client, RPL_ENDOFALIST, channel->chname); - return; - } - } + /* List mode request? Eg: "MODE #channel b" to list all bans */ + if (MyUser(client) && BadPtr(parv[3]) && list_mode_request(client, channel, parv[2])) + return; opermode = 0; #ifndef NO_OPEROVERRIDE - if (IsUser(client) && !IsULine(client) && !is_chan_op(client, channel) && - !is_half_op(client, channel) && ValidatePermissionsForPath("channel:override:mode",client,NULL,channel,NULL)) + if (IsUser(client) && !IsULine(client) && !check_channel_access(client, channel, "oaq") && + !check_channel_access(client, channel, "h") && ValidatePermissionsForPath("channel:override:mode",client,NULL,channel,NULL)) { sendts = 0; opermode = 1; goto aftercheck; } - if (IsUser(client) && !IsULine(client) && !is_chan_op(client, channel) && - is_half_op(client, channel) && ValidatePermissionsForPath("channel:override:mode",client,NULL,channel,NULL)) + if (IsUser(client) && !IsULine(client) && !check_channel_access(client, channel, "oaq") && + check_channel_access(client, channel, "h") && ValidatePermissionsForPath("channel:override:mode",client,NULL,channel,NULL)) { opermode = 2; goto aftercheck; @@ -233,36 +163,27 @@ CMD_FUNC(cmd_mode) #endif /* User does not have permission to use the MODE command */ - if (IsUser(client) && !IsULine(client) && !is_chan_op(client, channel) && - !is_half_op(client, channel) && + if (MyUser(client) && !IsULine(client) && !check_channel_access(client, channel, "hoaq") && !ValidatePermissionsForPath("channel:override:mode",client,NULL,channel,NULL)) { - if (MyUser(client)) - { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); - return; - } - sendto_one(client, NULL, ":%s MODE %s -oh %s %s 0", - me.name, channel->chname, client->name, client->name); - /* Tell the other server that the user is - * de-opped. Fix op desyncs. */ - bounce_mode(channel, client, parc - 2, parv + 2); + sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); + return; + } + + if (parv[2] && (*parv[2] == '&')) + { + /* We don't do any bounce-mode handling anymore since UnrealIRCd 6 */ return; } if (IsServer(client) && (sendts = atol(parv[parc - 1])) && - !IsULine(client) && channel->creationtime && - sendts > channel->creationtime) + !IsULine(client) && (sendts > channel->creationtime)) { - if (!(*parv[2] == '&')) /* & denotes a bounce */ - { - /* !!! */ - sendto_snomask(SNO_EYES, - "*** TS bounce for %s - %lld(ours) %lld(theirs)", - channel->chname, (long long)channel->creationtime, - (long long)sendts); - bounce_mode(channel, client, parc - 2, parv + 2); - } + unreal_log(ULOG_INFO, "mode", "MODE_TS_IGNORED", client, + "MODE change ignored for $channel from $client: " + "timestamp mismatch, ours=$channel.creationtime, theirs=$their_ts", + log_data_channel("channel", channel), + log_data_integer("their_ts", sendts)); return; } if (IsServer(client) && !sendts && *parv[parc - 1] != '0') @@ -275,8 +196,7 @@ aftercheck: /* This is to prevent excess + modes. -- Syzop */ if (MyUser(client) && parv[2]) { - mode_cutoff(parv[2]); - mode_cutoff2(client, channel, &parc, parv); + parv[2] = mode_cutoff(parv[2]); } /* Filter out the unprivileged FIRST. * @@ -290,939 +210,472 @@ aftercheck: /** Cut off mode string (eg: +abcdfjkdsgfgs) at MAXMODEPARAMS modes. * @param s The mode string (modes only, no parameters) * @note Should only used on local clients - * @author Syzop + * @returns The cleaned up string */ -static void mode_cutoff(char *s) +static char *mode_cutoff(const char *i) { -unsigned short modesleft = MAXMODEPARAMS * 2; /* be generous... */ + static char newmodebuf[BUFSIZE]; + char *o; + unsigned short modesleft = MAXMODEPARAMS * 2; /* be generous... */ - for (; *s && modesleft; s++) - if ((*s != '-') && (*s != '+')) + strlcpy(newmodebuf, i, sizeof(newmodebuf)); + + for (o = newmodebuf; *o && modesleft; o++) + if ((*o != '-') && (*o != '+')) modesleft--; - *s = '\0'; -} - -/** Another mode cutoff routine - this one for the server-side - * amplification/enlargement problem that happens with bans/exempts/invex - * as explained in #2837. -- Syzop - */ -static void mode_cutoff2(Client *client, Channel *channel, int *parc_out, char *parv[]) -{ - int len, i; - int parc = *parc_out; - - if (parc-2 <= 3) - return; /* Less than 3 mode parameters? Then we don't even have to check */ - - /* Calculate length of MODE if it would go through fully as-is */ - /* :nick!user@host MODE #channel +something param1 param2 etc... */ - len = strlen(client->name) + strlen(client->user->username) + strlen(GetHost(client)) + - strlen(channel->chname) + 11; - - len += strlen(parv[2]); - - if (*parv[2] != '+' && *parv[2] != '-') - len++; - - for (i = 3; parv[i]; i++) - { - len += strlen(parv[i]) + 1; /* (+1 for the space character) */ - /* +4 is another potential amplification (per-param). - * If we were smart we would only check this for b/e/I and only for - * relevant cases (not for all extended), but this routine is dumb, - * so we just +4 for any case where the full mask is missing. - * It's better than assuming +4 for all cases, though... - */ - if (!match_simple("*!*@*", parv[i])) - len += 4; - } - - /* Now check if the result is acceptable... */ - if (len < 510) - return; /* Ok, no problem there... */ - - /* Ok, we have a potential problem... - * we just dump the last parameter... check how much space we saved... - * and try again if that did not help - */ - for (i = parc-1; parv[i] && (i > 3); i--) - { - len -= strlen(parv[i]); - if (!match_simple("*!*@*", parv[i])) - len -= 4; /* must adjust accordingly.. */ - parv[i] = NULL; - (*parc_out)--; - if (len < 510) - break; - } - /* This may be reached if like the first parameter is really insane long.. - * which is no problem, as other layers (eg: ban) takes care of that. - * We're done... - */ -} - -/* bounce_mode -- written by binary - * User or server is NOT authorized to change the mode. This takes care - * of making the bounce string and bounce it. Because of the 1 for the bounce - * param (last param) of the calls to set_mode and make_mode_str, it will not - * set the mode, but create the bounce string. - */ -static void bounce_mode(Channel *channel, Client *client, int parc, char *parv[]) -{ - char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]; - int pcount; - - set_mode(channel, client, parc, parv, &pcount, pvar, 1); - - if (channel->creationtime) - sendto_one(client, NULL, ":%s MODE %s &%s %s %lld", me.id, - channel->chname, modebuf, parabuf, (long long)channel->creationtime); - else - sendto_one(client, NULL, ":%s MODE %s &%s %s", me.id, channel->chname, - modebuf, parabuf); - - /* the '&' denotes a bounce so servers won't bounce a bounce */ + *o = '\0'; + return newmodebuf; } /* do_mode -- written by binary * User or server is authorized to do the mode. This takes care of * setting the mode and relaying it to other users and servers. */ -void _do_mode(Channel *channel, Client *client, MessageTag *recv_mtags, int parc, char *parv[], time_t sendts, int samode) +void _do_mode(Channel *channel, Client *client, MessageTag *recv_mtags, int parc, const char *parv[], time_t sendts, int samode) { + Client *orig_client = client; /* (needed for samode replacement in a loop) */ char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]; int pcount; - char tschange = 0, isbounce = 0; /* fwd'ing bounce */ - MessageTag *mtags = NULL; - - new_message(client, recv_mtags, &mtags); - - /* IMPORTANT: if you return, don't forget to free mtags!! */ - - if (**parv == '&') - isbounce = 1; + int i; + char tschange = 0; + MultiLineMode *m; /* Please keep the next 3 lines next to each other */ samode_in_progress = samode; - set_mode(channel, client, parc, parv, &pcount, pvar, 0); + m = set_mode(channel, client, parc, parv, &pcount, pvar); samode_in_progress = 0; - if (MyConnect(client)) - RunHook7(HOOKTYPE_PRE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode); - else - RunHook7(HOOKTYPE_PRE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode); - if (IsServer(client)) { if (sendts > 0) { - if (!channel->creationtime || sendts < channel->creationtime) + if (IsInvalidChannelTS(sendts)) { + unreal_log(ULOG_WARNING, "mode", "MODE_INVALID_TIMESTAMP", client, + "MODE for channel $channel has invalid timestamp $send_timestamp (from $client.name)\n" + "Buffer: $modebuf $parabuf", + log_data_channel("channel", channel), + log_data_integer("send_timestamp", sendts), + log_data_string("modebuf", m?m->modeline[0]:""), + log_data_string("parabuf", m?m->modeline[0]:"")); + /* Yeah, so what to do in this case? + * Don't set channel->creationtime + * and assume merging. + */ + sendts = channel->creationtime; + } else + if (sendts < channel->creationtime) + { + /* Our timestamp is wrong or this is a new channel */ tschange = 1; channel->creationtime = sendts; - if (sendts < 750000) - { - sendto_realops( - "Warning! Possible desync: MODE for channel %s ('%s %s') has fishy timestamp (%lld) (from %s/%s)", - channel->chname, modebuf, parabuf, (long long)sendts, client->direction->name, client->name); - ircd_log(LOG_ERROR, "Possible desync: MODE for channel %s ('%s %s') has fishy timestamp (%lld) (from %s/%s)", - channel->chname, modebuf, parabuf, (long long)sendts, client->direction->name, client->name); - } - /* new chan or our timestamp is wrong */ - /* now works for double-bounce prevention */ } if (sendts > channel->creationtime && channel->creationtime) { - /* theirs is wrong but we let it pass anyway */ + /* Their timestamp is wrong */ sendts = channel->creationtime; sendto_one(client, NULL, ":%s MODE %s + %lld", me.name, - channel->chname, (long long)channel->creationtime); + channel->name, (long long)channel->creationtime); } } - if (sendts == -1 && channel->creationtime) + if (sendts == -1) sendts = channel->creationtime; } - if (*modebuf == '\0' || (*(modebuf + 1) == '\0' && (*modebuf == '+' || *modebuf == '-'))) + if (!m) { - if (tschange || isbounce) + /* No modes changed (empty mode change) */ + if (tschange && !m) { - /* relay bounce time changes */ - if (channel->creationtime) - { - sendto_server(client, 0, 0, NULL, ":%s MODE %s %s+ %lld", - me.id, channel->chname, isbounce ? "&" : "", - (long long)channel->creationtime); - } else { - sendto_server(client, 0, 0, NULL, ":%s MODE %s %s+", - me.id, channel->chname, isbounce ? "&" : ""); - } - free_message_tags(mtags); - return; /* nothing to send */ + /* Message from the other server is an empty mode, BUT they + * did change the channel->creationtime to an earlier TS + * (see above "Our timestamp is wrong or this is a new channel"). + * We need to relay this MODE message to all other servers + * (all except from where it came from, client). + */ + sendto_server(client, 0, 0, NULL, ":%s MODE %s + %lld", + me.id, channel->name, + (long long)channel->creationtime); } - } - - /* opermode for twimodesystem --sts */ -#ifndef NO_OPEROVERRIDE - if ((opermode == 1) && IsUser(client)) - { - if (modebuf[1]) - { - sendto_snomask(SNO_EYES, - "*** OperOverride -- %s (%s@%s) MODE %s %s %s", - client->name, client->user->username, client->user->realhost, - channel->chname, modebuf, parabuf); - - /* Logging Implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) MODE %s %s %s", - client->name, client->user->username, client->user->realhost, - channel->chname, modebuf, parabuf); - } - - sendts = 0; - } -#endif - - /* Should stop null modes */ - if (*(modebuf + 1) == '\0') - { - free_message_tags(mtags); + /* Nothing to send */ + safe_free_multilinemode(m); + opermode = 0; return; } - if (IsUser(client) && samode && MyUser(client)) + /* Now loop through the multiline modes... */ + for (i = 0; i < m->numlines; i++) { - if (!sajoinmode) - sendto_umode_global(UMODE_OPER, "%s used SAMODE %s (%s%s%s)", - client->name, channel->chname, modebuf, *parabuf ? " " : "", parabuf); + char *modebuf = m->modeline[i]; + char *parabuf = m->paramline[i]; + MessageTag *mtags = NULL; + int should_destroy = 0; - client = &me; - sendts = 0; + if (m->numlines == 1) + { + /* Single mode lines are easy: retain original msgid etc */ + new_message(client, recv_mtags, &mtags); + } else { + /* We have a multi-mode line: + * This only happens when the input was a single mode line + * that got expanded into a multi mode line due to expansion + * issues. The sender could be a local client, but could also + * be a remote server like UnrealIRCd 5. + * We can't use the same msgid multiple times, and (if the + * sender was a server) then we can't use the original msgid + * either, not for both events and not for the first event + * (since the modeline differs for all events, including first). + * Obviously message ids must be unique for the event... + * So here is our special version again, just like we use in + * SJOIN and elsewhere sporadically for cases like this: + */ + new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->name, modebuf, parabuf); + } + + /* IMPORTANT: if you return, don't forget to free mtags!! */ + + if (MyConnect(client)) + RunHook(HOOKTYPE_PRE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode); + else + RunHook(HOOKTYPE_PRE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode); + + /* opermode for twimodesystem --sts */ +#ifndef NO_OPEROVERRIDE + if ((opermode == 1) && IsUser(client)) + { + mode_operoverride_msg(client, channel, modebuf, parabuf); + + sendts = 0; + } +#endif + + if (IsUser(orig_client) && samode && MyUser(orig_client)) + { + if (!sajoinmode) + { + char buf[512]; + snprintf(buf, sizeof(buf), "%s%s%s", modebuf, *parabuf ? " " : "", parabuf); + unreal_log(ULOG_INFO, "samode", "SAMODE_COMMAND", orig_client, + "Client $client used SAMODE $channel ($mode)", + log_data_channel("channel", channel), + log_data_string("mode", buf)); + } + + client = &me; + sendts = 0; + } + + sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, + ":%s MODE %s %s %s", + client->name, channel->name, modebuf, parabuf); + + if (IsServer(client) && sendts != -1) + { + 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); + } 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)) + RunHook(HOOKTYPE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode, &should_destroy); + else + RunHook(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode, &should_destroy); + + free_message_tags(mtags); + + if (should_destroy) + break; /* eg channel went -P with nobody in it. 'channel' is freed now */ } - - sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s %s %s", - client->name, channel->chname, modebuf, parabuf); - - if (IsServer(client) && sendts != -1) - { - sendto_server(client, 0, 0, mtags, - ":%s MODE %s %s%s %s %lld", - client->id, channel->chname, - isbounce ? "&" : "", 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->chname, modebuf, parabuf); - } else - { - sendto_server(client, 0, 0, mtags, - ":%s MODE %s %s%s %s", - client->id, channel->chname, isbounce ? "&" : "", modebuf, parabuf); - /* tell them it's not a timestamp, in case the last param - ** is a number. */ - } - - if (MyConnect(client)) - RunHook7(HOOKTYPE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode); - else - RunHook7(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, sendts, samode); - - /* After this, don't touch 'channel' anymore! As permanent module may have destroyed the channel. */ - - free_message_tags(mtags); - + safe_free_multilinemode(m); + opermode = 0; } + /* make_mode_str -- written by binary * Reconstructs the mode string, to make it look clean. mode_buf will * contain the +x-y stuff, and the parabuf will contain the parameters. - * If bounce is set to 1, it will make the string it needs for a bounce. */ -void make_mode_str(Channel *channel, long oldm, Cmode_t oldem, long oldl, int pcount, - char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], char *mode_buf, char *para_buf, - size_t mode_buf_size, size_t para_buf_size, char bounce) +MultiLineMode *make_mode_str(Client *client, Channel *channel, Cmode_t oldem, int pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]) { - char tmpbuf[MODEBUFLEN+3], *tmpstr; - CoreChannelModeTable *tab = &corechannelmodetable[0]; - char *x = mode_buf; - int what, cnt, z; - int i; - char *m; + Cmode *cm; + int what; + int cnt, z, i; + MultiLineMode *m = safe_alloc(sizeof(MultiLineMode)); + int curr = 0; + int initial_len; + + if (client->user) + initial_len = strlen(client->name) + strlen(client->user->username) + strlen(GetHost(client)) + strlen(channel->name) + 11; + else + initial_len = strlen(client->name) + strlen(channel->name) + 11; + + /* Reserve room for the first element */ + curr = 0; + m->modeline[curr] = safe_alloc(BUFSIZE); + m->paramline[curr] = safe_alloc(BUFSIZE); + m->numlines = curr+1; what = 0; - *tmpbuf = '\0'; - *mode_buf = '\0'; - *para_buf = '\0'; - what = 0; - /* + param-less modes */ - tab = &corechannelmodetable[0]; - while (tab->mode != 0x0) - { - if (channel->mode.mode & tab->mode) - { - if (!(oldm & tab->mode)) - { - if (what != MODE_ADD) - { - *x++ = bounce ? '-' : '+'; - what = MODE_ADD; - } - *x++ = tab->flag; - } - } - tab++; - } + /* The first element will be filled with all paramless modes. + * That is: both the ones that got set, and the ones that got unset. + * This will always fit. + */ - /* + paramless extmodes... */ - for (i=0; i <= Channelmode_highest; i++) + /* Which paramless modes got set? Eg +snt */ + for (cm=channelmodes; cm; cm = cm->next) { - if (!Channelmode_Table[i].flag || Channelmode_Table[i].paracount) + if (!cm->letter || cm->paracount) continue; /* have it now and didn't have it before? */ - if ((channel->mode.extmode & Channelmode_Table[i].mode) && - !(oldem & Channelmode_Table[i].mode)) + if ((channel->mode.mode & cm->mode) && + !(oldem & cm->mode)) { if (what != MODE_ADD) { - *x++ = bounce ? '-' : '+'; + strlcat_letter(m->modeline[curr], '+', BUFSIZE); what = MODE_ADD; } - *x++ = Channelmode_Table[i].flag; + strlcat_letter(m->modeline[curr], cm->letter, BUFSIZE); } } - *x = '\0'; - /* - param-less modes */ - tab = &corechannelmodetable[0]; - while (tab->mode != 0x0) + /* Which paramless modes got unset? Eg -r */ + for (cm=channelmodes; cm; cm = cm->next) { - if (!(channel->mode.mode & tab->mode)) - { - if (oldm & tab->mode) - { - if (what != MODE_DEL) - { - *x++ = bounce ? '+' : '-'; - what = MODE_DEL; - } - *x++ = tab->flag; - } - } - tab++; - } - - /* - extmodes (both "param modes" and paramless don't have - * any params when unsetting... well, except one special type, that is (we skip those here) - */ - for (i=0; i <= Channelmode_highest; i++) - { - if (!Channelmode_Table[i].flag || Channelmode_Table[i].unset_with_param) + if (!cm->letter || cm->unset_with_param) continue; /* don't have it now and did have it before */ - if (!(channel->mode.extmode & Channelmode_Table[i].mode) && - (oldem & Channelmode_Table[i].mode)) + if (!(channel->mode.mode & cm->mode) && (oldem & cm->mode)) { if (what != MODE_DEL) { - *x++ = bounce ? '+' : '-'; + strlcat_letter(m->modeline[curr], '-', BUFSIZE); what = MODE_DEL; } - *x++ = Channelmode_Table[i].flag; + strlcat_letter(m->modeline[curr], cm->letter, BUFSIZE); } } - *x = '\0'; - /* user limit */ - if (channel->mode.limit != oldl) - { - if ((!bounce && channel->mode.limit == 0) || - (bounce && channel->mode.limit != 0)) - { - if (what != MODE_DEL) - { - *x++ = '-'; - what = MODE_DEL; - } - if (bounce) - channel->mode.limit = 0; /* set it back */ - *x++ = 'l'; - } - else - { - if (what != MODE_ADD) - { - *x++ = '+'; - what = MODE_ADD; - } - *x++ = 'l'; - if (bounce) - channel->mode.limit = oldl; /* set it back */ - ircsnprintf(para_buf, para_buf_size, "%s%d ", para_buf, channel->mode.limit); - } - } - /* reconstruct bkov chain */ + /* Now for parameter modes we do both addition and removal. Eg +b-e ban!x@y exempt!z@z */ for (cnt = 0; cnt < pcount; cnt++) { + if ((strlen(m->modeline[curr]) + strlen(m->paramline[curr]) + strlen(&pvar[cnt][2])) > 507) + { + if (curr == MAXMULTILINEMODES) + { + /* Should be impossible.. */ + unreal_log(ULOG_ERROR, "mode", "MODE_MULTINE_EXCEEDED", client, + "A mode string caused an avalanche effect of more than $max_multiline modes " + "in channel $channel. Caused by client $client. Expect a desync.", + log_data_integer("max_multiline_modes", MAXMULTILINEMODES), + log_data_channel("channel", channel)); + break; + } + curr++; + m->modeline[curr] = safe_alloc(BUFSIZE); + m->paramline[curr] = safe_alloc(BUFSIZE); + m->numlines = curr+1; + what = 0; + } if ((*(pvar[cnt]) == '+') && what != MODE_ADD) { - *x++ = bounce ? '-' : '+'; + strlcat_letter(m->modeline[curr], '+', BUFSIZE); what = MODE_ADD; } if ((*(pvar[cnt]) == '-') && what != MODE_DEL) { - *x++ = bounce ? '+' : '-'; + strlcat_letter(m->modeline[curr], '-', BUFSIZE); what = MODE_DEL; } - *x++ = *(pvar[cnt] + 1); - tmpstr = &pvar[cnt][2]; - z = (MODEBUFLEN * MAXMODEPARAMS); - m = para_buf; - while ((*m)) { m++; } - while ((*tmpstr) && ((m-para_buf) < z)) - { - *m = *tmpstr; - m++; - tmpstr++; - } - *m++ = ' '; - *m = '\0'; + strlcat_letter(m->modeline[curr], *(pvar[cnt] + 1), BUFSIZE); + strlcat(m->paramline[curr], &pvar[cnt][2], BUFSIZE); + strlcat_letter(m->paramline[curr], ' ', BUFSIZE); } - if (bounce) + + for (i = 0; i <= curr; i++) { - channel->mode.mode = oldm; - channel->mode.extmode = oldem; + char *para_buf = m->paramline[i]; + /* Strip off useless space character (' ') at the end, if there is any */ + z = strlen(para_buf); + if ((z > 0) && (para_buf[z - 1] == ' ')) + para_buf[z - 1] = '\0'; } - z = strlen(para_buf); - if ((z > 0) && (para_buf[z - 1] == ' ')) - para_buf[z - 1] = '\0'; - *x = '\0'; - if (*mode_buf == '\0') + + /* Now check for completely empty mode: */ + if ((curr == 0) && empty_mode(m->modeline[0])) { - *mode_buf = '+'; - mode_buf++; - *mode_buf = '\0'; - /* Don't send empty lines. */ + /* And convert it to a NULL result */ + safe_free_multilinemode(m); + return NULL; } - return; + + return m; } +const char *mode_ban_handler(Client *client, Channel *channel, const char *param, int what, int extbtype, Ban **banlist) +{ + const char *tmpstr; + BanContext *b; -/* do_mode_char - * processes one mode character - * returns 1 if it ate up a param, otherwise 0 - * written by binary - * modified for Unreal by stskeeps.. + tmpstr = clean_ban_mask(param, what, client, 0); + if (BadPtr(tmpstr)) + { + /* Invalid ban. See if we can send an error about that (only for extbans) */ + if (MyUser(client) && is_extended_ban(param)) + { + const char *nextbanstr; + Extban *extban = findmod_by_bantype(param, &nextbanstr); + BanContext *b; + + b = safe_alloc(sizeof(BanContext)); + b->client = client; + b->channel = channel; + b->banstr = nextbanstr; + b->is_ok_check = EXBCHK_PARAM; + b->what = what; + b->ban_type = extbtype; + if (extban && extban->is_ok) + extban->is_ok(b); + safe_free(b); + } + + return NULL; + } + if (MyUser(client) && is_extended_ban(param)) + { + /* extban: check access if needed */ + const char *nextbanstr; + Extban *extban = findmod_by_bantype(tmpstr, &nextbanstr); + if (extban) + { + if ((extbtype == EXBTYPE_INVEX) && !(extban->options & EXTBOPT_INVEX)) + return NULL; /* this extended ban type does not support INVEX */ + if (extban->is_ok) + { + BanContext *b = safe_alloc(sizeof(BanContext)); + b->client = client; + b->channel = channel; + b->what = what; + b->ban_type = extbtype; + + b->is_ok_check = EXBCHK_ACCESS; + b->banstr = nextbanstr; + if (!extban->is_ok(b)) + { + if (ValidatePermissionsForPath("channel:override:mode:extban",client,NULL,channel,NULL)) + { + /* TODO: send operoverride notice */ + } else { + b->banstr = nextbanstr; + b->is_ok_check = EXBCHK_ACCESS_ERR; + extban->is_ok(b); + safe_free(b); + return NULL; + } + } + b->banstr = nextbanstr; + b->is_ok_check = EXBCHK_PARAM; + if (!extban->is_ok(b)) + { + safe_free(b); + return NULL; + } + safe_free(b); + } + } + } + + if ( (what == MODE_ADD && add_listmode(banlist, client, channel, tmpstr)) || + (what == MODE_DEL && del_listmode(banlist, channel, tmpstr))) + { + return NULL; /* already exists */ + } + + return tmpstr; +} + +/** Write the result of a mode change. + * This is used by do_mode_char_list_mode(), do_mode_char_member_mode() + * and do_extmode_char(). + * The result is later used by make_mode_str() to create the + * actual MODE line to be broadcasted to the channel and other servers. */ - -#define REQUIRE_PARAMETER() { if (!param || *pcount >= MAXMODEPARAMS) { retval = 0; break; } retval = 1; } - -#ifdef PREFIX_AQ -#define is_xchanop(x) ((x & (CHFL_CHANOP|CHFL_CHANADMIN|CHFL_CHANOWNER))) +void do_mode_char_write(char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], u_int *pcount, u_int what, char modeletter, const char *str) +{ + /* Caller should have made sure there was room! */ + if (*pcount >= MAXMODEPARAMS) +#ifdef DEBUGMODE + abort(); #else -#define is_xchanop(x) ((x & CHFL_CHANOP)) + return; #endif -int do_mode_char(Channel *channel, long modetype, char modechar, char *param, - u_int what, Client *client, - u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], - char bounce, long my_access) -{ - CoreChannelModeTable *tab = &corechannelmodetable[0]; - int retval = 0; - Member *member = NULL; - Membership *membership = NULL; - Client *target; - unsigned int tmp = 0; - char tmpbuf[512], *tmpstr; - char tc = ' '; /* */ - int chasing = 0, x; - Hook *h; + ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, + "%c%c%s", + (what == MODE_ADD) ? '+' : '-', + modeletter, + str); + (*pcount)++; +} + +int do_mode_char_list_mode(Channel *channel, long modetype, char modechar, const char *param, + u_int what, Client *client, + u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]) +{ + const char *tmpstr; + + /* Check if there is a parameter present */ + if (!param || *pcount >= MAXMODEPARAMS) + return 0; - if ((my_access & CHFL_HALFOP) && !is_xchanop(my_access) && !IsULine(client) && - !op_can_override("channel:override:mode",client,channel,&modetype) && !samode_in_progress) - { - if (MyUser(client) && (modetype == MODE_HALFOP) && (what == MODE_DEL) && - param && (find_client(param, NULL) == client)) - { - /* halfop -h'ing him/herself */ - /* ALLOW */ - } else - { - /* Ugly halfop hack --sts - - this allows halfops to do +b +e +v and so on */ - /* (Syzop/20040413: Allow remote halfop modes */ - if ((Halfop_mode(modetype) == FALSE) && MyUser(client)) - { - int eaten = 0; - while (tab->mode != 0x0) - { - if (tab->mode == modetype) - { - sendnumeric(client, ERR_NOTFORHALFOPS, tab->flag); - eaten = tab->parameters; - break; - } - tab++; - } - return eaten; - } - } /* not -h self */ - } switch (modetype) { - case MODE_RGSTR: - if (!IsServer(client) && !IsULine(client)) - { - sendnumeric(client, ERR_ONLYSERVERSCANCHANGE, channel->chname); - break; - } - goto setmode; - case MODE_SECRET: - case MODE_PRIVATE: - case MODE_MODERATED: - case MODE_TOPICLIMIT: - case MODE_NOPRIVMSGS: - case MODE_INVITEONLY: - goto setmode; - setmode: - retval = 0; - if (what == MODE_ADD) { - /* +sp bugfix.. (by JK/Luke)*/ - if ((modetype == MODE_SECRET) && (channel->mode.mode & MODE_PRIVATE)) - channel->mode.mode &= ~MODE_PRIVATE; - if ((modetype == MODE_PRIVATE) && (channel->mode.mode & MODE_SECRET)) - channel->mode.mode &= ~MODE_SECRET; - channel->mode.mode |= modetype; - } - else - { - channel->mode.mode &= ~modetype; - RunHook2(HOOKTYPE_MODECHAR_DEL, channel, (int)modechar); - } - break; - -/* do pro-opping here (popping) */ - case MODE_CHANOWNER: - REQUIRE_PARAMETER() - if (!IsULine(client) && !IsServer(client) && !is_chanowner(client, channel) && !samode_in_progress) - { - if (MyUser(client) && !op_can_override("channel:override:mode",client,channel,&modetype)) - { - sendnumeric(client, ERR_CHANOWNPRIVNEEDED, channel->chname); - break; - } - if (!is_half_op(client, channel)) /* htrig will take care of halfop override notices */ - opermode = 1; - } - goto process_listmode; - case MODE_CHANADMIN: - REQUIRE_PARAMETER() - /* not uline, not server, not chanowner, not an samode, not -a'ing yourself... */ - if (!IsULine(client) && !IsServer(client) && !is_chanowner(client, channel) && !samode_in_progress && - !(param && (what == MODE_DEL) && (find_client(param, NULL) == client))) - { - if (MyUser(client) && !op_can_override("channel:override:mode",client,channel,&modetype)) - { - sendnumeric(client, ERR_CHANOWNPRIVNEEDED, channel->chname); - break; - } - if (!is_half_op(client, channel)) /* htrig will take care of halfop override notices */ - opermode = 1; - } - goto process_listmode; - - case MODE_HALFOP: - case MODE_CHANOP: - case MODE_VOICE: - REQUIRE_PARAMETER() -process_listmode: - if (!(target = find_chasing(client, param, &chasing))) - break; - if (!target->user) - break; - if (!(membership = find_membership_link(target->user->channel, channel))) - { - sendnumeric(client, ERR_USERNOTINCHANNEL, target->name, channel->chname); - break; - } - member = find_member_link(channel->members, target); - if (!member) - { - /* should never happen */ - sendto_realops("crap! find_membership_link && !find_member_link !!. Report to unreal team"); - break; - } - /* we make the rules, we bend the rules */ - if (IsServer(client) || IsULine(client)) - goto breaktherules; - - if (what == MODE_DEL) - { - int ret = EX_ALLOW; - char *badmode = NULL; - - for (h = Hooks[HOOKTYPE_MODE_DEOP]; h; h = h->next) - { - int n = (*(h->func.intfunc))(client, member->client, channel, what, modechar, my_access, &badmode); - if (n == EX_DENY) - ret = n; - else if (n == EX_ALWAYS_DENY) - { - ret = n; - break; - } - } - - if (ret == EX_ALWAYS_DENY) - { - if (MyUser(client) && badmode) - sendto_one(client, NULL, "%s", badmode); /* send error message, if any */ - - if (MyUser(client)) - break; /* stop processing this mode */ - } - - /* This probably should work but is completely untested (the operoverride stuff, I mean): */ - if (ret == EX_DENY) - { - if (!op_can_override("channel:override:mode:del",client,channel,&modetype)) - { - if (badmode) - sendto_one(client, NULL, "%s", badmode); /* send error message, if any */ - break; /* stop processing this mode */ - } else { - opermode = 1; - } - } - } - - /* This check not only prevents unprivileged users from doing a -q on chanowners, - * it also protects against -o/-h/-v on them. - */ - if (is_chanowner(member->client, channel) - && member->client != client - && !is_chanowner(client, channel) && !IsServer(client) - && !IsULine(client) && !opermode && !samode_in_progress && (what == MODE_DEL)) - { - if (MyUser(client)) - { - /* Need this !op_can_override() here again, even with the !opermode - * check a few lines up, all due to halfops. -- Syzop - */ - if (!op_can_override("channel:override:mode:del",client,channel,&modetype)) - { - char errbuf[NICKLEN+30]; - ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel owner", member->client->name); - sendnumeric(client, ERR_CANNOTCHANGECHANMODE, modechar, errbuf); - break; - } - } else { - if (IsOper(client)) - opermode = 1; - } - } - - /* This check not only prevents unprivileged users from doing a -a on chanadmins, - * it also protects against -o/-h/-v on them. - */ - if (is_chanadmin(member->client, channel) - && member->client != client - && !is_chanowner(client, channel) && !IsServer(client) && !opermode && !samode_in_progress - && modetype != MODE_CHANOWNER && (what == MODE_DEL)) - { - if (MyUser(client)) - { - /* Need this !op_can_override() here again, even with the !opermode - * check a few lines up, all due to halfops. -- Syzop - */ - if (!op_can_override("channel:override:mode:del",client,channel,&modetype)) - { - char errbuf[NICKLEN+30]; - ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel admin", member->client->name); - sendnumeric(client, ERR_CANNOTCHANGECHANMODE, modechar, errbuf); - break; - } - } else { - if (IsOper(client)) - opermode = 1; - } - } - breaktherules: - tmp = member->flags; - if (what == MODE_ADD) - member->flags |= modetype; - else - member->flags &= ~modetype; - if ((tmp == member->flags) && (bounce || !IsULine(client))) - break; - /* It's easier to undo the mode here instead of later - * when you call make_mode_str for a bounce string. - * Why set it if it will be instantly removed? - * Besides, pvar keeps a log of it. */ - if (bounce) - member->flags = tmp; - if (modetype == MODE_CHANOWNER) - tc = 'q'; - if (modetype == MODE_CHANADMIN) - tc = 'a'; - if (modetype == MODE_CHANOP) - tc = 'o'; - if (modetype == MODE_HALFOP) - tc = 'h'; - if (modetype == MODE_VOICE) - tc = 'v'; - /* Make sure membership->flags and member->flags is the same */ - membership->flags = member->flags; - ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, - "%c%c%s", - (what == MODE_ADD) ? '+' : '-', tc, target->name); - (*pcount)++; - break; - case MODE_LIMIT: - if (what == MODE_ADD) - { - int v; - REQUIRE_PARAMETER() - v = atoi(param); - if (v < 0) - v = 1; /* setting +l with a negative number makes no sense */ - if (v > 1000000000) - v = 1000000000; /* some kind of limit, 1 billion (mrah...) */ - if (channel->mode.limit == v) - break; - channel->mode.limit = v; - } - else - { - retval = 0; - if (!channel->mode.limit) - break; - channel->mode.limit = 0; - RunHook2(HOOKTYPE_MODECHAR_DEL, channel, (int)modechar); - } - break; - case MODE_KEY: - REQUIRE_PARAMETER() - for (x = 0; x < *pcount; x++) - { - if (pvar[x][1] == 'k') - { /* don't allow user to change key - * more than once per command. */ - retval = 0; - break; - } - } - if (retval == 0) /* you can't break a case from loop */ - break; - if (what == MODE_ADD) - { - if (!bounce) { /* don't do the mode at all. */ - char *tmp; - if ((tmp = strchr(param, ' '))) - *tmp = '\0'; - if ((tmp = strchr(param, ':'))) - *tmp = '\0'; - if ((tmp = strchr(param, ','))) - *tmp = '\0'; - if (*param == '\0') - break; - if (strlen(param) > KEYLEN) - param[KEYLEN] = '\0'; - if (!strcmp(channel->mode.key, param)) - break; - strlcpy(channel->mode.key, param, sizeof(channel->mode.key)); - } - tmpstr = param; - } - else - { - if (!*channel->mode.key) - break; /* no change */ - strlcpy(tmpbuf, channel->mode.key, sizeof(tmpbuf)); - tmpstr = tmpbuf; - if (!bounce) - strcpy(channel->mode.key, ""); - RunHook2(HOOKTYPE_MODECHAR_DEL, channel, (int)modechar); - } - retval = 1; - - ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, - "%ck%s", - (what == MODE_ADD) ? '+' : '-', tmpstr); - (*pcount)++; - break; - case MODE_BAN: - REQUIRE_PARAMETER() - retval = 1; - tmpstr = clean_ban_mask(param, what, client); - if (BadPtr(tmpstr)) - { - /* Invalid ban. See if we can send an error about that (only for extbans) */ - if (MyUser(client) && !bounce && is_extended_ban(param)) - { - Extban *p = findmod_by_bantype(param[1]); - if (p && p->is_ok) - p->is_ok(client, channel, param, EXBCHK_PARAM, what, EXBTYPE_BAN); - } - - break; /* ignore ban, but eat param */ - } - if (MyUser(client) && !bounce && is_extended_ban(param)) - { - /* extban: check access if needed */ - Extban *p = findmod_by_bantype(tmpstr[1]); - if (p && p->is_ok) - { - if (!p->is_ok(client, channel, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_BAN)) - { - if (ValidatePermissionsForPath("channel:override:mode:extban",client,NULL,channel,NULL)) - { - /* TODO: send operoverride notice */ - } else { - p->is_ok(client, channel, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_BAN); - break; - } - } - if (!p->is_ok(client, channel, tmpstr, EXBCHK_PARAM, what, EXBTYPE_BAN)) - break; - } - } - /* For bounce, we don't really need to worry whether - * or not it exists on our server. We'll just always - * bounce it. */ - if (!bounce && - ((what == MODE_ADD && add_listmode(&channel->banlist, client, channel, tmpstr)) - || (what == MODE_DEL && del_listmode(&channel->banlist, channel, tmpstr)))) - { - break; /* already exists */ - } - ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, - "%cb%s", - (what == MODE_ADD) ? '+' : '-', tmpstr); - (*pcount)++; + if (!(tmpstr = mode_ban_handler(client, channel, param, what, EXBTYPE_BAN, &channel->banlist))) + break; /* rejected or duplicate */ + do_mode_char_write(pvar, pcount, what, modechar, tmpstr); break; case MODE_EXCEPT: - REQUIRE_PARAMETER() - tmpstr = clean_ban_mask(param, what, client); - if (BadPtr(tmpstr)) - { - /* Invalid except. See if we can send an error about that (only for extbans) */ - if (MyUser(client) && !bounce && is_extended_ban(param)) - { - Extban *p = findmod_by_bantype(param[1]); - if (p && p->is_ok) - p->is_ok(client, channel, param, EXBCHK_PARAM, what, EXBTYPE_EXCEPT); - } - - break; /* ignore except, but eat param */ - } - if (MyUser(client) && !bounce && is_extended_ban(param)) - { - /* extban: check access if needed */ - Extban *p = findmod_by_bantype(tmpstr[1]); - if (p && p->is_ok) - { - if (!p->is_ok(client, channel, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_EXCEPT)) - { - if (ValidatePermissionsForPath("channel:override:mode:extban",client,NULL,channel,NULL)) - { - /* TODO: send operoverride notice */ - } else { - p->is_ok(client, channel, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_EXCEPT); - break; - } - } - if (!p->is_ok(client, channel, tmpstr, EXBCHK_PARAM, what, EXBTYPE_EXCEPT)) - break; - } - } - /* For bounce, we don't really need to worry whether - * or not it exists on our server. We'll just always - * bounce it. */ - if (!bounce && - ((what == MODE_ADD && add_listmode(&channel->exlist, client, channel, tmpstr)) - || (what == MODE_DEL && del_listmode(&channel->exlist, channel, tmpstr)))) - { - break; /* already exists */ - } - ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, - "%ce%s", - (what == MODE_ADD) ? '+' : '-', tmpstr); - (*pcount)++; + if (!(tmpstr = mode_ban_handler(client, channel, param, what, EXBTYPE_EXCEPT, &channel->exlist))) + break; /* rejected or duplicate */ + do_mode_char_write(pvar, pcount, what, modechar, tmpstr); break; case MODE_INVEX: - REQUIRE_PARAMETER() - tmpstr = clean_ban_mask(param, what, client); - if (BadPtr(tmpstr)) - { - /* Invalid invex. See if we can send an error about that (only for extbans) */ - if (MyUser(client) && !bounce && is_extended_ban(param)) - { - Extban *p = findmod_by_bantype(param[1]); - if (p && p->is_ok) - p->is_ok(client, channel, param, EXBCHK_PARAM, what, EXBTYPE_INVEX); - } - - break; /* ignore invex, but eat param */ - } - if (MyUser(client) && !bounce && is_extended_ban(param)) - { - /* extban: check access if needed */ - Extban *p = findmod_by_bantype(tmpstr[1]); - if (p) - { - if (!(p->options & EXTBOPT_INVEX)) - break; /* this extended ban type does not support INVEX */ - if (p->is_ok && !p->is_ok(client, channel, tmpstr, EXBCHK_ACCESS, what, EXBTYPE_INVEX)) - { - if (ValidatePermissionsForPath("channel:override:mode:extban",client,NULL,channel,NULL)) - { - /* TODO: send operoverride notice */ - } else { - p->is_ok(client, channel, tmpstr, EXBCHK_ACCESS_ERR, what, EXBTYPE_INVEX); - break; - } - } - if (p->is_ok && !p->is_ok(client, channel, tmpstr, EXBCHK_PARAM, what, EXBTYPE_INVEX)) - break; - } - } - /* For bounce, we don't really need to worry whether - * or not it exists on our server. We'll just always - * bounce it. */ - if (!bounce && - ((what == MODE_ADD && add_listmode(&channel->invexlist, client, channel, tmpstr)) - || (what == MODE_DEL && del_listmode(&channel->invexlist, channel, tmpstr)))) - { - break; /* already exists */ - } - ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, - "%cI%s", - (what == MODE_ADD) ? '+' : '-', tmpstr); - (*pcount)++; + if (!(tmpstr = mode_ban_handler(client, channel, param, what, EXBTYPE_INVEX, &channel->invexlist))) + break; /* rejected or duplicate */ + do_mode_char_write(pvar, pcount, what, modechar, tmpstr); break; } - return retval; + return 1; } /** Check access and if granted, set the extended chanmode to the requested value in memory. - * note: if bounce is requested then the mode will not be set. * @returns amount of params eaten (0 or 1) */ -int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what, - Client *client, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], - char bounce) +int do_extmode_char(Channel *channel, Cmode *handler, const char *param, u_int what, + Client *client, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]) { int paracnt = (what == MODE_ADD) ? handler->paracount : 0; - char mode = handler->flag; + char mode = handler->letter; int x; - char *morphed; + const char *morphed; if ((what == MODE_DEL) && handler->unset_with_param) paracnt = 1; /* there's always an exception! */ @@ -1244,7 +697,7 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what, handler->is_ok(client, channel, mode, param, EXCHK_ACCESS_ERR, what); return paracnt; /* Denied & error msg sent */ } - if (x == EX_DENY) + if ((x == EX_DENY) && !samode_in_progress) opermode = 1; /* override in progress... */ } else { /* remote user: we only need to check if we need to generate an operoverride msg */ @@ -1255,10 +708,16 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what, } } + if (handler->type == CMODE_MEMBER) + { + do_mode_char_member_mode_new(channel, handler, param, what, client, pcount, pvar); + return 1; + } + /* Check for multiple changes in 1 command (like +y-y+y 1 2, or +yy 1 2). */ for (x = 0; x < *pcount; x++) { - if (pvar[x][1] == handler->flag) + if (pvar[x][1] == handler->letter) { /* this is different than the old chanmode system, coz: * "mode #chan +kkL #a #b #c" will get "+kL #a #b" which is wrong :p. @@ -1273,16 +732,14 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what, { if (what == MODE_DEL) { - if (!(channel->mode.extmode & handler->mode)) + if (!(channel->mode.mode & handler->mode)) return paracnt; /* There's nothing to remove! */ if (handler->unset_with_param) { /* Special extended channel mode requiring a parameter on unset. * Any provided parameter is ok, the current one (that is set) will be used. */ - ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, "-%c%s", - handler->flag, cm_getparameter(channel, handler->flag)); - (*pcount)++; + do_mode_char_write(pvar, pcount, what, handler->letter, cm_getparameter(channel, handler->letter)); } else { /* Normal extended channel mode: deleting is just -X, no parameter. * Nothing needs to be done here. @@ -1298,40 +755,92 @@ int do_extmode_char(Channel *channel, Cmode *handler, char *param, u_int what, return paracnt; /* rejected by conv_param */ /* is it already set at the same value? if so, ignore it. */ - if (channel->mode.extmode & handler->mode) + if (channel->mode.mode & handler->mode) { - char *now, *requested; - char flag = handler->flag; + const char *now, *requested; + char flag = handler->letter; now = cm_getparameter(channel, flag); requested = handler->conv_param(param, client, channel); if (now && requested && !strcmp(now, requested)) return paracnt; /* ignore... */ } - ircsnprintf(pvar[*pcount], MODEBUFLEN + 3, "+%c%s", - handler->flag, handler->conv_param(param, client, channel)); - (*pcount)++; + do_mode_char_write(pvar, pcount, what, handler->letter, handler->conv_param(param, client, channel)); param = morphed; /* set param to converted parameter. */ } } - if (bounce) /* bounce here means: only check access and return return value */ - return paracnt; - if (what == MODE_ADD) { /* + */ - channel->mode.extmode |= handler->mode; + channel->mode.mode |= handler->mode; if (handler->paracount) - cm_putparameter(channel, handler->flag, param); + cm_putparameter(channel, handler->letter, param); + RunHook(HOOKTYPE_MODECHAR_ADD, channel, (int)mode); } else { /* - */ - channel->mode.extmode &= ~(handler->mode); - RunHook2(HOOKTYPE_MODECHAR_DEL, channel, (int)mode); + channel->mode.mode &= ~(handler->mode); + RunHook(HOOKTYPE_MODECHAR_DEL, channel, (int)mode); if (handler->paracount) - cm_freeparameter(channel, handler->flag); + cm_freeparameter(channel, handler->letter); } return paracnt; } +/** Set or unset a mode on a member (eg +vhoaq/-vhoaq) */ +void do_mode_char_member_mode_new(Channel *channel, Cmode *handler, const char *param, u_int what, + Client *client, u_int *pcount, char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]) +{ + Member *member = NULL; + Membership *membership = NULL; + Client *target; + int chasing = 0; + Hook *h; + char c[2]; + char modechar = handler->letter; + + if (!(target = find_chasing(client, param, &chasing))) + return; + + if (!target->user) + return; + + if (!(membership = find_membership_link(target->user->channel, channel))) + { + sendnumeric(client, ERR_USERNOTINCHANNEL, target->name, channel->name); + return; + } + member = find_member_link(channel->members, target); + if (!member) + { + /* should never happen */ + unreal_log(ULOG_ERROR, "mode", "BUG_FIND_MEMBER_LINK_FAILED", target, + "[BUG] Client $target.details on channel $channel: " + "found via find_membership_link() but NOT found via find_member_link(). " + "This should never happen! Please report on https://bugs.unrealircd.org/", + log_data_channel("channel", channel)); + return; + } + + if ((what == MODE_ADD) && strchr(member->member_modes, modechar)) + return; /* already set */ + if ((what == MODE_DEL) && !strchr(member->member_modes, modechar)) + return; /* already unset */ + + if (what == MODE_ADD) + { + if (strchr(member->member_modes, modechar)) + return; /* already set */ + /* Set the mode */ + add_member_mode_fast(member, membership, modechar); + } else { + if (!strchr(member->member_modes, modechar)) + return; /* already unset */ + del_member_mode_fast(member, membership, modechar); + } + + /* And write out the mode */ + do_mode_char_write(pvar, pcount, what, modechar, target->name); +} + /** In 2003 I introduced PROTOCTL CHANMODES= so remote servers (and services) * could deal with unknown "parameter eating" channel modes, minimizing desyncs. * Now, in 2015, I finally added the code to deal with this. -- Syzop @@ -1341,27 +850,27 @@ int paracount_for_chanmode_from_server(Client *client, u_int what, char mode) if (MyUser(client)) return 0; /* no server, we have no idea, assume 0 paracount */ - if (!client->serv) + if (!client->server) { /* If it's from a remote client then figure out from which "uplink" we * received this MODE. The uplink is the directly-connected-server to us * and may differ from the server the user is actually on. This is correct. */ - if (!client->direction || !client->direction->serv) + if (!client->direction || !client->direction->server) return 0; client = client->direction; } - if (client->serv->features.chanmodes[0] && strchr(client->serv->features.chanmodes[0], mode)) + if (client->server->features.chanmodes[0] && strchr(client->server->features.chanmodes[0], mode)) return 1; /* 1 parameter for set, 1 parameter for unset */ - if (client->serv->features.chanmodes[1] && strchr(client->serv->features.chanmodes[1], mode)) + if (client->server->features.chanmodes[1] && strchr(client->server->features.chanmodes[1], mode)) return 1; /* 1 parameter for set, 1 parameter for unset */ - if (client->serv->features.chanmodes[2] && strchr(client->serv->features.chanmodes[2], mode)) + if (client->server->features.chanmodes[2] && strchr(client->server->features.chanmodes[2], mode)) return (what == MODE_ADD) ? 1 : 0; /* 1 parameter for set, no parameter for unset */ - if (client->serv->features.chanmodes[3] && strchr(client->serv->features.chanmodes[3], mode)) + if (client->server->features.chanmodes[3] && strchr(client->server->features.chanmodes[3], mode)) return 0; /* no parameter for set, no parameter for unset */ if (mode == '&') @@ -1374,10 +883,10 @@ int paracount_for_chanmode_from_server(Client *client, u_int what, char mode) * channel mode. That's actually pretty bad. This shouldn't happen since CHANMODES= * is sent since 2003 and the (often also required) EAUTH PROTOCTL is in there since 2010. */ - sendto_realops("Unknown channel mode %c%c from server %s!", - (what == MODE_ADD) ? '+' : '-', - mode, - client->name); + unreal_log(ULOG_WARNING, "mode", "REMOTE_UNKNOWN_CHANNEL_MODE", client, + "Server $client sent us an unknown channel mode $what$mode_character!", + log_data_string("what", ((what == MODE_ADD) ? "+" : "-")), + log_data_char("mode_character", mode)); return 0; } @@ -1388,30 +897,30 @@ int paracount_for_chanmode_from_server(Client *client, u_int what, char mode) */ int paracount_for_chanmode(u_int what, char mode) { - if (me.serv->features.chanmodes[0] && strchr(me.serv->features.chanmodes[0], mode)) + if (me.server->features.chanmodes[0] && strchr(me.server->features.chanmodes[0], mode)) return 1; /* 1 parameter for set, 1 parameter for unset */ - if (me.serv->features.chanmodes[1] && strchr(me.serv->features.chanmodes[1], mode)) + if (me.server->features.chanmodes[1] && strchr(me.server->features.chanmodes[1], mode)) return 1; /* 1 parameter for set, 1 parameter for unset */ - if (me.serv->features.chanmodes[2] && strchr(me.serv->features.chanmodes[2], mode)) + if (me.server->features.chanmodes[2] && strchr(me.server->features.chanmodes[2], mode)) return (what == MODE_ADD) ? 1 : 0; /* 1 parameter for set, no parameter for unset */ - if (me.serv->features.chanmodes[3] && strchr(me.serv->features.chanmodes[3], mode)) + if (me.server->features.chanmodes[3] && strchr(me.server->features.chanmodes[3], mode)) return 0; /* no parameter for set, no parameter for unset */ /* Not found: */ return 0; } -/* set_mode - * written by binary - */ -void _set_mode(Channel *channel, Client *client, int parc, char *parv[], u_int *pcount, - char pvar[MAXMODEPARAMS][MODEBUFLEN + 3], int bounce) +MultiLineMode *_set_mode(Channel *channel, Client *client, int parc, const char *parv[], u_int *pcount, + char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]) { - char *curchr; - char *argument; + Cmode *cm = NULL; + MultiLineMode *mlm = NULL; + const char *curchr; + const char *argument; + char argumentbuf[MODEBUFLEN+1]; u_int what = MODE_ADD; long modetype = 0; int paracount = 1; @@ -1422,24 +931,15 @@ void _set_mode(Channel *channel, Client *client, int parc, char *parv[], u_int * CoreChannelModeTable foundat; int found = 0; int sent_mlock_warning = 0; - unsigned int htrig = 0; - long oldm, oldl; int checkrestr = 0, warnrestr = 1; - int extm = 1000000; /* (default value not used but stops gcc from complaining) */ Cmode_t oldem; - long my_access; paracount = 1; *pcount = 0; - oldm = channel->mode.mode; - oldl = channel->mode.limit; - oldem = channel->mode.extmode; + oldem = channel->mode.mode; if (RESTRICT_CHANNELMODES && !ValidatePermissionsForPath("immune:restrict-channelmodes",client,NULL,channel,NULL)) /* "cache" this */ checkrestr = 1; - /* Set access to the status we have */ - my_access = IsUser(client) ? get_access(client, channel) : 0; - for (curchr = parv[0]; *curchr; curchr++) { switch (*curchr) @@ -1458,7 +958,7 @@ void _set_mode(Channel *channel, Client *client, int parc, char *parv[], u_int * { if (!sent_mlock_warning) { - sendnumeric(client, ERR_MLOCKRESTRICTED, channel->chname, *curchr, channel->mode_lock); + sendnumeric(client, ERR_MLOCKRESTRICTED, channel->name, *curchr, channel->mode_lock); sent_mlock_warning++; } continue; @@ -1480,9 +980,9 @@ void _set_mode(Channel *channel, Client *client, int parc, char *parv[], u_int * modetype = foundat.mode; } else { /* Maybe in extmodes */ - for (extm=0; extm <= Channelmode_highest; extm++) + for (cm=channelmodes; cm; cm = cm->next) { - if (Channelmode_Table[extm].flag == *curchr) + if (cm->letter == *curchr) { found = 2; break; @@ -1510,74 +1010,24 @@ void _set_mode(Channel *channel, Client *client, int parc, char *parv[], u_int * break; } - if (paracount < parc) - argument = parv[paracount]; /* can still be NULL */ - else + if ((paracount < parc) && parv[paracount]) + { + strlcpy(argumentbuf, parv[paracount], sizeof(argumentbuf)); + argument = argumentbuf; + } else { argument = NULL; - -#ifndef NO_OPEROVERRIDE - if (found == 1) - { - if ((Halfop_mode(modetype) == FALSE) && opermode == 2 && htrig != 1) - { - /* YUCK! */ - if ((foundat.flag == 'h') && argument && (find_person(argument, NULL) == client)) - { - /* ircop with halfop doing a -h on himself. no warning. */ - } else { - opermode = 0; - htrig = 1; - } - } } - else if (found == 2) { - /* Extended mode: all override stuff is in do_extmode_char which will set - * opermode if appropriate. -- Syzop - */ - } -#endif /* !NO_OPEROVERRIDE */ - - /* Not sure how useful this is, but I'll let it stay... */ - if (argument && strlen(argument) >= MODEBUFLEN) - argument[MODEBUFLEN-1] = '\0'; if (found == 1) - { - paracount += do_mode_char(channel, modetype, *curchr, - argument, what, client, pcount, - pvar, bounce, my_access); - } + paracount += do_mode_char_list_mode(channel, modetype, *curchr, argument, what, client, pcount, pvar); else if (found == 2) - { - paracount += do_extmode_char(channel, &Channelmode_Table[extm], argument, - what, client, pcount, pvar, bounce); - } + paracount += do_extmode_char(channel, cm, argument, what, client, pcount, pvar); break; } /* switch(*curchr) */ } /* for loop through mode letters */ - make_mode_str(channel, oldm, oldem, oldl, *pcount, pvar, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), bounce); - -#ifndef NO_OPEROVERRIDE - if ((htrig == 1) && IsUser(client)) - { - /* This is horrible. Just horrible. */ - if (!((modebuf[0] == '+' || modebuf[0] == '-') && modebuf[1] == '\0')) - { - sendto_snomask(SNO_EYES, "*** OperOverride -- %s (%s@%s) MODE %s %s %s", - client->name, client->user->username, client->user->realhost, - channel->chname, modebuf, parabuf); - } - - /* Logging Implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE,"OVERRIDE: %s (%s@%s) MODE %s %s %s", - client->name, client->user->username, client->user->realhost, - channel->chname, modebuf, parabuf); - - htrig = 0; - opermode = 0; /* stop double override notices... but is this ok??? -- Syzop */ - } -#endif + mlm = make_mode_str(client, channel, oldem, *pcount, pvar); + return mlm; } /* @@ -1587,16 +1037,17 @@ void _set_mode(Channel *channel, Client *client, int parc, char *parv[], u_int * */ CMD_FUNC(_cmd_umode) { - int i; - char **p, *m; + Umode *um; + const char *m; Client *acptr; - int what, setsnomask = 0; + int what; long oldumodes = 0; - int oldsnomasks = 0; + char oldsnomask[64]; /* (small note: keep 'what' as an int. -- Syzop). */ short rpterror = 0, umode_restrict_err = 0, chk_restrict = 0, modex_err = 0; what = MODE_ADD; + *oldsnomask = '\0'; if (parc < 2) { @@ -1604,7 +1055,7 @@ CMD_FUNC(_cmd_umode) return; } - if (!(acptr = find_person(parv[1], NULL))) + if (!(acptr = find_user(parv[1], NULL))) { if (MyConnect(client)) { @@ -1622,31 +1073,24 @@ CMD_FUNC(_cmd_umode) { sendnumeric(client, RPL_UMODEIS, get_usermode_string(client)); if (client->user->snomask) - sendnumeric(client, RPL_SNOMASK, get_snomask_string(client)); + sendnumeric(client, RPL_SNOMASK, client->user->snomask); return; } userhost_save_current(client); /* save host, in case we do any +x/-x or similar */ - /* find flags already set for user */ - for (i = 0; i <= Usermode_highest; i++) - if ((client->umodes & Usermode_Table[i].mode)) - oldumodes |= Usermode_Table[i].mode; - - for (i = 0; i <= Snomask_highest; i++) - if ((client->user->snomask & Snomask_Table[i].mode)) - oldsnomasks |= Snomask_Table[i].mode; + oldumodes = client->umodes; if (RESTRICT_USERMODES && MyUser(client) && !ValidatePermissionsForPath("immune:restrict-usermodes",client,NULL,NULL,NULL)) chk_restrict = 1; - if (MyConnect(client)) - setsnomask = client->user->snomask; + if (client->user->snomask) + strlcpy(oldsnomask, client->user->snomask, sizeof(oldsnomask)); + /* * parse mode change string(s) */ - p = &parv[2]; - for (m = *p; *m; m++) + for (m = parv[2]; *m; m++) { if (chk_restrict && strchr(RESTRICT_USERMODES, *m)) { @@ -1678,7 +1122,7 @@ CMD_FUNC(_cmd_umode) if (parc >= 4 && client->user->snomask) { set_snomask(client, parv[3]); - if (client->user->snomask == 0) + if (client->user->snomask == NULL) goto def; break; } else { @@ -1686,10 +1130,10 @@ CMD_FUNC(_cmd_umode) goto def; } } - if (what == MODE_ADD) + if ((what == MODE_ADD) && IsOper(client)) { if (parc < 4) - set_snomask(client, IsOper(client) ? SNO_DEFOPER : SNO_DEFUSER); + set_snomask(client, OPER_SNOMASKS); else set_snomask(client, parv[3]); goto def; @@ -1699,7 +1143,8 @@ CMD_FUNC(_cmd_umode) case 'O': if (IsQuarantined(client->direction)) { - sendto_realops("QUARANTINE: Oper %s on server %s killed, due to quarantine", client->name, client->srvptr->name); + unreal_log(ULOG_INFO, "mode", "OPER_KILLED_QUARANTINE", client, + "QUARANTINE: Oper $client.details on server $client.user.servername killed, due to quarantine"); sendto_server(NULL, 0, 0, NULL, ":%s KILL %s :Quarantined: no oper privileges allowed", me.id, client->name); exit_client(client, NULL, "Quarantined: no oper privileges allowed"); return; @@ -1749,24 +1194,23 @@ CMD_FUNC(_cmd_umode) break; default: def: - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (*m == Usermode_Table[i].flag) + if (um->letter == *m) { - if (Usermode_Table[i].allowed) - if (!Usermode_Table[i].allowed(client,what)) + if (um->allowed && !um->allowed(client,what)) break; if (what == MODE_ADD) - client->umodes |= Usermode_Table[i].mode; + client->umodes |= um->mode; else - client->umodes &= ~Usermode_Table[i].mode; + client->umodes &= ~um->mode; break; } - else if (i == Usermode_highest && MyConnect(client) && !rpterror) - { - sendnumeric(client, ERR_UMODEUNKNOWNFLAG); - rpterror = 1; - } + } + if (!um && MyConnect(client) && !rpterror) + { + sendnumeric(client, ERR_UMODEUNKNOWNFLAG); + rpterror = 1; } break; } /* switch */ @@ -1784,35 +1228,44 @@ CMD_FUNC(_cmd_umode) int i; /* MODES */ - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].flag) - continue; - if (Usermode_Table[i].unset_on_deoper) + if (um->unset_on_deoper) { /* This is an oper mode. Is it set now and wasn't earlier? * then it needs to be stripped, as setting it is not * permitted. */ - long m = Usermode_Table[i].mode; - if ((client->umodes & m) && !(oldumodes & m)) - client->umodes &= ~Usermode_Table[i].mode; /* remove */ + if ((client->umodes & um->mode) && !(oldumodes & um->mode)) + client->umodes &= ~um->mode; /* remove */ } } /* SNOMASKS: user can delete existing but not add new ones */ - for (i = 0; i <= Snomask_highest; i++) + if (client->user->snomask) { - int sno = Snomask_Table[i].mode; + char rerun; + do { + char *p; - if (!Snomask_Table[i].flag) - continue; - /* Is it set now and wasn't earlier? Then it - * needs to be stripped, as setting it is not - * permitted. - */ - if ((client->user->snomask & sno) && !(oldsnomasks & sno)) - client->user->snomask &= ~Snomask_Table[i].mode; /* remove */ + rerun = 0; + for (p = client->user->snomask; *p; p++) + { + if (!strchr(oldsnomask, *p)) + { + /* It is set now, but was not earlier? + * Then it needs to be stripped, as setting is not permitted. + * And re-run the loop + */ + delletterfromstring(client->user->snomask, *p); + rerun = 1; + break; + } + } + } while(rerun); + /* And make sure an empty snomask ("") becomes a NULL pointer */ + if (client->user->snomask && !*client->user->snomask) + remove_all_snomasks(client); } } else { /* User isn't an ircop at all. The solution is simple: */ @@ -1876,8 +1329,9 @@ CMD_FUNC(_cmd_umode) if ((oldumodes & UMODE_OPER) && !IsOper(client) && MyConnect(client)) { list_del(&client->special_node); + if (MyUser(client)) + RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL); remove_oper_privileges(client, 0); - RunHook2(HOOKTYPE_LOCAL_OPER, client, 0); } if (!(oldumodes & UMODE_OPER) && IsOper(client)) @@ -1913,12 +1367,12 @@ CMD_FUNC(_cmd_umode) * will cause servers to update correctly. */ if (oldumodes != client->umodes) - RunHook3(HOOKTYPE_UMODE_CHANGE, client, oldumodes, client->umodes); + RunHook(HOOKTYPE_UMODE_CHANGE, client, oldumodes, client->umodes); if (dontspread == 0) send_umode_out(client, 1, oldumodes); - if (MyConnect(client) && setsnomask != client->user->snomask) - sendnumeric(client, RPL_SNOMASK, get_snomask_string(client)); + if (MyConnect(client) && client->user->snomask && strcmp(oldsnomask, client->user->snomask)) + sendnumeric(client, RPL_SNOMASK, client->user->snomask); } CMD_FUNC(cmd_mlock) @@ -1932,7 +1386,7 @@ CMD_FUNC(cmd_mlock) t = (time_t) atol(parv[1]); /* Now, try to find the channel in question */ - channel = find_channel(parv[2], NULL); + channel = find_channel(parv[2]); if (!channel) return; @@ -1943,3 +1397,117 @@ CMD_FUNC(cmd_mlock) if (IsServer(client)) set_channel_mlock(client, channel, parv[3], TRUE); } + +void mode_operoverride_msg(Client *client, Channel *channel, char *modebuf, char *parabuf) +{ + char buf[1024]; + + if (empty_mode(modebuf)) + return; + + /* Internally we have this distinction between modebuf and parabuf, + * but this makes little sense to maintain in JSON. + */ + snprintf(buf, sizeof(buf), "%s %s", modebuf, parabuf); + + unreal_log(ULOG_INFO, "operoverride", "OPEROVERRIDE_MODE", client, + "OperOverride: $client.details changed channel mode of $channel to: $channel_mode", + log_data_string("override_type", "mode"), + log_data_string("channel_mode", buf), + log_data_channel("channel", channel)); +} + +/* Deal with information requests from local users, such as: + * MODE #chan b Show the ban list + * MODE #chan e Show the ban exemption list + * MODE #chan I Show the invite exception list + * MODE #chan q Show list of channel owners + * MODE #chan a Show list of channel admins + * @returns 1 if processed as a mode list (please return), + * 0 if not (continue with the MODE as it likely is a set request). + */ +int list_mode_request(Client *client, Channel *channel, const char *req) +{ + const char *p; + Ban *ban; + Member *member; + + for (p = req; *p; p++) + if (strchr("beIqa", *p)) + break; + + if (!*p) + return 0; /* not handled, proceed with the MODE set attempt */ + + /* First, check access */ + if (strchr("beI", *p)) + { + if (!IsMember(client, channel) && !ValidatePermissionsForPath("channel:see:mode:remotebanlist",client,NULL,channel,NULL)) + { + sendnumeric(client, ERR_NOTONCHANNEL, channel->name); + return 1; /* handled */ + } + } else { + if (!IsMember(client, channel) && !ValidatePermissionsForPath("channel:see:mode:remoteownerlist",client,NULL,channel,NULL)) + { + sendnumeric(client, ERR_NOTONCHANNEL, channel->name); + return 1; /* handled */ + } + } + + switch(*p) + { + case 'b': + for (ban = channel->banlist; ban; ban = ban->next) + sendnumeric(client, RPL_BANLIST, channel->name, ban->banstr, ban->who, (long long)ban->when); + sendnumeric(client, RPL_ENDOFBANLIST, channel->name); + break; + case 'e': + for (ban = channel->exlist; ban; ban = ban->next) + sendnumeric(client, RPL_EXLIST, channel->name, ban->banstr, ban->who, (long long)ban->when); + sendnumeric(client, RPL_ENDOFEXLIST, channel->name); + break; + case 'I': + for (ban = channel->invexlist; ban; ban = ban->next) + sendnumeric(client, RPL_INVEXLIST, channel->name, ban->banstr, ban->who, (long long)ban->when); + sendnumeric(client, RPL_ENDOFINVEXLIST, channel->name); + break; + case 'q': + for (member = channel->members; member; member = member->next) + if (strchr(member->member_modes, 'q')) + sendnumeric(client, RPL_QLIST, channel->name, member->client->name); + sendnumeric(client, RPL_ENDOFQLIST, channel->name); + break; + case 'a': + for (member = channel->members; member; member = member->next) + if (strchr(member->member_modes, 'a')) + sendnumeric(client, RPL_ALIST, channel->name, member->client->name); + sendnumeric(client, RPL_ENDOFALIST, channel->name); + break; + } + + return 1; /* handled */ +} + +void _set_channel_mode(Channel *channel, char *modes, char *parameters) +{ + char buf[512]; + char *p, *param; + int myparc = 1, i; + char *myparv[512]; + + memset(&myparv, 0, sizeof(myparv)); + myparv[0] = raw_strdup(modes); + + strlcpy(buf, parameters, sizeof(buf)); + for (param = strtoken(&p, buf, " "); param; param = strtoken(&p, NULL, " ")) + myparv[myparc++] = raw_strdup(param); + myparv[myparc] = NULL; + + SetULine(&me); // hack for crash.. set ulined so no access checks. + do_mode(channel, &me, NULL, myparc, (const char **)myparv, 0, 0); + ClearULine(&me); // and clear it again.. + + for (i = 0; i < myparc; i++) + safe_free(myparv[i]); +} diff --git a/src/modules/monitor.c b/src/modules/monitor.c new file mode 100644 index 0000000..e88e809 --- /dev/null +++ b/src/modules/monitor.c @@ -0,0 +1,232 @@ +/* + * IRC - Internet Relay Chat, src/modules/monitor.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +#define MSG_MONITOR "MONITOR" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +CMD_FUNC(cmd_monitor); +char *monitor_isupport_param(void); +int monitor_nickchange(Client *client, MessageTag *mtags, const char *newnick); +int monitor_post_nickchange(Client *client, MessageTag *mtags, const char *oldnick); +int monitor_quit(Client *client, MessageTag *mtags, const char *comment); +int monitor_connect(Client *client); +int monitor_notification(Client *client, Watch *watch, Link *lp, int event); + +ModuleHeader MOD_HEADER + = { + "monitor", + "5.0", + "command /monitor", + "UnrealIRCd Team", + "unrealircd-6", + }; + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + + CommandAdd(modinfo->handle, MSG_MONITOR, cmd_monitor, 2, CMD_USER); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_NICKCHANGE, 0, monitor_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_NICKCHANGE, 0, monitor_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_POST_LOCAL_NICKCHANGE, 0, monitor_post_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_POST_REMOTE_NICKCHANGE, 0, monitor_post_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, monitor_quit); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, monitor_quit); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, monitor_connect); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, monitor_connect); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + ISupportAdd(modinfo->handle, "MONITOR", monitor_isupport_param()); + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +char *monitor_isupport_param(void) +{ + /* i find it unlikely for a client to use WATCH and MONITOR at the same time, so keep a single limit for both */ + return STR(MAXWATCH); +} + +int monitor_nickchange(Client *client, MessageTag *mtags, const char *newnick) +{ + if (!smycmp(client->name, newnick)) // new nick is same as old one, maybe the case changed + return 0; + + watch_check(client, WATCH_EVENT_OFFLINE, monitor_notification); + return 0; +} + +int monitor_post_nickchange(Client *client, MessageTag *mtags, const char *oldnick) +{ + if (!smycmp(client->name, oldnick)) // new nick is same as old one, maybe the case changed + return 0; + + watch_check(client, WATCH_EVENT_ONLINE, monitor_notification); + return 0; +} + +int monitor_quit(Client *client, MessageTag *mtags, const char *comment) +{ + watch_check(client, WATCH_EVENT_OFFLINE, monitor_notification); + return 0; +} + +int monitor_connect(Client *client) +{ + watch_check(client, WATCH_EVENT_ONLINE, monitor_notification); + return 0; +} + +int monitor_notification(Client *client, Watch *watch, Link *lp, int event) +{ + if (!(lp->flags & WATCH_FLAG_TYPE_MONITOR)) + return 0; + + switch (event) + { + case WATCH_EVENT_ONLINE: + sendnumeric(lp->value.client, RPL_MONONLINE, client->name, client->user->username, GetHost(client)); + break; + case WATCH_EVENT_OFFLINE: + sendnumeric(lp->value.client, RPL_MONOFFLINE, client->name); + break; + default: + break; /* may be handled by other modules */ + } + + return 0; +} + +void send_status(Client *client, MessageTag *recv_mtags, const char *nick) +{ + MessageTag *mtags = NULL; + Client *user; + user = find_user(nick, NULL); + new_message(client, recv_mtags, &mtags); + if (!user){ + sendnumeric(client, RPL_MONOFFLINE, nick); + } else { + sendnumeric(client, RPL_MONONLINE, user->name, user->user->username, GetHost(user)); + } + free_message_tags(mtags); +} + +#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i) +#define WATCH(client) (moddata_local_client(client, watchListMD).ptr) + +CMD_FUNC(cmd_monitor) +{ + char request[BUFSIZE]; + char cmd; + char *s, *p = NULL; + int i; + int toomany = 0; + Link *lp; + + if (!MyUser(client)) + return; + + if (parc < 2 || BadPtr(parv[1])) + cmd = 'l'; + else + cmd = tolower(*parv[1]); + + ModDataInfo *watchCounterMD = findmoddata_byname("watchCount", MODDATATYPE_LOCAL_CLIENT); + ModDataInfo *watchListMD = findmoddata_byname("watchList", MODDATATYPE_LOCAL_CLIENT); + + if (!watchCounterMD || !watchListMD) + { + unreal_log(ULOG_ERROR, "monitor", "WATCH_BACKEND_MISSING", NULL, + "[monitor] moddata unavailable. Is the 'watch-backend' module loaded?"); + sendnotice(client, "MONITOR command is not available at this moment. Please try again later."); + return; + } + + switch(cmd) + { + case 'c': + watch_del_list(client, WATCH_FLAG_TYPE_MONITOR); + break; + case 'l': + lp = WATCH(client); + while (lp) + { + if (!(lp->flags & WATCH_FLAG_TYPE_MONITOR)) + { + lp = lp->next; + continue; /* this one is not ours */ + } + sendnumeric(client, RPL_MONLIST, lp->value.wptr->nick); + lp = lp->next; + } + + sendnumeric(client, RPL_ENDOFMONLIST); + break; + case 's': + lp = WATCH(client); + while (lp) + { + if (!(lp->flags & WATCH_FLAG_TYPE_MONITOR)) + { + lp = lp->next; + continue; /* this one is not ours */ + } + send_status(client, recv_mtags, lp->value.wptr->nick); + lp = lp->next; + } + break; + case '-': + case '+': + if (parc < 3 || BadPtr(parv[2])) + return; + strlcpy(request, parv[2], sizeof(request)); + for (s = strtoken(&p, request, ","); s; s = strtoken(&p, NULL, ",")) + { + if (cmd == '-') { + watch_del(s, client, WATCH_FLAG_TYPE_MONITOR); + } else { + if (WATCHES(client) >= MAXWATCH) + { + sendnumeric(client, ERR_MONLISTFULL, MAXWATCH, s); + continue; + } + if (do_nick_name(s)) + watch_add(s, client, WATCH_FLAG_TYPE_MONITOR); + send_status(client, recv_mtags, s); + } + } + break; + } +} + diff --git a/src/modules/motd.c b/src/modules/motd.c index 0397604..80b22a3 100644 --- a/src/modules/motd.c +++ b/src/modules/motd.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /motd", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -66,10 +66,10 @@ CMD_FUNC(cmd_motd) if (IsServer(client)) return; - if (hunt_server(client, recv_mtags, ":%s MOTD :%s", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "MOTD", 1, parc, parv) != HUNTED_ISME) { if (MyUser(client)) - client->local->since += 15; + add_fake_lag(client, 15000); return; } @@ -101,7 +101,7 @@ CMD_FUNC(cmd_motd) } motdline = NULL; - if(themotd) + if (themotd) motdline = themotd->lines; while (motdline) { diff --git a/src/modules/names.c b/src/modules/names.c index 405405f..e61bce0 100644 --- a/src/modules/names.c +++ b/src/modules/names.c @@ -24,6 +24,9 @@ CMD_FUNC(cmd_names); +long CAP_MULTI_PREFIX = 0L; +long CAP_USERHOST_IN_NAMES = 0L; + #define MSG_NAMES "NAMES" ModuleHeader MOD_HEADER @@ -32,11 +35,19 @@ ModuleHeader MOD_HEADER "5.0", "command /names", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() { + ClientCapabilityInfo c; + memset(&c, 0, sizeof(c)); + c.name = "multi-prefix"; + ClientCapabilityAdd(modinfo->handle, &c, &CAP_MULTI_PREFIX); + memset(&c, 0, sizeof(c)); + c.name = "userhost-in-names"; + ClientCapabilityAdd(modinfo->handle, &c, &CAP_USERHOST_IN_NAMES); + CommandAdd(modinfo->handle, MSG_NAMES, cmd_names, MAXPARA, CMD_USER|CMD_SERVER); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -66,8 +77,8 @@ static char buf[BUFSIZE]; #define TRUNCATED_NAMES 64 CMD_FUNC(cmd_names) { - int multiprefix = (MyConnect(client) && HasCapability(client, "multi-prefix")); - int uhnames = (MyConnect(client) && HasCapability(client, "userhost-in-names")); // cache UHNAMES support + int multiprefix = (MyConnect(client) && HasCapabilityFast(client, CAP_MULTI_PREFIX)); + int uhnames = (MyConnect(client) && HasCapabilityFast(client, CAP_USERHOST_IN_NAMES)); // cache UHNAMES support int bufLen = NICKLEN + (!uhnames ? 0 : (1 + USERLEN + 1 + HOSTLEN)); int mlen = strlen(me.name) + bufLen + 7; Channel *channel; @@ -75,7 +86,7 @@ CMD_FUNC(cmd_names) int member; Member *cm; int idx, flag = 1, spos; - char *s, *para = parv[1]; + const char *para = parv[1], *s; char nuhBuffer[NICKLEN+USERLEN+HOSTLEN+3]; if (parc < 2 || !MyConnect(client)) @@ -88,16 +99,12 @@ CMD_FUNC(cmd_names) { if (*s == ',') { - if (strlen(para) > TRUNCATED_NAMES) - para[TRUNCATED_NAMES] = '\0'; - sendto_realops("names abuser %s %s", - get_client_name(client, FALSE), para); sendnumeric(client, ERR_TOOMANYTARGETS, s+1, 1, "NAMES"); return; } } - channel = find_channel(para, NULL); + channel = find_channel(para); if (!channel || (!ShowChannel(client, channel) && !ValidatePermissionsForPath("channel:see:names:secret",client,NULL,channel,NULL))) { @@ -108,6 +115,8 @@ CMD_FUNC(cmd_names) /* cache whether this user is a member of this channel or not */ member = IsMember(client, channel); + // FIXME: consider rewriting this whole thing to get rid of pointer juggling and stuff. + if (PubChannel(channel)) buf[0] = '='; else if (SecretChannel(channel)) @@ -117,7 +126,7 @@ CMD_FUNC(cmd_names) idx = 1; buf[idx++] = ' '; - for (s = channel->chname; *s; s++) + for (s = channel->name; *s; s++) buf[idx++] = *s; buf[idx++] = ' '; buf[idx++] = ':'; @@ -140,34 +149,14 @@ CMD_FUNC(cmd_names) if (!multiprefix) { - /* Standard NAMES reply */ -#ifdef PREFIX_AQ - if (cm->flags & CHFL_CHANOWNER) - buf[idx++] = '~'; - else if (cm->flags & CHFL_CHANADMIN) - buf[idx++] = '&'; - else -#endif - if (cm->flags & CHFL_CHANOP) - buf[idx++] = '@'; - else if (cm->flags & CHFL_HALFOP) - buf[idx++] = '%'; - else if (cm->flags & CHFL_VOICE) - buf[idx++] = '+'; + /* Standard NAMES reply (single character) */ + char c = mode_to_prefix(*cm->member_modes); + if (c) + buf[idx++] = c; } else { /* NAMES reply with all rights included (multi-prefix / NAMESX) */ -#ifdef PREFIX_AQ - if (cm->flags & CHFL_CHANOWNER) - buf[idx++] = '~'; - if (cm->flags & CHFL_CHANADMIN) - buf[idx++] = '&'; -#endif - if (cm->flags & CHFL_CHANOP) - buf[idx++] = '@'; - if (cm->flags & CHFL_HALFOP) - buf[idx++] = '%'; - if (cm->flags & CHFL_VOICE) - buf[idx++] = '+'; + strcpy(&buf[idx], modes_to_prefix(cm->member_modes)); + idx += strlen(&buf[idx]); } if (!uhnames) { @@ -187,7 +176,7 @@ CMD_FUNC(cmd_names) buf[idx++] = ' '; buf[idx] = '\0'; flag = 1; - if (mlen + idx + bufLen > BUFSIZE - 7) + if (mlen + idx + bufLen + MEMBERMODESLEN >= BUFSIZE - 1) { sendnumeric(client, RPL_NAMREPLY, buf); idx = spos; diff --git a/src/modules/netinfo.c b/src/modules/netinfo.c index a1fac6b..9cbd17d 100644 --- a/src/modules/netinfo.c +++ b/src/modules/netinfo.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /netinfo", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -61,7 +61,7 @@ MOD_UNLOAD() * parv[1] = max global count * parv[2] = time of end sync * parv[3] = unreal protocol using (numeric) - * parv[4] = cloak-crc (> u2302) + * parv[4] = cloak key check (> u2302) * parv[5] = free(**) * parv[6] = free(**) * parv[7] = free(**) @@ -70,7 +70,6 @@ MOD_UNLOAD() CMD_FUNC(cmd_netinfo) { long lmax; - time_t xx; long endsync, protocol; char buf[512]; @@ -83,7 +82,8 @@ CMD_FUNC(cmd_netinfo) if (IsNetInfo(client)) { - sendto_realops("Already got NETINFO from Link %s", client->name); + unreal_log(ULOG_WARNING, "link", "NETINFO_ALREADY_RECEIVED", client, + "Got NETINFO from server $client, but we already received it earlier!"); return; } @@ -96,46 +96,46 @@ CMD_FUNC(cmd_netinfo) if (lmax > irccounts.global_max) { irccounts.global_max = lmax; - sendto_realops("Max Global Count is now %li (set by link %s)", - lmax, client->name); + unreal_log(ULOG_INFO, "link", "NEW_GLOBAL_RECORD", client, + "Record global users is now $record_global_users (set by server $client)", + log_data_integer("record_global_users", lmax)); } - xx = TStime(); - if ((xx - endsync) < -2) - { - char *emsg = ""; - if (xx - endsync < -10) - { - emsg = " [\002PLEASE SYNC YOUR CLOCKS!\002]"; - } - sendto_umode_global(UMODE_OPER, - "Possible negative TS split at link %s (%lld - %lld = %lld)%s", - client->name, (long long)(xx), (long long)(endsync), (long long)(xx - endsync), emsg); - } - sendto_umode_global(UMODE_OPER, - "Link %s -> %s is now synced [secs: %lld recv: %ld.%hu sent: %ld.%hu]", - client->name, me.name, (long long)(TStime() - endsync), client->local->receiveK, - client->local->receiveB, client->local->sendK, client->local->sendB); + unreal_log(ULOG_INFO, "link", "SERVER_SYNCED", client, + "Link $client -> $me is now synced " + "[secs: $synced_after_seconds, recv: $received_bytes, sent: $sent_bytes]", + log_data_client("me", &me), + log_data_integer("synced_after_seconds", TStime() - endsync), + log_data_integer("received_bytes", client->local->traffic.bytes_received), + log_data_integer("sent_bytes", client->local->traffic.bytes_sent)); - if (!(strcmp(ircnetwork, parv[8]) == 0)) + if (!(strcmp(NETWORK_NAME, parv[8]) == 0)) { - sendto_umode_global(UMODE_OPER, - "Network name mismatch from link %s (%s != %s)", - client->name, parv[8], ircnetwork); + unreal_log(ULOG_WARNING, "link", "NETWORK_NAME_MISMATCH", client, + "Network name mismatch: server $client has '$their_network_name', " + "server $me has '$our_network_name'.", + log_data_client("me", &me), + log_data_string("their_network_name", parv[8]), + log_data_string("our_network_name", NETWORK_NAME)); } if ((protocol != UnrealProtocol) && (protocol != 0)) { - sendto_umode_global(UMODE_OPER, - "Link %s is running Protocol %li while %s is running %d", - client->name, protocol, me.name, UnrealProtocol); + unreal_log(ULOG_INFO, "link", "LINK_PROTOCOL_MISMATCH", client, + "Server $client is running UnrealProtocol $their_link_protocol, " + "server $me uses $our_link_protocol.", + log_data_client("me", &me), + log_data_integer("their_link_protocol", protocol), + log_data_integer("our_link_protocol", UnrealProtocol)); } - strlcpy(buf, CLOAK_KEYCRC, sizeof(buf)); + strlcpy(buf, CLOAK_KEY_CHECKSUM, sizeof(buf)); if (*parv[4] != '*' && strcmp(buf, parv[4])) { - sendto_realops - ("Link %s has a DIFFERENT CLOAK KEY - %s != %s. \002YOU SHOULD CORRECT THIS ASAP\002.", - client->name, parv[4], buf); + unreal_log(ULOG_WARNING, "link", "CLOAK_KEY_MISMATCH", client, + "Server $client has a DIFFERENT CLOAK KEY (OR METHOD)!!! You should fix this ASAP!\n" + "When the cloaking configuration is different on servers, this will cause " + "channel bans on cloaked hosts/IPs not to work correctly, " + "meaning users can bypass channel bans!"); } SetNetInfo(client); } diff --git a/src/modules/nick.c b/src/modules/nick.c index 22f4340..377b375 100644 --- a/src/modules/nick.c +++ b/src/modules/nick.c @@ -28,17 +28,35 @@ ModuleHeader MOD_HEADER "5.0", "command /nick", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; +/* Defines */ + +#define NICKCOL_EQUAL 0 +#define NICKCOL_NEW_WON 1 +#define NICKCOL_EXISTING_WON 2 + +/* Assume that on collision a NICK is in flight and the other server will take + * the exact same decision we would do, and thus we don't send a KILL to cptr? + * This works great with this code, seems to kill the correct person and not + * cause desyncs even without UID/SID. HOWEVER.. who knows what code the other servers run? + * Should use UID/SID anyway, then this whole problem doesn't exist. + */ +#define ASSUME_NICK_IN_FLIGHT + +/* Variables */ +static char buf[BUFSIZE]; +static char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; + /* Forward declarations */ CMD_FUNC(cmd_nick); CMD_FUNC(cmd_nick_local); CMD_FUNC(cmd_nick_remote); CMD_FUNC(cmd_uid); -int _register_user(Client *client, char *nick, char *username, char *umode, char *virthost, char *ip); -void nick_collision(Client *cptr, char *newnick, char *newid, Client *new, Client *existing, int type); -int AllowClient(Client *client, char *username); +int _register_user(Client *client); +void nick_collision(Client *cptr, const char *newnick, const char *newid, Client *new, Client *existing, int type); +int AllowClient(Client *client); MOD_TEST() { @@ -65,67 +83,52 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static char buf[BUFSIZE]; -static char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; +/** Hmm.. don't we already have such a function? */ +void set_user_modes_dont_spread(Client *client, const char *umode) +{ + const char *args[4]; -#define NICKCOL_EQUAL 0 -#define NICKCOL_NEW_WON 1 -#define NICKCOL_EXISTING_WON 2 + args[0] = client->name; + args[1] = client->name; + args[2] = umode; + args[3] = NULL; -/* Assume that on collision a NICK is in flight and the other server will take - * the exact same decision we would do, and thus we don't send a KILL to cptr? - * This works great with this code, seems to kill the correct person and not - * cause desyncs even without UID/SID. HOWEVER.. who knows what code the other servers run? - * Should use UID/SID anyway, then this whole problem doesn't exist. - */ -#define ASSUME_NICK_IN_FLIGHT + dontspread = 1; + do_cmd(client, NULL, "MODE", 3, args); + dontspread = 0; +} -/** The NICK command. - * In UnrealIRCd 4/5 this is only used in 2 cases: - * 1) A local user setting or changing the nick name ("NICK xyz") - * 2) A remote user changing their nick name (": NICK ") - */ +/** Remote client (already fully registered) changing their nick */ CMD_FUNC(cmd_nick_remote) { TKL *tklban; int ishold; Client *acptr; char nick[NICKLEN + 2]; + char oldnick[NICKLEN + 1]; time_t lastnick = 0; int differ = 1; unsigned char removemoder = (client->umodes & UMODE_REGNICK) ? 1 : 0; - char *nickid = (IsUser(client) && *client->id) ? client->id : NULL; - Client *cptr = client->direction; /* Pending a complete overhaul... (TODO) */ MessageTag *mtags = NULL; - if ((parc < 2) || BadPtr(parv[1])) - { - sendnumeric(client, ERR_NONICKNAMEGIVEN); - return; - } - - if (!IsUser(client)) - { - /* Old NICK protocol for introducing users, not supported as you should use UID */ - sendto_umode_global(UMODE_OPER, "Old NICK protocol detected from server %s, should use UID instead -- delinking", - client->name); - exit_client(cptr->direction, NULL, "Old NICK protocol detected, bad, use UID!"); - return; - } + /* 'client' is always the fully registered user doing the nick change */ strlcpy(nick, parv[1], NICKLEN + 1); + strlcpy(oldnick, client->name, sizeof(oldnick)); if (parc > 2) lastnick = atol(parv[2]); - if (!do_remote_nick_name(nick)) + if (!do_remote_nick_name(nick) || !strcasecmp("ircd", nick) || !strcasecmp("irc", nick)) { ircstats.is_kill++; - sendto_umode(UMODE_OPER, "Bad Nick: %s From: %s %s", - parv[1], client->name, get_client_name(cptr, FALSE)); + unreal_log(ULOG_ERROR, "nick", "BAD_NICK_REMOTE", client, + "Server link $server tried to introduce bad nick '$nick' -- rejected.", + log_data_string("nick", parv[1]), + log_data_client("server", client->direction)); mtags = NULL; new_message(client, NULL, &mtags); - sendto_one(cptr, mtags, ":%s KILL %s :Illegal nick name", me.id, client->id); + sendto_one(client, mtags, ":%s KILL %s :Illegal nick name", me.id, client->id); SetKilled(client); exit_client(client, mtags, "Illegal nick name"); free_message_tags(mtags); @@ -133,26 +136,16 @@ CMD_FUNC(cmd_nick_remote) return; } - if (!strcasecmp("ircd", nick) || !strcasecmp("irc", nick)) - { - sendto_umode(UMODE_OPER, "Bad Reserved Nick: %s From: %s %s", - parv[1], client->name, get_client_name(cptr, FALSE)); - mtags = NULL; - new_message(client, NULL, &mtags); - sendto_one(cptr, mtags, ":%s KILL %s :Reserved nick name", me.id, client->id); - SetKilled(client); - exit_client(client, mtags, "Reserved nick name"); - free_message_tags(mtags); - mtags = NULL; - return; - } - /* Check Q-lines / ban nick */ if (!IsULine(client) && (tklban = find_qline(client, nick, &ishold)) && !ishold) { - /* Remote user changing nick - warning only */ - sendto_snomask(SNO_QLINE, "Q-Lined nick %s from %s on %s", nick, - client->name, client->srvptr ? client->srvptr->name : ""); + unreal_log(ULOG_INFO, "nick", "QLINE_NICK_REMOTE", client, + "Banned nick $nick [$ip] from server $server ($reason)", + log_data_string("nick", parv[1]), + log_data_string("ip", GetIP(client)), + log_data_client("server", client->uplink), + log_data_string("reason", tklban->ptr.nameban->reason)); + /* Let it through */ } if ((acptr = find_client(nick, NULL))) @@ -178,26 +171,21 @@ CMD_FUNC(cmd_nick_remote) differ = (mycmp(acptr->user->username, client->user->username) || mycmp(acptr->user->realhost, client->user->realhost)); - sendto_umode(UMODE_OPER, "Nick change collision from %s to %s (%s %lld <- %s %lld)", - client->name, acptr->name, acptr->direction->name, - (long long)acptr->lastnick, - client->direction->name, (long long)lastnick); - if (!(parc > 2) || lastnick == acptr->lastnick) { - nick_collision(client, parv[1], nickid, client, acptr, NICKCOL_EQUAL); + nick_collision(client, parv[1], client->id, client, acptr, NICKCOL_EQUAL); return; /* Now that I killed them both, ignore the NICK */ } else if ((differ && (acptr->lastnick > lastnick)) || (!differ && (acptr->lastnick < lastnick))) { - nick_collision(client, parv[1], nickid, client, acptr, NICKCOL_NEW_WON); + nick_collision(client, parv[1], client->id, client, acptr, NICKCOL_NEW_WON); /* fallthrough: their user won, continue and proceed with the nick change */ } else if ((differ && (acptr->lastnick < lastnick)) || (!differ && (acptr->lastnick > lastnick))) { - nick_collision(client, parv[1], nickid, client, acptr, NICKCOL_EXISTING_WON); + nick_collision(client, parv[1], client->id, client, acptr, NICKCOL_EXISTING_WON); return; /* their user lost, ignore the NICK */ } else { @@ -208,53 +196,50 @@ CMD_FUNC(cmd_nick_remote) mtags = NULL; - /* Existing client nick-changing */ - if (!IsULine(client)) - sendto_snomask(SNO_FNICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s", - client->name, client->user->username, client->user->realhost, nick); + { + unreal_log(ULOG_INFO, "nick", "REMOTE_NICK_CHANGE", client, + "Client $client.details has changed their nickname to $new_nick", + log_data_string("new_nick", nick)); + } new_message(client, recv_mtags, &mtags); - RunHook3(HOOKTYPE_REMOTE_NICKCHANGE, client, mtags, nick); + RunHook(HOOKTYPE_REMOTE_NICKCHANGE, client, mtags, nick); client->lastnick = lastnick ? lastnick : TStime(); add_history(client, 1); sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld", client->id, nick, (long long)client->lastnick); sendto_local_common_channels(client, client, 0, mtags, ":%s NICK :%s", client->name, nick); - free_message_tags(mtags); if (removemoder) client->umodes &= ~UMODE_REGNICK; /* Finally set new nick name. */ del_from_client_hash_table(client->name, client); - hash_check_watch(client, RPL_LOGOFF); - - strcpy(client->name, nick); + strlcpy(client->name, nick, sizeof(client->name)); add_to_client_hash_table(nick, client); - hash_check_watch(client, RPL_LOGON); + RunHook(HOOKTYPE_POST_REMOTE_NICKCHANGE, client, mtags, oldnick); + free_message_tags(mtags); } +/* Local user: either setting their nick for the first time (registration) + * or changing their nick (fully registered already, or not) + */ CMD_FUNC(cmd_nick_local) { TKL *tklban; int ishold; Client *acptr; - char nick[NICKLEN + 2], descbuf[BUFSIZE]; + char nick[NICKLEN + 2]; + char oldnick[NICKLEN + 1]; + char descbuf[BUFSIZE]; Membership *mp; - long lastnick = 0l; - int differ = 1, update_watch = 1; + int newuser = 0; unsigned char removemoder = (client->umodes & UMODE_REGNICK) ? 1 : 0; Hook *h; - int i = 0; - char *nickid = (IsUser(client) && *client->id) ? client->id : NULL; - Client *cptr = client->direction; /* Pending a complete overhaul... (TODO) */ + int ret; - if ((parc < 2) || BadPtr(parv[1])) - { - sendnumeric(client, ERR_NONICKNAMEGIVEN); - return; - } + strlcpy(oldnick, client->name, sizeof(oldnick)); /* Enforce minimum nick length */ if (iConf.min_nick_length && !IsOper(client) && !IsULine(client) && strlen(parv[1]) < iConf.min_nick_length) @@ -299,27 +284,21 @@ CMD_FUNC(cmd_nick_local) } if (!ValidatePermissionsForPath("immune:server-ban:ban-nick",client,NULL,NULL,nick)) { - client->local->since += 4; /* lag them up */ + add_fake_lag(client, 4000); /* lag them up */ sendnumeric(client, ERR_ERRONEUSNICKNAME, nick, tklban->ptr.nameban->reason); - sendto_snomask(SNO_QLINE, "Forbidding Q-lined nick %s from %s (%s)", - nick, get_client_name(cptr, FALSE), tklban->ptr.nameban->reason); + unreal_log(ULOG_INFO, "nick", "QLINE_NICK_LOCAL_ATTEMPT", client, + "Attempt to use banned nick $nick [$ip] blocked ($reason)", + log_data_string("nick", parv[1]), + log_data_string("ip", GetIP(client)), + log_data_client("server", client->uplink), + log_data_string("reason", tklban->ptr.nameban->reason)); return; /* NICK message ignored */ } /* fallthrough for ircops that have sufficient privileges */ } - /* set::anti-flood::nick-flood */ - if (client->user && - !ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL) && - flood_limit_exceeded(client, FLD_NICK)) - { - /* Throttle... */ - sendnumeric(client, ERR_NCHANGETOOFAST, nick); - return; - } - if (!ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL)) - cptr->local->since += 3; /* Nick-flood prot. -Donwulff */ + add_fake_lag(client, 3000); if ((acptr = find_client(nick, NULL))) { @@ -350,9 +329,21 @@ CMD_FUNC(cmd_nick_local) } } + /* set::anti-flood::nick-flood */ + if (client->user && + !ValidatePermissionsForPath("immune:nick-flood",client,NULL,NULL,NULL) && + flood_limit_exceeded(client, FLD_NICK)) + { + /* Throttle... */ + sendnumeric(client, ERR_NCHANGETOOFAST, nick); + return; + } + /* New local client? */ if (!client->name[0]) { + newuser = 1; + if (iConf.ping_cookie) { /* @@ -380,7 +371,7 @@ CMD_FUNC(cmd_nick_local) sendto_one(client, NULL, ":IRC!IRC@%s PRIVMSG %s :\1VERSION\1", me.name, nick); client->lastnick = TStime(); - if (!register_user(client, nick, client->user->username, NULL, NULL, NULL)) + if (!register_user(client)) { if (IsDead(client)) return; @@ -389,7 +380,6 @@ CMD_FUNC(cmd_nick_local) */ } else { /* New user! */ - update_watch = 0; /* already done in register_user() */ strlcpy(nick, client->name, sizeof(nick)); /* don't ask, but I need this. do not remove! -- Syzop */ } } @@ -397,6 +387,7 @@ CMD_FUNC(cmd_nick_local) if (MyUser(client)) { MessageTag *mtags = NULL; + int ret; /* Existing client nick-changing */ @@ -409,38 +400,40 @@ CMD_FUNC(cmd_nick_local) */ for (mp = client->user->channel; mp; mp = mp->next) { - if (!is_skochanop(client, mp->channel) && is_banned(client, mp->channel, BANCHK_NICK, NULL, NULL)) + int ret = HOOK_CONTINUE; + Hook *h; + if (!check_channel_access(client, mp->channel, "hoaq") && is_banned(client, mp->channel, BANCHK_NICK, NULL, NULL)) { sendnumeric(client, ERR_BANNICKCHANGE, - mp->channel->chname); + mp->channel->name); return; } - if (CHECK_TARGET_NICK_BANS && !is_skochanop(client, mp->channel) && is_banned_with_nick(client, mp->channel, BANCHK_NICK, nick, NULL, NULL)) + if (CHECK_TARGET_NICK_BANS && !check_channel_access(client, mp->channel, "hoaq") && is_banned_with_nick(client, mp->channel, BANCHK_NICK, nick, NULL, NULL)) { - sendnumeric(client, ERR_BANNICKCHANGE, mp->channel->chname); + sendnumeric(client, ERR_BANNICKCHANGE, mp->channel->name); return; } for (h = Hooks[HOOKTYPE_CHAN_PERMIT_NICK_CHANGE]; h; h = h->next) { - i = (*(h->func.intfunc))(client,mp->channel); - if (i != HOOK_CONTINUE) + ret = (*(h->func.intfunc))(client,mp->channel); + if (ret != HOOK_CONTINUE) break; } - if (i == HOOK_DENY) + if (ret == HOOK_DENY) { - sendnumeric(client, ERR_NONICKCHANGE, - mp->channel->chname); + sendnumeric(client, ERR_NONICKCHANGE, mp->channel->name); return; } } - sendto_snomask(SNO_NICKCHANGE, "*** %s (%s@%s) has changed their nickname to %s", - client->name, client->user->username, client->user->realhost, nick); + unreal_log(ULOG_INFO, "nick", "LOCAL_NICK_CHANGE", client, + "Client $client.details has changed their nickname to $new_nick", + log_data_string("new_nick", nick)); new_message(client, recv_mtags, &mtags); - RunHook3(HOOKTYPE_LOCAL_NICKCHANGE, client, mtags, nick); + RunHook(HOOKTYPE_LOCAL_NICKCHANGE, client, mtags, nick); client->lastnick = TStime(); add_history(client, 1); sendto_server(client, 0, 0, mtags, ":%s NICK %s %lld", @@ -456,8 +449,6 @@ CMD_FUNC(cmd_nick_local) } del_from_client_hash_table(client->name, client); - if (update_watch && IsUser(client)) - hash_check_watch(client, RPL_LOGOFF); strlcpy(client->name, nick, sizeof(client->name)); add_to_client_hash_table(nick, client); @@ -466,11 +457,11 @@ CMD_FUNC(cmd_nick_local) snprintf(descbuf, sizeof(descbuf), "Client: %s", nick); fd_desc(client->local->fd, descbuf); - if (update_watch && IsUser(client)) - hash_check_watch(client, RPL_LOGON); - if (removemoder && MyUser(client)) sendto_one(client, NULL, ":%s MODE %s :-r", me.name, client->name); + + if (MyUser(client) && !newuser) + RunHook(HOOKTYPE_POST_LOCAL_NICKCHANGE, client, recv_mtags, oldnick); } /* @@ -481,7 +472,7 @@ CMD_FUNC(cmd_nick_local) ** parv[4] = username ** parv[5] = hostname ** parv[6] = UID -** parv[7] = servicestamp +** parv[7] = account name (SVID) ** parv[8] = umodes ** parv[9] = virthost, * if none ** parv[10] = cloaked host, * if none @@ -498,9 +489,10 @@ CMD_FUNC(cmd_uid) Client *acptr, *serv = NULL; Client *acptrs; char nick[NICKLEN + 1]; - long lastnick = 0l; + long lastnick = 0; int differ = 1; - char *hostname, *username, *sstamp, *umodes, *virthost, *ip, *realname; + const char *hostname, *username, *sstamp, *umodes, *virthost, *ip_raw, *realname; + const char *ip = NULL; if (parc < 13) { @@ -518,65 +510,103 @@ CMD_FUNC(cmd_uid) } strlcpy(nick, parv[1], sizeof(nick)); + hostname = parv[5]; + sstamp = parv[7]; + username = parv[4]; + umodes = parv[8]; + virthost = parv[9]; + ip_raw = parv[11]; + realname = parv[12]; /* Do some *MINIMAL* nick name checking for remote nicknames. * This will only catch things that severely break things. -- Syzop */ - if (!do_remote_nick_name(nick)) + if (!do_remote_nick_name(nick) || !strcasecmp("ircd", nick) || !strcasecmp("irc", nick)) { - sendnumeric(client, ERR_ERRONEUSNICKNAME, parv[1], "Illegal characters"); + unreal_log(ULOG_ERROR, "nick", "BAD_NICK_REMOTE", client->uplink, + "Server link $client tried to introduce bad nick '$nick' -- rejected.", + log_data_string("nick", parv[1])); + sendnumeric(client, ERR_ERRONEUSNICKNAME, parv[1], "Illegal nick name"); ircstats.is_kill++; - sendto_umode(UMODE_OPER, "Bad Nick: %s From: %s %s", - parv[1], client->name, get_client_name(client, FALSE)); /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ sendto_one(client, NULL, ":%s KILL %s :Bad nick", me.id, parv[1]); return; } + if (!valid_uid(parv[6]) || strncmp(parv[6], client->id, 3)) + { + ircstats.is_kill++; + unreal_log(ULOG_ERROR, "link", "BAD_UID", client, + "Server link $client ($sid) used bad UID $uid in UID command.", + log_data_string("sid", client->id), + log_data_string("uid", parv[6])); + /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ + sendto_one(client, NULL, ":%s KILL %s :Bad UID", me.id, parv[6]); + return; + } + + if (!valid_host(hostname, 0)) + { + ircstats.is_kill++; + unreal_log(ULOG_ERROR, "link", "BAD_HOSTNAME", client, + "Server link $client ($client.id) introduced user $nick with bad host name: $bad_hostname.", + log_data_string("nick", nick), + log_data_string("bad_hostname", hostname)); + /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ + sendto_one(client, NULL, ":%s KILL %s :Bad hostname", me.id, parv[6]); + return; + } + + if (strcmp(virthost, "*") && !valid_host(virthost, 0)) + { + ircstats.is_kill++; + unreal_log(ULOG_ERROR, "link", "BAD_HOSTNAME", client, + "Server link $client ($client.id) introduced user $nick with bad virtual hostname: $bad_hostname.", + log_data_string("nick", nick), + log_data_string("bad_hostname", virthost)); + /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ + sendto_one(client, NULL, ":%s KILL %s :Bad virtual host", me.id, parv[6]); + return; + } + + if (strcmp(ip_raw, "*")) + { + if (!(ip = decode_ip(ip_raw))) + { + ircstats.is_kill++; + unreal_log(ULOG_ERROR, "link", "BAD_IP", client, + "Server link $client ($client.id) introduced user $nick with bad IP: $bad_ip.", + log_data_string("nick", nick), + log_data_string("bad_ip", ip_raw)); + /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ + sendto_one(client, NULL, ":%s KILL %s :Bad IP in UID command", me.id, parv[6]); + return; + } + } + /* Kill quarantined opers early... */ if (IsQuarantined(client->direction) && strchr(parv[8], 'o')) { ircstats.is_kill++; /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ - sendto_one(client, NULL, ":%s KILL %s :Quarantined: no oper privileges allowed", - me.id, parv[1]); - sendto_umode_global(UMODE_OPER, "QUARANTINE: Oper %s on server %s killed, due to quarantine", - parv[1], client->name); - return; - } - - /* This one is never allowed, even from remotes */ - if (!strcasecmp("ircd", nick) || !strcasecmp("irc", nick)) - { - sendnumeric(client, ERR_ERRONEUSNICKNAME, nick, "Reserved for internal IRCd purposes"); - sendto_one(client, NULL, ":%s KILL %s :Bad reserved nick", me.id, parv[1]); + unreal_log(ULOG_INFO, "link", "OPER_KILLED_QUARANTINE", NULL, + "QUARANTINE: Oper $nick on server $server killed, due to quarantine", + log_data_string("nick", parv[1]), + log_data_client("server", client)); + sendto_one(client, NULL, ":%s KILL %s :Quarantined: no oper privileges allowed", me.id, parv[6]); return; } if (!IsULine(client) && (tklban = find_qline(client, nick, &ishold))) { - if (IsServer(client) && !ishold) /* server introducing new client */ - { - acptrs = find_server(client->user == NULL ? parv[6] : client->user->server, NULL); - /* (NEW: no unregistered Q-Line msgs anymore during linking) */ - if (!acptrs || (acptrs->serv && acptrs->serv->flags.synced)) - sendto_snomask(SNO_QLINE, "Q-Lined nick %s from %s on %s", nick, - (*client->name != 0 - && !IsServer(client) ? client->name : ""), - acptrs ? acptrs->name : "unknown server"); - } - } - - /* Now check if 'nick' already exists - first, collisions with server names/ids (extremely rare) */ - if ((acptr = find_server(nick, NULL)) != NULL) - { - sendto_umode(UMODE_OPER, "Nick collision on %s(%s <- %s)", - client->name, acptr->direction->name, - get_client_name(client, FALSE)); - ircstats.is_kill++; - sendto_one(client, NULL, ":%s KILL %s :Nick-server-collision", me.id, parv[1]); - return; + unreal_log(ULOG_INFO, "nick", "QLINE_NICK_REMOTE", client, + "Banned nick $nick [$nick.ip] from server $server ($reason)", + log_data_string("nick", parv[1]), + log_data_string("ip", ip), + log_data_client("server", client->uplink), + log_data_string("reason", tklban->ptr.nameban->reason)); + /* Let it through */ } /* Now check if 'nick' already exists - collision with a user (or still in handshake, unknown) */ @@ -594,22 +624,7 @@ CMD_FUNC(cmd_uid) lastnick = atol(parv[3]); differ = (mycmp(acptr->user->username, parv[4]) || mycmp(acptr->user->realhost, parv[5])); - sendto_umode(UMODE_OPER, "Nick collision on %s (%s %lld <- %s %lld)", - acptr->name, acptr->direction->name, (long long)acptr->lastnick, - client->direction->name, (long long)lastnick); - /* - ** I'm putting the KILL handling here just to make it easier - ** to read, it's hard to follow it the way it used to be. - ** Basically, this is what it will do. It will kill both - ** users if no timestamp is given, or they are equal. It will - ** kill the user on our side if the other server is "correct" - ** (user@host differ and their user is older, or user@host are - ** the same and their user is younger), otherwise just kill the - ** user an reintroduce our correct user. - ** The old code just sat there and "hoped" the other server - ** would kill their user. Not anymore. - ** -- binary - */ + if (acptr->lastnick == lastnick) { nick_collision(client, parv[1], parv[6], NULL, acptr, NICKCOL_EQUAL); @@ -647,29 +662,49 @@ nickkill2done: make_user(client); - hostname = parv[5]; - sstamp = parv[7]; - username = parv[4]; - umodes = parv[8]; - virthost = parv[9]; - ip = parv[11]; - realname = parv[12]; /* Note that cloaked host aka parv[10] is unused */ - client->user->server = find_or_add(client->srvptr->name); + client->user->server = find_or_add(client->uplink->name); strlcpy(client->user->realhost, hostname, sizeof(client->user->realhost)); - // FIXME: some validation would be nice ^ + if (ip) + safe_strdup(client->ip, ip); if (*sstamp != '*') - strlcpy(client->user->svid, sstamp, sizeof(client->user->svid)); + strlcpy(client->user->account, sstamp, sizeof(client->user->account)); strlcpy(client->info, realname, sizeof(client->info)); strlcpy(client->user->username, username, USERLEN + 1); - register_user(client, client->name, username, umodes, virthost, ip); - if (IsDead(client)) - return; + SetUser(client); - if (client->user->svid[0] != '0') + make_cloakedhost(client, client->user->realhost, client->user->cloakedhost, sizeof(client->user->cloakedhost)); + safe_strdup(client->user->virthost, client->user->cloakedhost); + + /* Inherit flags from server, makes it easy in the send routines + * and this also makes clients inherit ulines. + */ + client->flags |= client->uplink->flags; + + /* Update counts */ + irccounts.clients++; + if (client->uplink->server) + client->uplink->server->users++; + if (client->umodes & UMODE_INVISIBLE) + irccounts.invisible++; + + /* Set user modes */ + set_user_modes_dont_spread(client, umodes); + + /* Set the vhost */ + if (*virthost != '*') + safe_strdup(client->user->virthost, virthost); + + build_umode_string(client, 0, SEND_UMODES|UMODE_SERVNOTICE, buf); + + sendto_serv_butone_nickcmd(client->direction, recv_mtags, client, (*buf == '\0' ? "+" : buf)); + + moddata_extract_s2s_mtags(client, recv_mtags); + + if (IsLoggedIn(client)) { user_account_login(recv_mtags, client); /* no need to check for kill upon user_account_login() here @@ -680,510 +715,430 @@ nickkill2done: RunHook(HOOKTYPE_REMOTE_CONNECT, client); if (!IsULine(serv) && IsSynched(serv)) - sendto_fconnectnotice(client, 0, NULL); + { + unreal_log(ULOG_INFO, "connect", "REMOTE_CLIENT_CONNECT", client, + "Client connecting: $client ($client.user.username@$client.hostname) [$client.ip] $extended_client_info", + log_data_string("extended_client_info", get_connect_extinfo(client)), + log_data_string("from_server_name", client->user->server)); + } } /** The NICK command. - * In UnrealIRCd 4/5 this is only used in 2 cases: + * In UnrealIRCd 4 and later this should only happen for: * 1) A local user setting or changing the nick name ("NICK xyz") + * -> cmd_nick_local() * 2) A remote user changing their nick name (": NICK ") + * -> cmd_nick_remote() */ CMD_FUNC(cmd_nick) { + if ((parc < 2) || BadPtr(parv[1])) + { + sendnumeric(client, ERR_NONICKNAMEGIVEN); + return; + } + if (MyConnect(client) && !IsServer(client)) + { cmd_nick_local(client, recv_mtags, parc, parv); - else + } else + if (!IsUser(client)) + { + unreal_log(ULOG_ERROR, "link", "LINK_OLD_PROTOCOL_NICK", client->direction, + "Server link $client tried to introduce $nick using NICK command. " + "Server is using an old and unsupported protocol from UnrealIRCd 3.2.x or earlier, should use the UID command. " + "See https://www.unrealircd.org/docs/FAQ#old-server-protocol", + log_data_string("nick", parv[1])); + /* Split the entire uplink, as it should never have allowed this (and probably they are to blame too) */ + exit_client(client->direction, NULL, "Server used NICK command, bad, must use UID!"); + return; + } else + { cmd_nick_remote(client, recv_mtags, parc, parv); + } } -/** Register the connection as a User. +void welcome_user(Client *client, TKL *viruschan_tkl) +{ + int i; + ConfigItem_tld *tlds; + + RunHook(HOOKTYPE_WELCOME, client, 0); + sendnumeric(client, RPL_WELCOME, NETWORK_NAME, client->name, client->user->username, client->user->realhost); + + RunHook(HOOKTYPE_WELCOME, client, 1); + sendnumeric(client, RPL_YOURHOST, me.name, version); + + RunHook(HOOKTYPE_WELCOME, client, 2); + sendnumeric(client, RPL_CREATED, creation); + + RunHook(HOOKTYPE_WELCOME, client, 3); + sendnumeric(client, RPL_MYINFO, me.name, version, umodestring, cmodestring); + + RunHook(HOOKTYPE_WELCOME, client, 4); + for (i = 0; ISupportStrings[i]; i++) + sendnumeric(client, RPL_ISUPPORT, ISupportStrings[i]); + + RunHook(HOOKTYPE_WELCOME, client, 5); + + if (IsHidden(client)) + { + sendnumeric(client, RPL_HOSTHIDDEN, client->user->virthost); + RunHook(HOOKTYPE_WELCOME, client, 396); + } + + if (IsSecureConnect(client)) + { + if (client->local->ssl && !iConf.no_connect_tls_info) + { + sendnotice(client, "*** You are connected to %s with %s", + me.name, tls_get_cipher(client)); + } + } + + { + const char *parv[2]; + parv[0] = NULL; + parv[1] = NULL; + do_cmd(client, NULL, "LUSERS", 1, parv); + if (IsDead(client)) + return; + } + + RunHook(HOOKTYPE_WELCOME, client, 266); + + short_motd(client); + + RunHook(HOOKTYPE_WELCOME, client, 376); + +#ifdef EXPERIMENTAL + sendnotice(client, + "*** \2NOTE:\2 This server is running experimental IRC server software (UnrealIRCd %s). " + "If you find any bugs or problems, please report them at https://bugs.unrealircd.org/", + VERSIONONLY); +#endif + + if (client->umodes & UMODE_INVISIBLE) + irccounts.invisible++; + + build_umode_string(client, 0, SEND_UMODES|UMODE_SERVNOTICE, buf); + + sendto_serv_butone_nickcmd(client->direction, NULL, client, (*buf == '\0' ? "+" : buf)); + + broadcast_moddata_client(client); + RunHook(HOOKTYPE_LOCAL_CONNECT, client); + if (buf[0] != '\0' && buf[1] != '\0') + sendto_one(client, NULL, ":%s MODE %s :%s", client->name, + client->name, buf); + if (client->user->snomask) + sendnumeric(client, RPL_SNOMASK, client->user->snomask); + + if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_user == POLICY_WARN)) + sendnotice_multiline(client, iConf.plaintext_policy_user_message); + + if (IsSecure(client) && (iConf.outdated_tls_policy_user == POLICY_WARN) && outdated_tls_client(client)) + sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_user_message, client)); + + /* Make creation time the real 'online since' time, excluding registration time. + * Otherwise things like set::anti-spam-quit-messagetime 10s could mean + * 1 second in practice (#2174). + */ + client->local->creationtime = TStime(); + client->local->idle_since = TStime(); + + /* Give the user a fresh start as far as fake-lag is concerned. + * Otherwise the user could be lagged up already due to all the CAP stuff. + */ + client->local->fake_lag = TStime(); + + RunHook(HOOKTYPE_WELCOME, client, 999); + + /* NOTE: Code after this 'if (viruschan_tkl)' will not be executed for quarantined- + * virus-users. So be carefull with the order. -- Syzop + */ + // FIXME: verify if this works, trace code path upstream!!!! + if (viruschan_tkl) + { + join_viruschan(client, viruschan_tkl, SPAMF_USER); + return; + } + + /* Force the user to join the given chans -- codemastr */ + tlds = find_tld(client); + + if (tlds && !BadPtr(tlds->channel)) + { + char *chans = strdup(tlds->channel); + const char *args[3] = { + NULL, + chans, + NULL + }; + do_cmd(client, NULL, "JOIN", 3, args); + safe_free(chans); + if (IsDead(client)) + return; + } + else if (!BadPtr(AUTO_JOIN_CHANS) && strcmp(AUTO_JOIN_CHANS, "0")) + { + char *chans = strdup(AUTO_JOIN_CHANS); + const char *args[3] = { + NULL, + chans, + NULL + }; + do_cmd(client, NULL, "JOIN", 3, args); + safe_free(chans); + if (IsDead(client)) + return; + } +} + +/** Validate client->user->username. + * @param client The client to check + * @param noident Whether we should ignore the first ~ or not + * @returns 1 if the username is acceptable, 0 if not. + * @note This function will modify client->user->username to make it valid. + * Only if there are zero valid characters it will return 0. + */ +int valid_username(Client *client, int noident) +{ + static char stripuser[USERLEN + 1]; + char *i; + char *o = stripuser; + char filtered = 0; /* any changes? */ + + *stripuser = '\0'; + + for (i = client->user->username + noident; *i; i++) + { + if (isallowed(*i)) + *o++ = *i; + else + filtered = 1; + } + *o = '\0'; + + if (filtered == 0) + return 1; /* No change needed, all good */ + + if (*stripuser == '\0') + return 0; /* Zero valid characters, reject it */ + + strlcpy(client->user->username + 1, stripuser, sizeof(client->user->username)-1); + client->user->username[0] = '~'; + client->user->username[USERLEN] = '\0'; + return 1; /* Filtered, but OK */ +} + +/** Register the connection as a User - only for local connections! * This is called after NICK + USER (in no particular order) * and possibly other protocol messages as well (eg CAP). - * @param client Client to be made a user. - * @param nick Nick name - * @param username Username - * @param umode User modes - * @param virthost Virtual host (can be NULL) - * @param ip IP address string (can be NULL) + * @param client Client to be made a user. * @returns 1 if successfully registered, 0 if not (client might be killed). */ -int _register_user(Client *client, char *nick, char *username, char *umode, char *virthost, char *ip) +int _register_user(Client *client) { ConfigItem_ban *bconf; char *tmpstr; - char stripuser[USERLEN + 1], *u1 = stripuser, *u2, olduser[USERLEN + 1], - userbad[USERLEN * 2 + 1], *ubad = userbad, noident = 0; - int i, xx; + char noident = 0; + int i; Hook *h; - User *user = client->user; - char *tkllayer[9] = { - me.name, /*0 server.name */ - "+", /*1 +|- */ - "z", /*2 G */ - "*", /*3 user */ - NULL, /*4 host */ - NULL, - NULL, /*6 expire_at */ - NULL, /*7 set_at */ - NULL /*8 reason */ - }; TKL *savetkl = NULL; - ConfigItem_tld *tlds; + char temp[USERLEN + 1]; + char descbuf[BUFSIZE]; - nick = client->name; /* <- The data is always the same, but the pointer is sometimes not, - * I need this for one of my modules, so do not remove! ;) -- Syzop */ + if (!MyConnect(client)) + abort(); - if (MyConnect(client)) + /* Set client->local->sockhost: + * First deal with the special 'localhost' case and + * then with generic setting based on DNS. + */ + if (!strcmp(GetIP(client), "127.0.0.1") || + !strcmp(GetIP(client), "0:0:0:0:0:0:0:1") || + !strcmp(GetIP(client), "0:0:0:0:0:ffff:127.0.0.1")) { - char temp[USERLEN + 1]; - - if (!AllowClient(client, username)) - { - ircstats.is_ref++; - /* For safety, we have an extra kill here */ - if (!IsDead(client)) - exit_client(client, NULL, "Rejected"); - return 0; - } - + set_sockhost(client, "localhost"); if (client->local->hostp) { - /* reject ASCII < 32 and ASCII >= 127 (note: upper resolver might be even more strict). */ - for (tmpstr = client->local->sockhost; *tmpstr > ' ' && *tmpstr < 127; tmpstr++); + unreal_free_hostent(client->local->hostp); + client->local->hostp = NULL; + } + } else + { + struct hostent *hp = client->local->hostp; + if (hp && hp->h_name) + set_sockhost(client, hp->h_name); + } - /* if host contained invalid ASCII _OR_ the DNS reply is an IP-like reply - * (like: 1.2.3.4 or ::ffff:1.2.3.4), then reject it and use IP instead. + /* Set the hostname (client->user->realhost). + * This may later be overwritten by the AllowClient() call to + * revert to the IP again if allow::options::useip is set. + */ + strlcpy(client->user->realhost, client->local->sockhost, sizeof(client->local->sockhost)); + + /* Check allow { } blocks... */ + if (!AllowClient(client)) + { + ircstats.is_ref++; + /* For safety, we have an extra kill here */ + if (!IsDead(client)) + exit_client(client, NULL, "Rejected"); + return 0; + } + + if (IsUseIdent(client)) + { + if (IsIdentSuccess(client)) + { + /* ident succeeded: overwite client->user->username with the ident reply */ + strlcpy(client->user->username, client->ident, sizeof(client->user->username)); + } else + if (IDENT_CHECK) + { + /* ident check is enabled and it failed: prefix the username with ~ */ + char temp[USERLEN+1]; + strlcpy(temp, client->user->username, sizeof(temp)); + snprintf(client->user->username, sizeof(client->user->username), "~%s", temp); + noident = 1; + } + } + + /* Now validate the username. This may alter client->user->username + * or reject it completely. + */ + if (!valid_username(client, noident)) + { + exit_client(client, NULL, "Hostile username. Please use only 0-9 a-z A-Z _ - and . in your username."); + return 0; + } + + /* Check ban realname { } blocks */ + if ((bconf = find_ban(NULL, client->info, CONF_BAN_REALNAME))) + { + ircstats.is_ref++; + banned_client(client, "realname", bconf->reason?bconf->reason:"", 0, 0); + return 0; + } + /* Check G/Z lines before shuns -- kill before quite -- codemastr */ + if (find_tkline_match(client, 0)) + { + if (!IsDead(client) && client->local->class) + { + /* Fix client count bug, in case that it was a hold such as via authprompt */ + client->local->class->clients--; + client->local->class = NULL; + } + ircstats.is_ref++; + return 0; + } + find_shun(client); + + spamfilter_build_user_string(spamfilter_user, client->name, client); + if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, 0, &savetkl)) + { + if (savetkl && ((savetkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || + (savetkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN))) + { + /* 'viruschan' action: + * Continue with registering the client, and at the end + * of this function we will do the actual joining to the + * virus channel. */ - if (*tmpstr || !*user->realhost || (isdigit(*client->local->sockhost) && (client->local->sockhost > tmpstr && isdigit(*(tmpstr - 1))) ) - || (client->local->sockhost[0] == ':')) - strlcpy(client->local->sockhost, client->ip, sizeof(client->local->sockhost)); - } - if (client->local->sockhost[0]) - { - strlcpy(user->realhost, client->local->sockhost, sizeof(client->local->sockhost)); /* SET HOSTNAME */ } else { - sendto_realops("[HOSTNAME BUG] client->local->sockhost is empty for user %s (%s, %s)", - client->name, client->ip ? client->ip : "", user->realhost); - ircd_log(LOG_ERROR, "[HOSTNAME BUG] client->local->sockhost is empty for user %s (%s, %s)", - client->name, client->ip ? client->ip : "", user->realhost); - } - - /* - * I do not consider *, ~ or ! 'hostile' in usernames, - * as it is easy to differentiate them (Use \*, \? and \\) - * with the possible? - * exception of !. With mIRC etc. ident is easy to fake - * to contain @ though, so if that is found use non-ident - * username. -Donwulff - * - * I do, We only allow a-z A-Z 0-9 _ - and . now so the - * !strchr(client->ident, '@') check is out of date. -Cabal95 - * - * Moved the noident stuff here. -OnyxDragon - */ - - /* because username may point to user->username */ - strlcpy(temp, username, USERLEN + 1); - - if (!IsUseIdent(client)) - strlcpy(user->username, temp, USERLEN + 1); - else if (IsIdentSuccess(client)) - strlcpy(user->username, client->ident, USERLEN+1); - else - { - if (IDENT_CHECK == 0) { - strlcpy(user->username, temp, USERLEN+1); - } - else { - *user->username = '~'; - strlcpy((user->username + 1), temp, sizeof(user->username)-1); - noident = 1; - } - - } - /* - * Limit usernames to just 0-9 a-z A-Z _ - and . - * It strips the "bad" chars out, and if nothing is left - * changes the username to the first 8 characters of their - * nickname. After the MOTD is displayed it sends numeric - * 455 to the user telling them what(if anything) happened. - * -Cabal95 - * - * Moved the noident thing to the right place - see above - * -OnyxDragon - * - * No longer use nickname if the entire ident is invalid, - * if thats the case, it is likely the user is trying to cause - * problems so just ban them. (Using the nick could introduce - * hostile chars) -- codemastr - */ - for (u2 = user->username + noident; *u2; u2++) - { - if (isallowed(*u2)) - *u1++ = *u2; - else if (*u2 < 32) - { - /* - * Make sure they can read what control - * characters were in their username. - */ - *ubad++ = '^'; - *ubad++ = *u2 + '@'; - } - else - *ubad++ = *u2; - } - *u1 = '\0'; - *ubad = '\0'; - if (strlen(stripuser) != strlen(user->username + noident)) - { - if (stripuser[0] == '\0') - { - exit_client(client, NULL, "Hostile username. Please use only 0-9 a-z A-Z _ - and . in your username."); - return 0; - } - - strlcpy(olduser, user->username + noident, USERLEN+1); - strlcpy(user->username + 1, stripuser, sizeof(user->username)-1); - user->username[0] = '~'; - user->username[USERLEN] = '\0'; - } - else - u1 = NULL; - - /* Check ban realname { } blocks */ - if ((bconf = find_ban(NULL, client->info, CONF_BAN_REALNAME))) - { - ircstats.is_ref++; - banned_client(client, "realname", bconf->reason?bconf->reason:"", 0, 0); + /* Client is either dead or blocked (will hang, on purpose, and timeout) */ return 0; } - /* Check G/Z lines before shuns -- kill before quite -- codemastr */ - if (find_tkline_match(client, 0)) + } + + for (h = Hooks[HOOKTYPE_PRE_LOCAL_CONNECT]; h; h = h->next) + { + int ret = (*(h->func.intfunc))(client); + if (ret == HOOK_DENY) { if (!IsDead(client) && client->local->class) { - /* Fix client count bug, in case that it was a hold such as via authprompt */ + /* Fix client count bug, in case that + * the HOOK_DENY was only meant temporarily. + */ client->local->class->clients--; client->local->class = NULL; } - ircstats.is_ref++; return 0; } - find_shun(client); - - spamfilter_build_user_string(spamfilter_user, client->name, client); - if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, 0, &savetkl)) - { - if (savetkl && ((savetkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || - (savetkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN))) - { - /* 'viruschan' action: - * Continue with registering the client, and at the end - * of this function we will do the actual joining to the - * virus channel. - */ - } else { - /* Client is either dead or blocked (will hang, on purpose, and timeout) */ - return 0; - } - } - - for (h = Hooks[HOOKTYPE_PRE_LOCAL_CONNECT]; h; h = h->next) - { - i = (*(h->func.intfunc))(client); - if (i == HOOK_DENY) - { - if (!IsDead(client) && client->local->class) - { - /* Fix client count bug, in case that - * the HOOK_DENY was only meant temporarily. - */ - client->local->class->clients--; - client->local->class = NULL; - } - return 0; - } - if (i == HOOK_ALLOW) - break; - } - } - else - { - strlcpy(user->username, username, USERLEN+1); + if (ret == HOOK_ALLOW) + break; } + SetUser(client); + + make_cloakedhost(client, client->user->realhost, client->user->cloakedhost, sizeof(client->user->cloakedhost)); + safe_strdup(client->user->virthost, client->user->cloakedhost); + + snprintf(descbuf, sizeof descbuf, "Client: %s", client->name); + fd_desc(client->local->fd, descbuf); + + /* Move user from unknown list to client list */ + list_move(&client->lclient_node, &lclient_list); + + /* Update counts */ + irccounts.unknown--; irccounts.clients++; - if (client->srvptr && client->srvptr->serv) - client->srvptr->serv->users++; + irccounts.me_clients++; + if (client->uplink && client->uplink->server) + client->uplink->server->users++; - make_cloakedhost(client, user->realhost, user->cloakedhost, sizeof(user->cloakedhost)); - safe_strdup(user->virthost, user->cloakedhost); - - if (MyConnect(client)) + if (IsSecure(client)) { - char descbuf[BUFSIZE]; - int i; - - snprintf(descbuf, sizeof descbuf, "Client: %s", nick); - fd_desc(client->local->fd, descbuf); - - list_move(&client->lclient_node, &lclient_list); - - irccounts.unknown--; - irccounts.me_clients++; - - if (IsSecure(client)) - { - client->umodes |= UMODE_SECURE; - RunHook(HOOKTYPE_SECURE_CONNECT, client); - } - - if (IsHidden(client)) - { - ircd_log(LOG_CLIENT, "Connect - %s!%s@%s [%s] [vhost: %s] %s", - nick, user->username, user->realhost, GetIP(client), user->virthost, get_connect_extinfo(client)); - } else - { - ircd_log(LOG_CLIENT, "Connect - %s!%s@%s [%s] %s", - nick, user->username, user->realhost, GetIP(client), get_connect_extinfo(client)); - } - - RunHook2(HOOKTYPE_WELCOME, client, 0); - sendnumeric(client, RPL_WELCOME, ircnetwork, nick, user->username, user->realhost); - - RunHook2(HOOKTYPE_WELCOME, client, 1); - sendnumeric(client, RPL_YOURHOST, me.name, version); - - RunHook2(HOOKTYPE_WELCOME, client, 2); - sendnumeric(client, RPL_CREATED, creation); - - RunHook2(HOOKTYPE_WELCOME, client, 3); - sendnumeric(client, RPL_MYINFO, me.name, version, umodestring, cmodestring); - - RunHook2(HOOKTYPE_WELCOME, client, 4); - for (i = 0; ISupportStrings[i]; i++) - sendnumeric(client, RPL_ISUPPORT, ISupportStrings[i]); - - RunHook2(HOOKTYPE_WELCOME, client, 5); - - if (IsHidden(client)) - { - sendnumeric(client, RPL_HOSTHIDDEN, user->virthost); - RunHook2(HOOKTYPE_WELCOME, client, 396); - } - - if (IsSecureConnect(client)) - { - if (client->local->ssl && !iConf.no_connect_tls_info) - { - sendnotice(client, "*** You are connected to %s with %s", - me.name, tls_get_cipher(client->local->ssl)); - } - } - - { - char *parv[2]; - parv[0] = client->name; - parv[1] = NULL; - do_cmd(client, NULL, "LUSERS", 1, parv); - if (IsDead(client)) - return 0; - } - - RunHook2(HOOKTYPE_WELCOME, client, 266); - - short_motd(client); - - RunHook2(HOOKTYPE_WELCOME, client, 376); - -#ifdef EXPERIMENTAL - sendnotice(client, - "*** \2NOTE:\2 This server is running experimental IRC server software (UnrealIRCd %s). " - "If you find any bugs or problems, please report them at https://bugs.unrealircd.org/", - VERSIONONLY); -#endif - /* - * Now send a numeric to the user telling them what, if - * anything, happened. - */ - if (u1) - sendnumeric(client, ERR_HOSTILENAME, olduser, userbad, stripuser); - } - else - { - Client *acptr; - - /* Remote client */ - /* The following two cases probably cannot happen anymore? at all? */ - if (!(acptr = find_server_quick(user->server))) - { - sendto_ops("Bad USER [%s] :%s USER %s %s : No such server", - client->name, nick, user->username, user->server); - sendto_one(client, NULL, ":%s KILL %s :No such server: %s", - me.id, client->id, user->server); - SetKilled(client); - exit_client(client, NULL, "USER without prefix(2.8) or wrong prefix"); - return 0; - } - else if (acptr->direction != client->direction) - { - sendto_ops("Bad User [%s] :%s USER %s %s, != %s[%s]", - client->name, nick, user->username, user->server, - acptr->name, acptr->direction->name); - sendto_one(client, NULL, ":%s KILL %s :Wrong user-server-direction", - me.id, client->id); - SetKilled(client); - exit_client(client, NULL, "USER server wrong direction"); - return 0; - } else - { - client->flags |= acptr->flags; - } - - if (IsULine(client->srvptr)) - SetULine(client); - } - if (client->umodes & UMODE_INVISIBLE) - { - irccounts.invisible++; + client->umodes |= UMODE_SECURE; + RunHook(HOOKTYPE_SECURE_CONNECT, client); } - if (virthost && umode) - { - /* Set the IP address first */ - if (ip && (*ip != '*')) - { - char *ipstring = decode_ip(ip); - if (!ipstring) - { - sendto_ops("USER with invalid IP (%s) (%s) -- " - "IP must be base64 encoded binary representation of either IPv4 or IPv6", - client->name, ip); - exit_client(client, NULL, "USER with invalid IP"); - return 0; - } - safe_strdup(client->ip, ipstring); - } + safe_free(client->local->passwd); - /* For remote clients we recalculate the cloakedhost here because - * it may depend on the IP address (bug #5064). - */ - make_cloakedhost(client, user->realhost, user->cloakedhost, sizeof(user->cloakedhost)); - safe_strdup(user->virthost, user->cloakedhost); + unreal_log(ULOG_INFO, "connect", "LOCAL_CLIENT_CONNECT", client, + "Client connecting: $client ($client.user.username@$client.hostname) [$client.ip] $extended_client_info", + log_data_string("extended_client_info", get_connect_extinfo(client))); - /* Set the umodes */ - tkllayer[0] = nick; - tkllayer[1] = nick; - tkllayer[2] = umode; - tkllayer[3] = NULL; - dontspread = 1; - do_cmd(client, NULL, "MODE", 3, tkllayer); - dontspread = 0; + /* Send the RPL_WELCOME, LUSERS, MOTD, auto join channels, everything... */ + welcome_user(client, savetkl); - /* Set the vhost */ - if (virthost && *virthost != '*') - safe_strdup(client->user->virthost, virthost); - } - - hash_check_watch(client, RPL_LOGON); /* Uglier hack */ - build_umode_string(client, 0, SEND_UMODES|UMODE_SERVNOTICE, buf); - - sendto_serv_butone_nickcmd(client->direction, client, (*buf == '\0' ? "+" : buf)); - - if (MyConnect(client)) - { - broadcast_moddata_client(client); - sendto_connectnotice(client, 0, NULL); /* moved down, for modules. */ - if (buf[0] != '\0' && buf[1] != '\0') - sendto_one(client, NULL, ":%s MODE %s :%s", client->name, - client->name, buf); - if (user->snomask) - sendnumeric(client, RPL_SNOMASK, get_snomask_string_raw(user->snomask)); - - if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_user == POLICY_WARN)) - sendnotice_multiline(client, iConf.plaintext_policy_user_message); - - if (IsSecure(client) && (iConf.outdated_tls_policy_user == POLICY_WARN) && outdated_tls_client(client)) - sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_user_message, client)); - - /* Make creation time the real 'online since' time, excluding registration time. - * Otherwise things like set::anti-spam-quit-messagetime 10s could mean - * 1 second in practice (#2174). - */ - client->local->firsttime = TStime(); - client->local->last = TStime(); - - /* Give the user a fresh start as far as fake-lag is concerned. - * Otherwise the user could be lagged up already due to all the CAP stuff. - */ - client->local->since = TStime(); - - RunHook2(HOOKTYPE_WELCOME, client, 999); - - /* NOTE: Code after this 'if (savetkl)' will not be executed for quarantined- - * virus-users. So be carefull with the order. -- Syzop - */ - // FIXME: verify if this works, trace code path upstream!!!! - if (savetkl) - return join_viruschan(client, savetkl, SPAMF_USER); /* [RETURN!] */ - - /* Force the user to join the given chans -- codemastr */ - tlds = find_tld(client); - - if (tlds && !BadPtr(tlds->channel)) - { - char *chans = strdup(tlds->channel); - char *args[3] = { - client->name, - chans, - NULL - }; - do_cmd(client, NULL, "JOIN", 3, args); - safe_free(chans); - if (IsDead(client)) - return 0; - } - else if (!BadPtr(AUTO_JOIN_CHANS) && strcmp(AUTO_JOIN_CHANS, "0")) - { - char *chans = strdup(AUTO_JOIN_CHANS); - char *args[3] = { - client->name, - chans, - NULL - }; - do_cmd(client, NULL, "JOIN", 3, args); - safe_free(chans); - if (IsDead(client)) - return 0; - } - /* NOTE: If you add something here.. be sure to check the 'if (savetkl)' note above */ - } - - if (MyConnect(client) && !BadPtr(client->local->passwd)) - { - safe_free(client->local->passwd); - client->local->passwd = NULL; - } - - /* User successfully registered */ - return 1; + return IsDead(client) ? 0 : 1; } /** Nick collission detected. A winner has been decided upstream. Deal with killing. * I moved this all to a single routine here rather than having all code duplicated * due to SID vs NICK and some code quadruplicated. */ -void nick_collision(Client *cptr, char *newnick, char *newid, Client *new, Client *existing, int type) +void nick_collision(Client *cptr, const char *newnick, const char *newid, Client *new, Client *existing, int type) { char comment[512]; - char *new_server, *existing_server; + const char *new_server, *existing_server; + const char *who_won; + const char *nickcol_reason; - ircd_log(LOG_ERROR, "Nick collision: %s[%s]@%s (new) vs %s[%s]@%s (existing). Winner: %s. Type: %s", - newnick, newid, cptr->name, - existing->name, existing->id, existing->srvptr->name, - (type == NICKCOL_EQUAL) ? "None (equal)" : ((type == NICKCOL_NEW_WON) ? "New won" : "Existing won"), - new ? "nick-change" : "new user connecting"); + if (type == NICKCOL_NEW_WON) + who_won = "new"; + else if (type == NICKCOL_EXISTING_WON) + who_won = "existing"; + else + who_won = "none"; + + nickcol_reason = new ? "nick change" : "new user connecting"; + + unreal_log(ULOG_ERROR, "nick", "NICK_COLLISION", NULL, + "Nick collision: " + "$new_nick[$new_id]@$uplink (new) vs " + "$existing_client[$existing_client.id]@$existing_client.user.servername (existing). " + "Winner: $nick_collision_winner. " + "Cause: $nick_collision_reason", + log_data_string("new_nick", newnick), + log_data_string("new_id", newid), + log_data_client("uplink", cptr), + log_data_client("existing_client", existing), + log_data_string("nick_collision_winner", who_won), + log_data_string("nick_collision_reason", nickcol_reason)); new_server = cptr->name; existing_server = (existing == existing->direction) ? me.name : existing->direction->name; @@ -1246,32 +1201,6 @@ void nick_collision(Client *cptr, char *newnick, char *newid, Client *new, Clien } } -/* This used to initialize the various name strings used to store hostnames. - * But nowadays this takes place much earlier (in add_connection?). - * It's mainly used for "localhost" and WEBIRC magic only now... - */ -int check_init(Client *client, char *sockn, size_t size) -{ - strlcpy(sockn, client->local->sockhost, HOSTLEN); - - RunHookReturnInt3(HOOKTYPE_CHECK_INIT, client, sockn, size, !=0); - - /* Some silly hack to convert 127.0.0.1 and such into 'localhost' */ - if (!strcmp(GetIP(client), "127.0.0.1") || - !strcmp(GetIP(client), "0:0:0:0:0:0:0:1") || - !strcmp(GetIP(client), "0:0:0:0:0:ffff:127.0.0.1")) - { - if (client->local->hostp) - { - unreal_free_hostent(client->local->hostp); - client->local->hostp = NULL; - } - strlcpy(sockn, "localhost", HOSTLEN); - } - - return 1; -} - /** Returns 1 if allow::maxperip is exceeded by 'client' */ int exceeds_maxperip(Client *client, ConfigItem_allow *aconf) { @@ -1305,23 +1234,15 @@ int exceeds_maxperip(Client *client, ConfigItem_allow *aconf) * @param username Username, for some reason... * @returns 1 if OK, 0 if client is rejected (likely killed too) */ -int AllowClient(Client *client, char *username) +int AllowClient(Client *client) { static char sockhost[HOSTLEN + 1]; - struct hostent *hp = NULL; int i; ConfigItem_allow *aconf; char *hname; static char uhost[HOSTLEN + USERLEN + 3]; static char fullname[HOSTLEN + 1]; - Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]", client->name, client->local->sockhost)); - - if (!check_init(client, sockhost, sizeof(sockhost))) - return 0; - - hp = client->local->hostp; - if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_user == POLICY_DENY)) { exit_client(client, NULL, iConf.plaintext_policy_user_message->line); @@ -1330,7 +1251,7 @@ int AllowClient(Client *client, char *username) if (IsSecure(client) && (iConf.outdated_tls_policy_user == POLICY_DENY) && outdated_tls_client(client)) { - char *msg = outdated_tls_client_build_string(iConf.outdated_tls_policy_user_message, client); + const char *msg = outdated_tls_client_build_string(iConf.outdated_tls_policy_user_message, client); exit_client(client, NULL, msg); return 0; } @@ -1340,62 +1261,9 @@ int AllowClient(Client *client, char *username) if (aconf->flags.tls && !IsSecure(client)) continue; - if (hp && hp->h_name) - { - hname = hp->h_name; - strlcpy(fullname, hname, sizeof(fullname)); - Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname)); - if (strchr(aconf->hostname, '@')) - { - if (aconf->flags.noident) - strlcpy(uhost, username, sizeof(uhost)); - else - strlcpy(uhost, client->ident, sizeof(uhost)); - strlcat(uhost, "@", sizeof(uhost)); - } - else - *uhost = '\0'; - strlcat(uhost, fullname, sizeof(uhost)); - if (match_simple(aconf->hostname, uhost)) - goto attach; - } + if (!unreal_mask_match(client, aconf->mask)) + continue; - if (strchr(aconf->ip, '@')) - { - if (aconf->flags.noident) - strlcpy(uhost, username, sizeof(uhost)); - else - strlcpy(uhost, client->ident, sizeof(uhost)); - strlcat(uhost, "@", sizeof(uhost)); - } - else - *uhost = '\0'; - strlcat(uhost, sockhost, sizeof(uhost)); - /* Check the IP */ - if (match_user(aconf->ip, client, MATCH_CHECK_IP)) - goto attach; - - /* Hmm, localhost is a special case, hp == NULL and sockhost contains - * 'localhost' instead of an ip... -- Syzop. */ - if (!strcmp(sockhost, "localhost")) - { - if (strchr(aconf->hostname, '@')) - { - if (aconf->flags.noident) - strlcpy(uhost, username, sizeof(uhost)); - else - strlcpy(uhost, client->ident, sizeof(uhost)); - strlcat(uhost, "@localhost", sizeof(uhost)); - } - else - strcpy(uhost, "localhost"); - - if (match_simple(aconf->hostname, uhost)) - goto attach; - } - - continue; /* No match */ - attach: /* Check authentication */ if (aconf->auth && !Auth_Check(client, aconf->auth, client->local->passwd)) { @@ -1411,11 +1279,9 @@ int AllowClient(Client *client, char *username) if (!aconf->flags.noident) SetUseIdent(client); - if (!aconf->flags.useip && hp) - strlcpy(uhost, fullname, sizeof(uhost)); - else - strlcpy(uhost, sockhost, sizeof(uhost)); - set_sockhost(client, uhost); + + if (aconf->flags.useip) + set_sockhost(client, GetIP(client)); if (exceeds_maxperip(client, aconf)) { @@ -1432,7 +1298,7 @@ int AllowClient(Client *client, char *username) else { /* Class is full */ - sendnumeric(client, RPL_REDIR, aconf->server ? aconf->server : defserv, aconf->port ? aconf->port : 6667); + sendnumeric(client, RPL_REDIR, aconf->server ? aconf->server : DEFAULT_SERVER, aconf->port ? aconf->port : 6667); exit_client(client, NULL, iConf.reject_message_server_full); return 0; } diff --git a/src/modules/nocodes.c b/src/modules/nocodes.c index c8fd9f3..aacf5fd 100644 --- a/src/modules/nocodes.c +++ b/src/modules/nocodes.c @@ -26,10 +26,10 @@ ModuleHeader MOD_HEADER "1.3", /* Version */ "Strip/block color/bold/underline/italic/reverse - by Syzop", /* Short description of module */ "UnrealIRCd Team", /* Author */ - "unrealircd-5", + "unrealircd-6", }; -int nocodes_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); +int nocodes_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); MOD_INIT() { @@ -49,7 +49,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static int has_controlcodes(char *p) +static int has_controlcodes(const char *p) { for (; *p; p++) if ((*p == '\002') || (*p == '\037') || (*p == '\026') || (*p == '\035')) /* bold, underline, reverse, italic */ @@ -57,7 +57,7 @@ static int has_controlcodes(char *p) return 0; } -int nocodes_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int nocodes_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { static char retbuf[4096]; Hook *h; diff --git a/src/modules/oper.c b/src/modules/oper.c index ee7d89a..c2a6541 100644 --- a/src/modules/oper.c +++ b/src/modules/oper.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /oper", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -82,7 +82,7 @@ void set_oper_host(Client *client, char *host) CMD_FUNC(cmd_oper) { ConfigItem_oper *operblock; - char *name, *password; + const char *operblock_name, *password; long old_umodes = client->umodes & ALL_UMODES; if (!MyUser(client)) @@ -103,24 +103,23 @@ CMD_FUNC(cmd_oper) if (IsOper(client)) { - sendnumeric(client, RPL_YOUREOPER); - // TODO: de-confuse this ? ;) + sendnotice(client, "You are already an IRC Operator. If you want to re-oper then de-oper first via /MODE yournick -o"); return; } - name = parv[1]; + operblock_name = parv[1]; password = (parc > 2) ? parv[2] : ""; /* set::plaintext-policy::oper 'deny' */ if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_DENY)) { sendnotice_multiline(client, iConf.plaintext_policy_oper_message); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) [not using SSL/TLS]", - client->name, client->user->username, client->local->sockhost); - ircd_log(LOG_OPER, "OPER NO-SSL/TLS (%s) by (%s!%s@%s)", name, client->name, - client->user->username, client->local->sockhost); - client->local->since += 7; + unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "Not using TLS"), + log_data_string("fail_type", "NO_TLS"), + log_data_string("oper_block", parv[1])); + add_fake_lag(client, 7000); return; } @@ -128,36 +127,40 @@ CMD_FUNC(cmd_oper) if (IsSecure(client) && (iConf.outdated_tls_policy_oper == POLICY_DENY) && outdated_tls_client(client)) { sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_oper_message, client)); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) [outdated SSL/TLS protocol or cipher]", - client->name, client->user->username, client->local->sockhost); - ircd_log(LOG_OPER, "OPER OUTDATED-SSL/TLS (%s) by (%s!%s@%s)", name, client->name, - client->user->username, client->local->sockhost); - client->local->since += 7; + unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "Outdated TLS protocol or cipher"), + log_data_string("fail_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER"), + log_data_string("oper_block", parv[1])); + add_fake_lag(client, 7000); return; } - if (!(operblock = find_oper(name))) + if (!(operblock = find_oper(operblock_name))) { sendnumeric(client, ERR_NOOPERHOST); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) [unknown oper]", - client->name, client->user->username, client->local->sockhost); - ircd_log(LOG_OPER, "OPER UNKNOWNOPER (%s) by (%s!%s@%s)", name, client->name, - client->user->username, client->local->sockhost); - client->local->since += 7; + unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "Unknown oper operblock_name"), + log_data_string("fail_type", "UNKNOWN_OPER_NAME"), + log_data_string("oper_block", parv[1])); + add_fake_lag(client, 7000); return; } + /* Below here, the oper block exists, any errors here we take (even) + * more seriously, they are logged as errors instead of warnings. + */ + if (!unreal_mask_match(client, operblock->mask)) { sendnumeric(client, ERR_NOOPERHOST); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) using UID %s [host doesnt match]", - client->name, client->user->username, client->local->sockhost, name); - ircd_log(LOG_OPER, "OPER NOHOSTMATCH (%s) by (%s!%s@%s)", name, client->name, - client->user->username, client->local->sockhost); - client->local->since += 7; + unreal_log(ULOG_ERROR, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "Host does not match"), + log_data_string("fail_type", "NO_HOST_MATCH"), + log_data_string("oper_block", parv[1])); + add_fake_lag(client, 7000); return; } @@ -167,12 +170,12 @@ CMD_FUNC(cmd_oper) if (FAILOPER_WARN) sendnotice(client, "*** Your attempt has been logged."); - ircd_log(LOG_OPER, "OPER FAILEDAUTH (%s) by (%s!%s@%s)", name, client->name, - client->user->username, client->local->sockhost); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) using UID %s [FAILEDAUTH]", - client->name, client->user->username, client->local->sockhost, name); - client->local->since += 7; + unreal_log(ULOG_ERROR, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "Authentication failed"), + log_data_string("fail_type", "AUTHENTICATION_FAILED"), + log_data_string("oper_block", parv[1])); + add_fake_lag(client, 7000); return; } @@ -185,25 +188,23 @@ CMD_FUNC(cmd_oper) if (operblock->require_modes & ~client->umodes) { sendnumericfmt(client, ERR_NOOPERHOST, ":You are missing user modes required to OPER"); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) [lacking modes '%s' in oper::require-modes]", - client->name, client->user->username, client->local->sockhost, get_usermode_string_raw(operblock->require_modes & ~client->umodes)); - ircd_log(LOG_OPER, "OPER MISSINGMODES (%s) by (%s!%s@%s), needs modes=%s", - name, client->name, client->user->username, client->local->sockhost, - get_usermode_string_raw(operblock->require_modes & ~client->umodes)); - client->local->since += 7; + unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "Not matching oper::require-modes"), + log_data_string("fail_type", "REQUIRE_MODES_NOT_SATISFIED"), + log_data_string("oper_block", parv[1])); + add_fake_lag(client, 7000); return; } if (!find_operclass(operblock->operclass)) { sendnotice(client, "ERROR: There is a non-existant oper::operclass specified for your oper block"); - ircd_log(LOG_ERROR, "OPER MISSINGOPERCLASS (%s) by (%s!%s@%s), oper::operclass does not exist: %s", - name, client->name, client->user->username, client->local->sockhost, - operblock->operclass); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) [oper::operclass does not exist: '%s']", - client->name, client->user->username, client->local->sockhost, operblock->operclass); + unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "Config error: invalid oper::operclass"), + log_data_string("fail_type", "OPER_OPERCLASS_INVALID"), + log_data_string("oper_block", parv[1])); return; } @@ -212,12 +213,12 @@ CMD_FUNC(cmd_oper) sendnumeric(client, ERR_NOOPERHOST); sendnotice(client, "Your maximum number of concurrent oper logins has been reached (%d)", operblock->maxlogins); - sendto_snomask_global - (SNO_OPER, "Failed OPER attempt by %s (%s@%s) using UID %s [maxlogins reached]", - client->name, client->user->username, client->local->sockhost, name); - ircd_log(LOG_OPER, "OPER TOOMANYLOGINS (%s) by (%s!%s@%s)", name, client->name, - client->user->username, client->local->sockhost); - client->local->since += 4; + unreal_log(ULOG_WARNING, "oper", "OPER_FAILED", client, + "Failed OPER attempt by $client.details [reason: $reason] [oper-block: $oper_block]", + log_data_string("reason", "oper::maxlogins limit reached"), + log_data_string("fail_type", "OPER_MAXLOGINS_LIMIT"), + log_data_string("oper_block", parv[1])); + add_fake_lag(client, 4000); return; } @@ -258,13 +259,9 @@ CMD_FUNC(cmd_oper) safe_strdup(client->user->virthost, client->user->cloakedhost); } - sendto_snomask_global(SNO_OPER, - "%s (%s@%s) [%s] is now an operator", - client->name, client->user->username, client->local->sockhost, - parv[1]); - - ircd_log(LOG_OPER, "OPER (%s) by (%s!%s@%s)", name, client->name, client->user->username, - client->local->sockhost); + unreal_log(ULOG_INFO, "oper", "OPER_SUCCESS", client, + "$client.details is now an IRC Operator [oper-block: $oper_block]", + log_data_string("oper_block", parv[1])); /* set oper snomasks */ if (operblock->snomask) @@ -272,19 +269,13 @@ CMD_FUNC(cmd_oper) else set_snomask(client, OPER_SNOMASK); /* set::snomask-on-oper */ - /* some magic to set user mode +s (and snomask +s) if you have any snomasks set */ - if (client->user->snomask) - { - client->user->snomask |= SNO_SNOTICE; - client->umodes |= UMODE_SERVNOTICE; - } - send_umode_out(client, 1, old_umodes); - sendnumeric(client, RPL_SNOMASK, get_snomask_string(client)); + if (client->user->snomask) + sendnumeric(client, RPL_SNOMASK, client->user->snomask); list_add(&client->special_node, &oper_list); - RunHook2(HOOKTYPE_LOCAL_OPER, client, 1); + RunHook(HOOKTYPE_LOCAL_OPER, client, 1, operblock); sendnumeric(client, RPL_YOUREOPER); @@ -300,7 +291,7 @@ CMD_FUNC(cmd_oper) if (!BadPtr(OPER_AUTO_JOIN_CHANS) && strcmp(OPER_AUTO_JOIN_CHANS, "0")) { char *chans = strdup(OPER_AUTO_JOIN_CHANS); - char *args[3] = { + const char *args[3] = { client->name, chans, NULL @@ -316,17 +307,19 @@ CMD_FUNC(cmd_oper) if (!IsSecure(client) && !IsLocalhost(client) && (iConf.plaintext_policy_oper == POLICY_WARN)) { sendnotice_multiline(client, iConf.plaintext_policy_oper_message); - sendto_snomask_global - (SNO_OPER, "OPER %s [%s] used an insecure (non-SSL/TLS) connection to /OPER.", - client->name, name); + unreal_log(ULOG_WARNING, "oper", "OPER_UNSAFE", client, + "Insecure (non-TLS) connection used to OPER up by $client.details [oper-block: $oper_block]", + log_data_string("oper_block", parv[1]), + log_data_string("warn_type", "NO_TLS")); } /* set::outdated-tls-policy::oper 'warn' */ if (IsSecure(client) && (iConf.outdated_tls_policy_oper == POLICY_WARN) && outdated_tls_client(client)) { sendnotice(client, "%s", outdated_tls_client_build_string(iConf.outdated_tls_policy_oper_message, client)); - sendto_snomask_global - (SNO_OPER, "OPER %s [%s] used a connection with an outdated SSL/TLS protocol or cipher to /OPER.", - client->name, name); + unreal_log(ULOG_WARNING, "oper", "OPER_UNSAFE", client, + "Outdated TLS protocol/cipher used to OPER up by $client.details [oper-block: $oper_block]", + log_data_string("oper_block", parv[1]), + log_data_string("warn_type", "OUTDATED_TLS_PROTOCOL_OR_CIPHER")); } } diff --git a/src/modules/operinfo.c b/src/modules/operinfo.c new file mode 100644 index 0000000..7571c35 --- /dev/null +++ b/src/modules/operinfo.c @@ -0,0 +1,99 @@ +/* + * Store oper login in ModData, used by WHOIS and for auditting purposes. + * (C) Copyright 2021-.. Syzop and The UnrealIRCd Team + * License: GPLv2 + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "operinfo", + "5.0", + "Store oper login in ModData", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +int operinfo_local_oper(Client *client, int up, ConfigItem_oper *oper_block); +void operinfo_free(ModData *m); +const char *operinfo_serialize(ModData *m); +void operinfo_unserialize(const char *str, ModData *m); + +ModDataInfo *operlogin_md = NULL; /* Module Data structure which we acquire */ +ModDataInfo *operclass_md = NULL; /* Module Data structure which we acquire */ + +MOD_INIT() +{ + ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&mreq, 0, sizeof(mreq)); + mreq.name = "operlogin"; + mreq.free = operinfo_free; + mreq.serialize = operinfo_serialize; + mreq.unserialize = operinfo_unserialize; + mreq.sync = MODDATA_SYNC_EARLY; + mreq.type = MODDATATYPE_CLIENT; + operlogin_md = ModDataAdd(modinfo->handle, mreq); + if (!operlogin_md) + abort(); + + memset(&mreq, 0, sizeof(mreq)); + mreq.name = "operclass"; + mreq.free = operinfo_free; + mreq.serialize = operinfo_serialize; + mreq.unserialize = operinfo_unserialize; + mreq.sync = MODDATA_SYNC_EARLY; + mreq.type = MODDATATYPE_CLIENT; + operclass_md = ModDataAdd(modinfo->handle, mreq); + if (!operclass_md) + abort(); + + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_OPER, 0, operinfo_local_oper); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int operinfo_local_oper(Client *client, int up, ConfigItem_oper *oper_block) +{ + if (up) + { + moddata_client_set(client, "operlogin", oper_block->name); + moddata_client_set(client, "operclass", oper_block->operclass); + } else { + moddata_client_set(client, "operlogin", NULL); + moddata_client_set(client, "operclass", NULL); + } + return 0; +} + +void operinfo_free(ModData *m) +{ + safe_free(m->str); +} + +const char *operinfo_serialize(ModData *m) +{ + if (!m->str) + return NULL; + return m->str; +} + +void operinfo_unserialize(const char *str, ModData *m) +{ + safe_strdup(m->str, str); +} diff --git a/src/modules/opermotd.c b/src/modules/opermotd.c index 63bd7c6..75398b7 100644 --- a/src/modules/opermotd.c +++ b/src/modules/opermotd.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /opermotd", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() diff --git a/src/modules/part.c b/src/modules/part.c index 937087a..84f940f 100644 --- a/src/modules/part.c +++ b/src/modules/part.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /part", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -59,11 +59,12 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_part) { + char request[BUFSIZE]; Channel *channel; Membership *lp; char *p = NULL, *name; - char *commentx = (parc > 2 && parv[2]) ? parv[2] : NULL; - char *comment; + const char *commentx = (parc > 2 && parv[2]) ? parv[2] : NULL; + const char *comment; int n; int ntargets = 0; int maxtargets = max_targets_for_command("PART"); @@ -96,7 +97,8 @@ CMD_FUNC(cmd_part) } } - for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL) + strlcpy(request, parv[1], sizeof(request)); + for (name = strtoken(&p, request, ","); name; name = strtoken(&p, NULL, ",")) { MessageTag *mtags = NULL; @@ -106,7 +108,7 @@ CMD_FUNC(cmd_part) break; } - channel = get_channel(client, name, 0); + channel = find_channel(name); if (!channel) { sendnumeric(client, ERR_NOSUCHCHANNEL, name); @@ -130,36 +132,30 @@ CMD_FUNC(cmd_part) continue; } - if (!ValidatePermissionsForPath("channel:override:banpartmsg",client,NULL,channel,NULL) && !is_chan_op(client, channel)) { + if (!ValidatePermissionsForPath("channel:override:banpartmsg",client,NULL,channel,NULL) && !check_channel_access(client, channel, "oaq")) { /* Banned? No comment allowed ;) */ if (comment && is_banned(client, channel, BANCHK_MSG, &comment, NULL)) comment = NULL; if (comment && is_banned(client, channel, BANCHK_LEAVE_MSG, &comment, NULL)) comment = NULL; - /* Same for +m */ - if ((channel->mode.mode & MODE_MODERATED) && comment && - !has_voice(client, channel) && !is_half_op(client, channel)) - { - comment = NULL; - } } if (MyConnect(client)) { Hook *tmphook; for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_PART]; tmphook; tmphook = tmphook->next) { - comment = (*(tmphook->func.pcharfunc))(client, channel, comment); + comment = (*(tmphook->func.stringfunc))(client, channel, comment); if (!comment) break; } } /* Create a new message, this one is actually used by 8 calls (though at most 4 max) */ - new_message_special(client, recv_mtags, &mtags, ":%s PART %s", client->name, channel->chname); + new_message_special(client, recv_mtags, &mtags, ":%s PART %s", client->name, channel->name); /* Send to other servers... */ sendto_server(client, 0, 0, mtags, ":%s PART %s :%s", - client->id, channel->chname, comment ? comment : ""); + client->id, channel->name, comment ? comment : ""); if (invisible_user_in_channel(client, channel)) { @@ -167,29 +163,29 @@ CMD_FUNC(cmd_part) if (!comment) { sendto_channel(channel, client, client, - PREFIX_HALFOP|PREFIX_OP|PREFIX_OWNER|PREFIX_ADMIN, 0, + "ho", 0, SEND_LOCAL, mtags, ":%s PART %s", - client->name, channel->chname); + client->name, channel->name); if (MyUser(client)) { sendto_one(client, mtags, ":%s!%s@%s PART %s", - client->name, client->user->username, GetHost(client), channel->chname); + client->name, client->user->username, GetHost(client), channel->name); } } else { sendto_channel(channel, client, client, - PREFIX_HALFOP|PREFIX_OP|PREFIX_OWNER|PREFIX_ADMIN, 0, + "ho", 0, SEND_LOCAL, mtags, ":%s PART %s %s", - client->name, channel->chname, comment); + client->name, channel->name, comment); if (MyUser(client)) { sendto_one(client, mtags, ":%s!%s@%s PART %s %s", client->name, client->user->username, GetHost(client), - channel->chname, comment); + channel->name, comment); } } } @@ -200,21 +196,21 @@ CMD_FUNC(cmd_part) { sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, ":%s PART %s", - client->name, channel->chname); + client->name, channel->name); } else { sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, ":%s PART %s :%s", - client->name, channel->chname, comment); + client->name, channel->name, comment); } } if (MyUser(client)) - RunHook4(HOOKTYPE_LOCAL_PART, client, channel, mtags, comment); + RunHook(HOOKTYPE_LOCAL_PART, client, channel, mtags, comment); else - RunHook4(HOOKTYPE_REMOTE_PART, client, channel, mtags, comment); + RunHook(HOOKTYPE_REMOTE_PART, client, channel, mtags, comment); free_message_tags(mtags); - remove_user_from_channel(client, channel); + remove_user_from_channel(client, channel, 0); } } diff --git a/src/modules/pass.c b/src/modules/pass.c index b67fb6c..89084e1 100644 --- a/src/modules/pass.c +++ b/src/modules/pass.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /pass", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ @@ -119,7 +119,7 @@ int _check_banned(Client *client, int exitflags) */ CMD_FUNC(cmd_pass) { - char *password = parc > 1 ? parv[1] : NULL; + const char *password = parc > 1 ? parv[1] : NULL; if (!MyConnect(client) || (!IsUnknown(client) && !IsHandshake(client))) { @@ -137,5 +137,5 @@ CMD_FUNC(cmd_pass) safe_strldup(client->local->passwd, password, PASSWDLEN+1); /* note: the original non-truncated password is supplied as 2nd parameter. */ - RunHookReturn2(HOOKTYPE_LOCAL_PASS, client, password, !=0); + RunHookReturn(HOOKTYPE_LOCAL_PASS, !=0, client, password); } diff --git a/src/modules/pingpong.c b/src/modules/pingpong.c index 42b577d..d9bdd9c 100644 --- a/src/modules/pingpong.c +++ b/src/modules/pingpong.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "ping, pong and nospoof", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ MOD_INIT() @@ -66,7 +66,7 @@ MOD_UNLOAD() CMD_FUNC(cmd_ping) { Client *target; - char *origin, *destination; + const char *origin, *destination; if (parc < 2 || BadPtr(parv[1])) { @@ -147,7 +147,7 @@ CMD_FUNC(cmd_nospoof) me.name, client->name); if (is_handshake_finished(client)) - register_user(client, client->name, client->user->username, NULL, NULL, NULL); + register_user(client); } /* @@ -158,7 +158,7 @@ CMD_FUNC(cmd_nospoof) CMD_FUNC(cmd_pong) { Client *target; - char *origin, *destination; + const char *origin, *destination; if (!IsRegistered(client)) { diff --git a/src/modules/plaintext-policy.c b/src/modules/plaintext-policy.c index 6df0cbe..d5159d6 100644 --- a/src/modules/plaintext-policy.c +++ b/src/modules/plaintext-policy.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "5.0", "Plaintext Policy CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -52,7 +52,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -char *plaintext_policy_capability_parameter(Client *client) +const char *plaintext_policy_capability_parameter(Client *client) { static char buf[128]; diff --git a/src/modules/protoctl.c b/src/modules/protoctl.c index c1b3c4e..a1c7b31 100644 --- a/src/modules/protoctl.c +++ b/src/modules/protoctl.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /protoctl", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -94,6 +94,10 @@ CMD_FUNC(cmd_protoctl) { SetCapability(client, "userhost-in-names"); } + else if (IsUser(client)) + { + return; + } else if (!strcmp(name, "VL")) { SetVL(client); @@ -114,6 +118,10 @@ CMD_FUNC(cmd_protoctl) { SetMTAGS(client); } + else if (!strcmp(name, "NEXTBANS")) + { + SetNEXTBANS(client); + } else if (!strcmp(name, "NICKCHARS") && value) { if (!IsServer(client) && !IsEAuth(client) && !IsHandshake(client)) @@ -124,22 +132,23 @@ CMD_FUNC(cmd_protoctl) */ if (strstr(charsys_get_current_languages(), "utf8") && !strstr(value, "utf8")) { - char buf[512]; - snprintf(buf, sizeof(buf), "Server %s has utf8 in set::allowed-nickchars but %s does not. Link rejected.", - me.name, *client->name ? client->name : "other side"); - sendto_realops("\002ERROR\001 %s", buf); - exit_client(client, NULL, buf); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_CHARSYS_INCOMPATIBLE", client, + "Server link $client rejected. Server $me_name has utf8 in set::allowed-nickchars but $client does not.", + log_data_string("me_name", me.name)); + exit_client(client, NULL, "Incompatible set::allowed-nickchars setting"); return; } /* We compare the character sets to see if we should warn opers about any mismatch... */ if (strcmp(value, charsys_get_current_languages())) { - sendto_realops("\002WARNING!!!!\002 Link %s does not have the same set::allowed-nickchars settings (or is " - "a different UnrealIRCd version), this MAY cause display issues. Our charset: '%s', theirs: '%s'", - get_client_name(client, FALSE), charsys_get_current_languages(), value); + unreal_log(ULOG_WARNING, "link", "LINK_WARNING_CHARSYS", client, + "Server link $client does not have the same set::allowed-nickchars settings, " + "this may possibly cause display issues. Our charset: '$our_charsys', theirs: '$their_charsys'", + log_data_string("our_charsys", charsys_get_current_languages()), + log_data_string("their_charsys", value)); } - if (client->serv) - safe_strdup(client->serv->features.nickchars, value); + if (client->server) + safe_strdup(client->server->features.nickchars, value); /* If this is a runtime change (so post-handshake): */ if (IsServer(client)) @@ -155,15 +164,13 @@ CMD_FUNC(cmd_protoctl) their_value = allowed_channelchars_strtoval(value); if (their_value != iConf.allowed_channelchars) { - char linkerr[256]; - ircsnprintf(linkerr, sizeof(linkerr), - "Link rejected. Server %s has set::allowed-channelchars '%s' " - "while %s has a value of '%s'. " - "Please choose the same value on all servers.", - client->name, value, - me.name, allowed_channelchars_valtostr(iConf.allowed_channelchars)); - sendto_realops("ERROR: %s", linkerr); - exit_client(client, NULL, linkerr); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_ALLOWED_CHANNELCHARS_INCOMPATIBLE", client, + "Server link $client rejected. Server has set::allowed-channelchars setting " + "of $their_allowed_channelchars, while we have $our_allowed_channelchars.\n" + "Please set set::allowed-channelchars to the same value on all servers.", + log_data_string("their_allowed_channelchars", value), + log_data_string("our_allowed_channelchars", allowed_channelchars_valtostr(iConf.allowed_channelchars))); + exit_client(client, NULL, "Incompatible set::allowed-channelchars setting"); return; } } @@ -198,9 +205,10 @@ CMD_FUNC(cmd_protoctl) if ((aclient = hash_find_id(sid, NULL)) != NULL) { - sendto_one(client, NULL, "ERROR :SID %s already exists from %s", aclient->id, aclient->name); - sendto_snomask(SNO_SNOTICE, "Link %s rejected - SID %s already exists from %s", - get_client_name(client, FALSE), aclient->id, aclient->name); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_SID_COLLISION", client, + "Server link $client rejected. Server with SID $sid already exist via uplink $exiting_client.server.uplink.", + log_data_string("sid", sid), + log_data_client("existing_client", aclient)); exit_client(client, NULL, "SID collision"); return; } @@ -236,13 +244,8 @@ CMD_FUNC(cmd_protoctl) } servername = strtoken(&p, buf, ","); - if (!servername || (strlen(servername) > HOSTLEN) || !strchr(servername, '.')) + if (!servername || !valid_server_name(servername)) { - sendto_one(client, NULL, "ERROR :Bogus server name in EAUTH (%s)", servername ? servername : ""); - sendto_snomask - (SNO_JUNK, - "WARNING: Bogus server name (%s) from %s in EAUTH (maybe just a fishy client)", - servername ? servername : "", get_client_name(client, TRUE)); exit_client(client, NULL, "Bogus server name"); return; } @@ -258,7 +261,12 @@ CMD_FUNC(cmd_protoctl) } } - if (!verify_link(client, servername, &aconf)) + /* Set client->name but don't add to hash list, this gives better + * log messages and should be safe. See CMTSRV941 in server.c. + */ + strlcpy(client->name, servername, sizeof(client->name)); + + if (!verify_link(client, &aconf)) return; /* note: software, protocol and flags may be NULL */ @@ -266,16 +274,11 @@ CMD_FUNC(cmd_protoctl) return; SetEAuth(client); - make_server(client); /* allocate and set client->serv */ - /* Set client->name but don't add to hash list. The real work on - * that is done in cmd_server. We just set it here for display - * purposes of error messages (such as reject due to clock). - */ - strlcpy(client->name, servername, sizeof(client->name)); + make_server(client); /* allocate and set client->server */ if (protocol) - client->serv->features.protocol = atoi(protocol); + client->server->features.protocol = atoi(protocol); if (software) - safe_strdup(client->serv->features.software, software); + safe_strdup(client->server->features.software, software); if (!IsHandshake(client) && aconf) /* Send PASS early... */ sendto_one(client, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); } @@ -287,12 +290,12 @@ CMD_FUNC(cmd_protoctl) if (!IsEAuth(client)) continue; - if (client->serv->features.protocol < 2351) + if (client->server->features.protocol < 2351) continue; /* old SERVERS= version */ /* Other side lets us know which servers are behind it. * SERVERS=[,id, aclient->name); - sendto_realops("Link %s cancelled, server with SID %s (%s) already exists", - get_client_name(aclient, TRUE), aclient->id, aclient->name); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_DUPLICATE_SID", client, + "Denied server $client: Server with SID $existing_client.id ($existing_client) is already linked.", + log_data_client("existing_client", aclient)); exit_client(client, NULL, "Server Exists (or non-unique me::sid)"); return; } @@ -311,12 +313,13 @@ CMD_FUNC(cmd_protoctl) aclient = find_pending_net_duplicates(client, &srv, &sid); if (aclient) { - sendto_one(client, NULL, "ERROR :Server with SID %s is being introduced by another server as well. " - "Just wait a moment for it to synchronize...", sid); - sendto_realops("Link %s cancelled, server would introduce server with SID %s, which " - "server %s is also about to introduce. Just wait a moment for it to synchronize...", - get_client_name(aclient, TRUE), sid, get_client_name(srv, TRUE)); - exit_client(client, NULL, "Server Exists (just wait a moment)"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_DUPLICATE_SID_LINKED", client, + "Denied server $client: Server would (later) introduce SID $sid, " + "but we already have SID $sid linked ($existing_client)\n" + "Possible race condition, just wait a moment for the network to synchronize...", + log_data_string("sid", sid), + log_data_client("existing_client", aclient)); + exit_client(client, NULL, "Server Exists (just wait a moment...)"); return; } @@ -327,51 +330,34 @@ CMD_FUNC(cmd_protoctl) else if (!strcmp(name, "TS") && value && (IsServer(client) || IsEAuth(client))) { long t = atol(value); - char msg[512], linkerr[512]; - + if (t < 10000) continue; /* ignore */ - - *msg = *linkerr = '\0'; - + if ((TStime() - t) > MAX_SERVER_TIME_OFFSET) { - snprintf(linkerr, sizeof(linkerr), - "Your clock is %lld seconds behind my clock. " - "Please verify both your clock and mine, " - "fix it and try linking again.", - (long long)(TStime() - t)); - snprintf(msg, sizeof(msg), - "Rejecting link %s: our clock is %lld seconds ahead. " - "Please verify the clock on both %s (them) and %s (us). " - "Correct time is very important for IRC servers, " - "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", - get_client_name(client, TRUE), - (long long)(TStime() - t), - client->name, me.name); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_CLOCK_INCORRECT", client, + "Denied server $client: clock on server $client is $time_delta " + "seconds behind the clock of $me_name.\n" + "Correct time is very important for IRC servers, " + "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", + log_data_integer("time_delta", TStime() - t), + log_data_string("me_name", me.name)); + exit_client_fmt(client, NULL, "Incorrect clock. Our clocks are %lld seconds apart.", + (long long)(TStime() - t)); + return; } else if ((t - TStime()) > MAX_SERVER_TIME_OFFSET) { - snprintf(linkerr, sizeof(linkerr), - "Your clock is %lld seconds ahead of my clock. " - "Please verify both your clock and mine, fix it, " - "and try linking again.", - (long long)(t - TStime())); - snprintf(msg, sizeof(msg), - "Rejecting link %s: our clock is %lld seconds behind. " - "Please verify the clock on both %s (them) and %s (us). " - "Correct time is very important for IRC servers, " - "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", - get_client_name(client, TRUE), - (long long)(t - TStime()), - client->name, me.name); - } - - if (*msg) - { - sendto_realops("%s", msg); - ircd_log(LOG_ERROR, "%s", msg); - exit_client(client, NULL, linkerr); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_CLOCK_INCORRECT", client, + "Denied server $client: clock on server $client is $time_delta " + "seconds ahead the clock of $me_name.\n" + "Correct time is very important for IRC servers, " + "see https://www.unrealircd.org/docs/FAQ#fix-your-clock", + log_data_integer("time_delta", t - TStime()), + log_data_string("me_name", me.name)); + exit_client_fmt(client, NULL, "Incorrect clock. Our clocks are %lld seconds apart.", + (long long)(t - TStime())); return; } } @@ -379,23 +365,23 @@ CMD_FUNC(cmd_protoctl) { client->local->proto |= PROTO_MLOCK; } - else if (!strcmp(name, "CHANMODES") && value && client->serv) + else if (!strcmp(name, "CHANMODES") && value && client->server) { parse_chanmodes_protoctl(client, value); /* If this is a runtime change (so post-handshake): */ if (IsServer(client)) broadcast_sinfo(client, NULL, client); } - else if (!strcmp(name, "USERMODES") && value && client->serv) + else if (!strcmp(name, "USERMODES") && value && client->server) { - safe_strdup(client->serv->features.usermodes, value); + safe_strdup(client->server->features.usermodes, value); /* If this is a runtime change (so post-handshake): */ if (IsServer(client)) broadcast_sinfo(client, NULL, client); } - else if (!strcmp(name, "BOOTED") && value && client->serv) + else if (!strcmp(name, "BOOTED") && value && client->server) { - client->serv->boottime = atol(value); + client->server->boottime = atol(value); } else if (!strcmp(name, "EXTSWHOIS")) { @@ -409,7 +395,7 @@ CMD_FUNC(cmd_protoctl) */ } - if (first_protoctl && IsHandshake(client) && client->serv && !IsServerSent(client)) /* first & outgoing connection to server */ + if (first_protoctl && IsHandshake(client) && client->server && !IsServerSent(client)) /* first & outgoing connection to server */ { /* SERVER message moved from completed_connection() to here due to EAUTH/SERVERS PROTOCTL stuff, * which needed to be delayed until after both sides have received SERVERS=xx (..or not.. in case diff --git a/src/modules/quit.c b/src/modules/quit.c index 8c59821..4bddef2 100644 --- a/src/modules/quit.c +++ b/src/modules/quit.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /quit", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -59,11 +59,17 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_quit) { - char *comment = (parc > 1 && parv[1]) ? parv[1] : client->name; - static char commentbuf[MAXQUITLEN + 1]; + const char *comment = (parc > 1 && parv[1]) ? parv[1] : client->name; + char commentbuf[MAXQUITLEN + 1]; + char commentbuf2[MAXQUITLEN + 1]; - if (parv[1] && (strlen(comment) > iConf.quit_length)) - comment[iConf.quit_length] = '\0'; + if (parc > 1 && parv[1]) + { + strlncpy(commentbuf, parv[1], sizeof(commentbuf), iConf.quit_length); + comment = commentbuf; + } else { + comment = client->name; + } if (MyUser(client)) { @@ -91,14 +97,14 @@ CMD_FUNC(cmd_quit) if (!ValidatePermissionsForPath("immune:anti-spam-quit-message-time",client,NULL,NULL,NULL) && ANTI_SPAM_QUIT_MSG_TIME) { - if (client->local->firsttime+ANTI_SPAM_QUIT_MSG_TIME > TStime()) + if (client->local->creationtime+ANTI_SPAM_QUIT_MSG_TIME > TStime()) comment = client->name; } if (iConf.part_instead_of_quit_on_comment_change && MyUser(client)) { Membership *lp, *lp_next; - char *newcomment; + const char *newcomment; Channel *channel; for (lp = client->user->channel; lp; lp = lp_next) @@ -109,7 +115,7 @@ CMD_FUNC(cmd_quit) for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_QUIT_CHAN]; tmphook; tmphook = tmphook->next) { - newcomment = (*(tmphook->func.pcharfunc))(client, channel, comment); + newcomment = (*(tmphook->func.stringfunc))(client, channel, comment); if (!newcomment) break; } @@ -120,13 +126,21 @@ CMD_FUNC(cmd_quit) /* Comment changed? Then PART the user before we do the QUIT. */ if (comment != newcomment) { - char *parx[4]; + const char *parx[4]; + char tmp[512]; int ret; + parx[0] = NULL; - parx[1] = channel->chname; - parx[2] = newcomment; - parx[3] = NULL; + parx[1] = channel->name; + if (newcomment) + { + strlcpy(tmp, newcomment, sizeof(tmp)); + parx[2] = tmp; + parx[3] = NULL; + } else { + parx[2] = NULL; + } do_cmd(client, recv_mtags, "PART", newcomment ? 3 : 2, parx); /* This would be unusual, but possible (somewhere in the future perhaps): */ @@ -138,7 +152,7 @@ CMD_FUNC(cmd_quit) for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_QUIT]; tmphook; tmphook = tmphook->next) { - comment = (*(tmphook->func.pcharfunc))(client, comment); + comment = (*(tmphook->func.stringfunc))(client, comment); if (!comment) { comment = client->name; @@ -147,11 +161,11 @@ CMD_FUNC(cmd_quit) } if (PREFIX_QUIT) - snprintf(commentbuf, sizeof(commentbuf), "%s: %s", PREFIX_QUIT, comment); + snprintf(commentbuf2, sizeof(commentbuf2), "%s: %s", PREFIX_QUIT, comment); else - strlcpy(commentbuf, comment, sizeof(commentbuf)); + strlcpy(commentbuf2, comment, sizeof(commentbuf2)); - exit_client(client, recv_mtags, commentbuf); + exit_client(client, recv_mtags, commentbuf2); } else { diff --git a/src/modules/reply-tag.c b/src/modules/reply-tag.c index 0364c78..ef9e295 100644 --- a/src/modules/reply-tag.c +++ b/src/modules/reply-tag.c @@ -30,11 +30,11 @@ ModuleHeader MOD_HEADER "5.0", "+reply client tag", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -int replytag_mtag_is_ok(Client *client, char *name, char *value); -void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int replytag_mtag_is_ok(Client *client, const char *name, const char *value); +void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -73,9 +73,9 @@ MOD_UNLOAD() /** This function verifies if the client sending the mtag is permitted to do so. */ -int replytag_mtag_is_ok(Client *client, char *name, char *value) +int replytag_mtag_is_ok(Client *client, const char *name, const char *value) { - char *p; + const char *p; /* Require a non-empty parameter */ if (BadPtr(value)) @@ -92,7 +92,7 @@ int replytag_mtag_is_ok(Client *client, char *name, char *value) return 1; /* OK */ } -void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_replytag(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m; diff --git a/src/modules/reputation.c b/src/modules/reputation.c index 6a76881..0ca0064 100644 --- a/src/modules/reputation.c +++ b/src/modules/reputation.c @@ -64,7 +64,7 @@ ModuleHeader MOD_HEADER REPUTATION_VERSION, "Known IP's scoring system", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Defines */ @@ -81,9 +81,10 @@ ModuleHeader MOD_HEADER #define WARN_WRITE_ERROR(fname) \ do { \ - sendto_realops_and_log("[reputation] Error writing to temporary database file " \ - "'%s': %s (DATABASE NOT SAVED)", \ - fname, unrealdb_get_error_string()); \ + unreal_log(ULOG_ERROR, "reputation", "REPUTATION_FILE_WRITE_ERROR", NULL, \ + "[reputation] Error writing to temporary database file $filename: $system_error", \ + log_data_string("filename", fname), \ + log_data_string("system_error", unrealdb_get_error_string())); \ } while(0) #define W_SAFE(x) \ @@ -131,21 +132,21 @@ ModDataInfo *reputation_md; /* Module Data structure which we acquire */ /* Forward declarations */ void reputation_md_free(ModData *m); -char *reputation_md_serialize(ModData *m); -void reputation_md_unserialize(char *str, ModData *m); +const char *reputation_md_serialize(ModData *m); +void reputation_md_unserialize(const char *str, ModData *m); void reputation_config_setdefaults(struct cfgstruct *cfg); void reputation_free_config(struct cfgstruct *cfg); CMD_FUNC(reputation_cmd); CMD_FUNC(reputationunperm); -int reputation_whois(Client *client, Client *target); +int reputation_whois(Client *client, Client *target, NameValuePrioList **list); int reputation_set_on_connect(Client *client); int reputation_pre_lconnect(Client *client); 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); int reputation_config_posttest(int *errs); -static uint64_t hash_reputation_entry(char *ip); -ReputationEntry *find_reputation_entry(char *ip); +static uint64_t hash_reputation_entry(const char *ip); +ReputationEntry *find_reputation_entry(const char *ip); void add_reputation_entry(ReputationEntry *e); EVENT(delete_old_records); EVENT(add_scores); @@ -162,7 +163,7 @@ MOD_TEST() reputation_config_setdefaults(&test); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, reputation_config_test); HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, reputation_config_posttest); - CallbackAddEx(modinfo->handle, CALLBACKTYPE_REPUTATION_STARTTIME, reputation_starttime_callback); + CallbackAdd(modinfo->handle, CALLBACKTYPE_REPUTATION_STARTTIME, reputation_starttime_callback); return MOD_SUCCESS; } @@ -239,7 +240,7 @@ MOD_LOAD() MOD_UNLOAD() { - if (loop.ircd_terminating) + if (loop.terminating) reputation_save_db(); reputation_free_config(&test); reputation_free_config(&cfg); @@ -283,37 +284,37 @@ int reputation_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; /* We are only interrested in set::reputation.. */ - if (!ce || strcmp(ce->ce_varname, "reputation")) + if (!ce || strcmp(ce->name, "reputation")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata) + if (!cep->value) { config_error("%s:%i: blank set::reputation::%s without value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; continue; } else - if (!strcmp(cep->ce_varname, "database")) + if (!strcmp(cep->name, "database")) { - convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); - safe_strdup(test.database, cep->ce_vardata); + convert_to_absolute_path(&cep->value, PERMDATADIR); + safe_strdup(test.database, cep->value); } else - if (!strcmp(cep->ce_varname, "db-secret")) + if (!strcmp(cep->name, "db-secret")) { - char *err; - if ((err = unrealdb_test_secret(cep->ce_vardata))) + const char *err; + if ((err = unrealdb_test_secret(cep->value))) { - config_error("%s:%i: set::channeldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err); + config_error("%s:%i: set::channeldb::db-secret: %s", cep->file->filename, cep->line_number, err); errors++; continue; } - safe_strdup(test.db_secret, cep->ce_vardata); + safe_strdup(test.db_secret, cep->value); } else { config_error("%s:%i: unknown directive set::reputation::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; continue; } @@ -331,18 +332,18 @@ int reputation_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::reputation.. */ - if (!ce || strcmp(ce->ce_varname, "reputation")) + if (!ce || strcmp(ce->name, "reputation")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "database")) + if (!strcmp(cep->name, "database")) { - safe_strdup(cfg.database, cep->ce_vardata); + safe_strdup(cfg.database, cep->value); } else - if (!strcmp(cep->ce_varname, "db-secret")) + if (!strcmp(cep->name, "db-secret")) { - safe_strdup(cfg.db_secret, cep->ce_vardata); + safe_strdup(cfg.db_secret, cep->value); } } return 1; @@ -465,8 +466,9 @@ void reputation_load_db_old(void) #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "Reputation benchmark: LOAD DB: %lld microseconds", - (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_BENCHMARK", NULL, + "[reputation] Benchmark: LOAD DB: $time_msec microseconds", + log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); #endif } @@ -529,8 +531,9 @@ int reputation_load_db_new(UnrealDB *db) unrealdb_close(db); #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "Reputation benchmark: LOAD DB: %lld microseconds", - (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_BENCHMARK", NULL, + "Reputation benchmark: LOAD DB: $time_msec microseconds", + log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); #endif return 1; } @@ -656,8 +659,9 @@ write_fail: #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "Reputation benchmark: SAVE DB: %lld microseconds", - (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_BENCHMARK", NULL, + "Reputation benchmark: SAVE DB: $time_msec microseconds", + log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); #endif return 1; @@ -677,7 +681,7 @@ int reputation_save_db(void) #endif #ifdef TEST - sendto_realops("REPUTATION IS RUNNING IN TEST MODE. SAVING DB'S..."); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_TEST", NULL, "Reputation in running in test mode. Saving DB's...."); #endif /* Comment this out after one or more releases (means you cannot downgrade to <=5.0.9.1 anymore) */ @@ -741,13 +745,14 @@ int reputation_save_db(void) #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "Reputation benchmark: SAVE DB: %lld microseconds", - (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_BENCHMARK", NULL, + "Reputation benchmark: SAVE DB: $time_msec microseconds", + log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); #endif return 1; } -static uint64_t hash_reputation_entry(char *ip) +static uint64_t hash_reputation_entry(const char *ip) { return siphash(ip, siphashkey_reputation) % REPUTATION_HASH_TABLE_SIZE; } @@ -759,7 +764,7 @@ void add_reputation_entry(ReputationEntry *e) AddListItem(e, ReputationHashTable[hashv]); } -ReputationEntry *find_reputation_entry(char *ip) +ReputationEntry *find_reputation_entry(const char *ip) { ReputationEntry *e; int hashv = hash_reputation_entry(ip); @@ -924,8 +929,11 @@ EVENT(delete_old_records) if (is_reputation_expired(e)) { #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "Deleting expired entry for '%s' (score %hd, last seen %lld seconds ago)", - e->ip, e->score, (long long)(TStime() - e->last_seen)); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_EXPIRY", NULL, + "Deleting expired entry for $ip (score $score, last seen $time_delta seconds ago)", + log_data_string("ip", e->ip), + log_data_integer("score", e->score), + log_data_integer("time_delta", TStime() - e->last_seen)); #endif DelListItem(e, ReputationHashTable[i]); safe_free(e); @@ -935,8 +943,9 @@ EVENT(delete_old_records) #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "Reputation benchmark: EXPIRY IN MEM: %lld microseconds", - (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_BENCHMARK", NULL, + "Reputation benchmark: EXPIRY IN MEM: $time_msec microseconds", + log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); #endif } @@ -955,9 +964,9 @@ CMD_FUNC(reputationunperm) ModuleSetOptions(ModInf.handle, MOD_OPT_PERM, 0); - sendto_realops("%s used /REPUTATIONUNPERM. On next REHASH the module can be RELOADED or UNLOADED. " - "Note however that for a few minutes the scoring may be skipped, so don't do this too often.", - client->name); + unreal_log(ULOG_INFO, "reputation", "REPUTATIONUNPERM_COMMAND", client, + "$client used /REPUTATIONUNPERM. On next REHASH the module can be RELOADED or UNLOADED. " + "Note however that for a few minutes the scoring may be skipped, so don't do this too often."); } int reputation_connect_extinfo(Client *client, NameValuePrioList **list) @@ -989,7 +998,7 @@ void reputation_channel_query(Client *client, Channel *channel) int cnt = 0, i, j; ReputationEntry *e; - sendtxtnumeric(client, "Users and reputation scores for %s:", channel->chname); + sendtxtnumeric(client, "Users and reputation scores for %s:", channel->name); /* Step 1: build a list of nicks and their reputation */ nicks = safe_alloc((channel->users+1) * sizeof(char *)); @@ -1005,8 +1014,11 @@ void reputation_channel_query(Client *client, Channel *channel) } if (++cnt > channel->users) { - sendto_ops("[BUG] reputation_channel_query() expected %d users but %d (or more) were present in %s", - channel->users, cnt, channel->chname); + unreal_log(ULOG_WARNING, "bug", "REPUTATION_CHANNEL_QUERY_BUG", client, + "[BUG] reputation_channel_query() expected $expected_users users, but $found_users (or more) users were present in $channel", + log_data_integer("expected_users", channel->users), + log_data_integer("found_users", cnt), + log_data_string("channel", channel->name)); #ifdef DEBUGMODE abort(); #endif @@ -1083,7 +1095,7 @@ void reputation_list_query(Client *client, int maxscore) CMD_FUNC(reputation_user_cmd) { ReputationEntry *e; - char *ip; + const char *ip; if (!IsOper(client)) { @@ -1121,16 +1133,16 @@ CMD_FUNC(reputation_user_cmd) } else if (parv[1][0] == '#') { - Channel *channel = find_channel(parv[1], NULL); + Channel *channel = find_channel(parv[1]); if (!channel) { sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]); return; } /* corner case: ircop without proper permissions and not in channel */ - if (!ValidatePermissionsForPath("channel:see:names:invisible",client,NULL,NULL,NULL) && !get_access(client,channel)) + if (!ValidatePermissionsForPath("channel:see:names:invisible",client,NULL,NULL,NULL) && !IsMember(client,channel)) { - sendnumeric(client, ERR_NOTONCHANNEL, channel->chname); + sendnumeric(client, ERR_NOTONCHANNEL, channel->name); return; } reputation_channel_query(client, channel); @@ -1147,7 +1159,7 @@ CMD_FUNC(reputation_user_cmd) reputation_list_query(client, max); return; } else { - Client *target = find_person(parv[1], NULL); + Client *target = find_user(parv[1], NULL); if (!target) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); @@ -1206,7 +1218,7 @@ CMD_FUNC(reputation_user_cmd) CMD_FUNC(reputation_server_cmd) { ReputationEntry *e; - char *ip; + const char *ip; int score; int allow_reply; @@ -1239,8 +1251,11 @@ CMD_FUNC(reputation_server_cmd) */ sendto_one(client, NULL, ":%s REPUTATION %s *%d", me.id, parv[1], e->score); #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[reputation] Score for '%s' from %s is %d, but we have %d, sending back %d", - ip, client->name, score, e->score, e->score); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_DIFFERS", client, + "Reputation score for for $ip from $client is $their_score, but we have $score, sending back $score", + log_data_string("ip", ip), + log_data_integer("their_score", score), + log_data_integer("score", e->score)); #endif score = e->score; /* Update for propagation in the non-client direction */ } @@ -1249,8 +1264,11 @@ CMD_FUNC(reputation_server_cmd) if (e && (score > e->score)) { #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[reputation] Score for '%s' from %s is %d, but we have %d, updating our score to %d", - ip, client->name, score, e->score, score); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_DIFFERS", client, + "Reputation score for for $ip from $client is $their_score, but we have $score, updating our score to $score", + log_data_string("ip", ip), + log_data_integer("their_score", score), + log_data_integer("score", e->score)); #endif e->score = score; } @@ -1259,8 +1277,11 @@ CMD_FUNC(reputation_server_cmd) if (!e && (score > 0)) { #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[reputation] Score for '%s' from %s is %d, we had no entry, adding it", - ip, client->name, score); + unreal_log(ULOG_DEBUG, "reputation", "REPUTATION_NEW", client, + "Reputation score for for $ip from $client is $their_score, we had no entry, adding it", + log_data_string("ip", ip), + log_data_integer("their_score", score), + log_data_integer("score", 0)); #endif e = safe_alloc(sizeof(ReputationEntry)+strlen(ip)); strcpy(e->ip, ip); /* safe, see alloc above */ @@ -1286,18 +1307,19 @@ CMD_FUNC(reputation_cmd) reputation_server_cmd(client, recv_mtags, parc, parv); } -int reputation_whois(Client *client, Client *target) +int reputation_whois(Client *client, Client *target, NameValuePrioList **list) { - int reputation = Reputation(target); + int reputation; - if (!IsOper(client)) - return 0; /* only opers can see this.. */ + if (whois_get_policy(client, target, "reputation") != WHOIS_CONFIG_DETAILS_FULL) + return 0; + reputation = Reputation(target); if (reputation > 0) { - sendto_one(client, NULL, ":%s %d %s %s :is using an IP with a reputation score of %d", - me.name, RPL_WHOISSPECIAL, client->name, - target->name, reputation); + add_nvplist_numeric_fmt(list, 0, "reputation", client, RPL_WHOISSPECIAL, + "%s :is using an IP with a reputation score of %d", + target->name, reputation); } return 0; } @@ -1308,7 +1330,7 @@ void reputation_md_free(ModData *m) m->l = 0; } -char *reputation_md_serialize(ModData *m) +const char *reputation_md_serialize(ModData *m) { static char buf[32]; if (m->i == 0) @@ -1317,7 +1339,7 @@ char *reputation_md_serialize(ModData *m) return buf; } -void reputation_md_unserialize(char *str, ModData *m) +void reputation_md_unserialize(const char *str, ModData *m) { m->i = atoi(str); } diff --git a/src/modules/require-module.c b/src/modules/require-module.c index 8b74b10..ab80469 100644 --- a/src/modules/require-module.c +++ b/src/modules/require-module.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER = { "5.0.1", "Require/deny modules across the network", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; typedef struct _denymod DenyMod; @@ -187,50 +187,50 @@ int reqmods_configtest_deny(ConfigFile *cf, ConfigEntry *ce, int type, int *errs int has_name, has_reason; // We are only interested in deny module { } - if (strcmp(ce->ce_vardata, "module")) + if (strcmp(ce->value, "module")) return 0; has_name = has_reason = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strlen(cep->ce_varname)) + if (!strlen(cep->name)) { - config_error("%s:%i: blank directive for deny module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + config_error("%s:%i: blank directive for deny module { } block", cep->file->filename, cep->line_number); errors++; continue; } - if (!cep->ce_vardata || !strlen(cep->ce_vardata)) + if (!cep->value || !strlen(cep->value)) { - config_error("%s:%i: blank %s without value for deny module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: blank %s without value for deny module { } block", cep->file->filename, cep->line_number, cep->name); errors++; continue; } - if (!strcmp(cep->ce_varname, "name")) + if (!strcmp(cep->name, "name")) { if (has_name) { - config_error("%s:%i: duplicate %s for deny module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: duplicate %s for deny module { } block", cep->file->filename, cep->line_number, cep->name); continue; } // We do a loose check here because a module might not be fully loaded yet - if (find_modptr_byname(cep->ce_vardata, 0)) + if (find_modptr_byname(cep->value, 0)) { - config_error("[require-module] Module '%s' was specified as denied but we've actually loaded it ourselves", cep->ce_vardata); + config_error("[require-module] Module '%s' was specified as denied but we've actually loaded it ourselves", cep->value); errors++; } has_name = 1; continue; } - if (!strcmp(cep->ce_varname, "reason")) // Optional + if (!strcmp(cep->name, "reason")) // Optional { // Still check for duplicate directives though if (has_reason) { - config_error("%s:%i: duplicate %s for deny module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: duplicate %s for deny module { } block", cep->file->filename, cep->line_number, cep->name); errors++; continue; } @@ -238,13 +238,13 @@ int reqmods_configtest_deny(ConfigFile *cf, ConfigEntry *ce, int type, int *errs continue; } - config_error("%s:%i: unknown directive %s for deny module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: unknown directive %s for deny module { } block", cep->file->filename, cep->line_number, cep->name); errors++; } if (!has_name) { - config_error("%s:%i: missing required 'name' directive for deny module { } block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + config_error("%s:%i: missing required 'name' directive for deny module { } block", ce->file->filename, ce->line_number); errors++; } @@ -257,21 +257,21 @@ int reqmods_configrun_deny(ConfigFile *cf, ConfigEntry *ce, int type) ConfigEntry *cep; DenyMod *dmod; - if (strcmp(ce->ce_vardata, "module")) + if (strcmp(ce->value, "module")) return 0; dmod = safe_alloc(sizeof(DenyMod)); - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "name")) + if (!strcmp(cep->name, "name")) { - safe_strdup(dmod->name, cep->ce_vardata); + safe_strdup(dmod->name, cep->value); continue; } - if (!strcmp(cep->ce_varname, "reason")) + if (!strcmp(cep->name, "reason")) { - safe_strdup(dmod->reason, cep->ce_vardata); + safe_strdup(dmod->reason, cep->value); continue; } } @@ -290,37 +290,37 @@ int reqmods_configtest_require(ConfigFile *cf, ConfigEntry *ce, int type, int *e int has_name, has_minversion; // We are only interested in require module { } - if (strcmp(ce->ce_vardata, "module")) + if (strcmp(ce->value, "module")) return 0; has_name = has_minversion = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strlen(cep->ce_varname)) + if (!strlen(cep->name)) { - config_error("%s:%i: blank directive for require module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + config_error("%s:%i: blank directive for require module { } block", cep->file->filename, cep->line_number); errors++; continue; } - if (!cep->ce_vardata || !strlen(cep->ce_vardata)) + if (!cep->value || !strlen(cep->value)) { - config_error("%s:%i: blank %s without value for require module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: blank %s without value for require module { } block", cep->file->filename, cep->line_number, cep->name); errors++; continue; } - if (!strcmp(cep->ce_varname, "name")) + if (!strcmp(cep->name, "name")) { if (has_name) { - config_error("%s:%i: duplicate %s for require module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: duplicate %s for require module { } block", cep->file->filename, cep->line_number, cep->name); continue; } - if (!find_modptr_byname(cep->ce_vardata, 0)) + if (!find_modptr_byname(cep->value, 0)) { - config_error("[require-module] Module '%s' was specified as required but we didn't even load it ourselves (maybe double check the name?)", cep->ce_vardata); + config_error("[require-module] Module '%s' was specified as required but we didn't even load it ourselves (maybe double check the name?)", cep->value); errors++; } @@ -329,12 +329,12 @@ int reqmods_configtest_require(ConfigFile *cf, ConfigEntry *ce, int type, int *e continue; } - if (!strcmp(cep->ce_varname, "min-version")) // Optional + if (!strcmp(cep->name, "min-version")) // Optional { // Still check for duplicate directives though if (has_minversion) { - config_error("%s:%i: duplicate %s for require module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: duplicate %s for require module { } block", cep->file->filename, cep->line_number, cep->name); errors++; continue; } @@ -343,13 +343,13 @@ int reqmods_configtest_require(ConfigFile *cf, ConfigEntry *ce, int type, int *e } // Reason directive is not used for require module { }, so error on that too - config_error("%s:%i: unknown directive %s for require module { } block", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: unknown directive %s for require module { } block", cep->file->filename, cep->line_number, cep->name); errors++; } if (!has_name) { - config_error("%s:%i: missing required 'name' directive for require module { } block", ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + config_error("%s:%i: missing required 'name' directive for require module { } block", ce->file->filename, ce->line_number); errors++; } @@ -364,28 +364,28 @@ int reqmods_configrun_require(ConfigFile *cf, ConfigEntry *ce, int type) ReqMod *rmod; char *name, *minversion; - if (strcmp(ce->ce_vardata, "module")) + if (strcmp(ce->value, "module")) return 0; name = minversion = NULL; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "name")) + if (!strcmp(cep->name, "name")) { - if (!(mod = find_modptr_byname(cep->ce_vardata, 0))) + if (!(mod = find_modptr_byname(cep->value, 0))) { // Something went very wrong :D - config_warn("[require-module] [BUG?] Passed configtest_require() but not configrun_require() for module '%s' (seems to not be loaded after all)", cep->ce_vardata); + config_warn("[require-module] [BUG?] Passed configtest_require() but not configrun_require() for module '%s' (seems to not be loaded after all)", cep->value); continue; } - name = cep->ce_vardata; + name = cep->value; continue; } - if (!strcmp(cep->ce_varname, "min-version")) + if (!strcmp(cep->name, "min-version")) { - minversion = cep->ce_vardata; + minversion = cep->value; continue; } } @@ -443,7 +443,11 @@ CMD_FUNC(cmd_smod) if ((dmod = find_denymod_byname(name))) { // Send this particular notice to local opers only - sendto_umode_global(UMODE_OPER, "Server %s is using module '%s', which is specified in a deny module { } config block (reason: %s)", client->name, name, dmod->reason); + unreal_log(ULOG_ERROR, "link", "LINK_DENY_MODULE", client, + "Server $client is using module '$module_name', " + "which is specified in a deny module { } config block (reason: $ban_reason) -- aborting link", + log_data_string("module_name", name), + log_data_string("ban_reason", dmod->reason)); abort = 1; // Always SQUIT because it was explicitly denied by admins continue; } @@ -458,13 +462,21 @@ CMD_FUNC(cmd_smod) if (modflag == 'R') { // We don't need to check the version yet because there's nothing to compare it to, so we'll treat it as if no require module::min-version was specified - sendto_umode_global(UMODE_OPER, "Required module wasn't (fully) loaded or is missing entirely: %s", name); + unreal_log(ULOG_ERROR, "link", "LINK_MISSING_REQUIRED_MODULE", client, + "Server $me is missing module '$module_name' which " + "is required by server $client. -- aborting link", + log_data_client("me", &me), + log_data_string("module_name", name)); abort = 1; // Always SQUIT here too (explicitly required by admins) } - else if (modflag == 'G') - sendto_umode_global(UMODE_OPER, "[WARN] Module marked as global wasn't (fully) loaded or is missing entirely: %s", name); - + { + unreal_log(ULOG_WARNING, "link", "LINK_MISSING_GLOBAL_MODULE", client, + "Server $me is missing module '$module_name', which is " + "marked as global at $client", + log_data_client("me", &me), + log_data_string("module_name", name)); + } continue; } @@ -476,15 +488,21 @@ CMD_FUNC(cmd_smod) // An explicit version was specified in require module { } but our module version is less than that if (*version != '*' && strnatcasecmp(mod->header->version, version) < 0) { - sendto_umode_global(UMODE_OPER, "Module version mismatch for required module '%s' (should be equal to or greater than %s but we're running %s)", name, version, mod->header->version); + unreal_log(ULOG_ERROR, "link", "LINK_MODULE_OLD_VERSION", client, + "Server $me is using an old version of module '$module_name'. " + "Server $client requires us to have version $minimum_module_version or later (we have $our_module_version). " + "-- aborting link", + log_data_client("me", &me), + log_data_string("module_name", name), + log_data_string("minimum_module_version", version), + log_data_string("our_module_version", mod->header->version)); abort = 1; } } if (abort) { - sendto_umode_global(UMODE_OPER, "ABORTING LINK: %s <=> %s", me.name, client->name); - exit_client(client, NULL, "ABORTING LINK"); + exit_client_fmt(client, NULL, "Link aborted due to missing or banned modules (see previous errors)"); return; } } diff --git a/src/modules/restrict-commands.c b/src/modules/restrict-commands.c index f104cb6..1884bfb 100644 --- a/src/modules/restrict-commands.c +++ b/src/modules/restrict-commands.c @@ -24,7 +24,7 @@ ModuleHeader MOD_HEADER = { "1.0.2", "Restrict specific commands unless certain conditions have been met", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; typedef struct RestrictedCommand RestrictedCommand; @@ -45,14 +45,14 @@ typedef struct { } CmdMap; // Forward declarations -char *find_cmd_byconftag(char *conftag); -RestrictedCommand *find_restrictions_bycmd(char *cmd); -RestrictedCommand *find_restrictions_byconftag(char *conftag); +const char *find_cmd_byconftag(const char *conftag); +RestrictedCommand *find_restrictions_bycmd(const char *cmd); +RestrictedCommand *find_restrictions_byconftag(const char *conftag); int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type); -int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -int rcmd_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); -int rcmd_block_message(Client *client, char *text, SendType sendtype, char **errmsg, char *display, char *conftag); +int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +int rcmd_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); +int rcmd_block_message(Client *client, const char *text, SendType sendtype, const char **errmsg, const char *display, const char *conftag); CMD_OVERRIDE_FUNC(rcmd_override); // Globals @@ -110,7 +110,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -char *find_cmd_byconftag(char *conftag) { +const char *find_cmd_byconftag(const char *conftag) { CmdMap *cmap; for (cmap = conf_cmdmaps; cmap->conftag; cmap++) { @@ -120,7 +120,7 @@ char *find_cmd_byconftag(char *conftag) { return NULL; } -RestrictedCommand *find_restrictions_bycmd(char *cmd) { +RestrictedCommand *find_restrictions_bycmd(const char *cmd) { RestrictedCommand *rcmd; for (rcmd = RestrictedCommandList; rcmd; rcmd = rcmd->next) { @@ -130,7 +130,7 @@ RestrictedCommand *find_restrictions_bycmd(char *cmd) { return NULL; } -RestrictedCommand *find_restrictions_byconftag(char *conftag) { +RestrictedCommand *find_restrictions_byconftag(const char *conftag) { RestrictedCommand *rcmd; for (rcmd = RestrictedCommandList; rcmd; rcmd = rcmd->next) { @@ -150,17 +150,17 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type != CONFIG_SET) return 0; - if (!ce || strcmp(ce->ce_varname, "restrict-commands")) + if (!ce || strcmp(ce->name, "restrict-commands")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) + for (cep2 = cep->items; cep2; cep2 = cep2->next) { - if (!strcmp(cep2->ce_varname, "disable")) + if (!strcmp(cep2->name, "disable")) { config_warn("%s:%i: set::restrict-commands::%s: the 'disable' option has been removed.", - cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname); + cep2->file->filename, cep2->line_number, cep->name); if (!warn_disable) { config_warn("Simply remove 'disable yes;' from the configuration file and " @@ -170,45 +170,45 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) continue; } - if (!cep2->ce_vardata) + if (!cep2->value) { - config_error("%s:%i: blank set::restrict-commands::%s:%s without value", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname, cep2->ce_varname); + config_error("%s:%i: blank set::restrict-commands::%s:%s without value", cep2->file->filename, cep2->line_number, cep->name, cep2->name); errors++; continue; } - if (!strcmp(cep2->ce_varname, "connect-delay")) + if (!strcmp(cep2->name, "connect-delay")) { - long v = config_checkval(cep2->ce_vardata, CFG_TIME); + long v = config_checkval(cep2->value, CFG_TIME); if ((v < 1) || (v > 3600)) { - config_error("%s:%i: set::restrict-commands::%s::connect-delay should be in range 1-3600", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: set::restrict-commands::%s::connect-delay should be in range 1-3600", cep2->file->filename, cep2->line_number, cep->name); errors++; } continue; } - if (!strcmp(cep2->ce_varname, "exempt-identified")) + if (!strcmp(cep2->name, "exempt-identified")) continue; - if (!strcmp(cep2->ce_varname, "exempt-webirc")) + if (!strcmp(cep2->name, "exempt-webirc")) continue; - if (!strcmp(cep2->ce_varname, "exempt-tls")) + if (!strcmp(cep2->name, "exempt-tls")) continue; - if (!strcmp(cep2->ce_varname, "exempt-reputation-score")) + if (!strcmp(cep2->name, "exempt-reputation-score")) { - int v = atoi(cep2->ce_vardata); + int v = atoi(cep2->value); if (v <= 0) { - config_error("%s:%i: set::restrict-commands::%s::exempt-reputation-score must be greater than 0", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: set::restrict-commands::%s::exempt-reputation-score must be greater than 0", cep2->file->filename, cep2->line_number, cep->name); errors++; } continue; } - config_error("%s:%i: unknown directive set::restrict-commands::%s::%s", cep2->ce_fileptr->cf_filename, cep2->ce_varlinenum, cep->ce_varname, cep2->ce_varname); + config_error("%s:%i: unknown directive set::restrict-commands::%s::%s", cep2->file->filename, cep2->line_number, cep->name, cep2->name); errors++; } } @@ -220,24 +220,24 @@ int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type) { ConfigEntry *cep, *cep2; - char *cmd, *conftag; + const char *cmd, *conftag; RestrictedCommand *rcmd; // We are only interested in set::restrict-commands if (type != CONFIG_SET) return 0; - if (!ce || strcmp(ce->ce_varname, "restrict-commands")) + if (!ce || strcmp(ce->name, "restrict-commands")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { // May need to switch some stuff around for special cases where the config directive doesn't match the actual command conftag = NULL; - if ((cmd = find_cmd_byconftag(cep->ce_varname))) - conftag = cep->ce_varname; + if ((cmd = find_cmd_byconftag(cep->name))) + conftag = cep->name; else - cmd = cep->ce_varname; + cmd = cep->name; // Try to add override before even allocating the struct so we can bail early // Also don't override anything from the conf_cmdmaps[] list because those are handled through hooks instead @@ -250,7 +250,7 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type) continue; } - if (!CommandOverrideAdd(ModInf.handle, cmd, rcmd_override)) + if (!CommandOverrideAdd(ModInf.handle, cmd, 0, rcmd_override)) { config_warn("[restrict-commands] Failed to add override for '%s' (NO RESTRICTIONS APPLY)", cmd); continue; @@ -260,38 +260,38 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type) rcmd = safe_alloc(sizeof(RestrictedCommand)); safe_strdup(rcmd->cmd, cmd); safe_strdup(rcmd->conftag, conftag); - for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next) + for (cep2 = cep->items; cep2; cep2 = cep2->next) { - if (!cep2->ce_vardata) + if (!cep2->value) continue; - if (!strcmp(cep2->ce_varname, "connect-delay")) + if (!strcmp(cep2->name, "connect-delay")) { - rcmd->connect_delay = config_checkval(cep2->ce_vardata, CFG_TIME); + rcmd->connect_delay = config_checkval(cep2->value, CFG_TIME); continue; } - if (!strcmp(cep2->ce_varname, "exempt-identified")) + if (!strcmp(cep2->name, "exempt-identified")) { - rcmd->exempt_identified = config_checkval(cep2->ce_vardata, CFG_YESNO); + rcmd->exempt_identified = config_checkval(cep2->value, CFG_YESNO); continue; } - if (!strcmp(cep2->ce_varname, "exempt-webirc")) + if (!strcmp(cep2->name, "exempt-webirc")) { - rcmd->exempt_webirc = config_checkval(cep2->ce_vardata, CFG_YESNO); + rcmd->exempt_webirc = config_checkval(cep2->value, CFG_YESNO); continue; } - if (!strcmp(cep2->ce_varname, "exempt-tls")) + if (!strcmp(cep2->name, "exempt-tls")) { - rcmd->exempt_tls = config_checkval(cep2->ce_vardata, CFG_YESNO); + rcmd->exempt_tls = config_checkval(cep2->value, CFG_YESNO); continue; } - if (!strcmp(cep2->ce_varname, "exempt-reputation-score")) + if (!strcmp(cep2->name, "exempt-reputation-score")) { - rcmd->exempt_reputation_score = atoi(cep2->ce_vardata); + rcmd->exempt_reputation_score = atoi(cep2->value); continue; } } @@ -313,12 +313,12 @@ int rcmd_canbypass(Client *client, RestrictedCommand *rcmd) return 1; if (rcmd->exempt_reputation_score > 0 && (GetReputation(client) >= rcmd->exempt_reputation_score)) return 1; - if (rcmd->connect_delay && client->local && (TStime() - client->local->firsttime >= rcmd->connect_delay)) + if (rcmd->connect_delay && client->local && (TStime() - client->local->creationtime >= rcmd->connect_delay)) return 1; return 0; } -int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { if (rcmd_block_message(client, *msg, sendtype, errmsg, "channel", (sendtype == SEND_TYPE_NOTICE ? "channel-notice" : "channel-message"))) return HOOK_DENY; @@ -326,7 +326,7 @@ int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, c return HOOK_CONTINUE; } -int rcmd_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int rcmd_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { // Need a few extra exceptions for user messages only =] if ((client == target) || IsULine(target)) @@ -338,7 +338,7 @@ int rcmd_can_send_to_user(Client *client, Client *target, char **text, char **er return HOOK_CONTINUE; } -int rcmd_block_message(Client *client, char *text, SendType sendtype, char **errmsg, char *display, char *conftag) +int rcmd_block_message(Client *client, const char *text, SendType sendtype, const char **errmsg, const char *display, const char *conftag) { RestrictedCommand *rcmd; static char errbuf[256]; diff --git a/src/modules/rmtkl.c b/src/modules/rmtkl.c index 8ba0f64..fd97b27 100644 --- a/src/modules/rmtkl.c +++ b/src/modules/rmtkl.c @@ -24,7 +24,7 @@ ModuleHeader MOD_HEADER = { "1.4", "Adds /rmtkl command to easily remove *-Lines in bulk", "Gottem and the UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #define IsParam(x) (parc > (x) && !BadPtr(parv[(x)])) @@ -37,10 +37,10 @@ typedef struct { char *operpriv; } TKLType; -static void dump_str(Client *client, char **buf); +static void dump_str(Client *client, const char **buf); static TKLType *find_TKLType_by_flag(char flag); -void rmtkl_check_options(char *param, int *skipperm, int *silent); -int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, char *uhmask, char *commentmask, int skipperm, int silent); +void rmtkl_check_options(const char *param, int *skipperm, int *silent); +int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, const char *uhmask, const char *commentmask, int skipperm, int silent); CMD_FUNC(rmtkl); TKLType tkl_types[] = { @@ -53,7 +53,7 @@ TKLType tkl_types[] = { { 0, 0, "Unknown *-Line", 0 }, }; -static char *rmtkl_help[] = { +static const char *rmtkl_help[] = { "*** \002Help on /rmtkl\002 *** ", "Removes all TKLs matching the given conditions from the local server, or the entire", "network if it's a global-type ban.", @@ -104,7 +104,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static void dump_str(Client *client, char **buf) +static void dump_str(Client *client, const char **buf) { if (!MyUser(client)) return; @@ -114,7 +114,7 @@ static void dump_str(Client *client, char **buf) sendto_one(client, NULL, ":%s %03d %s :%s", me.name, RPL_TEXT, client->name, *buf); // Let user take 8 seconds to read it - client->local->since += 8; + add_fake_lag(client, 8000); } static TKLType *find_TKLType_by_flag(char flag) @@ -126,14 +126,14 @@ static TKLType *find_TKLType_by_flag(char flag) return t; } -void rmtkl_check_options(char *param, int *skipperm, int *silent) { +void rmtkl_check_options(const char *param, int *skipperm, int *silent) { if (!strcasecmp("-skipperm", param)) *skipperm = 1; if (!strcasecmp("-silent", param)) *silent = 1; } -int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, char *uhmask, char *commentmask, int skipperm, int silent) +int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, const char *uhmask, const char *commentmask, int skipperm, int silent) { if (tkl->type != tkltype->type) return 0; @@ -171,7 +171,7 @@ int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, char *uhmask, ch if (!silent) sendnotice_tkl_del(client->name, tkl); - RunHook2(HOOKTYPE_TKL_DEL, client, tkl); + RunHook(HOOKTYPE_TKL_DEL, client, tkl); if (tkl->type & TKL_SHUN) tkl_check_local_remove_shun(tkl); @@ -183,7 +183,7 @@ CMD_FUNC(rmtkl) { TKL *tkl, *next; TKLType *tkltype; - char *types, *uhmask, *commentmask, *p; + const char *types, *uhmask, *commentmask, *p; char tklchar; int tklindex, tklindex2, skipperm, silent; unsigned int count; @@ -289,5 +289,7 @@ CMD_FUNC(rmtkl) } } - sendto_snomask(SNO_TKL, "*** %s removed %d TKLine(s) using /rmtkl", client->name, count); + unreal_log(ULOG_INFO, "tkl", "RMTKL_COMMAND", client, + "[rmtkl] $client removed $tkl_removed_count TKLine(s) using /RMTKL", + log_data_integer("tkl_removed_count", count)); } diff --git a/src/modules/rules.c b/src/modules/rules.c index 8c09287..2845af7 100644 --- a/src/modules/rules.c +++ b/src/modules/rules.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /rules", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -63,14 +63,14 @@ CMD_FUNC(cmd_rules) temp = NULL; - if (hunt_server(client, recv_mtags, ":%s RULES :%s", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "RULES", 1, parc, parv) != HUNTED_ISME) return; ptr = find_tld(client); if (ptr) temp = ptr->rules.lines; - if(!temp) + if (!temp) temp = rules.lines; if (temp == NULL) diff --git a/src/modules/sajoin.c b/src/modules/sajoin.c index 57bd4e1..6f8767a 100644 --- a/src/modules/sajoin.c +++ b/src/modules/sajoin.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /sajoin", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -52,6 +52,13 @@ MOD_UNLOAD() return MOD_SUCCESS; } +static void log_sajoin(Client *client, Client *target, const char *channels) +{ + unreal_log(ULOG_INFO, "sacmds", "SAJOIN_COMMAND", client, "SAJOIN: $client used SAJOIN to make $target join $channels", + log_data_client("target", target), + log_data_string("channels", channels)); +} + /* cmd_sajoin() - Lamego - Wed Jul 21 20:04:48 1999 Copied off PTlink IRCd (C) PTlink coders team. Coded for Sadmin by Stskeeps @@ -62,10 +69,8 @@ MOD_UNLOAD() CMD_FUNC(cmd_sajoin) { Client *target; + char request[BUFSIZE]; char jbuf[BUFSIZE]; - char mode = '\0'; - char sjmode = '\0'; - char *mode_args[3]; int did_anything = 0; int ntargets = 0; int maxtargets = max_targets_for_command("SAJOIN"); @@ -76,7 +81,7 @@ CMD_FUNC(cmd_sajoin) return; } - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); return; @@ -89,32 +94,35 @@ CMD_FUNC(cmd_sajoin) return; } + /* Broadcast so other servers can log it appropriately as an SAJOIN */ + sendto_server(client, 0, 0, recv_mtags, ":%s SAJOIN %s %s", client->id, target->id, parv[2]); + /* If it's not for our client, then simply pass on the message... */ if (!MyUser(target)) { - sendto_one(target, NULL, ":%s SAJOIN %s %s", client->id, target->id, parv[2]); - - /* Logging function added by XeRXeS */ - ircd_log(LOG_SACMDS,"SAJOIN: %s used SAJOIN to make %s join %s", - client->name, target->name, parv[2]); - + log_sajoin(client, target, parv[2]); return; } + /* 'target' is our client... */ + /* Can't this just use do_join() or something with a parameter to bypass some checks? * This duplicate code is damn ugly. Ah well.. */ { char *name, *p = NULL; - int i, parted = 0; + int parted = 0; *jbuf = 0; /* Now works like cmd_join */ - for (i = 0, name = strtoken(&p, parv[2], ","); name; name = strtoken(&p, NULL, ",")) + strlcpy(request, parv[2], sizeof(request)); + for (name = strtoken(&p, request, ","); name; name = strtoken(&p, NULL, ",")) { Channel *channel; Membership *lp; + char mode = '\0'; + char prefix = '\0'; if (++ntargets > maxtargets) { @@ -122,38 +130,11 @@ CMD_FUNC(cmd_sajoin) break; } - switch (name[0]) + mode = prefix_to_mode(*name); + if (mode) { -#ifdef PREFIX_AQ - case '~': - mode = 'q'; - sjmode = '~'; - ++name; - break; - case '&': - mode = 'a'; - sjmode = '&'; - ++name; - break; -#endif - case '@': - mode = 'o'; - sjmode = '@'; - ++name; - break; - case '%': - mode = 'h'; - sjmode = '%'; - ++name; - break; - case '+': - mode = 'v'; - sjmode = '+'; - ++name; - break; - default: - mode = sjmode = '\0'; /* make sure sjmode is 0. */ - break; + prefix = *name; + name++; /* skip the prefix */ } if (strlen(name) > CHANNELLEN) @@ -162,10 +143,9 @@ CMD_FUNC(cmd_sajoin) continue; } - if (*name == '0' && !atoi(name) && !sjmode) + if (*name == '0' && !atoi(name) && !mode) { - strcpy(jbuf, "0"); - i = 1; + strlcpy(jbuf, "0", sizeof(jbuf)); parted = 1; continue; } @@ -176,7 +156,7 @@ CMD_FUNC(cmd_sajoin) continue; } - channel = get_channel(target, name, 0); + channel = make_channel(name); /* If this _specific_ channel is not permitted, skip it */ if (!IsULine(client) && !ValidatePermissionsForPath("sacmd:sajoin",client,target,channel,NULL)) @@ -191,25 +171,36 @@ CMD_FUNC(cmd_sajoin) continue; } if (*jbuf) - strlcat(jbuf, ",", sizeof jbuf); - strlncat(jbuf, name, sizeof jbuf, sizeof(jbuf) - i - 1); - i += strlen(name) + 1; + strlcat(jbuf, ",", sizeof(jbuf)); + if (prefix) + strlcat_letter(jbuf, prefix, sizeof(jbuf)); + strlcat(jbuf, name, sizeof(jbuf)); } if (!*jbuf) return; - i = 0; - strcpy(parv[2], jbuf); + + strlcpy(request, jbuf, sizeof(request)); *jbuf = 0; - for (name = strtoken(&p, parv[2], ","); name; name = strtoken(&p, NULL, ",")) + for (name = strtoken(&p, request, ","); name; name = strtoken(&p, NULL, ",")) { MessageTag *mtags = NULL; - int flags; + const char *member_modes; Channel *channel; Membership *lp; Hook *h; int i = 0; + char mode = '\0'; + char prefix = '\0'; - if (*name == '0' && !atoi(name) && !sjmode) + mode = prefix_to_mode(*name); + if (mode != '\0') + { + /* Yup, it was a real prefix. */ + prefix = *name; + name++; + } + + if (*name == '0' && !atoi(name) && !mode) { /* Rewritten so to generate a PART for each channel to servers, * so the same msgid is used for each part on all servers. -- Syzop @@ -223,18 +214,18 @@ CMD_FUNC(cmd_sajoin) new_message(target, NULL, &mtags); sendto_channel(channel, target, NULL, 0, 0, SEND_LOCAL, mtags, ":%s PART %s :%s", - target->name, channel->chname, "Left all channels"); - sendto_server(NULL, 0, 0, mtags, ":%s PART %s :Left all channels", target->name, channel->chname); + target->name, channel->name, "Left all channels"); + sendto_server(NULL, 0, 0, mtags, ":%s PART %s :Left all channels", target->name, channel->name); if (MyConnect(target)) - RunHook4(HOOKTYPE_LOCAL_PART, target, channel, mtags, "Left all channels"); + RunHook(HOOKTYPE_LOCAL_PART, target, channel, mtags, "Left all channels"); free_message_tags(mtags); - remove_user_from_channel(target, channel); + remove_user_from_channel(target, channel, 0); } - strcpy(jbuf, "0"); + strlcpy(jbuf, "0", sizeof(jbuf)); continue; } - flags = (ChannelExists(name)) ? CHFL_DEOPPED : LEVEL_ON_JOIN; - channel = get_channel(target, name, CREATE); + member_modes = (ChannelExists(name)) ? "" : LEVEL_ON_JOIN; + channel = make_channel(name); if (channel && (lp = find_membership_link(target->user->channel, channel))) continue; @@ -255,19 +246,26 @@ CMD_FUNC(cmd_sajoin) * Each with their own unique msgid. */ new_message(target, NULL, &mtags); - join_channel(channel, target, mtags, flags); - if (sjmode) + join_channel(channel, target, mtags, member_modes); + if (prefix) { + char *modes; + const char *mode_args[3]; + opermode = 0; sajoinmode = 1; - mode_args[0] = safe_alloc(2); - mode_args[0][0] = mode; - mode_args[0][1] = '\0'; + + modes = safe_alloc(2); + modes[0] = mode; + + mode_args[0] = modes; mode_args[1] = target->name; mode_args[2] = 0; + do_mode(channel, target, NULL, 3, mode_args, 0, 1); + sajoinmode = 0; - safe_free(mode_args[0]); + safe_free(modes); } free_message_tags(mtags); did_anything = 1; @@ -275,5 +273,11 @@ CMD_FUNC(cmd_sajoin) strlcat(jbuf, ",", sizeof jbuf); strlcat(jbuf, name, sizeof jbuf); } + + if (did_anything) + { + //sendnotice(target, "*** You were forced to join %s", jbuf); + log_sajoin(client, target, jbuf); + } } } diff --git a/src/modules/samode.c b/src/modules/samode.c index 7dc48b8..9ca4396 100644 --- a/src/modules/samode.c +++ b/src/modules/samode.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /samode", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -68,7 +68,7 @@ CMD_FUNC(cmd_samode) return; } - channel = find_channel(parv[1], NULL); + channel = find_channel(parv[1]); if (!channel) { sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]); diff --git a/src/modules/sapart.c b/src/modules/sapart.c index d783fdd..6008389 100644 --- a/src/modules/sapart.c +++ b/src/modules/sapart.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /sapart", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -52,6 +52,24 @@ MOD_UNLOAD() return MOD_SUCCESS; } +static void log_sapart(Client *client, Client *target, const char *channels, const char *comment) +{ + if (comment) + { + unreal_log(ULOG_INFO, "sacmds", "SAPART_COMMAND", client, "SAPART: $client used SAPART to make $target part $channels ($reason)", + log_data_client("target", target), + log_data_string("channels", channels), + log_data_string("reason", comment)); + } + else + { + unreal_log(ULOG_INFO, "sacmds", "SAPART_COMMAND", client, "SAPART: $client used SAPART to make $target part $channels", + log_data_client("target", target), + log_data_string("channels", channels)); + } +} + + /* cmd_sapart() - Lamego - Wed Jul 21 20:04:48 1999 Copied off PTlink IRCd (C) PTlink coders team. Coded for Sadmin by Stskeeps @@ -68,8 +86,9 @@ CMD_FUNC(cmd_sapart) Membership *lp; char *name, *p = NULL; int i; - char *comment = (parc > 3 && parv[3] ? parv[3] : NULL); + const char *comment = (parc > 3 && parv[3] ? parv[3] : NULL); char commentx[512]; + char request[BUFSIZE]; char jbuf[BUFSIZE]; int ntargets = 0; int maxtargets = max_targets_for_command("SAPART"); @@ -80,7 +99,7 @@ CMD_FUNC(cmd_sapart) return; } - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); return; @@ -93,37 +112,33 @@ CMD_FUNC(cmd_sapart) return; } - /* Relay it on, if it's not my target */ + /* Broadcast so other servers can log it appropriately as an SAPART */ + if (parv[3]) + sendto_server(client, 0, 0, recv_mtags, ":%s SAPART %s %s :%s", client->id, target->id, parv[2], comment); + else + sendto_server(client, 0, 0, recv_mtags, ":%s SAPART %s %s", client->id, target->id, parv[2]); + if (!MyUser(target)) { - if (comment) - { - sendto_one(target, NULL, ":%s SAPART %s %s :%s", client->id, target->id, parv[2], comment); - ircd_log(LOG_SACMDS,"SAPART: %s used SAPART to make %s part %s (%s)", - client->name, target->name, parv[2], comment); - } - else - { - sendto_one(target, NULL, ":%s SAPART %s %s", client->id, target->id, parv[2]); - ircd_log(LOG_SACMDS,"SAPART: %s used SAPART to make %s part %s", - client->name, target->name, parv[2]); - } + log_sapart(client, target, parv[2], comment); return; } - /* Now works like cmd_join */ + /* 'target' is our client... */ + *jbuf = 0; - for (i = 0, name = strtoken(&p, parv[2], ","); name; name = strtoken(&p, NULL, ",")) + strlcpy(request, parv[2], sizeof(request)); + for (i = 0, name = strtoken(&p, request, ","); name; name = strtoken(&p, NULL, ",")) { if (++ntargets > maxtargets) { sendnumeric(client, ERR_TOOMANYTARGETS, name, maxtargets, "SAPART"); break; } - if (!(channel = get_channel(target, name, 0))) + + if (!(channel = find_channel(name))) { - sendnumeric(client, ERR_NOSUCHCHANNEL, - name); + sendnumeric(client, ERR_NOSUCHCHANNEL, name); continue; } @@ -148,17 +163,24 @@ CMD_FUNC(cmd_sapart) if (!*jbuf) return; - strcpy(parv[2], jbuf); + strlcpy(request, jbuf, sizeof(request)); + + log_sapart(client, target, request, comment); if (comment) { - strcpy(commentx, "SAPart: "); - strlcat(commentx, comment, 512); + snprintf(commentx, sizeof(commentx), "SAPart: %s", comment); + //sendnotice(target, "*** You were forced to part %s (%s)", request, commentx); + } else { + //sendnotice(target, "*** You were forced to part %s", request); } parv[0] = target->name; // nick - parv[1] = parv[2]; // chan + parv[1] = request; // chan parv[2] = comment ? commentx : NULL; // comment + + /* Now, do the actual parting: */ do_cmd(target, NULL, "PART", comment ? 3 : 2, parv); - /* target may be killed now due to the part reason @ spamfilter */ + + /* NOTE: target may be killed now due to the part reason @ spamfilter */ } diff --git a/src/modules/sasl.c b/src/modules/sasl.c index cceb9fe..c764bbc 100644 --- a/src/modules/sasl.c +++ b/src/modules/sasl.c @@ -25,17 +25,17 @@ ModuleHeader MOD_HEADER = { "sasl", - "5.0", + "5.2.1", "SASL", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ void saslmechlist_free(ModData *m); -char *saslmechlist_serialize(ModData *m); -void saslmechlist_unserialize(char *str, ModData *m); -char *sasl_capability_parameter(Client *client); +const char *saslmechlist_serialize(ModData *m); +void saslmechlist_unserialize(const char *str, ModData *m); +const char *sasl_capability_parameter(Client *client); int sasl_server_synced(Client *client); int sasl_account_login(Client *client, MessageTag *mtags); EVENT(sasl_timeout); @@ -69,14 +69,15 @@ int sasl_account_login(Client *client, MessageTag *mtags) { if (!MyConnect(client)) return 0; + /* Notify user */ - if (client->user->svid[0] != '0') + if (IsLoggedIn(client)) { sendnumeric(client, RPL_LOGGEDIN, BadPtr(client->name) ? "*" : client->name, BadPtr(client->user->username) ? "*" : client->user->username, BadPtr(client->user->realhost) ? "*" : client->user->realhost, - client->user->svid, client->user->svid); + client->user->account, client->user->account); } else { @@ -93,13 +94,13 @@ int sasl_account_login(Client *client, MessageTag *mtags) * * parv[1]: propagation mask * parv[2]: target - * parv[3]: ESVID + * parv[3]: account name (SVID) */ CMD_FUNC(cmd_svslogin) { Client *target; - if (!SASL_SERVER || MyUser(client) || (parc < 3) || !parv[3]) + if (MyUser(client) || (parc < 3) || !parv[3]) return; /* We actually ignore parv[1] since this is a broadcast message. @@ -116,7 +117,7 @@ CMD_FUNC(cmd_svslogin) if (target->user == NULL) make_user(target); - strlcpy(target->user->svid, parv[3], sizeof(target->user->svid)); + strlcpy(target->user->account, parv[3], sizeof(target->user->account)); user_account_login(recv_mtags, target); if (MyConnect(target) && IsDead(target)) return; /* was killed due to *LINE on ~a probably */ @@ -169,7 +170,7 @@ CMD_FUNC(cmd_sasl) if (*parv[3] == 'C') { - RunHookReturn2(HOOKTYPE_SASL_CONTINUATION, target, parv[4], !=0); + RunHookReturn(HOOKTYPE_SASL_CONTINUATION, !=0, target, parv[4]); sendto_one(target, NULL, "AUTHENTICATE %s", parv[4]); } else if (*parv[3] == 'D') @@ -178,15 +179,15 @@ CMD_FUNC(cmd_sasl) if (*parv[4] == 'F') { target->local->sasl_sent_time = 0; - target->local->since += 7; /* bump fakelag due to failed authentication attempt */ - RunHookReturn2(HOOKTYPE_SASL_RESULT, target, 0, !=0); + add_fake_lag(target, 7000); /* bump fakelag due to failed authentication attempt */ + RunHookReturn(HOOKTYPE_SASL_RESULT, !=0, target, 0); sendnumeric(target, ERR_SASLFAIL); } else if (*parv[4] == 'S') { target->local->sasl_sent_time = 0; target->local->sasl_complete++; - RunHookReturn2(HOOKTYPE_SASL_RESULT, target, 1, !=0); + RunHookReturn(HOOKTYPE_SASL_RESULT, !=0, target, 1); sendnumeric(target, RPL_SASLSUCCESS); } } @@ -232,7 +233,7 @@ CMD_FUNC(cmd_authenticate) if (agent_p == NULL) { char *addr = BadPtr(client->ip) ? "0" : client->ip; - char *certfp = moddata_client_get(client, "certfp"); + const char *certfp = moddata_client_get(client, "certfp"); sendto_server(NULL, 0, 0, NULL, ":%s SASL %s %s H %s %s", me.name, SASL_SERVER, client->id, addr, addr); @@ -308,7 +309,7 @@ int sasl_connect(Client *client) return abort_sasl(client); } -int sasl_quit(Client *client, MessageTag *mtags, char *comment) +int sasl_quit(Client *client, MessageTag *mtags, const char *comment) { return abort_sasl(client); } @@ -335,14 +336,9 @@ void auto_discover_sasl_server(int justlinked) /* SASL server found */ if (justlinked) { - /* Let's send this message only on link and not also on /rehash */ - sendto_realops("Services server '%s' provides SASL authentication, good! " - "I'm setting set::sasl-server to '%s' internally.", - SERVICES_NAME, SERVICES_NAME); - /* We should really get some LOG_INFO or something... I keep abusing LOG_ERROR :) */ - ircd_log(LOG_ERROR, "Services server '%s' provides SASL authentication, good! " - "I'm setting set::sasl-server to '%s' internally.", - SERVICES_NAME, SERVICES_NAME); + unreal_log(ULOG_INFO, "config", "SASL_SERVER_AUTODETECT", client, + "Services server $client provides SASL authentication, good! " + "I'm setting set::sasl-server to \"$client\" internally."); } safe_strdup(SASL_SERVER, SERVICES_NAME); if (justlinked) @@ -394,7 +390,8 @@ MOD_INIT() mreq.free = saslmechlist_free; mreq.serialize = saslmechlist_serialize; mreq.unserialize = saslmechlist_unserialize; - mreq.sync = 1; + mreq.sync = MODDATA_SYNC_EARLY; + mreq.self_write = 1; mreq.type = MODDATATYPE_CLIENT; ModDataAdd(modinfo->handle, mreq); @@ -419,19 +416,19 @@ void saslmechlist_free(ModData *m) safe_free(m->str); } -char *saslmechlist_serialize(ModData *m) +const char *saslmechlist_serialize(ModData *m) { if (!m->str) return NULL; return m->str; } -void saslmechlist_unserialize(char *str, ModData *m) +void saslmechlist_unserialize(const char *str, ModData *m) { safe_strdup(m->str, str); } -char *sasl_capability_parameter(Client *client) +const char *sasl_capability_parameter(Client *client) { Client *server; diff --git a/src/modules/sdesc.c b/src/modules/sdesc.c index 6c1f730..685c044 100644 --- a/src/modules/sdesc.c +++ b/src/modules/sdesc.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /sdesc", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -80,13 +80,13 @@ CMD_FUNC(cmd_sdesc) REALLEN); return; } - parv[1][REALLEN] = '\0'; } - ircsnprintf(client->srvptr->info, sizeof(client->srvptr->info), "%s", parv[1]); + strlncpy(client->uplink->info, parv[1], sizeof(client->uplink->info), REALLEN); sendto_server(client, 0, 0, NULL, ":%s SDESC :%s", client->name, parv[1]); - sendto_ops("Server description for %s is now '%s' (changed by %s)", - client->srvptr->name, client->srvptr->info, client->name); + unreal_log(ULOG_INFO, "sdesc", "SDESC_COMMAND", client, + "Server description for $server is now '$server.server.info' (changed by $client)", + log_data_client("server", client->uplink)); } diff --git a/src/modules/sendsno.c b/src/modules/sendsno.c index cabea21..1f1d52f 100644 --- a/src/modules/sendsno.c +++ b/src/modules/sendsno.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /sendsno", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -63,9 +63,7 @@ MOD_UNLOAD() CMD_FUNC(cmd_sendsno) { MessageTag *mtags = NULL; - char *sno, *msg, *p; - long snomask = 0; - int i; + const char *sno, *msg, *p; Client *acptr; if ((parc < 3) || BadPtr(parv[2])) @@ -81,22 +79,22 @@ CMD_FUNC(cmd_sendsno) /* Forward to others... */ sendto_server(client, 0, 0, mtags, ":%s SENDSNO %s :%s", client->id, parv[1], parv[2]); - for (p = sno; *p; p++) - { - for(i = 0; i <= Snomask_highest; i++) - { - if (Snomask_Table[i].flag == *p) - { - snomask |= Snomask_Table[i].mode; - break; - } - } - } - list_for_each_entry(acptr, &oper_list, special_node) { - if (acptr->user->snomask & snomask) - sendto_one(acptr, mtags, ":%s NOTICE %s :%s", client->name, acptr->name, msg); + if (acptr->user->snomask) + { + char found = 0; + for (p = sno; *p; p++) + { + if (strchr(acptr->user->snomask, *p)) + { + found = 1; + break; + } + } + if (found) + sendto_one(acptr, mtags, ":%s NOTICE %s :%s", client->name, acptr->name, msg); + } } free_message_tags(mtags); diff --git a/src/modules/sendumode.c b/src/modules/sendumode.c index a8504d7..18c7c40 100644 --- a/src/modules/sendumode.c +++ b/src/modules/sendumode.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /sendumode", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -66,8 +66,8 @@ CMD_FUNC(cmd_sendumode) { MessageTag *mtags = NULL; Client *acptr; - char *message; - char *p; + const char *message; + const char *p; int i; long umode_s = 0; @@ -83,19 +83,7 @@ CMD_FUNC(cmd_sendumode) sendto_server(client, 0, 0, mtags, ":%s SENDUMODE %s :%s", client->id, parv[1], message); - for (p = parv[1]; *p; p++) - { - for(i = 0; i <= Usermode_highest; i++) - { - if (!Usermode_Table[i].flag) - continue; - if (Usermode_Table[i].flag == *p) - { - umode_s |= Usermode_Table[i].mode; - break; - } - } - } + umode_s = set_usermode(parv[1]); list_for_each_entry(acptr, &oper_list, special_node) { diff --git a/src/modules/server-time.c b/src/modules/server-time.c index f204e2a..7116977 100644 --- a/src/modules/server-time.c +++ b/src/modules/server-time.c @@ -28,14 +28,14 @@ ModuleHeader MOD_HEADER "5.0", "server-time CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ long CAP_SERVER_TIME = 0L; -int server_time_mtag_is_ok(Client *client, char *name, char *value); -void mtag_add_or_inherit_time(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int server_time_mtag_is_ok(Client *client, const char *name, const char *value); +void mtag_add_or_inherit_time(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -75,7 +75,7 @@ MOD_UNLOAD() * syntax. * We simply allow server-time ONLY from servers. */ -int server_time_mtag_is_ok(Client *client, char *name, char *value) +int server_time_mtag_is_ok(Client *client, const char *name, const char *value) { if (IsServer(client) && !BadPtr(value)) return 1; @@ -83,7 +83,7 @@ int server_time_mtag_is_ok(Client *client, char *name, char *value) return 0; } -void mtag_add_or_inherit_time(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_or_inherit_time(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m = find_mtag(recv_mtags, "time"); if (m) diff --git a/src/modules/server.c b/src/modules/server.c index df660f4..a673aee 100644 --- a/src/modules/server.c +++ b/src/modules/server.c @@ -22,21 +22,46 @@ #include "unrealircd.h" +/* Definitions */ +typedef enum AutoConnectStrategy { + AUTOCONNECT_PARALLEL = 0, + AUTOCONNECT_SEQUENTIAL = 1, + AUTOCONNECT_SEQUENTIAL_FALLBACK = 2 +} AutoConnectStrategy; + +typedef struct cfgstruct cfgstruct; +struct cfgstruct { + AutoConnectStrategy autoconnect_strategy; + long connect_timeout; + long handshake_timeout; +}; + /* Forward declarations */ +void server_config_setdefaults(cfgstruct *cfg); +int server_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +int server_config_run(ConfigFile *cf, ConfigEntry *ce, int type); +EVENT(server_autoconnect); +EVENT(server_handshake_timeout); void send_channel_modes_sjoin3(Client *to, Channel *channel); CMD_FUNC(cmd_server); CMD_FUNC(cmd_sid); -int _verify_link(Client *client, char *servername, ConfigItem_link **link_out); +int _verify_link(Client *client, ConfigItem_link **link_out); void _send_protoctl_servers(Client *client, int response); void _send_server_message(Client *client); void _introduce_user(Client *to, Client *acptr); int _check_deny_version(Client *cptr, char *software, int protocol, char *flags); void _broadcast_sinfo(Client *acptr, Client *to, Client *except); +int server_sync(Client *cptr, ConfigItem_link *conf, int incoming); +void tls_link_notification_verify(Client *acptr, ConfigItem_link *aconf); +void server_generic_free(ModData *m); +int server_post_connect(Client *client); +void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp); +static int connect_server_helper(ConfigItem_link *, Client *); /* Global variables */ static char buf[BUFSIZE]; - -#define MSG_SERVER "SERVER" +static cfgstruct cfg; +static char *last_autoconnect_server = NULL; ModuleHeader MOD_HEADER = { @@ -44,7 +69,7 @@ ModuleHeader MOD_HEADER "5.0", "command /server", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_TEST() @@ -56,30 +81,410 @@ MOD_TEST() EfunctionAddVoid(modinfo->handle, EFUNC_INTRODUCE_USER, _introduce_user); EfunctionAdd(modinfo->handle, EFUNC_CHECK_DENY_VERSION, _check_deny_version); EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_SINFO, _broadcast_sinfo); + EfunctionAddVoid(modinfo->handle, EFUNC_CONNECT_SERVER, _connect_server); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, server_config_test); return MOD_SUCCESS; } MOD_INIT() { - CommandAdd(modinfo->handle, MSG_SERVER, cmd_server, MAXPARA, CMD_UNREGISTERED|CMD_SERVER); - CommandAdd(modinfo->handle, "SID", cmd_sid, MAXPARA, CMD_SERVER); - MARK_AS_OFFICIAL_MODULE(modinfo); + LoadPersistentPointer(modinfo, last_autoconnect_server, server_generic_free); + server_config_setdefaults(&cfg); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, server_config_run); + HookAdd(modinfo->handle, HOOKTYPE_POST_SERVER_CONNECT, 0, server_post_connect); + CommandAdd(modinfo->handle, "SERVER", cmd_server, MAXPARA, CMD_UNREGISTERED|CMD_SERVER); + CommandAdd(modinfo->handle, "SID", cmd_sid, MAXPARA, CMD_SERVER); return MOD_SUCCESS; } MOD_LOAD() { + EventAdd(modinfo->handle, "server_autoconnect", server_autoconnect, NULL, 2000, 0); + EventAdd(modinfo->handle, "server_handshake_timeout", server_handshake_timeout, NULL, 1000, 0); return MOD_SUCCESS; } MOD_UNLOAD() { + SavePersistentPointer(modinfo, last_autoconnect_server); return MOD_SUCCESS; } -int server_sync(Client *cptr, ConfigItem_link *conf); +/** Convert 'str' to a AutoConnectStrategy value. + * @param str The string, eg "parallel" + * @returns a valid AutoConnectStrategy value or -1 if not found. + */ +AutoConnectStrategy autoconnect_strategy_strtoval(char *str) +{ + if (!strcmp(str, "parallel")) + return AUTOCONNECT_PARALLEL; + if (!strcmp(str, "sequential")) + return AUTOCONNECT_SEQUENTIAL; + if (!strcmp(str, "sequential-fallback")) + return AUTOCONNECT_SEQUENTIAL_FALLBACK; + return -1; +} + +/** Convert an AutoConnectStrategy value to a string. + * @param val The value to convert to a string + * @returns a string, such as "parallel". + */ +char *autoconnect_strategy_valtostr(AutoConnectStrategy val) +{ + switch (val) + { + case AUTOCONNECT_PARALLEL: + return "parallel"; + case AUTOCONNECT_SEQUENTIAL: + return "sequential"; + case AUTOCONNECT_SEQUENTIAL_FALLBACK: + return "sequential-fallback"; + default: + return "???"; + } +} + +void server_config_setdefaults(cfgstruct *cfg) +{ + cfg->autoconnect_strategy = AUTOCONNECT_SEQUENTIAL; + cfg->connect_timeout = 10; + cfg->handshake_timeout = 20; +} + +int server_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + int errors = 0; + ConfigEntry *cep; + + if (type != CONFIG_SET) + return 0; + + /* We are only interrested in set::server-linking.. */ + if (!ce || strcmp(ce->name, "server-linking")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!cep->value) + { + config_error("%s:%i: blank set::server-linking::%s without value", + cep->file->filename, cep->line_number, cep->name); + errors++; + continue; + } else + if (!strcmp(cep->name, "autoconnect-strategy")) + { + if (autoconnect_strategy_strtoval(cep->value) < 0) + { + config_error("%s:%i: set::server-linking::autoconnect-strategy: invalid value '%s'. " + "Should be one of: parallel", + cep->file->filename, cep->line_number, cep->value); + errors++; + continue; + } + } else + if (!strcmp(cep->name, "connect-timeout")) + { + long v = config_checkval(cep->value, CFG_TIME); + if ((v < 5) || (v > 30)) + { + config_error("%s:%i: set::server-linking::connect-timeout should be between 5 and 60 seconds", + cep->file->filename, cep->line_number); + errors++; + continue; + } + } else + if (!strcmp(cep->name, "handshake-timeout")) + { + long v = config_checkval(cep->value, CFG_TIME); + if ((v < 10) || (v > 120)) + { + config_error("%s:%i: set::server-linking::handshake-timeout should be between 10 and 120 seconds", + cep->file->filename, cep->line_number); + errors++; + continue; + } + } else + { + config_error("%s:%i: unknown directive set::server-linking::%s", + cep->file->filename, cep->line_number, cep->name); + errors++; + continue; + } + } + + *errs = errors; + return errors ? -1 : 1; +} + +int server_config_run(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep; + + if (type != CONFIG_SET) + return 0; + + /* We are only interrested in set::server-linking.. */ + if (!ce || strcmp(ce->name, "server-linking")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (!strcmp(cep->name, "autoconnect-strategy")) + { + cfg.autoconnect_strategy = autoconnect_strategy_strtoval(cep->value); + } else + if (!strcmp(cep->name, "connect-timeout")) + { + cfg.connect_timeout = config_checkval(cep->value, CFG_TIME); + } else + if (!strcmp(cep->name, "handshake-timeout")) + { + cfg.handshake_timeout = config_checkval(cep->value, CFG_TIME); + } + } + return 1; +} + +int server_needs_linking(ConfigItem_link *aconf) +{ + ConfigItem_deny_link *deny; + Client *client; + ConfigItem_class *class; + + /* We're only interested in autoconnect blocks that are valid. Also, we ignore temporary link blocks. */ + if (!(aconf->outgoing.options & CONNECT_AUTO) || !aconf->outgoing.hostname || (aconf->flag.temporary == 1)) + return 0; + + class = aconf->class; + + /* Never do more than one connection attempt per seconds (for the same server) */ + if ((aconf->hold > TStime())) + return 0; + + aconf->hold = TStime() + class->connfreq; + + client = find_client(aconf->servername, NULL); + if (client) + return 0; /* Server already connected (or connecting) */ + + if (class->clients >= class->maxclients) + return 0; /* Class is full */ + + /* Check connect rules to see if we're allowed to try the link */ + for (deny = conf_deny_link; deny; deny = deny->next) + if (unreal_mask_match_string(aconf->servername, deny->mask) && crule_eval(deny->rule)) + return 0; + + /* Yes, this server is a linking candidate */ + return 1; +} + +void server_autoconnect_parallel(void) +{ + ConfigItem_link *aconf; + + for (aconf = conf_link; aconf; aconf = aconf->next) + { + if (!server_needs_linking(aconf)) + continue; + + connect_server(aconf, NULL, NULL); + } +} + +/** Find first (valid) autoconnect server in link blocks. + * This function should not be used directly. It is a helper function + * for find_next_autoconnect_server(). + */ +ConfigItem_link *find_first_autoconnect_server(void) +{ + ConfigItem_link *aconf; + + for (aconf = conf_link; aconf; aconf = aconf->next) + { + if (!server_needs_linking(aconf)) + continue; + return aconf; /* found! */ + } + return NULL; /* none */ +} + +/** Find next server that we should try to autoconnect to. + * Taking into account that we last tried server 'current'. + * @param current Server the previous autoconnect attempt was made to + * @returns A link block, or NULL if no servers are suitable. + */ +ConfigItem_link *find_next_autoconnect_server(char *current) +{ + ConfigItem_link *aconf; + + /* If the current autoconnect server is NULL then + * just find whichever valid server is first. + */ + if (current == NULL) + return find_first_autoconnect_server(); + + /* Next code is a bit convulted, it would have + * been easier if conf_link was a circular list ;) + */ + + /* Otherwise, walk the list up to 'current' */ + for (aconf = conf_link; aconf; aconf = aconf->next) + { + if (!strcmp(aconf->servername, current)) + break; + } + + /* If the 'current' server dissapeared, then let's + * just pick the first one from the list. + * It is a rare event to have the link { } block + * removed of a server that we just happened to + * try to link to before, so we can afford to do + * it this way. + */ + if (!aconf) + return find_first_autoconnect_server(); + + /* Check the remainder for the list, in other words: + * check all servers after 'current' if they are + * ready for an outgoing connection attempt... + */ + for (aconf = aconf->next; aconf; aconf = aconf->next) + { + if (!server_needs_linking(aconf)) + continue; + return aconf; /* found! */ + } + + /* If we get here then there are no valid servers + * after 'current', so now check for before 'current' + * (and including 'current', since we may + * have to autoconnect to that one again, + * eg if it is the only autoconnect server)... + */ + for (aconf = conf_link; aconf; aconf = aconf->next) + { + if (!server_needs_linking(aconf)) + { + if (!strcmp(aconf->servername, current)) + break; /* need to stop here */ + continue; + } + return aconf; /* found! */ + } + + return NULL; /* none */ +} + +/** Check if we are currently connecting to a server (outgoing). + * This function takes into account not only an outgoing TCP/IP connect + * or TLS handshake, but also if we are 'somewhat connected' to that + * server but have not completed the full sync, eg we may still need + * to receive SIDs or other sync data. + * NOTE: This implicitly assumes that outgoing links only go to + * servers that will (eventually) send "EOS". + * Should be a reasonable assumption given that in nearly all + * cases we only connect to UnrealIRCd servers for the outgoing + * case, as services are "always" incoming links. + * @returns 1 if an outgoing link is in progress, 0 if not. + */ +int current_outgoing_link_in_process(void) +{ + Client *client; + + list_for_each_entry(client, &unknown_list, lclient_node) + { + if (client->server && *client->server->by && client->local->creationtime && + (IsConnecting(client) || IsTLSConnectHandshake(client) || !IsSynched(client))) + { + return 1; + } + } + + list_for_each_entry(client, &server_list, special_node) + { + if (client->server && *client->server->by && client->local->creationtime && + (IsConnecting(client) || IsTLSConnectHandshake(client) || !IsSynched(client))) + { + return 1; + } + } + + return 0; +} + +void server_autoconnect_sequential(void) +{ + ConfigItem_link *aconf; + + if (current_outgoing_link_in_process()) + return; + + /* We are currently not in the process of doing an outgoing connect, + * let's see if we need to connect to somewhere... + */ + aconf = find_next_autoconnect_server(last_autoconnect_server); + if (aconf == NULL) + return; /* No server to connect to at this time */ + + /* Start outgoing link attempt */ + safe_strdup(last_autoconnect_server, aconf->servername); + connect_server(aconf, NULL, NULL); +} + +/** Perform autoconnect to servers that are not linked yet. */ +EVENT(server_autoconnect) +{ + switch (cfg.autoconnect_strategy) + { + case AUTOCONNECT_PARALLEL: + server_autoconnect_parallel(); + break; + case AUTOCONNECT_SEQUENTIAL: + /* Fallback is the same as sequential but we reset last_autoconnect_server on connect */ + case AUTOCONNECT_SEQUENTIAL_FALLBACK: + server_autoconnect_sequential(); + break; + } +} + +EVENT(server_handshake_timeout) +{ + Client *client, *next; + + list_for_each_entry_safe(client, next, &unknown_list, lclient_node) + { + /* We are only interested in outgoing server connects */ + if (!client->server || !*client->server->by || !client->local->creationtime) + continue; + + /* Handle set::server-linking::connect-timeout */ + if ((IsConnecting(client) || IsTLSConnectHandshake(client)) && + ((TStime() - client->local->creationtime) >= cfg.connect_timeout)) + { + /* If this is a connect timeout to an outgoing server then notify ops & log it */ + unreal_log(ULOG_INFO, "link", "LINK_CONNECT_TIMEOUT", client, + "Connect timeout while trying to link to server '$client' ($client.ip)"); + + exit_client(client, NULL, "Connection timeout"); + continue; + } + + /* Handle set::server-linking::handshake-timeout */ + if ((TStime() - client->local->creationtime) >= cfg.handshake_timeout) + { + /* If this is a handshake timeout to an outgoing server then notify ops & log it */ + unreal_log(ULOG_INFO, "link", "LINK_HANDSHAKE_TIMEOUT", client, + "Connect handshake timeout while trying to link to server '$client' ($client.ip)"); + + exit_client(client, NULL, "Handshake Timeout"); + continue; + } + } +} /** Check deny version { } blocks. * @param cptr Client (a server) @@ -211,7 +616,7 @@ void _send_protoctl_servers(Client *client, int response) void _send_server_message(Client *client) { - if (client->serv && client->serv->flags.server_sent) + if (client->server && client->server->flags.server_sent) { #ifdef DEBUGMODE abort(); @@ -222,24 +627,22 @@ void _send_server_message(Client *client) sendto_one(client, NULL, "SERVER %s 1 :U%d-%s%s-%s %s", me.name, UnrealProtocol, serveropts, extraflags ? extraflags : "", me.id, me.info); - if (client->serv) - client->serv->flags.server_sent = 1; + if (client->server) + client->server->flags.server_sent = 1; } +#define LINK_DEFAULT_ERROR_MSG "Link denied (No link block found with your server name or link::incoming::mask did not match)" /** Verify server link. * This does authentication and authorization checks. * @param cptr The client directly connected to us (cptr). * @param client The client which (originally) issued the server command (client). - * @param servername The server name provided by the client. * @param link_out Pointer-to-pointer-to-link block. Will be set when auth OK. Caller may pass NULL if he doesn't care. * @returns This function returns 1 on successful authentication, 0 otherwise - in which case the client has been killed. */ -int _verify_link(Client *client, char *servername, ConfigItem_link **link_out) +int _verify_link(Client *client, ConfigItem_link **link_out) { - char xerrmsg[256]; - ConfigItem_link *link; - char *inpath = get_client_name(client, TRUE); + ConfigItem_link *link, *orig_link; Client *acptr = NULL, *ocptr = NULL; ConfigItem_ban *bconf; @@ -252,81 +655,72 @@ int _verify_link(Client *client, char *servername, ConfigItem_link **link_out) if (link_out) *link_out = NULL; - strcpy(xerrmsg, "No matching link configuration"); - if (!client->local->passwd) { - sendto_one(client, NULL, "ERROR :Missing password"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_NO_PASSWORD", client, + "Link with server $client.details denied: No password provided. Protocol error."); exit_client(client, NULL, "Missing password"); return 0; } - /* First check if the server is in the list */ - if (!servername) { - strcpy(xerrmsg, "Null servername"); - goto errlink; - } - - if (client->serv && client->serv->conf) + if (client->server && client->server->conf) { /* This is an outgoing connect so we already know what link block we are - * dealing with. It's the one in: client->serv->conf + * dealing with. It's the one in: client->server->conf */ /* Actually we still need to double check the servername to avoid confusion. */ - if (strcasecmp(servername, client->serv->conf->servername)) + if (strcasecmp(client->name, client->server->conf->servername)) { - ircsnprintf(xerrmsg, sizeof(xerrmsg), "Outgoing connect from link block '%s' but server " - "introduced himself as '%s'. Server name mismatch.", - client->serv->conf->servername, - servername); - - sendto_one(client, NULL, "ERROR :%s", xerrmsg); - sendto_ops_and_log("Outgoing link aborted to %s(%s@%s) (%s) %s", - client->serv->conf->servername, client->ident, client->local->sockhost, xerrmsg, inpath); - exit_client(client, NULL, xerrmsg); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_SERVERNAME_MISMATCH", client, + "Link with server $client.details denied: " + "Outgoing connect from link block '$link_block' but server " + "introduced itself as '$client'. Server name mismatch.", + log_data_link_block(client->server->conf)); + exit_client_fmt(client, NULL, "Servername (%s) does not match name in my link block (%s)", + client->name, client->server->conf->servername); return 0; } - link = client->serv->conf; + link = client->server->conf; goto skip_host_check; } else { /* Hunt the linkblock down ;) */ for(link = conf_link; link; link = link->next) - if (match_simple(link->servername, servername)) + if (match_simple(link->servername, client->name)) break; } if (!link) { - ircsnprintf(xerrmsg, sizeof(xerrmsg), "No link block named '%s'", servername); - goto errlink; + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_UNKNOWN_SERVER", client, + "Link with server $client.details denied: No link block named '$client'"); + exit_client(client, NULL, LINK_DEFAULT_ERROR_MSG); + return 0; } if (!link->incoming.mask) { - ircsnprintf(xerrmsg, sizeof(xerrmsg), "Link block '%s' exists but has no link::incoming::mask", servername); - goto errlink; + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_NO_INCOMING", client, + "Link with server $client.details denied: Link block exists, but there is no link::incoming::mask set.", + log_data_link_block(link)); + exit_client(client, NULL, LINK_DEFAULT_ERROR_MSG); + return 0; } - link = find_link(servername, client); + orig_link = link; + link = find_link(client->name, client); if (!link) { - ircsnprintf(xerrmsg, sizeof(xerrmsg), "Server is in link block but link::incoming::mask didn't match"); -errlink: - /* Send the "simple" error msg to the server */ - sendto_one(client, NULL, - "ERROR :Link denied (No link block found named '%s' or link::incoming::mask did not match your IP %s) %s", - servername, GetIP(client), inpath); - /* And send the "verbose" error msg only to locally connected ircops */ - sendto_ops_and_log("Link denied for %s(%s@%s) (%s) %s", - servername, client->ident, client->local->sockhost, xerrmsg, inpath); - exit_client(client, NULL, "Link denied (No link block found with your server name or link::incoming::mask did not match)"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_INCOMING_MASK_MISMATCH", client, + "Link with server $client.details denied: Server is in link block but link::incoming::mask didn't match", + log_data_link_block(orig_link)); + exit_client(client, NULL, LINK_DEFAULT_ERROR_MSG); return 0; } skip_host_check: - /* Now for checking passwords */ + /* Try to authenticate the server... */ if (!Auth_Check(client, link->auth, client->local->passwd)) { /* Let's help admins a bit with a good error message in case @@ -339,32 +733,39 @@ skip_host_check: if (((link->auth->type == AUTHTYPE_PLAINTEXT) && client->local->passwd && !strcmp(client->local->passwd, "*")) || ((link->auth->type != AUTHTYPE_PLAINTEXT) && client->local->passwd && strcmp(client->local->passwd, "*"))) { - sendto_ops_and_log("Link denied for '%s' (Authentication failed due to different password types on both sides of the link) %s", - servername, inpath); - sendto_ops_and_log("Read https://www.unrealircd.org/docs/FAQ#auth-fail-mixed for more information"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_AUTH_FAILED", client, + "Link with server $client.details denied: Authentication failed: $auth_failure_msg", + log_data_string("auth_failure_msg", "different password types on both sides of the link\n" + "Read https://www.unrealircd.org/docs/FAQ#auth-fail-mixed for more information"), + log_data_link_block(link)); } else if (link->auth->type == AUTHTYPE_SPKIFP) { - sendto_ops_and_log("Link denied for '%s' (Authentication failed [spkifp mismatch]) %s", - servername, inpath); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_AUTH_FAILED", client, + "Link with server $client.details denied: Authentication failed: $auth_failure_msg", + log_data_string("auth_failure_msg", "spkifp mismatch"), + log_data_link_block(link)); } else if (link->auth->type == AUTHTYPE_TLS_CLIENTCERT) { - sendto_ops_and_log("Link denied for '%s' (Authentication failed [tlsclientcert mismatch]) %s", - servername, inpath); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_AUTH_FAILED", client, + "Link with server $client.details denied: Authentication failed: $auth_failure_msg", + log_data_string("auth_failure_msg", "tlsclientcert mismatch"), + log_data_link_block(link)); } else if (link->auth->type == AUTHTYPE_TLS_CLIENTCERTFP) { - sendto_ops_and_log("Link denied for '%s' (Authentication failed [tlsclientcertfp mismatch]) %s", - servername, inpath); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_AUTH_FAILED", client, + "Link with server $client.details denied: Authentication failed: $auth_failure_msg", + log_data_string("auth_failure_msg", "certfp mismatch"), + log_data_link_block(link)); } else { - sendto_ops_and_log("Link denied for '%s' (Authentication failed [Bad password?]) %s", - servername, inpath); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_AUTH_FAILED", client, + "Link with server $client.details denied: Authentication failed: $auth_failure_msg", + log_data_string("auth_failure_msg", "bad password"), + log_data_link_block(link)); } - sendto_one(client, NULL, - "ERROR :Link '%s' denied (Authentication failed) %s", - servername, inpath); exit_client(client, NULL, "Link denied (Authentication failed)"); return 0; } @@ -376,88 +777,90 @@ skip_host_check: if (!IsTLS(client)) { - sendto_one(client, NULL, - "ERROR :Link '%s' denied (Not using SSL/TLS) %s", - servername, inpath); - sendto_ops_and_log("Link denied for '%s' (Not using SSL/TLS and verify-certificate is on) %s", - servername, inpath); - exit_client(client, NULL, "Link denied (Not using SSL/TLS)"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_VERIFY_CERTIFICATE_FAILED", client, + "Link with server $client.details denied: verify-certificate failed: $certificate_failure_msg", + log_data_string("certificate_failure_msg", "not using TLS"), + log_data_link_block(link)); + exit_client(client, NULL, "Link denied (Not using TLS)"); return 0; } if (!verify_certificate(client->local->ssl, link->servername, &errstr)) { - sendto_one(client, NULL, - "ERROR :Link '%s' denied (Certificate verification failed) %s", - servername, inpath); - sendto_ops_and_log("Link denied for '%s' (Certificate verification failed) %s", - servername, inpath); - sendto_ops_and_log("Reason for certificate verification failure: %s", errstr); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_VERIFY_CERTIFICATE_FAILED", client, + "Link with server $client.details denied: verify-certificate failed: $certificate_failure_msg", + log_data_string("certificate_failure_msg", errstr), + log_data_link_block(link)); exit_client(client, NULL, "Link denied (Certificate verification failed)"); return 0; } } - /* - * Third phase, we check that the server does not exist - * already - */ - if ((acptr = find_server(servername, NULL))) + if ((bconf = find_ban(NULL, client->name, CONF_BAN_SERVER))) { - /* Found. Bad. Quit. */ - - if (IsMe(acptr)) - { - sendto_ops_and_log("Link %s rejected, server trying to link with my name (%s)", - get_client_name(client, TRUE), me.name); - sendto_one(client, NULL, "ERROR: Server %s exists (it's me!)", me.name); - exit_client(client, NULL, "Server Exists"); - return 0; - } - - acptr = acptr->direction; - ocptr = (client->local->firsttime > acptr->local->firsttime) ? acptr : client; - acptr = (client->local->firsttime > acptr->local->firsttime) ? client : acptr; - sendto_one(acptr, NULL, - "ERROR :Server %s already exists from %s", - servername, - (ocptr->direction ? ocptr->direction->name : "")); - sendto_ops_and_log - ("Link %s cancelled, server %s already exists from %s", - get_client_name(acptr, TRUE), servername, - (ocptr->direction ? ocptr->direction->name : "")); - exit_client(acptr, NULL, "Server Exists"); - return 0; - } - if ((bconf = find_ban(NULL, servername, CONF_BAN_SERVER))) - { - sendto_ops_and_log - ("Cancelling link %s, banned server", - get_client_name(client, TRUE)); - sendto_one(client, NULL, "ERROR :Banned server (%s)", bconf->reason ? bconf->reason : "no reason"); - exit_client(client, NULL, "Banned server"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_SERVER_BAN", client, + "Link with server $client.details denied: " + "Server is banned ($ban_reason)", + log_data_string("ban_reason", bconf->reason), + log_data_link_block(link)); + exit_client_fmt(client, NULL, "Banned server: %s", bconf->reason); return 0; } + if (link->class->clients + 1 > link->class->maxclients) { - sendto_ops_and_log("Cancelling link %s, full class", - get_client_name(client, TRUE)); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_CLASS_FULL", client, + "Link with server $client.details denied: " + "class '$link_block.class' is full", + log_data_link_block(link)); exit_client(client, NULL, "Full class"); return 0; } if (!IsLocalhost(client) && (iConf.plaintext_policy_server == POLICY_DENY) && !IsSecure(client)) { - sendto_one(client, NULL, "ERROR :Servers need to use SSL/TLS (set::plaintext-policy::server is 'deny')"); - sendto_ops_and_log("Rejected insecure server %s. See https://www.unrealircd.org/docs/FAQ#ERROR:_Servers_need_to_use_SSL.2FTLS", client->name); - exit_client(client, NULL, "Servers need to use SSL/TLS (set::plaintext-policy::server is 'deny')"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_NO_TLS", client, + "Link with server $client.details denied: " + "Server needs to use TLS (set::plaintext-policy::server is 'deny')\n" + "See https://www.unrealircd.org/docs/FAQ#server-requires-tls", + log_data_link_block(link)); + exit_client(client, NULL, "Servers need to use TLS (set::plaintext-policy::server is 'deny')"); return 0; } if (IsSecure(client) && (iConf.outdated_tls_policy_server == POLICY_DENY) && outdated_tls_client(client)) { - sendto_one(client, NULL, "ERROR :Server is using an outdated SSL/TLS protocol or cipher (set::outdated-tls-policy::server is 'deny')"); - sendto_ops_and_log("Rejected server %s using outdated %s. See https://www.unrealircd.org/docs/FAQ#server-outdated-tls", tls_get_cipher(client->local->ssl), client->name); - exit_client(client, NULL, "Server using outdates SSL/TLS protocol or cipher (set::outdated-tls-policy::server is 'deny')"); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_OUTDATED_TLS", client, + "Link with server $client.details denied: " + "Server is using an outdated TLS protocol or cipher ($tls_cipher) and set::outdated-tls-policy::server is 'deny'.\n" + "See https://www.unrealircd.org/docs/FAQ#server-outdated-tls", + log_data_link_block(link), + log_data_string("tls_cipher", tls_get_cipher(client))); + exit_client(client, NULL, "Server using outdates TLS protocol or cipher (set::outdated-tls-policy::server is 'deny')"); return 0; } + /* This one is at the end, because it causes us to delink another server, + * so we want to be (reasonably) sure that this one will succeed before + * breaking the other one. + */ + if ((acptr = find_server(client->name, NULL))) + { + if (IsMe(acptr)) + { + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_SERVER_EXISTS", client, + "Link with server $client.details denied: " + "Server is trying to link with my name ($me_name)", + log_data_string("me_name", me.name), + log_data_link_block(link)); + exit_client(client, NULL, "Server Exists (server trying to link with same name as myself)"); + return 0; + } else { + unreal_log(ULOG_ERROR, "link", "LINK_DROPPED_REINTRODUCED", client, + "Link with server $client.details causes older link " + "with same server via $existing_client.server.uplink to be dropped.", + log_data_client("existing_client", acptr), + log_data_link_block(link)); + exit_client_ex(acptr, client->direction, NULL, "Old link dropped, resyncing"); + } + } + if (link_out) *link_out = link; return 1; @@ -470,7 +873,7 @@ skip_host_check: */ CMD_FUNC(cmd_server) { - char *servername = NULL; /* Pointer for servername */ + const char *servername = NULL; /* Pointer for servername */ char *ch = NULL; /* */ char descbuf[BUFSIZE]; int hop = 0; @@ -478,6 +881,7 @@ CMD_FUNC(cmd_server) ConfigItem_link *aconf = NULL; ConfigItem_deny_link *deny; char *flags = NULL, *protocol = NULL, *inf = NULL, *num = NULL; + int incoming; if (IsUser(client)) { @@ -488,8 +892,7 @@ CMD_FUNC(cmd_server) if (parc < 4 || (!*parv[3])) { - sendto_one(client, NULL, "ERROR :Not enough SERVER parameters"); - exit_client(client, NULL, "Not enough parameters"); + exit_client(client, NULL, "Not enough SERVER parameters"); return; } @@ -498,10 +901,11 @@ CMD_FUNC(cmd_server) /* Remote 'SERVER' command is not possible on a 100% SID network */ if (!MyConnect(client)) { - char buf[256]; - sendto_umode_global(UMODE_OPER, "Server %s introduced %s which is using old unsupported protocol from UnrealIRCd 3.2.x or earlier. " - "See https://www.unrealircd.org/docs/FAQ#old-server-protocol", - client->direction->name, servername); + unreal_log(ULOG_ERROR, "link", "LINK_OLD_PROTOCOL", client, + "Server link $client tried to introduce $servername using SERVER command. " + "Server is using an old and unsupported protocol from UnrealIRCd 3.2.x or earlier. " + "See https://www.unrealircd.org/docs/FAQ#old-server-protocol", + log_data_string("servername", servername)); exit_client(client->direction, NULL, "Introduced another server with unsupported protocol"); return; } @@ -514,38 +918,37 @@ CMD_FUNC(cmd_server) if (!valid_server_name(servername)) { - sendto_one(client, NULL, "ERROR :Bogus server name (%s)", servername); - sendto_snomask - (SNO_JUNK, - "WARNING: Bogus server name (%s) from %s (maybe just a fishy client)", - servername, get_client_name(client, TRUE)); exit_client(client, NULL, "Bogus server name"); return; } if (!client->local->passwd) { - sendto_one(client, NULL, "ERROR :Missing password"); exit_client(client, NULL, "Missing password"); return; } - if (!verify_link(client, servername, &aconf)) + /* We set the client->name early here, even though it is not authenticated yet. + * Reason is that it makes the notices and logging more useful. + * This should be safe as it is not in the server linked list yet or hash table. + * CMTSRV941 -- Syzop. + */ + strlcpy(client->name, servername, sizeof(client->name)); + + if (!verify_link(client, &aconf)) return; /* Rejected */ /* From this point the server is authenticated, so we can be more verbose * with notices to ircops and in exit_client() and such. */ - strlcpy(client->name, servername, sizeof(client->name)); if (strlen(client->id) != 3) { - sendto_umode_global(UMODE_OPER, "Server %s is using old unsupported protocol from UnrealIRCd 3.2.x or earlier. " - "See https://www.unrealircd.org/docs/FAQ#old-server-protocol", - servername); - ircd_log(LOG_ERROR, "Server using old unsupported protocol from UnrealIRCd 3.2.x or earlier. " - "See https://www.unrealircd.org/docs/FAQ#old-server-protocol"); + unreal_log(ULOG_ERROR, "link", "LINK_OLD_PROTOCOL", client, + "Server link $servername rejected. Server is using an old and unsupported protocol from UnrealIRCd 3.2.x or earlier. " + "See https://www.unrealircd.org/docs/FAQ#old-server-protocol", + log_data_string("servername", servername)); exit_client(client, NULL, "Server using old unsupported protocol from UnrealIRCd 3.2.x or earlier. " "See https://www.unrealircd.org/docs/FAQ#old-server-protocol"); return; @@ -554,8 +957,10 @@ CMD_FUNC(cmd_server) hop = atol(parv[2]); if (hop != 1) { - sendto_umode_global(UMODE_OPER, "Directly linked server %s provided a hopcount of %d, while 1 was expected", - servername, hop); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_INVALID_HOPCOUNT", client, + "Server link $servername rejected. Directly linked server provided a hopcount of $hopcount, while 1 was expected.", + log_data_string("servername", servername), + log_data_integer("hopcount", hop)); exit_client(client, NULL, "Invalid SERVER message, hop count must be 1"); return; } @@ -597,11 +1002,12 @@ CMD_FUNC(cmd_server) /* Process deny server { } restrictions */ for (deny = conf_deny_link; deny; deny = deny->next) { - if (deny->flag.type == CRULE_ALL && match_simple(deny->mask, servername) + if (deny->flag.type == CRULE_ALL && unreal_mask_match_string(servername, deny->mask) && crule_eval(deny->rule)) { - sendto_ops_and_log("Refused connection from %s. Rejected by deny link { } block.", - get_client_host(client)); + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_DENY_LINK_BLOCK", client, + "Server link $servername rejected by deny link { } block.", + log_data_string("servername", servername)); exit_client(client, NULL, "Disallowed by connection rule"); return; } @@ -613,7 +1019,86 @@ CMD_FUNC(cmd_server) ircsnprintf(descbuf, sizeof descbuf, "Server: %s", servername); fd_desc(client->local->fd, descbuf); - server_sync(client, aconf); + incoming = IsUnknown(client) ? 1 : 0; + + if (client->local->passwd) + safe_free(client->local->passwd); + + /* Set up server structure */ + free_pending_net(client); + SetServer(client); + irccounts.me_servers++; + irccounts.servers++; + irccounts.unknown--; + list_move(&client->client_node, &global_server_list); + list_move(&client->lclient_node, &lclient_list); + list_add(&client->special_node, &server_list); + + if (find_uline(client->name)) + { + if (client->server && client->server->features.software && !strncmp(client->server->features.software, "UnrealIRCd-", 11)) + { + unreal_log(ULOG_ERROR, "link", "BAD_ULINES", client, + "Bad ulines! Server $client matches your ulines { } block, but this server " + "is an UnrealIRCd server. UnrealIRCd servers should never be ulined as it " + "causes security issues. Ulines should only be added for services! " + "See https://www.unrealircd.org/docs/FAQ#bad-ulines."); + exit_client(client, NULL, "Bad ulines. See https://www.unrealircd.org/docs/FAQ#bad-ulines"); + } + SetULine(client); + } + + find_or_add(client->name); + + if (IsSecure(client)) + { + unreal_log(ULOG_INFO, "link", "SERVER_LINKED", client, + "Server linked: $me -> $client [secure: $tls_cipher]", + log_data_string("tls_cipher", tls_get_cipher(client)), + log_data_client("me", &me)); + tls_link_notification_verify(client, aconf); + } + else + { + unreal_log(ULOG_INFO, "link", "SERVER_LINKED", client, + "Server linked: $me -> $client", + log_data_client("me", &me)); + /* Print out a warning if linking to a non-TLS server unless it's localhost. + * Yeah.. there are still other cases when non-TLS links are fine (eg: local IP + * of the same machine), we won't bother with detecting that. -- Syzop + */ + if (!IsLocalhost(client) && (iConf.plaintext_policy_server == POLICY_WARN)) + { + unreal_log(ULOG_WARNING, "link", "LINK_WARNING_NO_TLS", client, + "Link with server $client.details is unencrypted (not TLS). " + "We highly recommend to use TLS for server linking. " + "See https://www.unrealircd.org/docs/Linking_servers", + log_data_link_block(aconf)); + } + if (IsSecure(client) && (iConf.outdated_tls_policy_server == POLICY_WARN) && outdated_tls_client(client)) + { + unreal_log(ULOG_WARNING, "link", "LINK_WARNING_OUTDATED_TLS", client, + "Link with server $client.details is using an outdated " + "TLS protocol or cipher ($tls_cipher).", + log_data_link_block(aconf), + log_data_string("tls_cipher", tls_get_cipher(client))); + } + } + + add_to_client_hash_table(client->name, client); + /* doesnt duplicate client->server if allocted this struct already */ + make_server(client); + client->uplink = &me; + if (!client->server->conf) + client->server->conf = aconf; /* Only set serv->conf to aconf if not set already! Bug #0003913 */ + if (incoming) + client->server->conf->refcount++; + client->server->conf->class->clients++; + client->local->class = client->server->conf->class; + + RunHook(HOOKTYPE_SERVER_CONNECT, client); + + server_sync(client, aconf, incoming); } /** Remote server command (SID). @@ -627,9 +1112,9 @@ CMD_FUNC(cmd_sid) Client *acptr, *ocptr; ConfigItem_link *aconf; ConfigItem_ban *bconf; - int hop; - char *servername = parv[1]; - Client *cptr = client->direction; /* lazy, since this function may be removed soon */ + int hop; + const char *servername = parv[1]; + Client *direction = client->direction; /* lazy, since this function may be removed soon */ /* Only allow this command from server sockets */ if (!IsServer(client->direction)) @@ -640,7 +1125,25 @@ CMD_FUNC(cmd_sid) if (parc < 4 || BadPtr(parv[3])) { - sendto_one(client, NULL, "ERROR :Not enough SID parameters"); + sendnumeric(client, ERR_NEEDMOREPARAMS, "SID"); + return; + } + + /* The SID check is done early because we do all the killing by SID, + * so we want to know if that won't work first. + */ + if (!valid_sid(parv[3])) + { + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_INVALID_SID", client, + "Denied remote server $servername which was introduced by $client: " + "Invalid SID.", + log_data_string("servername", servername), + log_data_string("sid", parv[3])); + /* Since we cannot SQUIT via SID (since it is invalid), this gives + * us huge doubts about the accuracy of the uplink, so in this case + * we terminate the entire uplink. + */ + exit_client(client, NULL, "Trying to introduce a server with an invalid SID"); return; } @@ -651,87 +1154,116 @@ CMD_FUNC(cmd_sid) if (IsMe(acptr)) { - sendto_ops_and_log("Link %s rejected, server trying to link with my name (%s)", - get_client_name(client, TRUE), me.name); + /* This should never happen, not even due to a race condition. + * We cannot send SQUIT here either since it is unclear what + * side would be squitted. + * As said, not really important, as this does not happen anyway. + */ + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_DUPLICATE_SERVER_IS_ME", client, + "Denied remote server $servername which was introduced by $client: " + "Server is using our servername, this should be impossible!", + log_data_string("servername", servername)); sendto_one(client, NULL, "ERROR: Server %s exists (it's me!)", me.name); exit_client(client, NULL, "Server Exists"); return; } - // FIXME: verify this code: + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_DUPLICATE_SERVER", client, + "Denied remote server $servername which was introduced by $client: " + "Already linked via $existing_client.server.uplink.", + log_data_string("servername", servername), + log_data_client("existing_client", acptr)); + // FIXME: oldest should die. + // FIXME: code below looks wrong, it checks direction TS instead of anything else acptr = acptr->direction; - ocptr = (cptr->local->firsttime > acptr->local->firsttime) ? acptr : cptr; - acptr = (cptr->local->firsttime > acptr->local->firsttime) ? cptr : acptr; - sendto_one(acptr, NULL, - "ERROR :Server %s already exists from %s", - servername, - (ocptr->direction ? ocptr->direction->name : "")); - sendto_ops_and_log - ("Link %s cancelled, server %s already exists from %s", - get_client_name(acptr, TRUE), servername, - (ocptr->direction ? ocptr->direction->name : "")); + ocptr = (direction->local->creationtime > acptr->local->creationtime) ? acptr : direction; + acptr = (direction->local->creationtime > acptr->local->creationtime) ? direction : acptr; + // FIXME: Wait, this kills entire acptr? Without sending SQUIT even :D exit_client(acptr, NULL, "Server Exists"); return; } + if ((acptr = find_client(parv[3], NULL))) + { + unreal_log(ULOG_ERROR, "link", "LINK_DENIED_DUPLICATE_SID_SERVER", client, + "Denied server $servername with SID $sid: Server with SID $existing_client.id ($existing_client) is already linked.", + log_data_string("servername", servername), + log_data_string("sid", parv[3]), + log_data_client("existing_client", acptr)); + sendto_one(client, NULL, "SQUIT %s :Server with this SID (%s) already exists (%s)", parv[3], parv[3], acptr->name); + return; + } + /* Check deny server { } */ if ((bconf = find_ban(NULL, servername, CONF_BAN_SERVER))) { - sendto_ops_and_log("Cancelling link %s, banned server %s", - get_client_name(cptr, TRUE), servername); - sendto_one(cptr, NULL, "ERROR :Banned server (%s)", bconf->reason ? bconf->reason : "no reason"); - exit_client(cptr, NULL, "Brought in banned server"); + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_SERVER_BAN", client, + "Denied remote server $servername which was introduced by $client: " + "Server is banned ($ban_reason)", + log_data_string("ban_reason", bconf->reason)); + /* Before UnrealIRCd 6 this would SQUIT the server who introduced + * this server. That seems a bit of an overreaction, so we now + * send a SQUIT instead. + */ + sendto_one(client, NULL, "SQUIT %s :Banned server: %s", parv[3], bconf->reason); return; } /* OK, let us check the data now */ if (!valid_server_name(servername)) { - sendto_ops_and_log("Link %s introduced server with bad server name '%s' -- disconnecting", - client->name, servername); - exit_client(cptr, NULL, "Introduced server with bad server name"); + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_INVALID_SERVERNAME", client, + "Denied remote server $servername which was introduced by $client: " + "Invalid server name.", + log_data_string("servername", servername)); + sendto_one(client, NULL, "SQUIT %s :Invalid servername", parv[3]); return; } - hop = atol(parv[2]); + hop = atoi(parv[2]); if (hop < 2) { - sendto_ops_and_log("Server %s introduced server %s with hop count of %d, while >1 was expected", - client->name, servername, hop); - exit_client(cptr, NULL, "ERROR :Invalid hop count"); + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_INVALID_HOP_COUNT", client, + "Denied remote server $servername which was introduced by $client: " + "Invalid server name.", + log_data_string("servername", servername), + log_data_integer("hop_count", hop)); + sendto_one(client, NULL, "SQUIT %s :Invalid hop count (%d)", parv[3], hop); return; } - if (!valid_sid(parv[3])) + if (!client->direction->server->conf) { - sendto_ops_and_log("Server %s introduced server %s with invalid SID '%s' -- disconnecting", - client->name, servername, parv[3]); - exit_client(cptr, NULL, "ERROR :Invalid SID"); + unreal_log(ULOG_ERROR, "link", "BUG_LOST_CONFIG", client, + "[BUG] Lost link conf record for link $direction.", + log_data_client("direction", direction)); + exit_client(client->direction, NULL, "BUG: lost link configuration"); return; } - if (!cptr->serv->conf) - { - sendto_ops_and_log("Internal error: lost conf for %s!!, dropping link", cptr->name); - exit_client(cptr, NULL, "Internal error: lost configuration"); - return; - } - - aconf = cptr->serv->conf; + aconf = client->direction->server->conf; if (!aconf->hub) { - sendto_ops_and_log("Link %s cancelled, is Non-Hub but introduced Leaf %s", - cptr->name, servername); - exit_client(cptr, NULL, "Non-Hub Link"); + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_NO_HUB", client, + "Denied remote server $servername which was introduced by $client: " + "Server may not introduce this server ($direction is not a hub).", + log_data_string("servername", servername), + log_data_client("direction", client->direction)); + sendto_one(client, NULL, "SQUIT %s :Server is not permitted to be a hub: %s", + parv[3], client->direction->name); return; } if (!match_simple(aconf->hub, servername)) { - sendto_ops_and_log("Link %s cancelled, linked in %s, which hub config disallows", - cptr->name, servername); - exit_client(cptr, NULL, "Not matching hub configuration"); + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_NO_MATCHING_HUB", client, + "Denied remote server $servername which was introduced by $client: " + "Server may not introduce this server ($direction hubmask does not allow it).", + log_data_string("servername", servername), + log_data_client("direction", client->direction)); + sendto_one(client, NULL, "SQUIT %s :Hub config for %s does not allow introducing this server", + parv[3], client->direction->name); return; } @@ -739,31 +1271,37 @@ CMD_FUNC(cmd_sid) { if (!match_simple(aconf->leaf, servername)) { - sendto_ops_and_log("Link %s(%s) cancelled, disallowed by leaf configuration", - cptr->name, servername); - exit_client(cptr, NULL, "Disallowed by leaf configuration"); + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_NO_MATCHING_LEAF", client, + "Denied remote server $servername which was introduced by $client: " + "Server may not introduce this server ($direction leaf config does not allow it).", + log_data_string("servername", servername), + log_data_client("direction", client->direction)); + sendto_one(client, NULL, "SQUIT %s :Leaf config for %s does not allow introducing this server", + parv[3], client->direction->name); return; } } if (aconf->leaf_depth && (hop > aconf->leaf_depth)) { - sendto_ops_and_log("Link %s(%s) cancelled, too deep depth", - cptr->name, servername); - exit_client(cptr, NULL, "Too deep link depth (leaf)"); + unreal_log(ULOG_ERROR, "link", "REMOTE_LINK_DENIED_LEAF_DEPTH", client, + "Denied remote server $servername which was introduced by $client: " + "Server may not introduce this server ($direction leaf depth config does not allow it).", + log_data_string("servername", servername), + log_data_client("direction", client->direction)); + sendto_one(client, NULL, "SQUIT %s :Leaf depth config for %s does not allow introducing this server", + parv[3], client->direction->name); return; } /* All approved, add the server */ - acptr = make_client(cptr, find_server(client->name, cptr)); + acptr = make_client(direction, find_server(client->name, direction)); strlcpy(acptr->name, servername, sizeof(acptr->name)); acptr->hopcount = hop; strlcpy(acptr->id, parv[3], sizeof(acptr->id)); strlcpy(acptr->info, parv[parc - 1], sizeof(acptr->info)); make_server(acptr); - acptr->serv->up = find_or_add(acptr->srvptr->name); SetServer(acptr); - ircd_log(LOG_SERVER, "SERVER %s (from %s)", acptr->name, acptr->srvptr->name); /* If this server is U-lined, or the parent is, then mark it as U-lined */ if (IsULine(client) || find_uline(acptr->name)) SetULine(acptr); @@ -774,10 +1312,18 @@ CMD_FUNC(cmd_sid) add_to_id_hash_table(acptr->id, acptr); list_move(&acptr->client_node, &global_server_list); + if (IsULine(client->direction) || IsSynched(client->direction)) + { + /* Log these (but don't show when still syncing) */ + unreal_log(ULOG_INFO, "link", "SERVER_LINKED_REMOTE", acptr, + "Server linked: $client -> $other_server", + log_data_client("other_server", client)); + } + RunHook(HOOKTYPE_SERVER_CONNECT, acptr); sendto_server(client, 0, 0, NULL, ":%s SID %s %d %s :%s", - acptr->srvptr->id, acptr->name, hop + 1, acptr->id, acptr->info); + acptr->uplink->id, acptr->name, hop + 1, acptr->id, acptr->info); RunHook(HOOKTYPE_POST_SERVER_CONNECT, acptr); } @@ -786,7 +1332,7 @@ void _introduce_user(Client *to, Client *acptr) { build_umode_string(acptr, 0, SEND_UMODES, buf); - sendto_one_nickcmd(to, acptr, buf); + sendto_one_nickcmd(to, NULL, acptr, buf); send_moddata_client(to, acptr); @@ -811,14 +1357,164 @@ void _introduce_user(Client *to, Client *acptr) } } -void tls_link_notification_verify(Client *acptr, ConfigItem_link *aconf) +#define SafeStr(x) ((x && *(x)) ? (x) : "*") + +/** Broadcast SINFO. + * @param cptr The server to send the information about. + * @param to The server to send the information TO (NULL for broadcast). + * @param except The direction NOT to send to. + * This function takes into account that the server may not + * provide all of the detailed info. If any information is + * absent we will send 0 for numbers and * for NULL strings. + */ +void _broadcast_sinfo(Client *acptr, Client *to, Client *except) { - char *spki_fp; - char *tls_fp; + char chanmodes[128], buf[512]; + + if (acptr->server->features.chanmodes[0]) + { + snprintf(chanmodes, sizeof(chanmodes), "%s,%s,%s,%s", + acptr->server->features.chanmodes[0], + acptr->server->features.chanmodes[1], + acptr->server->features.chanmodes[2], + acptr->server->features.chanmodes[3]); + } else { + strlcpy(chanmodes, "*", sizeof(chanmodes)); + } + + snprintf(buf, sizeof(buf), "%lld %d %s %s %s :%s", + (long long)acptr->server->boottime, + acptr->server->features.protocol, + SafeStr(acptr->server->features.usermodes), + chanmodes, + SafeStr(acptr->server->features.nickchars), + SafeStr(acptr->server->features.software)); + + if (to) + { + /* Targetted to one server */ + sendto_one(to, NULL, ":%s SINFO %s", acptr->id, buf); + } else { + /* Broadcast (except one side...) */ + sendto_server(except, 0, 0, NULL, ":%s SINFO %s", acptr->id, buf); + } +} + +/** Sync all information with server 'client'. + * Eg: users, channels, everything. + * @param client The newly linked in server + * @param aconf The link block that belongs to this server + * @note This function (via cmd_server) is called from both sides, so + * from the incoming side and the outgoing side. + */ +int server_sync(Client *client, ConfigItem_link *aconf, int incoming) +{ + Client *acptr; + + if (incoming) + { + /* If this is an incomming connection, then we have just received + * their stuff and now send our PASS, PROTOCTL and SERVER messages back. + */ + if (!IsEAuth(client)) /* if eauth'd then we already sent the passwd */ + sendto_one(client, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); + + send_proto(client, aconf); + send_server_message(client); + } + + /* Broadcast new server to the rest of the network */ + sendto_server(client, 0, 0, NULL, ":%s SID %s 2 %s :%s", + client->uplink->id, client->name, client->id, client->info); + + /* Broadcast the just-linked-in featureset to other servers on our side */ + broadcast_sinfo(client, NULL, client); + + /* Send moddata of &me (if any, likely minimal) */ + send_moddata_client(client, &me); + + list_for_each_entry_reverse(acptr, &global_server_list, client_node) + { + /* acptr->direction == acptr for acptr == client */ + if (acptr->direction == client) + continue; + + if (IsServer(acptr)) + { + sendto_one(client, NULL, ":%s SID %s %d %s :%s", + acptr->uplink->id, + acptr->name, acptr->hopcount + 1, + acptr->id, acptr->info); + + /* Also signal to the just-linked server which + * servers are fully linked. + * Now you might ask yourself "Why don't we just + * assume every server you get during link phase + * is fully linked?", well.. there's a race condition + * if 2 servers link (almost) at the same time, + * then you would think the other one is fully linked + * while in fact he was not.. -- Syzop. + */ + if (acptr->server->flags.synced) + sendto_one(client, NULL, ":%s EOS", acptr->id); + /* Send SINFO of our servers to their side */ + broadcast_sinfo(acptr, client, NULL); + send_moddata_client(client, acptr); /* send moddata of server 'acptr' (if any, likely minimal) */ + } + } + + /* Synching nick information */ + list_for_each_entry_reverse(acptr, &client_list, client_node) + { + /* acptr->direction == acptr for acptr == client */ + if (acptr->direction == client) + continue; + if (IsUser(acptr)) + introduce_user(client, acptr); + } + /* + ** Last, pass all channels plus statuses + */ + { + Channel *channel; + for (channel = channels; channel; channel = channel->nextch) + { + send_channel_modes_sjoin3(client, channel); + if (channel->topic_time) + sendto_one(client, NULL, "TOPIC %s %s %lld :%s", + channel->name, channel->topic_nick, + (long long)channel->topic_time, channel->topic); + send_moddata_channel(client, channel); + } + } + + /* Send ModData for all member(ship) structs */ + send_moddata_members(client); + + /* pass on TKLs */ + tkl_sync(client); + + RunHook(HOOKTYPE_SERVER_SYNC, client); + + sendto_one(client, NULL, "NETINFO %i %lld %i %s 0 0 0 :%s", + irccounts.global_max, (long long)TStime(), UnrealProtocol, + CLOAK_KEY_CHECKSUM, + NETWORK_NAME); + + /* Send EOS (End Of Sync) to the just linked server... */ + sendto_one(client, NULL, ":%s EOS", me.id); + RunHook(HOOKTYPE_POST_SERVER_CONNECT, client); + return 0; +} + +void tls_link_notification_verify(Client *client, ConfigItem_link *aconf) +{ + const char *spki_fp; + const char *tls_fp; char *errstr = NULL; int verify_ok; - if (!MyConnect(acptr) || !acptr->local->ssl || !aconf) + if (!MyConnect(client) || !client->local->ssl || !aconf) return; if ((aconf->auth->type == AUTHTYPE_TLS_CLIENTCERT) || @@ -835,270 +1531,54 @@ void tls_link_notification_verify(Client *acptr, ConfigItem_link *aconf) return; } - tls_fp = moddata_client_get(acptr, "certfp"); - spki_fp = spki_fingerprint(acptr); + tls_fp = moddata_client_get(client, "certfp"); + spki_fp = spki_fingerprint(client); if (!tls_fp || !spki_fp) return; /* wtf ? */ /* Only bother the user if we are linking to UnrealIRCd 4.0.16+, * since only for these versions we can give precise instructions. */ - if (!acptr->serv || acptr->serv->features.protocol < 4016) + if (!client->server || client->server->features.protocol < 4016) return; - sendto_realops("You may want to consider verifying this server link."); - sendto_realops("More information about this can be found on https://www.unrealircd.org/Link_verification"); - verify_ok = verify_certificate(acptr->local->ssl, aconf->servername, &errstr); + verify_ok = verify_certificate(client->local->ssl, aconf->servername, &errstr); if (errstr && strstr(errstr, "not valid for hostname")) { - sendto_realops("Unfortunately the certificate of server '%s' has a name mismatch:", acptr->name); - sendto_realops("%s", errstr); - sendto_realops("This isn't a fatal error but it will prevent you from using verify-certificate yes;"); + unreal_log(ULOG_INFO, "link", "HINT_VERIFY_LINK", client, + "You may want to consider verifying this server link.\n" + "More information about this can be found on https://www.unrealircd.org/Link_verification\n" + "Unfortunately the certificate of server '$client' has a name mismatch:\n" + "$tls_verify_error\n" + "This isn't a fatal error but it will prevent you from using verify-certificate yes;", + log_data_link_block(aconf), + log_data_string("tls_verify_error", errstr)); } else if (!verify_ok) { - sendto_realops("In short: in the configuration file, change the 'link %s {' block to use this as a password:", acptr->name); - sendto_realops("password \"%s\" { spkifp; };", spki_fp); - sendto_realops("And follow the instructions on the other side of the link as well (which will be similar, but will use a different hash)"); + unreal_log(ULOG_INFO, "link", "HINT_VERIFY_LINK", client, + "You may want to consider verifying this server link.\n" + "More information about this can be found on https://www.unrealircd.org/Link_verification\n" + "In short: in the configuration file, change the 'link $client {' block to use this as a password:\n" + "password \"$spki_fingerprint\" { spkifp; };\n" + "And follow the instructions on the other side of the link as well (which will be similar, but will use a different hash)", + log_data_link_block(aconf), + log_data_string("spki_fingerprint", spki_fp)); } else { - sendto_realops("In short: in the configuration file, add the following to your 'link %s {' block:", acptr->name); - sendto_realops("verify-certificate yes;"); - sendto_realops("Alternatively, you could use SPKI fingerprint verification. Then change the password in the link block to be:"); - sendto_realops("password \"%s\" { spkifp; };", spki_fp); + unreal_log(ULOG_INFO, "link", "HINT_VERIFY_LINK", client, + "You may want to consider verifying this server link.\n" + "More information about this can be found on https://www.unrealircd.org/Link_verification\n" + "In short: in the configuration file, add the following to your 'link $client {' block:\n" + "verify-certificate yes;\n" + "Alternatively, you could use SPKI fingerprint verification. Then change the password in the link block to be:\n" + "password \"$spki_fingerprint\" { spki_fp; };", + log_data_link_block(aconf), + log_data_string("spki_fingerprint", spki_fp)); } } -#define SafeStr(x) ((x && *(x)) ? (x) : "*") - -/** Broadcast SINFO. - * @param cptr The server to send the information about. - * @param to The server to send the information TO (NULL for broadcast). - * @param except The direction NOT to send to. - * This function takes into account that the server may not - * provide all of the detailed info. If any information is - * absent we will send 0 for numbers and * for NULL strings. - */ -void _broadcast_sinfo(Client *acptr, Client *to, Client *except) -{ - char chanmodes[128], buf[512]; - - if (acptr->serv->features.chanmodes[0]) - { - snprintf(chanmodes, sizeof(chanmodes), "%s,%s,%s,%s", - acptr->serv->features.chanmodes[0], - acptr->serv->features.chanmodes[1], - acptr->serv->features.chanmodes[2], - acptr->serv->features.chanmodes[3]); - } else { - strlcpy(chanmodes, "*", sizeof(chanmodes)); - } - - snprintf(buf, sizeof(buf), "%lld %d %s %s %s :%s", - (long long)acptr->serv->boottime, - acptr->serv->features.protocol, - SafeStr(acptr->serv->features.usermodes), - chanmodes, - SafeStr(acptr->serv->features.nickchars), - SafeStr(acptr->serv->features.software)); - - if (to) - { - /* Targetted to one server */ - sendto_one(to, NULL, ":%s SINFO %s", acptr->id, buf); - } else { - /* Broadcast (except one side...) */ - sendto_server(except, 0, 0, NULL, ":%s SINFO %s", acptr->id, buf); - } -} - -int server_sync(Client *cptr, ConfigItem_link *aconf) -{ - char *inpath = get_client_name(cptr, TRUE); - Client *acptr; - int incoming = IsUnknown(cptr) ? 1 : 0; - - ircd_log(LOG_SERVER, "SERVER %s", cptr->name); - - if (cptr->local->passwd) - { - safe_free(cptr->local->passwd); - cptr->local->passwd = NULL; - } - if (incoming) - { - /* If this is an incomming connection, then we have just received - * their stuff and now send our stuff back. - */ - if (!IsEAuth(cptr)) /* if eauth'd then we already sent the passwd */ - sendto_one(cptr, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); - - send_proto(cptr, aconf); - send_server_message(cptr); - } - - /* Set up server structure */ - free_pending_net(cptr); - SetServer(cptr); - irccounts.me_servers++; - irccounts.servers++; - irccounts.unknown--; - list_move(&cptr->client_node, &global_server_list); - list_move(&cptr->lclient_node, &lclient_list); - list_add(&cptr->special_node, &server_list); - if (find_uline(cptr->name)) - { - if (cptr->serv && cptr->serv->features.software && !strncmp(cptr->serv->features.software, "UnrealIRCd-", 11)) - { - sendto_realops("\002WARNING:\002 Bad ulines! It seems your server is misconfigured: " - "your ulines { } block is matching an UnrealIRCd server (%s). " - "This is not correct and will cause security issues. " - "ULines should only be added for services! " - "See https://www.unrealircd.org/docs/FAQ#bad-ulines", - cptr->name); - } - SetULine(cptr); - } - find_or_add(cptr->name); - if (IsSecure(cptr)) - { - sendto_umode_global(UMODE_OPER, - "(\2link\2) Secure link %s -> %s established (%s)", - me.name, inpath, tls_get_cipher(cptr->local->ssl)); - tls_link_notification_verify(cptr, aconf); - } - else - { - sendto_umode_global(UMODE_OPER, - "(\2link\2) Link %s -> %s established", - me.name, inpath); - /* Print out a warning if linking to a non-TLS server unless it's localhost. - * Yeah.. there are still other cases when non-TLS links are fine (eg: local IP - * of the same machine), we won't bother with detecting that. -- Syzop - */ - if (!IsLocalhost(cptr) && (iConf.plaintext_policy_server == POLICY_WARN)) - { - sendto_realops("\002WARNING:\002 This link is unencrypted (not SSL/TLS). We highly recommend to use " - "SSL/TLS for server linking. See https://www.unrealircd.org/docs/Linking_servers"); - } - if (IsSecure(cptr) && (iConf.outdated_tls_policy_server == POLICY_WARN) && outdated_tls_client(cptr)) - { - sendto_realops("\002WARNING:\002 This link is using an outdated SSL/TLS protocol or cipher (%s).", - tls_get_cipher(cptr->local->ssl)); - } - } - add_to_client_hash_table(cptr->name, cptr); - /* doesnt duplicate cptr->serv if allocted this struct already */ - make_server(cptr); - cptr->serv->up = me.name; - cptr->srvptr = &me; - if (!cptr->serv->conf) - cptr->serv->conf = aconf; /* Only set serv->conf to aconf if not set already! Bug #0003913 */ - if (incoming) - { - cptr->serv->conf->refcount++; - Debug((DEBUG_ERROR, "reference count for %s (%s) is now %d", - cptr->name, cptr->serv->conf->servername, cptr->serv->conf->refcount)); - } - cptr->serv->conf->class->clients++; - cptr->local->class = cptr->serv->conf->class; - RunHook(HOOKTYPE_SERVER_CONNECT, cptr); - - /* Broadcast new server to the rest of the network */ - sendto_server(cptr, 0, 0, NULL, ":%s SID %s 2 %s :%s", - cptr->srvptr->id, cptr->name, cptr->id, cptr->info); - - /* Broadcast the just-linked-in featureset to other servers on our side */ - broadcast_sinfo(cptr, NULL, cptr); - - /* Send moddata of &me (if any, likely minimal) */ - send_moddata_client(cptr, &me); - - list_for_each_entry_reverse(acptr, &global_server_list, client_node) - { - /* acptr->direction == acptr for acptr == cptr */ - if (acptr->direction == cptr) - continue; - - if (IsServer(acptr)) - { - sendto_one(cptr, NULL, ":%s SID %s %d %s :%s", - acptr->srvptr->id, - acptr->name, acptr->hopcount + 1, - acptr->id, acptr->info); - - /* Also signal to the just-linked server which - * servers are fully linked. - * Now you might ask yourself "Why don't we just - * assume every server you get during link phase - * is fully linked?", well.. there's a race condition - * if 2 servers link (almost) at the same time, - * then you would think the other one is fully linked - * while in fact he was not.. -- Syzop. - */ - if (acptr->serv->flags.synced) - { - sendto_one(cptr, NULL, ":%s EOS", acptr->id); -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[EOSDBG] server_sync: sending to uplink '%s' with src %s...", - cptr->name, acptr->name); -#endif - } - /* Send SINFO of our servers to their side */ - broadcast_sinfo(acptr, cptr, NULL); - send_moddata_client(cptr, acptr); /* send moddata of server 'acptr' (if any, likely minimal) */ - } - } - - /* Synching nick information */ - list_for_each_entry_reverse(acptr, &client_list, client_node) - { - /* acptr->direction == acptr for acptr == cptr */ - if (acptr->direction == cptr) - continue; - if (IsUser(acptr)) - introduce_user(cptr, acptr); - } - /* - ** Last, pass all channels plus statuses - */ - { - Channel *channel; - for (channel = channels; channel; channel = channel->nextch) - { - send_channel_modes_sjoin3(cptr, channel); - if (channel->topic_time) - sendto_one(cptr, NULL, "TOPIC %s %s %lld :%s", - channel->chname, channel->topic_nick, - (long long)channel->topic_time, channel->topic); - send_moddata_channel(cptr, channel); - } - } - - /* Send ModData for all member(ship) structs */ - send_moddata_members(cptr); - - /* pass on TKLs */ - tkl_sync(cptr); - - RunHook(HOOKTYPE_SERVER_SYNC, cptr); - - sendto_one(cptr, NULL, "NETINFO %i %lld %i %s 0 0 0 :%s", - irccounts.global_max, (long long)TStime(), UnrealProtocol, - CLOAK_KEYCRC, - ircnetwork); - - /* Send EOS (End Of Sync) to the just linked server... */ - sendto_one(cptr, NULL, ":%s EOS", me.id); -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[EOSDBG] server_sync: sending to justlinked '%s' with src ME...", - cptr->name); -#endif - RunHook(HOOKTYPE_POST_SERVER_CONNECT, cptr); - return 0; -} - /** This will send "to" a full list of the modes for channel channel, * * Half of it recoded by Syzop: the whole buffering and size checking stuff @@ -1118,8 +1598,9 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) char *p; /* points to somewhere in 'tbuf' */ int prebuflen = 0; /* points to after the > : part */ int sent = 0; /* we need this so we send at least 1 message about the channel (eg if +P and no members, no bans, #4459) */ + char modebuf[BUFSIZE], parabuf[BUFSIZE]; - if (*channel->chname != '#') + if (*channel->name != '#') return; nomode = 0; @@ -1129,7 +1610,11 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) /* First we'll send channel, channel modes and members and status */ *modebuf = *parabuf = '\0'; - channel_modes(to, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel); + channel_modes(to, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel, 1); + + /* Strip final space if needed */ + if (*parabuf && (parabuf[strlen(parabuf)-1] == ' ')) + parabuf[strlen(parabuf)-1] = '\0'; if (!modebuf[1]) nomode = 1; @@ -1148,19 +1633,19 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) { ircsnprintf(buf, sizeof(buf), ":%s SJOIN %lld %s :", me.id, - (long long)channel->creationtime, channel->chname); + (long long)channel->creationtime, channel->name); } if (nopara && !nomode) { ircsnprintf(buf, sizeof(buf), ":%s SJOIN %lld %s %s :", me.id, - (long long)channel->creationtime, channel->chname, modebuf); + (long long)channel->creationtime, channel->name, modebuf); } if (!nopara && !nomode) { ircsnprintf(buf, sizeof(buf), ":%s SJOIN %lld %s %s %s :", me.id, - (long long)channel->creationtime, channel->chname, modebuf, parabuf); + (long long)channel->creationtime, channel->name, modebuf, parabuf); } prebuflen = strlen(buf); @@ -1190,19 +1675,8 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) for (lp = members; lp; lp = lp->next) { - p = tbuf; - if (lp->flags & MODE_CHANOP) - *p++ = '@'; - if (lp->flags & MODE_VOICE) - *p++ = '+'; - if (lp->flags & MODE_HALFOP) - *p++ = '%'; - if (lp->flags & MODE_CHANOWNER) - *p++ = '*'; - if (lp->flags & MODE_CHANADMIN) - *p++ = '~'; - - p = mystpcpy(p, lp->client->id); + p = mystpcpy(tbuf, modes_to_sjoin_prefix(lp->member_modes)); /* eg @+ */ + p = mystpcpy(p, lp->client->id); /* nick (well, id) */ *p++ = ' '; *p = '\0'; @@ -1212,6 +1686,10 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) /* Would overflow, so send our current stuff right now (except new stuff) */ sendto_one(to, mtags, "%s", buf); sent++; + ircsnprintf(buf, sizeof(buf), + ":%s SJOIN %lld %s :", me.id, + (long long)channel->creationtime, channel->name); + prebuflen = strlen(buf); bufptr = buf + prebuflen; *bufptr = '\0'; } @@ -1235,6 +1713,10 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) /* Would overflow, so send our current stuff right now (except new stuff) */ sendto_one(to, mtags, "%s", buf); sent++; + ircsnprintf(buf, sizeof(buf), + ":%s SJOIN %lld %s :", me.id, + (long long)channel->creationtime, channel->name); + prebuflen = strlen(buf); bufptr = buf + prebuflen; *bufptr = '\0'; } @@ -1258,6 +1740,10 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) /* Would overflow, so send our current stuff right now (except new stuff) */ sendto_one(to, mtags, "%s", buf); sent++; + ircsnprintf(buf, sizeof(buf), + ":%s SJOIN %lld %s :", me.id, + (long long)channel->creationtime, channel->name); + prebuflen = strlen(buf); bufptr = buf + prebuflen; *bufptr = '\0'; } @@ -1281,6 +1767,10 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) /* Would overflow, so send our current stuff right now (except new stuff) */ sendto_one(to, mtags, "%s", buf); sent++; + ircsnprintf(buf, sizeof(buf), + ":%s SJOIN %lld %s :", me.id, + (long long)channel->creationtime, channel->name); + prebuflen = strlen(buf); bufptr = buf + prebuflen; *bufptr = '\0'; } @@ -1293,3 +1783,203 @@ void send_channel_modes_sjoin3(Client *to, Channel *channel) free_message_tags(mtags); } + +void server_generic_free(ModData *m) +{ + safe_free(m->ptr); +} + +int server_post_connect(Client *client) { + if (cfg.autoconnect_strategy == AUTOCONNECT_SEQUENTIAL_FALLBACK && last_autoconnect_server + && !strcmp(last_autoconnect_server, client->name)) + { + last_autoconnect_server = NULL; + } + return 0; +} + +/** Start an outgoing connection to a server, for server linking. + * @param aconf Configuration attached to this server + * @param by The user initiating the connection (can be NULL) + * @param hp The address to connect to. + */ +void _connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp) +{ + Client *client; + + if (!aconf->outgoing.hostname) + { + /* Actually the caller should make sure that this doesn't happen, + * so this error may never be triggered: + */ + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_NO_OUTGOING", NULL, + "Connect to $link_block failed: link block is for incoming only (no link::outgoing::hostname set)", + log_data_link_block(aconf)); + return; + } + + if (!hp) + { + /* Remove "cache" */ + safe_free(aconf->connect_ip); + } + /* + * If we dont know the IP# for this host and itis a hostname and + * not a ip# string, then try and find the appropriate host record. + */ + if (!aconf->connect_ip) + { + if (is_valid_ip(aconf->outgoing.hostname)) + { + /* link::outgoing::hostname is an IP address. No need to resolve host. */ + safe_strdup(aconf->connect_ip, aconf->outgoing.hostname); + } else + { + /* It's a hostname, let the resolver look it up. */ + int ipv4_explicit_bind = 0; + + if (aconf->outgoing.bind_ip && (is_valid_ip(aconf->outgoing.bind_ip) == 4)) + ipv4_explicit_bind = 1; + + /* We need this 'aconf->refcount++' or else there's a race condition between + * starting resolving the host and the result of the resolver (we could + * REHASH in that timeframe) leading to an invalid (freed!) 'aconf'. + * -- Syzop, bug #0003689. + */ + aconf->refcount++; + unrealdns_gethostbyname_link(aconf->outgoing.hostname, aconf, ipv4_explicit_bind); + unreal_log(ULOG_INFO, "link", "LINK_RESOLVING", NULL, + "Resolving hostname $link_block.hostname...", + log_data_link_block(aconf)); + /* Going to resolve the hostname, in the meantime we return (asynchronous operation) */ + return; + } + } + client = make_client(NULL, &me); + client->local->hostp = hp; + /* + * Copy these in so we have something for error detection. + */ + strlcpy(client->name, aconf->servername, sizeof(client->name)); + strlcpy(client->local->sockhost, aconf->outgoing.hostname, HOSTLEN + 1); + + if (!connect_server_helper(aconf, client)) + { + fd_close(client->local->fd); + --OpenFiles; + client->local->fd = -2; + free_client(client); + /* Fatal error */ + return; + } + /* The socket has been connected or connect is in progress. */ + make_server(client); + client->server->conf = aconf; + client->server->conf->refcount++; + if (by && IsUser(by)) + strlcpy(client->server->by, by->name, sizeof(client->server->by)); + else + strlcpy(client->server->by, "AutoConn.", sizeof client->server->by); + SetConnecting(client); + SetOutgoing(client); + irccounts.unknown++; + list_add(&client->lclient_node, &unknown_list); + set_sockhost(client, aconf->outgoing.hostname); + add_client_to_list(client); + + if (aconf->outgoing.options & CONNECT_TLS) + { + SetTLSConnectHandshake(client); + fd_setselect(client->local->fd, FD_SELECT_WRITE, unreal_tls_client_handshake, client); + } + else + fd_setselect(client->local->fd, FD_SELECT_WRITE, completed_connection, client); + + unreal_log(ULOG_INFO, "link", "LINK_CONNECTING", client, + "Trying to activate link with server $client ($link_block.ip:$link_block.port)...", + log_data_link_block(aconf)); +} + +/** Helper function for connect_server() to prepare the actual bind()'ing and connect(). + * This will also take care of logging/sending error messages. + * @param aconf Configuration entry of the server. + * @param client The client entry that we will use and fill in. + * @returns 1 on success, 0 on failure. + */ +static int connect_server_helper(ConfigItem_link *aconf, Client *client) +{ + char *bindip; + char buf[BUFSIZE]; + + if (!aconf->connect_ip) + { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_NOIP", client, + "Connect to $client failed: no IP address to connect to", + log_data_link_block(aconf)); + return 0; /* handled upstream or shouldn't happen */ + } + + if (strchr(aconf->connect_ip, ':')) + SetIPV6(client); + + safe_strdup(client->ip, aconf->connect_ip); + + snprintf(buf, sizeof buf, "Outgoing connection: %s", get_client_name(client, TRUE)); + client->local->fd = fd_socket(IsIPV6(client) ? AF_INET6 : AF_INET, SOCK_STREAM, 0, buf); + if (client->local->fd < 0) + { + if (ERRNO == P_EMFILE) + { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_MAXCLIENTS", client, + "Connect to $client failed: no more sockets available", + log_data_link_block(aconf)); + return 0; + } + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_SOCKET", client, + "Connect to $client failed: could not create socket: $socket_error", + log_data_socket_error(-1), + log_data_link_block(aconf)); + return 0; + } + if (++OpenFiles >= maxclients) + { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_MAXCLIENTS", client, + "Connect to $client failed: no more connections available", + log_data_link_block(aconf)); + return 0; + } + + set_sockhost(client, aconf->outgoing.hostname); + + if (!aconf->outgoing.bind_ip && iConf.link_bindip) + bindip = iConf.link_bindip; + else + bindip = aconf->outgoing.bind_ip; + + if (bindip && strcmp("*", bindip)) + { + if (!unreal_bind(client->local->fd, bindip, 0, IsIPV6(client))) + { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_SOCKET_BIND", client, + "Connect to $client failed: could not bind socket to $link_block.bind_ip: $socket_error -- " + "Your link::outgoing::bind-ip is probably incorrect.", + log_data_socket_error(client->local->fd), + log_data_link_block(aconf)); + return 0; + } + } + + set_sock_opts(client->local->fd, client, IsIPV6(client)); + + if (!unreal_connect(client->local->fd, client->ip, aconf->outgoing.port, IsIPV6(client))) + { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, + "Connect to $client ($link_block.ip:$link_block.port) failed: $socket_error", + log_data_socket_error(client->local->fd), + log_data_link_block(aconf)); + return 0; + } + + return 1; +} + diff --git a/src/modules/sethost.c b/src/modules/sethost.c index 01be159..510123f 100644 --- a/src/modules/sethost.c +++ b/src/modules/sethost.c @@ -33,7 +33,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /sethost", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -61,7 +61,7 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_sethost) { - char *vhost; + const char *vhost; if (MyUser(client) && !ValidatePermissionsForPath("self:set:host",client,NULL,NULL,NULL)) { @@ -88,7 +88,7 @@ CMD_FUNC(cmd_sethost) return; } - if (!valid_host(vhost)) + if (!valid_host(vhost, 0)) { sendnotice(client, "*** /SetHost Error: A hostname may only contain a-z, A-Z, 0-9, '-' & '.'."); return; diff --git a/src/modules/setident.c b/src/modules/setident.c index 05179e3..6409ce3 100644 --- a/src/modules/setident.c +++ b/src/modules/setident.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "/setident", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -61,8 +61,7 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_setident) { - - char *vident, *s; + const char *vident, *s; if ((parc < 2) || BadPtr(parv[1])) { diff --git a/src/modules/setname.c b/src/modules/setname.c index 85ab9c3..b37106f 100644 --- a/src/modules/setname.c +++ b/src/modules/setname.c @@ -23,8 +23,13 @@ #include "unrealircd.h" CMD_FUNC(cmd_setname); +char *setname_isupport_param(void); #define MSG_SETNAME "SETNAME" /* setname */ +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +long CAP_SETNAME = 0L; ModuleHeader MOD_HEADER = { @@ -32,18 +37,32 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /setname", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() { - CommandAdd(modinfo->handle, MSG_SETNAME, cmd_setname, 1, CMD_USER); + ClientCapabilityInfo cap; + ClientCapability *c; MARK_AS_OFFICIAL_MODULE(modinfo); + + CommandAdd(modinfo->handle, MSG_SETNAME, cmd_setname, 1, CMD_USER); + + memset(&cap, 0, sizeof(cap)); + cap.name = "setname"; + c = ClientCapabilityAdd(modinfo->handle, &cap, &CAP_SETNAME); + if (!c) + { + config_error("[%s] Failed to request setname cap: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); + return MOD_FAILED; + } + return MOD_SUCCESS; } MOD_LOAD() { + ISupportAdd(modinfo->handle, "NAMELEN", setname_isupport_param()); return MOD_SUCCESS; } @@ -52,19 +71,23 @@ MOD_UNLOAD() return MOD_SUCCESS; } +char *setname_isupport_param(void){ + return STR(REALLEN); +} + /* cmd_setname - 12/05/1999 - Stskeeps * :prefix SETNAME :gecos * parv[1] - gecos * D: This will set your gecos to be (like (/setname :The lonely wanderer)) - yes it is experimental but anyways ;P - FREEDOM TO THE USERS! ;) + this is now compatible with IRCv3 SETNAME --k4be */ CMD_FUNC(cmd_setname) { int xx; - char tmpinfo[REALLEN + 1]; + char oldinfo[REALLEN + 1]; char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; ConfigItem_ban *bconf; + MessageTag *mtags = NULL; if ((parc < 2) || BadPtr(parv[1])) { @@ -74,25 +97,38 @@ CMD_FUNC(cmd_setname) if (strlen(parv[1]) > REALLEN) { - if (MyConnect(client)) + if (!MyConnect(client)) + return; + if (HasCapabilityFast(client, CAP_SETNAME)) { - sendnotice(client, "*** /SetName Error: \"Real names\" may maximum be %i characters of length", - REALLEN); + new_message(client, recv_mtags, &mtags); + sendto_one(client, mtags, ":%s FAIL SETNAME INVALID_REALNAME :\"Real names\" may maximum be %i characters of length", me.name, REALLEN); + free_message_tags(mtags); + } + else + { + sendnotice(client, "*** /SetName Error: \"Real names\" may maximum be %i characters of length", REALLEN); } return; } + strlcpy(oldinfo, client->info, sizeof(oldinfo)); + if (MyUser(client)) { - /* set temp info for spamfilter check*/ - strcpy(tmpinfo, client->info); /* set the new name before we check, but don't send to servers unless it is ok */ - strcpy(client->info, parv[1]); + strlcpy(client->info, parv[1], sizeof(client->info)); spamfilter_build_user_string(spamfilter_user, client->name, client); if (match_spamfilter(client, spamfilter_user, SPAMF_USER, "SETNAME", NULL, 0, NULL)) { /* Was rejected by spamfilter, restore the realname */ - strcpy(client->info, tmpinfo); + if (HasCapabilityFast(client, CAP_SETNAME)) + { + new_message(client, recv_mtags, &mtags); + sendto_one(client, mtags, "%s FAIL SETNAME CANNOT_CHANGE_REALNAME :Rejected by server", me.name); + free_message_tags(mtags); + } + strlcpy(client->info, oldinfo, sizeof(client->info)); return; } @@ -105,14 +141,22 @@ CMD_FUNC(cmd_setname) } } else { /* remote user */ - strcpy(client->info, parv[1]); + strlcpy(client->info, parv[1], sizeof(client->info)); } - sendto_server(client, 0, 0, NULL, ":%s SETNAME :%s", client->id, parv[1]); + new_message(client, recv_mtags, &mtags); + sendto_local_common_channels(client, client, CAP_SETNAME, mtags, ":%s SETNAME :%s", client->name, client->info); + sendto_server(client, 0, 0, mtags, ":%s SETNAME :%s", client->id, parv[1]); + /* notify the sender */ if (MyConnect(client)) { - sendnotice(client, "Your \"real name\" is now set to be %s - you have to set it manually to undo it", - parv[1]); + if (HasCapabilityFast(client, CAP_SETNAME)) + sendto_prefix_one(client, client, mtags, ":%s SETNAME :%s", client->name, client->info); + else + sendnotice(client, "Your \"real name\" is now set to be %s - you have to set it manually to undo it", parv[1]); } + free_message_tags(mtags); + + RunHook(HOOKTYPE_REALNAME_CHANGED, client, oldinfo); } diff --git a/src/modules/silence.c b/src/modules/silence.c index 6fd6bb3..e2af9c8 100644 --- a/src/modules/silence.c +++ b/src/modules/silence.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", "command /silence", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Structs */ @@ -104,7 +104,8 @@ MOD_UNLOAD() CMD_FUNC(cmd_silence) { Silence *s; - char action, *p; + const char *p; + char action; if (MyUser(client)) { @@ -121,7 +122,7 @@ CMD_FUNC(cmd_silence) { p++; } else - if (!strchr(p, '@') && !strchr(p, '.') && !strchr(p, '!') && !strchr(p, '*') && !find_person(p, NULL)) + if (!strchr(p, '@') && !strchr(p, '.') && !strchr(p, '!') && !strchr(p, '*') && !find_user(p, NULL)) { sendnumeric(client, ERR_NOSUCHNICK, parv[1]); return; diff --git a/src/modules/sinfo.c b/src/modules/sinfo.c index d41eedf..d297001 100644 --- a/src/modules/sinfo.c +++ b/src/modules/sinfo.c @@ -12,7 +12,7 @@ ModuleHeader MOD_HEADER "5.0", "Server information", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ @@ -73,36 +73,36 @@ CMD_FUNC(sinfo_server) return; } - client->serv->boottime = atol(parv[1]); - client->serv->features.protocol = atoi(parv[2]); + client->server->boottime = atol(parv[1]); + client->server->features.protocol = atoi(parv[2]); if (!strcmp(parv[3], "*")) - safe_free(client->serv->features.usermodes); + safe_free(client->server->features.usermodes); else - safe_strdup(client->serv->features.usermodes, parv[3]); + safe_strdup(client->server->features.usermodes, parv[3]); if (!strcmp(parv[4], "*")) { - safe_free(client->serv->features.chanmodes[0]); - safe_free(client->serv->features.chanmodes[1]); - safe_free(client->serv->features.chanmodes[2]); - safe_free(client->serv->features.chanmodes[3]); + safe_free(client->server->features.chanmodes[0]); + safe_free(client->server->features.chanmodes[1]); + safe_free(client->server->features.chanmodes[2]); + safe_free(client->server->features.chanmodes[3]); } else { parse_chanmodes_protoctl(client, parv[4]); } if (!strcmp(parv[5], "*")) - safe_free(client->serv->features.nickchars); + safe_free(client->server->features.nickchars); else - safe_strdup(client->serv->features.nickchars, parv[5]); + safe_strdup(client->server->features.nickchars, parv[5]); /* Software is always the last parameter. It is currently parv[6] * but may change later. So always use parv[parc-1]. */ if (!strcmp(parv[parc-1], "*")) - safe_free(client->serv->features.software); + safe_free(client->server->features.software); else - safe_strdup(client->serv->features.software, parv[parc-1]); + safe_strdup(client->server->features.software, parv[parc-1]); /* Broadcast to 'the other side' of the net */ concat_params(buf, sizeof(buf), parc, parv); @@ -124,33 +124,33 @@ CMD_FUNC(sinfo_user) { sendtxtnumeric(client, "*** Server %s:", acptr->name); sendtxtnumeric(client, "Protocol: %d", - acptr->serv->features.protocol); + acptr->server->features.protocol); sendtxtnumeric(client, "Software: %s", - SafeDisplayStr(acptr->serv->features.software)); - if (!acptr->serv->boottime) + SafeDisplayStr(acptr->server->features.software)); + if (!acptr->server->boottime) { sendtxtnumeric(client, "Up since: -"); sendtxtnumeric(client, "Uptime: -"); } else { sendtxtnumeric(client, "Up since: %s", - pretty_date(acptr->serv->boottime)); + pretty_date(acptr->server->boottime)); sendtxtnumeric(client, "Uptime: %s", - pretty_time_val(TStime() - acptr->serv->boottime)); + pretty_time_val(TStime() - acptr->server->boottime)); } sendtxtnumeric(client, "User modes: %s", - SafeDisplayStr(acptr->serv->features.usermodes)); - if (!acptr->serv->features.chanmodes[0]) + SafeDisplayStr(acptr->server->features.usermodes)); + if (!acptr->server->features.chanmodes[0]) { sendtxtnumeric(client, "Channel modes: -"); } else { sendtxtnumeric(client, "Channel modes: %s,%s,%s,%s", - SafeDisplayStr(acptr->serv->features.chanmodes[0]), - SafeDisplayStr(acptr->serv->features.chanmodes[1]), - SafeDisplayStr(acptr->serv->features.chanmodes[2]), - SafeDisplayStr(acptr->serv->features.chanmodes[3])); + SafeDisplayStr(acptr->server->features.chanmodes[0]), + SafeDisplayStr(acptr->server->features.chanmodes[1]), + SafeDisplayStr(acptr->server->features.chanmodes[2]), + SafeDisplayStr(acptr->server->features.chanmodes[3])); } sendtxtnumeric(client, "Allowed nick characters: %s", - SafeDisplayStr(acptr->serv->features.nickchars)); + SafeDisplayStr(acptr->server->features.nickchars)); } } diff --git a/src/modules/sjoin.c b/src/modules/sjoin.c index 3a8048c..509be2b 100644 --- a/src/modules/sjoin.c +++ b/src/modules/sjoin.c @@ -29,12 +29,14 @@ CMD_FUNC(cmd_sjoin); ModuleHeader MOD_HEADER = { "sjoin", - "5.0", + "5.1", "command /sjoin", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; +char modebuf[BUFSIZE], parabuf[BUFSIZE]; + MOD_INIT() { CommandAdd(modinfo->handle, MSG_SJOIN, cmd_sjoin, MAXPARA, CMD_SERVER); @@ -55,7 +57,7 @@ MOD_UNLOAD() typedef struct xParv aParv; struct xParv { int parc; - char *parv[256]; + const char *parv[256]; }; aParv pparv; @@ -78,20 +80,32 @@ aParv *mp2parv(char *xmbuf, char *parmbuf) return (&pparv); } -void send_local_chan_mode(MessageTag *recv_mtags, Client *client, Channel *channel, char *modebuf, char *parabuf) +static void send_local_chan_mode(MessageTag *recv_mtags, Client *client, Channel *channel, char *modebuf, char *parabuf) { MessageTag *mtags = NULL; + int destroy_channel = 0; - new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); + new_message_special(client, recv_mtags, &mtags, ":%s MODE %s %s %s", client->name, channel->name, modebuf, parabuf); sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, - ":%s MODE %s %s %s", client->name, channel->chname, modebuf, parabuf); + ":%s MODE %s %s %s", client->name, channel->name, modebuf, parabuf); if (MyConnect(client)) - RunHook7(HOOKTYPE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1); + RunHook(HOOKTYPE_LOCAL_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1, &destroy_channel); else - RunHook7(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1); + RunHook(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, -1, &destroy_channel); free_message_tags(mtags); } +/** Call send_local_chan_mode() for multiline modes */ +static void send_local_chan_mode_mlm(MessageTag *recv_mtags, Client *client, Channel *channel, MultiLineMode *mlm) +{ + if (mlm) + { + int i; + for (i = 0; i < mlm->numlines; i++) + send_local_chan_mode(recv_mtags, client, channel, mlm->modeline[i], mlm->paramline[i]); + } +} + /** SJOIN: Synchronize channel modes, +beI lists and users (server-to-server command) * Extensive technical documentation is available at: * https://www.unrealircd.org/docs/Server_protocol:SJOIN_command @@ -147,9 +161,10 @@ CMD_FUNC(cmd_sjoin) unsigned short merge; /**< same timestamp: merge their & our modes */ char pvar[MAXMODEPARAMS][MODEBUFLEN + 3]; char cbuf[1024]; - char nick[1024]; /**< nick or ban/invex/exempt being processed */ char scratch_buf[1024]; /**< scratch buffer */ - char prefix[16]; /**< prefix of nick for server to server traffic (eg: @) */ + char item[1024]; /**< nick or ban/invex/exempt being processed */ + char item_modes[MEMBERMODESLEN]; /**< item modes, eg "b" or "vhoaq" */ + char prefix[16]; /**< SJOIN prefix of item for server to server traffic (eg: @) */ char uid_buf[BUFSIZE]; /**< Buffer for server-to-server traffic which will be broadcasted to others (servers supporting SID/UID) */ char uid_sjsby_buf[BUFSIZE]; /**< Buffer for server-to-server traffic which will be broadcasted to others (servers supporting SID/UID and SJSBY) */ char sj3_parabuf[BUFSIZE]; /**< Prefix for the above SJOIN buffers (":xxx SJOIN #channel +mode :") */ @@ -158,11 +173,10 @@ CMD_FUNC(cmd_sjoin) aParv *ap; int pcount, i; Hook *h; + Cmode *cm; time_t ts, oldts; unsigned short b=0; char *tp, *p, *saved = NULL; - long modeflags; - char queue_s=0, queue_c=0; /* oh this is soooooo ugly :p */ if (!IsServer(client) || parc < 4) return; @@ -178,17 +192,38 @@ CMD_FUNC(cmd_sjoin) if (parc < 5) nomode = 1; - channel = get_channel(client, parv[2], CREATE); + channel = find_channel(parv[2]); + if (!channel) + { + channel = make_channel(parv[2]); + oldts = -1; + } else { + oldts = channel->creationtime; + } ts = (time_t)atol(parv[1]); + if (IsInvalidChannelTS(ts)) + { + unreal_log(ULOG_WARNING, "sjoin", "SJOIN_INVALID_TIMESTAMP", client, + "SJOIN for channel $channel has invalid timestamp $send_timestamp (from $client)", + log_data_channel("channel", channel), + log_data_integer("send_timestamp", ts)); + /* Pretend they match our creation time (matches U6 behavior in m_mode.c) */ + ts = channel->creationtime; + } + + if (oldts == -1) + { + /* Newly created channel (from our POV), so set the correct creationtime here */ + channel->creationtime = ts; + } else if (channel->creationtime > ts) { removeours = 1; - oldts = channel->creationtime; channel->creationtime = ts; } - else if ((channel->creationtime < ts) && (channel->creationtime != 0)) + else if (channel->creationtime < ts) { removetheirs = 1; } @@ -197,47 +232,29 @@ CMD_FUNC(cmd_sjoin) merge = 1; } - if (channel->creationtime == 0) - { - oldts = -1; - channel->creationtime = ts; - } - else - { - oldts = channel->creationtime; - } - - // FIXME: make it so services cannot screw this up so easily --- if possible... - if (ts < 750000) - { - if (ts != 0) - sendto_ops - ("Warning! Possible desync: SJOIN for channel %s has a fishy timestamp (%lld) [%s/%s]", - channel->chname, (long long)ts, client->name, client->direction->name); - } - parabuf[0] = '\0'; modebuf[0] = '+'; modebuf[1] = '\0'; /* Grab current modes -> modebuf & parabuf */ - channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel); + channel_modes(client, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel, 1); /* Do we need to remove all our modes, bans/exempt/inves lists and -vhoaq our users? */ if (removeours) { Member *lp; - Membership *lp2; modebuf[0] = '-'; /* remove our modes if any */ - if (modebuf[1] != '\0') + if (!empty_mode(modebuf)) { MessageTag *mtags = NULL; + MultiLineMode *mlm; ap = mp2parv(modebuf, parabuf); - set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0); - send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); + mlm = set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar); + send_local_chan_mode_mlm(recv_mtags, client, channel, mlm); + safe_free_multilinemode(mlm); } /* remove bans */ /* reset the buffers */ @@ -274,39 +291,15 @@ CMD_FUNC(cmd_sjoin) } for (lp = channel->members; lp; lp = lp->next) { - lp2 = find_membership_link(lp->client->user->channel, channel); - if (!lp2) + Membership *lp2 = find_membership_link(lp->client->user->channel, channel); + + /* Remove all our modes, one by one */ + for (p = lp->member_modes; *p; p++) { - sendto_realops("Oops! channel->members && !find_membership_link"); - continue; + Addit(*p, lp->client->name); } - if (lp->flags & MODE_CHANOWNER) - { - lp->flags &= ~MODE_CHANOWNER; - Addit('q', lp->client->name); - } - if (lp->flags & MODE_CHANADMIN) - { - lp->flags &= ~MODE_CHANADMIN; - Addit('a', lp->client->name); - } - if (lp->flags & MODE_CHANOP) - { - lp->flags &= ~MODE_CHANOP; - Addit('o', lp->client->name); - } - if (lp->flags & MODE_HALFOP) - { - lp->flags &= ~MODE_HALFOP; - Addit('h', lp->client->name); - } - if (lp->flags & MODE_VOICE) - { - lp->flags &= ~MODE_VOICE; - Addit('v', lp->client->name); - } - /* Those should always match anyways */ - lp2->flags = lp->flags; + /* And clear all the flags in memory */ + *lp->member_modes = *lp2->member_modes = '\0'; } if (b > 1) { @@ -327,11 +320,6 @@ CMD_FUNC(cmd_sjoin) sj3_parabuf[0] = '\0'; for (i = 2; i <= (parc - 2); i++) { - if (!parv[i]) - { - sendto_ops("Got null parv in SJ3 code"); - continue; - } strlcat(sj3_parabuf, parv[i], sizeof sj3_parabuf); if (((i + 1) <= (parc - 2))) strlcat(sj3_parabuf, " ", sizeof sj3_parabuf); @@ -348,7 +336,7 @@ CMD_FUNC(cmd_sjoin) time_t setat = TStime(); /**< Set at timestamp */ int sjsby_info = 0; /**< Set to 1 if we receive SJSBY info to alter the above 2 vars */ - modeflags = 0; + *item_modes = 0; i = 0; tp = s; @@ -365,8 +353,10 @@ CMD_FUNC(cmd_sjoin) if (!end) { /* this obviously should never happen */ - sendto_ops("Malformed SJOIN piece from %s for channel %s: %s", - client->name, channel->chname, tp); + unreal_log(ULOG_WARNING, "sjoin", "SJOIN_INVALID_SJSBY", client, + "SJOIN for channel $channel has invalid SJSBY in item '$item' (from $client)", + log_data_channel("channel", channel), + log_data_string("item", s)); continue; } *end++ = '\0'; @@ -375,8 +365,10 @@ CMD_FUNC(cmd_sjoin) if (!p) { /* missing setby parameter */ - sendto_ops("Malformed SJOIN piece from %s for channel %s: %s", - client->name, channel->chname, tp); + unreal_log(ULOG_WARNING, "sjoin", "SJOIN_INVALID_SJSBY", client, + "SJOIN for channel $channel has invalid SJSBY in item '$item' (from $client)", + log_data_channel("channel", channel), + log_data_string("item", s)); continue; } *p++ = '\0'; @@ -388,92 +380,46 @@ CMD_FUNC(cmd_sjoin) tp = end; /* the remainder is used for the actual ban/exempt/invex */ } - while ( - (*tp == '@') || (*tp == '+') || (*tp == '%') - || (*tp == '*') || (*tp == '~') || (*tp == '&') - || (*tp == '"') || (*tp == '\'')) + /* Process the SJOIN prefixes... */ + for (p = tp; *p; p++) { - switch (*(tp++)) + char m = sjoin_prefix_to_mode(*p); + if (!m) + break; /* end of prefix stuff, or so we hope anyway :D */ + // TODO: do we want safety here for if one side has prefixmodes loaded + // and the other does not? and if so, in what way do we want this? + + strlcat_letter(item_modes, m, sizeof(item_modes)); + + /* For list modes (+beI) stop processing immediately, + * so we don't accidentally eat additional prefix chars. + */ + if (strchr("beI", m)) { - case '@': - modeflags |= CHFL_CHANOP; - break; - case '%': - modeflags |= CHFL_HALFOP; - break; - case '+': - modeflags |= CHFL_VOICE; - break; - case '*': - modeflags |= CHFL_CHANOWNER; - break; - case '~': - modeflags |= CHFL_CHANADMIN; - break; - case '&': - modeflags = CHFL_BAN; - goto getnick; - case '"': - modeflags = CHFL_EXCEPT; - goto getnick; - case '\'': - modeflags = CHFL_INVEX; - goto getnick; + p++; + break; } } -getnick: - /* First, set the appropriate prefix for server to server traffic. - * Note that 'prefix' is a 16 byte buffer but it's safe due to the limited - * number of choices as can be seen below: + /* Now set 'prefix' to the prefixes we encountered. + * This is basically the range tp..p */ - *prefix = '\0'; - p = prefix; - if (modeflags == CHFL_INVEX) - *p++ = '\''; - else if (modeflags == CHFL_EXCEPT) - *p++ = '\"'; - else if (modeflags == CHFL_BAN) - *p++ = '&'; - else - { - /* multiple options possible at the same time */ - if (modeflags & CHFL_CHANOWNER) - *p++ = '*'; - if (modeflags & CHFL_CHANADMIN) - *p++ = '~'; - if (modeflags & CHFL_CHANOP) - *p++ = '@'; - if (modeflags & CHFL_HALFOP) - *p++ = '%'; - if (modeflags & CHFL_VOICE) - *p++ = '+'; - } - *p = '\0'; + strlncpy(prefix, tp, sizeof(prefix), p - tp); - /* Now copy the "nick" (which can actually be a ban/invex/exempt). - * There's no size checking here but nick is 1024 bytes and we - * have 512 bytes input max. - */ - i = 0; - while ((*tp != ' ') && (*tp != '\0')) - nick[i++] = *(tp++); /* get nick */ - nick[i] = '\0'; - if (nick[0] == ' ') + /* Now copy the "nick" (which can actually be a ban/invex/exempt) */ + strlcpy(item, p, sizeof(item)); + if (*item == '\0') continue; - if (nick[0] == '\0') - continue; - Debug((DEBUG_DEBUG, "Got nick: %s", nick)); - if (!(modeflags & CHFL_BAN) && !(modeflags & CHFL_EXCEPT) && !(modeflags & CHFL_INVEX)) + + /* If not a list mode... then we deal with users... */ + if (!strchr(item_modes, 'b') && !strchr(item_modes, 'e') && !strchr(item_modes, 'I')) { Client *acptr; - /* A person joining */ - /* The user may no longer exist. This can happen in case of a * SVSKILL traveling in the other direction. Nothing to worry about. */ - if (!(acptr = find_person(nick, NULL))) + if (!(acptr = find_user(item, NULL))) continue; if (acptr->direction != client->direction) @@ -486,18 +432,18 @@ getnick: sendto_one(client, NULL, ":%s KICK %s %s :Fake direction", - me.id, channel->chname, acptr->name); - sendto_realops - ("Fake direction from user %s in SJOIN from %s(%s) at %s", - nick, client->srvptr->name, - client->name, channel->chname); + me.id, channel->name, acptr->name); + unreal_log(ULOG_WARNING, "sjoin", "SJOIN_FAKE_DIRECTION", client, + "Fake direction from server $client in SJOIN " + "for user $existing_client on $existing_client.user.servername " + "(item: $buf)", + log_data_client("existing_client", acptr), + log_data_string("buf", item)); continue; } if (removetheirs) - { - modeflags = 0; - } + *item_modes = '\0'; if (!IsMember(acptr, channel)) { @@ -505,30 +451,32 @@ getnick: */ MessageTag *mtags = NULL; - add_user_to_channel(channel, acptr, modeflags); - RunHook4(HOOKTYPE_REMOTE_JOIN, acptr, channel, recv_mtags, NULL); - new_message_special(acptr, recv_mtags, &mtags, ":%s JOIN %s", acptr->name, channel->chname); + add_user_to_channel(channel, acptr, item_modes); + RunHook(HOOKTYPE_REMOTE_JOIN, acptr, channel, recv_mtags); + new_message_special(acptr, recv_mtags, &mtags, ":%s JOIN %s", acptr->name, channel->name); send_join_to_local_users(acptr, channel, mtags); free_message_tags(mtags); } - CheckStatus('q', CHFL_CHANOWNER); - CheckStatus('a', CHFL_CHANADMIN); - CheckStatus('o', CHFL_CHANOP); - CheckStatus('h', CHFL_HALFOP); - CheckStatus('v', CHFL_VOICE); + /* Set the +vhoaq */ + for (p = item_modes; *p; p++) + Addit(*p, acptr->name); if (strlen(uid_buf) + strlen(prefix) + IDLEN > BUFSIZE - 10) { /* Send what we have and start a new buffer */ sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf); - snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf); + snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); /* Double-check the new buffer is sufficient to concat the data */ if (strlen(uid_buf) + strlen(prefix) + strlen(acptr->id) > BUFSIZE - 5) { - ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s%s'", - uid_buf, prefix, acptr->id); - sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname); + unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, + "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str$str2' to '$buf'", + log_data_channel("channel", channel), + log_data_string("sjoin_place", "UID-MEMBER"), + log_data_string("str", prefix), + log_data_string("str2", acptr->id), + log_data_string("buf", uid_buf)); continue; } } @@ -537,14 +485,18 @@ getnick: if (strlen(uid_sjsby_buf) + strlen(prefix) + IDLEN > BUFSIZE - 10) { /* Send what we have and start a new buffer */ - sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_sjsby_buf); - snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf); + sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf); + snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); /* Double-check the new buffer is sufficient to concat the data */ if (strlen(uid_sjsby_buf) + strlen(prefix) + strlen(acptr->id) > BUFSIZE - 5) { - ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s%s'", - uid_sjsby_buf, prefix, acptr->id); - sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname); + unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, + "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str$str2' to '$buf'", + log_data_channel("channel", channel), + log_data_string("sjoin_place", "SJS-MEMBER"), + log_data_string("str", prefix), + log_data_string("str2", acptr->id), + log_data_string("buf", uid_sjsby_buf)); continue; } } @@ -552,79 +504,86 @@ getnick: } else { + /* It's a list mode................ */ + const char *str; + if (removetheirs) continue; - /* For list modes (beI): validate the syntax */ - if (modeflags & (CHFL_BAN|CHFL_EXCEPT|CHFL_INVEX)) - { - char *str; - - /* non-extbans: prevent bans without ! or @. a good case of "should never happen". */ - if ((nick[0] != '~') && (!strchr(nick, '!') || !strchr(nick, '@') || (nick[0] == '!'))) - continue; - - str = clean_ban_mask(nick, MODE_ADD, client); - if (!str) - continue; /* invalid ban syntax */ - strlcpy(nick, str, sizeof(nick)); - } + /* Validate syntax */ + + /* non-extbans: prevent bans without ! or @. a good case of "should never happen". */ + if ((item[0] != '~') && (!strchr(item, '!') || !strchr(item, '@') || (item[0] == '!'))) + continue; + + str = clean_ban_mask(item, MODE_ADD, client, 0); + if (!str) + continue; /* invalid ban syntax */ + strlcpy(item, str, sizeof(item)); /* Adding of list modes */ - if (modeflags & CHFL_BAN) + if (*item_modes == 'b') { - if (add_listmode_ex(&channel->banlist, client, channel, nick, setby, setat) != -1) + if (add_listmode_ex(&channel->banlist, client, channel, item, setby, setat) != -1) { - Addit('b', nick); + Addit('b', item); } } - if (modeflags & CHFL_EXCEPT) + if (*item_modes == 'e') { - if (add_listmode_ex(&channel->exlist, client, channel, nick, setby, setat) != -1) + if (add_listmode_ex(&channel->exlist, client, channel, item, setby, setat) != -1) { - Addit('e', nick); + Addit('e', item); } } - if (modeflags & CHFL_INVEX) + if (*item_modes == 'I') { - if (add_listmode_ex(&channel->invexlist, client, channel, nick, setby, setat) != -1) + if (add_listmode_ex(&channel->invexlist, client, channel, item, setby, setat) != -1) { - Addit('I', nick); + Addit('I', item); } } - if (strlen(uid_buf) + strlen(prefix) + strlen(nick) > BUFSIZE - 10) + if (strlen(uid_buf) + strlen(prefix) + strlen(item) > BUFSIZE - 10) { /* Send what we have and start a new buffer */ sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf); - snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf); + snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); /* Double-check the new buffer is sufficient to concat the data */ - if (strlen(uid_buf) + strlen(prefix) + strlen(nick) > BUFSIZE - 5) + if (strlen(uid_buf) + strlen(prefix) + strlen(item) > BUFSIZE - 5) { - ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s%s'", - uid_buf, prefix, nick); - sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname); + unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, + "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str$str2' to '$buf'", + log_data_channel("channel", channel), + log_data_string("sjoin_place", "UID-LMODE"), + log_data_string("str", prefix), + log_data_string("str2", item), + log_data_string("buf", uid_buf)); continue; } } - sprintf(uid_buf+strlen(uid_buf), "%s%s ", prefix, nick); + sprintf(uid_buf+strlen(uid_buf), "%s%s ", prefix, item); *scratch_buf = '\0'; if (sjsby_info) add_sjsby(scratch_buf, setby, setat); strcat(scratch_buf, prefix); - strcat(scratch_buf, nick); + strcat(scratch_buf, item); strcat(scratch_buf, " "); if (strlen(uid_sjsby_buf) + strlen(scratch_buf) > BUFSIZE - 10) { /* Send what we have and start a new buffer */ sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf); - snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, sj3_parabuf); + snprintf(uid_sjsby_buf, sizeof(uid_sjsby_buf), ":%s SJOIN %lld %s :", client->id, (long long)ts, channel->name); /* Double-check the new buffer is sufficient to concat the data */ if (strlen(uid_sjsby_buf) + strlen(scratch_buf) > BUFSIZE - 5) { - ircd_log(LOG_ERROR, "Oversized SJOIN: '%s' + '%s'", uid_sjsby_buf, scratch_buf); - sendto_realops("Oversized SJOIN for %s -- see ircd log", channel->chname); + unreal_log(ULOG_ERROR, "sjoin", "BUG_OVERSIZED_SJOIN", client, + "Oversized SJOIN [$sjoin_place] in channel $channel when adding '$str' to '$buf'", + log_data_channel("channel", channel), + log_data_string("sjoin_place", "SJS-LMODE"), + log_data_string("str", scratch_buf), + log_data_string("buf", uid_sjsby_buf)); continue; } } @@ -634,11 +593,10 @@ getnick: } /* Send out any possible remainder.. */ - Debug((DEBUG_DEBUG, "Sending '%li %s :%s' to ", ts, parabuf, parv[parc - 1])); sendto_server(client, 0, PROTO_SJSBY, recv_mtags, "%s", uid_buf); sendto_server(client, PROTO_SJSBY, 0, recv_mtags, "%s", uid_sjsby_buf); - if (modebuf[1]) + if (!empty_mode(modebuf)) { modebuf[b] = '\0'; send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); @@ -646,8 +604,8 @@ getnick: if (!merge && !removetheirs && !nomode) { - char paraback[1024]; MessageTag *mtags = NULL; + MultiLineMode *mlm; strlcpy(modebuf, parv[3], sizeof modebuf); parabuf[0] = '\0'; @@ -659,22 +617,22 @@ getnick: strlcat(parabuf, " ", sizeof parabuf); } } - strlcpy(paraback, parabuf, sizeof paraback); ap = mp2parv(modebuf, parabuf); - - set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0); - send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); + mlm = set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar); + send_local_chan_mode_mlm(recv_mtags, client, channel, mlm); + safe_free_multilinemode(mlm); } if (merge && !nomode) { CoreChannelModeTable *acp; + MultiLineMode *mlm; Mode oldmode; /**< The old mode (OUR mode) */ /* Copy current mode to oldmode (need to duplicate all extended mode params too..) */ memcpy(&oldmode, &channel->mode, sizeof(oldmode)); - memset(&oldmode.extmodeparams, 0, sizeof(oldmode.extmodeparams)); - extcmode_duplicate_paramlist(channel->mode.extmodeparams, oldmode.extmodeparams); + memset(&oldmode.mode_params, 0, sizeof(oldmode.mode_params)); + extcmode_duplicate_paramlist(channel->mode.mode_params, oldmode.mode_params); /* Now merge the modes */ strlcpy(modebuf, parv[3], sizeof modebuf); @@ -687,8 +645,15 @@ getnick: strlcat(parabuf, " ", sizeof parabuf); } } + + /* First we set the mode (in memory) BUT we don't send the + * mode change out to anyone, hence the immediate freeing + * of 'mlm'. We do the actual rebuilding of the string and + * sending it out a few lines further down. + */ ap = mp2parv(modebuf, parabuf); - set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar, 0); + mlm = set_mode(channel, client, ap->parc, ap->parv, &pcount, pvar); + safe_free_multilinemode(mlm); /* Good, now we got modes, now for the differencing and outputting of modes * We first see if any para modes are set. @@ -696,53 +661,40 @@ getnick: strlcpy(modebuf, "-", sizeof modebuf); parabuf[0] = '\0'; b = 1; - /* however, is this really going to happen at all? may be unneeded */ - if (oldmode.limit && !channel->mode.limit) - { - Addsingle('l'); - } - if (oldmode.key[0] && !channel->mode.key[0]) - { - Addit('k', oldmode.key); - } - /* First, check if we have something they don't have.. - * note that: oldmode.* = us, channel->mode.* = them. - */ - for (i=0; i <= Channelmode_highest; i++) + /* Check if we had +s and it became +p, then revert it silently (as it is no-change) */ + if (has_channel_mode_raw(oldmode.mode, 's') && has_channel_mode(channel, 'p')) { - if ((Channelmode_Table[i].flag) && - (oldmode.extmode & Channelmode_Table[i].mode) && - !(channel->mode.extmode & Channelmode_Table[i].mode)) + /* stay +s ! */ + long mode_p = get_extmode_bitbychar('p'); + long mode_s = get_extmode_bitbychar('s'); + channel->mode.mode &= ~mode_p; + channel->mode.mode |= mode_s; + /* TODO: all the code of above would ideally be in a module */ + } + /* (And the other condition, +p to +s, is already handled below by the generic code) */ + + /* First, check if we had something that is now gone + * note that: oldmode.* = us, channel->mode.* = merged. + */ + for (cm=channelmodes; cm; cm = cm->next) + { + if (cm->letter && + !cm->local && + (oldmode.mode & cm->mode) && + !(channel->mode.mode & cm->mode)) { - if (Channelmode_Table[i].paracount) + if (cm->paracount) { - char *parax = cm_getparameter_ex(oldmode.extmodeparams, Channelmode_Table[i].flag); - //char *parax = Channelmode_Table[i].get_param(extcmode_get_struct(oldmode.extmodeparam, Channelmode_Table[i].flag)); - Addit(Channelmode_Table[i].flag, parax); + const char *parax = cm_getparameter_ex(oldmode.mode_params, cm->letter); + //char *parax = cm->get_param(extcmode_get_struct(oldmode.modeparam, cm->letter)); + Addit(cm->letter, parax); } else { - Addsingle(Channelmode_Table[i].flag); + Addsingle(cm->letter); } } } - /* Check if we had +s and it became +p, then revert it... */ - if ((oldmode.mode & MODE_SECRET) && (channel->mode.mode & MODE_PRIVATE)) - { - /* stay +s ! */ - channel->mode.mode &= ~MODE_PRIVATE; - channel->mode.mode |= MODE_SECRET; - Addsingle('p'); /* - */ - queue_s = 1; - } - /* Add single char modes... */ - for (acp = corechannelmodetable; acp->mode; acp++) - { - if ((oldmode.mode & acp->mode) && !(channel->mode.mode & acp->mode) && !acp->parameters) - { - Addsingle(acp->flag); - } - } if (b > 1) { Addsingle('+'); @@ -753,111 +705,74 @@ getnick: b = 1; } - if (queue_s) - Addsingle('s'); - - if (queue_c) - Addsingle('c'); - - for (acp = corechannelmodetable; acp->mode; acp++) - { - if (!(oldmode.mode & acp->mode) && (channel->mode.mode & acp->mode) && !acp->parameters) - { - Addsingle(acp->flag); - } - } - - /* Now, check if they have something we don't have.. - * note that: oldmode.* = us, channel->mode.* = them. + /* Now, check if merged modes contain something we didn't have before. + * note that: oldmode.* = us before, channel->mode.* = merged. + * + * First the simple single letter modes... */ - for (i=0; i <= Channelmode_highest; i++) + for (cm=channelmodes; cm; cm = cm->next) { - if ((Channelmode_Table[i].flag) && - !(oldmode.extmode & Channelmode_Table[i].mode) && - (channel->mode.extmode & Channelmode_Table[i].mode)) + if ((cm->letter) && + !(oldmode.mode & cm->mode) && + (channel->mode.mode & cm->mode)) { - if (Channelmode_Table[i].paracount) + if (cm->paracount) { - char *parax = cm_getparameter(channel, Channelmode_Table[i].flag); + const char *parax = cm_getparameter(channel, cm->letter); if (parax) { - Addit(Channelmode_Table[i].flag, parax); + Addit(cm->letter, parax); } } else { - Addsingle(Channelmode_Table[i].flag); + Addsingle(cm->letter); } } } /* now, if we had diffent para modes - this loop really could be done better, but */ - /* +l (limit) difference? */ - if (oldmode.limit && channel->mode.limit && (oldmode.limit != channel->mode.limit)) - { - channel->mode.limit = MAX(oldmode.limit, channel->mode.limit); - if (oldmode.limit != channel->mode.limit) - { - Addit('l', my_itoa(channel->mode.limit)); - } - } - - /* +k (key) difference? */ - if (oldmode.key[0] && channel->mode.key[0] && strcmp(oldmode.key, channel->mode.key)) - { - if (strcmp(oldmode.key, channel->mode.key) > 0) - { - strlcpy(channel->mode.key, oldmode.key, sizeof channel->mode.key); - } - else - { - Addit('k', channel->mode.key); - } - } - /* Now, check for any param differences in extended channel modes.. - * note that: oldmode.* = us, channel->mode.* = them. + * note that: oldmode.* = us before, channel->mode.* = merged. * if we win: copy oldmode to channel mode, if they win: send the mode */ - for (i=0; i <= Channelmode_highest; i++) + for (cm=channelmodes; cm; cm = cm->next) { - if (Channelmode_Table[i].flag && Channelmode_Table[i].paracount && - (oldmode.extmode & Channelmode_Table[i].mode) && - (channel->mode.extmode & Channelmode_Table[i].mode)) + if (cm->letter && cm->paracount && + (oldmode.mode & cm->mode) && + (channel->mode.mode & cm->mode)) { int r; - char *parax; - char flag = Channelmode_Table[i].flag; - void *ourm = GETPARASTRUCTEX(oldmode.extmodeparams, flag); + const char *parax; + char flag = cm->letter; + void *ourm = GETPARASTRUCTEX(oldmode.mode_params, flag); void *theirm = GETPARASTRUCT(channel, flag); - //CmodeParam *ourm = extcmode_get_struct(oldmode.extmodeparam,Channelmode_Table[i].flag); - //CmodeParam *theirm = extcmode_get_struct(channel->mode.extmodeparam, Channelmode_Table[i].flag); - r = Channelmode_Table[i].sjoin_check(channel, ourm, theirm); + r = cm->sjoin_check(channel, ourm, theirm); switch (r) { case EXSJ_WEWON: - parax = cm_getparameter_ex(oldmode.extmodeparams, flag); /* grab from old */ + parax = cm_getparameter_ex(oldmode.mode_params, flag); /* grab from old */ cm_putparameter(channel, flag, parax); /* put in new (won) */ break; case EXSJ_THEYWON: parax = cm_getparameter(channel, flag); - Debug((DEBUG_DEBUG, "sjoin: they won: '%s'", parax)); - Addit(Channelmode_Table[i].flag, parax); + Addit(cm->letter, parax); break; case EXSJ_SAME: - Debug((DEBUG_DEBUG, "sjoin: equal")); break; case EXSJ_MERGE: - parax = cm_getparameter_ex(oldmode.extmodeparams, flag); /* grab from old */ + parax = cm_getparameter_ex(oldmode.mode_params, flag); /* grab from old */ cm_putparameter(channel, flag, parax); /* put in new (won) */ Addit(flag, parax); break; default: - ircd_log(LOG_ERROR, "channel.c:m_sjoin:param diff checker: got unk. retval 0x%x??", r); + unreal_log(ULOG_ERROR, "sjoin", "BUG_SJOIN_CHECK", client, + "[BUG] channel.c:m_sjoin:param diff checker: unknown return value $return_value", + log_data_integer("return_value", r)); break; } } @@ -865,14 +780,11 @@ getnick: Addsingle('\0'); - if (modebuf[1]) - { + if (!empty_mode(modebuf)) send_local_chan_mode(recv_mtags, client, channel, modebuf, parabuf); - } /* free the oldmode.* crap :( */ - extcmode_free_paramlist(oldmode.extmodeparams); - /* memset(&oldmode.extmodeparams, 0, sizeof(oldmode.extmodeparams)); -- redundant? */ + extcmode_free_paramlist(oldmode.mode_params); } for (h = Hooks[HOOKTYPE_CHANNEL_SYNCED]; h; h = h->next) @@ -885,13 +797,12 @@ getnick: /* we should be synced by now, */ if ((oldts != -1) && (oldts != channel->creationtime)) { - MessageTag *mtags = NULL; - new_message(client, NULL, &mtags); - sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, NULL, - ":%s NOTICE %s :*** TS for %s changed from %lld to %lld", - me.name, channel->chname, channel->chname, - (long long)oldts, (long long)channel->creationtime); - free_message_tags(mtags); + unreal_log(ULOG_INFO, "channel", "CHANNEL_SYNC_TS_CHANGE", client, + "Channel $channel: timestamp changed from $old_ts -> $new_ts " + "after syncing with server $client.", + log_data_channel("channel", channel), + log_data_integer("old_ts", oldts), + log_data_integer("new_ts", channel->creationtime)); } /* If something went wrong with processing of the SJOIN above and diff --git a/src/modules/slog.c b/src/modules/slog.c new file mode 100644 index 0000000..63a8d82 --- /dev/null +++ b/src/modules/slog.c @@ -0,0 +1,190 @@ +/* + * IRC - Internet Relay Chat, src/modules/monitor.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "slog", + "5.0", + "S2S logging", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +CMD_FUNC(cmd_slog); +void _do_unreal_log_remote_deliver(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized); + +MOD_TEST() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + EfunctionAddVoid(modinfo->handle, EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER, _do_unreal_log_remote_deliver); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + + CommandAdd(modinfo->handle, "SLOG", cmd_slog, MAXPARA, CMD_SERVER); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +/** Server to server logging command. + * This way remote servers can send a log message to all servers. + * The message is broadcasted on to the rest of the network. + * Syntax: + * parv[1]: loglevel (eg "info") + * parv[2]: subsystem (eg: "link") + * parv[3]: event ID (eg: "LINK_DENIED_AUTH_FAILED") + * parv[4]: log message (only the first line!) + * We also require the "unrealircd.org/json-log" message tag to be present + * and to contain a valid UnrealIRCd JSON log. + * In fact, for sending the log message to disk and everything, we ignore + * the message in parv[4] and use the "msg" in the JSON itself. + * This because the "msg" in the JSON can be multi-line (can contain \n's) + * while the message in parv[4] will only be the first line. + * + * Why not skip parv[4] altogether and not send it all? + * I think it is still useful to send these, both for easy watching + * at server to server traffic, and also so (services) servers don't have + * to implement a full JSON parser. + */ +CMD_FUNC(cmd_slog) +{ + LogLevel loglevel; + const char *subsystem; + const char *event_id; + const char *msg; + const char *msg_in_json; + char *json_incoming = NULL; + char *json_serialized = NULL; + MessageTag *m; + MultiLine *mmsg = NULL; + json_t *j, *jt; + json_error_t jerr; + const char *original_timestamp; + + if ((parc < 4) || BadPtr(parv[4])) + { + sendnumeric(client, ERR_NEEDMOREPARAMS, "SLOG"); + return; + } + + loglevel = log_level_stringtoval(parv[1]); + if (loglevel == ULOG_INVALID) + return; + subsystem = parv[2]; + if (!valid_subsystem(subsystem)) + return; + event_id = parv[3]; + if (!valid_event_id(event_id)) + return; + msg = parv[4]; + + m = find_mtag(recv_mtags, "unrealircd.org/json-log"); + if (m) + json_incoming = m->value; + + if (!json_incoming) + return; + // Was previously: unreal_log_raw(loglevel, subsystem, event_id, NULL, msg); // WRONG: this may re-broadcast too, so twice, including back to direction!!! + + /* Validate the JSON */ + j = json_loads(json_incoming, JSON_REJECT_DUPLICATES, &jerr); + if (!j) + { + unreal_log(ULOG_INFO, "log", "REMOTE_LOG_INVALID", client, + "Received malformed JSON in server-to-server log message (SLOG) from $client", + log_data_string("bad_json_serialized", json_incoming)); + return; + } + + jt = json_object_get(j, "msg"); + if (!jt || !(msg_in_json = json_string_value(jt))) + { + unreal_log(ULOG_INFO, "log", "REMOTE_LOG_INVALID", client, + "Missing 'msg' in JSON in server-to-server log message (SLOG) from $client", + log_data_string("bad_json_serialized", json_incoming)); + json_decref(j); + return; + } + mmsg = line2multiline(msg_in_json); + + /* Set "timestamp", and save the original one in "original_timestamp" (if it existed) */ + jt = json_object_get(j, "timestamp"); + if (jt) + { + original_timestamp = json_string_value(jt); + if (original_timestamp) + json_object_set_new(j, "original_timestamp", json_string(original_timestamp)); + } + json_object_set_new(j, "timestamp", json_string(timestamp_iso8601_now())); + json_object_set_new(j, "log_source", json_string(client->name)); + + /* Re-serialize the result */ + json_serialized = json_dumps(j, JSON_COMPACT); + + if (json_serialized) + do_unreal_log_internal_from_remote(loglevel, subsystem, event_id, mmsg, json_serialized, client); + + /* Broadcast to the other servers */ + sendto_server(client, 0, 0, recv_mtags, ":%s SLOG %s %s %s :%s", + client->id, + parv[1], parv[2], parv[3], parv[4]); + + /* Free everything */ + safe_free(json_serialized); + json_decref(j); + safe_free_multiline(mmsg); +} + +void _do_unreal_log_remote_deliver(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, const char *json_serialized) +{ + MessageTag *mtags = safe_alloc(sizeof(MessageTag)); + + safe_strdup(mtags->name, "unrealircd.org/json-log"); + safe_strdup(mtags->value, json_serialized); + + /* Note that we only send the first line (msg->line), + * even for a multi-line event. + * If the recipient really wants to see everything then + * they can use the JSON data. + */ + sendto_server(NULL, 0, 0, mtags, ":%s SLOG %s %s %s :%s", + me.id, + log_level_valtostring(loglevel), subsystem, event_id, msg->line); + + free_message_tags(mtags); +} diff --git a/src/modules/snomasks/Makefile.in b/src/modules/snomasks/Makefile.in deleted file mode 100644 index d5bb70f..0000000 --- a/src/modules/snomasks/Makefile.in +++ /dev/null @@ -1,55 +0,0 @@ -#************************************************************************ -#* IRC - Internet Relay Chat, src/modules/snomasks/Makefile -#* Copyright (C) The UnrealIRCd team -#* -#* 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. -#*/ - -CC = "==== DO NOT RUN MAKE FROM THIS DIRECTORY ====" - -INCLUDES = ../../include/channel.h \ - ../../include/common.h ../../include/config.h ../../include/dbuf.h \ - ../../include/dynconf.h ../../include/fdlist.h ../../include/h.h \ - ../../include/ircsprintf.h \ - ../../include/license.h \ - ../../include/modules.h ../../include/modversion.h ../../include/msg.h \ - ../../include/numeric.h ../../include/proto.h ../../include/dns.h \ - ../../include/resource.h ../../include/setup.h \ - ../../include/struct.h ../../include/sys.h \ - ../../include/types.h ../../include/url.h \ - ../../include/version.h ../../include/whowas.h - -R_MODULES=\ - dccreject.so - -MODULES=$(R_MODULES) -MODULEFLAGS=@MODULEFLAGS@ -RM=@RM@ - -all: build - -build: $(MODULES) - -clean: - $(RM) -f *.o *.so *~ core - -############################################################################# -# .so's section -############################################################################# - -dccreject.so: dccreject.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o dccreject.so dccreject.c - diff --git a/src/modules/sqline.c b/src/modules/sqline.c index 6955c2d..a3a0bd9 100644 --- a/src/modules/sqline.c +++ b/src/modules/sqline.c @@ -33,7 +33,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /sqline", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -64,8 +64,8 @@ MOD_UNLOAD() CMD_FUNC(cmd_sqline) { char mo[32]; - char *comment = (parc == 3) ? parv[2] : NULL; - char *tkllayer[9] = { + const char *comment = (parc == 3) ? parv[2] : NULL; + const char *tkllayer[9] = { me.name, /*0 server.name */ "+", /*1 +|- */ "Q", /*2 G */ diff --git a/src/modules/squit.c b/src/modules/squit.c index 28325d3..e17ac49 100644 --- a/src/modules/squit.c +++ b/src/modules/squit.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /squit", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -59,9 +59,9 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_squit) { - char *server; + const char *server; Client *target; - char *comment = (parc > 2 && parv[parc - 1]) ? parv[parc - 1] : client->name; + const char *comment = (parc > 2 && parv[parc - 1]) ? parv[parc - 1] : client->name; // FIXME: this function is way too confusing, and full of old shit? @@ -130,8 +130,10 @@ CMD_FUNC(cmd_squit) */ if (MyConnect(target) && !MyUser(client)) { - sendto_umode_global(UMODE_OPER, "Received SQUIT %s from %s (%s)", - target->name, get_client_name(client, FALSE), comment); + unreal_log(ULOG_INFO, "link", "SQUIT", client, + "SQUIT: Forced server disconnect of $target by $client ($reason)", + log_data_client("target", target), + log_data_string("reason", comment)); } else if (MyConnect(target)) { @@ -141,8 +143,10 @@ CMD_FUNC(cmd_squit) me.name); return; } - sendto_umode_global(UMODE_OPER, "Received SQUIT %s from %s (%s)", - target->name, get_client_name(client, FALSE), comment); + unreal_log(ULOG_INFO, "link", "SQUIT", client, + "SQUIT: Forced server disconnect of $target by $client ($reason)", + log_data_client("target", target), + log_data_string("reason", comment)); } exit_client_ex(target, client->direction, recv_mtags, comment); diff --git a/src/modules/staff.c b/src/modules/staff.c index e9462be..af8bc81 100644 --- a/src/modules/staff.c +++ b/src/modules/staff.c @@ -26,18 +26,13 @@ ModuleHeader MOD_HEADER "3.8", "/STAFF command", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #define MSG_STAFF "STAFF" #define DEF_STAFF_FILE CONFDIR "/network.staff" -#define CONF_STAFF_FILE (staff_file ? staff_file : DEF_STAFF_FILE) -#ifdef USE_LIBCURL -#define STAFF_FILE (Download.path ? Download.path : CONF_STAFF_FILE) -#else -#define STAFF_FILE CONF_STAFF_FILE -#endif +#define STAFF_FILE (staff_file ? staff_file : DEF_STAFF_FILE) #define RPL_STAFF ":%s 700 %s :- %s" #define RPL_STAFFSTART ":%s 701 %s :- %s IRC Network Staff Information -" @@ -47,31 +42,13 @@ ModuleHeader MOD_HEADER /* Forward declarations */ static void unload_motd_file(MOTDFile *list); CMD_FUNC(cmd_staff); -static int cb_rehashflag(Client *client, char *flag); static int cb_test(ConfigFile *, ConfigEntry *, int, int *); static int cb_conf(ConfigFile *, ConfigEntry *, int); -static int cb_rehash(); -static int cb_stats(Client *client, char *flag); -#ifdef USE_LIBCURL -static int download_staff_file(ConfigEntry *ce); -static void download_staff_file_complete(char *url, char *file, char *errorbuf, int cached, void *dummy); -#endif -static void InitConf(); +static int cb_stats(Client *client, const char *flag); static void FreeConf(); static MOTDFile staff; -static char *staff_file; - -#ifdef USE_LIBCURL -struct { - unsigned is_url : 1; - unsigned once_completed : 1; - unsigned in_progress : 1; - char *file; // File name - char *path; // File path - char *url; // Full URL address -} Download; -#endif +static char *staff_file = NULL; MOD_TEST() { @@ -82,17 +59,10 @@ MOD_TEST() MOD_INIT() { MARK_AS_OFFICIAL_MODULE(modinfo); -#ifdef USE_LIBCURL - memset(&Download, 0, sizeof(Download)); - ModuleSetOptions(modinfo->handle, MOD_OPT_PERM, 1); -#endif memset(&staff, 0, sizeof(staff)); - InitConf(); CommandAdd(modinfo->handle, MSG_STAFF, cmd_staff, MAXPARA, CMD_USER); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, cb_conf); - HookAdd(modinfo->handle, HOOKTYPE_REHASH, 0, cb_rehash); - HookAdd(modinfo->handle, HOOKTYPE_REHASHFLAG, 0, cb_rehashflag); HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, cb_stats); return MOD_SUCCESS; @@ -108,140 +78,14 @@ MOD_UNLOAD() FreeConf(); unload_motd_file(&staff); -#ifdef USE_LIBCURL - safe_free(Download.path); - safe_free(Download.file); - safe_free(Download.url); -#endif - return MOD_SUCCESS; } -static int cb_rehash() -{ - FreeConf(); - InitConf(); - return 1; -} - -static void InitConf() -{ - staff_file = NULL; -} - static void FreeConf() { safe_free(staff_file); } -/*** web routines */ -#ifdef USE_LIBCURL -static void remove_staff_file() -{ - if (Download.path) - { - if (remove(Download.path) == -1) - { - if (config_verbose > 0) - config_status("Cannot remove file %s: %s", - Download.path, strerror(errno)); - } - safe_free(Download.path); - Download.path = NULL; - } -} - -static int download_staff_file(ConfigEntry *ce) -{ - int ret = 0; - struct stat sb; - char *file, *filename; - - if (Download.in_progress) - return 0; - - Download.is_url = 1; - safe_strdup(Download.url, ce->ce_vardata); - - file = url_getfilename(ce->ce_vardata); - filename = unreal_getfilename(file); - /* TODO: handle NULL returns */ - safe_strdup(Download.file, filename); - safe_free(file); - - if (!loop.ircd_rehashing && !Download.once_completed) - { - char *error; - - if (config_verbose > 0) - config_status("Downloading %s", displayurl(Download.url)); - - if (!(file = download_file(ce->ce_vardata, &error))) - { - config_error("%s:%i: test: error downloading '%s': %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - displayurl(ce->ce_vardata), error); - return -1; - } - - Download.once_completed = 1; - safe_strdup(Download.path, file); - read_motd(Download.path, &staff); - - safe_free(file); - return 0; - } - - file = Download.path ? Download.path : Download.file; - - if ((ret = stat(file, &sb)) && errno != ENOENT) - { - /* I know, stat shouldn't fail... */ - config_error("%s:%i: could not get the creation time of %s: stat() returned %d: %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - Download.file, ret, strerror(errno)); - return -1; - } - - if (config_verbose > 0) - config_status("Downloading %s", displayurl(Download.url)); - - Download.in_progress = 1; - download_file_async(Download.url, sb.st_ctime, download_staff_file_complete, NULL); - return 0; -} - -static void download_staff_file_complete(char *url, char *file, char *errorbuf, int cached, void *dummy) -{ - Download.in_progress = 0; - Download.once_completed = 1; - - if (!cached) - { - if (!file) - { - config_error("Error downloading %s: %s", - displayurl(url), errorbuf); - return; - } - - remove_staff_file(); - safe_strdup(Download.path, file); - read_motd(Download.path, &staff); - } else - { - char *urlfile = url_getfilename(url); - char *file = unreal_getfilename(urlfile); - char *tmp = unreal_mktemp("tmp", file); - /* TODO: handle null returns ? */ - unreal_copyfile(Download.path, tmp); - remove_staff_file(); - safe_strdup(Download.path, tmp); - safe_free(urlfile); - } -} -#endif - static void unload_motd_file(MOTDFile *list) { MOTDLine *old, *new; @@ -266,28 +110,11 @@ static void unload_motd_file(MOTDFile *list) static int cb_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { int errors = 0; -#ifdef USE_LIBCURL - char *file = NULL, *filename = NULL; -#endif if (type == CONFIG_SET) { - if (!strcmp(ce->ce_varname, "staff-file")) + if (!strcmp(ce->name, "staff-file")) { -#ifdef USE_LIBCURL - if (url_is_valid(ce->ce_vardata)) - { - /* TODO: hm, relax this one? */ - if (!(file = url_getfilename(ce->ce_vardata)) || !(filename = unreal_getfilename(file))) - { - config_error("%s:%i: invalid filename in URL", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - errors++; - } - safe_free(file); - } -#endif - *errs = errors; return errors ? -1 : 1; } @@ -300,26 +127,10 @@ static int cb_conf(ConfigFile *cf, ConfigEntry *ce, int type) { if (type == CONFIG_SET) { - if (!strcmp(ce->ce_varname, "staff-file")) + if (!strcmp(ce->name, "staff-file")) { -#ifdef USE_LIBCURL - if (!Download.in_progress) - { - safe_strdup(staff_file, ce->ce_vardata); - if (url_is_valid(ce->ce_vardata)) - { - download_staff_file(ce); - } - else -#endif - { - convert_to_absolute_path(&ce->ce_vardata, CONFDIR); - read_motd(ce->ce_vardata, &staff); - } -#ifdef USE_LIBCURL - } - -#endif + convert_to_absolute_path(&ce->value, CONFDIR); + read_motd(ce->value, &staff); return 1; } } @@ -327,7 +138,7 @@ static int cb_conf(ConfigFile *cf, ConfigEntry *ce, int type) return 0; } -static int cb_stats(Client *client, char *flag) +static int cb_stats(Client *client, const char *flag) { if (*flag == 'S') { @@ -338,28 +149,6 @@ static int cb_stats(Client *client, char *flag) return 0; } -static int cb_rehashflag(Client *client, char *flag) -{ - int myflag = 0; - - /* "-all" only keeps compatibility with beta19 */ - if (match_simple("-all", flag) || (myflag = match_simple("-staff", flag))) - { - if (myflag) - sendto_ops("%sRehashing network staff file on the request of %s", - MyUser(client) ? "Remotely " : "", client->name); - -#ifdef USE_LIBCURL - if (Download.is_url) - read_motd(Download.path, &staff); - else -#endif - read_motd(CONF_STAFF_FILE, &staff); - } - - return 0; -} - /** The routine that actual does the /STAFF command */ CMD_FUNC(cmd_staff) { @@ -369,7 +158,7 @@ CMD_FUNC(cmd_staff) if (!IsUser(client)) return; - if (hunt_server(client, recv_mtags, ":%s STAFF", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "STAFF", 1, parc, parv) != HUNTED_ISME) return; if (!staff.lines) @@ -378,7 +167,7 @@ CMD_FUNC(cmd_staff) return; } - sendto_one(client, NULL, RPL_STAFFSTART, me.name, client->name, ircnetwork); + sendto_one(client, NULL, RPL_STAFFSTART, me.name, client->name, NETWORK_NAME); temp = &staff; diff --git a/src/modules/starttls.c b/src/modules/starttls.c index 03dc883..bdaee2f 100644 --- a/src/modules/starttls.c +++ b/src/modules/starttls.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /starttls", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; long CLICAP_STARTTLS; @@ -71,7 +71,7 @@ CMD_FUNC(cmd_starttls) ctx = client->local->listener->ssl_ctx ? client->local->listener->ssl_ctx : ctx_server; tls_options = client->local->listener->tls_options ? client->local->listener->tls_options->options : iConf.tls_options->options; - /* Is SSL support enabled? (may not, if failed to load cert/keys/..) */ + /* This should never happen? */ if (!ctx) { /* Pretend STARTTLS is an unknown command, this is the safest approach */ @@ -97,14 +97,13 @@ CMD_FUNC(cmd_starttls) send_queued(client); SetStartTLSHandshake(client); - Debug((DEBUG_DEBUG, "Starting SSL handshake (due to STARTTLS) for %s", client->local->sockhost)); if ((client->local->ssl = SSL_new(ctx)) == NULL) goto fail; SetTLS(client); SSL_set_fd(client->local->ssl, client->local->fd); SSL_set_nonblocking(client->local->ssl); - if (!ircd_SSL_accept(client, client->local->fd)) { - Debug((DEBUG_DEBUG, "Failed SSL accept handshake in instance 1: %s", client->local->sockhost)); + if (!unreal_tls_accept(client, client->local->fd)) + { SSL_set_shutdown(client->local->ssl, SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(client->local->ssl); SSL_free(client->local->ssl); diff --git a/src/modules/stats.c b/src/modules/stats.c index fcb84e2..513cfda 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /stats", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -53,39 +53,38 @@ MOD_UNLOAD() } extern MODVAR int max_connection_count; -extern char *get_client_name2(Client *, int); -int stats_banversion(Client *, char *); -int stats_links(Client *, char *); -int stats_denylinkall(Client *, char *); -int stats_gline(Client *, char *); -int stats_except(Client *, char *); -int stats_allow(Client *, char *); -int stats_command(Client *, char *); -int stats_oper(Client *, char *); -int stats_port(Client *, char *); -int stats_bannick(Client *, char *); -int stats_traffic(Client *, char *); -int stats_uline(Client *, char *); -int stats_vhost(Client *, char *); -int stats_denylinkauto(Client *, char *); -int stats_kline(Client *, char *); -int stats_banrealname(Client *, char *); -int stats_sqline(Client *, char *); -int stats_linkinfoint(Client *, char *, int); -int stats_linkinfo(Client *, char *); -int stats_linkinfoall(Client *, char *); -int stats_chanrestrict(Client *, char *); -int stats_shun(Client *, char *); -int stats_set(Client *, char *); -int stats_tld(Client *, char *); -int stats_uptime(Client *, char *); -int stats_denyver(Client *, char *); -int stats_notlink(Client *, char *); -int stats_class(Client *, char *); -int stats_officialchannels(Client *, char *); -int stats_spamfilter(Client *, char *); -int stats_fdtable(Client *, char *); +int stats_banversion(Client *, const char *); +int stats_links(Client *, const char *); +int stats_denylinkall(Client *, const char *); +int stats_gline(Client *, const char *); +int stats_except(Client *, const char *); +int stats_allow(Client *, const char *); +int stats_command(Client *, const char *); +int stats_oper(Client *, const char *); +int stats_port(Client *, const char *); +int stats_bannick(Client *, const char *); +int stats_traffic(Client *, const char *); +int stats_uline(Client *, const char *); +int stats_vhost(Client *, const char *); +int stats_denylinkauto(Client *, const char *); +int stats_kline(Client *, const char *); +int stats_banrealname(Client *, const char *); +int stats_sqline(Client *, const char *); +int stats_linkinfoint(Client *, const char *, int); +int stats_linkinfo(Client *, const char *); +int stats_linkinfoall(Client *, const char *); +int stats_chanrestrict(Client *, const char *); +int stats_shun(Client *, const char *); +int stats_set(Client *, const char *); +int stats_tld(Client *, const char *); +int stats_uptime(Client *, const char *); +int stats_denyver(Client *, const char *); +int stats_notlink(Client *, const char *); +int stats_class(Client *, const char *); +int stats_officialchannels(Client *, const char *); +int stats_spamfilter(Client *, const char *); +int stats_fdtable(Client *, const char *); #define SERVER_AS_PARA 0x1 #define FLAGS_AS_PARA 0x2 @@ -93,7 +92,7 @@ int stats_fdtable(Client *, char *); struct statstab { char flag; char *longflag; - int (*func)(Client *client, char *para); + int (*func)(Client *client, const char *para); int options; }; @@ -142,7 +141,7 @@ struct statstab StatsTable[] = { { 0, NULL, NULL, 0 } }; -int stats_compare(char *s1, char *s2) +int stats_compare(const char *s1, const char *s2) { /* The long stats flags are always lowercase */ while (*s1 == tolower(*s2)) @@ -171,7 +170,7 @@ static inline struct statstab *stats_binary_search(char c) { return NULL; } -static inline struct statstab *stats_search(char *s) { +static inline struct statstab *stats_search(const char *s) { int i; for (i = 0; StatsTable[i].flag; i++) if (!stats_compare(StatsTable[i].longflag,s)) @@ -179,7 +178,7 @@ static inline struct statstab *stats_search(char *s) { return NULL; } -static inline char *stats_combine_parv(char *p1, char *p2) +static inline char *stats_combine_parv(const char *p1, const char *p2) { static char buf[BUFSIZE+1]; ircsnprintf(buf, sizeof(buf), "%s %s", p1, p2); @@ -259,7 +258,7 @@ static inline int allow_user_stats_short(char c) return 0; } -static inline int allow_user_stats_long(char *s) +static inline int allow_user_stats_long(const char *s) { OperStat *os; for (os = iConf.allow_user_stats_ext; os; os = os->next) @@ -295,12 +294,12 @@ CMD_FUNC(cmd_stats) if (parc == 3 && parv[2][0] != '+' && parv[2][0] != '-') { - if (hunt_server(client, recv_mtags, ":%s STATS %s :%s", 2, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "STATS", 2, parc, parv) != HUNTED_ISME) return; } else if (parc == 4 && parv[2][0] != '+' && parv[2][0] != '-') { - if (hunt_server(client, recv_mtags, ":%s STATS %s %s %s", 2, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "STATS", 2, parc, parv) != HUNTED_ISME) return; } if (parc < 2 || !*parv[1]) @@ -382,20 +381,13 @@ CMD_FUNC(cmd_stats) */ if (stat->flag != 'S') { - RunHook2(HOOKTYPE_STATS, client, flags); + RunHook(HOOKTYPE_STATS, client, flags); } sendnumeric(client, RPL_ENDOFSTATS, stat->flag); - - if (!IsULine(client)) - sendto_snomask(SNO_EYES, "Stats \'%c\' requested by %s (%s@%s)", - stat->flag, client->name, client->user->username, GetHost(client)); - else - sendto_snomask(SNO_JUNK, "Stats \'%c\' requested by %s (%s@%s) [ulined]", - stat->flag, client->name, client->user->username, GetHost(client)); } -int stats_banversion(Client *client, char *para) +int stats_banversion(Client *client, const char *para) { ConfigItem_ban *bans; for (bans = conf_ban; bans; bans = bans->next) @@ -408,7 +400,7 @@ int stats_banversion(Client *client, char *para) return 0; } -int stats_links(Client *client, char *para) +int stats_links(Client *client, const char *para) { ConfigItem_link *link_p; #ifdef DEBUGMODE @@ -433,40 +425,42 @@ int stats_links(Client *client, char *para) else if (link_p->leaf) sendnumericfmt(client, RPL_STATSLLINE, "L %s * %s %d", link_p->leaf, link_p->servername, link_p->leaf_depth); - // TODO: send incoming allow list? (for opers only) } #ifdef DEBUGMODE list_for_each_entry(acptr, &client_list, client_node) - if (MyConnect(acptr) && acptr->serv && !IsMe(acptr)) + if (MyConnect(acptr) && acptr->server && !IsMe(acptr)) { - if (!acptr->serv->conf) + if (!acptr->server->conf) sendnotice(client, "client '%s' (%p) has NO CONF attached (? :P)", acptr->name, acptr); else sendnotice(client, "client '%s' (%p) has conf %p attached, refcount: %d, temporary: %s", acptr->name, acptr, - acptr->serv->conf, - acptr->serv->conf->refcount, - acptr->serv->conf->flag.temporary ? "YES" : "NO"); + acptr->server->conf, + acptr->server->conf->refcount, + acptr->server->conf->flag.temporary ? "YES" : "NO"); } #endif return 0; } -int stats_denylinkall(Client *client, char *para) +int stats_denylinkall(Client *client, const char *para) { ConfigItem_deny_link *links; + ConfigItem_mask *m; for (links = conf_deny_link; links; links = links->next) { if (links->flag.type == CRULE_ALL) - sendnumeric(client, RPL_STATSDLINE, - 'D', links->mask, links->prettyrule); + { + for (m = links->mask; m; m = m->next) + sendnumeric(client, RPL_STATSDLINE, 'D', m->mask, links->prettyrule); + } } return 0; } -int stats_gline(Client *client, char *para) +int stats_gline(Client *client, const char *para) { int cnt = 0; tkl_stats(client, TKL_GLOBAL|TKL_KILL, para, &cnt); @@ -474,7 +468,7 @@ int stats_gline(Client *client, char *para) return 0; } -int stats_spamfilter(Client *client, char *para) +int stats_spamfilter(Client *client, const char *para) { int cnt = 0; tkl_stats(client, TKL_SPAMF, para, &cnt); @@ -482,7 +476,7 @@ int stats_spamfilter(Client *client, char *para) return 0; } -int stats_except(Client *client, char *para) +int stats_except(Client *client, const char *para) { int cnt = 0; tkl_stats(client, TKL_EXCEPTION, para, &cnt); @@ -490,43 +484,41 @@ int stats_except(Client *client, char *para) return 0; } -int stats_allow(Client *client, char *para) +int stats_allow(Client *client, const char *para) { ConfigItem_allow *allows; + ConfigItem_mask *m; + for (allows = conf_allow; allows; allows = allows->next) { - sendnumeric(client, RPL_STATSILINE, - allows->ip, allows->hostname, - allows->maxperip, - allows->global_maxperip, - allows->class->name, - allows->server ? allows->server : defserv, - allows->port ? allows->port : 6667); + for (m = allows->mask; m; m = m->next) + { + sendnumeric(client, RPL_STATSILINE, + m->mask, "-", + allows->maxperip, + allows->global_maxperip, + allows->class->name, + allows->server ? allows->server : DEFAULT_SERVER, + allows->port ? allows->port : 6667); + } } return 0; } -int stats_command(Client *client, char *para) +int stats_command(Client *client, const char *para) { int i; RealCommand *mptr; for (i = 0; i < 256; i++) for (mptr = CommandHash[i]; mptr; mptr = mptr->next) if (mptr->count) -#ifndef DEBUGMODE sendnumeric(client, RPL_STATSCOMMANDS, mptr->cmd, mptr->count, mptr->bytes); -#else - sendnumeric(client, RPL_STATSCOMMANDS, mptr->cmd, - mptr->count, mptr->bytes, - mptr->lticks, mptr->lticks / CLOCKS_PER_SEC, - mptr->rticks, mptr->rticks / CLOCKS_PER_SEC); -#endif return 0; } -int stats_oper(Client *client, char *para) +int stats_oper(Client *client, const char *para) { ConfigItem_oper *oper_p; ConfigItem_mask *m; @@ -556,7 +548,7 @@ static char *stats_port_helper(ConfigItem_listen *listener) return buf; } -int stats_port(Client *client, char *para) +int stats_port(Client *client, const char *para) { ConfigItem_listen *listener; @@ -577,7 +569,7 @@ int stats_port(Client *client, char *para) return 0; } -int stats_bannick(Client *client, char *para) +int stats_bannick(Client *client, const char *para) { int cnt = 0; tkl_stats(client, TKL_NAME, para, &cnt); @@ -585,7 +577,7 @@ int stats_bannick(Client *client, char *para) return 0; } -int stats_traffic(Client *client, char *para) +int stats_traffic(Client *client, const char *para) { Client *acptr; IRCStatistics *sp; @@ -599,41 +591,13 @@ int stats_traffic(Client *client, char *para) { if (IsServer(acptr)) { - sp->is_sbs += acptr->local->sendB; - sp->is_sbr += acptr->local->receiveB; - sp->is_sks += acptr->local->sendK; - sp->is_skr += acptr->local->receiveK; - sp->is_sti += now - acptr->local->firsttime; + sp->is_sti += now - acptr->local->creationtime; sp->is_sv++; - if (sp->is_sbs > 1023) - { - sp->is_sks += (sp->is_sbs >> 10); - sp->is_sbs &= 0x3ff; - } - if (sp->is_sbr > 1023) - { - sp->is_skr += (sp->is_sbr >> 10); - sp->is_sbr &= 0x3ff; - } } else if (IsUser(acptr)) { - sp->is_cbs += acptr->local->sendB; - sp->is_cbr += acptr->local->receiveB; - sp->is_cks += acptr->local->sendK; - sp->is_ckr += acptr->local->receiveK; - sp->is_cti += now - acptr->local->firsttime; + sp->is_cti += now - acptr->local->creationtime; sp->is_cl++; - if (sp->is_cbs > 1023) - { - sp->is_cks += (sp->is_cbs >> 10); - sp->is_cbs &= 0x3ff; - } - if (sp->is_cbr > 1023) - { - sp->is_ckr += (sp->is_cbr >> 10); - sp->is_cbr &= 0x3ff; - } } else if (IsUnknown(acptr)) sp->is_ni++; @@ -648,17 +612,17 @@ int stats_traffic(Client *client, char *para) sendnumericfmt(client, RPL_STATSDEBUG, "local connections %u udp packets %u", sp->is_loc, sp->is_udp); sendnumericfmt(client, RPL_STATSDEBUG, "Client Server"); sendnumericfmt(client, RPL_STATSDEBUG, "connected %u %u", sp->is_cl, sp->is_sv); - sendnumericfmt(client, RPL_STATSDEBUG, "bytes sent %ld.%huK %ld.%huK", - sp->is_cks, sp->is_cbs, sp->is_sks, sp->is_sbs); - sendnumericfmt(client, RPL_STATSDEBUG, "bytes recv %ld.%huK %ld.%huK", - sp->is_ckr, sp->is_cbr, sp->is_skr, sp->is_sbr); + sendnumericfmt(client, RPL_STATSDEBUG, "messages sent %lld", me.local->traffic.messages_sent); + sendnumericfmt(client, RPL_STATSDEBUG, "messages received %lld", me.local->traffic.messages_received); + sendnumericfmt(client, RPL_STATSDEBUG, "bytes sent %lld", me.local->traffic.bytes_sent); + sendnumericfmt(client, RPL_STATSDEBUG, "bytes received %lld", me.local->traffic.bytes_received); sendnumericfmt(client, RPL_STATSDEBUG, "time connected %lld %lld", (long long)sp->is_cti, (long long)sp->is_sti); return 0; } -int stats_fdtable(Client *client, char *para) +int stats_fdtable(Client *client, const char *para) { int i; @@ -677,14 +641,14 @@ int stats_fdtable(Client *client, char *para) return 0; } -int stats_uline(Client *client, char *para) +int stats_uline(Client *client, const char *para) { ConfigItem_ulines *ulines; for (ulines = conf_ulines; ulines; ulines = ulines->next) sendnumeric(client, RPL_STATSULINE, ulines->servername); return 0; } -int stats_vhost(Client *client, char *para) +int stats_vhost(Client *client, const char *para) { ConfigItem_mask *m; ConfigItem_vhost *vhosts; @@ -700,20 +664,23 @@ int stats_vhost(Client *client, char *para) return 0; } -int stats_denylinkauto(Client *client, char *para) +int stats_denylinkauto(Client *client, const char *para) { ConfigItem_deny_link *links; + ConfigItem_mask *m; for (links = conf_deny_link; links; links = links->next) { if (links->flag.type == CRULE_AUTO) - sendnumeric(client, RPL_STATSDLINE, - 'd', links->mask, links->prettyrule); + { + for (m = links->mask; m; m = m->next) + sendnumeric(client, RPL_STATSDLINE, 'd', m->mask, links->prettyrule); + } } return 0; } -int stats_kline(Client *client, char *para) +int stats_kline(Client *client, const char *para) { int cnt = 0; tkl_stats(client, TKL_KILL, NULL, &cnt); @@ -721,7 +688,7 @@ int stats_kline(Client *client, char *para) return 0; } -int stats_banrealname(Client *client, char *para) +int stats_banrealname(Client *client, const char *para) { ConfigItem_ban *bans; for (bans = conf_ban; bans; bans = bans->next) @@ -735,14 +702,14 @@ int stats_banrealname(Client *client, char *para) return 0; } -int stats_sqline(Client *client, char *para) +int stats_sqline(Client *client, const char *para) { int cnt = 0; tkl_stats(client, TKL_NAME|TKL_GLOBAL, para, &cnt); return 0; } -int stats_chanrestrict(Client *client, char *para) +int stats_chanrestrict(Client *client, const char *para) { ConfigItem_deny_channel *dchans; ConfigItem_allow_channel *achans; @@ -757,7 +724,7 @@ int stats_chanrestrict(Client *client, char *para) return 0; } -int stats_shun(Client *client, char *para) +int stats_shun(Client *client, const char *para) { int cnt = 0; tkl_stats(client, TKL_GLOBAL|TKL_SHUN, para, &cnt); @@ -765,13 +732,13 @@ int stats_shun(Client *client, char *para) } /* should this be moved to a seperate stats flag? */ -int stats_officialchannels(Client *client, char *para) +int stats_officialchannels(Client *client, const char *para) { ConfigItem_offchans *x; for (x = conf_offchans; x; x = x->next) { - sendtxtnumeric(client, "%s %s", x->chname, x->topic ? x->topic : ""); + sendtxtnumeric(client, "%s %s", x->name, x->topic ? x->topic : ""); } return 0; } @@ -791,6 +758,14 @@ static void stats_set_anti_flood(Client *client, FloodSettings *f) f->name, floodoption_names[i], (int)f->limit[i], pretty_time_val(f->period[i])); } + if (i == FLD_LAG_PENALTY) + { + sendtxtnumeric(client, "anti-flood::%s::lag-penalty: %d msec", + f->name, (int)f->period[i]); + sendtxtnumeric(client, "anti-flood::%s::lag-penalty-bytes: %d", + f->name, + f->limit[i] == INT_MAX ? 0 : (int)f->limit[i]); + } else { sendtxtnumeric(client, "anti-flood::%s::%s: %d per %s", @@ -800,11 +775,12 @@ static void stats_set_anti_flood(Client *client, FloodSettings *f) } } -int stats_set(Client *client, char *para) +int stats_set(Client *client, const char *para) { char *uhallow; SecurityGroup *s; FloodSettings *f; + char modebuf[BUFSIZE], parabuf[BUFSIZE]; if (!ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL)) { @@ -813,8 +789,8 @@ int stats_set(Client *client, char *para) } sendtxtnumeric(client, "*** Configuration Report ***"); - sendtxtnumeric(client, "network-name: %s", ircnetwork); - sendtxtnumeric(client, "default-server: %s", defserv); + sendtxtnumeric(client, "network-name: %s", NETWORK_NAME); + sendtxtnumeric(client, "default-server: %s", DEFAULT_SERVER); if (SERVICES_NAME) { sendtxtnumeric(client, "services-server: %s", SERVICES_NAME); @@ -827,9 +803,9 @@ int stats_set(Client *client, char *para) { sendtxtnumeric(client, "sasl-server: %s", SASL_SERVER); } - sendtxtnumeric(client, "hiddenhost-prefix: %s", hidden_host); - sendtxtnumeric(client, "help-channel: %s", helpchan); - sendtxtnumeric(client, "cloak-keys: %s", CLOAK_KEYCRC); + sendtxtnumeric(client, "cloak-prefix: %s", CLOAK_PREFIX); + sendtxtnumeric(client, "help-channel: %s", HELP_CHANNEL); + sendtxtnumeric(client, "cloak-keys: %s", CLOAK_KEY_CHECKSUM); sendtxtnumeric(client, "kline-address: %s", KLINE_ADDRESS); if (GLINE_ADDRESS) sendtxtnumeric(client, "gline-address: %s", GLINE_ADDRESS); @@ -896,8 +872,6 @@ int stats_set(Client *client, char *para) sendtxtnumeric(client, "static-part: %s", STATIC_PART ? STATIC_PART : ""); sendtxtnumeric(client, "who-limit: %d", WHOLIMIT); sendtxtnumeric(client, "silence-limit: %d", SILENCE_LIMIT); - if (DNS_BINDIP) - sendtxtnumeric(client, "dns::bind-ip: %s", DNS_BINDIP); sendtxtnumeric(client, "ban-version-tkl-time: %s", pretty_time_val(BAN_VERSION_TKL_TIME)); if (LINK_BINDIP) sendtxtnumeric(client, "link::bind-ip: %s", LINK_BINDIP); @@ -932,7 +906,7 @@ int stats_set(Client *client, char *para) sendtxtnumeric(client, "outdated-tls-policy::user: %s", policy_valtostr(iConf.outdated_tls_policy_user)); sendtxtnumeric(client, "outdated-tls-policy::oper: %s", policy_valtostr(iConf.outdated_tls_policy_oper)); sendtxtnumeric(client, "outdated-tls-policy::server: %s", policy_valtostr(iConf.outdated_tls_policy_server)); - RunHook2(HOOKTYPE_STATS, client, "S"); + RunHook(HOOKTYPE_STATS, client, "S"); #ifndef _WIN32 sendtxtnumeric(client, "This server can handle %d concurrent sockets (%d clients + %d reserve)", maxclients+CLIENTS_RESERVE, maxclients, CLIENTS_RESERVE); @@ -940,34 +914,34 @@ int stats_set(Client *client, char *para) return 1; } -int stats_tld(Client *client, char *para) +int stats_tld(Client *client, const char *para) { ConfigItem_tld *tld; + ConfigItem_mask *m; for (tld = conf_tld; tld; tld = tld->next) { - sendnumeric(client, RPL_STATSTLINE, - tld->mask, tld->motd_file, tld->rules_file ? - tld->rules_file : "none"); + for (m = tld->mask; m; m = m->next) + sendnumeric(client, RPL_STATSTLINE, m->mask, tld->motd_file, tld->rules_file ? tld->rules_file : "none"); } return 0; } -int stats_uptime(Client *client, char *para) +int stats_uptime(Client *client, const char *para) { - time_t tmpnow; + long long uptime; - tmpnow = TStime() - me.local->since; + uptime = TStime() - me.local->fake_lag; sendnumeric(client, RPL_STATSUPTIME, - tmpnow / 86400, (tmpnow / 3600) % 24, (tmpnow / 60) % 60, - tmpnow % 60); + uptime / 86400, (uptime / 3600) % 24, (uptime / 60) % 60, + uptime % 60); sendnumeric(client, RPL_STATSCONN, max_connection_count, irccounts.me_max); return 0; } -int stats_denyver(Client *client, char *para) +int stats_denyver(Client *client, const char *para) { ConfigItem_deny_version *versions; for (versions = conf_deny_version; versions; versions = versions->next) @@ -978,7 +952,7 @@ int stats_denyver(Client *client, char *para) return 0; } -int stats_notlink(Client *client, char *para) +int stats_notlink(Client *client, const char *para) { ConfigItem_link *link_p; @@ -993,7 +967,7 @@ int stats_notlink(Client *client, char *para) return 0; } -int stats_class(Client *client, char *para) +int stats_class(Client *client, const char *para) { ConfigItem_class *classes; @@ -1009,31 +983,23 @@ int stats_class(Client *client, char *para) return 0; } -int stats_linkinfo(Client *client, char *para) +int stats_linkinfo(Client *client, const char *para) { return stats_linkinfoint(client, para, 0); } -int stats_linkinfoall(Client *client, char *para) +int stats_linkinfoall(Client *client, const char *para) { return stats_linkinfoint(client, para, 1); } -int stats_linkinfoint(Client *client, char *para, int all) +int stats_linkinfoint(Client *client, const char *para, int all) { -#ifndef DEBUGMODE - static char Sformat[] = "SendQ SendM SendBytes RcveM RcveBytes Open_since :Idle"; - static char Lformat[] = "%s%s %u %u %u %u %u %u :%u"; -#else - static char Sformat[] = "SendQ SendM SendBytes RcveM RcveBytes Open_since CPU :Idle"; - static char Lformat[] = "%s%s %u %u %u %u %u %u %s"; - char pbuf[96]; /* Should be enough for to ints */ -#endif int remote = 0; int wilds = 0; int doall = 0; - int showports = ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL); Client *acptr; + /* * send info about connections which match, or all if the * mask matches me.name. Only restrictions are on those who @@ -1051,7 +1017,9 @@ int stats_linkinfoint(Client *client, char *para, int all) } else para = me.name; - sendnumericfmt(client, RPL_STATSLINKINFO, "%s", Sformat); + + sendnumericfmt(client, RPL_STATSLINKINFO, "Name SendQ SendM SendBytes RcveM RcveBytes Open_since :Idle"); + if (!MyUser(client)) { remote = 1; @@ -1076,54 +1044,23 @@ int stats_linkinfoint(Client *client, char *para, int all) continue; } -#ifdef DEBUGMODE - ircsnprintf(pbuf, sizeof(pbuf), "%ld :%ld", (long)acptr->local->cputime, - (long)(acptr->user && MyConnect(acptr)) ? TStime() - acptr->local->last : 0); -#endif - if (ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL)) - { - sendnumericfmt(client, RPL_STATSLINKINFO, Lformat, - all ? - (get_client_name2(acptr, showports)) : - (get_client_name(acptr, FALSE)), - get_client_status(acptr), - (int)DBufLength(&acptr->local->sendQ), - (int)acptr->local->sendM, (int)acptr->local->sendK, - (int)acptr->local->receiveM, - (int)acptr->local->receiveK, - TStime() - acptr->local->firsttime, -#ifndef DEBUGMODE - (acptr->user && MyConnect(acptr)) ? - TStime() - acptr->local->last : 0); -#else - pbuf); -#endif - } - else if (!strchr(acptr->name, '.')) - sendnumericfmt(client, RPL_STATSLINKINFO, Lformat, - IsHidden(acptr) ? acptr->name : - all ? /* Potvin - PreZ */ - get_client_name2(acptr, showports) : - get_client_name(acptr, FALSE), - get_client_status(acptr), - (int)DBufLength(&acptr->local->sendQ), - (int)acptr->local->sendM, (int)acptr->local->sendK, - (int)acptr->local->receiveM, - (int)acptr->local->receiveK, - TStime() - acptr->local->firsttime, -#ifndef DEBUGMODE - (acptr->user && MyConnect(acptr)) ? - TStime() - acptr->local->last : 0); -#else - pbuf); -#endif + sendnumericfmt(client, RPL_STATSLINKINFO, + "%s%s %lld %lld %lld %lld %lld %lld :%lld", + acptr->name, get_client_status(acptr), + (long long)DBufLength(&acptr->local->sendQ), + (long long)acptr->local->traffic.messages_sent, + (long long)acptr->local->traffic.bytes_sent, + (long long)acptr->local->traffic.messages_received, + (long long)acptr->local->traffic.bytes_received, + (long long)(TStime() - acptr->local->creationtime), + (long long)(TStime() - acptr->local->last_msg_received)); } #ifdef DEBUGMODE list_for_each_entry(acptr, &client_list, client_node) { if (IsServer(acptr)) sendnotice(client, "Server %s is %s", - acptr->name, acptr->serv->flags.synced ? "SYNCED" : "NOT SYNCED!!"); + acptr->name, acptr->server->flags.synced ? "SYNCED" : "NOT SYNCED!!"); } #endif return 0; diff --git a/src/modules/sts.c b/src/modules/sts.c index 4d78d01..35a8c78 100644 --- a/src/modules/sts.c +++ b/src/modules/sts.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "5.0", "Strict Transport Security CAP", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -66,7 +66,7 @@ int sts_capability_visible(Client *client) if (!IsSecure(client)) { if (iConf.tls_options && iConf.tls_options->sts_port) - return 1; /* YES, non-SSL user and set::tls::sts-policy configured */ + return 1; /* YES, non-TLS user and set::tls::sts-policy configured */ return 0; /* NO, there is no sts-policy */ } @@ -78,7 +78,7 @@ int sts_capability_visible(Client *client) return 0; } -char *sts_capability_parameter(Client *client) +const char *sts_capability_parameter(Client *client) { TLSOptions *ssl; static char buf[256]; diff --git a/src/modules/svsjoin.c b/src/modules/svsjoin.c index f0175ae..3dc7ddb 100644 --- a/src/modules/svsjoin.c +++ b/src/modules/svsjoin.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /svsjoin", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -66,7 +66,7 @@ CMD_FUNC(cmd_svsjoin) if (!IsULine(client)) return; - if ((parc < 3) || !(target = find_person(parv[1], NULL))) + if ((parc < 3) || !(target = find_user(parv[1], NULL))) return; if (MyUser(target)) diff --git a/src/modules/svskill.c b/src/modules/svskill.c index a1121d4..415326c 100644 --- a/src/modules/svskill.c +++ b/src/modules/svskill.c @@ -29,7 +29,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /svskill", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; @@ -63,7 +63,7 @@ CMD_FUNC(cmd_svskill) { MessageTag *mtags = NULL; Client *target; - char *comment = "SVS Killed"; + const char *comment = "SVS Killed"; int n; if (parc < 2) @@ -76,7 +76,7 @@ CMD_FUNC(cmd_svskill) if (!IsULine(client)) return; - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) return; /* for new_message() we use target here, makes sense for the exit_client, right? */ diff --git a/src/modules/svslusers.c b/src/modules/svslusers.c index a231929..9d9e069 100644 --- a/src/modules/svslusers.c +++ b/src/modules/svslusers.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", "command /svslusers", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -66,7 +66,7 @@ CMD_FUNC(cmd_svslusers) { if (!IsULine(client) || parc < 4) return; - if (hunt_server(client, NULL, ":%s SVSLUSERS %s %s :%s", 1, parc, parv) == HUNTED_ISME) + if (hunt_server(client, NULL, "SVSLUSERS", 1, parc, parv) == HUNTED_ISME) { int temp; temp = atoi(parv[2]); diff --git a/src/modules/svsmode.c b/src/modules/svsmode.c index 3bbd85b..5fb4025 100644 --- a/src/modules/svsmode.c +++ b/src/modules/svsmode.c @@ -39,9 +39,11 @@ ModuleHeader MOD_HEADER "5.0", "command /svsmode and svs2mode", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; +char modebuf[BUFSIZE], parabuf[BUFSIZE]; + MOD_INIT() { CommandAdd(modinfo->handle, MSG_SVSMODE, cmd_svsmode, MAXPARA, CMD_SERVER|CMD_USER); @@ -63,8 +65,10 @@ MOD_UNLOAD() void unban_user(Client *client, Channel *channel, Client *acptr, char chmode) { Extban *extban; + const char *nextbanstr; Ban *ban, *bnext; Ban **banlist; + BanContext *b; char uhost[NICKLEN+USERLEN+HOSTLEN+6], vhost[NICKLEN+USERLEN+HOSTLEN+6]; char ihost[NICKLEN+USERLEN+HOSTLEN+6], chost[NICKLEN+USERLEN+HOSTLEN+6]; @@ -114,6 +118,11 @@ void unban_user(Client *client, Channel *channel, Client *acptr, char chmode) /* DO THE ACTUAL WORK */ + b = safe_alloc(sizeof(BanContext)); + b->client = acptr; + b->channel = channel; + b->ban_check_types = BANCHK_JOIN; + for (ban = *banlist; ban; ban = bnext) { bnext = ban->next; @@ -122,15 +131,15 @@ void unban_user(Client *client, Channel *channel, Client *acptr, char chmode) (*ihost && match_simple(ban->banstr, ihost)) || (*chost && match_simple(ban->banstr, chost))) { - add_send_mode_param(channel, client, '-', chmode, - ban->banstr); + add_send_mode_param(channel, client, '-', chmode, ban->banstr); del_listmode(banlist, channel, ban->banstr); } - else if (chmode != 'I' && *ban->banstr == '~' && (extban = findmod_by_bantype(ban->banstr[1]))) + else if (chmode != 'I' && *ban->banstr == '~' && (extban = findmod_by_bantype(ban->banstr, &nextbanstr))) { - if (extban->options & EXTBOPT_CHSVSMODE) + if ((extban->options & EXTBOPT_CHSVSMODE) && (extban->is_banned_events & b->ban_check_types)) { - if (extban->is_banned(acptr, channel, ban->banstr, BANCHK_JOIN, NULL, NULL)) + b->banstr = nextbanstr; + if (extban->is_banned(b)) { add_send_mode_param(channel, acptr, '-', chmode, ban->banstr); del_listmode(banlist, channel, ban->banstr); @@ -138,6 +147,7 @@ void unban_user(Client *client, Channel *channel, Client *acptr, char chmode) } } } + safe_free(b); } void clear_bans(Client *client, Channel *channel, char chmode) @@ -164,7 +174,7 @@ void clear_bans(Client *client, Channel *channel, char chmode) for (ban = *banlist; ban; ban = bnext) { bnext = ban->next; - if (chmode != 'I' && (*ban->banstr == '~') && (extban = findmod_by_bantype(ban->banstr[1]))) + if (chmode != 'I' && (*ban->banstr == '~') && (extban = findmod_by_bantype(ban->banstr, NULL))) { if (!(extban->options & EXTBOPT_CHSVSMODE)) continue; @@ -195,14 +205,14 @@ void clear_bans(Client *client, Channel *channel, char chmode) * * OLD syntax had a 'ts' parameter. No services are known to use this. */ -void channel_svsmode(Client *client, int parc, char *parv[]) +void channel_svsmode(Client *client, int parc, const char *parv[]) { Channel *channel; Client *target; - char *m; + const char *m; int what = MODE_ADD; int i = 4; // wtf is this - Member *cm; + Member *member; int channel_flags; *parabuf = *modebuf = '\0'; @@ -210,67 +220,70 @@ void channel_svsmode(Client *client, int parc, char *parv[]) if ((parc < 3) || BadPtr(parv[2])) return; - if (!(channel = find_channel(parv[1], NULL))) + if (!(channel = find_channel(parv[1]))) return; - for(m = parv[2]; *m; m++) + for (m = parv[2]; *m; m++) { - switch (*m) + if (*m == '+') { - case '+': - what = MODE_ADD; - break; - case '-': - what = MODE_DEL; - break; - case 'v': - case 'h': - case 'o': - case 'a': - case 'q': - if (what != MODE_DEL) + what = MODE_ADD; + } else + if (*m == '-') + { + what = MODE_DEL; + } else + if ((*m == 'b') || (*m == 'e') || (*m == 'I')) + { + if (parc >= i) + { + if (!(target = find_user(parv[i-1], NULL))) { - sendto_realops("Warning! Received SVS(2)MODE with +%c for %s from %s, which is invalid!!", - *m, channel->chname, client->name); - continue; - } - channel_flags = char_to_channelflag(*m); - for (cm = channel->members; cm; cm = cm->next) - { - if (cm->flags & channel_flags) - { - Membership *mb; - mb = find_membership_link(cm->client->user->channel, channel); - add_send_mode_param(channel, client, '-', *m, cm->client->name); - cm->flags &= ~channel_flags; - if (mb) - mb->flags = cm->flags; - } - } - break; - case 'b': - case 'e': - case 'I': - if (parc >= i) - { - if (!(target = find_person(parv[i-1], NULL))) - { - i++; - break; - } i++; + break; + } + i++; - unban_user(client, channel, target, *m); + unban_user(client, channel, target, *m); + } + else { + clear_bans(client, channel, *m); + } + } else + { + /* Find member mode handler (vhoaq) */ + Cmode *cm = find_channel_mode_handler(*m); + if (!cm || (cm->type != CMODE_MEMBER)) + { + unreal_log(ULOG_WARNING, "svsmode", "INVALID_SVSMODE", client, + "Invalid SVSMODE for mode '$mode_character' in channel $channel from $client.", + log_data_char("mode_character", *m), + log_data_channel("channel", channel)); + continue; + } + if (what != MODE_DEL) + { + unreal_log(ULOG_WARNING, "svsmode", "INVALID_SVSMODE", client, + "Invalid SVSMODE from $client trying to add '$mode_character' in $channel.", + log_data_char("mode_character", *m), + log_data_channel("channel", channel)); + continue; + } + for (member = channel->members; member; member = member->next) + { + if (check_channel_access_letter(member->member_modes, *m)) + { + Membership *mb = find_membership_link(member->client->user->channel, channel); + if (!mb) + continue; /* bug */ + + /* Send the -x out */ + add_send_mode_param(channel, client, '-', *m, member->client->name); + + /* And remove from memory */ + del_member_mode_fast(member, mb, *m); } - else { - clear_bans(client, channel, *m); - } - break; - default: - sendto_realops("Warning! Invalid mode `%c' used with 'SVSMODE %s %s %s' (from %s %s)", - *m, channel->chname, parv[2], parv[3] ? parv[3] : "", - client->direction->name, client->name); - break; + } } } @@ -278,16 +291,17 @@ void channel_svsmode(Client *client, int parc, char *parv[]) if (*parabuf) { MessageTag *mtags = NULL; + int destroy_channel = 0; /* NOTE: cannot use 'recv_mtag' here because MODE could be rewrapped. Not ideal :( */ new_message(client, NULL, &mtags); sendto_channel(channel, client, client, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", - client->name, channel->chname, modebuf, parabuf); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s", client->id, channel->chname, modebuf, parabuf); + client->name, channel->name, modebuf, parabuf); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s", client->id, channel->name, modebuf, parabuf); /* Activate this hook just like cmd_mode.c */ - RunHook7(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, 0); + RunHook(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, 0, &destroy_channel); free_message_tags(mtags); @@ -300,17 +314,17 @@ void channel_svsmode(Client *client, int parc, char *parv[]) * This is used by both SVSMODE and SVS2MODE, when dealing with users (not channels). * parv[1] - nick to change mode for * parv[2] - modes to change - * parv[3] - Service Stamp (if mode == d) + * parv[3] - account name (if mode contains 'd') * * show_change can be 0 (for svsmode) or 1 (for svs2mode). */ -void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], int show_change) +void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], int show_change) { - int i; - char *m; + Umode *um; + const char *m; Client *target; int what; - long setflags = 0; + long oldumodes = 0; if (!IsULine(client)) return; @@ -326,15 +340,12 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], return; } - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) return; userhost_save_current(target); - /* initialize setflag to be the user's pre-SVSMODE flags */ - for (i = 0; i <= Usermode_highest; i++) - if (Usermode_Table[i].flag && (target->umodes & Usermode_Table[i].mode)) - setflags |= Usermode_Table[i].mode; + oldumodes = target->umodes; /* parse mode change string(s) */ for (m = parv[2]; *m; m++) @@ -383,6 +394,8 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], /* User is no longer oper (after the goto below, anyway)... * so remove all oper-only modes and snomasks. */ + if (MyUser(client)) + RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL); remove_oper_privileges(target, 0); } goto setmodex; @@ -392,10 +405,15 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], if (!IsOper(target) && !strchr(parv[2], 'o')) /* (ofcoz this strchr() is flawed) */ { /* isn't an oper, and would not become one either.. abort! */ - sendto_realops( - "[BUG] server %s tried to set +H while user not an oper, para=%s/%s, " - "umodes=%ld, please fix your services or if you think it's our fault, " - "report at https://bugs.unrealircd.org/", client->name, parv[1], parv[2], target->umodes); + unreal_log(ULOG_WARNING, "svsmode", "SVSMODE_INVALID", client, + "[BUG] Server $client tried to set user mode +H (hidden ircop) " + "on a user that is not +o (not ircop)! " + "Please fix your services, or if you think it is our fault, then " + "report at https://bugs.unrealircd.org/. " + "Parameters: $para1 $para2. Target: $target.", + log_data_string("para1", parv[1]), + log_data_string("para2", parv[2]), + log_data_client("target", target)); break; /* abort! */ } irccounts.operators--; @@ -406,8 +424,18 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], case 'd': if (parv[3]) { - strlcpy(target->user->svid, parv[3], sizeof(target->user->svid)); - user_account_login(recv_mtags, target); + int was_logged_in = IsLoggedIn(target) ? 1 : 0; + strlcpy(target->user->account, parv[3], sizeof(target->user->account)); + if (!was_logged_in && !IsLoggedIn(target)) + { + /* We don't care about users going from not logged in + * to not logged in, which is something that can happen + * from 0 to 123456, eg from no account to unconfirmed account. + */ + } else { + /* LOGIN or LOGOUT (or account change) */ + user_account_login(recv_mtags, target); + } if (MyConnect(target) && IsDead(target)) return; /* was killed due to *LINE on ~a probably */ } @@ -477,16 +505,14 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], break; default: setmodex: - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].flag) - continue; - if (*m == Usermode_Table[i].flag) + if (um->letter == *m) { if (what == MODE_ADD) - target->umodes |= Usermode_Table[i].mode; + target->umodes |= um->mode; else - target->umodes &= ~Usermode_Table[i].mode; + target->umodes &= ~um->mode; break; } } @@ -503,15 +529,15 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], parv[1], parv[2]); /* Here we trigger the same hooks that cmd_mode does and, likewise, - only if the old flags (setflags) are different than the newly- + only if the old flags (oldumodes) are different than the newly- set ones */ - if (setflags != target->umodes) - RunHook3(HOOKTYPE_UMODE_CHANGE, target, setflags, target->umodes); + if (oldumodes != target->umodes) + RunHook(HOOKTYPE_UMODE_CHANGE, target, oldumodes, target->umodes); if (show_change) { char buf[BUFSIZE]; - build_umode_string(target, setflags, ALL_UMODES, buf); + build_umode_string(target, oldumodes, ALL_UMODES, buf); if (MyUser(target) && *buf) sendto_one(target, NULL, ":%s MODE %s :%s", client->name, target->name, buf); } @@ -525,7 +551,7 @@ void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, char *parv[], * cmd_svsmode() added by taz * parv[1] - username to change mode for * parv[2] - modes to change - * parv[3] - Service Stamp (if mode == d) + * parv[3] - account name (if mode contains 'd') */ CMD_FUNC(cmd_svsmode) { @@ -536,7 +562,7 @@ CMD_FUNC(cmd_svsmode) * cmd_svs2mode() added by Potvin * parv[1] - username to change mode for * parv[2] - modes to change - * parv[3] - Service Stamp (if mode == d) + * parv[3] - account name (if mode contains 'd') */ CMD_FUNC(cmd_svs2mode) { @@ -588,8 +614,8 @@ void add_send_mode_param(Channel *channel, Client *from, char what, char mode, c new_message(from, NULL, &mtags); sendto_channel(channel, from, from, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s %s %s", - from->name, channel->chname, modebuf, parabuf); - sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s", from->id, channel->chname, modebuf, parabuf); + from->name, channel->name, modebuf, parabuf); + sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s", from->id, channel->name, modebuf, parabuf); free_message_tags(mtags); send = 0; *parabuf = 0; diff --git a/src/modules/svsmotd.c b/src/modules/svsmotd.c index 2f70740..514bd64 100644 --- a/src/modules/svsmotd.c +++ b/src/modules/svsmotd.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", "command /svsmotd", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -60,59 +60,53 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_svsmotd) { - FILE *conf = NULL; + FILE *conf = NULL; - if (!IsULine(client)) - { - sendnumeric(client, ERR_NOPRIVILEGES); - return; - } - if (parc < 2) - { - sendnumeric(client, ERR_NEEDMOREPARAMS, "SVSMOTD"); - return; - } + if (!IsULine(client)) + { + sendnumeric(client, ERR_NOPRIVILEGES); + return; + } + if (parc < 2) + { + sendnumeric(client, ERR_NEEDMOREPARAMS, "SVSMOTD"); + return; + } - if ((*parv[1] != '!') && parc < 3) - { - sendnumeric(client, ERR_NEEDMOREPARAMS, "SVSMOTD"); - return; - } + if ((*parv[1] != '!') && parc < 3) + { + sendnumeric(client, ERR_NEEDMOREPARAMS, "SVSMOTD"); + return; + } - switch (*parv[1]) - { - case '#': - conf = fopen(conf_files->svsmotd_file, "a"); - sendto_ops("Added '%s' to services motd", parv[2]); - break; - case '!': - { - remove(conf_files->svsmotd_file); - free_motd(&svsmotd); - sendto_ops("Wiped out services motd data"); - break; - } - default: - return; - } - if (parv[2]) - sendto_server(client, 0, 0, NULL, ":%s SVSMOTD %s :%s", client->id, parv[1], parv[2]); - else - sendto_server(client, 0, 0, NULL, ":%s SVSMOTD %s", client->id, parv[1]); + if (parv[2]) + sendto_server(client, 0, 0, NULL, ":%s SVSMOTD %s :%s", client->id, parv[1], parv[2]); + else + sendto_server(client, 0, 0, NULL, ":%s SVSMOTD %s", client->id, parv[1]); - if (conf == NULL) - return; + switch (*parv[1]) + { + case '#': + unreal_log(ULOG_INFO, "svsmotd", "SVSMOTD_ADDED", client, + "Services added '$line' to services motd", + log_data_string("line", parv[2])); + conf = fopen(conf_files->svsmotd_file, "a"); + if (conf) + { + fprintf(conf, "%s\n", parv[2]); + fclose(conf); + } + break; + case '!': + unreal_log(ULOG_INFO, "svsmotd", "SVSMOTD_REMOVED", client, + "Services deleted the services motd"); + remove(conf_files->svsmotd_file); + free_motd(&svsmotd); + break; + default: + return; + } - if (parc < 3 && (*parv[1] == '!')) - { - fclose(conf); - return; - } - fprintf(conf, "%s\n", parv[2]); - if (*parv[1] == '!') - sendto_ops("Added '%s' to services motd", parv[2]); - - fclose(conf); - /* We editted it, so rehash it -- codemastr */ - read_motd(conf_files->svsmotd_file, &svsmotd); + /* We editted it, so rehash it -- codemastr */ + read_motd(conf_files->svsmotd_file, &svsmotd); } diff --git a/src/modules/svsnick.c b/src/modules/svsnick.c index 3a43e89..d619d16 100644 --- a/src/modules/svsnick.c +++ b/src/modules/svsnick.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", "command /svsnick", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -64,20 +64,23 @@ CMD_FUNC(cmd_svsnick) Client *acptr; Client *ocptr; /* Other client */ MessageTag *mtags = NULL; + char nickname[NICKLEN+1]; + char oldnickname[NICKLEN+1]; if (!IsULine(client) || parc < 4 || (strlen(parv[2]) > NICKLEN)) return; /* This looks like an error anyway -Studded */ - if (hunt_server(client, NULL, ":%s SVSNICK %s %s :%s", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, NULL, "SVSNICK", 1, parc, parv) != HUNTED_ISME) return; /* Forwarded, done */ - if (do_nick_name(parv[2]) == 0) + strlcpy(nickname, parv[2], sizeof(nickname)); + if (do_nick_name(nickname) == 0) return; - if (!(acptr = find_person(parv[1], NULL))) + if (!(acptr = find_user(parv[1], NULL))) return; /* User not found, bail out */ - if ((ocptr = find_client(parv[2], NULL)) && ocptr != acptr) /* Collision */ + if ((ocptr = find_client(nickname, NULL)) && ocptr != acptr) /* Collision */ { exit_client(acptr, NULL, "Nickname collision due to Services enforced " @@ -86,30 +89,31 @@ CMD_FUNC(cmd_svsnick) } /* if the new nickname is identical to the old one, ignore it */ - if (!strcmp(acptr->name, parv[2])) + if (!strcmp(acptr->name, nickname)) return; + strlcpy(oldnickname, acptr->name, sizeof(oldnickname)); + if (acptr != ocptr) acptr->umodes &= ~UMODE_REGNICK; acptr->lastnick = atol(parv[3]); /* no 'recv_mtags' here, we do not inherit from SVSNICK but generate a new NICK event */ new_message(acptr, NULL, &mtags); - RunHook3(HOOKTYPE_LOCAL_NICKCHANGE, acptr, mtags, parv[2]); - sendto_local_common_channels(acptr, acptr, 0, mtags, ":%s NICK :%s", acptr->name, parv[2]); - sendto_one(acptr, mtags, ":%s NICK :%s", acptr->name, parv[2]); - sendto_server(NULL, 0, 0, mtags, ":%s NICK %s :%ld", acptr->id, parv[2], atol(parv[3])); - free_message_tags(mtags); + RunHook(HOOKTYPE_LOCAL_NICKCHANGE, acptr, mtags, nickname); + sendto_local_common_channels(acptr, acptr, 0, mtags, ":%s NICK :%s", acptr->name, nickname); + sendto_one(acptr, mtags, ":%s NICK :%s", acptr->name, nickname); + sendto_server(NULL, 0, 0, mtags, ":%s NICK %s :%lld", acptr->id, nickname, (long long)acptr->lastnick); add_history(acptr, 1); del_from_client_hash_table(acptr->name, acptr); - hash_check_watch(acptr, RPL_LOGOFF); - sendto_snomask(SNO_NICKCHANGE, - "*** %s (%s@%s) has been forced to change their nickname to %s", - acptr->name, acptr->user->username, acptr->user->realhost, parv[2]); + unreal_log(ULOG_INFO, "nick", "FORCED_NICK_CHANGE", acptr, + "$client.details has been forced by services to change their nickname to $new_nick_name", + log_data_string("new_nick_name", nickname)); - strlcpy(acptr->name, parv[2], sizeof acptr->name); - add_to_client_hash_table(parv[2], acptr); - hash_check_watch(acptr, RPL_LOGON); + strlcpy(acptr->name, nickname, sizeof acptr->name); + add_to_client_hash_table(nickname, acptr); + RunHook(HOOKTYPE_POST_LOCAL_NICKCHANGE, acptr, mtags, oldnickname); + free_message_tags(mtags); } diff --git a/src/modules/svsnline.c b/src/modules/svsnline.c index 2f8e945..4a31aad 100644 --- a/src/modules/svsnline.c +++ b/src/modules/svsnline.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /svsnline", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ diff --git a/src/modules/svsnolag.c b/src/modules/svsnolag.c index cc71f85..e8aef9f 100644 --- a/src/modules/svsnolag.c +++ b/src/modules/svsnolag.c @@ -33,7 +33,7 @@ ModuleHeader MOD_HEADER "5.0", "commands /svsnolag and /svs2nolag", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -54,7 +54,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -void do_svsnolag(Client *client, int parc, char *parv[], int show_change) +void do_svsnolag(Client *client, int parc, const char *parv[], int show_change) { Client *target; char *cmd = show_change ? MSG_SVS2NOLAG : MSG_SVSNOLAG; @@ -65,7 +65,7 @@ void do_svsnolag(Client *client, int parc, char *parv[], int show_change) if (parc < 3) return; - if (!(target = find_person(parv[2], NULL))) + if (!(target = find_user(parv[2], NULL))) return; if (!MyUser(target)) diff --git a/src/modules/svsnoop.c b/src/modules/svsnoop.c index 28e802e..c86d022 100644 --- a/src/modules/svsnoop.c +++ b/src/modules/svsnoop.c @@ -35,7 +35,7 @@ ModuleHeader MOD_HEADER "5.0", "command /svsnoop", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -62,12 +62,13 @@ CMD_FUNC(cmd_svsnoop) if (!(IsULine(client) && parc > 2)) return; - if (hunt_server(client, NULL, ":%s SVSNOOP %s :%s", 1, parc, parv) == HUNTED_ISME) + if (hunt_server(client, NULL, "SVSNOOP", 1, parc, parv) == HUNTED_ISME) { if (parv[2][0] == '+') { SVSNOOP = 1; - sendto_ops("This server has been placed in NOOP mode"); + unreal_log(ULOG_INFO, "svsnoop", "SVSNOOP_ENABLED", client, + "This server has been placed in NOOP mode (by $client) -- all IRCOp rights disabled"); list_for_each_entry(acptr, &client_list, client_node) { if (MyUser(acptr) && IsOper(acptr)) @@ -81,15 +82,16 @@ CMD_FUNC(cmd_svsnoop) if (!list_empty(&acptr->special_node)) list_del(&acptr->special_node); + RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL); remove_oper_privileges(acptr, 1); - RunHook2(HOOKTYPE_LOCAL_OPER, acptr, 0); } } } else { SVSNOOP = 0; - sendto_ops("This server is no longer in NOOP mode"); + unreal_log(ULOG_INFO, "svsnoop", "SVSNOOP_ENABLED", client, + "This server is no longer in NOOP mode (by $client) -- IRCOps can oper up again"); } } } diff --git a/src/modules/svspart.c b/src/modules/svspart.c index dd9007c..79f2ae5 100644 --- a/src/modules/svspart.c +++ b/src/modules/svspart.c @@ -29,7 +29,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /svspart", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -62,11 +62,11 @@ MOD_UNLOAD() CMD_FUNC(cmd_svspart) { Client *target; - char *comment = (parc > 3 && parv[3] ? parv[3] : NULL); + const char *comment = (parc > 3 && parv[3] ? parv[3] : NULL); if (!IsULine(client)) return; - if (parc < 3 || !(target = find_person(parv[1], NULL))) + if (parc < 3 || !(target = find_user(parv[1], NULL))) return; if (MyUser(target)) diff --git a/src/modules/svssilence.c b/src/modules/svssilence.c index 5d64f54..203903f 100644 --- a/src/modules/svssilence.c +++ b/src/modules/svssilence.c @@ -27,7 +27,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /svssilence", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -60,11 +60,12 @@ CMD_FUNC(cmd_svssilence) Client *target; int mine; char *p, *cp, c; + char request[BUFSIZE]; if (!IsULine(client)) return; - if (parc < 3 || BadPtr(parv[2]) || !(target = find_person(parv[1], NULL))) + if (parc < 3 || BadPtr(parv[2]) || !(target = find_user(parv[1], NULL))) return; if (!MyUser(target)) @@ -74,7 +75,8 @@ CMD_FUNC(cmd_svssilence) } /* It's for our client */ - for (p = strtok(parv[2], " "); p; p = strtok(NULL, " ")) + strlcpy(request, parv[2], sizeof(request)); + for (p = strtok(request, " "); p; p = strtok(NULL, " ")) { c = *p; if ((c == '-') || (c == '+')) diff --git a/src/modules/svssno.c b/src/modules/svssno.c index ced2ea4..93eddab 100644 --- a/src/modules/svssno.c +++ b/src/modules/svssno.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", "command /svssno", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -61,9 +61,9 @@ MOD_UNLOAD() * parv[2] - snomasks to change * show_change determines whether to show the change to the user */ -void do_svssno(Client *client, int parc, char *parv[], int show_change) +void do_svssno(Client *client, int parc, const char *parv[], int show_change) { - char *p; + const char *p; Client *target; int what = MODE_ADD, i; @@ -76,50 +76,22 @@ void do_svssno(Client *client, int parc, char *parv[], int show_change) if (parv[1][0] == '#') return; - if (!(target = find_person(parv[1], NULL))) + if (!(target = find_user(parv[1], NULL))) return; - if (hunt_server(client, NULL, - show_change ? ":%s SVS2SNO %s %s" : ":%s SVSSNO %s %s", - 1, parc, parv) != HUNTED_ISME) - { + if (hunt_server(client, NULL, show_change ? "SVS2SNO" : "SVSSNO", 1, parc, parv) != HUNTED_ISME) return; - } if (MyUser(target)) { if (parc == 2) - target->user->snomask = 0; + set_snomask(target, NULL); else - { - for (p = parv[2]; p && *p; p++) { - switch (*p) { - case '+': - what = MODE_ADD; - break; - case '-': - what = MODE_DEL; - break; - default: - for (i = 0; i <= Snomask_highest; i++) - { - if (!Snomask_Table[i].flag) - continue; - if (*p == Snomask_Table[i].flag) - { - if (what == MODE_ADD) - target->user->snomask |= Snomask_Table[i].mode; - else - target->user->snomask &= ~Snomask_Table[i].mode; - } - } - } - } - } + set_snomask(target, parv[2]); } - if (show_change) - sendnumeric(target, RPL_SNOMASK, get_snomask_string(target)); + if (show_change && target->user->snomask) + sendnumeric(target, RPL_SNOMASK, target->user->snomask); } CMD_FUNC(cmd_svssno) diff --git a/src/modules/svswatch.c b/src/modules/svswatch.c index 040ab0d..afab9a8 100644 --- a/src/modules/svswatch.c +++ b/src/modules/svswatch.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /svswatch", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -65,7 +65,7 @@ CMD_FUNC(cmd_svswatch) if (!IsULine(client)) return; - if (parc < 3 || BadPtr(parv[2]) || !(target = find_person(parv[1], NULL))) + if (parc < 3 || BadPtr(parv[2]) || !(target = find_user(parv[1], NULL))) return; if (MyUser(target)) diff --git a/src/modules/swhois.c b/src/modules/swhois.c index 78d72b1..8d8c71b 100644 --- a/src/modules/swhois.c +++ b/src/modules/swhois.c @@ -34,7 +34,7 @@ ModuleHeader MOD_HEADER "5.0", "command /swhois", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -77,7 +77,7 @@ CMD_FUNC(cmd_swhois) if (parc < 3) return; - target = find_person(parv[1], NULL); + target = find_user(parv[1], NULL); if (!target) return; diff --git a/src/modules/targetfloodprot.c b/src/modules/targetfloodprot.c index 56151b1..5d7d6ac 100644 --- a/src/modules/targetfloodprot.c +++ b/src/modules/targetfloodprot.c @@ -11,7 +11,7 @@ ModuleHeader MOD_HEADER "5.0", "Target flood protection (set::anti-flood::target-flood)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #define TFP_PRIVMSG 0 @@ -35,8 +35,8 @@ struct TargetFloodConfig { int targetfloodprot_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int targetfloodprot_config_run(ConfigFile *cf, ConfigEntry *ce, int type); void targetfloodprot_mdata_free(ModData *m); -int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype); -int targetfloodprot_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); +int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); +int targetfloodprot_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); /* Global variables */ ModDataInfo *targetfloodprot_client_md = NULL; @@ -117,13 +117,11 @@ MOD_LOAD() MOD_UNLOAD() { + safe_free(channelcfg); + safe_free(privatecfg); return MOD_SUCCESS; } -#ifndef CheckNull - #define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } -#endif - int targetfloodprot_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { int errors = 0; @@ -133,36 +131,36 @@ int targetfloodprot_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int * return 0; /* We are only interrested in set::anti-flood::target-flood.. */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "target-flood")) + if (!ce || !ce->name || strcmp(ce->name, "target-flood")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { CheckNull(cep); - if (!strcmp(cep->ce_varname, "channel-privmsg") || - !strcmp(cep->ce_varname, "channel-notice") || - !strcmp(cep->ce_varname, "channel-tagmsg") || - !strcmp(cep->ce_varname, "private-privmsg") || - !strcmp(cep->ce_varname, "private-notice") || - !strcmp(cep->ce_varname, "private-tagmsg")) + if (!strcmp(cep->name, "channel-privmsg") || + !strcmp(cep->name, "channel-notice") || + !strcmp(cep->name, "channel-tagmsg") || + !strcmp(cep->name, "private-privmsg") || + !strcmp(cep->name, "private-notice") || + !strcmp(cep->name, "private-tagmsg")) { int cnt = 0, period = 0; - if (!config_parse_flood(cep->ce_vardata, &cnt, &period) || + if (!config_parse_flood(cep->value, &cnt, &period) || (cnt < 1) || (cnt > 10000) || (period < 1) || (period > 120)) { config_error("%s:%i: set::anti-flood::target-flood::%s error. " "Syntax is ':' (eg 5:60). " "Count must be 1-10000 and period must be 1-120.", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_varname); + cep->file->filename, cep->line_number, + cep->name); errors++; } } else { config_error("%s:%i: unknown directive set::anti-flood::target-flood:%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; continue; } @@ -180,23 +178,23 @@ int targetfloodprot_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::anti-flood::target-flood.. */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "target-flood")) + if (!ce || !ce->name || strcmp(ce->name, "target-flood")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "channel-privmsg")) - config_parse_flood(cep->ce_vardata, &channelcfg->cnt[TFP_PRIVMSG], &channelcfg->t[TFP_PRIVMSG]); - else if (!strcmp(cep->ce_varname, "channel-notice")) - config_parse_flood(cep->ce_vardata, &channelcfg->cnt[TFP_NOTICE], &channelcfg->t[TFP_NOTICE]); - else if (!strcmp(cep->ce_varname, "channel-tagmsg")) - config_parse_flood(cep->ce_vardata, &channelcfg->cnt[TFP_TAGMSG], &channelcfg->t[TFP_TAGMSG]); - else if (!strcmp(cep->ce_varname, "private-privmsg")) - config_parse_flood(cep->ce_vardata, &privatecfg->cnt[TFP_PRIVMSG], &privatecfg->t[TFP_PRIVMSG]); - else if (!strcmp(cep->ce_varname, "private-notice")) - config_parse_flood(cep->ce_vardata, &privatecfg->cnt[TFP_NOTICE], &privatecfg->t[TFP_NOTICE]); - else if (!strcmp(cep->ce_varname, "private-tagmsg")) - config_parse_flood(cep->ce_vardata, &privatecfg->cnt[TFP_TAGMSG], &privatecfg->t[TFP_TAGMSG]); + if (!strcmp(cep->name, "channel-privmsg")) + config_parse_flood(cep->value, &channelcfg->cnt[TFP_PRIVMSG], &channelcfg->t[TFP_PRIVMSG]); + else if (!strcmp(cep->name, "channel-notice")) + config_parse_flood(cep->value, &channelcfg->cnt[TFP_NOTICE], &channelcfg->t[TFP_NOTICE]); + else if (!strcmp(cep->name, "channel-tagmsg")) + config_parse_flood(cep->value, &channelcfg->cnt[TFP_TAGMSG], &channelcfg->t[TFP_TAGMSG]); + else if (!strcmp(cep->name, "private-privmsg")) + config_parse_flood(cep->value, &privatecfg->cnt[TFP_PRIVMSG], &privatecfg->t[TFP_PRIVMSG]); + else if (!strcmp(cep->name, "private-notice")) + config_parse_flood(cep->value, &privatecfg->cnt[TFP_NOTICE], &privatecfg->t[TFP_NOTICE]); + else if (!strcmp(cep->name, "private-tagmsg")) + config_parse_flood(cep->value, &privatecfg->cnt[TFP_TAGMSG], &privatecfg->t[TFP_TAGMSG]); } return 1; @@ -218,13 +216,15 @@ int sendtypetowhat(SendType sendtype) if (sendtype == SEND_TYPE_TAGMSG) return 2; #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "sendtypetowhat() for unknown value %d", (int)sendtype); + unreal_log(ULOG_ERROR, "flood", "BUG_SENDTYPETOWHAT_UNKNOWN_VALUE", NULL, + "[BUG] sendtypetowhat() called for unknown sendtype $send_type", + log_data_integer("send_type", sendtype)); abort(); #endif return 0; /* otherwise, default to privmsg i guess */ } -int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, SendType sendtype) +int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) { TargetFlood *flood; static char errbuf[256]; @@ -269,7 +269,7 @@ int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Member return HOOK_CONTINUE; } -int targetfloodprot_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int targetfloodprot_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { TargetFlood *flood; static char errbuf[256]; diff --git a/src/modules/third/Makefile.in b/src/modules/third/Makefile.in index 5fd8f47..cff2940 100644 --- a/src/modules/third/Makefile.in +++ b/src/modules/third/Makefile.in @@ -25,19 +25,22 @@ INCLUDES = ../../include/channel.h \ ../../include/ircsprintf.h \ ../../include/license.h \ ../../include/modules.h ../../include/modversion.h ../../include/msg.h \ - ../../include/numeric.h ../../include/proto.h ../../include/dns.h \ + ../../include/numeric.h ../../include/dns.h \ ../../include/resource.h ../../include/setup.h \ ../../include/struct.h ../../include/sys.h \ - ../../include/types.h ../../include/url.h \ + ../../include/types.h \ ../../include/version.h ../../include/whowas.h MODULEFLAGS=@MODULEFLAGS@ RM=@RM@ +.SUFFIXES: +.SUFFIXES: .c .h .so + all: build build: - ../../buildmod + ../../buildmod $(MAKE) custommodule: $(MODULEFILE).c $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ diff --git a/src/modules/time.c b/src/modules/time.c index 3b882fe..945aa47 100644 --- a/src/modules/time.c +++ b/src/modules/time.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /time", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; @@ -61,6 +61,6 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_time) { - if (hunt_server(client, recv_mtags, ":%s TIME :%s", 1, parc, parv) == HUNTED_ISME) + if (hunt_server(client, recv_mtags, "TIME", 1, parc, parv) == HUNTED_ISME) sendnumeric(client, RPL_TIME, me.name, long_date(0)); } diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 3bea4f1..c8d93f1 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -29,7 +29,7 @@ ModuleHeader MOD_HEADER "5.0", "Server ban commands such as /GLINE, /SPAMFILTER, etc.", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Forward declarations */ @@ -49,12 +49,13 @@ CMD_FUNC(cmd_kline); CMD_FUNC(cmd_zline); CMD_FUNC(cmd_spamfilter); CMD_FUNC(cmd_eline); -void cmd_tkl_line(Client *client, int parc, char *parv[], char *type); +void cmd_tkl_line(Client *client, int parc, const char *parv[], char *type); int _tkl_hash(unsigned int c); char _tkl_typetochar(int type); int _tkl_chartotype(char c); int tkl_banexception_chartotype(char c); char *_tkl_type_string(TKL *tk); +char *_tkl_type_config_string(TKL *tk); char *tkl_banexception_configname_to_chars(char *name); TKL *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason, char *set_by, time_t expire_at, time_t set_at, int soft, int flags); @@ -71,6 +72,7 @@ void _sendnotice_tkl_add(TKL *tkl); void _free_tkl(TKL *tkl); void _tkl_del_line(TKL *tkl); static void _tkl_check_local_remove_shun(TKL *tmp); +char *_tkl_uhost(TKL *tkl, char *buf, size_t buflen, int options); void tkl_expire_entry(TKL * tmp); EVENT(tkl_check_expire); int _find_tkline_match(Client *client, int skip_soft); @@ -78,18 +80,18 @@ int _find_shun(Client *client); int _find_spamfilter_user(Client *client, int flags); TKL *_find_qline(Client *client, char *nick, int *ishold); TKL *_find_tkline_match_zap(Client *client); -void _tkl_stats(Client *client, int type, char *para, int *cnt); +void _tkl_stats(Client *client, int type, const char *para, int *cnt); void _tkl_sync(Client *client); CMD_FUNC(_cmd_tkl); int _place_host_ban(Client *client, BanAction action, char *reason, long duration); -int _match_spamfilter(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk); +int _match_spamfilter(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, TKL **rettk); int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd); int check_mtag_spamfilters_present(void); int _join_viruschan(Client *client, TKL *tk, int type); void _spamfilter_build_user_string(char *buf, char *nick, Client *client); -int _match_user(char *rmask, Client *client, int options); -int _match_user_extended_server_ban(char *banstr, Client *client); -void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, char **tkl_username, char **tkl_hostname); +int _match_user(const char *rmask, Client *client, int options); +int _match_user_extended_server_ban(const char *banstr, Client *client); +void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, const char **tkl_username, const char **tkl_hostname); int _tkl_ip_hash(char *ip); int _tkl_ip_hash_type(int type); TKL *_find_tkl_serverban(int type, char *usermask, char *hostmask, int softban); @@ -98,6 +100,7 @@ TKL *_find_tkl_nameban(int type, char *name, int hold); TKL *_find_tkl_spamfilter(int type, char *match_string, BanAction action, unsigned short target); int _find_tkl_exception(int ban_type, Client *client); static void add_default_exempts(void); +int parse_extended_server_ban(const char *mask_in, Client *client, char **error, int skip_checking, char *buf1, size_t buf1len, char *buf2, size_t buf2len); /* Externals (only for us :D) */ extern int MODVAR spamf_ugly_vchanoverride; @@ -134,8 +137,8 @@ TKLTypeTable tkl_types[] = { { "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0, 0 }, { "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1, 0 }, { "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0, 0 }, - { "local-spamfilter", 'e', TKL_EXCEPTION, "Local Exception", 1, 0, 0 }, - { "local-exception", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0, 0 }, + { "local-exception", 'e', TKL_EXCEPTION, "Local Exception", 1, 0, 0 }, + { "local-spamfilter", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0, 0 }, { "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1, 1 }, { "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1, 1 }, { "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1, 0 }, @@ -158,9 +161,17 @@ MOD_TEST() HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_except); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_set); EfunctionAdd(modinfo->handle, EFUNC_TKL_HASH, _tkl_hash); +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif EfunctionAdd(modinfo->handle, EFUNC_TKL_TYPETOCHAR, TO_INTFUNC(_tkl_typetochar)); EfunctionAdd(modinfo->handle, EFUNC_TKL_CHARTOTYPE, TO_INTFUNC(_tkl_chartotype)); - EfunctionAddPChar(modinfo->handle, EFUNC_TKL_TYPE_STRING, _tkl_type_string); +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + EfunctionAddString(modinfo->handle, EFUNC_TKL_TYPE_STRING, _tkl_type_string); + EfunctionAddString(modinfo->handle, EFUNC_TKL_TYPE_CONFIG_STRING, _tkl_type_config_string); EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_SERVERBAN, TO_PVOIDFUNC(_tkl_add_serverban)); EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_BANEXCEPTION, TO_PVOIDFUNC(_tkl_add_banexception)); EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_NAMEBAN, TO_PVOIDFUNC(_tkl_add_nameban)); @@ -191,6 +202,7 @@ MOD_TEST() EfunctionAddVoid(modinfo->handle, EFUNC_SENDNOTICE_TKL_ADD, _sendnotice_tkl_add); EfunctionAddVoid(modinfo->handle, EFUNC_SENDNOTICE_TKL_DEL, _sendnotice_tkl_del); EfunctionAdd(modinfo->handle, EFUNC_FIND_TKL_EXCEPTION, _find_tkl_exception); + EfunctionAddString(modinfo->handle, EFUNC_TKL_UHOST, _tkl_uhost); return MOD_SUCCESS; } @@ -237,130 +249,130 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e int match_type = 0; /* We are only interested in spamfilter { } blocks */ - if ((type != CONFIG_MAIN) || strcmp(ce->ce_varname, "spamfilter")) + if ((type != CONFIG_MAIN) || strcmp(ce->name, "spamfilter")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "target")) + if (!strcmp(cep->name, "target")) { if (has_target) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "spamfilter::target"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::target"); continue; } has_target = 1; - if (cep->ce_vardata) + if (cep->value) { - if (!spamfilter_getconftargets(cep->ce_vardata)) + if (!spamfilter_getconftargets(cep->value)) { config_error("%s:%i: unknown spamfiler target type '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } } - else if (cep->ce_entries) + else if (cep->items) { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!spamfilter_getconftargets(cepp->ce_varname)) + if (!spamfilter_getconftargets(cepp->name)) { config_error("%s:%i: unknown spamfiler target type '%s'", - cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, cepp->ce_varname); + cepp->file->filename, + cepp->line_number, cepp->name); errors++; } } } else { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "spamfilter", cep->ce_varname); + config_error_empty(cep->file->filename, + cep->line_number, "spamfilter", cep->name); errors++; } continue; } - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "spamfilter", cep->ce_varname); + config_error_empty(cep->file->filename, cep->line_number, + "spamfilter", cep->name); errors++; continue; } - if (!strcmp(cep->ce_varname, "reason")) + if (!strcmp(cep->name, "reason")) { if (has_reason) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "spamfilter::reason"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::reason"); continue; } has_reason = 1; - reason = cep->ce_vardata; + reason = cep->value; } - else if (!strcmp(cep->ce_varname, "match")) + else if (!strcmp(cep->name, "match")) { if (has_match) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "spamfilter::match"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::match"); continue; } has_match = 1; - match = cep->ce_vardata; + match = cep->value; } - else if (!strcmp(cep->ce_varname, "action")) + else if (!strcmp(cep->name, "action")) { if (has_action) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "spamfilter::action"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::action"); continue; } has_action = 1; - if (!banact_stringtoval(cep->ce_vardata)) + if (!banact_stringtoval(cep->value)) { config_error("%s:%i: spamfilter::action has unknown action type '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } } - else if (!strcmp(cep->ce_varname, "ban-time")) + else if (!strcmp(cep->name, "ban-time")) { if (has_bantime) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "spamfilter::ban-time"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::ban-time"); continue; } has_bantime = 1; } - else if (!strcmp(cep->ce_varname, "match-type")) + else if (!strcmp(cep->name, "match-type")) { if (has_match_type) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "spamfilter::match-type"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::match-type"); continue; } - if (!strcasecmp(cep->ce_vardata, "posix")) + if (!strcasecmp(cep->value, "posix")) { config_error("%s:%i: this spamfilter uses match-type 'posix' which is no longer supported. " "You must switch over to match-type 'regex' instead. " "See https://www.unrealircd.org/docs/FAQ#spamfilter-posix-deprecated", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; *errs = errors; return -1; /* return now, otherwise there will be issues */ } - match_type = unreal_match_method_strtoval(cep->ce_vardata); + match_type = unreal_match_method_strtoval(cep->value); if (match_type == 0) { config_error("%s:%i: spamfilter::match-type: unknown match type '%s', " "should be one of: 'simple', 'regex' or 'posix'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata); + cep->file->filename, cep->line_number, + cep->value); errors++; continue; } @@ -368,8 +380,8 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "spamfilter", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "spamfilter", cep->name); errors++; continue; } @@ -384,8 +396,8 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e if (!m) { config_error("%s:%i: spamfilter::match contains an invalid regex: %s", - ce->ce_fileptr->cf_filename, - ce->ce_varlinenum, + ce->file->filename, + ce->line_number, err); errors++; } else @@ -396,19 +408,19 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e if (!has_match) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "spamfilter::match"); errors++; } if (!has_target) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "spamfilter::target"); errors++; } if (!has_action) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "spamfilter::action"); errors++; } @@ -416,21 +428,16 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e { config_error("%s:%i: spamfilter block problem: match + reason field are together over 505 bytes, " "please choose a shorter regex or reason", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } if (!has_match_type) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "spamfilter::match-type"); errors++; } - if (!has_match_type && !has_match && has_action && has_target) - { - need_34_upgrade = 1; - } - if (match && !strcmp(match, "^LOL! //echo -a \\$\\(\\$decode\\(.+,m\\),[0-9]\\)$")) { config_warn("*** IMPORTANT ***"); @@ -449,47 +456,47 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) ConfigEntry *cep; ConfigEntry *cepp; char *word = NULL; - time_t bantime = (SPAMFILTER_BAN_TIME ? SPAMFILTER_BAN_TIME : 86400); - char *banreason = ""; + time_t bantime = tempiConf.spamfilter_ban_time; + char *banreason = tempiConf.spamfilter_ban_reason; int action = 0, target = 0; int match_type = 0; Match *m; /* We are only interested in spamfilter { } blocks */ - if ((type != CONFIG_MAIN) || strcmp(ce->ce_varname, "spamfilter")) + if ((type != CONFIG_MAIN) || strcmp(ce->name, "spamfilter")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "match")) + if (!strcmp(cep->name, "match")) { - word = cep->ce_vardata; + word = cep->value; } - else if (!strcmp(cep->ce_varname, "target")) + else if (!strcmp(cep->name, "target")) { - if (cep->ce_vardata) - target = spamfilter_getconftargets(cep->ce_vardata); + if (cep->value) + target = spamfilter_getconftargets(cep->value); else { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - target |= spamfilter_getconftargets(cepp->ce_varname); + for (cepp = cep->items; cepp; cepp = cepp->next) + target |= spamfilter_getconftargets(cepp->name); } } - else if (!strcmp(cep->ce_varname, "action")) + else if (!strcmp(cep->name, "action")) { - action = banact_stringtoval(cep->ce_vardata); + action = banact_stringtoval(cep->value); } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { - banreason = cep->ce_vardata; + banreason = cep->value; } - else if (!strcmp(cep->ce_varname, "ban-time")) + else if (!strcmp(cep->name, "ban-time")) { - bantime = config_checkval(cep->ce_vardata, CFG_TIME); + bantime = config_checkval(cep->value, CFG_TIME); } - else if (!strcmp(cep->ce_varname, "match-type")) + else if (!strcmp(cep->name, "match-type")) { - match_type = unreal_match_method_strtoval(cep->ce_vardata); + match_type = unreal_match_method_strtoval(cep->value); } } @@ -518,35 +525,35 @@ int tkl_config_test_ban(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type != CONFIG_BAN) return 0; - if (strcmp(ce->ce_vardata, "nick") && strcmp(ce->ce_vardata, "user") && - strcmp(ce->ce_vardata, "ip")) + if (strcmp(ce->value, "nick") && strcmp(ce->value, "user") && + strcmp(ce->value, "ip")) { return 0; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "ban")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { if (has_mask) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "ban::mask"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "ban::mask"); continue; } has_mask = 1; } - else if (!strcmp(cep->ce_varname, "reason")) + else if (!strcmp(cep->name, "reason")) { if (has_reason) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "ban::reason"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "ban::reason"); continue; } has_reason = 1; @@ -554,23 +561,23 @@ int tkl_config_test_ban(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) else { config_error("%s:%i: unknown directive ban %s::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - ce->ce_vardata, - cep->ce_varname); + cep->file->filename, cep->line_number, + ce->value, + cep->name); errors++; } } if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "ban::mask"); errors++; } if (!has_reason) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "ban::reason"); errors++; } @@ -592,44 +599,35 @@ int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) if (configtype != CONFIG_BAN) return 0; - if (strcmp(ce->ce_vardata, "nick") && strcmp(ce->ce_vardata, "user") && - strcmp(ce->ce_vardata, "ip")) + if (strcmp(ce->value, "nick") && strcmp(ce->value, "user") && + strcmp(ce->value, "ip")) { return 0; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - char buf[512], *p; - strlcpy(buf, cep->ce_vardata, sizeof(buf)); - if (is_extended_ban(buf)) + if (is_extended_server_ban(cep->value)) { - char *str; - Extban *extban; - char buf2[BUFSIZE]; - extban = findmod_by_bantype(buf[1]); - if (!extban || !(extban->options & EXTBOPT_TKL)) + char mask1buf[512], mask2buf[512]; + char *err = NULL; + + if (!parse_extended_server_ban(cep->value, NULL, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf))) { - config_warn("%s:%d: Invalid or unsupported extended server ban requested: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, buf); + config_warn("%s:%d: Could not add extended server ban '%s': %s", + cep->file->filename, cep->line_number, cep->value, err); goto tcrb_end; } - /* is_ok() is not called, since there is no client, similar to like remote bans set */ - str = extban->conv_param(buf); - if (!str || (strlen(str) <= 4)) - { - config_warn("%s:%d: Extended server ban has a problem: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, buf); - goto tcrb_end; - } - strlcpy(buf2, str+3, sizeof(buf2)); - buf[3] = '\0'; - safe_strdup(usermask, buf); /* eg ~S: */ - safe_strdup(hostmask, buf2); + safe_strdup(usermask, mask1buf); + safe_strdup(hostmask, mask2buf); } else { + char buf[512]; + char *p; + + strlcpy(buf, cep->value, sizeof(buf)); p = strchr(buf, '@'); if (p) { @@ -637,13 +635,13 @@ int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) safe_strdup(usermask, buf); safe_strdup(hostmask, p); } else { - safe_strdup(hostmask, cep->ce_vardata); + safe_strdup(hostmask, cep->value); } } } else - if (!strcmp(cep->ce_varname, "reason")) + if (!strcmp(cep->name, "reason")) { - safe_strdup(reason, cep->ce_vardata); + safe_strdup(reason, cep->value); } } @@ -653,11 +651,11 @@ int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) if (!reason) safe_strdup(reason, "-"); - if (!strcmp(ce->ce_vardata, "nick")) + if (!strcmp(ce->value, "nick")) tkltype = TKL_NAME; - else if (!strcmp(ce->ce_vardata, "user")) + else if (!strcmp(ce->value, "user")) tkltype = TKL_KILL; - else if (!strcmp(ce->ce_vardata, "ip")) + else if (!strcmp(ce->value, "ip")) tkltype = TKL_ZAP; else abort(); /* impossible */ @@ -685,82 +683,82 @@ int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int return 0; /* These are the types that we handle */ - if (strcmp(ce->ce_vardata, "ban") && strcmp(ce->ce_vardata, "throttle") && - strcmp(ce->ce_vardata, "tkl") && strcmp(ce->ce_vardata, "blacklist") && - strcmp(ce->ce_vardata, "spamfilter")) + if (strcmp(ce->value, "ban") && strcmp(ce->value, "throttle") && + strcmp(ce->value, "tkl") && strcmp(ce->value, "blacklist") && + strcmp(ce->value, "spamfilter")) { return 0; } - if (!strcmp(ce->ce_vardata, "tkl")) + if (!strcmp(ce->value, "tkl")) { config_error("%s:%d: except tkl { } has been renamed to except ban { }", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); config_status("Please rename your block in the configuration file."); *errs = 1; return -1; } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - if (cep->ce_entries) + if (cep->items) { /* mask { *@1.1.1.1; *@2.2.2.2; *@3.3.3.3; }; */ - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - if (!cepp->ce_varname) + if (!cepp->name) { - config_error_empty(cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, "except ban", "mask"); + config_error_empty(cepp->file->filename, + cepp->line_number, "except ban", "mask"); errors++; continue; } has_mask = 1; } } else - if (cep->ce_vardata) + if (cep->value) { /* mask *@1.1.1.1; */ - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except ban", "mask"); + config_error_empty(cep->file->filename, + cep->line_number, "except ban", "mask"); errors++; continue; } has_mask = 1; } } else - if (!strcmp(cep->ce_varname, "type")) + if (!strcmp(cep->name, "type")) { - if (cep->ce_entries) + if (cep->items) { /* type { x; y; z; }; */ - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - if (!tkl_banexception_configname_to_chars(cepp->ce_varname)) + for (cepp = cep->items; cepp; cepp = cepp->next) + if (!tkl_banexception_configname_to_chars(cepp->name)) { config_error("%s:%d: except ban::type '%s' unknown. Must be one of: %s", - cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname, + cepp->file->filename, cepp->line_number, cepp->name, ALL_VALID_EXCEPTION_TYPES); errors++; } } else - if (cep->ce_vardata) + if (cep->value) { /* type x; */ - if (!tkl_banexception_configname_to_chars(cep->ce_vardata)) + if (!tkl_banexception_configname_to_chars(cep->value)) { config_error("%s:%d: except ban::type '%s' unknown. Must be one of: %s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata, + cep->file->filename, cep->line_number, cep->value, ALL_VALID_EXCEPTION_TYPES); errors++; } } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except", cep->ce_varname); + config_error_unknown(cep->file->filename, + cep->line_number, "except", cep->name); errors++; continue; } @@ -768,7 +766,7 @@ int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "except ban::mask"); errors++; } @@ -782,7 +780,10 @@ void config_create_tkl_except(char *mask, char *bantypes) char *usermask = NULL; char *hostmask = NULL; int soft = 0; - char buf[256], buf2[256], *p; + char buf[256]; + char mask1buf[512]; + char mask2buf[512]; + char *p; if (*mask == '%') { @@ -790,27 +791,16 @@ void config_create_tkl_except(char *mask, char *bantypes) mask++; } strlcpy(buf, mask, sizeof(buf)); - if (is_extended_ban(buf)) + if (is_extended_server_ban(buf)) { - char *str; - Extban *extban; - extban = findmod_by_bantype(buf[1]); - if (!extban || !(extban->options & EXTBOPT_TKL)) + char *err = NULL; + if (!parse_extended_server_ban(buf, NULL, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf))) { - config_warn("Invalid or unsupported extended server ban exemption requested: %s", buf); + config_warn("Could not add extended server ban '%s': %s", buf, err); return; } - /* is_ok() is not called, since there is no client, similar to like remote bans set */ - str = extban->conv_param(buf); - if (!str || (strlen(str) <= 4)) - { - config_warn("Extended server ban exemption has a problem: %s", buf); - return; - } - strlcpy(buf2, str+3, sizeof(buf2)); - buf[3] = '\0'; - usermask = buf; /* eg ~S: */ - hostmask = buf2; + usermask = mask1buf; + hostmask = mask2buf; } else { p = strchr(buf, '@'); @@ -827,7 +817,7 @@ void config_create_tkl_except(char *mask, char *bantypes) if ((*usermask == ':') || (*hostmask == ':')) { - config_error("Cannot add illegal ban '%s': for a given user@host neither" + config_error("Cannot add illegal ban '%s': for a given user@host - neither " "user nor host may start with a : character (semicolon)", mask); return; } @@ -846,9 +836,9 @@ int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype) return 0; /* These are the types that we handle */ - if (strcmp(ce->ce_vardata, "ban") && strcmp(ce->ce_vardata, "throttle") && - strcmp(ce->ce_vardata, "blacklist") && - strcmp(ce->ce_vardata, "spamfilter")) + if (strcmp(ce->value, "ban") && strcmp(ce->value, "throttle") && + strcmp(ce->value, "blacklist") && + strcmp(ce->value, "spamfilter")) { return 0; } @@ -856,23 +846,23 @@ int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype) *bantypes = '\0'; /* First configure all the types */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "type")) + if (!strcmp(cep->name, "type")) { - if (cep->ce_entries) + if (cep->items) { /* type { x; y; z; }; */ - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + for (cepp = cep->items; cepp; cepp = cepp->next) { - char *str = tkl_banexception_configname_to_chars(cepp->ce_varname); + char *str = tkl_banexception_configname_to_chars(cepp->name); strlcat(bantypes, str, sizeof(bantypes)); } } else - if (cep->ce_vardata) + if (cep->value) { /* type x; */ - char *str = tkl_banexception_configname_to_chars(cep->ce_vardata); + char *str = tkl_banexception_configname_to_chars(cep->value); strlcat(bantypes, str, sizeof(bantypes)); } } @@ -881,33 +871,33 @@ int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype) if (!*bantypes) { /* Default setting if no 'type' is specified: */ - if (!strcmp(ce->ce_vardata, "ban")) + if (!strcmp(ce->value, "ban")) strlcpy(bantypes, "kGzZs", sizeof(bantypes)); - else if (!strcmp(ce->ce_vardata, "throttle")) + else if (!strcmp(ce->value, "throttle")) strlcpy(bantypes, "c", sizeof(bantypes)); - else if (!strcmp(ce->ce_vardata, "blacklist")) + else if (!strcmp(ce->value, "blacklist")) strlcpy(bantypes, "b", sizeof(bantypes)); - else if (!strcmp(ce->ce_vardata, "spamfilter")) + else if (!strcmp(ce->value, "spamfilter")) strlcpy(bantypes, "f", sizeof(bantypes)); else abort(); /* someone can't code */ } /* Now walk through all mask entries */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - if (cep->ce_entries) + if (cep->items) { /* mask { *@1.1.1.1; *@2.2.2.2; *@3.3.3.3; }; */ - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - config_create_tkl_except(cepp->ce_varname, bantypes); + for (cepp = cep->items; cepp; cepp = cepp->next) + config_create_tkl_except(cepp->name, bantypes); } else - if (cep->ce_vardata) + if (cep->value) { /* mask *@1.1.1.1; */ - config_create_tkl_except(cep->ce_vardata, bantypes); + config_create_tkl_except(cep->value, bantypes); } } } @@ -923,12 +913,12 @@ int tkl_config_test_set(ConfigFile *cf, ConfigEntry *ce, int configtype, int *er if (configtype != CONFIG_SET) return 0; - if (!strcmp(ce->ce_varname, "max-stats-matches")) + if (!strcmp(ce->name, "max-stats-matches")) { - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: set::max-stats-matches: no value specified", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } // allow any other value, including 0 and negative. @@ -944,9 +934,9 @@ int tkl_config_run_set(ConfigFile *cf, ConfigEntry *ce, int configtype) if (configtype != CONFIG_SET) return 0; - if (!strcmp(ce->ce_varname, "max-stats-matches")) + if (!strcmp(ce->name, "max-stats-matches")) { - max_stats_matches = atoi(ce->ce_vardata); + max_stats_matches = atoi(ce->value); return 1; } @@ -982,7 +972,7 @@ CMD_FUNC(cmd_gline) if (parc == 1) { - char *parv[3]; + const char *parv[3]; parv[0] = NULL; parv[1] = "gline"; parv[2] = NULL; @@ -1008,7 +998,7 @@ CMD_FUNC(cmd_gzline) if (parc == 1) { - char *parv[3]; + const char *parv[3]; parv[0] = NULL; parv[1] = "gline"; /* (there's no /STATS gzline, it's included in /STATS gline output) */ parv[2] = NULL; @@ -1033,7 +1023,7 @@ CMD_FUNC(cmd_shun) if (parc == 1) { - char *parv[3]; + const char *parv[3]; parv[0] = NULL; parv[1] = "shun"; parv[2] = NULL; @@ -1049,8 +1039,8 @@ CMD_FUNC(cmd_shun) CMD_FUNC(cmd_tempshun) { Client *target; - char *comment = ((parc > 2) && !BadPtr(parv[2])) ? parv[2] : "no reason"; - char *name; + const char *comment = ((parc > 2) && !BadPtr(parv[2])) ? parv[2] : "no reason"; + const char *name; int remove = 0; if (MyUser(client) && (!ValidatePermissionsForPath("server-ban:shun:temporary",client,NULL,NULL,NULL))) @@ -1072,7 +1062,7 @@ CMD_FUNC(cmd_tempshun) } else name = parv[1]; - target = find_person(name, NULL); + target = find_user(name, NULL); if (!target) { sendnumeric(client, ERR_NOSUCHNICK, name); @@ -1095,10 +1085,10 @@ CMD_FUNC(cmd_tempshun) } else { SetShunned(target); - ircsnprintf(buf, sizeof(buf), "Temporary shun added on user %s (%s@%s) by %s [%s]", - target->name, target->user->username, target->user->realhost, - client->name, comment); - sendto_snomask_global(SNO_TKL, "%s", buf); + unreal_log(ULOG_INFO, "tkl", "TKL_ADD_TEMPSHUN", client, + "Temporary shun added on user $target.details [reason: $shun_reason] [by: $client]", + log_data_string("shun_reason", comment), + log_data_client("target", target)); } } else { if (!IsShunned(target)) @@ -1106,10 +1096,9 @@ CMD_FUNC(cmd_tempshun) sendnotice(client, "User '%s' is not shunned", target->name); } else { ClearShunned(target); - ircsnprintf(buf, sizeof(buf), "Removed temporary shun on user %s (%s@%s) by %s", - target->name, target->user->username, target->user->realhost, - client->name); - sendto_snomask_global(SNO_TKL, "%s", buf); + unreal_log(ULOG_INFO, "tkl", "TKL_DEL_TEMPSHUN", client, + "Temporary shun removed from user $target.details [by: $client]", + log_data_client("target", target)); } } } @@ -1130,7 +1119,7 @@ CMD_FUNC(cmd_kline) if (parc == 1) { - char *parv[3]; + const char *parv[3]; parv[0] = NULL; parv[1] = "kline"; parv[2] = NULL; @@ -1184,7 +1173,7 @@ void tkl_general_stats(Client *client) /** ZLINE - Kill a user as soon as it tries to connect to the server. * This happens before any DNS/ident lookups have been done and - * before any data has been processed (including no SSL/TLS handshake, etc.) + * before any data has been processed (including no TLS handshake, etc.) */ CMD_FUNC(cmd_zline) { @@ -1199,7 +1188,7 @@ CMD_FUNC(cmd_zline) if (parc == 1) { - char *parv[3]; + const char *parv[3]; parv[0] = NULL; parv[1] = "kline"; /* (there's no /STATS zline, it's included in /STATS kline output) */ parv[2] = NULL; @@ -1276,22 +1265,142 @@ static int xline_exists(char *type, char *usermask, char *hostmask) return find_tkl_serverban(tpe, umask, hostmask, softban) ? 1 : 0; } +/** Parse an extended server ban such as ~S:aabbccddetc.. + * Used for both syntax checking and to split it into userbuf/hostbuf for TKL protocol. + * @param mask_in The input mask (eg: ~S:aabbccddetc) + * @param client Client doing the request (used to send errors), can be NULL. + * @param error Pointer to set to the error buffer (must be set!) + * @param skip_checking Set this to 1 if coming from a remote user/server to skip the .is_ok() check. + * Note that a .conv_param() call can still fail. + * @param buf1 Buffer to store the extban starter in (eg "~S:") -- can be NULL if you don't need it + * @param buf1len Length of buf1 + * @param buf2 Buffer to store the extban remainder in (eg "aabbccddetc") -- can be NULL if you don't need it + * @param buf2len Length of buf2 + * @returns 1 if the server ban is acceptable. The ban will then be stored in buf1/buf2 (unless those + * were set to NULL by the caller). On failure we return 0 and 'error' is set appropriately. + */ +int parse_extended_server_ban(const char *mask_in, Client *client, char **error, int skip_checking, char *buf1, size_t buf1len, char *buf2, size_t buf2len) +{ + const char *nextbanstr = NULL; + Extban *extban; + const char *str; + char *p; + BanContext *b = NULL; + char mask[USERLEN + NICKLEN + HOSTLEN + 32]; // same as extban_conv_param_nuh_or_extban() + char newmask[USERLEN + NICKLEN + HOSTLEN + 32]; + char soft_ban = 0; + + *error = NULL; + if (buf1 && buf2) + *buf1 = *buf2 = '\0'; + + /* Work on a copy */ + if (*mask_in == '%') + { + strlcpy(mask, mask_in+1, sizeof(mask)); + soft_ban = 1; + } else { + strlcpy(mask, mask_in, sizeof(mask)); + } + + extban = findmod_by_bantype(mask, &nextbanstr); + if (!extban || !(extban->options & EXTBOPT_TKL)) + { + *error = "Invalid or unsupported extended server ban requested. Valid types are for example ~a, ~r, ~S."; + goto fail_parse_extended_server_ban; + } + + b = safe_alloc(sizeof(BanContext)); + b->client = client; + b->banstr = nextbanstr; + b->is_ok_check = EXBCHK_PARAM; + b->what = MODE_ADD; + b->ban_type = EXBTYPE_TKL; + + /* Run .is_ok() for the extban. This check is skipped if coming from a remote user/server */ + if (skip_checking == 0) + { + if (extban->is_ok && !extban->is_ok(b)) + { + *error = "Invalid extended server ban"; + goto fail_parse_extended_server_ban; + } + } + + b->banstr = nextbanstr; + str = extban->conv_param(b, extban); + if (!str) + { + *error = "Invalid extended server ban"; + goto fail_parse_extended_server_ban; + } + str = prefix_with_extban(str, b, extban, newmask, sizeof(newmask)); + if (str == NULL) + { + *error = "Unexpected error (1)"; + goto fail_parse_extended_server_ban; + } + + p = strchr(newmask, ':'); + if (!p) + { + *error = "Unexpected error (2)"; + goto fail_parse_extended_server_ban; + } + + if (p[1] == ':') + { + *error = "For technical reasons you cannot use a double : at the beginning of an extended server ban (eg ~a::xyz)"; + goto fail_parse_extended_server_ban; + } + + if (!p[1]) + { + *error = "Empty / too short extended server ban"; + goto fail_parse_extended_server_ban; + } + + /* Now convert the result into two buffers for TKL protocol usage */ + if (buf1 && buf2) + { + char save; + p++; + save = *p; + *p = '\0'; + /* First buffer is eg ~S: or %~S: */ + snprintf(buf1, buf1len, "%s%s", + soft_ban ? "%" : "", + newmask); + *p = save; + strlcpy(buf2, p, buf2len); /* eg 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef */ + } + safe_free(b); + return 1; + +fail_parse_extended_server_ban: + safe_free(b); + return 0; +} + + /** Intermediate layer between user functions such as KLINE/GLINE * and the TKL layer (cmd_tkl). * This allows us doing some syntax checking and other helpful * things that are the same for many types of *LINES. */ -void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) +void cmd_tkl_line(Client *client, int parc, const char *parv[], char *type) { time_t secs; - int whattodo = 0; /* 0 = add 1 = del */ + int add = 1; time_t i; Client *acptr = NULL; - char *mask = NULL; + char maskbuf[BUFSIZE]; + char *mask; char mo[64], mo2[64]; + char mask1buf[BUFSIZE]; char mask2buf[BUFSIZE]; char *p, *usermask, *hostmask; - char *tkllayer[10] = { + const char *tkllayer[10] = { me.name, /*0 server.name */ NULL, /*1 +|- */ NULL, /*2 G */ @@ -1308,15 +1417,16 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) if ((parc == 1) || BadPtr(parv[1])) return; /* shouldn't happen */ - mask = parv[1]; + strlcpy(maskbuf, parv[1], sizeof(maskbuf)); + mask = maskbuf; if (*mask == '-') { - whattodo = 1; + add = 0; mask++; } else if (*mask == '+') { - whattodo = 0; + add = 1; mask++; } @@ -1345,57 +1455,42 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) } /* Check if it's an extended server ban */ - if (is_extended_ban(mask)) + if (is_extended_server_ban(mask)) { - if (whattodo == 0) - { - /* Add */ - char *str; - Extban *extban; - extban = findmod_by_bantype(mask[1]); - if (!extban || !(extban->options & EXTBOPT_TKL)) - { - sendnotice(client, "Invalid or unsupported extended server ban requested: %s", mask); - sendnotice(client, "Valid types are for example ~a, ~r, ~S"); - return; - } - if (extban->is_ok && !extban->is_ok(client, NULL, mask, EXBCHK_PARAM, MODE_ADD, EXBTYPE_TKL)) - return; /* rejected */ - str = extban->conv_param(mask); - if (!str || (strlen(str) <= 4)) - return; /* rejected */ - strlcpy(mask2buf, str+3, sizeof(mask2buf)); - mask[3] = '\0'; - usermask = mask; /* eg ~S: */ - hostmask = mask2buf; + char *err; - if (((*type == 'z') || (*type == 'Z'))) + if (!parse_extended_server_ban(mask, client, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf))) + { + /* If adding, reject it */ + if (add) { - sendnotice(client, "ERROR: (g)zlines must be placed at *@\037IPMASK\037. " - "Extended server bans don't work here because (g)zlines are processed" - "BEFORE dns and ident lookups are done and before reading any client data. " - "If you want to use extended server bans then use a KLINE/GLINE instead."); + sendnotice(client, "ERROR: %s", err); return; + } else + { + /* Always allow any removal attempt... */ + char *p; + char save; + p = strchr(mask, ':'); + p++; + save = *p; + *p = '\0'; + strlcpy(mask1buf, mask, sizeof(mask1buf)); + *p = save; + strlcpy(mask2buf, p, sizeof(mask2buf)); + /* fallthrough */ } - } else { - /* Delete: allow any attempt */ - strlcpy(mask2buf, mask+3, sizeof(mask2buf)); - mask[3] = '\0'; - usermask = mask; /* eg ~S: */ - hostmask = mask2buf; } - /* Make sure we don't screw up S2S traffic ;) */ - if (*hostmask == ':') + if (add && ((*type == 'z') || (*type == 'Z'))) { - sendnotice(client, "[error] For technical reasons you cannot use double :: at the beginning " - "of an extended server ban (eg ~a::xyz). You probably don't want to do this either."); - return; - } - if (!*hostmask) - { - sendnotice(client, "[error] Empty hostmask encountered, eg -~S:"); + sendnotice(client, "ERROR: (g)zlines must be placed at *@\037IPMASK\037. " + "Extended server bans don't work here because (g)zlines are processed" + "BEFORE dns and ident lookups are done and before reading any client data. " + "If you want to use extended server bans then use a KLINE/GLINE instead."); return; } + usermask = mask1buf; /* eg ~S: */ + hostmask = mask2buf; /* eg 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef */ } else { /* Check if it's a hostmask and legal .. */ @@ -1420,7 +1515,7 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) sendnotice(client, "[error] For technical reasons you cannot start the host with a ':', sorry"); return; } - if (((*type == 'z') || (*type == 'Z')) && !whattodo) + if (add && ((*type == 'z') || (*type == 'Z'))) { /* It's a (G)ZLINE, make sure the user isn't specyfing a HOST. * Just a warning in 3.2.3, but an error in 3.2.4. @@ -1446,12 +1541,12 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) else { /* It's seemingly a nick .. let's see if we can find the user */ - if ((acptr = find_person(mask, NULL))) + if ((acptr = find_user(mask, NULL))) { BanAction action = BAN_ACT_KLINE; // just a dummy default if ((*type == 'z') || (*type == 'Z')) action = BAN_ACT_ZLINE; // to indicate zline (no hostname, no dns, etc) - ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, &usermask, &hostmask); + ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, (const char **)&usermask, (const char **)&hostmask); } else { @@ -1461,7 +1556,7 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) } } - if (!whattodo && ban_too_broad(usermask, hostmask)) + if (add && ban_too_broad(usermask, hostmask)) { sendnotice(client, "*** [error] Too broad mask"); return; @@ -1469,7 +1564,7 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) secs = 0; - if (whattodo == 0 && (parc > 3)) + if (add && (parc > 3)) { secs = config_checkval(parv[2], CFG_TIME); if (secs < 0) @@ -1478,12 +1573,12 @@ void cmd_tkl_line(Client *client, int parc, char *parv[], char *type) return; } } - tkllayer[1] = whattodo == 0 ? "+" : "-"; + tkllayer[1] = add ? "+" : "-"; tkllayer[2] = type; tkllayer[3] = usermask; tkllayer[4] = hostmask; tkllayer[5] = make_nick_user_host(client->name, client->user->username, GetHost(client)); - if (whattodo == 0) + if (add) { if (secs == 0) { @@ -1557,7 +1652,7 @@ void eline_syntax(Client *client) * exception to be placed on *@ip rather than * user@host or *@host. For eg zlines. */ -TKLTypeTable *eline_type_requires_ip(char *bantypes) +TKLTypeTable *eline_type_requires_ip(const char *bantypes) { int i; @@ -1568,9 +1663,9 @@ TKLTypeTable *eline_type_requires_ip(char *bantypes) } /** Checks a string to see if it contains invalid ban exception types */ -int contains_invalid_server_ban_exception_type(char *str, char *c) +int contains_invalid_server_ban_exception_type(const char *str, char *c) { - char *p; + const char *p; for (p = str; *p; p++) { if (!tkl_banexception_chartotype(*p)) @@ -1589,9 +1684,12 @@ CMD_FUNC(cmd_eline) Client *acptr = NULL; char *mask = NULL; char mo[64], mo2[64]; + char maskbuf[BUFSIZE]; + char mask1buf[BUFSIZE]; char mask2buf[BUFSIZE]; - char *p, *usermask, *hostmask, *bantypes=NULL, *reason=NULL; - char *tkllayer[11] = { + const char *p, *bantypes=NULL, *reason=NULL; + char *usermask, *hostmask; + const char *tkllayer[11] = { me.name, /*0 server.name */ NULL, /*1 +|- */ NULL, /*2 E */ @@ -1625,7 +1723,8 @@ CMD_FUNC(cmd_eline) return; } - mask = parv[1]; + strlcpy(maskbuf, parv[1], sizeof(maskbuf)); + mask = maskbuf; if (*mask == '-') { add = 0; @@ -1665,55 +1764,40 @@ CMD_FUNC(cmd_eline) return; /* Check if it's an extended server ban */ - if (is_extended_ban(mask)) + if (is_extended_server_ban(mask)) { - if (add) + char *err; + if (!parse_extended_server_ban(mask, client, &err, 0, mask1buf, sizeof(mask1buf), mask2buf, sizeof(mask2buf))) { - /* Add */ - char *str; - Extban *extban; - extban = findmod_by_bantype(mask[1]); - if (!extban || !(extban->options & EXTBOPT_TKL)) + /* If adding, reject it */ + if (add) { - sendnotice(client, "Invalid or unsupported extended server ban requested: %s", mask); - sendnotice(client, "Valid types are for example ~a, ~r, ~S"); + sendnotice(client, "ERROR: %s", err); return; - } - if (extban->is_ok && !extban->is_ok(client, NULL, mask, EXBCHK_PARAM, MODE_ADD, EXBTYPE_TKL)) - return; /* rejected */ - str = extban->conv_param(mask); - if (!str || (strlen(str) <= 4)) - return; /* rejected */ - strlcpy(mask2buf, str+3, sizeof(mask2buf)); - mask[3] = '\0'; - usermask = mask; /* eg ~S: */ - hostmask = mask2buf; - if ((t = eline_type_requires_ip(bantypes))) + } else { - sendnotice(client, "ERROR: Ban exception with type '%c' does not work on extended server bans. " - "This is because checking for %s takes places BEFORE " - "extended bans can be checked.", t->letter, t->log_name); - return; + /* Always allow any removal attempt... */ + char *p; + char save; + p = strchr(mask, ':'); + p++; + save = *p; + *p = '\0'; + strlcpy(mask1buf, mask, sizeof(mask1buf)); + *p = save; + strlcpy(mask2buf, p, sizeof(mask2buf)); + /* fallthrough */ } - } else { - /* Delete: allow any attempt */ - strlcpy(mask2buf, mask+3, sizeof(mask2buf)); - mask[3] = '\0'; - usermask = mask; /* eg ~S: */ - hostmask = mask2buf; } - /* Make sure we don't screw up S2S traffic ;) */ - if (*hostmask == ':') + if (add && (t = eline_type_requires_ip(bantypes))) { - sendnotice(client, "[error] For technical reasons you cannot use double :: at the beginning " - "of an extended server ban (eg ~a::xyz). You probably don't want to do this either."); - return; - } - if (!*hostmask) - { - sendnotice(client, "[error] Empty hostmask encountered, eg -~S:"); + sendnotice(client, "ERROR: Ban exception with type '%c' does not work on extended server bans. " + "This is because checking for %s takes places BEFORE " + "extended bans can be checked.", t->letter, t->log_name); return; } + usermask = mask1buf; /* eg ~S: */ + hostmask = mask2buf; /* eg 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef */ } else { /* Check if it's a hostmask and legal .. */ @@ -1769,12 +1853,12 @@ CMD_FUNC(cmd_eline) else { /* It's seemingly a nick .. let's see if we can find the user */ - if ((acptr = find_person(mask, NULL))) + if ((acptr = find_user(mask, NULL))) { BanAction action = BAN_ACT_KLINE; // just a dummy default if (add && eline_type_requires_ip(bantypes)) action = BAN_ACT_ZLINE; // to indicate zline (no hostname, no dns, etc) - ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, &usermask, &hostmask); + ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, (const char **)&usermask, (const char **)&hostmask); } else { @@ -1844,7 +1928,7 @@ void spamfilter_usage(Client *client) } /** Helper function for cmd_spamfilter, explaining usage has changed. */ -void spamfilter_new_usage(Client *client, char *parv[]) +void spamfilter_new_usage(Client *client, const char *parv[]) { sendnotice(client, "Unknown match-type '%s'. Must be one of: -regex (new fast PCRE regexes) or " "-simple (simple text with ? and * wildcards)", @@ -1857,13 +1941,13 @@ void spamfilter_new_usage(Client *client, char *parv[]) } /** Delete a spamfilter by ID (the ID can be obtained via '/SPAMFILTER del' */ -void spamfilter_del_by_id(Client *client, char *id) +void spamfilter_del_by_id(Client *client, const char *id) { int index; TKL *tk; int found = 0; char mo[32], mo2[32]; - char *tkllayer[13] = { + const char *tkllayer[13] = { me.name, /* 0 server.name */ NULL, /* 1 +|- */ "F", /* 2 F */ @@ -1924,9 +2008,9 @@ void spamfilter_del_by_id(Client *client, char *id) */ CMD_FUNC(cmd_spamfilter) { - int whattodo = 0; /* 0 = add 1 = del */ + int add = 1; char mo[32], mo2[32]; - char *tkllayer[13] = { + const char *tkllayer[13] = { me.name, /* 0 server.name */ NULL, /* 1 +|- */ "F", /* 2 F */ @@ -1960,7 +2044,7 @@ CMD_FUNC(cmd_spamfilter) if (parc == 1) { - char *parv[3]; + const char *parv[3]; parv[0] = NULL; parv[1] = "spamfilter"; parv[2] = NULL; @@ -1973,7 +2057,7 @@ CMD_FUNC(cmd_spamfilter) if (!parv[2]) { /* Show STATS with appropriate SPAMFILTER del command */ - char *parv[5]; + const char *parv[5]; parv[0] = NULL; parv[1] = "spamfilter"; parv[2] = me.name; @@ -2007,9 +2091,9 @@ CMD_FUNC(cmd_spamfilter) * parv[7]: regex */ if (!strcasecmp(parv[1], "add") || !strcmp(parv[1], "+")) - whattodo = 0; + add = 1; else if (!strcasecmp(parv[1], "del") || !strcmp(parv[1], "-") || !strcasecmp(parv[1], "remove")) - whattodo = 1; + add = 0; else { sendnotice(client, "1st parameter invalid"); @@ -2017,7 +2101,7 @@ CMD_FUNC(cmd_spamfilter) return; } - if ((whattodo == 0) && !strcasecmp(parv[2]+1, "posix")) + if (add && !strcasecmp(parv[2]+1, "posix")) { sendnotice(client, "ERROR: Spamfilter type 'posix' is DEPRECATED. You must use type 'regex' instead."); sendnotice(client, "See https://www.unrealircd.org/docs/FAQ#spamfilter-posix-deprecated"); @@ -2050,7 +2134,7 @@ CMD_FUNC(cmd_spamfilter) actionbuf[0] = banact_valtochar(action); actionbuf[1] = '\0'; - if (whattodo == 0) + if (add) { /* now check the regex / match field... */ m = unreal_create_match(match_type, parv[7], &err); @@ -2062,7 +2146,7 @@ CMD_FUNC(cmd_spamfilter) unreal_delete_match(m); } - tkllayer[1] = whattodo ? "-" : "+"; + tkllayer[1] = add ? "+" : "-"; tkllayer[3] = targetbuf; tkllayer[4] = actionbuf; tkllayer[5] = make_nick_user_host(client->name, client->user->username, GetHost(client)); @@ -2093,14 +2177,14 @@ CMD_FUNC(cmd_spamfilter) * on 50 characters for the rest... -- Syzop */ n = strlen(reason) + strlen(parv[7]) + strlen(tkllayer[6]) + (NICKLEN * 2) + 40; - if ((n > 500) && (whattodo == 0)) + if ((n > 500) && add) { sendnotice(client, "Sorry, spamfilter too long. You'll either have to trim down the " "reason or the regex (exceeded by %d bytes)", n - 500); return; } - if (whattodo == 0) + if (add) { ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); tkllayer[7] = mo2; @@ -2123,8 +2207,9 @@ int _tkl_hash(unsigned int c) else if ((c >= 'A') && (c <= 'Z')) return c-'A'; else { - sendto_realops("[BUG] tkl_hash() called with out of range parameter (c = '%c') !!!", c); - ircd_log(LOG_ERROR, "[BUG] tkl_hash() called with out of range parameter (c = '%c') !!!", c); + unreal_log(ULOG_ERROR, "bug", "TKL_HASH_INVALID", NULL, + "tkl_hash() called with out of range parameter (c = '$tkl_char') !!!", + log_data_char("tkl_char", c)); return 0; } #else @@ -2141,8 +2226,9 @@ char _tkl_typetochar(int type) for (i=0; tkl_types[i].config_name; i++) if ((tkl_types[i].type == type) && tkl_types[i].tkltype) return tkl_types[i].letter; - sendto_realops("[BUG]: tkl_typetochar(): unknown type 0x%x !!!", type); - ircd_log(LOG_ERROR, "[BUG] tkl_typetochar(): unknown type 0x%x !!!", type); + unreal_log(ULOG_ERROR, "bug", "TKL_TYPETOCHAR_INVALID", NULL, + "tkl_typetochar(): unknown type $tkl_type!!!", + log_data_integer("tkl_type", type)); return 0; } @@ -2201,13 +2287,13 @@ char *tkl_banexception_configname_to_chars(char *name) char *_tkl_type_string(TKL *tkl) { static char txt[256]; + int i; *txt = '\0'; if (TKLIsServerBan(tkl) && (tkl->ptr.serverban->subtype == TKL_SUBTYPE_SOFT)) strlcpy(txt, "Soft ", sizeof(txt)); - int i; for (i=0; tkl_types[i].config_name; i++) { if ((tkl_types[i].type == tkl->type) && tkl_types[i].tkltype) @@ -2221,6 +2307,18 @@ char *_tkl_type_string(TKL *tkl) return txt; } +/** Short config string, lowercase alnum with possibly hyphens (eg: 'kline') */ +char *_tkl_type_config_string(TKL *tkl) +{ + int i; + + for (i=0; tkl_types[i].config_name; i++) + if ((tkl_types[i].type == tkl->type) && tkl_types[i].tkltype) + return tkl_types[i].config_name; + + return "???"; +} + int tkl_banexception_matches_type(TKL *except, int bantype) { char *p; @@ -2632,18 +2730,10 @@ void _tkl_del_line(TKL *tkl) } if (!really_found) { - ircd_log(LOG_ERROR, "[BUG] [Crash] tkl_del_line() for %s (%d): " - "NOT found in tklines_ip_hash[%d][%d], " - "this should never happen!", - tkl_type_string(tkl), - tkl->type, - index, index2); - if (TKLIsServerBan(tkl)) - { - ircd_log(LOG_ERROR, "Additional information: the ban was on %s@%s", - tkl->ptr.serverban->usermask ? tkl->ptr.serverban->usermask : "", - tkl->ptr.serverban->hostmask ? tkl->ptr.serverban->hostmask : ""); - } + unreal_log(ULOG_FATAL, "tkl", "BUG_TKL_DEL_LINE_HASH", NULL, + "[BUG] [Crash] tkl_del_line() for $tkl (type: $tkl.type_string): " + "NOT found in tklines_ip_hash. This should never happen!", + log_data_tkl("tkl", tkl)); abort(); } #endif @@ -2672,7 +2762,7 @@ static void add_default_exempts(void) * Currently the list is: gline, kline, gzline, zline, shun, blacklist, * connect-flood, handshake-data-flood. */ - tkl_add_banexception(TKL_EXCEPTION, "*", "127.*", "localhost is always exempt", + tkl_add_banexception(TKL_EXCEPTION, "*", "127.0.0.0/8", "localhost is always exempt", "-default-", 0, TStime(), 0, "GkZzsbcd", TKL_FLAG_CONFIG); } @@ -2720,7 +2810,7 @@ void _tkl_check_local_remove_shun(TKL *tmp) */ keep_shun = 0; for(tk = tklines[tkl_hash('s')]; tk && !keep_shun; tk = tk->next) - if(tk != tmp && match_simple(tk->ptr.serverban->usermask, cname)) + if (tk != tmp && match_simple(tk->ptr.serverban->usermask, cname)) { if ((*tk->ptr.serverban->hostmask >= '0') && (*tk->ptr.serverban->hostmask <= '9') /* the hostmask is an IP */ @@ -2732,7 +2822,7 @@ void _tkl_check_local_remove_shun(TKL *tmp) keep_shun = 1; } - if(!keep_shun) + if (!keep_shun) { ClearShunned(client); } @@ -2746,11 +2836,11 @@ void _tkl_check_local_remove_shun(TKL *tmp) * that can be used in oper notices like expiring kline, added kline, etc. */ #define NO_SOFT_PREFIX 1 -char *tkl_uhost(TKL *tkl, char *buf, size_t buflen, int options) +char *_tkl_uhost(TKL *tkl, char *buf, size_t buflen, int options) { if (TKLIsServerBan(tkl)) { - if (is_extended_ban(tkl->ptr.serverban->usermask)) + if (is_extended_server_ban(tkl->ptr.serverban->usermask)) { ircsnprintf(buf, buflen, "%s%s%s", (!(options & NO_SOFT_PREFIX) && (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT)) ? "%" : "", @@ -2763,7 +2853,7 @@ char *tkl_uhost(TKL *tkl, char *buf, size_t buflen, int options) } else if (TKLIsBanException(tkl)) { - if (is_extended_ban(tkl->ptr.banexception->usermask)) + if (is_extended_server_ban(tkl->ptr.banexception->usermask)) { ircsnprintf(buf, buflen, "%s%s%s", (!(options & NO_SOFT_PREFIX) && (tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT)) ? "%" : "", @@ -2784,60 +2874,33 @@ char *tkl_uhost(TKL *tkl, char *buf, size_t buflen, int options) */ void tkl_expire_entry(TKL *tkl) { - char *whattype = tkl_type_string(tkl); - - if (!tkl) - return; - - if (tkl->type & TKL_SPAMF) - { - /* Impossible */ - } else if (TKLIsServerBan(tkl)) { - char uhostbuf[BUFSIZE]; - char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); - sendto_snomask(SNO_TKL, - "*** Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, uhost, tkl->set_by, tkl->ptr.serverban->reason, - (long long)(TStime() - tkl->set_at)); - ircd_log - (LOG_TKL, "Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, uhost, tkl->set_by, tkl->ptr.serverban->reason, - (long long)(TStime() - tkl->set_at)); + unreal_log(ULOG_INFO, "tkl", "TKL_EXPIRE", NULL, + "Expiring $tkl.type_string '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", + log_data_tkl("tkl", tkl)); } else if (TKLIsNameBan(tkl)) { if (!tkl->ptr.nameban->hold) { - sendto_snomask(SNO_TKL, - "*** Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, tkl->ptr.nameban->name, tkl->set_by, tkl->ptr.nameban->reason, - (long long)(TStime() - tkl->set_at)); - ircd_log - (LOG_TKL, "Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, tkl->ptr.nameban->name, tkl->set_by, tkl->ptr.nameban->reason, - (long long)(TStime() - tkl->set_at)); + unreal_log(ULOG_INFO, "tkl", "TKL_EXPIRE", NULL, + "Expiring $tkl.type_string '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", + log_data_tkl("tkl", tkl)); } } else if (TKLIsBanException(tkl)) { - char uhostbuf[BUFSIZE]; - char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); - sendto_snomask(SNO_TKL, - "*** Expiring %s (%s) for types '%s' made by %s (Reason: %s) set %lld seconds ago", - whattype, uhost, tkl->ptr.banexception->bantypes, tkl->set_by, tkl->ptr.banexception->reason, - (long long)(TStime() - tkl->set_at)); - ircd_log - (LOG_TKL, "Expiring %s (%s) for types '%s' made by %s (Reason: %s) set %lld seconds ago", - whattype, uhost, tkl->ptr.banexception->bantypes, tkl->set_by, tkl->ptr.banexception->reason, - (long long)(TStime() - tkl->set_at)); + unreal_log(ULOG_INFO, "tkl", "TKL_EXPIRE", NULL, + "Expiring $tkl.type_string '$tkl' [type: $tkl.exception_types] [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", + log_data_tkl("tkl", tkl)); } + // FIXME: so.. this isn't logged? or what? if (tkl->type & TKL_SHUN) tkl_check_local_remove_shun(tkl); - RunHook2(HOOKTYPE_TKL_DEL, NULL, tkl); + RunHook(HOOKTYPE_TKL_DEL, NULL, tkl); tkl_del_line(tkl); } @@ -3032,7 +3095,7 @@ int _find_tkline_match(Client *client, int skip_soft) /* User is banned... */ - RunHookReturnInt2(HOOKTYPE_FIND_TKLINE_MATCH, client, tkl, !=99); + RunHookReturnInt(HOOKTYPE_FIND_TKLINE_MATCH, !=99, client, tkl); if (tkl->type & TKL_KILL) { @@ -3161,45 +3224,13 @@ int spamfilter_check_users(TKL *tkl) continue; /* No match */ /* matched! */ - ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s: '%s'] [%s]", - client->name, client->user->username, client->user->realhost, - tkl->ptr.spamfilter->match->str, - "user", spamfilter_user, - unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); + unreal_log(ULOG_INFO, "tkl", "SPAMFILTER_MATCH", client, + "[Spamfilter] $client.details matches filter '$tkl': [cmd: $command: '$str'] [reason: $tkl.reason] [action: $tkl.ban_action]", + log_data_tkl("tkl", tkl), + log_data_string("command", "USER"), + log_data_string("str", spamfilter_user)); - sendto_snomask_global(SNO_SPAMF, "%s", buf); - ircd_log(LOG_SPAMFILTER, "%s", buf); - RunHook6(HOOKTYPE_LOCAL_SPAMFILTER, client, spamfilter_user, spamfilter_user, SPAMF_USER, NULL, tkl); - matches++; - } - } - - return matches; -} - -/** Similarly to previous, but match against all global users. - * FUNCTION IS UNUSED !! - */ -int spamfilter_check_all_users(Client *from, TKL *tkl) -{ - char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; /* n!u@h:r */ - int matches = 0; - Client *acptr; - - list_for_each_entry(acptr, &client_list, client_node) - { - if (IsUser(acptr)) - { - spamfilter_build_user_string(spamfilter_user, acptr->name, acptr); - if (!unreal_match(tkl->ptr.spamfilter->match, spamfilter_user)) - continue; /* No match */ - - /* matched! */ - sendnotice(from, "[Spamfilter] %s!%s@%s matches filter '%s': [%s: '%s'] [%s]", - acptr->name, acptr->user->username, acptr->user->realhost, - tkl->ptr.spamfilter->match->str, - "user", spamfilter_user, - unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); + RunHook(HOOKTYPE_LOCAL_SPAMFILTER, client, spamfilter_user, spamfilter_user, SPAMF_USER, NULL, tkl); matches++; } } @@ -3317,15 +3348,15 @@ TKL *_find_tkline_match_zap(Client *client) typedef struct { int flags; - char *mask; - char *reason; - char *set_by; + const char *mask; + const char *reason; + const char *set_by; } TKLFlag; /** Parse STATS tkl parameters. * TODO: I don't think this is documented anywhere? Or underdocumented at least. */ -static void parse_stats_params(char *para, TKLFlag *flag) +static void parse_stats_params(const char *para, TKLFlag *flag) { static char paratmp[512]; /* <- copy of para, because it gets fragged by strtok() */ char *flags, *tmp; @@ -3381,7 +3412,7 @@ static void parse_stats_params(char *para, TKLFlag *flag) /** Does this TKL entry match the search terms? * This is a helper function for tkl_stats(). */ -int tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, TKL *tkl) +int tkl_stats_matcher(Client *client, int type, const char *para, TKLFlag *tklflags, TKL *tkl) { /***** First, handle the selection ******/ @@ -3460,32 +3491,32 @@ int tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, T if (tkl->type == (TKL_KILL | TKL_GLOBAL)) { sendnumeric(client, RPL_STATSGLINE, 'G', uhost, - (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } else if (tkl->type == (TKL_ZAP | TKL_GLOBAL)) { sendnumeric(client, RPL_STATSGLINE, 'Z', uhost, - (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } else if (tkl->type == (TKL_SHUN | TKL_GLOBAL)) { sendnumeric(client, RPL_STATSGLINE, 's', uhost, - (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } else if (tkl->type == (TKL_KILL)) { sendnumeric(client, RPL_STATSGLINE, 'K', uhost, - (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } else if (tkl->type == (TKL_ZAP)) { sendnumeric(client, RPL_STATSGLINE, 'z', uhost, - (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } } else if (TKLIsSpamfilter(tkl)) @@ -3495,9 +3526,10 @@ int tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, T unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type), spamfilter_target_inttostring(tkl->ptr.spamfilter->target), banact_valtostring(tkl->ptr.spamfilter->action), - (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - TStime() - tkl->set_at, - tkl->ptr.spamfilter->tkl_duration, tkl->ptr.spamfilter->tkl_reason, + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), + (long long)tkl->ptr.spamfilter->tkl_duration, + tkl->ptr.spamfilter->tkl_reason, tkl->set_by, tkl->ptr.spamfilter->match->str); if (para && !strcasecmp(para, "del")) @@ -3515,9 +3547,13 @@ int tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, T } else if (TKLIsNameBan(tkl)) { - sendnumeric(client, RPL_STATSQLINE, (tkl->type & TKL_GLOBAL) ? 'Q' : 'q', - tkl->ptr.nameban->name, (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - TStime() - tkl->set_at, tkl->set_by, tkl->ptr.nameban->reason); + sendnumeric(client, RPL_STATSQLINE, + (tkl->type & TKL_GLOBAL) ? 'Q' : 'q', + tkl->ptr.nameban->name, + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), + tkl->set_by, + tkl->ptr.nameban->reason); } else if (TKLIsBanException(tkl)) { @@ -3525,8 +3561,8 @@ int tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, T char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); sendnumeric(client, RPL_STATSEXCEPTTKL, uhost, tkl->ptr.banexception->bantypes, - (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, - (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason); + (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, + (long long)(TStime() - tkl->set_at), tkl->set_by, tkl->ptr.banexception->reason); } else { /* That's weird, unknown TKL type */ @@ -3536,7 +3572,7 @@ int tkl_stats_matcher(Client *client, int type, char *para, TKLFlag *tklflags, T } /* TKL Stats. This is used by /STATS gline and all the others */ -void _tkl_stats(Client *client, int type, char *para, int *cnt) +void _tkl_stats(Client *client, int type, const char *para, int *cnt) { TKL *tk; TKLFlag tklflags; @@ -3661,8 +3697,10 @@ void tkl_sync_send_entry(int add, Client *sender, Client *to, TKL *tkl) tkl->ptr.banexception->reason); } else { - sendto_ops_and_log("[BUG] tkl_sync_send_entry() called, but unknown type %d/'%c'", - tkl->type, typ); + unreal_log(ULOG_FATAL, "tkl", "BUG_TKL_SYNC_SEND_ENTRY", NULL, + "[BUG] tkl_sync_send_entry() called for '%s' but unknown type: $tkl.type_string ($tkl_type_int)", + log_data_tkl("tkl", tkl), + log_data_integer("tkl_type_int", typ)); abort(); } } @@ -3809,138 +3847,81 @@ TKL *_find_tkl_spamfilter(int type, char *match_string, BanAction action, unsign /** Send a notice to opers about the TKL that is being added */ void _sendnotice_tkl_add(TKL *tkl) { - char buf[512]; - char set_at[128]; - char expire_at[128]; - char *tkl_type_str; /**< Eg: "K-Line" */ - /* Don't show notices for temporary nick holds (issued by services) */ if (TKLIsNameBan(tkl) && tkl->ptr.nameban->hold) return; - tkl_type_str = tkl_type_string(tkl); - - *buf = *set_at = *expire_at = '\0'; - short_date(tkl->set_at, set_at); - if (tkl->expire_at > 0) - short_date(tkl->expire_at, expire_at); - if (TKLIsServerBan(tkl)) { - char uhostbuf[BUFSIZE]; - char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); - if (tkl->expire_at != 0) - { - ircsnprintf(buf, sizeof(buf), "%s added for %s on %s GMT (from %s to expire at %s GMT: %s)", - tkl_type_str, uhost, - set_at, tkl->set_by, expire_at, tkl->ptr.serverban->reason); - } else { - ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s on %s GMT (from %s: %s)", - tkl_type_str, uhost, - set_at, tkl->set_by, tkl->ptr.serverban->reason); - } + unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, + "$tkl.type_string added: '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", + log_data_tkl("tkl", tkl)); } else if (TKLIsNameBan(tkl)) { - if (tkl->expire_at > 0) - { - ircsnprintf(buf, sizeof(buf), "%s added for %s on %s GMT (from %s to expire at %s GMT: %s)", - tkl_type_str, tkl->ptr.nameban->name, set_at, tkl->set_by, expire_at, tkl->ptr.nameban->reason); - } else { - ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s on %s GMT (from %s: %s)", - tkl_type_str, tkl->ptr.nameban->name, set_at, tkl->set_by, tkl->ptr.nameban->reason); - } + unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, + "$tkl.type_string added: '$tkl' [reason: $tkl.reason] [by: $tkl.set_by] [duration: $tkl.duration_string]", + log_data_tkl("tkl", tkl)); } else if (TKLIsSpamfilter(tkl)) { - /* Spamfilter */ - ircsnprintf(buf, sizeof(buf), - "Spamfilter added: '%s' [type: %s] [target: %s] [action: %s] [reason: %s] on %s GMT (from %s)", - tkl->ptr.spamfilter->match->str, - unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type), - spamfilter_target_inttostring(tkl->ptr.spamfilter->target), - banact_valtostring(tkl->ptr.spamfilter->action), - unreal_decodespace(tkl->ptr.spamfilter->tkl_reason), - set_at, - tkl->set_by); + unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, + "Spamfilter added: '$tkl' [type: $tkl.match_type] [targets: $tkl.spamfilter_targets] " + "[action: $tkl.ban_action] [reason: $tkl.reason] [by: $tkl.set_by]", + log_data_tkl("tkl", tkl)); } else if (TKLIsBanException(tkl)) { - char uhostbuf[BUFSIZE]; - char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); - if (tkl->expire_at != 0) - { - ircsnprintf(buf, sizeof(buf), "%s added for %s for types '%s' on %s GMT (from %s to expire at %s GMT: %s)", - tkl_type_str, uhost, - tkl->ptr.banexception->bantypes, - set_at, tkl->set_by, expire_at, tkl->ptr.banexception->reason); - } else { - ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s for types '%s' on %s GMT (from %s: %s)", - tkl_type_str, uhost, - tkl->ptr.banexception->bantypes, - set_at, tkl->set_by, tkl->ptr.banexception->reason); - } + unreal_log(ULOG_INFO, "tkl", "TKL_ADD", NULL, + "$tkl.type_string added: '$tkl' [types: $tkl.exception_types] [by: $tkl.set_by] [duration: $tkl.duration_string]", + log_data_tkl("tkl", tkl)); } else { - ircsnprintf(buf, sizeof(buf), "[BUG] %s added but type unhandled in sendnotice_tkl_add()!!!", tkl_type_str); + unreal_log(ULOG_ERROR, "tkl", "BUG_UNKNOWN_TKL", NULL, + "[BUG] TKL added of unknown type, unhandled in sendnotice_tkl_add()!!!!"); } - - sendto_snomask(SNO_TKL, "*** %s", buf); - ircd_log(LOG_TKL, "%s", buf); } /** Send a notice to opers about the TKL that is being deleted */ void _sendnotice_tkl_del(char *removed_by, TKL *tkl) { - char buf[512]; - char set_at[128]; - char *tkl_type_str; - /* Don't show notices for temporary nick holds (issued by services) */ if (TKLIsNameBan(tkl) && tkl->ptr.nameban->hold) return; - tkl_type_str = tkl_type_string(tkl); /* eg: "K-Line" */ - - *buf = *set_at = '\0'; - short_date(tkl->set_at, set_at); - if (TKLIsServerBan(tkl)) { - char uhostbuf[BUFSIZE]; - char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); - ircsnprintf(buf, sizeof(buf), - "%s removed %s %s (set at %s - reason: %s)", - removed_by, tkl_type_str, uhost, - set_at, tkl->ptr.serverban->reason); + unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, + "$tkl.type_string removed: '$tkl' [reason: $tkl.reason] [by: $removed_by] [set at: $tkl.set_at_string]", + log_data_tkl("tkl", tkl), + log_data_string("removed_by", removed_by)); } else if (TKLIsNameBan(tkl)) { - ircsnprintf(buf, sizeof(buf), - "%s removed %s %s (set at %s - reason: %s)", - removed_by, tkl_type_str, tkl->ptr.nameban->name, set_at, tkl->ptr.nameban->reason); + unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, + "$tkl.type_string removed: '$tkl' [reason: $tkl.reason] [by: $removed_by] [set at: $tkl.set_at_string]", + log_data_tkl("tkl", tkl), + log_data_string("removed_by", removed_by)); } else if (TKLIsSpamfilter(tkl)) { - ircsnprintf(buf, sizeof(buf), - "%s removed Spamfilter '%s' (set at %s)", - removed_by, tkl->ptr.spamfilter->match->str, set_at); + unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, + "Spamfilter removed: '$tkl' [type: $tkl.match_type] [targets: $tkl.spamfilter_targets] " + "[action: $tkl.ban_action] [reason: $tkl.reason] [by: $removed_by] [set at: $tkl.set_at_string]", + log_data_tkl("tkl", tkl), + log_data_string("removed_by", removed_by)); } else if (TKLIsBanException(tkl)) { - char uhostbuf[BUFSIZE]; - char *uhost = tkl_uhost(tkl, uhostbuf, sizeof(uhostbuf), 0); - ircsnprintf(buf, sizeof(buf), - "%s removed exception on %s (set at %s - reason: %s)", - removed_by, uhost, - set_at, tkl->ptr.banexception->reason); + unreal_log(ULOG_INFO, "tkl", "TKL_DEL", NULL, + "$tkl.type_string removed: '$tkl' [types: $tkl.exception_types] [by: $removed_by] [set at: $tkl.set_at_string]", + log_data_tkl("tkl", tkl), + log_data_string("removed_by", removed_by)); } else { - ircsnprintf(buf, sizeof(buf), "[BUG] %s added but type unhandled in sendnotice_tkl_del()!!!!!", tkl_type_str); + unreal_log(ULOG_ERROR, "tkl", "BUG_UNKNOWN_TKL", NULL, + "[BUG] TKL removed of unknown type, unhandled in sendnotice_tkl_del()!!!!"); } - - sendto_snomask(SNO_TKL, "*** %s", buf); - ircd_log(LOG_TKL, "%s", buf); } /** Add a TKL using the TKL layer. See cmd_tkl for parv[] and protocol documentation. */ @@ -3949,7 +3930,7 @@ CMD_FUNC(cmd_tkl_add) TKL *tkl; int type; time_t expire_at, set_at; - char *set_by; + const char *set_by; char tkl_entry_exists = 0; /* we rely on servers to be failsafe.. */ @@ -3977,14 +3958,18 @@ CMD_FUNC(cmd_tkl_add) /* Validate set and expiry time */ if ((set_at < 0) || !short_date(set_at, NULL)) { - sendto_realops("Invalid TKL entry from %s, set-at time is out of range (%lld) -- not added. Clock on other server incorrect or bogus entry.", - client->name, (long long)set_at); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "The set-at time is out of range ($set_at). Clock on other server incorrect or bogus entry.", + log_data_integer("set_at", set_at)); return; } if ((expire_at < 0) || !short_date(expire_at, NULL)) { - sendto_realops("Invalid TKL entry from %s, expiry time is out of range (%lld) -- not added. Clock on other server incorrect or bogus entry.", - client->name, (long long)expire_at); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "The expire-at time is out of range ($expire_at). Clock on other server incorrect or bogus entry.", + log_data_integer("expire_at", expire_at)); return; } @@ -3996,9 +3981,9 @@ CMD_FUNC(cmd_tkl_add) { /* Validate server ban TKL fields */ int softban = 0; - char *usermask = parv[3]; - char *hostmask = parv[4]; - char *reason = parv[8]; + const char *usermask = parv[3]; + const char *hostmask = parv[4]; + const char *reason = parv[8]; /* Some simple validation on usermask and hostmask: * may not contain an @. Yeah, some services or self-written @@ -4006,9 +3991,11 @@ CMD_FUNC(cmd_tkl_add) */ if (strchr(usermask, '@') || strchr(hostmask, '@')) { - sendto_realops("Ignoring TKL entry %s@%s from %s. " - "Invalid usermask '%s' or hostmask '%s'.", - usermask, hostmask, client->name, usermask, hostmask); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "Invalid user@host $usermask@$hostmask", + log_data_string("usermask", usermask), + log_data_string("hostmask", hostmask)); return; } @@ -4035,10 +4022,10 @@ CMD_FUNC(cmd_tkl_add) { /* Validate ban exception TKL fields */ int softban = 0; - char *usermask = parv[3]; - char *hostmask = parv[4]; - char *bantypes = parv[8]; - char *reason; + const char *usermask = parv[3]; + const char *hostmask = parv[4]; + const char *bantypes = parv[8]; + const char *reason; if (parc < 10) return; @@ -4051,9 +4038,11 @@ CMD_FUNC(cmd_tkl_add) */ if (strchr(usermask, '@') || strchr(hostmask, '@')) { - sendto_realops("Ignoring TKL exception entry %s@%s from %s. " - "Invalid usermask '%s' or hostmask '%s'.", - usermask, hostmask, client->name, usermask, hostmask); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "Invalid TKL except user@host $usermask@$hostmask", + log_data_string("usermask", usermask), + log_data_string("hostmask", hostmask)); return; } @@ -4083,8 +4072,8 @@ CMD_FUNC(cmd_tkl_add) { /* Validate name ban TKL fields */ int hold = 0; - char *name = parv[4]; - char *reason = parv[8]; + const char *name = parv[4]; + const char *reason = parv[8]; if (*parv[3] == 'H') hold = 1; @@ -4102,10 +4091,10 @@ CMD_FUNC(cmd_tkl_add) { /* Validate spamfilter-specific TKL fields */ MatchType match_method; - char *match_string; + const char *match_string; Match *m; /* compiled match_string */ time_t tkl_duration; - char *tkl_reason; + const char *tkl_reason; BanAction action; unsigned short target; /* helper variables */ @@ -4113,38 +4102,42 @@ CMD_FUNC(cmd_tkl_add) if (parc < 12) { - sendto_realops("Ignoring spamfilter from %s. Running very old UnrealIRCd protocol (3.2.X?)", client->name); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "Spamfilter with too few parameters. Running very old UnrealIRCd protocol (3.2.X?)"); return; } match_string = parv[11]; - if (!strcasecmp(parv[10], "posix")) - { - sendto_realops("Ignoring spamfilter from %s. Spamfilter is of type 'posix' (TRE) which " - "is not supported in UnrealIRCd 5. Suggestion: upgrade the other server.", - client->name); - return; - } match_method = unreal_match_method_strtoval(parv[10]); if (match_method == 0) { - sendto_realops("Ignoring spamfilter '%s' from %s with unknown match type '%s'", - match_string, client->name, parv[10]); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "Spamfilter '$spamfilter_string' has unkown match-type '$spamfilter_type'", + log_data_string("spamfilter_string", match_string), + log_data_string("spamfilter_type", parv[10])); return; } if (!(target = spamfilter_gettargets(parv[3], NULL))) { - sendto_realops("Ignoring spamfilter '%s' from %s with unknown target type '%s'", - match_string, client->name, parv[3]); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "Spamfilter '$spamfilter_string' has unkown targets '$spamfilter_targets'", + log_data_string("spamfilter_string", match_string), + log_data_string("spamfilter_targets", parv[3])); return; } if (!(action = banact_chartoval(*parv[4]))) { - sendto_realops("Ignoring spamfilter '%s' from %s with unknown action type '%s'", - match_string, client->name, parv[4]); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "Spamfilter '$spamfilter_string' has unkown action '$spamfilter_action'", + log_data_string("spamfilter_string", match_string), + log_data_string("spamfilter_action", parv[4])); return; } @@ -4160,9 +4153,11 @@ CMD_FUNC(cmd_tkl_add) m = unreal_create_match(match_method, match_string, &err); if (!m) { - sendto_realops("[TKL ERROR] ERROR: Trying to add a spamfilter which does not compile. " - " ERROR='%s', Spamfilter='%s', from='%s'", - err, match_string, client->name); + unreal_log(ULOG_WARNING, "tkl", "TKL_ADD_INVALID", client, + "Invalid TKL entry from $client: " + "Spamfilter '$spamfilter_string': regex does not compile: $spamfilter_regex_error", + log_data_string("spamfilter_string", match_string), + log_data_string("spamfilter_regex_error", err)); return; } tkl = tkl_add_spamfilter(type, target, action, m, set_by, expire_at, set_at, @@ -4213,7 +4208,7 @@ CMD_FUNC(cmd_tkl_add) /* Below this line we will only use 'tkl'. No parc/parv reading anymore. */ - RunHook2(HOOKTYPE_TKL_ADD, client, tkl); + RunHook(HOOKTYPE_TKL_ADD, client, tkl); sendnotice_tkl_add(tkl); @@ -4233,7 +4228,7 @@ CMD_FUNC(cmd_tkl_del) { TKL *tkl; int type; - char *removed_by; + const char *removed_by; if (!IsServer(client) && !IsMe(client)) return; @@ -4249,8 +4244,8 @@ CMD_FUNC(cmd_tkl_del) if (TKLIsServerBanType(type)) { - char *usermask = parv[3]; - char *hostmask = parv[4]; + const char *usermask = parv[3]; + const char *hostmask = parv[4]; int softban = 0; if (*usermask == '%') @@ -4263,8 +4258,8 @@ CMD_FUNC(cmd_tkl_del) } else if (TKLIsBanExceptionType(type)) { - char *usermask = parv[3]; - char *hostmask = parv[4]; + const char *usermask = parv[3]; + const char *hostmask = parv[4]; int softban = 0; /* other parameters are ignored */ @@ -4279,7 +4274,7 @@ CMD_FUNC(cmd_tkl_del) else if (TKLIsNameBanType(type)) { int hold = 0; - char *name = parv[4]; + const char *name = parv[4]; if (*parv[3] == 'H') hold = 1; @@ -4287,14 +4282,15 @@ CMD_FUNC(cmd_tkl_del) } else if (TKLIsSpamfilterType(type)) { - char *match_string; + const char *match_string; unsigned short target; BanAction action; if (parc < 9) { - sendto_realops("[BUG] cmd_tkl called with bogus spamfilter removal request [f/F], from=%s, parc=%d", - client->name, parc); + unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client, + "Invalid TKL deletion request from $client: " + "Spamfilter with too few parameters. Running very old UnrealIRCd protocol (3.2.X?)"); return; /* bogus */ } if (parc >= 12) @@ -4306,15 +4302,21 @@ CMD_FUNC(cmd_tkl_del) if (!(target = spamfilter_gettargets(parv[3], NULL))) { - sendto_realops("Ignoring spamfilter deletion request for '%s' from %s with unknown target type '%s'", - match_string, client->name, parv[3]); + unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client, + "Invalid TKL deletion request from $client: " + "Spamfilter '$spamfilter_string' has unkown targets '$spamfilter_targets'", + log_data_string("spamfilter_string", match_string), + log_data_string("spamfilter_targets", parv[3])); return; } if (!(action = banact_chartoval(*parv[4]))) { - sendto_realops("Ignoring spamfilter deletion request for '%s' from %s with unknown action type '%s'", - match_string, client->name, parv[4]); + unreal_log(ULOG_WARNING, "tkl", "TKL_DEL_INVALID", client, + "Invalid TKL deletion request from $client: " + "Spamfilter '$spamfilter_string' has unkown action '$spamfilter_action'", + log_data_string("spamfilter_string", match_string), + log_data_string("spamfilter_action", parv[4])); return; } tkl = find_tkl_spamfilter(type, match_string, action, target); @@ -4338,7 +4340,7 @@ CMD_FUNC(cmd_tkl_del) if (type & TKL_SHUN) tkl_check_local_remove_shun(tkl); - RunHook2(HOOKTYPE_TKL_DEL, client, tkl); + RunHook(HOOKTYPE_TKL_DEL, client, tkl); if (type & TKL_GLOBAL) { @@ -4412,7 +4414,7 @@ CMD_FUNC(_cmd_tkl) } /** Configure the username/hostname TKL layer based on the BAN_TARGET_* configuration */ -void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, char **tkl_username, char **tkl_hostname) +void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, const char **tkl_username, const char **tkl_hostname) { static char username[USERLEN+1]; static char hostname[HOSTLEN+8]; @@ -4422,13 +4424,11 @@ void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *cli if (ban_target == BAN_TARGET_ACCOUNT) { - if (client->user && client->user->svid && - strcmp(client->user->svid, "0") && - (*client->user->svid != ':')) + if (IsLoggedIn(client) && (*client->user->account != ':')) { /* Place a ban on ~a:Accountname */ strlcpy(username, "~a:", sizeof(username)); - strlcpy(hostname, client->user->svid, sizeof(hostname)); + strlcpy(hostname, client->user->account, sizeof(hostname)); *tkl_username = username; *tkl_hostname = hostname; return; @@ -4437,7 +4437,7 @@ void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *cli } else if (ban_target == BAN_TARGET_CERTFP) { - char *fp = moddata_client_get(client, "certfp"); + const char *fp = moddata_client_get(client, "certfp"); if (fp) { /* Place a ban on ~S:sha256sumofclientcertificate */ @@ -4453,7 +4453,7 @@ void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *cli /* Below we deal with the more common choices... */ /* First, set the username */ - if (((ban_target == BAN_TARGET_USERIP) || (ban_target == BAN_TARGET_USERHOST)) && client->ident && strcmp(client->ident, "unknown")) + if (((ban_target == BAN_TARGET_USERIP) || (ban_target == BAN_TARGET_USERHOST)) && strcmp(client->ident, "unknown")) strlcpy(username, client->ident, sizeof(username)); else strlcpy(username, "*", sizeof(username)); @@ -4494,11 +4494,10 @@ int _place_host_ban(Client *client, BanAction action, char *reason, long duratio { case BAN_ACT_TEMPSHUN: /* We simply mark this connection as shunned and do not add a ban record */ - sendto_snomask(SNO_TKL, "Temporary shun added at user %s (%s@%s) [%s]", - client->name, - client->user ? client->user->username : "unknown", - client->user ? client->user->realhost : GetIP(client), - reason); + unreal_log(ULOG_INFO, "tkl", "TKL_ADD_TEMPSHUN", &me, + "Temporary shun added on user $target.details [reason: $shun_reason] [by: $client]", + log_data_string("shun_reason", reason), + log_data_client("target", client)); SetShunned(client); return 1; case BAN_ACT_GZLINE: @@ -4511,7 +4510,7 @@ int _place_host_ban(Client *client, BanAction action, char *reason, long duratio case BAN_ACT_SOFT_SHUN: { char ip[128], user[USERLEN+3], mo[100], mo2[100]; - char *tkllayer[9] = { + const char *tkllayer[9] = { me.name, /*0 server.name */ "+", /*1 +|- */ "?", /*2 type */ @@ -4554,7 +4553,7 @@ int _place_host_ban(Client *client, BanAction action, char *reason, long duratio tkllayer[7] = mo2; tkllayer[8] = reason; cmd_tkl(&me, NULL, 9, tkllayer); - RunHookReturnInt4(HOOKTYPE_PLACE_HOST_BAN, client, action, reason, duration, !=99); + RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action, reason, duration); if ((action == BAN_ACT_SHUN) || (action == BAN_ACT_SOFT_SHUN)) { find_shun(client); @@ -4565,7 +4564,7 @@ int _place_host_ban(Client *client, BanAction action, char *reason, long duratio case BAN_ACT_SOFT_KILL: case BAN_ACT_KILL: default: - RunHookReturnInt4(HOOKTYPE_PLACE_HOST_BAN, client, action, reason, duration, !=99); + RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action, reason, duration); exit_client(client, NULL, reason); return 1; } @@ -4618,7 +4617,7 @@ TKL *choose_winning_spamfilter(TKL *one, TKL *two) /** Checks if 'target' is on the spamfilter exception list. * RETURNS 1 if found in list, 0 if not. */ -static int target_is_spamexcept(char *target) +static int target_is_spamexcept(const char *target) { SpamExcept *e; @@ -4638,12 +4637,13 @@ static int target_is_spamexcept(char *target) */ int _join_viruschan(Client *client, TKL *tkl, int type) { - char *xparv[3], chbuf[CHANNELLEN + 16], buf[2048]; + const char *xparv[3]; + char chbuf[CHANNELLEN + 16], buf[2048]; Channel *channel; int ret; snprintf(buf, sizeof(buf), "0,%s", SPAMFILTER_VIRUSCHAN); - xparv[0] = client->name; + xparv[0] = NULL; xparv[1] = buf; xparv[2] = NULL; @@ -4658,16 +4658,16 @@ int _join_viruschan(Client *client, TKL *tkl, int type) sendnotice(client, "You are now restricted to talking in %s: %s", SPAMFILTER_VIRUSCHAN, unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); - channel = find_channel(SPAMFILTER_VIRUSCHAN, NULL); + channel = find_channel(SPAMFILTER_VIRUSCHAN); if (channel) { MessageTag *mtags = NULL; - ircsnprintf(chbuf, sizeof(chbuf), "@%s", channel->chname); + ircsnprintf(chbuf, sizeof(chbuf), "@%s", channel->name); ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s matched filter '%s' [%s] [%s]", client->name, tkl->ptr.spamfilter->match->str, cmdname_by_spamftarget(type), unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); new_message(&me, NULL, &mtags); - sendto_channel(channel, &me, NULL, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER, + sendto_channel(channel, &me, NULL, "o", 0, SEND_ALL|SKIP_DEAF, mtags, ":%s NOTICE %s :%s", me.name, chbuf, buf); free_message_tags(mtags); @@ -4687,11 +4687,11 @@ int _join_viruschan(Client *client, TKL *tkl, int type) * 1 if spamfilter matched and it should be blocked (or client exited), 0 if not matched. * In case of 1, be sure to check IsDead(client).. */ -int _match_spamfilter(Client *client, char *str_in, int target, char *cmd, char *destination, int flags, TKL **rettkl) +int _match_spamfilter(Client *client, const char *str_in, int target, const char *cmd, const char *destination, int flags, TKL **rettkl) { TKL *tkl; TKL *winner_tkl = NULL; - char *str; + const char *str; int ret = -1; char *reason = NULL; #ifdef SPAMFILTER_DETECTSLOW @@ -4708,7 +4708,7 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *cmd, char if (target == SPAMF_USER) str = str_in; else - str = (char *)StripControlCodes(str_in); + str = StripControlCodes(str_in); /* (note: using client->user check here instead of IsUser() * due to SPAMF_USER where user isn't marked as client/person yet. @@ -4753,22 +4753,26 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *cmd, char if ((SPAMFILTER_DETECTSLOW_FATAL > 0) && (ms_past > SPAMFILTER_DETECTSLOW_FATAL)) { - sendto_realops("[Spamfilter] WARNING: Too slow spamfilter detected (took %ld msec to execute) " - "-- spamfilter will be \002REMOVED!\002: %s", ms_past, tkl->ptr.spamfilter->match->str); + unreal_log(ULOG_ERROR, "tkl", "SPAMFILTER_SLOW_FATAL", NULL, + "[Spamfilter] WARNING: Too slow spamfilter detected (took $msec_time msec to execute) " + "-- spamfilter will be \002REMOVED!\002: $tkl", + log_data_tkl("tkl", tkl), + log_data_integer("msec_time", ms_past)); tkl_del_line(tkl); return 0; /* Act as if it didn't match, even if it did.. it's gone now anyway.. */ } else if ((SPAMFILTER_DETECTSLOW_WARN > 0) && (ms_past > SPAMFILTER_DETECTSLOW_WARN)) { - sendto_realops("[Spamfilter] WARNING: SLOW Spamfilter detected (took %ld msec to execute): %s", - ms_past, tkl->ptr.spamfilter->match->str); + unreal_log(ULOG_WARNING, "tkl", "SPAMFILTER_SLOW_WARN", NULL, + "[Spamfilter] WARNING: Slow spamfilter detected (took $msec_time msec to execute): $tkl", + log_data_tkl("tkl", tkl), + log_data_integer("msec_time", ms_past)); } #endif if (ret) { /* We have a match! */ - char buf[1024]; char destinationbuf[48]; if (destination) { @@ -4781,15 +4785,14 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *cmd, char if (!winner_tkl && destination && target_is_spamexcept(destination)) return 0; /* No problem! */ - ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s%s: '%s'] [%s]", - client->name, client->user->username, client->user->realhost, - tkl->ptr.spamfilter->match->str, - cmd, destinationbuf, str, - unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); + 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]", + log_data_tkl("tkl", tkl), + log_data_string("command", cmd), + log_data_string("destination", destination ? destination : ""), + log_data_string("str", str)); - sendto_snomask_global(SNO_SPAMF, "%s", buf); - ircd_log(LOG_SPAMFILTER, "%s", buf); - RunHook6(HOOKTYPE_LOCAL_SPAMFILTER, client, str, str_in, target, destination, tkl); + RunHook(HOOKTYPE_LOCAL_SPAMFILTER, client, str, str_in, target, destination, tkl); /* If we should stop after the first match, we end here... */ if (SPAMFILTER_STOP_ON_FIRST_MATCH) @@ -4990,7 +4993,7 @@ static int comp_with_mask(void *addr, void *dest, u_int mask) * CIDR support is available so 'host' may be like '1.2.0.0/16'. * @returns 1 on match, 0 on no match. */ -int _match_user(char *rmask, Client *client, int options) +int _match_user(const char *rmask, Client *client, int options) { char mask[NICKLEN+USERLEN+HOSTLEN+8]; char clientip[IPSZ], maskip[IPSZ]; @@ -5001,8 +5004,8 @@ int _match_user(char *rmask, Client *client, int options) strlcpy(mask, rmask, sizeof(mask)); if ((options & MATCH_CHECK_EXTENDED) && - is_extended_ban(mask) && - client && client->user) + is_extended_server_ban(mask) && + client->user) { /* Check user properties / extbans style */ return _match_user_extended_server_ban(rmask, client); @@ -5144,17 +5147,29 @@ int _match_user(char *rmask, Client *client, int options) return 0; /* NOMATCH: nothing of the above matched */ } -int _match_user_extended_server_ban(char *banstr, Client *client) +int _match_user_extended_server_ban(const char *banstr, Client *client) { - char *msg = NULL, *errmsg = NULL; + const char *nextbanstr; Extban *extban; + BanContext *b; + int ret; - if (!is_extended_ban(banstr)) + if (!is_extended_server_ban(banstr)) return 0; /* we should never have been called */ - extban = findmod_by_bantype(banstr[1]); - if (!extban || !(extban->options & EXTBOPT_TKL)) + extban = findmod_by_bantype(banstr, &nextbanstr); + if (!extban || + !(extban->options & EXTBOPT_TKL) || + !(extban->is_banned_events & BANCHK_TKL)) + { return 0; /* extban not found or of incorrect type (eg ~T) */ + } - return extban->is_banned(client, NULL, banstr, BANCHK_TKL, &msg, &errmsg); + b = safe_alloc(sizeof(BanContext)); + b->client = client; + b->banstr = nextbanstr; + b->ban_check_types = BANCHK_TKL; + ret = extban->is_banned(b); + safe_free(b); + return ret; } diff --git a/src/modules/tkldb.c b/src/modules/tkldb.c index 57a2274..fdc05a5 100644 --- a/src/modules/tkldb.c +++ b/src/modules/tkldb.c @@ -24,7 +24,7 @@ ModuleHeader MOD_HEADER = { "1.10", "Stores active TKL entries (*-Lines) persistently/across IRCd restarts", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #define TKLDB_MAGIC 0x10101010 @@ -38,8 +38,7 @@ ModuleHeader MOD_HEADER = { */ #define TKLDB_SAVE_EVERY_DELTA +15 -#ifdef DEBUGMODE - #define BENCHMARK +// #undef BENCHMARK /* Benchmark results (2GHz Xeon Skylake, compiled with -O2, Linux): * 100,000 zlines: * - load db: 510 ms @@ -48,7 +47,6 @@ ModuleHeader MOD_HEADER = { * which executes every 5 minutes. * Of course, exact figures will depend on the machine. */ -#endif #define FreeTKLRead() \ do { \ @@ -59,9 +57,10 @@ ModuleHeader MOD_HEADER = { #define WARN_WRITE_ERROR(fname) \ do { \ - sendto_realops_and_log("[tkldb] Error writing to temporary database file " \ - "'%s': %s (DATABASE NOT SAVED)", \ - fname, unrealdb_get_error_string()); \ + unreal_log(ULOG_ERROR, "tkldb", "TKLDB_FILE_WRITE_ERROR", NULL, \ + "[tkldb] Error writing to temporary database file $filename: $system_error", \ + log_data_string("filename", fname), \ + log_data_string("system_error", unrealdb_get_error_string())); \ } while(0) #define R_SAFE(x) \ @@ -163,7 +162,7 @@ MOD_LOAD() MOD_UNLOAD() { - if (loop.ircd_terminating) + if (loop.terminating) write_tkldb(); freecfg(&test); freecfg(&cfg); @@ -199,34 +198,34 @@ int tkldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type != CONFIG_SET) return 0; - if (!ce || strcmp(ce->ce_varname, "tkldb")) + if (!ce || strcmp(ce->name, "tkldb")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata) + if (!cep->value) { - config_error("%s:%i: blank set::tkldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: blank set::tkldb::%s without value", cep->file->filename, cep->line_number, cep->name); errors++; } else - if (!strcmp(cep->ce_varname, "database")) + if (!strcmp(cep->name, "database")) { - convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); - safe_strdup(test.database, cep->ce_vardata); + convert_to_absolute_path(&cep->value, PERMDATADIR); + safe_strdup(test.database, cep->value); } else - if (!strcmp(cep->ce_varname, "db-secret")) + if (!strcmp(cep->name, "db-secret")) { - char *err; - if ((err = unrealdb_test_secret(cep->ce_vardata))) + const char *err; + if ((err = unrealdb_test_secret(cep->value))) { - config_error("%s:%i: set::tkldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err); + config_error("%s:%i: set::tkldb::db-secret: %s", cep->file->filename, cep->line_number, err); errors++; continue; } - safe_strdup(test.db_secret, cep->ce_vardata); + safe_strdup(test.db_secret, cep->value); } else { - config_error("%s:%i: unknown directive set::tkldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + config_error("%s:%i: unknown directive set::tkldb::%s", cep->file->filename, cep->line_number, cep->name); errors++; } } @@ -258,15 +257,15 @@ int tkldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type) if (type != CONFIG_SET) return 0; - if (!ce || strcmp(ce->ce_varname, "tkldb")) + if (!ce || strcmp(ce->name, "tkldb")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "database")) - safe_strdup(cfg.database, cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "db-secret")) - safe_strdup(cfg.db_secret, cep->ce_vardata); + if (!strcmp(cep->name, "database")) + safe_strdup(cfg.database, cep->value); + else if (!strcmp(cep->name, "db-secret")) + safe_strdup(cfg.db_secret, cep->value); } return 1; } @@ -370,7 +369,7 @@ int write_tkldb(void) #endif if (rename(tmpfname, cfg.database) < 0) { - sendto_realops_and_log("[tkldb] Error renaming '%s' to '%s': %s (DATABASE NOT SAVED)", tmpfname, cfg.database, strerror(errno)); + config_error("[tkldb] Error renaming '%s' to '%s': %s (DATABASE NOT SAVED)", tmpfname, cfg.database, strerror(errno)); return 0; } #ifdef BENCHMARK @@ -748,12 +747,13 @@ int read_tkldb(void) unrealdb_close(db); if (added_cnt) - sendto_realops_and_log("[tkldb] Re-added %d *-Lines", added_cnt); + config_status("[tkldb] Re-added %d *-Lines", added_cnt); #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); - ircd_log(LOG_ERROR, "[tkldb] Benchmark: LOAD DB: %lld microseconds", - (long long)(((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); + unreal_log(ULOG_DEBUG, "tkldb", "TKLDB_BENCHMARK", NULL, + "[tkldb] Benchmark: LOAD DB: $time_msec microseconds", + log_data_integer("time_msec", ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec))); #endif return 1; } diff --git a/src/modules/tls_antidos.c b/src/modules/tls_antidos.c index 03615cd..b3f6d91 100644 --- a/src/modules/tls_antidos.c +++ b/src/modules/tls_antidos.c @@ -1,6 +1,6 @@ /* - * SSL/TLS Anti DoS module - * This protects against SSL renegotiation attacks while still allowing us + * TLS Anti DoS module + * This protects against TLS renegotiation attacks while still allowing us * to leave renegotiation on with all it's security benefits. * * (C) Copyright 2015- Bram Matthys and the UnrealIRCd team. @@ -16,7 +16,7 @@ ModuleHeader MOD_HEADER "5.0", "TLS Renegotiation DoS protection", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #define HANDSHAKE_LIMIT_COUNT 3 @@ -81,8 +81,7 @@ void ssl_info_callback(const SSL *ssl, int where, int ret) e->n++; if (e->n >= HANDSHAKE_LIMIT_COUNT) { - ircd_log(LOG_ERROR, "TLS Handshake flood detected from %s -- killed", get_client_name(client, TRUE)); - sendto_realops("TLS Handshake flood detected from %s -- killed", get_client_name(client, TRUE)); + unreal_log(ULOG_INFO, "flood", "TLS_HANDSHAKE_FLOOD", client, "TLS Handshake flood detected from $client -- killed"); dead_socket(client, "TLS Handshake flood detected"); } } @@ -105,7 +104,7 @@ int tls_antidos_handshake(Client *client) return 0; } -/** Called by OpenSSL when the SSL structure is freed (so we can free up our custom struct too) */ +/** Called by OpenSSL when the SSL * structure is freed (so we can free up our custom struct too) */ void tls_antidos_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) { safe_free(ptr); diff --git a/src/modules/tls_cipher.c b/src/modules/tls_cipher.c new file mode 100644 index 0000000..a7e84be --- /dev/null +++ b/src/modules/tls_cipher.c @@ -0,0 +1,91 @@ +/* + * Store TLS cipher in ModData + * (C) Copyright 2021-.. Syzop and The UnrealIRCd Team + * License: GPLv2 + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "tls_cipher", + "5.0", + "Store and retrieve TLS cipher string", + "UnrealIRCd Team", + "unrealircd-6", + }; + +/* Forward declarations */ +void tls_cipher_free(ModData *m); +const char *tls_cipher_serialize(ModData *m); +void tls_cipher_unserialize(const char *str, ModData *m); +int tls_cipher_handshake(Client *client); +int tls_cipher_connect(Client *client); +int tls_cipher_whois(Client *client, Client *target); + +ModDataInfo *tls_cipher_md; /* Module Data structure which we acquire */ + +MOD_INIT() +{ +ModDataInfo mreq; + + MARK_AS_OFFICIAL_MODULE(modinfo); + + memset(&mreq, 0, sizeof(mreq)); + mreq.name = "tls_cipher"; + mreq.free = tls_cipher_free; + mreq.serialize = tls_cipher_serialize; + mreq.unserialize = tls_cipher_unserialize; + mreq.sync = MODDATA_SYNC_EARLY; + mreq.type = MODDATATYPE_CLIENT; + tls_cipher_md = ModDataAdd(modinfo->handle, mreq); + if (!tls_cipher_md) + abort(); + + HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, tls_cipher_handshake); + HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, tls_cipher_handshake); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int tls_cipher_handshake(Client *client) +{ + if (client->local->ssl) + { + const char *cipher = tls_get_cipher(client); + + if (!cipher) + return 0; + + moddata_client_set(client, "tls_cipher", cipher); + } + return 0; +} + +void tls_cipher_free(ModData *m) +{ + safe_free(m->str); +} + +const char *tls_cipher_serialize(ModData *m) +{ + if (!m->str) + return NULL; + return m->str; +} + +void tls_cipher_unserialize(const char *str, ModData *m) +{ + safe_strdup(m->str, str); +} diff --git a/src/modules/topic.c b/src/modules/topic.c index 309de5d..a6ee5bb 100644 --- a/src/modules/topic.c +++ b/src/modules/topic.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /topic", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -52,17 +52,13 @@ MOD_UNLOAD() return MOD_SUCCESS; } -void topicoverride(Client *client, Channel *channel, char *topic) +void topic_operoverride_msg(Client *client, Channel *channel, const char *topic) { - sendto_snomask(SNO_EYES, - "*** OperOverride -- %s (%s@%s) TOPIC %s \'%s\'", - client->name, client->user->username, client->user->realhost, - channel->chname, topic); - - /* Logging implementation added by XeRXeS */ - ircd_log(LOG_OVERRIDE, "OVERRIDE: %s (%s@%s) TOPIC %s \'%s\'", - client->name, client->user->username, client->user->realhost, - channel->chname, topic); + unreal_log(ULOG_INFO, "operoverride", "OPEROVERRIDE_TOPIC", client, + "OperOverride: $client.details changed the topic of $channel to '$topic'", + log_data_string("override_type", "topic"), + log_data_string("topic", topic), + log_data_channel("channel", channel)); } /** Query or change the channel topic. @@ -80,13 +76,12 @@ void topicoverride(Client *client, Channel *channel, char *topic) CMD_FUNC(cmd_topic) { Channel *channel = NULL; - char *topic = NULL, *name, *tnick = client->name; - char *errmsg = NULL; + const char *topic = NULL; + const char *name, *tnick = client->name; + const char *errmsg = NULL; time_t ttime = 0; int i = 0; Hook *h; - int ismember; /* cache: IsMember() */ - long flags = 0; /* cache: membership flags */ MessageTag *mtags = NULL; if ((parc < 2) || BadPtr(parv[1])) @@ -97,20 +92,16 @@ CMD_FUNC(cmd_topic) name = parv[1]; - channel = find_channel(parv[1], NULL); + channel = find_channel(parv[1]); if (!channel) { sendnumeric(client, ERR_NOSUCHCHANNEL, name); return; } - ismember = IsMember(client, channel); /* CACHE */ - if (ismember) - flags = get_access(client, channel); /* CACHE */ - if (parc > 2 || SecretChannel(channel)) { - if (!ismember && !IsServer(client) + if (!IsMember(client, channel) && !IsServer(client) && !ValidatePermissionsForPath("channel:see:list:secret",client,NULL,channel,NULL) && !IsULine(client)) { sendnumeric(client, ERR_NOTONCHANNEL, name); @@ -140,7 +131,7 @@ CMD_FUNC(cmd_topic) } /* If you're not a member, and you can't view outside channel, deny */ - if ((!ismember && i == HOOK_DENY) || + if ((!IsMember(client, channel) && i == HOOK_DENY) || (is_banned(client,channel,BANCHK_JOIN,NULL,NULL) && !ValidatePermissionsForPath("channel:see:topic",client,NULL,channel,NULL))) { @@ -149,13 +140,12 @@ CMD_FUNC(cmd_topic) } if (!channel->topic) - sendnumeric(client, RPL_NOTOPIC, channel->chname); + sendnumeric(client, RPL_NOTOPIC, channel->name); else { - sendnumeric(client, RPL_TOPIC, - channel->chname, channel->topic); - sendnumeric(client, RPL_TOPICWHOTIME, channel->chname, - channel->topic_nick, channel->topic_time); + sendnumeric(client, RPL_TOPIC, channel->name, channel->topic); + sendnumeric(client, RPL_TOPICWHOTIME, channel->name, + channel->topic_nick, (long long)channel->topic_time); } return; } @@ -173,13 +163,13 @@ CMD_FUNC(cmd_topic) channel->topic_time = ttime; new_message(client, recv_mtags, &mtags); - RunHook4(HOOKTYPE_TOPIC, client, channel, mtags, topic); + RunHook(HOOKTYPE_TOPIC, client, channel, mtags, topic); sendto_server(client, 0, 0, mtags, ":%s TOPIC %s %s %lld :%s", - client->id, channel->chname, channel->topic_nick, + client->id, channel->name, channel->topic_nick, (long long)channel->topic_time, channel->topic); sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, ":%s TOPIC %s :%s", - client->name, channel->chname, channel->topic); + client->name, channel->name, channel->topic); free_message_tags(mtags); } return; @@ -188,53 +178,67 @@ CMD_FUNC(cmd_topic) /* Topic change. Either locally (check permissions!) or remote, check permissions: */ if (IsUser(client)) { - char *newtopic = NULL; + const char *newtopic = NULL; + const char *errmsg = NULL; + int ret = EX_ALLOW; + int operoverride = 0; - /* +t and not +hoaq ? */ - if ((channel->mode.mode & MODE_TOPICLIMIT) && - !is_skochanop(client, channel) && !IsULine(client) && !IsServer(client)) + for (h = Hooks[HOOKTYPE_CAN_SET_TOPIC]; h; h = h->next) + { + int n = (*(h->func.intfunc))(client, channel, topic, &errmsg); + + if (n == EX_DENY) + { + ret = n; + } else + if (n == EX_ALWAYS_DENY) + { + ret = n; + break; + } + } + + if (ret == EX_ALWAYS_DENY) + { + if (MyUser(client) && errmsg) + sendto_one(client, NULL, "%s", errmsg); /* send error, if any */ + + if (MyUser(client)) + return; /* reject the topic set (note: we never block remote sets) */ + } + + if (ret == EX_DENY) { if (MyUser(client) && !ValidatePermissionsForPath("channel:override:topic", client, NULL, channel, NULL)) { - sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->chname); - return; + if (errmsg) + sendto_one(client, NULL, "%s", errmsg); + return; /* reject */ + } else { + operoverride = 1; /* allow */ } - topicoverride(client, channel, topic); } - /* -t and banned? */ + /* banned? */ newtopic = topic; - if (!(channel->mode.mode & MODE_TOPICLIMIT) && - !is_skochanop(client, channel) && is_banned(client, channel, BANCHK_MSG, &newtopic, &errmsg)) + if (!check_channel_access(client, channel, "hoaq") && is_banned(client, channel, BANCHK_MSG, &newtopic, &errmsg)) { char buf[512]; if (MyUser(client) && !ValidatePermissionsForPath("channel:override:topic", client, NULL, channel, NULL)) { - ircsnprintf(buf, sizeof(buf), "You cannot change the topic on %s while being banned", channel->chname); + ircsnprintf(buf, sizeof(buf), "You cannot change the topic on %s while being banned", channel->name); sendnumeric(client, ERR_CANNOTDOCOMMAND, "TOPIC", buf); return; } - topicoverride(client, channel, topic); + operoverride = 1; } + if (MyUser(client) && newtopic) topic = newtopic; /* process is_banned() changes of topic (eg: text replacement), but only for local clients */ - /* -t, +m, and not +vhoaq */ - if (((flags&CHFL_OVERLAP) == 0) && (channel->mode.mode & MODE_MODERATED)) - { - char buf[512]; - - if (MyUser(client) && ValidatePermissionsForPath("channel:override:topic", client, NULL, channel, NULL)) - { - topicoverride(client, channel, topic); - } else { - /* With +m and -t, only voice and higher may change the topic */ - ircsnprintf(buf, sizeof(buf), "Voice (+v) or higher is required in order to change the topic on %s (channel is +m)", channel->chname); - sendnumeric(client, ERR_CANNOTDOCOMMAND, "TOPIC", buf); - return; - } - } + if (operoverride) + topic_operoverride_msg(client, channel, topic); /* For local users, run spamfilters and hooks.. */ if (MyUser(client)) @@ -242,11 +246,11 @@ CMD_FUNC(cmd_topic) Hook *tmphook; int n; - if (match_spamfilter(client, topic, SPAMF_TOPIC, "TOPIC", channel->chname, 0, NULL)) + if (match_spamfilter(client, topic, SPAMF_TOPIC, "TOPIC", channel->name, 0, NULL)) return; for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_TOPIC]; tmphook; tmphook = tmphook->next) { - topic = (*(tmphook->func.pcharfunc))(client, channel, topic); + topic = (*(tmphook->func.stringfunc))(client, channel, topic); if (!topic) return; } @@ -270,12 +274,12 @@ CMD_FUNC(cmd_topic) channel->topic_time = TStime(); new_message(client, recv_mtags, &mtags); - RunHook4(HOOKTYPE_TOPIC, client, channel, mtags, topic); + RunHook(HOOKTYPE_TOPIC, client, channel, mtags, topic); sendto_server(client, 0, 0, mtags, ":%s TOPIC %s %s %lld :%s", - client->id, channel->chname, channel->topic_nick, + client->id, channel->name, channel->topic_nick, (long long)channel->topic_time, channel->topic); sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags, ":%s TOPIC %s :%s", - client->name, channel->chname, channel->topic); + client->name, channel->name, channel->topic); free_message_tags(mtags); } diff --git a/src/modules/trace.c b/src/modules/trace.c index 8d88d11..1df322a 100644 --- a/src/modules/trace.c +++ b/src/modules/trace.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /trace", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -61,7 +61,7 @@ CMD_FUNC(cmd_trace) int i; Client *acptr; ConfigItem_class *cltmp; - char *tname; + const char *tname; int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS]; int cnt = 0, wilds, dow; time_t now; @@ -73,7 +73,7 @@ CMD_FUNC(cmd_trace) labeled_response_inhibit = 1; if (parc > 2) - if (hunt_server(client, NULL, ":%s TRACE %s :%s", 2, parc, parv)) + if (hunt_server(client, NULL, "TRACE", 2, parc, parv)) return; if (parc > 1) @@ -98,7 +98,7 @@ CMD_FUNC(cmd_trace) } } - switch (hunt_server(client, NULL, ":%s TRACE :%s", 1, parc, parv)) + switch (hunt_server(client, NULL, "TRACE", 1, parc, parv)) { case HUNTED_PASS: /* note: gets here only if parv[1] exists */ { @@ -140,8 +140,8 @@ CMD_FUNC(cmd_trace) now = TStime(); list_for_each_entry(acptr, &lclient_list, lclient_node) { - char *name; - char *class; + const char *name; + const char *class; if (!ValidatePermissionsForPath("client:see:trace:invisible-users",client,acptr,NULL,NULL) && (acptr != client)) continue; @@ -182,21 +182,21 @@ CMD_FUNC(cmd_trace) sendnumeric(client, RPL_TRACEOPERATOR, class, acptr->name, GetHost(acptr), - now - acptr->local->lasttime); + (long long)(now - acptr->local->last_msg_received)); else sendnumeric(client, RPL_TRACEUSER, class, acptr->name, acptr->user->realhost, - now - acptr->local->lasttime); + (long long)(now - acptr->local->last_msg_received)); cnt++; } break; case CLIENT_STATUS_SERVER: sendnumeric(client, RPL_TRACESERVER, class, acptr->local->fd >= 0 ? link_s[acptr->local->fd] : -1, - acptr->local->fd >= 0 ? link_u[acptr->local->fd] : -1, name, *(acptr->serv->by) ? - acptr->serv->by : "*", "*", me.name, - now - acptr->local->lasttime); + acptr->local->fd >= 0 ? link_u[acptr->local->fd] : -1, name, *(acptr->server->by) ? + acptr->server->by : "*", "*", me.name, + (long long)(now - acptr->local->last_msg_received)); cnt++; break; diff --git a/src/modules/tsctl.c b/src/modules/tsctl.c index aa8d9e8..39aebf6 100644 --- a/src/modules/tsctl.c +++ b/src/modules/tsctl.c @@ -26,7 +26,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /tsctl", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; CMD_FUNC(cmd_tsctl); @@ -64,8 +64,10 @@ CMD_FUNC(cmd_tsctl) if (parv[1] && !strcasecmp(parv[1], "alltime")) { - sendnotice(client, "*** Server=%s TStime=%lld", - me.name, (long long)TStime()); + struct timeval currenttime_tv; + gettimeofday(¤ttime_tv, NULL); + sendnotice(client, "*** Server=%s TStime=%lld.%ld", + me.name, (long long)currenttime_tv.tv_sec, (long)currenttime_tv.tv_usec); sendto_server(client, 0, 0, NULL, ":%s TSCTL alltime", client->id); return; } diff --git a/src/modules/typing-indicator.c b/src/modules/typing-indicator.c index 1bc61d0..76f0320 100644 --- a/src/modules/typing-indicator.c +++ b/src/modules/typing-indicator.c @@ -28,11 +28,11 @@ ModuleHeader MOD_HEADER "5.0", "+typing client tag", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -int ti_mtag_is_ok(Client *client, char *name, char *value); -void mtag_add_ti(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int ti_mtag_is_ok(Client *client, const char *name, const char *value); +void mtag_add_ti(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -69,7 +69,7 @@ MOD_UNLOAD() /** This function verifies if the client sending the mtag is permitted to do so. */ -int ti_mtag_is_ok(Client *client, char *name, char *value) +int ti_mtag_is_ok(Client *client, const char *name, const char *value) { /* Require a non-empty parameter */ if (BadPtr(value)) @@ -83,7 +83,7 @@ int ti_mtag_is_ok(Client *client, char *name, char *value) return 0; } -void mtag_add_ti(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_ti(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m; diff --git a/src/modules/umode2.c b/src/modules/umode2.c index 8deb930..ce38b34 100644 --- a/src/modules/umode2.c +++ b/src/modules/umode2.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /umode2", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -60,7 +60,7 @@ MOD_UNLOAD() CMD_FUNC(cmd_umode2) { - char *xparv[5] = { + const char *xparv[5] = { client->name, client->name, parv[1], diff --git a/src/modules/unreal_server_compat.c b/src/modules/unreal_server_compat.c new file mode 100644 index 0000000..b768761 --- /dev/null +++ b/src/modules/unreal_server_compat.c @@ -0,0 +1,319 @@ +/* + * unreal_server_compat - Compatibility with pre-U6 servers + * (C) Copyright 2016-2021 Bram Matthys (Syzop) + * License: GPLv2 + * + * Currently the only purpose of this module is to rewrite MODE + * and SJOIN lines to older servers so any bans/exempts/invex + * will show up with their single letter syntax, + * eg "MODE #test +b ~account:someacc" will be rewritten + * as "MODE #test +b ~a:someacc". + * It uses rather complex mode reparsing techniques to + * achieve this, but this was deemed to be the only way + * that we could achieve this in a doable way. + * The alternative was complicating the mode.c code with + * creating multiple strings for multiple clients, and + * doing the same in any other MODE change routine. + * That would have caused rather intrussive compatibility + * code, so I don't want that. + * With this we can just rip out the module at some point + * that we no longer want to support pre-U6 protocol. + * For SJOIN we do something similar, though in that case + * it would have been quite doable to handle it in there. + * Just figured I would stuff it in here as well, since + * it is basically the same case. + * -- Syzop + */ + +#include "unrealircd.h" + +ModuleHeader MOD_HEADER + = { + "unreal_server_compat", + "1.0.0", + "Provides compatibility with non-U6 servers", + "Bram Matthys (Syzop)", + "unrealircd-6" + }; + +/* Forward declarations */ +int usc_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length); +int usc_reparse_mode(char **msg, char *p, int *length); +int usc_reparse_sjoin(char **msg, char *p, int *length); +void skip_spaces(char **p); +void read_until_space(char **p); +int eat_parameter(char **p); + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + HookAdd(modinfo->handle, HOOKTYPE_PACKET, 0, usc_packet); + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +int usc_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length) +{ + char *p, *buf = *msg; + + /* We are only interested in outgoing servers + * that do not support PROTOCTL NEXTBANS + */ + if (IsMe(to) || !IsServer(to) || SupportNEXTBANS(to) || !buf || !length || !*length) + return 0; + + buf[*length] = '\0'; /* safety */ + + p = *msg; + + skip_spaces(&p); + /* Skip over message tags */ + if (*p == '@') + { + read_until_space(&p); + if (*p == '\0') + return 0; /* unexpected ending */ + p++; + } + + skip_spaces(&p); + if (*p == '\0') + return 0; + + /* Skip origin */ + if (*p == ':') + { + read_until_space(&p); + if (*p == '\0') + return 0; /* unexpected ending */ + } + + skip_spaces(&p); + if (*p == '\0') + return 0; + + if (!strncmp(p, "MODE ", 5)) /* MODE #channel */ + { + if (!eat_parameter(&p)) + return 0; + /* p now points to #channel */ + + /* Now it gets interesting... we have to re-parse and re-write the entire MODE line. */ + return usc_reparse_mode(msg, p, length); + } + + if (!strncmp(p, "SJOIN ", 6)) /* SJOIN timestamp #channel */ + { + if (!eat_parameter(&p) || !eat_parameter(&p)) + return 0; + /* p now points to #channel */ + + /* Now it gets interesting... we have to re-parse and re-write the entire SJOIN line. */ + return usc_reparse_sjoin(msg, p, length); + } + + return 0; +} + +int usc_reparse_mode(char **msg, char *p, int *length) +{ + static char obuf[8192]; + char modebuf[512], *mode_buf_p, *para_buf_p; + char *channel_name; + int i; + int n; + ParseMode pm; + int modes_processed = 0; + + channel_name = p; + if (!eat_parameter(&p)) + return 0; + + mode_buf_p = p; + if (!eat_parameter(&p)) + return 0; + strlncpy(modebuf, mode_buf_p, sizeof(modebuf), p - mode_buf_p); + + /* If we get here then it is (for example) a + * MODE #channel +b nick!user@host + * So, has at least one parameter (nick!user@host in the example). + * p now points exactly to the 'n' from nick!user@host. + * + * Now, what we will do: + * everything BEFORE p is the 'header' that we will + * send exactly as-is. + * The only thing we may (potentially) change is + * everything AFTER p! + */ + + /* Fill 'obuf' with that 'header' */ + strlncpy(obuf, *msg, sizeof(obuf), p - *msg); + para_buf_p = p; + + /* Now parse the modes */ + for (n = parse_chanmode(&pm, modebuf, para_buf_p); n; n = parse_chanmode(&pm, NULL, NULL)) + { + /* We only rewrite the parameters, so don't care about paramless modes.. */ + if (!pm.param) + continue; + + if ((pm.modechar == 'b') || (pm.modechar == 'e') || (pm.modechar == 'I')) + { + const char *result = clean_ban_mask(pm.param, pm.what, &me, 1); + strlcat(obuf, result?result:"", sizeof(obuf)); + strlcat(obuf, " ", sizeof(obuf)); + } else + { + /* as-is */ + strlcat(obuf, pm.param, sizeof(obuf)); + strlcat(obuf, " ", sizeof(obuf)); + } + modes_processed++; + } + + /* Send line as-is */ + if (modes_processed == 0) + return 0; + + /* Strip final whitespace */ + if (obuf[strlen(obuf)-1] == ' ') + obuf[strlen(obuf)-1] = '\0'; + + if (pm.parabuf && *pm.parabuf) + { + strlcat(obuf, " ", sizeof(obuf)); + strlcat(obuf, pm.parabuf, sizeof(obuf)); + } + + /* Add CRLF */ + if (obuf[strlen(obuf)-1] != '\n') + strlcat(obuf, "\r\n", sizeof(obuf)); + + /* Line modified, use it! */ + *msg = obuf; + *length = strlen(obuf); + + return 0; +} + +int usc_reparse_sjoin(char **msg, char *p, int *length) +{ + static char obuf[8192]; + char parabuf[512]; + char *save = NULL; + char *s; + + /* Skip right to the last parameter, the only one we care about */ + p = strstr(p, " :"); + if (!p) + return 0; + p += 2; + + /* Save everything before p, put it in obuf... */ + + /* Fill 'obuf' with that 'header' */ + strlncpy(obuf, *msg, sizeof(obuf), p - *msg); + + /* Put parameters in parabuf so we can trash it :D */ + strlcpy(parabuf, p, sizeof(parabuf)); + + /* Now parse the SJOIN */ + for (s = strtoken(&save, parabuf, " "); s; s = strtoken(&save, NULL, " ")) + { + if (*s == '<') + { + /* SJSBY */ + char *next = strchr(s, '>'); + const char *result; + if (!next) + { + unreal_log(ULOG_WARNING, "unreal_server_compat", "USC_REPARSE_SJOIN_FAILURE", NULL, + "[unreal_server_compat] usc_reparse_sjoin(): sjoin data '$ban' seemed like a SJSBY but was not??", + log_data_string("ban", s)); + continue; + } + if (!strchr("&\"\\", next[1])) + goto fallback_usc_reparse_sjoin; + *next++ = '\0'; + result = clean_ban_mask(next+1, MODE_ADD, &me, 1); + if (!result) + { + unreal_log(ULOG_WARNING, "unreal_server_compat", "USC_REPARSE_SJOIN_FAILURE", NULL, + "[unreal_server_compat] usc_reparse_sjoin(): ban '$ban' could not be converted", + log_data_string("ban", s+1)); + continue; + } + strlcat(obuf, s, sizeof(obuf)); /* "<123,nick" */ + strlcat(obuf, ">", sizeof(obuf)); /* > */ + strlncat(obuf, next, sizeof(obuf), 1); /* & or \" or \\ */ + strlcat(obuf, result, sizeof(obuf)); /* the converted result */ + strlcat(obuf, " ", sizeof(obuf)); + } else + if (strchr("&\"\\", *s)) + { + /* +b / +e / +I */ + const char *result = clean_ban_mask(s+1, MODE_ADD, &me, 1); + if (!result) + { + unreal_log(ULOG_WARNING, "unreal_server_compat", "USC_REPARSE_SJOIN_FAILURE", NULL, + "[unreal_server_compat] usc_reparse_sjoin(): ban '$ban' could not be converted", + log_data_string("ban", s+1)); + continue; + } + strlncat(obuf, s, sizeof(obuf), 1); + strlcat(obuf, result, sizeof(obuf)); + strlcat(obuf, " ", sizeof(obuf)); + } else { +fallback_usc_reparse_sjoin: + strlcat(obuf, s, sizeof(obuf)); + strlcat(obuf, " ", sizeof(obuf)); + } + } + + /* Strip final whitespace */ + if (obuf[strlen(obuf)-1] == ' ') + obuf[strlen(obuf)-1] = '\0'; + + /* Add CRLF */ + if (obuf[strlen(obuf)-1] != '\n') + strlcat(obuf, "\r\n", sizeof(obuf)); + + /* And use it! */ + *msg = obuf; + *length = strlen(obuf); + + return 0; +} + +/** Skip space(s), if any. */ +void skip_spaces(char **p) +{ + for (; **p == ' '; *p = *p + 1); +} + +/** Keep reading until we hit space. */ +void read_until_space(char **p) +{ + for (; **p && (**p != ' '); *p = *p + 1); +} + +int eat_parameter(char **p) +{ + read_until_space(p); + if (**p == '\0') + return 0; /* was just a "MODE #channel" query - wait.. that's weird we are a server sending this :D */ + skip_spaces(p); + if (**p == '\0') + return 0; // impossible + return 1; +} diff --git a/src/modules/unsqline.c b/src/modules/unsqline.c index 1c07c6a..e1fc614 100644 --- a/src/modules/unsqline.c +++ b/src/modules/unsqline.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /unsqline", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -58,13 +58,13 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_unsqline) { - char *tkllayer[6] = { + const char *tkllayer[6] = { me.name, /*0 server.name */ "-", /*1 - */ "Q", /*2 Q */ "*", /*3 unused */ parv[1], /*4 host */ - client->name /*5 whoremoved */ + client->name /*5 whoremoved */ }; if (parc < 2) diff --git a/src/modules/user.c b/src/modules/user.c index ccc3980..31b8c66 100644 --- a/src/modules/user.c +++ b/src/modules/user.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /user", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -65,8 +65,9 @@ MOD_UNLOAD() */ CMD_FUNC(cmd_user) { - char *username; - char *realname; + const char *username; + const char *realname; + char *p; if (!MyConnect(client) || IsServer(client)) return; @@ -83,23 +84,20 @@ CMD_FUNC(cmd_user) return; } - /* This cuts the username off at @, uh okay.. */ - if ((username = strchr(parv[1], '@'))) - *username = '\0'; - username = parv[1]; realname = parv[4]; - if (strlen(username) > USERLEN) - username[USERLEN] = '\0'; /* cut-off */ - make_user(client); /* set::modes-on-connect */ client->umodes |= CONN_MODES; client->user->server = me_hash; strlcpy(client->info, realname, sizeof(client->info)); - strlcpy(client->user->username, username, USERLEN + 1); + strlcpy(client->user->username, username, sizeof(client->user->username)); + + /* This cuts the username off at @, uh okay.. */ + if ((p = strchr(client->user->username, '@'))) + *p = '\0'; if (*client->name && is_handshake_finished(client)) { @@ -109,7 +107,7 @@ CMD_FUNC(cmd_user) sendto_one(client, NULL, ":IRC!IRC@%s PRIVMSG %s :\1VERSION\1", me.name, client->name); } - register_user(client, client->name, username, NULL, NULL, NULL); + register_user(client); return; } } diff --git a/src/modules/userhost-tag.c b/src/modules/userhost-tag.c index 864775c..26a84a6 100644 --- a/src/modules/userhost-tag.c +++ b/src/modules/userhost-tag.c @@ -28,15 +28,15 @@ ModuleHeader MOD_HEADER "5.0", "userhost message tag", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ long CAP_ACCOUNT_TAG = 0L; -int userhost_mtag_is_ok(Client *client, char *name, char *value); -int userhost_mtag_can_send(Client *target); -void mtag_add_userhost(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int userhost_mtag_is_ok(Client *client, const char *name, const char *value); +int userhost_mtag_should_send_to_client(Client *target); +void mtag_add_userhost(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -47,7 +47,7 @@ MOD_INIT() memset(&mtag, 0, sizeof(mtag)); mtag.name = "unrealircd.org/userhost"; mtag.is_ok = userhost_mtag_is_ok; - mtag.can_send = userhost_mtag_can_send; + mtag.should_send_to_client = userhost_mtag_should_send_to_client; mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED; MessageTagHandlerAdd(modinfo->handle, &mtag); @@ -71,7 +71,7 @@ MOD_UNLOAD() * syntax. * We simply allow userhost-tag ONLY from servers and with any syntax. */ -int userhost_mtag_is_ok(Client *client, char *name, char *value) +int userhost_mtag_is_ok(Client *client, const char *name, const char *value) { if (IsServer(client)) return 1; @@ -79,7 +79,7 @@ int userhost_mtag_is_ok(Client *client, char *name, char *value) return 0; } -void mtag_add_userhost(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_userhost(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m; @@ -103,7 +103,7 @@ void mtag_add_userhost(Client *client, MessageTag *recv_mtags, MessageTag **mtag } /** Outgoing filter for this message tag */ -int userhost_mtag_can_send(Client *target) +int userhost_mtag_should_send_to_client(Client *target) { if (IsServer(target) || IsOper(target)) return 1; diff --git a/src/modules/userhost.c b/src/modules/userhost.c index 4352c5d..05d4399 100644 --- a/src/modules/userhost.c +++ b/src/modules/userhost.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /userhost", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -65,6 +65,7 @@ CMD_FUNC(cmd_userhost) char *p; /* scratch end pointer */ char *cn; /* current name */ Client *acptr; + char request[BUFSIZE]; char response[MAXUSERHOSTREPLIES][NICKLEN * 2 + CHANNELLEN + USERLEN + HOSTLEN + 30]; int i; /* loop counter */ int w; @@ -83,14 +84,15 @@ CMD_FUNC(cmd_userhost) */ response[0][0] = response[1][0] = response[2][0] = response[3][0] = response[4][0] = '\0'; - cn = parv[1]; + strlcpy(request, parv[1], sizeof(request)); + cn = request; for (w = 0, i = 0; (i < MAXUSERHOSTREPLIES) && cn; i++) { if ((p = strchr(cn, ' '))) *p = '\0'; - if ((acptr = find_person(cn, NULL))) + if ((acptr = find_user(cn, NULL))) { ircsnprintf(response[w], NICKLEN * 2 + CHANNELLEN + USERLEN + HOSTLEN + 30, "%s%s=%c%s@%s", diff --git a/src/modules/userip-tag.c b/src/modules/userip-tag.c index cb7d20a..3fba6b9 100644 --- a/src/modules/userip-tag.c +++ b/src/modules/userip-tag.c @@ -28,15 +28,15 @@ ModuleHeader MOD_HEADER "5.0", "userip message tag", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Variables */ long CAP_ACCOUNT_TAG = 0L; -int userip_mtag_is_ok(Client *client, char *name, char *value); -int userip_mtag_can_send(Client *target); -void mtag_add_userip(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature); +int userip_mtag_is_ok(Client *client, const char *name, const char *value); +int userip_mtag_should_send_to_client(Client *target); +void mtag_add_userip(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature); MOD_INIT() { @@ -47,7 +47,7 @@ MOD_INIT() memset(&mtag, 0, sizeof(mtag)); mtag.name = "unrealircd.org/userip"; mtag.is_ok = userip_mtag_is_ok; - mtag.can_send = userip_mtag_can_send; + mtag.should_send_to_client = userip_mtag_should_send_to_client; mtag.flags = MTAG_HANDLER_FLAGS_NO_CAP_NEEDED; MessageTagHandlerAdd(modinfo->handle, &mtag); @@ -71,7 +71,7 @@ MOD_UNLOAD() * syntax. * We simply allow userip-tag ONLY from servers and with any syntax. */ -int userip_mtag_is_ok(Client *client, char *name, char *value) +int userip_mtag_is_ok(Client *client, const char *name, const char *value) { if (IsServer(client)) return 1; @@ -79,7 +79,7 @@ int userip_mtag_is_ok(Client *client, char *name, char *value) return 0; } -void mtag_add_userip(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, char *signature) +void mtag_add_userip(Client *client, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature) { MessageTag *m; @@ -103,7 +103,7 @@ void mtag_add_userip(Client *client, MessageTag *recv_mtags, MessageTag **mtag_l } /** Outgoing filter for this message tag */ -int userip_mtag_can_send(Client *target) +int userip_mtag_should_send_to_client(Client *target) { if (IsServer(target) || IsOper(target)) return 1; diff --git a/src/modules/userip.c b/src/modules/userip.c index 7802184..5ca5af6 100644 --- a/src/modules/userip.c +++ b/src/modules/userip.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /userip", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -69,6 +69,7 @@ CMD_FUNC(cmd_userip) char *cn; /* current name */ char *ip, ipbuf[HOSTLEN+1]; Client *acptr; + char request[BUFSIZE]; char response[MAXUSERHOSTREPLIES][NICKLEN * 2 + CHANNELLEN + USERLEN + HOSTLEN + 30]; int i; /* loop counter */ int w; @@ -88,17 +89,17 @@ CMD_FUNC(cmd_userip) * and our ircsnprintf() truncates it to fit anyway. There is * no danger of an overflow here. -Dianora */ - response[0][0] = response[1][0] = response[2][0] = - response[3][0] = response[4][0] = '\0'; + response[0][0] = response[1][0] = response[2][0] = response[3][0] = response[4][0] = '\0'; - cn = parv[1]; + strlcpy(request, parv[1], sizeof(request)); + cn = request; for (w = 0, i = 0; (i < MAXUSERHOSTREPLIES) && cn; i++) { if ((p = strchr(cn, ' '))) *p = '\0'; - if ((acptr = find_person(cn, NULL))) + if ((acptr = find_user(cn, NULL))) { if (!(ip = GetIP(acptr))) ip = ""; diff --git a/src/modules/usermodes/Makefile.in b/src/modules/usermodes/Makefile.in index 68c7dae..d940141 100644 --- a/src/modules/usermodes/Makefile.in +++ b/src/modules/usermodes/Makefile.in @@ -25,20 +25,23 @@ INCLUDES = ../../include/channel.h \ ../../include/ircsprintf.h \ ../../include/license.h \ ../../include/modules.h ../../include/modversion.h ../../include/msg.h \ - ../../include/numeric.h ../../include/proto.h ../../include/dns.h \ + ../../include/numeric.h ../../include/dns.h \ ../../include/resource.h ../../include/setup.h \ ../../include/struct.h ../../include/sys.h \ - ../../include/types.h ../../include/url.h \ + ../../include/types.h \ ../../include/version.h ../../include/whowas.h R_MODULES=\ noctcp.so censor.so bot.so showwhois.so nokick.so servicebot.so \ - privacy.so regonlymsg.so secureonlymsg.so privdeaf.so + privacy.so regonlymsg.so secureonlymsg.so privdeaf.so wallops.so MODULES=$(R_MODULES) MODULEFLAGS=@MODULEFLAGS@ RM=@RM@ +.SUFFIXES: +.SUFFIXES: .c .h .so + all: build build: $(MODULES) @@ -46,46 +49,6 @@ build: $(MODULES) clean: $(RM) -f *.o *.so *~ core -############################################################################# -# .so's section -############################################################################# - -noctcp.so: noctcp.c $(INCLUDES) +%.so: %.c $(INCLUDES) $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o noctcp.so noctcp.c - -censor.so: censor.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o censor.so censor.c - -bot.so: bot.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o bot.so bot.c - -showwhois.so: showwhois.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o showwhois.so showwhois.c - -nokick.so: nokick.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o nokick.so nokick.c - -servicebot.so: servicebot.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o servicebot.so servicebot.c - -privacy.so: privacy.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o privacy.so privacy.c - -regonlymsg.so: regonlymsg.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o regonlymsg.so regonlymsg.c - -secureonlymsg.so: secureonlymsg.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o secureonlymsg.so secureonlymsg.c - -privdeaf.so: privdeaf.c $(INCLUDES) - $(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \ - -o privdeaf.so privdeaf.c + -o $@ $< diff --git a/src/modules/usermodes/bot.c b/src/modules/usermodes/bot.c index 01a08e3..a15f90f 100644 --- a/src/modules/usermodes/bot.c +++ b/src/modules/usermodes/bot.c @@ -28,15 +28,15 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +B", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ long UMODE_BOT = 0L; /* Forward declarations */ -int bot_whois(Client *client, Client *acptr); -int bot_who_status(Client *client, Client *acptr, Channel *channel, Member *cm, char *status, int cansee); +int bot_whois(Client *client, Client *acptr, NameValuePrioList **list); +int bot_who_status(Client *client, Client *acptr, Channel *channel, Member *cm, const char *status, int cansee); int bot_umode_change(Client *client, long oldmode, long newmode); MOD_TEST() @@ -67,17 +67,24 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int bot_whois(Client *requester, Client *acptr) +int bot_whois(Client *client, Client *target, NameValuePrioList **list) { - if (IsBot(acptr)) - sendnumeric(requester, RPL_WHOISBOT, acptr->name, ircnetwork); + char buf[512]; + + if (!IsBot(target)) + return 0; + + if (whois_get_policy(client, target, "bot") == WHOIS_CONFIG_DETAILS_NONE) + return 0; + + add_nvplist_numeric(list, 0, "bot", client, RPL_WHOISBOT, target->name, NETWORK_NAME); return 0; } -int bot_who_status(Client *requester, Client *acptr, Channel *channel, Member *cm, char *status, int cansee) +int bot_who_status(Client *client, Client *target, Channel *channel, Member *cm, const char *status, int cansee) { - if (IsBot(acptr)) + if (IsBot(target)) return 'B'; return 0; @@ -88,7 +95,7 @@ int bot_umode_change(Client *client, long oldmode, long newmode) if ((newmode & UMODE_BOT) && !(oldmode & UMODE_BOT) && MyUser(client)) { /* now +B */ - char *parv[2]; + const char *parv[2]; parv[0] = client->name; parv[1] = NULL; do_cmd(client, NULL, "BOTMOTD", 1, parv); diff --git a/src/modules/usermodes/censor.c b/src/modules/usermodes/censor.c index b118501..fa8f291 100644 --- a/src/modules/usermodes/censor.c +++ b/src/modules/usermodes/censor.c @@ -12,7 +12,7 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +G", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; @@ -20,7 +20,7 @@ long UMODE_CENSOR = 0L; #define IsCensored(x) (x->umodes & UMODE_CENSOR) -int censor_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); +int censor_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); int censor_config_test(ConfigFile *, ConfigEntry *, int, int *); int censor_config_run(ConfigFile *, ConfigEntry *, int); @@ -31,7 +31,7 @@ ConfigItem_badword *conf_badword_message = NULL; static ConfigItem_badword *copy_badword_struct(ConfigItem_badword *ca, int regex, int regflags); -int censor_stats_badwords_user(Client *client, char *para); +int censor_stats_badwords_user(Client *client, const char *para); MOD_TEST() { @@ -79,89 +79,89 @@ int censor_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (type != CONFIG_MAIN) return 0; - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "badword")) + if (!ce || !ce->name || strcmp(ce->name, "badword")) return 0; /* not interested */ - if (!ce->ce_vardata) + if (!ce->value) { config_error("%s:%i: badword without type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); return 1; } - else if (strcmp(ce->ce_vardata, "message") && strcmp(ce->ce_vardata, "all")) { + else if (strcmp(ce->value, "message") && strcmp(ce->value, "all")) { /* config_error("%s:%i: badword with unknown type", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); -- can't do that.. */ + ce->file->filename, ce->line_number); -- can't do that.. */ return 0; /* unhandled */ } - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { if (config_is_blankorempty(cep, "badword")) { errors++; continue; } - if (!strcmp(cep->ce_varname, "word")) + if (!strcmp(cep->name, "word")) { - char *errbuf; + const char *errbuf; if (has_word) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "badword::word"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "badword::word"); continue; } has_word = 1; - if ((errbuf = badword_config_check_regex(cep->ce_vardata,1,1))) + if ((errbuf = badword_config_check_regex(cep->value,1,1))) { config_error("%s:%i: badword::%s contains an invalid regex: %s", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - cep->ce_varname, errbuf); + cep->file->filename, + cep->line_number, + cep->name, errbuf); errors++; } } - else if (!strcmp(cep->ce_varname, "replace")) + else if (!strcmp(cep->name, "replace")) { if (has_replace) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "badword::replace"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "badword::replace"); continue; } has_replace = 1; } - else if (!strcmp(cep->ce_varname, "action")) + else if (!strcmp(cep->name, "action")) { if (has_action) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "badword::action"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "badword::action"); continue; } has_action = 1; - if (!strcmp(cep->ce_vardata, "replace")) + if (!strcmp(cep->value, "replace")) action = 'r'; - else if (!strcmp(cep->ce_vardata, "block")) + else if (!strcmp(cep->value, "block")) action = 'b'; else { config_error("%s:%d: Unknown badword::action '%s'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - cep->ce_vardata); + cep->file->filename, cep->line_number, + cep->value); errors++; } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "badword", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "badword", cep->name); errors++; } } if (!has_word) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "badword::word"); errors++; } @@ -170,7 +170,7 @@ int censor_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (has_replace && action == 'b') { config_error("%s:%i: badword::action is block but badword::replace exists", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } } @@ -188,41 +188,41 @@ int censor_config_run(ConfigFile *cf, ConfigEntry *ce, int type) if (type != CONFIG_MAIN) return 0; - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "badword")) + if (!ce || !ce->name || strcmp(ce->name, "badword")) return 0; /* not interested */ - if (strcmp(ce->ce_vardata, "message") && strcmp(ce->ce_vardata, "all")) + if (strcmp(ce->value, "message") && strcmp(ce->value, "all")) return 0; /* not for us */ ca = safe_alloc(sizeof(ConfigItem_badword)); ca->action = BADWORD_REPLACE; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "action")) + if (!strcmp(cep->name, "action")) { - if (!strcmp(cep->ce_vardata, "block")) + if (!strcmp(cep->value, "block")) { ca->action = BADWORD_BLOCK; } } - else if (!strcmp(cep->ce_varname, "replace")) + else if (!strcmp(cep->name, "replace")) { - safe_strdup(ca->replace, cep->ce_vardata); + safe_strdup(ca->replace, cep->value); } - else if (!strcmp(cep->ce_varname, "word")) + else if (!strcmp(cep->name, "word")) { word = cep; } } - badword_config_process(ca, word->ce_vardata); + badword_config_process(ca, word->value); - if (!strcmp(ce->ce_vardata, "message")) + if (!strcmp(ce->value, "message")) { AddListItem(ca, conf_badword_message); } else - if (!strcmp(ce->ce_vardata, "all")) + if (!strcmp(ce->value, "all")) { AddListItem(ca, conf_badword_message); return 0; /* pretend we didn't see it, so other modules can handle 'all' as well */ @@ -231,12 +231,12 @@ int censor_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 1; } -char *stripbadwords_message(char *str, int *blocked) +const char *stripbadwords_message(const char *str, int *blocked) { return stripbadwords(str, conf_badword_message, blocked); } -int censor_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int censor_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { int blocked = 0; @@ -253,7 +253,7 @@ int censor_can_send_to_user(Client *client, Client *target, char **text, char ** return HOOK_CONTINUE; } -int censor_stats_badwords_user(Client *client, char *para) +int censor_stats_badwords_user(Client *client, const char *para) { ConfigItem_badword *words; diff --git a/src/modules/usermodes/noctcp.c b/src/modules/usermodes/noctcp.c index cc3e7de..28a712e 100644 --- a/src/modules/usermodes/noctcp.c +++ b/src/modules/usermodes/noctcp.c @@ -27,14 +27,14 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +T", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; long UMODE_NOCTCP = 0L; #define IsNoCTCP(client) (client->umodes & UMODE_NOCTCP) -int noctcp_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); +int noctcp_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); MOD_TEST() { @@ -63,7 +63,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static int IsACTCP(char *s) +static int IsACTCP(const char *s) { if (!s) return 0; @@ -74,7 +74,7 @@ static int IsACTCP(char *s) return 0; } -int noctcp_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int noctcp_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { if (MyUser(client) && (sendtype == SEND_TYPE_PRIVMSG) && IsNoCTCP(target) && !IsOper(client) && IsACTCP(*text)) diff --git a/src/modules/usermodes/nokick.c b/src/modules/usermodes/nokick.c index 7ea2440..688ee3a 100644 --- a/src/modules/usermodes/nokick.c +++ b/src/modules/usermodes/nokick.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +q", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ @@ -37,7 +37,7 @@ long UMODE_NOKICK = 0L; /* Forward declarations */ int umode_allow_unkickable_oper(Client *client, int what); int nokick_can_kick(Client *client, Client *target, Channel *channel, - char *comment, long client_flags, long target_flags, char **reject_reason); + const char *comment, const char *client_member_modes, const char *target_member_modes, const char **reject_reason); MOD_TEST() { @@ -76,8 +76,8 @@ int umode_allow_unkickable_oper(Client *client, int what) return 1; } -int nokick_can_kick(Client *client, Client *target, Channel *channel, char *comment, - long client_flags, long target_flags, char **reject_reason) +int nokick_can_kick(Client *client, Client *target, Channel *channel, const char *comment, + const char *client_member_modes, const char *target_member_modes, const char **reject_reason) { static char errmsg[NICKLEN+256]; @@ -91,7 +91,7 @@ int nokick_can_kick(Client *client, Client *target, Channel *channel, char *comm sendnotice(target, "*** umode q: %s tried to kick you from channel %s (%s)", - client->name, channel->chname, comment); + client->name, channel->name, comment); return EX_ALWAYS_DENY; } diff --git a/src/modules/usermodes/privacy.c b/src/modules/usermodes/privacy.c index 5a0fcce..3b43df0 100644 --- a/src/modules/usermodes/privacy.c +++ b/src/modules/usermodes/privacy.c @@ -28,7 +28,7 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +p", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ diff --git a/src/modules/usermodes/privdeaf.c b/src/modules/usermodes/privdeaf.c index 657e645..d252ad1 100644 --- a/src/modules/usermodes/privdeaf.c +++ b/src/modules/usermodes/privdeaf.c @@ -11,13 +11,13 @@ ModuleHeader MOD_HEADER "1.2", "Private Messages Deaf (+D) -- by Syzop", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; static long UMODE_PRIVDEAF = 0; static Umode *UmodePrivdeaf = NULL; -int privdeaf_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); +int privdeaf_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); MOD_INIT() { @@ -47,7 +47,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int privdeaf_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int privdeaf_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { if ((target->umodes & UMODE_PRIVDEAF) && !IsOper(client) && !IsULine(client) && !IsServer(client) && (client != target)) diff --git a/src/modules/usermodes/regonlymsg.c b/src/modules/usermodes/regonlymsg.c index 4555e67..4e08e4f 100644 --- a/src/modules/usermodes/regonlymsg.c +++ b/src/modules/usermodes/regonlymsg.c @@ -28,14 +28,14 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +R", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ long UMODE_REGONLYMSG = 0L; /* Forward declarations */ -int regonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); +int regonlymsg_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); MOD_INIT() { @@ -57,11 +57,11 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int regonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int regonlymsg_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { if (IsRegOnlyMsg(target) && !IsServer(client) && !IsULine(client) && !IsLoggedIn(client)) { - if (ValidatePermissionsForPath("client:override:message:regonlymsg",client,target,NULL,text)) + if (ValidatePermissionsForPath("client:override:message:regonlymsg",client,target,NULL,text?*text:NULL)) return HOOK_CONTINUE; /* bypass this restriction */ *errmsg = "You must identify to a registered nick to private message this user"; diff --git a/src/modules/usermodes/secureonlymsg.c b/src/modules/usermodes/secureonlymsg.c index 28859db..b4b554d 100644 --- a/src/modules/usermodes/secureonlymsg.c +++ b/src/modules/usermodes/secureonlymsg.c @@ -1,5 +1,5 @@ /* - * Recieve private messages only from SSL/TLS users (User mode +Z) + * Recieve private messages only from TLS users (User mode +Z) * (C) Copyright 2000-.. Bram Matthys (Syzop) and the UnrealIRCd team * Idea from "Stealth" * @@ -29,14 +29,14 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +Z", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ long UMODE_SECUREONLYMSG = 0L; /* Forward declarations */ -int secureonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype); +int secureonlymsg_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); MOD_INIT() { @@ -58,27 +58,27 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int secureonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, SendType sendtype) +int secureonlymsg_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) { if (IsSecureOnlyMsg(target) && !IsServer(client) && !IsULine(client) && !IsSecureConnect(client)) { - if (ValidatePermissionsForPath("client:override:message:secureonlymsg",client,target,NULL,text)) + if (ValidatePermissionsForPath("client:override:message:secureonlymsg",client,target,NULL,text?*text:NULL)) return HOOK_CONTINUE; /* bypass this restriction */ - *errmsg = "You must be connected via SSL/TLS to message this user"; + *errmsg = "You must be connected via TLS to message this user"; return HOOK_DENY; } else if (IsSecureOnlyMsg(client) && !IsSecureConnect(target) && !IsULine(target)) { - if (ValidatePermissionsForPath("client:override:message:secureonlymsg",client,target,NULL,text)) + if (ValidatePermissionsForPath("client:override:message:secureonlymsg",client,target,NULL,text?*text:NULL)) return HOOK_CONTINUE; /* bypass this restriction */ /* Similar to above but in this case we are +Z and are trying to message - * an SSL user (who does not have +Z set, note the 'else'). This does not + * a secure user (who does not have +Z set, note the 'else'). This does not * make sense since they could never message back to us. Better block the * message than leave the user confused. */ - *errmsg = "Recipient is not connected via SSL/TLS and you are +Z"; + *errmsg = "Recipient is not connected via TLS and you are +Z"; return HOOK_DENY; } diff --git a/src/modules/usermodes/servicebot.c b/src/modules/usermodes/servicebot.c index ba94e0a..8c7526c 100644 --- a/src/modules/usermodes/servicebot.c +++ b/src/modules/usermodes/servicebot.c @@ -21,8 +21,6 @@ #define IsServiceBot(client) (client->umodes & UMODE_SERVICEBOT) -#define WHOIS_SERVICE_STRING ":%s 313 %s %s :is a Network Service" - /* Module header */ ModuleHeader MOD_HEADER = { @@ -30,7 +28,7 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +S", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ @@ -38,11 +36,11 @@ long UMODE_SERVICEBOT = 0L; /* Forward declarations */ int servicebot_can_kick(Client *client, Client *target, Channel *channel, - char *comment, long client_flags, long target_flags, char **reject_reason); + const char *comment, const char *client_member_modes, const char *target_member_modes, const char **reject_reason); int servicebot_mode_deop(Client *client, Client *target, Channel *channel, - u_int what, int modechar, long my_access, char **reject_reason); -int servicebot_pre_kill(Client *client, Client *target, char *reason); -int servicebot_whois(Client *requester, Client *acptr); + u_int what, int modechar, const char *client_access, const char *target_access, const char **reject_reason); +int servicebot_pre_kill(Client *client, Client *target, const char *reason); +int servicebot_whois(Client *requester, Client *acptr, NameValuePrioList **list); int servicebot_see_channel_in_whois(Client *client, Client *target, Channel *channel); MOD_TEST() @@ -74,8 +72,8 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int servicebot_can_kick(Client *client, Client *target, Channel *channel, char *comment, - long client_flags, long target_flags, char **reject_reason) +int servicebot_can_kick(Client *client, Client *target, Channel *channel, const char *comment, + const char *client_member_modes, const char *target_member_modes, const char **reject_reason) { static char errmsg[NICKLEN+256]; @@ -96,7 +94,7 @@ int servicebot_can_kick(Client *client, Client *target, Channel *channel, char * } int servicebot_mode_deop(Client *client, Client *target, Channel *channel, - u_int what, int modechar, long my_access, char **reject_reason) + u_int what, int modechar, const char *client_access, const char *target_access, const char **reject_reason) { static char errmsg[NICKLEN+256]; @@ -113,7 +111,7 @@ int servicebot_mode_deop(Client *client, Client *target, Channel *channel, return EX_ALLOW; } -int servicebot_pre_kill(Client *client, Client *target, char *reason) +int servicebot_pre_kill(Client *client, Client *target, const char *reason) { if (IsServiceBot(target) && !(ValidatePermissionsForPath("services:servicebot:kill",client,target,NULL,NULL) || IsULine(client))) { @@ -123,12 +121,15 @@ int servicebot_pre_kill(Client *client, Client *target, char *reason) return EX_ALLOW; } -int servicebot_whois(Client *requester, Client *acptr) +int servicebot_whois(Client *client, Client *target, NameValuePrioList **list) { - int hideoper = (IsHideOper(acptr) && (requester != acptr) && !IsOper(requester)) ? 1 : 0; + int hideoper = (IsHideOper(target) && (client != target) && !IsOper(client)) ? 1 : 0; - if (IsServiceBot(acptr) && !hideoper) - sendto_one(requester, NULL, WHOIS_SERVICE_STRING, me.name, requester->name, acptr->name); + if (IsServiceBot(target) && !hideoper && + (whois_get_policy(client, target, "services") > WHOIS_CONFIG_DETAILS_NONE)) + { + add_nvplist_numeric(list, 0, "services", client, RPL_WHOISOPERATOR, target->name, "a Network Service"); + } return 0; } diff --git a/src/modules/usermodes/showwhois.c b/src/modules/usermodes/showwhois.c index 453fd61..74d426a 100644 --- a/src/modules/usermodes/showwhois.c +++ b/src/modules/usermodes/showwhois.c @@ -28,14 +28,14 @@ ModuleHeader MOD_HEADER "4.2", "User Mode +W", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ long UMODE_SHOWWHOIS = 0L; /* Forward declarations */ -int showwhois_whois(Client *requester, Client *target); +int showwhois_whois(Client *requester, Client *target, NameValuePrioList **list); MOD_TEST() { @@ -62,7 +62,7 @@ MOD_UNLOAD() return MOD_SUCCESS; } -int showwhois_whois(Client *requester, Client *target) +int showwhois_whois(Client *requester, Client *target, NameValuePrioList **list) { if (IsWhois(target) && (requester != target)) { diff --git a/src/modules/wallops.c b/src/modules/usermodes/wallops.c similarity index 51% rename from src/modules/wallops.c rename to src/modules/usermodes/wallops.c index 869e930..91b715f 100644 --- a/src/modules/wallops.c +++ b/src/modules/usermodes/wallops.c @@ -1,6 +1,6 @@ /* - * IRC - Internet Relay Chat, src/modules/wallops.c - * (C) 2004 The UnrealIRCd Team + * IRC - Internet Relay Chat, src/modules/usermodes/wallops.c + * (C) 2004-2021 The UnrealIRCd Team * * See file AUTHORS in IRC package for additional names of * the programmers. @@ -28,17 +28,20 @@ CMD_FUNC(cmd_wallops); ModuleHeader MOD_HEADER = { - "wallops", + "usermodes/wallops", "5.0", "command /wallops", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; +long UMODE_WALLOP = 0L; /* send wallops to them */ + MOD_INIT() { - CommandAdd(modinfo->handle, MSG_WALLOPS, cmd_wallops, 1, CMD_USER|CMD_SERVER); MARK_AS_OFFICIAL_MODULE(modinfo); + CommandAdd(modinfo->handle, MSG_WALLOPS, cmd_wallops, 1, CMD_USER|CMD_SERVER); + UmodeAdd(modinfo->handle, 'w', UMODE_GLOBAL, 0, umode_allow_all, &UMODE_WALLOP); return MOD_SUCCESS; } @@ -52,14 +55,43 @@ MOD_UNLOAD() return MOD_SUCCESS; } +#define SendWallops(x) (!IsMe(x) && IsUser(x) && ((x)->umodes & UMODE_WALLOP)) + +/** Send a message to all wallops, except one. + * @param one Skip sending the message to this client/direction + * @param from The sender (can not be NULL) + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + */ +void sendto_wallops(Client *one, Client *from, FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + Client *acptr; + + ++current_serial; + list_for_each_entry(acptr, &client_list, client_node) + { + if (!SendWallops(acptr)) + continue; + if (acptr->direction->local->serial == current_serial) /* sent message along it already ? */ + continue; + if (acptr->direction == one) + continue; /* ...was the one I should skip */ + acptr->direction->local->serial = current_serial; + + va_start(vl, pattern); + vsendto_prefix_one(acptr->direction, from, NULL, pattern, vl); + va_end(vl); + } +} + /* ** cmd_wallops (write to *all* opers currently online) ** parv[1] = message text */ CMD_FUNC(cmd_wallops) { - char *message; - message = parc > 1 ? parv[1] : NULL; + const char *message = parc > 1 ? parv[1] : NULL; if (BadPtr(message)) { @@ -73,5 +105,7 @@ CMD_FUNC(cmd_wallops) return; } - sendto_ops_butone(client->direction, client, ":%s WALLOPS :%s", client->name, message); + sendto_wallops(client->direction, client, ":%s WALLOPS :%s", client->name, message); + if (MyUser(client)) + sendto_prefix_one(client, client, NULL, ":%s WALLOPS :%s", client->name, message); } diff --git a/src/modules/vhost.c b/src/modules/vhost.c index e55969d..965ae28 100644 --- a/src/modules/vhost.c +++ b/src/modules/vhost.c @@ -30,7 +30,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /vhost", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -57,7 +57,8 @@ MOD_UNLOAD() CMD_FUNC(cmd_vhost) { ConfigItem_vhost *vhost; - char *login, *password; + char login[HOSTLEN+1]; + const char *password; char olduser[USERLEN+1]; if (!MyUser(client)) @@ -70,42 +71,42 @@ CMD_FUNC(cmd_vhost) } - login = parv[1]; - password = (parc > 2) ? parv[2] : ""; - /* cut-off too long login names. HOSTLEN is arbitrary, we just don't want our * error messages to be cut off because the user is sending huge login names. */ - if (strlen(login) > HOSTLEN) - login[HOSTLEN] = '\0'; + strlcpy(login, parv[1], sizeof(login)); + + password = (parc > 2) ? parv[2] : ""; if (!(vhost = find_vhost(login))) { - sendto_snomask(SNO_VHOST, - "[\2vhost\2] Failed login for vhost %s by %s!%s@%s - incorrect password", - login, client->name, - client->user->username, - client->user->realhost); + unreal_log(ULOG_WARNING, "vhost", "VHOST_FAILED", client, + "Failed VHOST attempt by $client.details [reason: $reason] [vhost-block: $vhost_block]", + log_data_string("reason", "Vhost block not found"), + log_data_string("fail_type", "UNKNOWN_VHOST_NAME"), + log_data_string("vhost_block", login)); sendnotice(client, "*** [\2vhost\2] Login for %s failed - password incorrect", login); return; } if (!unreal_mask_match(client, vhost->mask)) { - sendto_snomask(SNO_VHOST, - "[\2vhost\2] Failed login for vhost %s by %s!%s@%s - host does not match", - login, client->name, client->user->username, client->user->realhost); + unreal_log(ULOG_WARNING, "vhost", "VHOST_FAILED", client, + "Failed VHOST attempt by $client.details [reason: $reason] [vhost-block: $vhost_block]", + log_data_string("reason", "Host does not match"), + log_data_string("fail_type", "NO_HOST_MATCH"), + log_data_string("vhost_block", login)); sendnotice(client, "*** No vHost lines available for your host"); return; } if (!Auth_Check(client, vhost->auth, password)) { - sendto_snomask(SNO_VHOST, - "[\2vhost\2] Failed login for vhost %s by %s!%s@%s - incorrect password", - login, client->name, - client->user->username, - client->user->realhost); + unreal_log(ULOG_WARNING, "vhost", "VHOST_FAILED", client, + "Failed VHOST attempt by $client.details [reason: $reason] [vhost-block: $vhost_block]", + log_data_string("reason", "Authentication failed"), + log_data_string("fail_type", "AUTHENTICATION_FAILED"), + log_data_string("vhost_block", login)); sendnotice(client, "*** [\2vhost\2] Login for %s failed - password incorrect", login); return; } @@ -141,8 +142,8 @@ CMD_FUNC(cmd_vhost) safe_strdup(client->user->virthost, vhost->virthost); if (vhost->virtuser) { - strcpy(olduser, client->user->username); - strlcpy(client->user->username, vhost->virtuser, USERLEN); + strlcpy(olduser, client->user->username, sizeof(olduser)); + strlcpy(client->user->username, vhost->virtuser, sizeof(client->user->username)); sendto_server(client, 0, 0, NULL, ":%s SETIDENT %s", client->id, client->user->username); } @@ -161,12 +162,22 @@ CMD_FUNC(cmd_vhost) vhost->virtuser ? vhost->virtuser : "", vhost->virtuser ? "@" : "", vhost->virthost); - sendto_snomask(SNO_VHOST, - "[\2vhost\2] %s (%s!%s@%s) is now using vhost %s%s%s", - login, client->name, - vhost->virtuser ? olduser : client->user->username, - client->user->realhost, vhost->virtuser ? vhost->virtuser : "", - vhost->virtuser ? "@" : "", vhost->virthost); + + if (vhost->virtuser) + { + /* virtuser@virthost */ + unreal_log(ULOG_INFO, "vhost", "VHOST_SUCCESS", client, + "$client.details is now using vhost $virtuser@$virthost [vhost-block: $vhost_block]", + log_data_string("virtuser", vhost->virtuser), + log_data_string("virthost", vhost->virthost), + log_data_string("vhost_block", login)); + } else { + /* just virthost */ + unreal_log(ULOG_INFO, "vhost", "VHOST_SUCCESS", client, + "$client.details is now using vhost $virthost [vhost-block: $vhost_block]", + log_data_string("virthost", vhost->virthost), + log_data_string("vhost_block", login)); + } userhost_changed(client); } diff --git a/src/modules/watch-backend.c b/src/modules/watch-backend.c new file mode 100644 index 0000000..40f73c4 --- /dev/null +++ b/src/modules/watch-backend.c @@ -0,0 +1,382 @@ +/* + * IRC - Internet Relay Chat, src/modules/watch-backend.c + * (C) 2021 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. + */ + +#include "unrealircd.h" + +#define WATCH_HASH_TABLE_SIZE 32768 + +#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i) +#define WATCH(client) (moddata_local_client(client, watchListMD).ptr) + +ModDataInfo *watchCounterMD; +ModDataInfo *watchListMD; +static Watch *watchTable[WATCH_HASH_TABLE_SIZE]; +static int watch_initialized = 0; +static char siphashkey_watch[SIPHASH_KEY_LENGTH]; + +void dummy_free(ModData *md); +void watch_free(ModData *md); + +int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment); +int _watch_add(char *nick, Client *client, int flags); +int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event)); +Watch *_watch_get(char *nick); +int _watch_del(char *nick, Client *client, int flags); +int _watch_del_list(Client *client, int flags); +uint64_t hash_watch_nick_name(const char *name); + +ModuleHeader MOD_HEADER += { + "watch-backend", + "5.0", + "backend for /watch", + "UnrealIRCd Team", + "unrealircd-6", +}; + +MOD_TEST() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + + EfunctionAdd(modinfo->handle, EFUNC_WATCH_ADD, _watch_add); + EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL, _watch_del); + EfunctionAdd(modinfo->handle, EFUNC_WATCH_DEL_LIST, _watch_del_list); + EfunctionAddPVoid(modinfo->handle, EFUNC_WATCH_GET, TO_PVOIDFUNC(_watch_get)); + EfunctionAdd(modinfo->handle, EFUNC_WATCH_CHECK, _watch_check); + return MOD_SUCCESS; +} + +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) + { + memset(watchTable, 0, sizeof(watchTable)); + siphash_generate_key(siphashkey_watch); + watch_initialized = 1; + } + + memset(&mreq, 0 , sizeof(mreq)); + mreq.type = MODDATATYPE_LOCAL_CLIENT; + mreq.name = "watchCount", + mreq.free = dummy_free; + watchCounterMD = ModDataAdd(modinfo->handle, mreq); + if (!watchCounterMD) + { + config_error("[%s] Failed to request user watchCount moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); + return MOD_FAILED; + } + + memset(&mreq, 0 , sizeof(mreq)); + mreq.type = MODDATATYPE_LOCAL_CLIENT; + mreq.name = "watchList", + mreq.free = watch_free; + watchListMD = ModDataAdd(modinfo->handle, mreq); + if (!watchListMD) + { + config_error("[%s] Failed to request user watchList moddata: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); + return MOD_FAILED; + } + + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_backend_user_quit); + + return MOD_SUCCESS; +} + +MOD_LOAD() +{ + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + return MOD_SUCCESS; +} + +void dummy_free(ModData *md) +{ +} + +void watch_free(ModData *md) +{ + /* it should have been never requested to free as the module is PERM */ + if (md) + { + unreal_log(ULOG_ERROR, "watch-backend", "BUG_WATCH_FREE_MEMORY_LEAK", NULL, + "[BUG] watchList moddata was not freed -- memory leak!"); + } +} + +int watch_backend_user_quit(Client *client, MessageTag *mtags, const char *comment) +{ + /* Clean out list and watch structures -Donwulff */ + watch_del_list(client, 0); + return 0; +} + +/* + * _watch_add + */ +int _watch_add(char *nick, Client *client, int flags) +{ + unsigned int hashv; + Watch *watch; + Link *lp; + + + /* Get the right bucket... */ + hashv = hash_watch_nick_name(nick); + + /* Find the right nick (header) in the bucket, or NULL... */ + if ((watch = (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->lasttime = timeofday; + strcpy(watch->nick, nick); + + watch->watch = NULL; + + watch->hnext = watchTable[hashv]; + watchTable[hashv] = watch; + } + /* Is this client already on the watch-list? */ + if ((lp = watch->watch)) + while (lp && (lp->value.client != client)) + lp = lp->next; + + /* No it isn't, so add it in the bucket and client addint it */ + if (!lp) { + lp = watch->watch; + watch->watch = make_link(); + watch->watch->value.client = client; + watch->watch->flags = flags; + watch->watch->next = lp; + + lp = make_link(); + lp->next = WATCH(client); + lp->value.wptr = watch; + lp->flags = flags; + WATCH(client) = lp; + WATCHES(client)++; + } + + return 0; +} + +/* + * _watch_check + */ +int _watch_check(Client *client, int event, int (*watch_notify)(Client *client, Watch *watch, Link *lp, int event)) +{ + unsigned int hashv; + Watch *watch; + Link *lp; + + /* Get us the right bucket */ + hashv = hash_watch_nick_name(client->name); + + /* Find the right header in this bucket */ + if ((watch = (Watch *)watchTable[hashv])) + while (watch && mycmp(watch->nick, client->name)) + watch = watch->hnext; + if (!watch) + return 0; /* This nick isn't on watch */ + + /* Update the time of last change to item */ + watch->lasttime = TStime(); + + /* Send notifies out to everybody on the list in header */ + for (lp = watch->watch; lp; lp = lp->next) + { + watch_notify(client, watch, lp, event); + } + + return 0; +} + +/* + * _watch_get + */ +Watch *_watch_get(char *nick) +{ + unsigned int hashv; + Watch *watch; + + hashv = hash_watch_nick_name(nick); + + if ((watch = (Watch *)watchTable[hashv])) + while (watch && mycmp(watch->nick, nick)) + watch = watch->hnext; + + return watch; +} + +/* + * _watch_del + */ +int _watch_del(char *nick, Client *client, int flags) +{ + unsigned int hashv; + Watch **watch, *wprev; + Link **lp, *prev; + + /* Get the bucket for this nick... */ + hashv = hash_watch_nick_name(nick); + + /* Find the right header, maintaining last-link pointer... */ + watch = (Watch **)&watchTable[hashv]; + while (*watch && mycmp((*watch)->nick, nick)) + watch = &(*watch)->hnext; + if (!*watch) + return 0; /* No such watch */ + + /* Find this client from the list of notifies... with last-ptr. */ + lp = &(*watch)->watch; + while (*lp) + { + if ((*lp)->value.client == client && ((*lp)->flags & flags) == flags) + break; + lp = &(*lp)->next; + } + if (!*lp) + return 0; /* No such client to watch */ + + /* Fix the linked list under header, then remove the watch entry */ + prev = *lp; + *lp = prev->next; + free_link(prev); + + /* Do the same regarding the links in client-record... */ + lp = (Link **)&WATCH(client); + while (*lp && ((*lp)->value.wptr != *watch)) + lp = &(*lp)->next; + + /* + * Give error on the odd case... probobly not even neccessary + * No error checking in ircd is unneccessary ;) -Cabal95 + */ + if (!*lp) + { + unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL", client, + "[BUG] watch_del found a watch entry with no client counterpoint, " + "while processing nick $nick on client $client.details", + log_data_string("nick", nick)); + } else { + prev = *lp; + *lp = prev->next; + free_link(prev); + } + /* In case this header is now empty of notices, remove it */ + if (!(*watch)->watch) { + wprev = *watch; + *watch = wprev->hnext; + safe_free(wprev); + } + + /* Update count of notifies on nick */ + WATCHES(client)--; + + return 0; +} + +/* + * _watch_del_list + */ +int _watch_del_list(Client *client, int flags) +{ + unsigned int hashv; + Watch *watch; + Link **np, **lp, *prev; + + np = (Link **)&WATCH(client); + + while (*np) { + if (((*np)->flags & flags) != flags) + { + /* this entry is not fitting requested flags */ + np = &(*np)->next; + continue; + } + + WATCHES(client)--; + + /* Find the watch-record from hash-table... */ + watch = (*np)->value.wptr; + lp = &(watch->watch); + while (*lp && ((*lp)->value.client != client)) + lp = &(*lp)->next; + + /* Not found, another "worst case" debug error */ + if (!*lp) + { + unreal_log(ULOG_WARNING, "watch", "BUG_WATCH_DEL_LIST", client, + "[BUG] watch_del_list found a watch entry with no table counterpoint, " + "while processing client $client.details"); + } else { + /* Fix the watch-list and remove entry */ + Link *prev = *lp; + *lp = prev->next; + free_link(prev); + + /* + * If this leaves a header without notifies, + * remove it. Need to find the last-pointer! + */ + if (!watch->watch) { + Watch **np2, *wprev; + + hashv = hash_watch_nick_name(watch->nick); + + np2 = &watchTable[hashv]; + while (*np2 && *np2 != watch) + np2 = &(*np2)->hnext; + + *np2 = watch->hnext; + + safe_free(watch); + } + } + + prev = *np; /* Save last pointer processed */ + *np = prev->next; /* Jump to the next pointer */ + free_link(prev); /* Free the previous */ + } + + if (!flags) + WATCHES(client) = 0; + + return 0; +} + +uint64_t hash_watch_nick_name(const char *name) +{ + return siphash_nocase(name, siphashkey_watch) % WATCH_HASH_TABLE_SIZE; +} + diff --git a/src/modules/watch.c b/src/modules/watch.c index f84b607..9839b15 100644 --- a/src/modules/watch.c +++ b/src/modules/watch.c @@ -22,9 +22,15 @@ #include "unrealircd.h" -CMD_FUNC(cmd_watch); +#define MSG_WATCH "WATCH" -#define MSG_WATCH "WATCH" +CMD_FUNC(cmd_watch); +int watch_user_quit(Client *client, MessageTag *mtags, const char *comment); +int watch_away(Client *client, MessageTag *mtags, const char *reason, int already_as_away); +int watch_nickchange(Client *client, MessageTag *mtags, const char *newnick); +int watch_post_nickchange(Client *client, MessageTag *mtags, const char *oldnick); +int watch_user_connect(Client *client); +int watch_notification(Client *client, Watch *watch, Link *lp, int event); ModuleHeader MOD_HEADER = { @@ -32,13 +38,24 @@ ModuleHeader MOD_HEADER "5.0", "command /watch", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() -{ - CommandAdd(modinfo->handle, MSG_WATCH, cmd_watch, 1, CMD_USER); +{ MARK_AS_OFFICIAL_MODULE(modinfo); + + CommandAdd(modinfo->handle, MSG_WATCH, cmd_watch, 1, CMD_USER); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, watch_user_quit); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_QUIT, 0, watch_user_quit); + HookAdd(modinfo->handle, HOOKTYPE_AWAY, 0, watch_away); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_NICKCHANGE, 0, watch_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_NICKCHANGE, 0, watch_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_POST_LOCAL_NICKCHANGE, 0, watch_post_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_POST_REMOTE_NICKCHANGE, 0, watch_post_nickchange); + HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, watch_user_connect); + HookAdd(modinfo->handle, HOOKTYPE_REMOTE_CONNECT, 0, watch_user_connect); + return MOD_SUCCESS; } @@ -55,44 +72,66 @@ MOD_UNLOAD() /* * RPL_NOWON - Online at the moment (Successfully added to WATCH-list) * RPL_NOWOFF - Offline at the moement (Successfully added to WATCH-list) - * RPL_WATCHOFF - Successfully removed from WATCH-list. - * ERR_TOOMANYWATCH - Take a guess :> Too many WATCH entries. */ -static void show_watch(Client *client, char *name, int rpl1, int rpl2, int awaynotify) +static void show_watch(Client *client, char *name, int awaynotify) { Client *target; - if ((target = find_person(name, NULL))) + if ((target = find_user(name, NULL))) { if (awaynotify && target->user->away) { sendnumeric(client, RPL_NOWISAWAY, target->name, target->user->username, - IsHidden(target) ? target->user->virthost : target->user-> - realhost, target->user->lastaway); + IsHidden(target) ? target->user->virthost : target->user->realhost, + (long long)target->user->away_since); return; } - sendnumeric(client, rpl1, + sendnumeric(client, RPL_NOWON, target->name, target->user->username, - IsHidden(target) ? target->user->virthost : target->user-> - realhost, target->lastnick); + IsHidden(target) ? target->user->virthost : target->user->realhost, + (long long)target->lastnick); } else { - sendnumeric(client, rpl2, name, "*", "*", 0L); + sendnumeric(client, RPL_NOWOFF, name, "*", "*", 0LL); + } +} + +/* + * RPL_WATCHOFF - Successfully removed from WATCH-list. + */ +static void show_watch_removed(Client *client, char *name) +{ + Client *target; + + if ((target = find_user(name, NULL))) + { + sendnumeric(client, RPL_WATCHOFF, + target->name, target->user->username, + IsHidden(target) ? target->user->virthost : target->user->realhost, + (long long)target->lastnick); + } + else + { + sendnumeric(client, RPL_WATCHOFF, name, "*", "*", 0LL); } } static char buf[BUFSIZE]; +#define WATCHES(client) (moddata_local_client(client, watchCounterMD).i) +#define WATCH(client) (moddata_local_client(client, watchListMD).ptr) + /* * cmd_watch */ CMD_FUNC(cmd_watch) { + char request[BUFSIZE]; Client *target; - char *s, **pav = parv, *user; + char *s, *user; char *p = NULL, *def = "l"; int awaynotify = 0; int did_l=0, did_s=0; @@ -109,7 +148,20 @@ CMD_FUNC(cmd_watch) parv[1] = def; } - for (s = strtoken(&p, *++pav, " "); s; s = strtoken(&p, NULL, " ")) + + ModDataInfo *watchCounterMD = findmoddata_byname("watchCount", MODDATATYPE_LOCAL_CLIENT); + ModDataInfo *watchListMD = findmoddata_byname("watchList", MODDATATYPE_LOCAL_CLIENT); + + if (!watchCounterMD || !watchListMD) + { + unreal_log(ULOG_ERROR, "watch", "WATCH_BACKEND_MISSING", NULL, + "[watch] moddata unavailable. Is the 'watch-backend' module loaded?"); + sendnotice(client, "WATCH command is not available at this moment. Please try again later."); + return; + } + + strlcpy(request, parv[1], sizeof(request)); + for (s = strtoken(&p, request, " "); s; s = strtoken(&p, NULL, " ")) { if ((user = strchr(s, '!'))) *user++ = '\0'; /* Not used */ @@ -127,16 +179,18 @@ CMD_FUNC(cmd_watch) continue; if (do_nick_name(s + 1)) { - if (client->local->watches >= MAXWATCH) + if (WATCHES(client) >= MAXWATCH) { sendnumeric(client, ERR_TOOMANYWATCH, s + 1); continue; } - add_to_watch_hash_table(s + 1, client, awaynotify); + watch_add(s + 1, client, + WATCH_FLAG_TYPE_WATCH | (awaynotify ? WATCH_FLAG_AWAYNOTIFY : 0) + ); } - show_watch(client, s + 1, RPL_NOWON, RPL_NOWOFF, awaynotify); + show_watch(client, s + 1, awaynotify); continue; } @@ -148,9 +202,8 @@ CMD_FUNC(cmd_watch) { if (!*(s+1)) continue; - del_from_watch_hash_table(s + 1, client); - show_watch(client, s + 1, RPL_WATCHOFF, RPL_WATCHOFF, 0); - + watch_del(s + 1, client, WATCH_FLAG_TYPE_WATCH); + show_watch_removed(client, s + 1); continue; } @@ -160,8 +213,7 @@ CMD_FUNC(cmd_watch) */ if (*s == 'C' || *s == 'c') { - hash_del_watch_list(client); - + watch_del_list(client, WATCH_FLAG_TYPE_WATCH); continue; } @@ -173,38 +225,38 @@ CMD_FUNC(cmd_watch) if ((*s == 'S' || *s == 's') && !did_s) { Link *lp; - Watch *anptr; + Watch *watch; int count = 0; did_s = 1; /* * Send a list of how many users they have on their WATCH list - * and how many WATCH lists they are on. + * and how many WATCH lists they are on. This will also include + * other WATCH types if present - we're not checking for + * WATCH_FLAG_TYPE_*. */ - anptr = hash_get_watch(client->name); - if (anptr) - for (lp = anptr->watch, count = 1; + watch = watch_get(client->name); + if (watch) + for (lp = watch->watch, count = 1; (lp = lp->next); count++) ; - sendnumeric(client, RPL_WATCHSTAT, client->local->watches, count); + sendnumeric(client, RPL_WATCHSTAT, WATCHES(client), count); /* * Send a list of everybody in their WATCH list. Be careful * not to buffer overflow. */ - if ((lp = client->local->watch) == NULL) - { - sendnumeric(client, RPL_ENDOFWATCHLIST, *s); - continue; - } + lp = WATCH(client); *buf = '\0'; - strlcpy(buf, lp->value.wptr->nick, sizeof buf); - count = - strlen(client->name) + strlen(me.name) + 10 + - strlen(buf); - while ((lp = lp->next)) + count = strlen(client->name) + strlen(me.name) + 10; + while (lp) { + if (!(lp->flags & WATCH_FLAG_TYPE_WATCH)) + { + lp = lp->next; + continue; /* this one is not ours */ + } if (count + strlen(lp->value.wptr->nick) + 1 > BUFSIZE - 2) { @@ -215,8 +267,12 @@ CMD_FUNC(cmd_watch) strcat(buf, " "); strcat(buf, lp->value.wptr->nick); count += (strlen(lp->value.wptr->nick) + 1); + + lp = lp->next; } - sendnumeric(client, RPL_WATCHLIST, buf); + if (*buf) + /* anything to send */ + sendnumeric(client, RPL_WATCHLIST, buf); sendnumeric(client, RPL_ENDOFWATCHLIST, *s); continue; @@ -229,19 +285,24 @@ CMD_FUNC(cmd_watch) */ if ((*s == 'L' || *s == 'l') && !did_l) { - Link *lp = client->local->watch; + Link *lp = WATCH(client); did_l = 1; while (lp) { - if ((target = find_person(lp->value.wptr->nick, NULL))) + if (!(lp->flags & WATCH_FLAG_TYPE_WATCH)) + { + lp = lp->next; + continue; /* this one is not ours */ + } + if ((target = find_user(lp->value.wptr->nick, NULL))) { sendnumeric(client, RPL_NOWON, target->name, target->user->username, IsHidden(target) ? target->user-> virthost : target->user->realhost, - target->lastnick); + (long long)target->lastnick); } /* * But actually, only show them offline if its a capital @@ -250,7 +311,7 @@ CMD_FUNC(cmd_watch) else if (isupper(*s)) sendnumeric(client, RPL_NOWOFF, lp->value.wptr->nick, "*", "*", - lp->value.wptr->lasttime); + (long long)lp->value.wptr->lasttime); lp = lp->next; } @@ -264,3 +325,106 @@ CMD_FUNC(cmd_watch) */ } } + +int watch_user_quit(Client *client, MessageTag *mtags, const char *comment) +{ + if (IsUser(client)) + watch_check(client, WATCH_EVENT_OFFLINE, watch_notification); + return 0; +} + +int watch_away(Client *client, MessageTag *mtags, const char *reason, int already_as_away) +{ + if (reason) + watch_check(client, already_as_away ? WATCH_EVENT_REAWAY : WATCH_EVENT_AWAY, watch_notification); + else + watch_check(client, WATCH_EVENT_NOTAWAY, watch_notification); + + return 0; +} + +int watch_nickchange(Client *client, MessageTag *mtags, const char *newnick) +{ + watch_check(client, WATCH_EVENT_OFFLINE, watch_notification); + + return 0; +} + +int watch_post_nickchange(Client *client, MessageTag *mtags, const char *oldnick) +{ + watch_check(client, WATCH_EVENT_ONLINE, watch_notification); + + return 0; +} + +int watch_user_connect(Client *client) +{ + watch_check(client, WATCH_EVENT_ONLINE, watch_notification); + + return 0; +} + +int watch_notification(Client *client, Watch *watch, Link *lp, int event) +{ + int awaynotify = 0; + + if (!(lp->flags & WATCH_FLAG_TYPE_WATCH)) + return 0; + + if ((event == WATCH_EVENT_AWAY) || (event == WATCH_EVENT_NOTAWAY) || (event == WATCH_EVENT_REAWAY)) + awaynotify = 1; + + if (!awaynotify) + { + if (event == WATCH_EVENT_OFFLINE) + { + sendnumeric(lp->value.client, RPL_LOGOFF, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? (IsHidden(client) ? client->user->virthost : client->user->realhost) : ""), + (long long)watch->lasttime); + } else { + sendnumeric(lp->value.client, RPL_LOGON, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? (IsHidden(client) ? client->user->virthost : client->user->realhost) : ""), + (long long)watch->lasttime); + } + } + else + { + /* AWAY or UNAWAY */ + if (!(lp->flags & WATCH_FLAG_AWAYNOTIFY)) + return 0; /* skip away/unaway notification for users not interested in them */ + + if (event == WATCH_EVENT_NOTAWAY) + { + sendnumeric(lp->value.client, RPL_NOTAWAY, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? (IsHidden(client) ? client->user->virthost : client->user->realhost) : ""), + (long long)client->user->away_since); + } else + if (event == RPL_GONEAWAY) + { + sendnumeric(lp->value.client, RPL_GONEAWAY, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? (IsHidden(client) ? client->user->virthost : client->user->realhost) : ""), + (long long)client->user->away_since, + client->user->away); + } else + if (event == RPL_REAWAY) + { + sendnumeric(lp->value.client, RPL_REAWAY, + client->name, + (IsUser(client) ? client->user->username : ""), + (IsUser(client) ? (IsHidden(client) ? client->user->virthost : client->user->realhost) : ""), + (long long)client->user->away_since, + client->user->away); + } + } + + return 0; +} + diff --git a/src/modules/webirc.c b/src/modules/webirc.c index 42762f5..373761d 100644 --- a/src/modules/webirc.c +++ b/src/modules/webirc.c @@ -40,7 +40,7 @@ ModuleHeader MOD_HEADER "5.0", "WebIRC/CGI:IRC Support", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* Global variables */ @@ -49,14 +49,13 @@ ConfigItem_webirc *conf_webirc = NULL; /* Forward declarations */ CMD_FUNC(cmd_webirc); -int webirc_check_init(Client *client, char *sockn, size_t size); -int webirc_local_pass(Client *client, char *password); +int webirc_local_pass(Client *client, const char *password); int webirc_config_test(ConfigFile *, ConfigEntry *, int, int *); int webirc_config_run(ConfigFile *, ConfigEntry *, int); void webirc_free_conf(void); void delete_webircblock(ConfigItem_webirc *e); -char *webirc_md_serialize(ModData *m); -void webirc_md_unserialize(char *str, ModData *m); +const char *webirc_md_serialize(ModData *m); +void webirc_md_unserialize(const char *str, ModData *m); void webirc_md_free(ModData *md); int webirc_secure_connect(Client *client); @@ -87,7 +86,7 @@ MOD_INIT() mreq.serialize = webirc_md_serialize; mreq.unserialize = webirc_md_unserialize; mreq.free = webirc_md_free; - mreq.sync = 1; + mreq.sync = MODDATA_SYNC_EARLY; webirc_md = ModDataAdd(modinfo->handle, mreq); if (!webirc_md) { @@ -96,7 +95,6 @@ MOD_INIT() } HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, webirc_config_run); - HookAdd(modinfo->handle, HOOKTYPE_CHECK_INIT, 0, webirc_check_init); HookAdd(modinfo->handle, HOOKTYPE_LOCAL_PASS, 0, webirc_local_pass); HookAdd(modinfo->handle, HOOKTYPE_SECURE_CONNECT, 0, webirc_secure_connect); @@ -154,82 +152,81 @@ int webirc_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (!ce) return 0; - if (!strcmp(ce->ce_varname, "cgiirc")) + if (!strcmp(ce->name, "cgiirc")) { config_error("%s:%i: the cgiirc block has been renamed to webirc and " "the syntax has changed in UnrealIRCd 4", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - need_34_upgrade = 1; + ce->file->filename, ce->line_number); *errs = 1; return -1; } - if (strcmp(ce->ce_varname, "webirc")) + if (strcmp(ce->name, "webirc")) return 0; /* not interested in non-webirc stuff.. */ /* Now actually go parse the webirc { } block */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata) + if (!cep->value) { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "webirc", cep->ce_varname); + config_error_empty(cep->file->filename, cep->line_number, + "webirc", cep->name); errors++; continue; } - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) { - if (cep->ce_vardata || cep->ce_entries) + if (cep->value || cep->items) has_mask = 1; } - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) { if (has_password) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "webirc::password"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "webirc::password"); continue; } has_password = 1; if (Auth_CheckError(cep) < 0) errors++; } - else if (!strcmp(cep->ce_varname, "type")) + else if (!strcmp(cep->name, "type")) { if (has_type) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "webirc::type"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "webirc::type"); } has_type = 1; - if (!strcmp(cep->ce_vardata, "webirc")) + if (!strcmp(cep->value, "webirc")) webirc_type = WEBIRC_WEBIRC; - else if (!strcmp(cep->ce_vardata, "old")) + else if (!strcmp(cep->value, "old")) webirc_type = WEBIRC_PASS; else { config_error("%s:%i: unknown webirc::type '%s', should be either 'webirc' or 'old'", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } } else { - config_error_unknown(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "webirc", cep->ce_varname); + config_error_unknown(cep->file->filename, cep->line_number, + "webirc", cep->name); errors++; } } if (!has_mask) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "webirc::mask"); errors++; } if (!has_password && (webirc_type == WEBIRC_WEBIRC)) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "webirc::password"); errors++; } @@ -239,7 +236,7 @@ int webirc_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) config_error("%s:%i: webirc block has type set to 'old' but has a password set. " "Passwords are not used with type 'old'. Either remove the password or " "use the 'webirc' method instead.", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } @@ -255,23 +252,23 @@ int webirc_config_run(ConfigFile *cf, ConfigEntry *ce, int type) if (type != CONFIG_MAIN) return 0; - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "webirc")) + if (!ce || !ce->name || strcmp(ce->name, "webirc")) return 0; /* not interested */ webirc = safe_alloc(sizeof(ConfigItem_webirc)); webirc->type = WEBIRC_WEBIRC; /* default */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "mask")) + if (!strcmp(cep->name, "mask")) unreal_add_masks(&webirc->mask, cep); - else if (!strcmp(cep->ce_varname, "password")) + else if (!strcmp(cep->name, "password")) webirc->auth = AuthBlockToAuthConfig(cep); - else if (!strcmp(cep->ce_varname, "type")) + else if (!strcmp(cep->name, "type")) { - if (!strcmp(cep->ce_vardata, "webirc")) + if (!strcmp(cep->value, "webirc")) webirc->type = WEBIRC_WEBIRC; - else if (!strcmp(cep->ce_vardata, "old")) + else if (!strcmp(cep->value, "old")) webirc->type = WEBIRC_PASS; else abort(); @@ -283,7 +280,7 @@ int webirc_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; } -char *webirc_md_serialize(ModData *m) +const char *webirc_md_serialize(ModData *m) { static char buf[32]; if (m->i == 0) @@ -292,7 +289,7 @@ char *webirc_md_serialize(ModData *m) return buf; } -void webirc_md_unserialize(char *str, ModData *m) +void webirc_md_unserialize(const char *str, ModData *m) { m->i = atoi(str); } @@ -303,7 +300,7 @@ void webirc_md_free(ModData *md) md->l = 0; } -ConfigItem_webirc *find_webirc(Client *client, char *password, WEBIRCType type, char **errorstr) +ConfigItem_webirc *find_webirc(Client *client, const char *password, WEBIRCType type, char **errorstr) { ConfigItem_webirc *e; char *error = NULL; @@ -337,7 +334,7 @@ ConfigItem_webirc *find_webirc(Client *client, char *password, WEBIRCType type, #define WEBIRC_STRINGLEN (sizeof(WEBIRC_STRING)-1) /* Does the CGI:IRC host spoofing work */ -void dowebirc(Client *client, char *ip, char *host, char *options) +void dowebirc(Client *client, const char *ip, const char *host, const char *options) { char scratch[64]; @@ -352,8 +349,7 @@ void dowebirc(Client *client, char *ip, char *host, char *options) /* STEP 1: Update client->local->ip inet_pton() returns 1 on success, 0 on bad input, -1 on bad AF */ - if ((inet_pton(AF_INET, ip, scratch) != 1) && - (inet_pton(AF_INET6, ip, scratch) != 1)) + if (!is_valid_ip(ip)) { /* then we have an invalid IP */ exit_client(client, NULL, "Invalid IP address"); @@ -371,7 +367,7 @@ void dowebirc(Client *client, char *ip, char *host, char *options) client->local->hostp = NULL; } /* (create new) */ - if (host && verify_hostname(host)) + if (host && valid_host(host, 1)) client->local->hostp = unreal_create_hostent(host, client->ip); /* STEP 4: Update sockhost @@ -385,8 +381,10 @@ void dowebirc(Client *client, char *ip, char *host, char *options) if (options) { + char optionsbuf[BUFSIZE]; char *name, *p = NULL, *p2; - for (name = strtoken(&p, options, " "); name; name = strtoken(&p, NULL, " ")) + strlcpy(optionsbuf, options, sizeof(optionsbuf)); + for (name = strtoken(&p, optionsbuf, " "); name; name = strtoken(&p, NULL, " ")) { p2 = strchr(name, '='); if (p2) @@ -413,7 +411,7 @@ void dowebirc(Client *client, char *ip, char *host, char *options) /* WEBIRC "cgiirc" [:option1 [option2...]]*/ CMD_FUNC(cmd_webirc) { - char *ip, *host, *password, *options; + const char *ip, *host, *password, *options; ConfigItem_webirc *e; char *error = NULL; @@ -440,25 +438,17 @@ CMD_FUNC(cmd_webirc) dowebirc(client, ip, host, options); } -int webirc_check_init(Client *client, char *sockn, size_t size) -{ - if (IsWEBIRC(client)) - { - strlcpy(sockn, GetIP(client), size); /* use already set value */ - return HOOK_DENY; - } - - return HOOK_CONTINUE; /* nothing to do */ -} - -int webirc_local_pass(Client *client, char *password) +int webirc_local_pass(Client *client, const char *password) { if (!strncmp(password, WEBIRC_STRING, WEBIRC_STRINGLEN)) { + char buf[512]; char *ip, *host; ConfigItem_webirc *e; char *error = NULL; + /* Work on a copy as we may trash it */ + strlcpy(buf, password, sizeof(buf)); e = find_webirc(client, NULL, WEBIRC_PASS, &error); if (e) { @@ -467,7 +457,7 @@ int webirc_local_pass(Client *client, char *password) * The has been checked ip->host AND host->ip by CGI:IRC itself * already so we trust it. */ - ip = password + WEBIRC_STRINGLEN; + ip = buf + WEBIRC_STRINGLEN; host = strchr(ip, '_'); if (!host) { diff --git a/src/modules/webredir.c b/src/modules/webredir.c index d1261a0..13c3b41 100644 --- a/src/modules/webredir.c +++ b/src/modules/webredir.c @@ -29,7 +29,7 @@ ModuleHeader MOD_HEADER "1.0", "Do 301 redirect for HEAD/GET/POST/PUT commands", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; struct { @@ -116,36 +116,36 @@ int webredir_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; /* We are only interrested in set::webredir... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "webredir")) + if (!ce || !ce->name || strcmp(ce->name, "webredir")) return 0; nowebredir = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!cep->ce_vardata) + if (!cep->value) { config_error("%s:%i: set::webredir::%s with no value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } - else if (!strcmp(cep->ce_varname, "url")) + else if (!strcmp(cep->name, "url")) { - if (!*cep->ce_vardata || strchr(cep->ce_vardata, ' ')) + if (!*cep->value || strchr(cep->value, ' ')) { config_error("%s:%i: set::webredir::%s with empty value", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } - if (!strstr(cep->ce_vardata, "://") || !strcmp(cep->ce_vardata, "https://...")) + if (!strstr(cep->value, "://") || !strcmp(cep->value, "https://...")) { config_error("%s:%i: set::webredir::url needs to be a valid URL", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum); + cep->file->filename, cep->line_number); errors++; } if (has_url) { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "set::webredir::url"); + config_warn_duplicate(cep->file->filename, + cep->line_number, "set::webredir::url"); continue; } has_url = 1; @@ -153,14 +153,14 @@ int webredir_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) else { config_error("%s:%i: unknown directive set::webredir::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; } } if (!has_url) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + config_error_missing(ce->file->filename, ce->line_number, "set::webredir::url"); errors++; } @@ -177,14 +177,14 @@ int webredir_config_run(ConfigFile *cf, ConfigEntry *ce, int type) return 0; /* We are only interrested in set::webredir... */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "webredir")) + if (!ce || !ce->name || strcmp(ce->name, "webredir")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "url")) + if (!strcmp(cep->name, "url")) { - safe_strdup(cfg.url, cep->ce_vardata); + safe_strdup(cfg.url, cep->value); } } return 1; diff --git a/src/modules/websocket.c b/src/modules/websocket.c index 97cd7dd..73c9fcc 100644 --- a/src/modules/websocket.c +++ b/src/modules/websocket.c @@ -6,6 +6,7 @@ */ #include "unrealircd.h" +#include "dns.h" #define WEBSOCKET_VERSION "1.1.0" @@ -15,7 +16,7 @@ ModuleHeader MOD_HEADER WEBSOCKET_VERSION, "WebSocket support (RFC6455)", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; #if CHAR_MIN < 0 @@ -40,6 +41,8 @@ struct WebSocketUser { int lefttoparselen; /**< Length of lefttoparse buffer */ WebSocketType type; /**< WEBSOCKET_TYPE_BINARY or WEBSOCKET_TYPE_TEXT */ char *sec_websocket_protocol; /**< Only valid during parsing of the request, after that it is NULL again */ + char *forwarded; /**< Unparsed `Forwarded:` header, RFC 7239 */ + int secure; /**< If there is a Forwarded header, this indicates if the remote connection is secure */ }; #define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr) @@ -56,19 +59,33 @@ struct WebSocketUser { #define WSOP_PING 0x09 #define WSOP_PONG 0x0a +/* used to parse http Forwarded header (RFC 7239) */ +#define IPLEN 48 +#define FHEADER_NAMELEN 20 + +struct HTTPForwardedHeader +{ + int secure; + char hostname[HOSTLEN+1]; + char ip[IPLEN+1]; +}; + /* Forward declarations */ int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int websocket_config_run_ex(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr); int websocket_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length); -int websocket_packet_in(Client *client, char *readbuf, int *length); +int websocket_packet_in(Client *client, const char *readbuf, int *length); void websocket_mdata_free(ModData *m); -int websocket_handle_packet(Client *client, char *readbuf, int length); -int websocket_handle_handshake(Client *client, char *readbuf, int *length); +int websocket_handle_packet(Client *client, const char *readbuf, int length); +int websocket_handle_handshake(Client *client, const char *readbuf, int *length); int websocket_handshake_send_response(Client *client); -int websocket_handle_packet_ping(Client *client, char *buf, int len); -int websocket_handle_packet_pong(Client *client, char *buf, int len); +int websocket_handle_packet_ping(Client *client, const char *buf, int len); +int websocket_handle_packet_pong(Client *client, const char *buf, int len); int websocket_create_packet(int opcode, char **buf, int *len); -int websocket_send_pong(Client *client, char *buf, int len); +int websocket_send_pong(Client *client, const char *buf, int len); +int websocket_secure_connect(Client *client); +struct HTTPForwardedHeader *websocket_parse_forwarded_header(char *input); +int websocket_ip_compare(const char *ip1, const char *ip2); /* Global variables */ ModDataInfo *websocket_md; @@ -89,6 +106,7 @@ MOD_INIT() HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN_EX, 0, websocket_config_run_ex); HookAdd(modinfo->handle, HOOKTYPE_PACKET, INT_MAX, websocket_packet_out); HookAdd(modinfo->handle, HOOKTYPE_RAWPACKET_IN, INT_MIN, websocket_packet_in); + HookAdd(modinfo->handle, HOOKTYPE_SECURE_CONNECT, 0, websocket_secure_connect); memset(&mreq, 0, sizeof(mreq)); mreq.name = "websocket"; @@ -114,10 +132,6 @@ MOD_UNLOAD() return MOD_SUCCESS; } -#ifndef CheckNull - #define CheckNull(x) if ((!(x)->ce_vardata) || (!(*((x)->ce_vardata)))) { config_error("%s:%i: missing parameter", (x)->ce_fileptr->cf_filename, (x)->ce_varlinenum); errors++; continue; } -#endif - int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { int errors = 0; @@ -129,16 +143,16 @@ int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return 0; /* We are only interrested in listen::options::websocket.. */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "websocket")) + if (!ce || !ce->name || strcmp(ce->name, "websocket")) return 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "type")) + if (!strcmp(cep->name, "type")) { CheckNull(cep); has_type = 1; - if (!strcmp(cep->ce_vardata, "text")) + if (!strcmp(cep->value, "text")) { if (non_utf8_nick_chars_in_use && !errored_once_nick) { @@ -153,19 +167,33 @@ int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) errors++; } } - else if (!strcmp(cep->ce_vardata, "binary")) + else if (!strcmp(cep->value, "binary")) { } else { config_error("%s:%i: listen::options::websocket::type must be either 'binary' or 'text' (not '%s')", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata); + cep->file->filename, cep->line_number, cep->value); errors++; } + } else if (!strcmp(cep->name, "forward")) + { + if (!cep->value) + { + config_error_empty(cep->file->filename, cep->line_number, "listen::options::websocket::forward", cep->name); + errors++; + continue; + } + if (!is_valid_ip(cep->value)) + { + config_error("%s:%i: invalid IP address '%s' in listen::options::websocket::forward", cep->file->filename, cep->line_number, cep->value); + errors++; + continue; + } } else { config_error("%s:%i: unknown directive listen::options::websocket::%s", - cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + cep->file->filename, cep->line_number, cep->name); errors++; continue; } @@ -174,7 +202,7 @@ int websocket_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) if (!has_type) { config_error("%s:%i: websocket set, but type unspecified. Use something like: listen { ip *; port 443; websocket { type text; } }", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + ce->file->filename, ce->line_number); errors++; } @@ -192,18 +220,18 @@ int websocket_config_run_ex(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr return 0; /* We are only interrested in listen::options::websocket.. */ - if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "websocket")) + if (!ce || !ce->name || strcmp(ce->name, "websocket")) return 0; l = (ConfigItem_listen *)ptr; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + for (cep = ce->items; cep; cep = cep->next) { - if (!strcmp(cep->ce_varname, "type")) + if (!strcmp(cep->name, "type")) { - if (!strcmp(cep->ce_vardata, "binary")) + if (!strcmp(cep->value, "binary")) l->websocket_options = WEBSOCKET_TYPE_BINARY; - else if (!strcmp(cep->ce_vardata, "text")) + else if (!strcmp(cep->value, "text")) { l->websocket_options = WEBSOCKET_TYPE_TEXT; if ((tempiConf.allowed_channelchars == ALLOWED_CHANNELCHARS_ANY) && !warned_once_channel) @@ -217,6 +245,9 @@ int websocket_config_run_ex(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr warned_once_channel = 1; } } + } else if (!strcmp(cep->name, "forward")) + { + safe_strdup(l->websocket_forward, cep->value); } } return 1; @@ -230,6 +261,8 @@ void websocket_mdata_free(ModData *m) { safe_free(wsu->handshake_key); safe_free(wsu->lefttoparse); + safe_free(wsu->sec_websocket_protocol); + safe_free(wsu->forwarded); safe_free(m->ptr); } } @@ -239,6 +272,8 @@ void websocket_mdata_free(ModData *m) */ int websocket_packet_out(Client *from, Client *to, Client *intended_to, char **msg, int *length) { + static char utf8buf[510]; + if (MyConnect(to) && WSU(to) && WSU(to)->handshake_completed) { if (WEBSOCKET_TYPE(to) == WEBSOCKET_TYPE_BINARY) @@ -246,7 +281,7 @@ int websocket_packet_out(Client *from, Client *to, Client *intended_to, char **m else if (WEBSOCKET_TYPE(to) == WEBSOCKET_TYPE_TEXT) { /* Some more conversions are needed */ - char *safe_msg = unrl_utf8_make_valid(*msg); + char *safe_msg = unrl_utf8_make_valid(*msg, utf8buf, sizeof(utf8buf), 1); *msg = safe_msg; *length = *msg ? strlen(safe_msg) : 0; websocket_create_packet(WSOP_TEXT, msg, length); @@ -256,7 +291,7 @@ int websocket_packet_out(Client *from, Client *to, Client *intended_to, char **m return 0; } -int websocket_handle_websocket(Client *client, char *readbuf2, int length2) +int websocket_handle_websocket(Client *client, const char *readbuf2, int length2) { int n; char *ptr; @@ -308,9 +343,13 @@ int websocket_handle_websocket(Client *client, char *readbuf2, int length2) * 0 means: don't process this data, but you can read another packet if you want * >0 means: process this data (regular IRC data, non-websocket stuff) */ -int websocket_packet_in(Client *client, char *readbuf, int *length) +int websocket_packet_in(Client *client, const char *readbuf, int *length) { - if ((client->local->receiveM == 0) && WEBSOCKET_PORT(client) && !WSU(client) && (*length > 8) && !strncmp(readbuf, "GET ", 4)) + if ((client->local->traffic.messages_received == 0) && + WEBSOCKET_PORT(client) && + !WSU(client) && + (*length > 8) && + !strncmp(readbuf, "GET ", 4)) { /* Allocate a new WebSocketUser struct for this session */ moddata_client(client, websocket_md).ptr = safe_alloc(sizeof(WebSocketUser)); @@ -461,6 +500,141 @@ int websocket_handshake_helper(char *buffer, int len, char **key, char **value, return 0; } +#define FHEADER_STATE_NAME 0 +#define FHEADER_STATE_VALUE 1 +#define FHEADER_STATE_VALUE_QUOTED 2 + +#define FHEADER_ACTION_APPEND 0 +#define FHEADER_ACTION_IGNORE 1 +#define FHEADER_ACTION_PROCESS 2 + +/** If a valid Forwarded: http header is received from a trusted source (proxy server), this function will + * extract remote IP address and secure (https) status from it. If more than one field with same name is received, + * we'll accept the last one. This should work correctly with chained proxies. */ +struct HTTPForwardedHeader *websocket_parse_forwarded_header(char *input) +{ + static struct HTTPForwardedHeader forwarded; + int i, length; + int state = FHEADER_STATE_NAME, action = FHEADER_ACTION_APPEND; + char name[FHEADER_NAMELEN+1]; + char value[IPLEN+1]; + int name_length = 0; + int value_length = 0; + char c; + + memset(&forwarded, 0, sizeof(struct HTTPForwardedHeader)); + + length = strlen(input); + for (i = 0; i < length; i++) + { + c = input[i]; + switch (c) + { + case '"': + switch (state) + { + case FHEADER_STATE_NAME: + action = FHEADER_ACTION_APPEND; + break; + case FHEADER_STATE_VALUE: + action = FHEADER_ACTION_IGNORE; + state = FHEADER_STATE_VALUE_QUOTED; + break; + case FHEADER_STATE_VALUE_QUOTED: + action = FHEADER_ACTION_IGNORE; + state = FHEADER_STATE_VALUE; + break; + } + break; + case ',': case ';': case ' ': + switch (state) + { + case FHEADER_STATE_NAME: /* name without value */ + name_length = 0; + action = FHEADER_ACTION_IGNORE; + break; + case FHEADER_STATE_VALUE: /* end of value */ + action = FHEADER_ACTION_PROCESS; + break; + case FHEADER_STATE_VALUE_QUOTED: /* quoted character, process as normal */ + action = FHEADER_ACTION_APPEND; + break; + } + break; + case '=': + switch (state) + { + case FHEADER_STATE_NAME: /* end of name */ + name[name_length] = '\0'; + state = FHEADER_STATE_VALUE; + action = FHEADER_ACTION_IGNORE; + break; + case FHEADER_STATE_VALUE: case FHEADER_STATE_VALUE_QUOTED: /* none of the values is expected to contain = but proceed anyway */ + action = FHEADER_ACTION_APPEND; + break; + } + break; + default: + action = FHEADER_ACTION_APPEND; + break; + } + switch (action) + { + case FHEADER_ACTION_APPEND: + if (state == FHEADER_STATE_NAME) + { + if (name_length < FHEADER_NAMELEN) + { + name[name_length++] = c; + } else + { + /* truncate */ + } + } else + { + if (value_length < IPLEN) + { + value[value_length++] = c; + } else + { + /* truncate */ + } + } + break; + case FHEADER_ACTION_IGNORE: default: + break; + case FHEADER_ACTION_PROCESS: + value[value_length] = '\0'; + name[name_length] = '\0'; + if (!strcasecmp(name, "for")) + { + strlcpy(forwarded.ip, value, IPLEN+1); + } else if (!strcasecmp(name, "proto")) + { + if (!strcasecmp(value, "https")) + { + forwarded.secure = 1; + } else if (!strcasecmp(value, "http")) + { + forwarded.secure = 0; + } else + { + /* ignore unknown value */ + } + } else + { + /* ignore unknown field name */ + } + value_length = 0; + name_length = 0; + state = FHEADER_STATE_NAME; + break; + } + } + + return &forwarded; +} + /** Finally, validate the websocket request (handshake) and proceed or reject. */ int websocket_handshake_valid(Client *client) { @@ -468,7 +642,7 @@ int websocket_handshake_valid(Client *client) { if (is_module_loaded("webredir")) { - char *parx[2] = { NULL, NULL }; + const char *parx[2] = { NULL, NULL }; do_cmd(client, NULL, "GET", 1, parx); } dead_socket(client, "Invalid WebSocket request"); @@ -510,13 +684,83 @@ int websocket_handshake_valid(Client *client) safe_free(WSU(client)->sec_websocket_protocol); } } + if (WSU(client)->forwarded) + { + /* check for source ip */ + if (BadPtr(client->local->listener->websocket_forward) || !websocket_ip_compare(client->local->listener->websocket_forward, client->ip)) + { + unreal_log(ULOG_WARNING, "websocket", "UNAUTHORIZED_FORWARDED_HEADER", client, "Received unauthorized Forwarded header from $ip", log_data_string("ip", client->ip)); + dead_socket(client, "Forwarded: no access"); + 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)) + { + unreal_log(ULOG_WARNING, "websocket", "INVALID_FORWARDED_IP", client, "Received invalid IP in Forwarded header from $ip", log_data_string("ip", client->ip)); + dead_socket(client, "Forwarded: invalid IP"); + return 0; + } + /* store data */ + WSU(client)->secure = forwarded->secure; + 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 */ + /* (free old) */ + if (client->local->hostp) + { + unreal_free_hostent(client->local->hostp); + client->local->hostp = NULL; + } + /* (create new) */ + if (!DONT_RESOLVE) + { + /* taken from socket.c */ + struct hostent *he; + unrealdns_delreq_bycptr(client); /* in case the proxy ip is still in progress of being looked up */ + ClearDNSLookup(client); + he = unrealdns_doclient(client); /* call this once more */ + if (!client->local->hostp) + { + if (he) + client->local->hostp = he; + else + SetDNSLookup(client); + } else + { + /* 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); + } return 1; } +int websocket_secure_connect(Client *client) +{ + /* Remove secure mode (-z) if the WEBIRC gateway did not ensure + * us that their [client]--[webirc gateway] connection is also + * secure (eg: using https) + */ + if (IsSecureConnect(client) && WSU(client) && WSU(client)->forwarded && !WSU(client)->secure) + client->umodes &= ~UMODE_SECURE; + return 0; +} + /** Handle client GET WebSocket handshake. * Yes, I'm going to assume that the header fits in one packet and one packet only. */ -int websocket_handle_handshake(Client *client, char *readbuf, int *length) +int websocket_handle_handshake(Client *client, const char *readbuf, int *length) { char *key, *value; int r, end_of_request; @@ -566,12 +810,17 @@ int websocket_handle_handshake(Client *client, char *readbuf, int *length) { /* Save it here, will be processed later */ safe_strdup(WSU(client)->sec_websocket_protocol, value); + } else + if (!strcasecmp(key, "Forwarded")) + { + /* will be processed later too */ + safe_strdup(WSU(client)->forwarded, value); } } if (end_of_request) { - if (!websocket_handshake_valid(client)) + if (!websocket_handshake_valid(client) || IsDead(client)) return -1; websocket_handshake_send_response(client); return 0; @@ -589,16 +838,12 @@ int websocket_handle_handshake(Client *client, char *readbuf, int *length) int websocket_handshake_send_response(Client *client) { char buf[512], hashbuf[64]; - SHA_CTX hash; char sha1out[20]; /* 160 bits */ WSU(client)->handshake_completed = 1; snprintf(buf, sizeof(buf), "%s%s", WSU(client)->handshake_key, WEBSOCKET_MAGIC_KEY); - SHA1_Init(&hash); - SHA1_Update(&hash, buf, strlen(buf)); - SHA1_Final(sha1out, &hash); - + sha1hash_binary(sha1out, buf, strlen(buf)); b64_encode(sha1out, sizeof(sha1out), hashbuf, sizeof(hashbuf)); snprintf(buf, sizeof(buf), @@ -660,14 +905,16 @@ void add_lf_if_needed(char **buf, int *len) * OR 0 to indicate a possible short read (want more data) * OR -1 in case of an error. */ -int websocket_handle_packet(Client *client, char *readbuf, int length) +int websocket_handle_packet(Client *client, const char *readbuf, int length) { char opcode; /**< Opcode */ char masked; /**< Masked */ int len; /**< Length of the packet */ char maskkey[4]; /**< Key used for masking */ - char *p, *payload; + const char *p; int total_packet_size; + char *payload = NULL; + static char payloadbuf[READBUF_SIZE]; if (length < 4) { @@ -728,14 +975,20 @@ int websocket_handle_packet(Client *client, char *readbuf, int length) memcpy(maskkey, p, 4); p+= 4; - payload = (len > 0) ? p : NULL; + + if (len > 0) + { + memcpy(payloadbuf, p, len); + payload = payloadbuf; + } /* else payload is NULL */ if (len > 0) { /* Unmask this thing (page 33, section 5.3) */ int n; char v; - for (n = 0; n < len; n++) + char *p; + for (p = payload, n = 0; n < len; n++) { v = *p; *p++ = v ^ maskkey[n % 4]; @@ -777,7 +1030,7 @@ int websocket_handle_packet(Client *client, char *readbuf, int length) return -1; /* NOTREACHED */ } -int websocket_handle_packet_ping(Client *client, char *buf, int len) +int websocket_handle_packet_ping(Client *client, const char *buf, int len) { if (len > 500) { @@ -785,11 +1038,11 @@ int websocket_handle_packet_ping(Client *client, char *buf, int len) return -1; } websocket_send_pong(client, buf, len); - client->local->since++; /* lag penalty of 1 second */ + add_fake_lag(client, 1000); /* lag penalty of 1 second */ return 0; } -int websocket_handle_packet_pong(Client *client, char *buf, int len) +int websocket_handle_packet_pong(Client *client, const char *buf, int len) { /* We don't care */ return 0; @@ -799,7 +1052,7 @@ int websocket_handle_packet_pong(Client *client, char *buf, int len) * This is the simple version that is used ONLY for WSOP_PONG, * as it does not take \r\n into account. */ -int websocket_create_packet_simple(int opcode, char **buf, int *len) +int websocket_create_packet_simple(int opcode, const char **buf, int *len) { static char sendbuf[8192]; @@ -871,8 +1124,12 @@ int websocket_create_packet(int opcode, char **buf, int *len) if (bytes_in_sendbuf + bytes_single_frame > sizeof(sendbuf)) { /* Overflow. This should never happen. */ - sendto_ops("[websocket] [BUG] Overflow prevented: %d + %d > %d", - bytes_in_sendbuf, bytes_single_frame, (int)sizeof(sendbuf)); + unreal_log(ULOG_WARNING, "websocket", "BUG_WEBSOCKET_OVERFLOW", NULL, + "[BUG] [websocket] Overflow prevented in websocket_create_packet(): " + "$bytes_in_sendbuf + $bytes_single_frame > $sendbuf_size", + log_data_integer("bytes_in_sendbuf", bytes_in_sendbuf), + log_data_integer("bytes_single_frame", bytes_single_frame), + log_data_integer("sendbuf_size", sizeof(sendbuf))); return -1; } @@ -906,9 +1163,9 @@ int websocket_create_packet(int opcode, char **buf, int *len) } /** Create and send a WSOP_PONG frame */ -int websocket_send_pong(Client *client, char *buf, int len) +int websocket_send_pong(Client *client, const char *buf, int len) { - char *b = buf; + const char *b = buf; int l = len; if (websocket_create_packet_simple(WSOP_PONG, &b, &l) < 0) @@ -924,3 +1181,33 @@ int websocket_send_pong(Client *client, char *buf, int len) send_queued(client); return 0; } + +/** Compare IP addresses (for authorization checking) */ +int websocket_ip_compare(const char *ip1, const char *ip2) +{ + uint32_t ip4[2]; + uint16_t ip6[16]; + int i; + if (inet_pton(AF_INET, ip1, &ip4[0]) == 1) /* IPv4 */ + { + if (inet_pton(AF_INET, ip2, &ip4[1]) == 1) /* both are valid, let's compare */ + { + return ip4[0] == ip4[1]; + } + return 0; + } + if (inet_pton(AF_INET6, ip1, &ip6[0]) == 1) /* IPv6 */ + { + if (inet_pton(AF_INET6, ip2, &ip6[8]) == 1) + { + for (i = 0; i < 8; i++) + { + if (ip6[i] != ip6[i+8]) + return 0; + } + return 1; + } + } + return 0; /* neither valid IPv4 nor IPv6 */ +} + diff --git a/src/modules/who_old.c b/src/modules/who_old.c index df21686..d2a8572 100644 --- a/src/modules/who_old.c +++ b/src/modules/who_old.c @@ -36,7 +36,7 @@ ModuleHeader MOD_HEADER "5.0", /* Version */ "command /who (old version)", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; /* This is called on module init, before Server Ready */ @@ -64,12 +64,12 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static void do_channel_who(Client *client, Channel *channel, char *mask); +static void do_channel_who(Client *client, Channel *channel, const char *mask); static void make_who_status(Client *, Client *, Channel *, Member *, char *, int); -static void do_other_who(Client *client, char *mask); -static void send_who_reply(Client *, Client *, char *, char *, char *); -static char *first_visible_channel(Client *, Client *, int *); -static int parse_who_options(Client *, int, char**); +static void do_other_who(Client *client, const char *mask); +static void send_who_reply(Client *, Client *, const char *, const char *, const char *); +static const char *first_visible_channel(Client *, Client *, int *); +static int parse_who_options(Client *, int, const char **); static void who_sendhelp(Client *); #define WF_OPERONLY 0x01 /**< only show opers */ @@ -93,19 +93,19 @@ static int who_flags; struct { int want_away; int want_channel; - char *channel; /**< if they want one */ + const char *channel; /**< if they want one */ int want_gecos; - char *gecos; + const char *gecos; int want_server; - char *server; + const char *server; int want_host; - char *host; + const char *host; int want_nick; - char *nick; + const char *nick; int want_user; - char *user; + const char *user; int want_ip; - char *ip; + const char *ip; int want_port; int port; int want_umode; @@ -118,8 +118,8 @@ struct { CMD_FUNC(cmd_who) { Channel *target_channel; - char *mask = parv[1]; - char star[] = "*"; + const char *mask = parv[1]; + char maskbuf[512]; int i = 0; if (!MyUser(client)) @@ -139,14 +139,17 @@ CMD_FUNC(cmd_who) } if (parc-i < 2 || strcmp(parv[1 + i], "0") == 0) - mask = star; + mask = "*"; else mask = parv[1 + i]; if (!i && parc > 2 && *parv[2] == 'o') who_flags |= WF_OPERONLY; - collapse(mask); + /* Pfff... collapse... hate it! */ + strlcpy(maskbuf, mask, sizeof(maskbuf)); + collapse(maskbuf); + mask = maskbuf; if (*mask == '\0') { @@ -155,7 +158,7 @@ CMD_FUNC(cmd_who) return; } - if ((target_channel = find_channel(mask, NULL)) != NULL) + if ((target_channel = find_channel(mask)) != NULL) { do_channel_who(client, target_channel, mask); sendnumeric(client, RPL_ENDOFWHO, mask); @@ -163,7 +166,7 @@ CMD_FUNC(cmd_who) } if (wfl.channel && wfl.want_channel == WHO_WANT && - (target_channel = find_channel(wfl.channel, NULL)) != NULL) + (target_channel = find_channel(wfl.channel)) != NULL) { do_channel_who(client, target_channel, mask); sendnumeric(client, RPL_ENDOFWHO, mask); @@ -247,11 +250,11 @@ static void who_sendhelp(Client *client) #define WHO_ADD 1 #define WHO_DEL 2 -static int parse_who_options(Client *client, int argc, char **argv) +static int parse_who_options(Client *client, int argc, const char **argv) { -char *s = argv[0]; -int what = WHO_ADD; -int i = 1; + const char *s = argv[0]; + int what = WHO_ADD; + int i = 1; /* A few helper macro's because this is used a lot, added during recode by Syzop. */ @@ -319,7 +322,7 @@ int i = 1; case 'm': REQUIRE_PARAM() { - char *s = argv[i]; + const char *s = argv[i]; int *umodes; if (what == WHO_ADD) @@ -327,17 +330,7 @@ int i = 1; else umodes = &wfl.umodes_dontwant; - while (*s) - { - int i; - for (i = 0; i <= Usermode_highest; i++) - if (*s == Usermode_Table[i].flag) - { - *umodes |= Usermode_Table[i].mode; - break; - } - s++; - } + *umodes = set_usermode(s); if (!IsOper(client)) *umodes = *umodes & UMODE_OPER; /* these are usermodes regular users may search for. just oper now. */ @@ -418,7 +411,7 @@ static int can_see(Client *requester, Client *target, Channel *channel) /* if they only want people on a certain channel. */ if (wfl.want_channel != WHO_DONTCARE) { - Channel *chan = find_channel(wfl.channel, NULL); + Channel *chan = find_channel(wfl.channel); if (!chan && wfl.want_channel == WHO_WANT) return WHO_CANTSEE; if ((wfl.want_channel == WHO_WANT) && !IsMember(target, chan)) @@ -587,7 +580,7 @@ static int can_see(Client *requester, Client *target, Channel *channel) } } -static void do_channel_who(Client *client, Channel *channel, char *mask) +static void do_channel_who(Client *client, Channel *channel, const char *mask) { Member *cm = channel->members; if (IsMember(client, channel) || ValidatePermissionsForPath("channel:see:who:onchannel",client,NULL,channel,NULL)) @@ -602,7 +595,7 @@ static void do_channel_who(Client *client, Channel *channel, char *mask) continue; make_who_status(client, acptr, channel, cm, status, cansee); - send_who_reply(client, acptr, channel->chname, status, ""); + send_who_reply(client, acptr, channel->name, status, ""); } } @@ -617,7 +610,7 @@ static void make_who_status(Client *client, Client *acptr, Channel *channel, else status[i++] = 'H'; - if (IsARegNick(acptr)) + if (IsRegNick(acptr)) status[i++] = 'r'; if (IsSecureConnect(acptr)) @@ -643,39 +636,23 @@ static void make_who_status(Client *client, Client *acptr, Channel *channel, { if (HasCapability(client, "multi-prefix")) { -#ifdef PREFIX_AQ - if (cm->flags & CHFL_CHANOWNER) - status[i++] = '~'; - if (cm->flags & CHFL_CHANADMIN) - status[i++] = '&'; -#endif - if (cm->flags & CHFL_CHANOP) - status[i++] = '@'; - if (cm->flags & CHFL_HALFOP) - status[i++] = '%'; - if (cm->flags & CHFL_VOICE) - status[i++] = '+'; - } else { -#ifdef PREFIX_AQ - if (cm->flags & CHFL_CHANOWNER) - status[i++] = '~'; - else if (cm->flags & CHFL_CHANADMIN) - status[i++] = '&'; - else -#endif - if (cm->flags & CHFL_CHANOP) - status[i++] = '@'; - else if (cm->flags & CHFL_HALFOP) - status[i++] = '%'; - else if (cm->flags & CHFL_VOICE) - status[i++] = '+'; + /* Standard NAMES reply (single character) */ + char c = mode_to_prefix(*cm->member_modes); + if (c) + status[i++] = c; + } + else + { + /* NAMES reply with all rights included (multi-prefix / NAMESX) */ + strcpy(&status[i], modes_to_prefix(cm->member_modes)); + i += strlen(&status[i]); } } status[i] = '\0'; } -static void do_other_who(Client *client, char *mask) +static void do_other_who(Client *client, const char *mask) { int oper = IsOper(client); @@ -690,7 +667,7 @@ int oper = IsOper(client); { int cansee; char status[20]; - char *channel; + const char *channel; int flg; if (!IsUser(acptr)) @@ -733,7 +710,7 @@ matchok: Client *acptr = find_client(mask, NULL); int cansee; char status[20]; - char *channel; + const char *channel; int flg; if (!acptr) @@ -749,10 +726,10 @@ matchok: } static void send_who_reply(Client *client, Client *acptr, - char *channel, char *status, char *xstat) + const char *channel, const char *status, const char *xstat) { char *stat; - char *host; + const char *host; int flat = (FLAT_MAP && !IsOper(client)) ? 1 : 0; stat = safe_alloc(strlen(status) + strlen(xstat) + 1); @@ -799,7 +776,7 @@ static void send_who_reply(Client *client, Client *acptr, safe_free(stat); } -static char *first_visible_channel(Client *client, Client *acptr, int *flg) +static const char *first_visible_channel(Client *client, Client *acptr, int *flg) { Membership *lp; @@ -857,7 +834,7 @@ static char *first_visible_channel(Client *client, Client *acptr, int *flg) *flg |= FVC_HIDDEN; if (showchannel) - return channel->chname; + return channel->name; } /* no channels that they can see */ diff --git a/src/modules/whois.c b/src/modules/whois.c index c99b7fd..125fd17 100644 --- a/src/modules/whois.c +++ b/src/modules/whois.c @@ -1,7 +1,8 @@ /* * Unreal Internet Relay Chat Daemon, src/modules/whois.c * (C) 2000-2001 Carsten V. Munk and the UnrealIRCd Team - * Moved to modules by Fish (Justin Hammond) + * (C) 2003-2021 Bram Matthys and the UnrealIRCd team + * Moved to modules by Fish (Justin Hammond) in 2001 * * 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 @@ -20,54 +21,278 @@ #include "unrealircd.h" -static char buf[BUFSIZE]; - -CMD_FUNC(cmd_whois); - -#define MSG_WHOIS "WHOIS" - +/* Structs */ ModuleHeader MOD_HEADER = { "whois", /* Name of module */ "5.0", /* Version */ "command /whois", /* Short description of module */ "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; -/* This is called on module init, before Server Ready */ -MOD_INIT() +typedef enum WhoisConfigUser { + WHOIS_CONFIG_USER_EVERYONE = 1, + WHOIS_CONFIG_USER_SELF = 2, + WHOIS_CONFIG_USER_OPER = 3, +} WhoisConfigUser; +#define HIGHEST_WHOIS_CONFIG_USER_VALUE 3 /* adjust this if you edit the enum above !! */ + +//this one is in include/struct.h because it needs full API exposure: +//typedef enum WhoisConfigDetails { +// ... +//} WhoisConfigDetails; +// + +typedef struct WhoisConfig WhoisConfig; +struct WhoisConfig { + WhoisConfig *prev, *next; + char *name; + WhoisConfigDetails permissions[HIGHEST_WHOIS_CONFIG_USER_VALUE+1]; +}; + +/* Global variables */ +static char buf[BUFSIZE]; +WhoisConfig *whoisconfig = NULL; + +/* Forward declarations */ +WhoisConfigDetails _whois_get_policy(Client *client, Client *target, const char *name); +CMD_FUNC(cmd_whois); +static int whois_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +static int whois_config_run(ConfigFile *cf, ConfigEntry *ce, int type); +static void whois_config_setdefaults(void); + +MOD_TEST() { - CommandAdd(modinfo->handle, MSG_WHOIS, cmd_whois, MAXPARA, CMD_USER); MARK_AS_OFFICIAL_MODULE(modinfo); + EfunctionAdd(modinfo->handle, EFUNC_WHOIS_GET_POLICY, TO_INTFUNC(_whois_get_policy)); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, whois_config_test); + return MOD_SUCCESS; +} + +MOD_INIT() +{ + MARK_AS_OFFICIAL_MODULE(modinfo); + CommandAdd(modinfo->handle, "WHOIS", cmd_whois, MAXPARA, CMD_USER); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, whois_config_run); + whois_config_setdefaults(); return MOD_SUCCESS; } -/* Is first run when server is 100% ready */ MOD_LOAD() { return MOD_SUCCESS; } -/* Called when module is unloaded */ MOD_UNLOAD() { return MOD_SUCCESS; } +static WhoisConfig *find_whois_config(const char *name) +{ + WhoisConfig *w; + for (w = whoisconfig; w; w = w->next) + if (!strcmp(w->name, name)) + return w; + return NULL; +} -/* -** cmd_whois -** parv[1] = nickname masklist -*/ +/* Lazy helper for whois_config_setdefaults */ +static void whois_config_add(const char *name, WhoisConfigUser user, WhoisConfigDetails details) +{ + WhoisConfig *w = find_whois_config(name); + + if (!w) + { + /* New one */ + w = safe_alloc(sizeof(WhoisConfig)); + safe_strdup(w->name, name); + AddListItem(w, whoisconfig); + } + w->permissions[user] = details; +} + +static void whois_config_setdefaults(void) +{ + whois_config_add("basic", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("modes", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL); + whois_config_add("modes", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("realhost", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL); + whois_config_add("realhost", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("registered-nick", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("channels", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED); + whois_config_add("channels", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL); + whois_config_add("channels", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("server", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("away", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("oper", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED); + whois_config_add("oper", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL); + whois_config_add("oper", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("secure", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED); + whois_config_add("secure", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL); + whois_config_add("secure", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("bot", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("services", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("reputation", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("geo", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("certfp", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("shunned", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("account", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("swhois", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_FULL); + + whois_config_add("idle", WHOIS_CONFIG_USER_EVERYONE, WHOIS_CONFIG_DETAILS_LIMITED); + whois_config_add("idle", WHOIS_CONFIG_USER_SELF, WHOIS_CONFIG_DETAILS_FULL); + whois_config_add("idle", WHOIS_CONFIG_USER_OPER, WHOIS_CONFIG_DETAILS_FULL); +} + +static void whois_free_config(void) +{ +} + +static WhoisConfigUser whois_config_user_strtovalue(const char *str) +{ + if (!strcmp(str, "everyone")) + return WHOIS_CONFIG_USER_EVERYONE; + if (!strcmp(str, "self")) + return WHOIS_CONFIG_USER_SELF; + if (!strcmp(str, "oper")) + return WHOIS_CONFIG_USER_OPER; + return 0; +} + +static WhoisConfigDetails whois_config_details_strtovalue(const char *str) +{ + if (!strcmp(str, "full")) + return WHOIS_CONFIG_DETAILS_FULL; + if (!strcmp(str, "limited")) + return WHOIS_CONFIG_DETAILS_LIMITED; + if (!strcmp(str, "none")) + return WHOIS_CONFIG_DETAILS_NONE; + return 0; +} + +static int whois_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +{ + int errors = 0; + ConfigEntry *cep, *cepp; + + if (type != CONFIG_SET) + return 0; + + /* We are only interrested in set::whois-details.. */ + if (!ce || strcmp(ce->name, "whois-details")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + if (cep->value) + { + config_error("%s:%i: set::whois-details::%s item has a value, which is unexpected. Check your syntax!", + cep->file->filename, cep->line_number, cep->name); + errors++; + continue; + } + for (cepp = cep->items; cepp; cepp = cepp->next) + { + if (!whois_config_user_strtovalue(cepp->name)) + { + config_error("%s:%i: set::whois-details::%s contains unknown user category called '%s', must be one of: everyone, self, ircop", + cepp->file->filename, cepp->line_number, cep->name, cepp->name); + errors++; + continue; + } else + if (!cepp->value || !whois_config_details_strtovalue(cepp->value)) + { + config_error("%s:%i: set::whois-details::%s contains unknown details type '%s', must be one of: full, limited, none", + cepp->file->filename, cepp->line_number, cep->name, cepp->name); + errors++; + continue; + } /* else it is good */ + } + } + + *errs = errors; + return errors ? -1 : 1; +} + +static int whois_config_run(ConfigFile *cf, ConfigEntry *ce, int type) +{ + ConfigEntry *cep, *cepp; + + if (type != CONFIG_SET) + return 0; + + /* We are only interrested in set::whois-details.. */ + if (!ce || strcmp(ce->name, "whois-details")) + return 0; + + for (cep = ce->items; cep; cep = cep->next) + { + WhoisConfig *w = find_whois_config(cep->name); + if (!w) + { + /* New one */ + w = safe_alloc(sizeof(WhoisConfig)); + safe_strdup(w->name, cep->name); + AddListItem(w, whoisconfig); + } + for (cepp = cep->items; cepp; cepp = cepp->next) + { + WhoisConfigUser user = whois_config_user_strtovalue(cepp->name); + WhoisConfigDetails details = whois_config_details_strtovalue(cepp->value); + w->permissions[user] = details; + } + } + return 1; +} + +/** Get set::whois-details policy for an item. + * @param client The client doing the /WHOIS + * @param target The client being whoised, so the one to show all details for + * @param name The name of the whois item (eg "modes") + */ +WhoisConfigDetails _whois_get_policy(Client *client, Client *target, const char *name) +{ + WhoisConfig *w = find_whois_config(name); + if (!w) + return WHOIS_CONFIG_DETAILS_DEFAULT; + if ((client == target) && (w->permissions[WHOIS_CONFIG_USER_SELF] > 0)) + return w->permissions[WHOIS_CONFIG_USER_SELF]; + if (IsOper(client) && (w->permissions[WHOIS_CONFIG_USER_OPER] > 0)) + return w->permissions[WHOIS_CONFIG_USER_OPER]; + if (w->permissions[WHOIS_CONFIG_USER_EVERYONE] > 0) + return w->permissions[WHOIS_CONFIG_USER_EVERYONE]; + return WHOIS_CONFIG_DETAILS_NONE; +} + +/* WHOIS command. + * parv[1] = list of nicks (comma separated) + */ CMD_FUNC(cmd_whois) { Membership *lp; Client *target; Channel *channel; - char *nick, *tmp, *name; + char *nick, *tmp; char *p = NULL; - int found, len, mlen; + int len, mlen; char querybuf[BUFSIZE]; int ntargets = 0; int maxtargets = max_targets_for_command("WHOIS"); @@ -80,7 +305,7 @@ CMD_FUNC(cmd_whois) if (parc > 2) { - if (hunt_server(client, recv_mtags, ":%s WHOIS %s :%s", 1, parc, parv) != HUNTED_ISME) + if (hunt_server(client, recv_mtags, "WHOIS", 1, parc, parv) != HUNTED_ISME) return; parv[1] = parv[2]; } @@ -90,6 +315,8 @@ CMD_FUNC(cmd_whois) for (tmp = canonize(parv[1]); (nick = strtoken(&p, tmp, ",")); tmp = NULL) { unsigned char showchannel, wilds, hideoper; /* <- these are all boolean-alike */ + NameValuePrioList *list = NULL, *e; + int policy; /* for temporary stuff */ if (MyUser(client) && (++ntargets > maxtargets)) { @@ -97,55 +324,62 @@ CMD_FUNC(cmd_whois) break; } - found = 0; /* We do not support "WHOIS *" */ wilds = (strchr(nick, '?') || strchr(nick, '*')); if (wilds) continue; - if ((target = find_person(nick, NULL))) + target = find_user(nick, NULL); + if (!target) { - /* - * 'Rules' established for sending a WHOIS reply: - * - only send replies about common or public channels - * the target user(s) are on; - */ + sendnumeric(client, ERR_NOSUCHNICK, nick); + continue; + } - if (!IsUser(target)) - continue; + /* Ok, from this point we are going to proceed with the WHOIS. + * The idea here is NOT to send any lines, so don't call sendto functions. + * Instead, use add_nvplist_numeric() and add_nvplist_numeric_fmt() + * to add items to the whois list. + * Then at the end of this loop we call modules who can also add/remove + * whois lines, and only after that we FINALLY send all the whois lines + * in one go. + */ - name = (!*target->name) ? "?" : target->name; + hideoper = 0; + if (IsHideOper(target) && (target != client) && !IsOper(client)) + hideoper = 1; - hideoper = 0; - if (IsHideOper(target) && (target != client) && !IsOper(client)) - hideoper = 1; + if (whois_get_policy(client, target, "basic") > WHOIS_CONFIG_DETAILS_NONE) + { + add_nvplist_numeric(&list, -1000000, "basic", client, RPL_WHOISUSER, target->name, + target->user->username, + IsHidden(target) ? target->user->virthost : target->user->realhost, + target->info); + } - sendnumeric(client, RPL_WHOISUSER, name, - target->user->username, - IsHidden(target) ? target->user->virthost : target->user->realhost, - target->info); + if (whois_get_policy(client, target, "modes") > WHOIS_CONFIG_DETAILS_NONE) + { + add_nvplist_numeric(&list, -100000, "modes", client, RPL_WHOISMODES, target->name, + get_usermode_string(target), target->user->snomask ? target->user->snomask : ""); + } + if (whois_get_policy(client, target, "realhost") > WHOIS_CONFIG_DETAILS_NONE) + { + add_nvplist_numeric(&list, -90000, "realhost", client, RPL_WHOISHOST, target->name, + (MyConnect(target) && strcmp(target->ident, "unknown")) ? target->ident : "*", + target->user->realhost, target->ip ? target->ip : ""); + } - if (IsOper(client) || target == client) - { - char sno[128]; - strlcpy(sno, get_snomask_string(target), sizeof(sno)); - - /* send the target user's modes */ - sendnumeric(client, RPL_WHOISMODES, name, - get_usermode_string(target), sno[1] == 0 ? "" : sno); - } - if ((target == client) || IsOper(client)) - { - sendnumeric(client, RPL_WHOISHOST, target->name, - (MyConnect(target) && strcmp(target->ident, "unknown")) ? target->ident : "*", - target->user->realhost, target->ip ? target->ip : ""); - } + if (IsRegNick(target) && (whois_get_policy(client, target, "registered-nick") > WHOIS_CONFIG_DETAILS_NONE)) + { + add_nvplist_numeric(&list, -80000, "registered-nick", client, RPL_WHOISREGNICK, target->name); + } - if (IsARegNick(target)) - sendnumeric(client, RPL_WHOISREGNICK, name); - - found = 1; - mlen = strlen(me.name) + strlen(client->name) + 10 + strlen(name); + /* The following code deals with channels */ + policy = whois_get_policy(client, target, "channels"); + if (policy > WHOIS_CONFIG_DETAILS_NONE) + { + int channel_whois_lines = 0; + mlen = strlen(me.name) + strlen(client->name) + 10 + strlen(target->name); for (len = 0, *buf = '\0', lp = target->user->channel; lp; lp = lp->next) { Hook *h; @@ -181,7 +415,12 @@ CMD_FUNC(cmd_whois) if (ret == EX_DENY) showchannel = 0; - if (!showchannel && (ValidatePermissionsForPath("channel:see:whois",client,NULL,channel,NULL))) + /* If the channel is normally hidden, but the user is an IRCOp, + * and has the channel:see:whois privilege, + * and set::whois-details for 'channels' has 'oper full', + * then show it: + */ + if (!showchannel && (ValidatePermissionsForPath("channel:see:whois",client,NULL,channel,NULL)) && (policy == WHOIS_CONFIG_DETAILS_FULL)) { showchannel = 1; /* OperOverride */ operoverride = 1; @@ -190,19 +429,19 @@ CMD_FUNC(cmd_whois) if ((ret == EX_ALWAYS_DENY) && (target != client)) continue; /* a module asked us to really not expose this channel, so we don't (except target==ourselves). */ - if (target == client) + /* This deals with target==client but also for unusual set::whois-details overrides + * such as 'everyone full' + */ + if (policy == WHOIS_CONFIG_DETAILS_FULL) showchannel = 1; if (showchannel) { - long access; - if (len + strlen(channel->chname) > (size_t)BUFSIZE - 4 - mlen) + if (len + strlen(channel->name) > (size_t)BUFSIZE - 4 - mlen) { - sendto_one(client, NULL, - ":%s %d %s %s :%s", - me.name, - RPL_WHOISCHANNELS, - client->name, name, buf); + add_nvplist_numeric_fmt(&list, -70500-channel_whois_lines, "channels", client, RPL_WHOISCHANNELS, + "%s :%s", target->name, buf); + channel_whois_lines++; *buf = '\0'; len = 0; } @@ -224,125 +463,152 @@ CMD_FUNC(cmd_whois) } } - access = get_access(target, channel); if (!MyUser(client) || !HasCapability(client, "multi-prefix")) { -#ifdef PREFIX_AQ - if (access & CHFL_CHANOWNER) - *(buf + len++) = '~'; - else if (access & CHFL_CHANADMIN) - *(buf + len++) = '&'; - else -#endif - if (access & CHFL_CHANOP) - *(buf + len++) = '@'; - else if (access & CHFL_HALFOP) - *(buf + len++) = '%'; - else if (access & CHFL_VOICE) - *(buf + len++) = '+'; + /* Standard NAMES reply (single character) */ + char c = mode_to_prefix(*lp->member_modes); + if (c) + *(buf + len++) = c; } else { -#ifdef PREFIX_AQ - if (access & CHFL_CHANOWNER) - *(buf + len++) = '~'; - if (access & CHFL_CHANADMIN) - *(buf + len++) = '&'; -#endif - if (access & CHFL_CHANOP) - *(buf + len++) = '@'; - if (access & CHFL_HALFOP) - *(buf + len++) = '%'; - if (access & CHFL_VOICE) - *(buf + len++) = '+'; + /* NAMES reply with all rights included (multi-prefix / NAMESX) */ + strcpy(buf + len, modes_to_prefix(lp->member_modes)); + len += strlen(buf + len); } if (len) *(buf + len) = '\0'; - strcpy(buf + len, channel->chname); - len += strlen(channel->chname); + strcpy(buf + len, channel->name); + len += strlen(channel->name); strcat(buf + len, " "); len++; } } if (buf[0] != '\0') - sendnumeric(client, RPL_WHOISCHANNELS, name, buf); - - if (!(IsULine(target) && !IsOper(client) && HIDE_ULINES)) - sendnumeric(client, RPL_WHOISSERVER, name, target->user->server, - target->srvptr ? target->srvptr->info : "*Not On This Net*"); - - if (target->user->away) - sendnumeric(client, RPL_AWAY, name, target->user->away); - - if (IsOper(target) && !hideoper) { - buf[0] = '\0'; - if (IsOper(target)) - strlcat(buf, "an IRC Operator", sizeof buf); - - else - strlcat(buf, "a Local IRC Operator", sizeof buf); - if (buf[0]) - { - if (IsOper(client) && MyUser(target)) - { - char *operclass = "???"; - ConfigItem_oper *oper = find_oper(target->user->operlogin); - if (oper && oper->operclass) - operclass = oper->operclass; - sendto_one(client, NULL, - ":%s 313 %s %s :is %s (%s) [%s]", me.name, - client->name, name, buf, - target->user->operlogin ? target->user->operlogin : "unknown", - operclass); - } - else - sendnumeric(client, RPL_WHOISOPERATOR, name, buf); - } - } - - if (target->umodes & UMODE_SECURE) - sendnumeric(client, RPL_WHOISSECURE, name, - "is using a Secure Connection"); - - RunHook2(HOOKTYPE_WHOIS, client, target); - - if (IsOper(client) && MyUser(target) && IsShunned(target)) - { - sendto_one(client, NULL, ":%s %d %s %s :is shunned", - me.name, RPL_WHOISSPECIAL, client->name, target->name); - } - - if (target->user->swhois && !hideoper) - { - SWhois *s; - - for (s = target->user->swhois; s; s = s->next) - sendto_one(client, NULL, ":%s %d %s %s :%s", - me.name, RPL_WHOISSPECIAL, client->name, - name, s->line); - } - - /* - * display services account name if it's actually a services account name and - * not a legacy timestamp. --nenolod - */ - if (!isdigit(*target->user->svid)) - sendnumeric(client, RPL_WHOISLOGGEDIN, name, target->user->svid); - - /* - * Umode +I hides an oper's idle time from regular users. - * -Nath. - */ - if (MyConnect(target) && !hide_idle_time(client, target)) - { - sendnumeric(client, RPL_WHOISIDLE, name, - TStime() - target->local->last, target->local->firsttime); + add_nvplist_numeric_fmt(&list, -70500-channel_whois_lines, "channels", client, RPL_WHOISCHANNELS, + "%s :%s", target->name, buf); + channel_whois_lines++; } } - if (!found) - sendnumeric(client, ERR_NOSUCHNICK, nick); + + if (!(IsULine(target) && !IsOper(client) && HIDE_ULINES) && + whois_get_policy(client, target, "server") > WHOIS_CONFIG_DETAILS_NONE) + { + add_nvplist_numeric(&list, -60000, "server", client, RPL_WHOISSERVER, + target->name, target->user->server, target->uplink->info); + } + + if (target->user->away && (whois_get_policy(client, target, "away") > WHOIS_CONFIG_DETAILS_NONE)) + { + add_nvplist_numeric(&list, -50000, "away", client, RPL_AWAY, + target->name, target->user->away); + } + + if (IsOper(target) && !hideoper) + { + policy = whois_get_policy(client, target, "oper"); + if (policy == WHOIS_CONFIG_DETAILS_FULL) + { + const char *operlogin = get_operlogin(target); + const char *operclass = get_operclass(target); + + if (operlogin && operclass) + { + add_nvplist_numeric_fmt(&list, -40000, "oper", client, RPL_WHOISOPERATOR, + "%s :is %s (%s) [%s]", + target->name, "an IRC Operator", operlogin, operclass); + } else + if (operlogin) + { + add_nvplist_numeric_fmt(&list, -40000, "oper", client, RPL_WHOISOPERATOR, + "%s :is %s (%s)", + target->name, "an IRC Operator", operlogin); + } else + { + add_nvplist_numeric(&list, -40000, "oper", client, RPL_WHOISOPERATOR, + target->name, "an IRC Operator"); + } + } else + if (policy == WHOIS_CONFIG_DETAILS_LIMITED) + { + add_nvplist_numeric(&list, -40000, "oper", client, RPL_WHOISOPERATOR, + target->name, "an IRC Operator"); + } + } + + if (target->umodes & UMODE_SECURE) + { + policy = whois_get_policy(client, target, "secure"); + if (policy == WHOIS_CONFIG_DETAILS_LIMITED) + { + add_nvplist_numeric(&list, -30000, "secure", client, RPL_WHOISSECURE, + target->name, "is using a Secure Connection"); + } else + if (policy == WHOIS_CONFIG_DETAILS_FULL) + { + const char *ciphers = tls_get_cipher(target); + if (ciphers) + { + add_nvplist_numeric_fmt(&list, -30000, "secure", client, RPL_WHOISSECURE, + "%s :is using a Secure Connection [%s]", + target->name, ciphers); + } else { + add_nvplist_numeric(&list, -30000, "secure", client, RPL_WHOISSECURE, + target->name, "is using a Secure Connection"); + } + } + } + + if (MyUser(target) && IsShunned(target) && (whois_get_policy(client, target, "shunned") > WHOIS_CONFIG_DETAILS_NONE)) + { + add_nvplist_numeric(&list, -20000, "shunned", client, RPL_WHOISSPECIAL, + target->name, "is shunned"); + } + + if (target->user->swhois && !hideoper && (whois_get_policy(client, target, "swhois") > WHOIS_CONFIG_DETAILS_NONE)) + { + SWhois *s; + int swhois_lines = 0; + + for (s = target->user->swhois; s; s = s->next) + { + add_nvplist_numeric(&list, 100000+swhois_lines, "swhois", client, RPL_WHOISSPECIAL, + target->name, s->line); + swhois_lines++; + } + } + + /* TODO: hmm.. this should be a bit more towards the beginning of the whois, no ? */ + if (IsLoggedIn(target) && (whois_get_policy(client, target, "account") > WHOIS_CONFIG_DETAILS_NONE)) + { + add_nvplist_numeric(&list, 200000, "account", client, RPL_WHOISLOGGEDIN, + target->name, target->user->account); + } + + if (MyConnect(target)) + { + policy = whois_get_policy(client, target, "idle"); + /* If the policy is 'full' then show the idle time. + * If the policy is 'limited then show the idle time according to the +I rules + */ + if ((policy == WHOIS_CONFIG_DETAILS_FULL) || + ((policy == WHOIS_CONFIG_DETAILS_LIMITED) && !hide_idle_time(client, target))) + { + add_nvplist_numeric(&list, 500000, "idle", client, RPL_WHOISIDLE, + target->name, + (long long)(TStime() - target->local->idle_since), + (long long)target->local->creationtime); + } + } + + RunHook(HOOKTYPE_WHOIS, client, target, &list); + + for (e = list; e; e = e->next) + sendto_one(client, NULL, "%s", e->value); + + free_nvplist(list); } sendnumeric(client, RPL_ENDOFWHOIS, querybuf); } diff --git a/src/modules/whowas.c b/src/modules/whowas.c index 8cb41c6..2ab48a0 100644 --- a/src/modules/whowas.c +++ b/src/modules/whowas.c @@ -32,7 +32,7 @@ ModuleHeader MOD_HEADER "5.0", "command /whowas", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; MOD_INIT() @@ -53,8 +53,8 @@ MOD_UNLOAD() } /* externally defined functions */ -extern aWhowas MODVAR WHOWAS[NICKNAMEHISTORYLENGTH]; -extern aWhowas MODVAR *WHOWASHASH[WHOWAS_HASH_TABLE_SIZE]; +extern WhoWas MODVAR WHOWAS[NICKNAMEHISTORYLENGTH]; +extern WhoWas MODVAR *WHOWASHASH[WHOWAS_HASH_TABLE_SIZE]; /* ** cmd_whowas @@ -62,7 +62,8 @@ extern aWhowas MODVAR *WHOWASHASH[WHOWAS_HASH_TABLE_SIZE]; */ CMD_FUNC(cmd_whowas) { - aWhowas *temp; + char request[BUFSIZE]; + WhoWas *temp; int cur = 0; int max = -1, found = 0; char *p, *nick; @@ -75,16 +76,17 @@ CMD_FUNC(cmd_whowas) if (parc > 2) max = atoi(parv[2]); if (parc > 3) - if (hunt_server(client, recv_mtags, ":%s WHOWAS %s %s :%s", 3, parc, parv)) + if (hunt_server(client, recv_mtags, "WHOWAS", 3, parc, parv)) return; if (!MyConnect(client) && (max > 20)) max = 20; - p = strchr(parv[1], ','); + strlcpy(request, parv[1], sizeof(request)); + p = strchr(request, ','); if (p) *p = '\0'; - nick = parv[1]; + nick = request; temp = WHOWASHASH[hash_whowas_name(nick)]; found = 0; for (; temp; temp = temp->next) @@ -109,5 +111,5 @@ CMD_FUNC(cmd_whowas) if (!found) sendnumeric(client, ERR_WASNOSUCHNICK, nick); - sendnumeric(client, RPL_ENDOFWHOWAS, parv[1]); + sendnumeric(client, RPL_ENDOFWHOWAS, request); } diff --git a/src/modules/whox.c b/src/modules/whox.c index 7cbc953..ca4239e 100644 --- a/src/modules/whox.c +++ b/src/modules/whox.c @@ -15,7 +15,7 @@ ModuleHeader MOD_HEADER "5.0", "command /who", "UnrealIRCd Team", - "unrealircd-5", + "unrealircd-6", }; @@ -80,10 +80,11 @@ static void who_global(Client *client, char *mask, int operspy, struct who_forma static void do_who(Client *client, Client *acptr, Channel *channel, struct who_format *fmt); static void do_who_on_channel(Client *client, Channel *channel, int member, int operspy, struct who_format *fmt); -static int convert_classical_who_request(Client *client, int *parc, char *parv[], char **orig_mask, struct who_format *fmt); -char *whox_md_serialize(ModData *m); -void whox_md_unserialize(char *str, ModData *m); +static int convert_classical_who_request(Client *client, int *parc, const char *parv[], const char **orig_mask, struct who_format *fmt); +const char *whox_md_serialize(ModData *m); +void whox_md_unserialize(const char *str, ModData *m); void whox_md_free(ModData *md); +static void append_format(char *buf, size_t bufsize, size_t *pos, const char *fmt, ...) __attribute__((format(printf,4,5))); MOD_INIT() { @@ -126,7 +127,7 @@ MOD_UNLOAD() } /** whox module data operations: serialize (rare) */ -char *whox_md_serialize(ModData *m) +const char *whox_md_serialize(ModData *m) { static char buf[32]; if (m->i == 0) @@ -136,7 +137,7 @@ char *whox_md_serialize(ModData *m) } /** whox module data operations: unserialize (rare) */ -void whox_md_unserialize(char *str, ModData *m) +void whox_md_unserialize(const char *str, ModData *m) { m->i = atoi(str); } @@ -179,9 +180,9 @@ void whox_md_free(ModData *md) CMD_FUNC(cmd_whox) { char *mask; - char *orig_mask; + const char *orig_mask; char ch; /* Scratch char register */ - char *p; /* Scratch char pointer */ + const char *p; /* Scratch char pointer */ int member; int operspy = 0; struct who_format fmt; @@ -294,7 +295,7 @@ CMD_FUNC(cmd_whox) while (*s) { - int i; + Umode *um; switch (*s) { @@ -316,11 +317,11 @@ CMD_FUNC(cmd_whox) else umodes = &fmt.noumodes; - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (*s == Usermode_Table[i].flag) + if (um->letter == *s) { - *umodes |= Usermode_Table[i].mode; + *umodes |= um->mode; break; } } @@ -341,7 +342,7 @@ CMD_FUNC(cmd_whox) Channel *channel = NULL; /* List all users on a given channel */ - if ((channel = find_channel(orig_mask, NULL)) != NULL) + if ((channel = find_channel(orig_mask)) != NULL) { if (IsMember(client, channel) || operspy) do_who_on_channel(client, channel, 1, operspy, &fmt); @@ -426,8 +427,7 @@ static int do_match(Client *client, Client *acptr, char *mask, struct who_format return 1; /* match account */ - if (IsMatch(fmt, WMATCH_ACCOUNT) && !BadPtr(acptr->user->svid) && - !isdigit(*acptr->user->svid) && match_simple(mask, acptr->user->svid)) + if (IsMatch(fmt, WMATCH_ACCOUNT) && IsLoggedIn(acptr) && match_simple(mask, acptr->user->account)) { return 1; } @@ -491,12 +491,12 @@ static void who_common_channel(Client *client, Channel *channel, break; } - if (i != 0 && !(is_skochanop(client, channel)) && !(is_skochanop(acptr, channel) || has_voice(acptr,channel))) + if (i != 0 && !(check_channel_access(client, channel, "hoaq")) && !(check_channel_access(acptr, channel, "hoaq") || check_channel_access(acptr,channel, "v"))) continue; SetMark(acptr); - if(*maxmatches > 0) + if (*maxmatches > 0) { if (do_match(client, acptr, mask, fmt)) { @@ -530,7 +530,7 @@ static void who_global(Client *client, char *mask, int operspy, struct who_forma /* If searching for a nick explicitly, then include it later on in the result: */ if (mask && ((fmt->matchsel & WMATCH_NICK) || (fmt->matchsel == 0))) - hunted = find_person(mask, NULL); + hunted = find_user(mask, NULL); /* Initialize the markers to zero */ list_for_each_entry(acptr, &client_list, client_node) @@ -608,10 +608,10 @@ static void do_who_on_channel(Client *client, Channel *channel, break; } - if (!operspy && (acptr != client) && i != 0 && !(is_skochanop(client, channel)) && !(is_skochanop(acptr, channel) || has_voice(acptr,channel))) + if (!operspy && (acptr != client) && i != 0 && !(check_channel_access(client, channel, "hoaq")) && !(check_channel_access(acptr, channel, "hoaq") || check_channel_access(acptr,channel, "v"))) continue; - if(member || !IsInvisible(acptr)) + if (member || !IsInvisible(acptr)) do_who(client, acptr, channel, fmt); } } @@ -688,7 +688,7 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f else status[i++] = 'H'; - if (IsARegNick(acptr)) + if (IsRegNick(acptr)) status[i++] = 'r'; if (IsSecureConnect(acptr)) @@ -715,36 +715,16 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f { if (!(fmt->fields || HasCapability(client, "multi-prefix"))) { - /* Standard NAMES reply */ -#ifdef PREFIX_AQ - if (lp->flags & CHFL_CHANOWNER) - status[i++] = '~'; - else if (lp->flags & CHFL_CHANADMIN) - status[i++] = '&'; - else -#endif - if (lp->flags & CHFL_CHANOP) - status[i++] = '@'; - else if (lp->flags & CHFL_HALFOP) - status[i++] = '%'; - else if (lp->flags & CHFL_VOICE) - status[i++] = '+'; + /* Standard NAMES reply (single character) */ + char c = mode_to_prefix(*lp->member_modes); + if (c) + status[i++] = c; } else { /* NAMES reply with all rights included (multi-prefix / NAMESX) */ -#ifdef PREFIX_AQ - if (lp->flags & CHFL_CHANOWNER) - status[i++] = '~'; - if (lp->flags & CHFL_CHANADMIN) - status[i++] = '&'; -#endif - if (lp->flags & CHFL_CHANOP) - status[i++] = '@'; - if (lp->flags & CHFL_HALFOP) - status[i++] = '%'; - if (lp->flags & CHFL_VOICE) - status[i++] = '+'; + strcpy(&status[i], modes_to_prefix(lp->member_modes)); + i += strlen(&status[i]); } } } @@ -761,7 +741,7 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f else host = GetHost(acptr); sendnumeric(client, RPL_WHOREPLY, - channel ? channel->chname : "*", + channel ? channel->name : "*", acptr->user->username, host, hide ? "*" : acptr->user->server, acptr->name, status, hide ? 0 : acptr->hopcount, acptr->info); @@ -773,7 +753,7 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f if (HasField(fmt, FIELD_QUERYTYPE)) append_format(str, sizeof str, &pos, " %s", fmt->querytype); if (HasField(fmt, FIELD_CHANNEL)) - append_format(str, sizeof str, &pos, " %s", channel ? channel->chname : "*"); + append_format(str, sizeof str, &pos, " %s", channel ? channel->name : "*"); if (HasField(fmt, FIELD_USER)) append_format(str, sizeof str, &pos, " %s", acptr->user->username); if (HasField(fmt, FIELD_IP)) @@ -801,21 +781,26 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f if (HasField(fmt, FIELD_MODES)) { if (IsOper(client)) - append_format(str, sizeof str, &pos, " %s", strtok(get_usermode_string(acptr), "+")); - else + { + const char *umodes = get_usermode_string(acptr); + if (*umodes == '+') + umodes++; + append_format(str, sizeof str, &pos, " %s", umodes); + } else { append_format(str, sizeof str, &pos, " %s", "*"); + } } if (HasField(fmt, FIELD_HOP)) append_format(str, sizeof str, &pos, " %d", hide ? 0 : acptr->hopcount); if (HasField(fmt, FIELD_IDLE)) { append_format(str, sizeof str, &pos, " %d", - (int)((MyUser(acptr) && !hide_idle_time(client, acptr)) ? (TStime() - acptr->local->last) : 0)); + (int)((MyUser(acptr) && !hide_idle_time(client, acptr)) ? (TStime() - acptr->local->idle_since) : 0)); } if (HasField(fmt, FIELD_ACCOUNT)) - append_format(str, sizeof str, &pos, " %s", (!isdigit(*acptr->user->svid)) ? acptr->user->svid : "0"); + append_format(str, sizeof str, &pos, " %s", IsLoggedIn(acptr) ? acptr->user->account : "0"); if (HasField(fmt, FIELD_OPLEVEL)) - append_format(str, sizeof str, &pos, " %s", (channel && is_skochanop(acptr, channel)) ? "999" : "n/a"); + append_format(str, sizeof str, &pos, " %s", (channel && check_channel_access(acptr, channel, "hoaq")) ? "999" : "n/a"); if (HasField(fmt, FIELD_REPUTATION)) { if (IsOper(client)) @@ -826,22 +811,15 @@ static void do_who(Client *client, Client *acptr, Channel *channel, struct who_f if (HasField(fmt, FIELD_INFO)) append_format(str, sizeof str, &pos, " :%s", acptr->info); - if (pos >= sizeof str) - { - static int warned = 0; - if (!warned) - sendto_snomask(SNO_JUNK, "*** WHOX overflow while sending information about %s to %s", acptr->name, client->name); - warned = 1; - } sendto_one(client, NULL, "%s", str); } } /* Yeah, this is fun. Thank you WHOX !!! */ -static int convert_classical_who_request(Client *client, int *parc, char *parv[], char **orig_mask, struct who_format *fmt) +static int convert_classical_who_request(Client *client, int *parc, const char *parv[], const char **orig_mask, struct who_format *fmt) { - char *p; - static char pbuf1[256]; + const char *p; + static char pbuf1[512], pbuf2[512]; int points; /* Figure out if the user is doing a 'classical' UnrealIRCd request, @@ -887,7 +865,7 @@ static int convert_classical_who_request(Client *client, int *parc, char *parv[] parv[1], parv[2] ? " " : "", parv[2] ? parv[2] : ""); if (parv[2]) { - char *swap = parv[1]; + const char *swap = parv[1]; parv[1] = parv[2]; parv[2] = swap; } else { @@ -925,13 +903,19 @@ static int convert_classical_who_request(Client *client, int *parc, char *parv[] sendnotice(client, "WHO request '%s' failed: flag 'c' no longer exists with WHOX.", oldrequest); return 0; } - for (p = parv[2]; *p; p++) + if (strchr(parv[2], 'g')) { - if (*p == 'g') + char *w; + strlcpy(pbuf2, parv[2], sizeof(pbuf2)); + for (w = pbuf2; *w; w++) { - *p = 'r'; - break; + if (*w == 'g') + { + *w = 'r'; + break; + } } + parv[2] = pbuf2; } /* "WHO -m xyz" (now: xyz -m) should become "WHO -xyz m" diff --git a/src/numeric.c b/src/numeric.c deleted file mode 100644 index 1481240..0000000 --- a/src/numeric.c +++ /dev/null @@ -1,1056 +0,0 @@ -/* - * Unreal Internet Relay Chat Daemon, src/numeric.c - * Copyright (C) 1992 Darren Reed - * - * 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 Numeric replies in the IRC protocol. - */ - -#include "unrealircd.h" - -/** Numeric replies */ -static char *replies[] = { -/* 000 */ NULL, -/* 001 RPL_WELCOME */ ":Welcome to the %s IRC Network %s!%s@%s", -/* 002 RPL_YOURHOST */ ":Your host is %s, running version %s", -/* 003 RPL_CREATED */ ":This server was created %s", -/* 004 RPL_MYINFO */ "%s %s %s %s", -/* 005 RPL_ISUPPORT */ "%s :are supported by this server", -/* 006 RPL_MAP */ ":%s%-*s(%ld) %s", -/* 007 RPL_MAPEND */ ":End of /MAP", -/* 008 RPL_SNOMASK */ "%s :Server notice mask", -/* 009 */ NULL, /* ircu */ -/* 010 RPL_REDIR */ "%s %d :Please use this Server/Port instead", -/* 011 */ NULL, -/* 012 */ NULL, -/* 013 */ NULL, -/* 014 */ NULL, /* hybrid */ -/* 015 */ NULL, -/* 016 */ NULL, -/* 017 */ NULL, -/* 018 */ NULL, -/* 019 */ NULL, -/* 020 */ NULL, -/* 021 */ NULL, -/* 022 */ NULL, -/* 023 */ NULL, -/* 024 */ NULL, -/* 025 */ NULL, -/* 026 */ NULL, -/* 027 */ NULL, -/* 028 */ NULL, -/* 029 */ NULL, -/* 030 */ NULL, -/* 031 */ NULL, -/* 032 */ NULL, -/* 033 */ NULL, -/* 034 */ NULL, -/* 035 */ NULL, -/* 036 */ NULL, -/* 037 */ NULL, -/* 038 */ NULL, -/* 039 */ NULL, -/* 040 */ NULL, -/* 041 */ NULL, -/* 042 RPL_YOURID */ "%s :your unique ID", -/* 043 */ NULL, /* ircnet */ -/* 044 */ NULL, -/* 045 */ NULL, -/* 046 */ NULL, -/* 047 */ NULL, -/* 048 */ NULL, -/* 049 */ NULL, -/* 050 */ NULL, /* aircd */ -/* 051 */ NULL, /* aircd */ -/* 052 */ NULL, -/* 053 */ NULL, -/* 054 */ NULL, -/* 055 */ NULL, -/* 056 */ NULL, -/* 057 */ NULL, -/* 058 */ NULL, -/* 059 */ NULL, -/* 060 */ NULL, -/* 061 */ NULL, -/* 062 */ NULL, -/* 063 */ NULL, -/* 064 */ NULL, -/* 065 */ NULL, -/* 066 */ NULL, -/* 067 */ NULL, -/* 068 */ NULL, -/* 069 */ NULL, -/* 070 */ NULL, -/* 071 */ NULL, -/* 072 */ NULL, -/* 073 */ NULL, -/* 074 */ NULL, -/* 075 */ NULL, -/* 076 */ NULL, -/* 077 */ NULL, -/* 078 */ NULL, -/* 079 */ NULL, -/* 080 */ NULL, -/* 081 */ NULL, -/* 082 */ NULL, -/* 083 */ NULL, -/* 084 */ NULL, -/* 085 */ NULL, -/* 086 */ NULL, -/* 087 */ NULL, -/* 088 */ NULL, -/* 089 */ NULL, -/* 090 */ NULL, -/* 091 */ NULL, -/* 092 */ NULL, -/* 093 */ NULL, -/* 094 */ NULL, -/* 095 */ NULL, -/* 096 */ NULL, -/* 097 */ NULL, -/* 098 */ NULL, -/* 099 */ NULL, -/* 100 */ NULL, -/* 101 */ NULL, -/* 102 */ NULL, -/* 103 */ NULL, -/* 104 */ NULL, -/* 105 RPL_REMOTEISUPPORT */ "%s :are supported by this server", -/* 106 */ NULL, -/* 107 */ NULL, -/* 108 */ NULL, -/* 109 */ NULL, -/* 110 */ NULL, -/* 111 */ NULL, -/* 112 */ NULL, -/* 113 */ NULL, -/* 114 */ NULL, -/* 115 */ NULL, -/* 116 */ NULL, -/* 117 */ NULL, -/* 118 */ NULL, -/* 119 */ NULL, -/* 120 */ NULL, -/* 121 */ NULL, -/* 122 */ NULL, -/* 123 */ NULL, -/* 124 */ NULL, -/* 125 */ NULL, -/* 126 */ NULL, -/* 127 */ NULL, -/* 128 */ NULL, -/* 129 */ NULL, -/* 130 */ NULL, -/* 131 */ NULL, -/* 132 */ NULL, -/* 133 */ NULL, -/* 134 */ NULL, -/* 135 */ NULL, -/* 136 */ NULL, -/* 137 */ NULL, -/* 138 */ NULL, -/* 139 */ NULL, -/* 140 */ NULL, -/* 141 */ NULL, -/* 142 */ NULL, -/* 143 */ NULL, -/* 144 */ NULL, -/* 145 */ NULL, -/* 146 */ NULL, -/* 147 */ NULL, -/* 148 */ NULL, -/* 149 */ NULL, -/* 150 */ NULL, -/* 151 */ NULL, -/* 152 */ NULL, -/* 153 */ NULL, -/* 154 */ NULL, -/* 155 */ NULL, -/* 156 */ NULL, -/* 157 */ NULL, -/* 158 */ NULL, -/* 159 */ NULL, -/* 160 */ NULL, -/* 161 */ NULL, -/* 162 */ NULL, -/* 163 */ NULL, -/* 164 */ NULL, -/* 165 */ NULL, -/* 166 */ NULL, -/* 167 */ NULL, -/* 168 */ NULL, -/* 169 */ NULL, -/* 170 */ NULL, -/* 171 */ NULL, -/* 172 */ NULL, -/* 173 */ NULL, -/* 174 */ NULL, -/* 175 */ NULL, -/* 176 */ NULL, -/* 177 */ NULL, -/* 178 */ NULL, -/* 179 */ NULL, -/* 180 */ NULL, -/* 181 */ NULL, -/* 182 */ NULL, -/* 183 */ NULL, -/* 184 */ NULL, -/* 185 */ NULL, -/* 186 */ NULL, -/* 187 */ NULL, -/* 188 */ NULL, -/* 189 */ NULL, -/* 190 */ NULL, -/* 191 */ NULL, -/* 192 */ NULL, -/* 193 */ NULL, -/* 194 */ NULL, -/* 195 */ NULL, -/* 196 */ NULL, -/* 197 */ NULL, -/* 198 */ NULL, -/* 199 */ NULL, -/* 200 RPL_TRACELINK */ "Link %s%s %s %s", -/* 201 RPL_TRACECONNECTING */ "Attempt %s %s", -/* 202 RPL_TRACEHANDSHAKE */ "Handshaking %s %s", -/* 203 RPL_TRACEUNKNOWN */ "???? %s %s", -/* 204 RPL_TRACEOPERATOR */ "Operator %s %s [%s] %ld", -/* 205 RPL_TRACEUSER */ "User %s %s [%s] %ld", -/* 206 RPL_TRACESERVER */ "Server %s %dS %dC %s %s!%s@%s %ld", -/* 207 RPL_TRACESERVICE */ "Service %s %s", -/* 208 RPL_TRACENEWTYPE */ "%s 0 %s", -/* 209 RPL_TRACECLASS */ "Class %s %d", -/* 210 RPL_STATSHELP */ ":%s", -/* 211 */ NULL, /* Used */ -#ifdef DEBUGMODE -/* 212 RPL_STATSCOMMANDS */ "%s %u %lu %lu %lu %lu %lu", -#else -/* 212 RPL_STATSCOMMANDS */ "%s %u %lu", -#endif -/* 213 RPL_STATSCLINE */ "%c %s * %s %d %d %s", -/* 214 RPL_STATSOLDNLINE */ "%c %s * %s %d %d %s", -/* 215 RPL_STATSILINE */ "I %s %s %d %d %s %s %d", -/* 216 RPL_STATSKLINE */ "%s %s %s", -/* 217 RPL_STATSQLINE */ "%c %s %ld %ld %s :%s", -/* 218 RPL_STATSYLINE */ "Y %s %d %d %d %d %d", -/* 219 RPL_ENDOFSTATS */ "%c :End of /STATS report", -/* 220 RPL_STATSBLINE */ "%c %s %s %s %d %d", -/* 221 RPL_UMODEIS */ "%s", -/* 222 RPL_SQLINE_NICK */ "%s :%s", -/* 223 RPL_STATSGLINE */ "%c %s %li %li %s :%s", -/* 224 RPL_STATSTLINE */ "T %s %s %s", -/* 225 RPL_STATSELINE (we use 230 instead) */ NULL, -/* 226 RPL_STATSNLINE */ "n %s %s", -/* 227 RPL_STATSVLINE */ "v %s %s %s", -/* 228 RPL_STATSBANVER */ "%s %s", -/* 229 RPL_STATSSPAMF */ "%c %s %s %s %li %li %li %s %s :%s", -/* 230 RPL_STATSEXCEPTTKL */ "%s %s %li %li %s :%s", -/* 231 */ NULL, /* rfc1459 */ -/* 232 RPL_RULES */ ":- %s", -/* 233 */ NULL, /* rfc1459 */ -/* 234 */ NULL, /* rfc2812 */ -/* 235 */ NULL, /* rfc2812 */ -/* 236 */ NULL, /* ircu */ -/* 237 */ NULL, /* ircu */ -/* 238 */ NULL, /* ircu, ircnet */ -/* 239 */ NULL, /* ircnet */ -/* 240 */ NULL, /* rfc2812, austhex */ -/* 241 RPL_STATSLLINE */ "%c %s * %s %d %d", -/* 242 RPL_STATSUPTIME */ ":Server Up %ld days, %ld:%02ld:%02ld", -/* 243 RPL_STATSOLINE */ "%c %s * %s %s %s", -/* 244 RPL_STATSHLINE */ "%c %s * %s %d %d", -/* 245 RPL_STATSSLINE */ "%c %s * %s %d %d", -/* 246 */ NULL, /* rfc2812 */ -/* 247 RPL_STATSXLINE */ "X %s %d", -/* 248 RPL_STATSULINE */ "U %s", -/* 249 RPL_STATSDEBUG */ ":%s", -/* 250 RPL_STATSCONN */ ":Highest connection count: %d (%d clients)", -/* 251 RPL_LUSERCLIENT */ ":There are %d users and %d invisible on %d servers", -/* 252 RPL_LUSEROP */ "%d :operator(s) online", -/* 253 RPL_LUSERUNKNOWN */ "%d :unknown connection(s)", -/* 254 RPL_LUSERCHANNELS */ "%d :channels formed", -/* 255 RPL_LUSERME */ ":I have %d clients and %d servers", -/* 256 RPL_ADMINME */ ":Administrative info about %s", -/* 257 RPL_ADMINLOC1 */ ":%s", -/* 258 RPL_ADMINLOC2 */ ":%s", -/* 259 RPL_ADMINEMAIL */ ":%s", -/* 260 */ NULL, -/* 261 RPL_TRACELOG */ "File %s %d", -/* 262 */ NULL, /* rfc2812 */ -/* 263 RPL_TRYAGAIN */ "%s :Flooding detected. Please wait a while and try again.", -/* 264 */ NULL, -/* 265 RPL_LOCALUSERS */ "%d %d :Current local users %d, max %d", -/* 266 RPL_GLOBALUSERS */ "%d %d :Current global users %d, max %d", -/* 267 */ NULL, /* aircd */ -/* 268 */ NULL, /* aircd */ -/* 269 */ NULL, /* aircd */ -/* 270 */ NULL, /* ircu */ -/* 271 RPL_SILELIST */ "%s", -/* 272 RPL_ENDOFSILELIST */ ":End of Silence List", -/* 273 */ NULL, /* aircd */ -/* 274 */ NULL, /* ircnet */ -/* 275 RPL_STATSDLINE */ "%c %s %s", -/* 276 RPL_WHOISCERTFP */ "%s :has client certificate fingerprint %s", -/* 277 */ NULL, /* hybrid */ -/* 278 */ NULL, /* hybrid */ -/* 279 */ NULL, -/* 280 */ NULL, /* ircu */ -/* 281 */ NULL, /* ircu, hybrid */ -/* 282 */ NULL, /* ircu, hybrid */ -/* 283 */ NULL, /* ircu, hybrid */ -/* 284 */ NULL, /* hybrid, quakenet */ -/* 285 */ NULL, /* ircu, aircd, quakenet */ -/* 286 */ NULL, /* aircd, quakenet */ -/* 287 */ NULL, /* aircd, quakenet */ -/* 288 */ NULL, /* aircd, quakenet */ -/* 289 */ NULL, /* aircd, quakenet */ -/* 290 */ NULL, /* aircd, quakenet */ -/* 291 */ NULL, /* aircd, quakenet */ -/* 292 */ NULL, /* aircd */ -/* 293 */ NULL, /* aircd */ -/* 294 RPL_HELPFWD */ ":Your help-request has been forwarded to Help Operators", -/* 295 RPL_HELPIGN */ ":Your address has been ignored from forwarding", -/* 296 */ NULL, /* aircd */ -/* 297 */ NULL, -/* 298 */ NULL, /* Used */ -/* 299 */ NULL, /* aircd */ -/* 300 */ NULL, /* rfc1459 */ -/* 301 RPL_AWAY */ "%s :%s", -/* 302 RPL_USERHOST */ ":%s %s %s %s %s", -/* 303 RPL_ISON */ ":", -/* 304 */ NULL, /* RPL_TEXT */ -/* 305 RPL_UNAWAY */ ":You are no longer marked as being away", -/* 306 RPL_NOWAWAY */ ":You have been marked as being away", -/* 307 RPL_WHOISREGNICK */ "%s :is identified for this nick", -/* 308 RPL_RULESSTART */ ":- %s Server Rules - ", -/* 309 RPL_ENDOFRULES */ ":End of RULES command.", -/* 310 RPL_WHOISHELPOP */ "%s :is available for help.", -/* 311 RPL_WHOISUSER */ "%s %s %s * :%s", -/* 312 RPL_WHOISSERVER */ "%s %s :%s", -/* 313 RPL_WHOISOPERATOR */ "%s :is %s", -/* 314 RPL_WHOWASUSER */ "%s %s %s * :%s", -/* 315 RPL_ENDOFWHO */ "%s :End of /WHO list.", -/* 316 */ NULL, /* rfc1459 */ -/* 317 RPL_WHOISIDLE */ "%s %ld %ld :seconds idle, signon time", -/* 318 RPL_ENDOFWHOIS */ "%s :End of /WHOIS list.", -/* 319 RPL_WHOISCHANNELS */ "%s :%s", -/* 320 RPL_WHOISSPECIAL */ "%s :%s", -/* 321 RPL_LISTSTART */ "Channel :Users Name", -#ifndef LIST_SHOW_MODES -/* 322 RPL_LIST */ "%s %d :%s", -#else -/* 322 RPL_LIST */ "%s %d :%s %s", -#endif -/* 323 RPL_LISTEND */ ":End of /LIST", -/* 324 RPL_CHANNELMODEIS */ "%s %s %s", -/* 325 */ NULL, /* rfc2812 */ -/* 326 */ NULL, /* Used */ -/* 327 */ NULL, /* Used */ -/* 328 */ NULL, /* bahamut, austhex */ -/* 329 RPL_CREATIONTIME */ "%s %lu", -/* 330 RPL_WHOISLOGGEDIN */ "%s %s :is logged in as", -/* 331 RPL_NOTOPIC */ "%s :No topic is set.", -/* 332 RPL_TOPIC */ "%s :%s", -/* 333 RPL_TOPICWHOTIME */ "%s %s %lu", -/* 334 RPL_LISTSYNTAX */ ":%s", -/* 335 RPL_WHOISBOT */ "%s :is a \2Bot\2 on %s", -/* 336 RPL_INVITELIST */ ":%s", -/* 337 RPL_ENDOFINVITELIST */ ":End of /INVITE list.", -/* 338 */ NULL, /* ircu, bahamut */ -/* 339 */ NULL, /* Used */ -/* 340 RPL_USERIP */ ":%s %s %s %s %s", -/* 341 RPL_INVITING */ "%s %s", -/* 342 RPL_SUMMONING */ "%s :User summoned to irc", -/* 343 */ NULL, -/* 344 */ NULL, -/* 345 */ NULL, /* gamesurge */ -/* 346 RPL_INVEXLIST */ "%s %s %s %lu", -/* 347 RPL_ENDOFINVEXLIST */ "%s :End of Channel Invite List", -/* 348 RPL_EXLIST */ "%s %s %s %lu", -/* 349 RPL_ENDOFEXLIST */ "%s :End of Channel Exception List", -/* 350 */ NULL, -/* 351 RPL_VERSION */ "%s.%s %s :%s%s%s [%s=%d]", -/* 352 RPL_WHOREPLY */ "%s %s %s %s %s %s :%d %s", -/* 353 RPL_NAMREPLY */ "%s", -/* 354 */ NULL, /* ircu */ -/* 355 */ NULL, /* quakenet */ -/* 356 */ NULL, -/* 357 */ NULL, /* austhex */ -/* 358 */ NULL, /* austhex */ -/* 359 */ NULL, /* austhex */ -/* 360 */ NULL, -/* 361 */ NULL, /* rfc1459 */ -/* 362 RPL_CLOSING */ "%s :Closed. Status = %d", -/* 363 RPL_CLOSEEND */ "%d: Connections Closed", -/* 364 RPL_LINKS */ "%s %s :%d %s", -/* 365 RPL_ENDOFLINKS */ "%s :End of /LINKS list.", -/* 366 RPL_ENDOFNAMES */ "%s :End of /NAMES list.", -/* 367 RPL_BANLIST */ "%s %s %s %lu", -/* 368 RPL_ENDOFBANLIST */ "%s :End of Channel Ban List", -/* 369 RPL_ENDOFWHOWAS */ "%s :End of WHOWAS", -/* 370 */ NULL, -/* 371 RPL_INFO */ ":%s", -/* 372 RPL_MOTD */ ":- %s", -/* 373 RPL_INFOSTART */ ":Server INFO", -/* 374 RPL_ENDOFINFO */ ":End of /INFO list.", -/* 375 RPL_MOTDSTART */ ":- %s Message of the Day - ", -/* 376 RPL_ENDOFMOTD */ ":End of /MOTD command.", -/* 377 */ NULL, /* aircd, austhex */ -/* 378 RPL_WHOISHOST */ "%s :is connecting from %s@%s %s", -/* 379 RPL_WHOISMODES */ "%s :is using modes %s %s", -/* 380 */ NULL, /* aircd, austhex */ -/* 381 RPL_YOUREOPER */ ":You are now an IRC Operator", -/* 382 RPL_REHASHING */ "%s :Rehashing", -/* 383 */ NULL, /* rfc2812 */ -/* 384 RPL_MYPORTIS */ "%d :Port to local server is\r\n", -/* 385 */ NULL, /* austhex, hybrid */ -/* 386 RPL_QLIST */ "%s %s", -/* 387 RPL_ENDOFQLIST */ "%s :End of Channel Owner List", -/* 388 RPL_ALIST */ "%s %s", -/* 389 RPL_ENDOFALIST */ "%s :End of Protected User List", -/* 390 */ NULL, -/* 391 RPL_TIME */ "%s :%s", -#ifdef ENABLE_USERS -/* 392 RPL_USERSSTART */ ":UserID Terminal Host", -/* 393 RPL_USERS */ ":%-8s %-9s %-8s", -/* 394 RPL_ENDOFUSERS */ ":End of Users", -/* 395 RPL_NOUSERS */ ":Nobody logged in.", -#else -/* 392 */ NULL, -/* 393 */ NULL, -/* 394 */ NULL, -/* 395 */ NULL, -#endif -/* 396 RPL_HOSTHIDDEN */ "%s :is now your displayed host", -/* 397 */ NULL, -/* 398 */ NULL, -/* 399 */ NULL, -/* 400 */ NULL, /* Used */ -/* 401 ERR_NOSUCHNICK */ "%s :No such nick/channel", -/* 402 ERR_NOSUCHSERVER */ "%s :No such server", -/* 403 ERR_NOSUCHCHANNEL */ "%s :No such channel", -/* 404 ERR_CANNOTSENDTOCHAN */ "%s :%s (%s)", -/* 405 ERR_TOOMANYCHANNELS */ "%s :You have joined too many channels", -/* 406 ERR_WASNOSUCHNICK */ "%s :There was no such nickname", -/* 407 ERR_TOOMANYTARGETS */ "%s :Too many targets. The maximum is %d for %s.", -/* 408 */ NULL, /* rfc2812, bahamut */ -/* 409 ERR_NOORIGIN */ ":No origin specified", -/* 410 ERR_INVALIDCAPCMD */ "%s :Invalid CAP subcommand", -/* 411 ERR_NORECIPIENT */ ":No recipient given (%s)", -/* 412 ERR_NOTEXTTOSEND */ ":No text to send", -/* 413 ERR_NOTOPLEVEL */ "%s :No toplevel domain specified", -/* 414 ERR_WILDTOPLEVEL */ "%s :Wildcard in toplevel Domain", -/* 415 */ NULL, /* rfc2812 */ -/* 416 ERR_TOOMANYMATCHES */ "%s :%s", -/* 417 */ NULL, -/* 418 */ NULL, -/* 419 */ NULL, /* aircd */ -/* 420 */ NULL, -/* 421 ERR_UNKNOWNCOMMAND */ "%s :Unknown command", -/* 422 ERR_NOMOTD */ ":MOTD File is missing", -/* 423 ERR_NOADMININFO */ "%s :No administrative info available", -/* 424 ERR_FILEERROR */ ":File error doing %s on %s", -/* 425 ERR_NOOPERMOTD */ ":OPERMOTD File is missing", -/* 426 */ NULL, -/* 427 */ NULL, -/* 428 */ NULL, -/* 429 ERR_TOOMANYAWAY */ ":Too Many aways - Flood Protection activated", -/* 430 */ NULL, /* austhex */ -/* 431 ERR_NONICKNAMEGIVEN */ ":No nickname given", -/* 432 ERR_ERRONEUSNICKNAME */ "%s :Nickname is unavailable: %s", -/* 433 ERR_NICKNAMEINUSE */ "%s :Nickname is already in use.", -/* 434 ERR_NORULES */ ":RULES File is missing", -/* 435 */ NULL, /* bahamut */ -/* 436 ERR_NICKCOLLISION */ "%s :Nickname collision KILL", -/* 437 ERR_BANNICKCHANGE */ "%s :Cannot change nickname while banned on channel", -/* 438 ERR_NCHANGETOOFAST */ "%s :Nick change too fast. Please try again later.", -/* 439 ERR_TARGETTOOFAST */ "%s :Message target change too fast. Please wait %ld seconds", -/* 440 ERR_SERVICESDOWN */ "%s :Services are currently down. Please try again later.", -/* 441 ERR_USERNOTINCHANNEL */ "%s %s :They aren't on that channel", -/* 442 ERR_NOTONCHANNEL */ "%s :You're not on that channel", -/* 443 ERR_USERONCHANNEL */ "%s %s :is already on channel", -/* 444 ERR_NOLOGIN */ "%s :User not logged in", -/* 445 ERR_SUMMONDISABLED */ ":SUMMON has been disabled", -/* 446 ERR_USERSDISABLED */ ":USERS has been disabled", -/* 447 ERR_NONICKCHANGE */ ":Can not change nickname while on %s (+N)", -/* 448 ERR_FORBIDDENCHANNEL */ "%s :Cannot join channel: %s", -/* 449 */ NULL, /* ircu */ -/* 450 */ NULL, -/* 451 ERR_NOTREGISTERED */ ":You have not registered", -/* 452 */ NULL, /* Used */ -/* 453 */ NULL, /* Used */ -/* 454 */ NULL, -/* 455 ERR_HOSTILENAME */ ":Your username %s contained the invalid " - "character(s) %s and has been changed to %s. " - "Please use only the characters 0-9 a-z A-Z _ - " - "or . in your username. Your username is the part " - "before the @ in your email address.", -/* 456 */ NULL, /* hybrid */ -/* 457 */ NULL, /* hybrid */ -/* 458 */ NULL, /* hybrid */ -/* 459 ERR_NOHIDING */ "%s :Cannot join channel (+H)", -/* 460 ERR_NOTFORHALFOPS */ ":Halfops cannot set mode %c", -/* 461 ERR_NEEDMOREPARAMS */ "%s :Not enough parameters", -/* 462 ERR_ALREADYREGISTRED */ ":You may not reregister", -/* 463 ERR_NOPERMFORHOST */ ":Your host isn't among the privileged", -/* 464 ERR_PASSWDMISMATCH */ ":Password Incorrect", -/* 465 ERR_YOUREBANNEDCREEP */ ":%s", -/* 466 */ NULL, /* rfc1459 */ -/* 467 ERR_KEYSET */ "%s :Channel key already set", -/* 468 ERR_ONLYSERVERSCANCHANGE */ "%s :Only servers can change that mode", -/* 469 ERR_LINKSET */ "%s :Channel link already set", -/* 470 ERR_LINKCHANNEL */ "%s %s :[Link] %s has become full, so you are automatically being transferred to the linked channel %s", -/* 471 ERR_CHANNELISFULL */ "%s :Cannot join channel (+l)", -/* 472 ERR_UNKNOWNMODE */ "%c :is unknown mode char to me", -/* 473 ERR_INVITEONLYCHAN */ "%s :Cannot join channel (+i)", -/* 474 ERR_BANNEDFROMCHAN */ "%s :Cannot join channel (+b)", -/* 475 ERR_BADCHANNELKEY */ "%s :Cannot join channel (+k)", -/* 476 ERR_BADCHANMASK */ "%s :Bad Channel Mask", -/* 477 ERR_NEEDREGGEDNICK */ "%s :You need a registered nick to join that channel.", -/* 478 ERR_BANLISTFULL */ "%s %s :Channel ban/ignore list is full", -/* 479 ERR_LINKFAIL */ "%s :Sorry, the channel has an invalid channel link set.", -/* 480 ERR_CANNOTKNOCK */ ":Cannot knock on %s (%s)", -/* 481 ERR_NOPRIVILEGES */ ":Permission Denied- You do not have the correct IRC operator privileges", -/* 482 ERR_CHANOPRIVSNEEDED */ "%s :You're not channel operator", -/* 483 ERR_CANTKILLSERVER */ ":You cant kill a server!", -/* 484 ERR_ATTACKDENY */ "%s :Cannot kick protected user %s.", -/* 485 ERR_KILLDENY */ ":Cannot kill protected user %s.", -/* 486 ERR_NONONREG */ ":You must identify to a registered nick to private message %s", -/* 487 ERR_NOTFORUSERS */ ":%s is a server only command", -/* 488 */ NULL, -/* 489 ERR_SECUREONLYCHAN */ "%s :Cannot join channel (Secure connection is required)", -/* 490 ERR_NOSWEAR */ ":%s does not accept private messages containing swearing.", -/* 491 ERR_NOOPERHOST */ ":No O-lines for your host", -/* 492 ERR_NOCTCP */ ":%s does not accept CTCPs", -/* 493 */ NULL, /* ircu */ -/* 494 */ NULL, /* ircu */ -/* 495 */ NULL, /* ircu */ -/* 496 */ NULL, /* ircu */ -/* 497 */ NULL, /* ircu */ -/* 498 */ NULL, /* ircu */ -/* 499 ERR_CHANOWNPRIVNEEDED */ "%s :You're not a channel owner", -/* 500 ERR_TOOMANYJOINS */ "%s :Too many join requests. Please wait a while and try again.", -/* 501 ERR_UMODEUNKNOWNFLAG */ ":Unknown MODE flag", -/* 502 ERR_USERSDONTMATCH */ ":Cant change mode for other users", -/* 503 */ NULL, /* austhex */ -/* 504 */ NULL, /* Used */ -/* 505 */ NULL, -/* 506 */ NULL, -/* 507 */ NULL, -/* 508 */ NULL, -/* 509 */ NULL, -/* 510 */ NULL, -/* 511 ERR_SILELISTFULL */ "%s :Your silence list is full", -/* 512 ERR_TOOMANYWATCH */ "%s :Maximum size for WATCH-list is 128 entries", -/* 513 ERR_NEEDPONG */ ":To connect, type /QUOTE PONG %lX", -/* 514 ERR_TOOMANYDCC */ "%s :Your dcc allow list is full. Maximum size is %d entries", -/* 515 */ NULL, /* ircu */ -/* 516 */ NULL, /* ircu */ -/* 517 ERR_DISABLED*/ "%s :%s", /* ircu */ -/* 518 518 */ ":Cannot invite (+V) at channel %s", -/* 519 519 */ ":Cannot join channel %s (Admin only)", -/* 520 520 */ ":Cannot join channel %s (IRCops only)", -/* 521 ERR_LISTSYNTAX */ ":Bad list syntax, type /quote list ? or /raw list ?", -/* 522 ERR_WHOSYNTAX */ ":/WHO Syntax incorrect, use /who ? for help", -/* 523 ERR_WHOLIMEXCEED */ ":Error, /who limit of %d exceeded. Please narrow your search down and try again", -/* 524 ERR_OPERSPVERIFY */ ":Trying to join +s or +p channel as an oper. Please invite yourself first.", -/* 525 */ NULL, /* draft-brocklesby-irc-usercmdpfx */ -/* 526 */ NULL, /* draft-brocklesby-irc-usercmdpfx */ -/* 527 */ NULL, -/* 528 */ NULL, -/* 529 */ NULL, -/* 530 */ NULL, -/* 531 ERR_CANTSENDTOUSER */ "%s :%s", -/* 532 */ NULL, -/* 533 */ NULL, -/* 534 */ NULL, -/* 535 */ NULL, -/* 536 */ NULL, -/* 537 */ NULL, -/* 538 */ NULL, -/* 539 */ NULL, -/* 540 */ NULL, -/* 541 */ NULL, -/* 542 */ NULL, -/* 543 */ NULL, -/* 544 */ NULL, -/* 545 */ NULL, -/* 546 */ NULL, -/* 547 */ NULL, -/* 548 */ NULL, -/* 549 */ NULL, -/* 550 */ NULL, /* quakenet */ -/* 551 */ NULL, /* quakenet */ -/* 552 */ NULL, /* quakenet */ -/* 553 */ NULL, /* quakenet */ -/* 554 */ NULL, -/* 555 */ NULL, -/* 556 */ NULL, -/* 557 */ NULL, -/* 558 */ NULL, -/* 559 */ NULL, -/* 560 */ NULL, -/* 561 */ NULL, -/* 562 */ NULL, -/* 563 */ NULL, -/* 564 */ NULL, -/* 565 */ NULL, -/* 566 */ NULL, -/* 567 */ NULL, -/* 568 */ NULL, -/* 569 */ NULL, -/* 570 */ NULL, -/* 571 */ NULL, -/* 572 */ NULL, -/* 573 */ NULL, -/* 574 */ NULL, -/* 575 */ NULL, -/* 576 */ NULL, -/* 577 */ NULL, -/* 578 */ NULL, -/* 579 */ NULL, -/* 580 */ NULL, -/* 581 */ NULL, -/* 582 */ NULL, -/* 583 */ NULL, -/* 584 */ NULL, -/* 585 */ NULL, -/* 586 */ NULL, -/* 587 */ NULL, -/* 588 */ NULL, -/* 589 */ NULL, -/* 590 */ NULL, -/* 591 */ NULL, -/* 592 */ NULL, -/* 593 */ NULL, -/* 594 */ NULL, -/* 595 */ NULL, -/* 596 */ NULL, -/* 597 RPL_REAWAY */ "%s %s %s %d :%s", -/* 598 RPL_GONEAWAY */ "%s %s %s %d :%s", -/* 599 RPL_NOTAWAY */ "%s %s %s %d :is no longer away", -/* 600 RPL_LOGON */ "%s %s %s %d :logged online", -/* 601 RPL_LOGOFF */ "%s %s %s %d :logged offline", -/* 602 RPL_WATCHOFF */ "%s %s %s %d :stopped watching", -/* 603 RPL_WATCHSTAT */ ":You have %d and are on %d WATCH entries", -/* 604 RPL_NOWON */ "%s %s %s %ld :is online", -/* 605 RPL_NOWOFF */ "%s %s %s %ld :is offline", -/* 606 RPL_WATCHLIST */ ":%s", -/* 607 RPL_ENDOFWATCHLIST */ ":End of WATCH %c", -/* 608 RPL_CLEARWATCH */ ":Your WATCH list is now empty", -/* 609 RPL_NOWISAWAY */ "%s %s %s %ld :is away", -/* 610 RPL_MAPMORE */ ":%s%-*s --> *more*", -/* 611 */ NULL, /* ultimate */ -/* 612 */ NULL, /* ultimate */ -/* 613 */ NULL, /* ultimate */ -/* 614 */ NULL, -/* 615 */ NULL, /* ptlink, ultimate */ -/* 616 */ NULL, /* ultimate */ -/* 617 RPL_DCCSTATUS */ ":%s has been %s your DCC allow list", -/* 618 RPL_DCCLIST */ ":%s", -/* 619 RPL_ENDOFDCCLIST */ ":End of DCCALLOW %s", -/* 620 RPL_DCCINFO */ ":%s", -/* 621 */ NULL, /* ultimate */ -/* 622 */ NULL, /* ultimate */ -/* 623 */ NULL, /* ultimate */ -/* 624 */ NULL, /* ultimate */ -/* 625 */ NULL, /* ultimate */ -/* 626 */ NULL, /* ultimate */ -/* 627 */ NULL, -/* 628 */ NULL, -/* 629 */ NULL, -/* 630 */ NULL, /* ultimate */ -/* 631 */ NULL, /* ultimate */ -/* 632 */ NULL, -/* 633 */ NULL, -/* 634 */ NULL, -/* 635 */ NULL, -/* 636 */ NULL, -/* 637 */ NULL, -/* 638 */ NULL, -/* 639 */ NULL, -/* 640 */ NULL, -/* 641 */ NULL, -/* 642 */ NULL, -/* 643 */ NULL, -/* 644 */ NULL, -/* 645 */ NULL, -/* 646 */ NULL, -/* 647 */ NULL, -/* 648 */ NULL, -/* 649 */ NULL, -/* 650 */ NULL, -/* 651 */ NULL, -/* 652 */ NULL, -/* 653 */ NULL, -/* 654 */ NULL, -/* 655 */ NULL, -/* 656 */ NULL, -/* 657 */ NULL, -/* 658 */ NULL, -/* 659 RPL_SPAMCMDFWD */ "%s :Command processed, but a copy has been sent to ircops for evaluation (anti-spam) purposes. [%s]", -/* 660 */ NULL, /* kineircd */ -/* 661 */ NULL, /* kineircd */ -/* 662 */ NULL, /* kineircd */ -/* 663 */ NULL, /* kineircd */ -/* 664 */ NULL, /* kineircd */ -/* 665 */ NULL, /* kineircd */ -/* 666 */ NULL, /* kineircd */ -/* 667 */ NULL, -/* 668 */ NULL, -/* 669 */ NULL, -/* 670 RPL_STARTTLS */ ":STARTTLS successful, go ahead with TLS handshake", /* kineircd */ -/* 671 RPL_WHOISSECURE */ "%s :%s", /* our variation on the kineircd numeric */ -/* 672 */ NULL, /* ithildin */ -/* 673 */ NULL, /* ithildin */ -/* 674 */ NULL, -/* 675 */ NULL, -/* 676 */ NULL, -/* 677 */ NULL, -/* 678 */ NULL, /* kineircd */ -/* 679 */ NULL, /* kineircd */ -/* 680 */ NULL, -/* 681 */ NULL, -/* 682 */ NULL, /* kineircd */ -/* 683 */ NULL, -/* 684 */ NULL, -/* 685 */ NULL, -/* 686 */ NULL, -/* 687 */ NULL, /* kineircd */ -/* 688 */ NULL, /* kineircd */ -/* 689 */ NULL, /* kineircd */ -/* 690 */ NULL, /* kineircd */ -/* 691 ERR_STARTTLS */ ":%s", -/* 692 */ NULL, -/* 693 */ NULL, -/* 694 */ NULL, -/* 695 */ NULL, -/* 696 */ NULL, -/* 697 */ NULL, -/* 698 */ NULL, -/* 699 */ NULL, -/* 700 */ NULL, -/* 701 */ NULL, -/* 702 */ NULL, -/* 703 */ NULL, -/* 704 */ NULL, -/* 705 */ NULL, -/* 706 */ NULL, -/* 707 */ NULL, -/* 708 */ NULL, -/* 709 */ NULL, -/* 710 */ NULL, -/* 711 */ NULL, -/* 712 */ NULL, -/* 713 */ NULL, -/* 714 */ NULL, -/* 715 */ NULL, -/* 716 */ NULL, /* ratbox */ -/* 717 */ NULL, /* ratbox */ -/* 718 */ NULL, /* ratbox */ -/* 719 */ NULL, -/* 720 */ NULL, -/* 721 */ NULL, -/* 722 */ NULL, -/* 723 */ NULL, -/* 724 */ NULL, -/* 725 */ NULL, -/* 726 */ NULL, -/* 727 */ NULL, -/* 728 */ NULL, -/* 729 */ NULL, -/* 730 */ NULL, -/* 731 */ NULL, -/* 732 */ NULL, -/* 733 */ NULL, -/* 734 */ NULL, -/* 735 */ NULL, -/* 736 */ NULL, -/* 737 */ NULL, -/* 738 */ NULL, -/* 739 */ NULL, -/* 740 */ NULL, -/* 741 */ NULL, -/* 742 ERR_MLOCKRESTRICTED */ "%s %c %s :MODE cannot be set due to channel having an active MLOCK restriction policy", -/* 743 */ NULL, -/* 744 */ NULL, -/* 745 */ NULL, -/* 746 */ NULL, -/* 747 */ NULL, -/* 748 */ NULL, -/* 749 */ NULL, -/* 750 */ NULL, -/* 751 */ NULL, -/* 752 */ NULL, -/* 753 */ NULL, -/* 754 */ NULL, -/* 755 */ NULL, -/* 756 */ NULL, -/* 757 */ NULL, -/* 758 */ NULL, -/* 759 */ NULL, -/* 760 */ NULL, -/* 761 */ NULL, -/* 762 */ NULL, -/* 763 */ NULL, -/* 764 */ NULL, -/* 765 */ NULL, -/* 766 */ NULL, -/* 767 */ NULL, -/* 768 */ NULL, -/* 769 */ NULL, -/* 770 */ NULL, -/* 771 */ NULL, /* ithildin */ -/* 772 */ NULL, -/* 773 */ NULL, /* ithildin */ -/* 774 */ NULL, /* ithildin */ -/* 775 */ NULL, -/* 776 */ NULL, -/* 777 */ NULL, -/* 778 */ NULL, -/* 779 */ NULL, -/* 780 */ NULL, -/* 781 */ NULL, -/* 782 */ NULL, -/* 783 */ NULL, -/* 784 */ NULL, -/* 785 */ NULL, -/* 786 */ NULL, -/* 787 */ NULL, -/* 788 */ NULL, -/* 789 */ NULL, -/* 790 */ NULL, -/* 791 */ NULL, -/* 792 */ NULL, -/* 793 */ NULL, -/* 794 */ NULL, -/* 795 */ NULL, -/* 796 */ NULL, -/* 797 */ NULL, -/* 798 */ NULL, -/* 799 */ NULL, -/* 800 */ NULL, -/* 801 */ NULL, -/* 802 */ NULL, -/* 803 */ NULL, -/* 804 */ NULL, -/* 805 */ NULL, -/* 806 */ NULL, -/* 807 */ NULL, -/* 808 */ NULL, -/* 809 */ NULL, -/* 810 */ NULL, -/* 811 */ NULL, -/* 812 */ NULL, -/* 813 */ NULL, -/* 814 */ NULL, -/* 815 */ NULL, -/* 816 */ NULL, -/* 817 */ NULL, -/* 818 */ NULL, -/* 819 */ NULL, -/* 820 */ NULL, -/* 821 */ NULL, -/* 822 */ NULL, -/* 823 */ NULL, -/* 824 */ NULL, -/* 825 */ NULL, -/* 826 */ NULL, -/* 827 */ NULL, -/* 828 */ NULL, -/* 829 */ NULL, -/* 830 */ NULL, -/* 831 */ NULL, -/* 832 */ NULL, -/* 833 */ NULL, -/* 834 */ NULL, -/* 835 */ NULL, -/* 836 */ NULL, -/* 837 */ NULL, -/* 838 */ NULL, -/* 839 */ NULL, -/* 840 */ NULL, -/* 841 */ NULL, -/* 842 */ NULL, -/* 843 */ NULL, -/* 844 */ NULL, -/* 845 */ NULL, -/* 846 */ NULL, -/* 847 */ NULL, -/* 848 */ NULL, -/* 849 */ NULL, -/* 850 */ NULL, -/* 851 */ NULL, -/* 852 */ NULL, -/* 853 */ NULL, -/* 854 */ NULL, -/* 855 */ NULL, -/* 856 */ NULL, -/* 857 */ NULL, -/* 858 */ NULL, -/* 859 */ NULL, -/* 860 */ NULL, -/* 861 */ NULL, -/* 862 */ NULL, -/* 863 */ NULL, -/* 864 */ NULL, -/* 865 */ NULL, -/* 866 */ NULL, -/* 867 */ NULL, -/* 868 */ NULL, -/* 869 */ NULL, -/* 870 */ NULL, -/* 871 */ NULL, -/* 872 */ NULL, -/* 873 */ NULL, -/* 874 */ NULL, -/* 875 */ NULL, -/* 876 */ NULL, -/* 877 */ NULL, -/* 878 */ NULL, -/* 879 */ NULL, -/* 880 */ NULL, -/* 881 */ NULL, -/* 882 */ NULL, -/* 883 */ NULL, -/* 884 */ NULL, -/* 885 */ NULL, -/* 886 */ NULL, -/* 887 */ NULL, -/* 888 */ NULL, -/* 889 */ NULL, -/* 890 */ NULL, -/* 891 */ NULL, -/* 892 */ NULL, -/* 893 */ NULL, -/* 894 */ NULL, -/* 895 */ NULL, -/* 896 */ NULL, -/* 897 */ NULL, -/* 898 */ NULL, -/* 899 */ NULL, -/* 900 RPL_LOGGEDIN */ "%s!%s@%s %s :You are now logged in as %s.", -/* 901 RPL_LOGGEDOUT */ "%s!%s@%s :You are now logged out.", -/* 902 ERR_NICKLOCKED */ ":You must use a nick assigned to you.", -/* 903 RPL_SASLSUCCESS */ ":SASL authentication successful", -/* 904 ERR_SASLFAIL */ ":SASL authentication failed", -/* 905 ERR_SASLTOOLONG */ ":SASL message too long", -/* 906 ERR_SASLABORTED */ ":SASL authentication aborted", -/* 907 ERR_SASLALREADY */ ":You have already completed SASL authentication", -/* 908 RPL_SASLMECHS */ "%s :are available SASL mechanisms", -/* 909 */ NULL, -/* 910 */ NULL, -/* 911 */ NULL, -/* 912 */ NULL, -/* 913 */ NULL, -/* 914 */ NULL, -/* 915 */ NULL, -/* 916 */ NULL, -/* 917 */ NULL, -/* 918 */ NULL, -/* 919 */ NULL, -/* 920 */ NULL, -/* 921 */ NULL, -/* 922 */ NULL, -/* 923 */ NULL, -/* 924 */ NULL, -/* 925 */ NULL, -/* 926 */ NULL, -/* 927 */ NULL, -/* 928 */ NULL, -/* 929 */ NULL, -/* 930 */ NULL, -/* 931 */ NULL, -/* 932 */ NULL, -/* 933 */ NULL, -/* 934 */ NULL, -/* 935 */ NULL, -/* 936 */ NULL, -/* 937 */ NULL, -/* 938 */ NULL, -/* 939 */ NULL, -/* 940 */ NULL, -/* 941 */ NULL, -/* 942 */ NULL, -/* 943 */ NULL, -/* 944 */ NULL, -/* 945 */ NULL, -/* 946 */ NULL, -/* 947 */ NULL, -/* 948 */ NULL, -/* 949 */ NULL, -/* 950 */ NULL, -/* 951 */ NULL, -/* 952 */ NULL, -/* 953 */ NULL, -/* 954 */ NULL, -/* 955 */ NULL, -/* 956 */ NULL, -/* 957 */ NULL, -/* 958 */ NULL, -/* 959 */ NULL, -/* 960 */ NULL, -/* 961 */ NULL, -/* 962 */ NULL, -/* 963 */ NULL, -/* 964 */ NULL, -/* 965 */ NULL, -/* 966 */ NULL, -/* 967 */ NULL, -/* 968 */ NULL, -/* 969 */ NULL, -/* 970 */ NULL, -/* 971 */ NULL, -/* 972 ERR_CANNOTDOCOMMAND */ "%s :%s", -/* 973 */ NULL, /* kineircd */ -/* 974 ERR_CANNOTCHANGECHANMODE */ "%c :%s", -/* 975 */ NULL, /* kineircd */ -/* 976 */ NULL, /* kineircd */ -/* 977 */ NULL, /* kineircd */ -/* 978 */ NULL, /* kineircd */ -/* 979 */ NULL, /* kineircd */ -/* 980 */ NULL, /* kineircd */ -/* 981 */ NULL, /* kineircd */ -/* 982 */ NULL, /* kineircd */ -/* 983 */ NULL, /* kineircd */ -/* 984 */ NULL, -/* 985 */ NULL, -/* 986 */ NULL, -/* 987 */ NULL, -/* 988 */ NULL, -/* 989 */ NULL, -/* 990 */ NULL, -/* 991 */ NULL, -/* 992 */ NULL, -/* 993 */ NULL, -/* 994 */ NULL, -/* 995 */ NULL, -/* 996 */ NULL, -/* 997 */ NULL, -/* 998 */ NULL, -/* 999 ERR_NUMERICERR */ "Numeric error!", -/* 1000 */ NULL, -}; - -char *getreply(int numeric) -{ - if ((numeric < 0) || (numeric > 999) || !replies[numeric]) - return replies[ERR_NUMERICERR]; - else - return replies[numeric]; -} diff --git a/src/openssl_hostname_validation.c b/src/openssl_hostname_validation.c index 5d9f0eb..cd1695c 100644 --- a/src/openssl_hostname_validation.c +++ b/src/openssl_hostname_validation.c @@ -146,7 +146,7 @@ static char Curl_raw_toupper(char in) static int Curl_raw_equal(const char *first, const char *second) { while(*first && *second) { - if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) + if (Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) /* get out of the loop as soon as they don't match */ break; first++; @@ -161,14 +161,14 @@ static int Curl_raw_equal(const char *first, const char *second) static int Curl_raw_nequal(const char *first, const char *second, size_t max) { while(*first && *second && max) { - if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) { + if (Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) { break; } max--; first++; second++; } - if(0 == max) + if (0 == max) return 1; /* they are equal this far */ return Curl_raw_toupper(*first) == Curl_raw_toupper(*second); @@ -189,7 +189,7 @@ static int hostmatch(const char *hostname, const char *pattern) int wildcard_enabled; size_t prefixlen, suffixlen; pattern_wildcard = strchr(pattern, '*'); - if(pattern_wildcard == NULL) + if (pattern_wildcard == NULL) return Curl_raw_equal(pattern, hostname) ? CURL_HOST_MATCH : CURL_HOST_NOMATCH; @@ -197,24 +197,24 @@ static int hostmatch(const char *hostname, const char *pattern) match. */ wildcard_enabled = 1; pattern_label_end = strchr(pattern, '.'); - if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL || + if (pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL || pattern_wildcard > pattern_label_end || Curl_raw_nequal(pattern, "xn--", 4)) { wildcard_enabled = 0; } - if(!wildcard_enabled) + if (!wildcard_enabled) return Curl_raw_equal(pattern, hostname) ? CURL_HOST_MATCH : CURL_HOST_NOMATCH; hostname_label_end = strchr(hostname, '.'); - if(hostname_label_end == NULL || + if (hostname_label_end == NULL || !Curl_raw_equal(pattern_label_end, hostname_label_end)) return CURL_HOST_NOMATCH; /* The wildcard must match at least one character, so the left-most label of the hostname is at least as large as the left-most label of the pattern. */ - if(hostname_label_end - hostname < pattern_label_end - pattern) + if (hostname_label_end - hostname < pattern_label_end - pattern) return CURL_HOST_NOMATCH; prefixlen = pattern_wildcard - pattern; @@ -227,14 +227,14 @@ static int hostmatch(const char *hostname, const char *pattern) int Curl_cert_hostcheck(const char *match_pattern, const char *hostname) { - if(!match_pattern || !*match_pattern || + if (!match_pattern || !*match_pattern || !hostname || !*hostname) /* sanity check */ return 0; - if(Curl_raw_equal(hostname, match_pattern)) /* trivial case */ + if (Curl_raw_equal(hostname, match_pattern)) /* trivial case */ return 1; - if(hostmatch(hostname,match_pattern) == CURL_HOST_MATCH) + if (hostmatch(hostname,match_pattern) == CURL_HOST_MATCH) return 1; return 0; } @@ -388,7 +388,7 @@ static HostnameValidationResult matches_subject_alternative_name(const char *hos HostnameValidationResult validate_hostname(const char *hostname, const X509 *server_cert) { HostnameValidationResult result; - if((hostname == NULL) || (server_cert == NULL)) + if ((hostname == NULL) || (server_cert == NULL)) return Error; // First try the Subject Alternative Names extension diff --git a/src/operclass.c b/src/operclass.c index 86899ca..0eaaa1e 100644 --- a/src/operclass.c +++ b/src/operclass.c @@ -29,7 +29,7 @@ struct OperClassValidator OperClassCallbackNode *node; }; -OperClassACLPath *OperClass_parsePath(char *path); +OperClassACLPath *OperClass_parsePath(const char *path); void OperClass_freePath(OperClassACLPath *path); OperClassPathNode *OperClass_findPathNodeForIdentifier(char *identifier, OperClassPathNode *head); @@ -111,7 +111,7 @@ void OperClassValidatorDel(OperClassValidator *validator) safe_free(validator); } -OperClassACLPath *OperClass_parsePath(char *path) +OperClassACLPath *OperClass_parsePath(const char *path) { char *pathCopy = raw_strdup(path); OperClassACLPath *pathHead = NULL; @@ -279,7 +279,7 @@ OperPermission ValidatePermissionsForPathEx(OperClassACL *acl, OperClassACLPath return OPER_DENY; } -OperPermission ValidatePermissionsForPath(char *path, Client *client, Client *victim, Channel *channel, void *extra) +OperPermission ValidatePermissionsForPath(const char *path, Client *client, Client *victim, Channel *channel, const void *extra) { ConfigItem_oper *ce_oper; ConfigItem_operclass *ce_operClass; diff --git a/src/parse.c b/src/parse.c index 6966958..473453a 100644 --- a/src/parse.c +++ b/src/parse.c @@ -29,11 +29,11 @@ char backupbuf[8192]; static char *para[MAXPARA + 2]; /* Forward declarations of functions that are local (static) */ -static int do_numeric(int, Client *, MessageTag *, int, char **); +static int do_numeric(int, Client *, MessageTag *, int, const char **); static void cancel_clients(Client *, Client *, char *); static void remove_unknown(Client *, char *); -static void parse2(Client *client, Client **fromptr, MessageTag *mtags, char *ch); -static void parse_addlag(Client *client, int cmdbytes); +static void parse2(Client *client, Client **fromptr, MessageTag *mtags, int mtags_bytes, char *ch); +static void parse_addlag(Client *client, int command_bytes, int mtags_bytes); static int client_lagged_up(Client *client); static void ban_handshake_data_flooder(Client *client); @@ -63,7 +63,8 @@ int process_packet(Client *client, char *readbuf, int length, int killsafely) /* flood from unknown connection */ if (IsUnknown(client) && (DBufLength(&client->local->recvQ) > iConf.handshake_data_flood_amount)) { - sendto_snomask(SNO_FLOOD, "Handshake data flood from %s detected", client->local->sockhost); + unreal_log(ULOG_INFO, "flood", "HANDSHAKE_DATA_FLOOD", client, + "Handshake data flood detected from $client.details [$client.ip]"); if (!killsafely) ban_handshake_data_flooder(client); else @@ -74,12 +75,10 @@ int process_packet(Client *client, char *readbuf, int length, int killsafely) /* excess flood check */ if (IsUser(client) && DBufLength(&client->local->recvQ) > get_recvq(client)) { - sendto_snomask(SNO_FLOOD, - "*** Flood -- %s!%s@%s (%d) exceeds %d recvQ", - client->name[0] ? client->name : "*", - client->user ? client->user->username : "*", - client->user ? client->user->realhost : "*", - DBufLength(&client->local->recvQ), get_recvq(client)); + unreal_log(ULOG_INFO, "flood", "RECVQ_EXCEEDED", client, + "Flood from $client.details [$client.ip] exceeds class::recvq ($recvq > $class_recvq) (Client sending too much data)", + log_data_integer("recvq", DBufLength(&client->local->recvQ)), + log_data_integer("class_recvq", get_recvq(client))); if (!killsafely) exit_client(client, NULL, "Excess Flood"); else @@ -105,7 +104,7 @@ 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->firsttime < iConf.handshake_delay)) + !IsNoHandshakeDelay(client) && (TStime() - client->local->creationtime < iConf.handshake_delay)) { return; /* we delay processing of data until set::handshake-delay is reached */ } @@ -140,21 +139,11 @@ void parse_client_queued(Client *client) */ void dopacket(Client *client, char *buffer, int length) { - me.local->receiveB += length; /* Update bytes received */ - client->local->receiveB += length; - if (client->local->receiveB > 1023) - { - client->local->receiveK += (client->local->receiveB >> 10); - client->local->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ - } - if (me.local->receiveB > 1023) - { - me.local->receiveK += (me.local->receiveB >> 10); - me.local->receiveB &= 0x03ff; - } + client->local->traffic.bytes_received += length; + me.local->traffic.bytes_received += length; - me.local->receiveM += 1; /* Update messages received */ - client->local->receiveM += 1; + client->local->traffic.messages_received++; + me.local->traffic.messages_received++; parse(client, buffer, length); } @@ -175,6 +164,7 @@ void parse(Client *cptr, char *buffer, int length) char *ch; int i, ret; MessageTag *mtags = NULL; + int mtags_bytes = 0; /* Take extreme care in this function, as messages can be up to READBUFSIZE * in size, which is 8192 at the time of writing. @@ -184,18 +174,17 @@ void parse(Client *cptr, char *buffer, int length) for (h = Hooks[HOOKTYPE_PACKET]; h; h = h->next) { (*(h->func.intfunc))(from, &me, NULL, &buffer, &length); - if(!buffer) + if (!buffer) return; } - Debug((DEBUG_ERROR, "Parsing: %s (from %s)", buffer, (*cptr->name ? cptr->name : "*"))); - if (IsDeadSocket(cptr)) return; - if ((cptr->local->receiveK >= iConf.handshake_data_flood_amount/1024) && IsUnknown(cptr)) + if ((cptr->local->traffic.bytes_received >= iConf.handshake_data_flood_amount) && IsUnknown(cptr)) { - sendto_snomask(SNO_FLOOD, "Handshake data flood from %s detected", cptr->local->sockhost); + unreal_log(ULOG_INFO, "flood", "HANDSHAKE_DATA_FLOOD", cptr, + "Handshake data flood detected from $client.details [$client.ip]"); ban_handshake_data_flooder(cptr); return; } @@ -203,8 +192,10 @@ void parse(Client *cptr, char *buffer, int length) /* This stores the last executed command in 'backupbuf', useful for debugging crashes */ strlcpy(backupbuf, buffer, sizeof(backupbuf)); -#if defined(DEBUGMODE) && defined(RAWCMDLOGGING) - ircd_log(LOG_ERROR, "<- %s: %s", cptr->name, backupbuf); +#if defined(RAWCMDLOGGING) + unreal_log(ULOG_INFO, "rawtraffic", "TRAFFIC_IN", cptr, + "<- $client: $data", + log_data_string("data", backupbuf)); #endif /* This poisons unused para elements that code should never access */ @@ -218,31 +209,35 @@ void parse(Client *cptr, char *buffer, int length) /* Now, parse message tags, if any */ if (*ch == '@') { + char *start = ch; parse_message_tags(cptr, &ch, &mtags); + if (ch - start > 0) + mtags_bytes = ch - start; /* Skip whitespace again */ for (; *ch == ' '; ch++) ; } - parse2(cptr, &from, mtags, ch); + parse2(cptr, &from, mtags, mtags_bytes, ch); if (IsDead(cptr)) - RunHook3(HOOKTYPE_POST_COMMAND, NULL, mtags, ch); + RunHook(HOOKTYPE_POST_COMMAND, NULL, mtags, ch); else - RunHook3(HOOKTYPE_POST_COMMAND, from, mtags, ch); + RunHook(HOOKTYPE_POST_COMMAND, from, mtags, ch); free_message_tags(mtags); return; } /** Parse the remaining line - helper function for parse(). - * @param cptr The client from which the message was received - * @param from The sender, this may be changed by parse2() when - * the message has a sender, eg :xyz PRIVMSG .. - * @param mtags Message tags received for this message. - * @param ch The incoming line received (buffer), excluding message tags. + * @param cptr The client from which the message was received + * @param from The sender, this may be changed by parse2() when + * the message has a sender, eg :xyz PRIVMSG .. + * @param mtags Message tags received for this message. + * @param mtags_bytes The length of all message tags. + * @param ch The incoming line received (buffer), excluding message tags. */ -static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) +static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, int mtags_bytes, char *ch) { Client *from = cptr; char *s; @@ -328,12 +323,12 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) ch++; } - RunHook3(HOOKTYPE_PRE_COMMAND, from, mtags, ch); + RunHook(HOOKTYPE_PRE_COMMAND, from, mtags, ch); if (*ch == '\0') { if (!IsServer(cptr)) - cptr->local->since++; /* 1s fake lag */ + cptr->local->fake_lag++; /* 1s fake lag */ return; } @@ -351,7 +346,7 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0'); paramcount = MAXPARA; ircstats.is_num++; - parse_addlag(cptr, bytes); + parse_addlag(cptr, bytes, mtags_bytes); } else { @@ -377,7 +372,7 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) if (!cmptr || !(cmptr->flags & CMD_NOLAG)) { /* Add fake lag (doing this early in the code, so we don't forget) */ - parse_addlag(cptr, bytes); + parse_addlag(cptr, bytes, mtags_bytes); } if (!cmptr) { @@ -402,8 +397,6 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) sendto_one(from, NULL, ":%s %d %s %s :Unknown command", me.name, ERR_UNKNOWNCOMMAND, from->name, ch); - Debug((DEBUG_ERROR, "Unknown (%s) from %s", - ch, get_client_name(cptr, TRUE))); } } ircstats.is_unco++; @@ -492,34 +485,34 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) if (cmptr == NULL) { - do_numeric(numeric, from, mtags, i, para); + do_numeric(numeric, from, mtags, i, (const char **)para); return; } cmptr->count++; if (IsUser(cptr) && (cmptr->flags & CMD_RESETIDLE)) - cptr->local->last = TStime(); + cptr->local->idle_since = TStime(); /* Now ready to execute the command */ #ifndef DEBUGMODE if (cmptr->flags & CMD_ALIAS) { - (*cmptr->aliasfunc) (from, mtags, i, para, cmptr->cmd); + (*cmptr->aliasfunc) (from, mtags, i, (const char **)para, cmptr->cmd); } else { if (!cmptr->overriders) - (*cmptr->func) (from, mtags, i, para); + (*cmptr->func) (from, mtags, i, (const char **)para); else - (*cmptr->overriders->func) (cmptr->overriders, from, mtags, i, para); + (*cmptr->overriders->func) (cmptr->overriders, from, mtags, i, (const char **)para); } #else then = clock(); if (cmptr->flags & CMD_ALIAS) { - (*cmptr->aliasfunc) (from, mtags, i, para, cmptr->cmd); + (*cmptr->aliasfunc) (from, mtags, i, (const char **)para, cmptr->cmd); } else { if (!cmptr->overriders) - (*cmptr->func) (from, mtags, i, para); + (*cmptr->func) (from, mtags, i, (const char **)para); else - (*cmptr->overriders->func) (cmptr->overriders, from, mtags, i, para); + (*cmptr->overriders->func) (cmptr->overriders, from, mtags, i, (const char **)para); } if (!IsDead(cptr)) { @@ -528,7 +521,6 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) cmptr->rticks += ticks; else cmptr->lticks += ticks; - cptr->local->cputime += ticks; } #endif } @@ -568,21 +560,45 @@ static void ban_handshake_data_flooder(Client *client) * be able to flood at full speed causing potentially many Mbits or even * GBits of data to be sent out to other clients. * - * @param client The client. - * @param cmdbytes Number of bytes in the command. + * @param client The client. + * @param command_bytes Command length in bytes (excluding message tagss) + * @param mtags_bytes Length of message tags in bytes */ -void parse_addlag(Client *client, int cmdbytes) +void parse_addlag(Client *client, int command_bytes, int mtags_bytes) { + FloodSettings *settings = get_floodsettings_for_user(client, FLD_LAG_PENALTY); + if (!IsServer(client) && !IsNoFakeLag(client) && #ifdef FAKELAG_CONFIGURABLE !(client->local->class && (client->local->class->options & CLASS_OPT_NOFAKELAG)) && #endif !ValidatePermissionsForPath("immune:lag",client,NULL,NULL,NULL)) { - client->local->since += (1 + cmdbytes/90); + int lag_penalty = settings->period[FLD_LAG_PENALTY]; + int lag_penalty_bytes = settings->limit[FLD_LAG_PENALTY]; + + client->local->fake_lag_msec += (1 + (command_bytes/lag_penalty_bytes) + (mtags_bytes/lag_penalty_bytes)) * lag_penalty; + + /* This code takes into account not only the msecs we just calculated + * but also any leftover msec from previous lagging up. + */ + client->local->fake_lag += (client->local->fake_lag_msec / 1000); + client->local->fake_lag_msec = client->local->fake_lag_msec % 1000; } } +/* Add extra fake lag to client, such as after a failed oper attempt. + */ +void add_fake_lag(Client *client, long msec) +{ + if (!MyConnect(client)) + return; + + client->local->fake_lag_msec += msec; + client->local->fake_lag += (client->local->fake_lag_msec / 1000); + client->local->fake_lag_msec = client->local->fake_lag_msec % 1000; +} + /** Returns 1 if the client is lagged up and data should NOT be parsed. * See also parse_addlag() for more information on "fake lag". * @param client The client to check @@ -596,7 +612,7 @@ static int client_lagged_up(Client *client) return 0; if (ValidatePermissionsForPath("immune:lag",client,NULL,NULL,NULL)) return 0; - if (client->local->since - TStime() < 10) + if (client->local->fake_lag - TStime() < 10) return 0; return 1; } @@ -611,18 +627,19 @@ static int client_lagged_up(Client *client) * @note In general you should NOT send anything back if you receive * a numeric, this to prevent creating loops. */ -static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int parc, char *parv[]) +static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int parc, const char *parv[]) { Client *acptr; Channel *channel; char *nick, *p; int i; char buffer[BUFSIZE]; + char targets[BUFSIZE]; if ((numeric < 0) || (numeric > 999)) return -1; - if (MyConnect(client) && !IsServer(client) && !IsUser(client) && IsHandshake(client) && client->serv && !IsServerSent(client)) + if (MyConnect(client) && !IsServer(client) && !IsUser(client) && IsHandshake(client) && client->server && !IsServerSent(client)) { /* This is an outgoing server connect that is currently not yet IsServer() but in 'unknown' state. * We need to handle a few responses here. @@ -631,7 +648,7 @@ static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int p /* STARTTLS: unknown command */ if ((numeric == 451) && (parc > 2) && strstr(parv[1], "STARTTLS")) { - if (client->serv->conf && (client->serv->conf->outgoing.options & CONNECT_INSECURE)) + if (client->server->conf && (client->server->conf->outgoing.options & CONNECT_INSECURE)) start_server_handshake(client); else reject_insecure_server(client); @@ -641,7 +658,9 @@ static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int p /* STARTTLS failed */ if (numeric == 691) { - sendto_umode(UMODE_OPER, "STARTTLS failed for link %s. Please check the other side of the link.", client->name); + unreal_log(ULOG_WARNING, "link", "STARTTLS_FAILED", client, + "Switching from plaintext to TLS via STARTTLS failed for server $client, this is unusual. " + "Please check the other side of the link for errors."); reject_insecure_server(client); return 0; } @@ -652,7 +671,8 @@ static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int p int ret = client_starttls(client); if (ret < 0) { - sendto_umode(UMODE_OPER, "STARTTLS handshake failed for link %s. Strange.", client->name); + unreal_log(ULOG_WARNING, "link", "STARTTLS_FAILED", client, + "Switching from plaintext to TLS via STARTTLS failed for server $client, this is unusual."); reject_insecure_server(client); return ret; } @@ -680,7 +700,8 @@ static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int p concat_params(buffer, sizeof(buffer), parc, parv); /* Now actually process the numeric, IOTW: send it on */ - for (; (nick = strtoken(&p, parv[1], ",")); parv[1] = NULL) + strlcpy(targets, parv[1], sizeof(targets)); + for (nick = strtoken(&p, targets, ","); nick; nick = strtoken(&p, NULL, ",")) { if ((acptr = find_client(nick, NULL))) { @@ -710,7 +731,7 @@ static int do_numeric(int numeric, Client *client, MessageTag *recv_mtags, int p sendto_prefix_one(acptr, client, recv_mtags, ":%s %d %s", client->name, numeric, buffer); } - else if ((channel = find_channel(nick, NULL))) + else if ((channel = find_channel(nick))) { sendto_channel(channel, client, client->direction, 0, 0, SEND_ALL, recv_mtags, @@ -739,10 +760,6 @@ static void remove_unknown(Client *client, char *sender) if (!IsServer(client)) return; -#ifdef DEVELOP - sendto_ops("Killing %s (%s)", sender, backupbuf); - return; -#endif /* * Do kill if it came from a server because it means there is a ghost * user on the other server which needs to be removed. -avalon diff --git a/src/random.c b/src/random.c index 0fc33bc..ebb65cb 100644 --- a/src/random.c +++ b/src/random.c @@ -276,7 +276,7 @@ chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) */ /* Modified for UnrealIRCd by Bram Matthys ("Syzop") in 2019. - * Things like taking out #if(n)def's for openssl (which we always + * Things like taking out #if (n)def's for openssl (which we always * compile with), re-indenting, removing various stuff, etc. */ @@ -308,8 +308,9 @@ static void _rs_stir(void) if (RAND_bytes(rnd, sizeof(rnd)) <= 0) { - ircd_log(LOG_ERROR, "Couldn't obtain random bytes (error 0x%lx)", - (unsigned long)ERR_get_error()); + unreal_log(ULOG_FATAL, "random", "RANDOM_OUT_OF_BYTES", NULL, + "Could not obtain random bytes, error $tls_error_code", + log_data_integer("tls_error_code", ERR_get_error())); abort(); } @@ -422,12 +423,12 @@ static void arc4_addrandom(void *dat, int datlen) void add_entropy_configfile(struct stat *st, char *buf) { - unsigned char mdbuf[16]; + char sha256buf[SHA256_DIGEST_LENGTH]; arc4_addrandom(&st->st_size, sizeof(st->st_size)); arc4_addrandom(&st->st_mtime, sizeof(st->st_mtime)); - DoMD5(mdbuf, buf, strlen(buf)); - arc4_addrandom(&mdbuf, sizeof(mdbuf)); + sha256hash_binary(sha256buf, buf, strlen(buf)); + arc4_addrandom(sha256buf, sizeof(sha256buf)); } /* @@ -459,7 +460,6 @@ void init_random() if (fd >= 0) { int n = read(fd, &rdat.rnd, sizeof(rdat.rnd)); - Debug((DEBUG_INFO, "init_random: read from /dev/urandom returned %d", n)); close(fd); } #else diff --git a/src/send.c b/src/send.c index e4284be..13fbaf5 100644 --- a/src/send.c +++ b/src/send.c @@ -28,8 +28,8 @@ /* Some forward declarions are needed */ void vsendto_one(Client *to, MessageTag *mtags, const char *pattern, va_list vl); -void vsendto_prefix_one(Client *to, Client *from, MessageTag *mtags, const char *pattern, va_list vl); -static int vmakebuf_local_withprefix(char *buf, size_t buflen, Client *from, const char *pattern, va_list vl); +void vsendto_prefix_one(Client *to, Client *from, MessageTag *mtags, const char *pattern, va_list vl) __attribute__((format(printf,4,0))); +static int vmakebuf_local_withprefix(char *buf, size_t buflen, Client *from, const char *pattern, va_list vl) __attribute__((format(printf,4,0))); #define ADD_CRLF(buf, len) { if (len > 510) len = 510; \ buf[len++] = '\r'; buf[len++] = '\n'; buf[len] = '\0'; } while(0) @@ -59,7 +59,7 @@ MODVAR int current_serial; * @param to Client to mark as dead * @param notice The quit reason to use */ -int dead_socket(Client *to, char *notice) +int dead_socket(Client *to, const char *notice) { DBufClear(&to->local->recvQ); DBufClear(&to->local->sendQ); @@ -76,9 +76,14 @@ int dead_socket(Client *to, char *notice) return -1; /* don't overwrite & don't send multiple times */ if (!IsUser(to) && !IsUnknown(to) && !IsClosing(to)) - sendto_ops_and_log("Link to server %s (%s) closed: %s", - to->name, to->ip?to->ip:"", notice); - Debug((DEBUG_ERROR, "dead_socket: %s - %s", notice, get_client_name(to, FALSE))); + { + /* Looks like a duplicate error message to me? + * If so, remove it here. + */ + unreal_log(ULOG_ERROR, "link", "LINK_CLOSING", to, + "Link to server $client.details closed: $reason", + log_data_string("reason", notice)); + } safe_strdup(to->local->error_str, notice); return -1; } @@ -123,7 +128,6 @@ int send_queued(Client *to) return dead_socket(to, buf); } dbuf_delete(&to->local->sendQ, rlen); - to->local->lastsq = DBufLength(&to->local->sendQ) / 1024; if (want_read) { /* SSL_write indicated that it cannot write data at this @@ -207,9 +211,17 @@ void sendto_one(Client *to, MessageTag *mtags, FORMAT_STRING(const char *pattern */ void vsendto_one(Client *to, MessageTag *mtags, const char *pattern, va_list vl) { - char *mtags_str = mtags ? mtags_to_string(mtags, to) : NULL; + const char *mtags_str = mtags ? mtags_to_string(mtags, to) : NULL; + /* Need to ignore -Wformat-nonliteral here */ +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif ircvsnprintf(sendbuf, sizeof(sendbuf), pattern, vl); +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif if (BadPtr(mtags_str)) { @@ -248,8 +260,6 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) Hook *h; Client *intended_to = to; - Debug((DEBUG_ERROR, "Sending [%s] to %s", msg, to->name)); - if (to->direction) to = to->direction; if (IsDeadSocket(to)) @@ -259,11 +269,7 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) { /* This is normal when 'to' was being closed (via exit_client * and close_connection) --Run - * Print the debug message anyway... */ - Debug((DEBUG_ERROR, - "Local socket %s with negative fd %d... AARGH!", to->name, - to->local->fd)); return; } @@ -289,14 +295,17 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) p = strchr(msg+1, ' '); if (!p) { - ircd_log(LOG_ERROR, "[BUG] sendbufto_one(): Malformed message: %s", - msg); + unreal_log(ULOG_WARNING, "send", "SENDBUFTO_ONE_MALFORMED_MSG", to, + "Malformed message to $client: $buf", + log_data_string("buf", msg)); return; } - if (p - msg > 500) + if (p - msg > 4094) { - ircd_log(LOG_ERROR, "[BUG] sendbufto_one(): Spec-wise legal, but massively oversized message-tag (len %d)", - (int)(p - msg)); + unreal_log(ULOG_WARNING, "send", "SENDBUFTO_ONE_OVERSIZED_MSG", to, + "Oversized message to $client (length $length): $buf", + log_data_integer("length", p - msg), + log_data_string("buf", msg)); return; } p++; /* skip space character */ @@ -315,10 +324,17 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) len = quick; } - if (len >= 1024) + if (len >= 10240) { - ircd_log(LOG_ERROR, "sendbufto_one: len=%d, quick=%u", len, quick); + unreal_log(ULOG_WARNING, "send", "SENDBUFTO_ONE_OVERSIZED_MSG2", to, + "Oversized message to $client (length $length): $buf", + log_data_integer("length", len), + log_data_string("buf", msg)); +#ifdef DEBUGMODE abort(); +#else + return; +#endif } if (IsMe(to)) @@ -327,9 +343,9 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) p = strchr(msg, '\r'); if (p) *p = '\0'; - snprintf(tmp_msg, 500, "Trying to send data to myself! '%s'", msg); - ircd_log(LOG_ERROR, "%s", tmp_msg); - sendto_ops("%s", tmp_msg); /* recursion? */ + unreal_log(ULOG_WARNING, "send", "SENDBUFTO_ONE_ME_MESSAGE", to, + "Trying to send data to myself: $buf", + log_data_string("buf", tmp_msg)); return; } @@ -340,7 +356,7 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) return; } -#if defined(DEBUGMODE) && defined(RAWCMDLOGGING) +#if defined(RAWCMDLOGGING) { char copy[512], *p; strlcpy(copy, msg, len > sizeof(copy) ? sizeof(copy) : len); @@ -348,16 +364,18 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) if (p) *p = '\0'; p = strchr(copy, '\r'); if (p) *p = '\0'; - ircd_log(LOG_ERROR, "-> %s: %s", to->name, copy); + unreal_log(ULOG_INFO, "rawtraffic", "TRAFFIC_OUT", to, + "-> $client: $data", + log_data_string("data", copy)); } #endif if (DBufLength(&to->local->sendQ) > get_sendq(to)) { - if (IsServer(to)) - sendto_ops("Max SendQ limit exceeded for %s: %u > %d", - get_client_name(to, FALSE), DBufLength(&to->local->sendQ), - get_sendq(to)); + unreal_log(ULOG_INFO, "flood", "SENDQ_EXCEEDED", to, + "Flood of queued data to $client.details [$client.ip] exceeds class::sendq ($sendq > $class_sendq) (Too much data queued to be sent to this client)", + log_data_integer("sendq", DBufLength(&to->local->sendQ)), + log_data_integer("class_sendq", get_sendq(to))); dead_socket(to, "Max SendQ exceeded"); return; } @@ -370,8 +388,8 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) * only really sent. Queued bytes get updated in SendQueued. */ // FIXME: something is wrong here, I think we do double counts, either in message or in traffic, I forgot.. CHECK !!!! - to->local->sendM += 1; - me.local->sendM += 1; + to->local->traffic.messages_sent++; + me.local->traffic.messages_sent++; /* Previously we ran send_queued() here directly, but that is * a bad idea, CPU-wise. So now we just mark the client indicating @@ -385,11 +403,11 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) * now there is 1 single function. This also means that you most * likely will pass NULL or 0 as some parameters. * @param channel The channel to send to - * @param from The source of the message - * @param skip The client to skip (can be NULL). - * Note that if you specify a remote link then - * you usually mean xyz->direction and not xyz. - * @param prefix Any combination of PREFIX_* (can be 0 for all) + * @param from The source of the message + * @param skip The client to skip (can be NULL). + * Note that if you specify a remote link then + * you usually mean xyz->direction and not xyz. + * @param member_modes Require any of the member_modes to be set (eg: "o"), or NULL to skip this check. * @param clicap Client capability the recipient should have * (this only works for local clients, we will * always send the message to remote clients and @@ -414,14 +432,14 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) * sendnumeric(client, ERR_NEEDMOREPARAMS, "SAYHELLO"); * return; * } - * channel = find_channel(parv[1], NULL); + * channel = find_channel(parv[1]); * if (!channel) * { * sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]); * return; * } * new_message(client, recv_mtags, &mtags); - * sendto_channel(channel, client, client->direction, 0, 0, + * sendto_channel(channel, client, client->direction, NULL, 0, * SEND_LOCAL|SEND_REMOTE, mtags, * ":%s PRIVMSG %s :Hello everyone!!!", * client->name, channel->name); @@ -430,13 +448,20 @@ void sendbufto_one(Client *to, char *msg, unsigned int quick) * @endcode */ void sendto_channel(Channel *channel, Client *from, Client *skip, - int prefix, long clicap, int sendflags, + char *member_modes, long clicap, int sendflags, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) { va_list vl; Member *lp; Client *acptr; + char member_modes_ext[64]; + + if (member_modes) + { + channel_member_modes_generate_equal_or_greater(member_modes, member_modes_ext, sizeof(member_modes_ext)); + member_modes = member_modes_ext; + } ++current_serial; for (lp = channel->members; lp; lp = lp->next) @@ -452,23 +477,9 @@ void sendto_channel(Channel *channel, Client *from, Client *skip, /* Don't send to NOCTCP clients */ if (has_user_mode(acptr, 'T') && (sendflags & SKIP_CTCP)) continue; - /* Now deal with 'prefix' (if non-zero) */ - if (!prefix) - goto good; - if ((prefix & PREFIX_HALFOP) && (lp->flags & CHFL_HALFOP)) - goto good; - if ((prefix & PREFIX_VOICE) && (lp->flags & CHFL_VOICE)) - goto good; - if ((prefix & PREFIX_OP) && (lp->flags & CHFL_CHANOP)) - goto good; -#ifdef PREFIX_AQ - if ((prefix & PREFIX_ADMIN) && (lp->flags & CHFL_CHANADMIN)) - goto good; - if ((prefix & PREFIX_OWNER) && (lp->flags & CHFL_CHANOWNER)) - goto good; -#endif - continue; -good: + /* Now deal with 'member_modes' (if not NULL) */ + if (member_modes && !check_channel_access_member(lp, member_modes)) + continue; /* Now deal with 'clicap' (if non-zero) */ if (clicap && MyUser(acptr) && ((clicap & CAP_INVERT) ? HasCapabilityFast(acptr, clicap) : !HasCapabilityFast(acptr, clicap))) continue; @@ -629,7 +640,7 @@ void sendto_local_common_channels(Client *user, Client *skip, long clicap, Messa ** addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de) */ -static int match_it(Client *one, char *mask, int what) +static int match_it(Client *one, const char *mask, int what) { switch (what) { @@ -651,7 +662,7 @@ static int match_it(Client *one, char *mask, int what) * @param pattern Format string * @param ... Parameters to the format string */ -void sendto_match_butone(Client *one, Client *from, char *mask, int what, +void sendto_match_butone(Client *one, Client *from, const char *mask, int what, MessageTag *mtags, FORMAT_STRING(const char *pattern), ...) { va_list vl; @@ -693,116 +704,6 @@ void sendto_match_butone(Client *one, Client *from, char *mask, int what, } } -/** Send a message to all locally connected IRCOps - * @param pattern The format string / pattern to use. - * @param ... Format string parameters. - */ -void sendto_ops(FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - Client *acptr; - char nbuf[1024]; - - list_for_each_entry(acptr, &lclient_list, lclient_node) - if (!IsServer(acptr) && !IsMe(acptr) && SendServNotice(acptr)) - { - ircsnprintf(nbuf, sizeof(nbuf), ":%s NOTICE %s :*** ", me.name, acptr->name); - strlcat(nbuf, pattern, sizeof nbuf); - - va_start(vl, pattern); - vsendto_one(acptr, NULL, nbuf, vl); - va_end(vl); - } -} - -/* Hmm.. so local sending is called sendto_ops() and local+remote is sendto_ops_butone(), - * that is weird naming... (TODO fix some day in a new major series) - */ - -/** Send a message to all IRCOps (local and remote), except one. - * @param one Skip sending the message to this client/direction - * @param from The sender (can not be NULL) - * @param pattern The format string / pattern to use. - * @param ... Format string parameters. - */ -void sendto_ops_butone(Client *one, Client *from, FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - Client *acptr; - - ++current_serial; - list_for_each_entry(acptr, &client_list, client_node) - { - if (!SendWallops(acptr)) - continue; - if (acptr->direction->local->serial == current_serial) /* sent message along it already ? */ - continue; - if (acptr->direction == one) - continue; /* ...was the one I should skip */ - acptr->direction->local->serial = current_serial; - - va_start(vl, pattern); - vsendto_prefix_one(acptr->direction, from, NULL, pattern, vl); - va_end(vl); - } -} - -/** This function does exactly the same as sendto_ops() in practice in 5.x. - * There used to be a difference between sendto_ops() and sendto_realops() - * with regards to user-settable snomasks, but this is no longer the case. - * TODO: remove this function in some future cleanup - */ -void sendto_realops(FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - Client *acptr; - char nbuf[1024]; - - list_for_each_entry(acptr, &oper_list, special_node) - { - ircsnprintf(nbuf, sizeof(nbuf), ":%s NOTICE %s :*** ", me.name, acptr->name); - strlcat(nbuf, pattern, sizeof nbuf); - - va_start(vl, pattern); - vsendto_one(acptr, NULL, nbuf, vl); - va_end(vl); - } -} - -/** Send a message to all locally connected IRCOps and also log the error. - * @param pattern The format string / pattern to use. - * @param ... Format string parameters. - */ -void sendto_ops_and_log(FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - char buf[1024]; - - va_start(vl, pattern); - ircvsnprintf(buf, sizeof(buf), pattern, vl); - va_end(vl); - - ircd_log(LOG_ERROR, "%s", buf); - sendto_umode(UMODE_OPER, "%s", buf); -} - -/** This function does exactly the same as sendto_ops_and_log() - * TODO: remove this function in some future cleanup - */ -void sendto_realops_and_log(FORMAT_STRING(const char *fmt), ...) -{ - va_list vl; - static char buf[2048]; - - va_start(vl, fmt); - vsnprintf(buf, sizeof(buf), fmt, vl); - va_end(vl); - - sendto_realops("%s", buf); - ircd_log(LOG_ERROR, "%s", buf); -} - - /** Send a message to all locally connected users with specified user mode. * @param umodes The umode that the recipient should have set (one of UMODE_) * @param pattern The format string / pattern to use. @@ -835,22 +736,13 @@ void sendto_umode_global(int umodes, FORMAT_STRING(const char *pattern), ...) { va_list vl; Client *acptr; + Umode *um; char nbuf[1024]; - int i; char modestr[128]; char *p; /* Convert 'umodes' (int) to 'modestr' (string) */ - *modestr = '\0'; - p = modestr; - for(i = 0; i <= Usermode_highest; i++) - { - if (!Usermode_Table[i].flag) - continue; - if (umodes & Usermode_Table[i].mode) - *p++ = Usermode_Table[i].flag; - } - *p = '\0'; + get_usermode_string_raw_r(umodes, modestr, sizeof(modestr)); list_for_each_entry(acptr, &lclient_list, lclient_node) { @@ -873,66 +765,12 @@ void sendto_umode_global(int umodes, FORMAT_STRING(const char *pattern), ...) } } -/** Send a message to all locally connected users with specified snomask. - * @param snomask The snomask that the recipient should have set (one of SNO_*) - * @param pattern The format string / pattern to use. - * @param ... Format string parameters. - */ -void sendto_snomask(int snomask, FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - Client *acptr; - char nbuf[2048]; - - va_start(vl, pattern); - ircvsnprintf(nbuf, sizeof(nbuf), pattern, vl); - va_end(vl); - - list_for_each_entry(acptr, &oper_list, special_node) - { - if (acptr->user->snomask & snomask) - sendnotice(acptr, "%s", nbuf); - } -} - -/** Send a message to all users with specified snomask (local and remote users). - * @param snomask The snomask that the recipient should have set (one of SNO_*) - * @param pattern The format string / pattern to use. - * @param ... Format string parameters. - */ -void sendto_snomask_global(int snomask, FORMAT_STRING(const char *pattern), ...) -{ - va_list vl; - Client *acptr; - int i; - char nbuf[2048], snobuf[32], *p; - - va_start(vl, pattern); - ircvsnprintf(nbuf, sizeof(nbuf), pattern, vl); - va_end(vl); - - list_for_each_entry(acptr, &oper_list, special_node) - { - if (acptr->user->snomask & snomask) - sendnotice(acptr, "%s", nbuf); - } - - /* Build snomasks-to-send-to buffer */ - snobuf[0] = '\0'; - for (i = 0, p=snobuf; i<= Snomask_highest; i++) - if (snomask & Snomask_Table[i].mode) - *p++ = Snomask_Table[i].flag; - *p = '\0'; - - sendto_server(NULL, 0, 0, NULL, ":%s SENDSNO %s :%s", me.id, snobuf, nbuf); -} - /** Send CAP DEL and CAP NEW notification to clients supporting it. * This function is mostly meant to be used by the CAP and SASL modules. * @param add Whether the CAP token is added (1) or removed (0) * @param token The CAP token */ -void send_cap_notify(int add, char *token) +void send_cap_notify(int add, const char *token) { Client *client; ClientCapability *clicap = ClientCapabilityFindReal(token); @@ -944,7 +782,7 @@ void send_cap_notify(int add, char *token) { if (add) { - char *args = NULL; + const char *args = NULL; if (clicap) { if (clicap->visible && !clicap->visible(client)) @@ -989,7 +827,7 @@ static int vmakebuf_local_withprefix(char *buf, size_t buflen, Client *from, con va_arg(vl, char *); /* eat first parameter */ *buf = ':'; - strcpy(buf+1, from->name); + strlcpy(buf+1, from->name, buflen-1); if (IsUser(from)) { @@ -998,17 +836,24 @@ static int vmakebuf_local_withprefix(char *buf, size_t buflen, Client *from, con if (*username) { - strcat(buf, "!"); - strcat(buf, username); + strlcat(buf, "!", buflen); + strlcat(buf, username, buflen); } if (*host) { - strcat(buf, "@"); - strcat(buf, host); + strlcat(buf, "@", buflen); + strlcat(buf, host, buflen); } } /* Now build the remaining string */ +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif ircvsnprintf(buf + strlen(buf), buflen - strlen(buf), &pattern[3], vl); +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif } else { @@ -1048,7 +893,7 @@ void sendto_prefix_one(Client *to, Client *from, MessageTag *mtags, FORMAT_STRIN */ void vsendto_prefix_one(Client *to, Client *from, MessageTag *mtags, const char *pattern, va_list vl) { - char *mtags_str = mtags ? mtags_to_string(mtags, to) : NULL; + const char *mtags_str = mtags ? mtags_to_string(mtags, to) : NULL; if (to && from && MyUser(to) && from->user) vmakebuf_local_withprefix(sendbuf, sizeof sendbuf, from, pattern, vl); @@ -1066,65 +911,12 @@ void vsendto_prefix_one(Client *to, Client *from, MessageTag *mtags, const char } } -void sendto_connectnotice(Client *newuser, int disconnect, char *comment) -{ - Client *acptr; - char connect[512]; - - if (!disconnect) - { - RunHook(HOOKTYPE_LOCAL_CONNECT, newuser); - - ircsnprintf(connect, sizeof(connect), - "*** Client connecting: %s (%s@%s) [%s] %s", newuser->name, - newuser->user->username, newuser->user->realhost, newuser->ip, - get_connect_extinfo(newuser)); - } - else - { - ircsnprintf(connect, sizeof(connect), "*** Client exiting: %s (%s@%s) [%s] (%s)", - newuser->name, newuser->user->username, newuser->user->realhost, newuser->ip, comment); - } - - list_for_each_entry(acptr, &oper_list, special_node) - { - if (acptr->user->snomask & SNO_CLIENT) - sendnotice(acptr, "%s", connect); - } -} - -void sendto_fconnectnotice(Client *newuser, int disconnect, char *comment) -{ - Client *acptr; - char connect[512]; - - if (!disconnect) - { - ircsnprintf(connect, sizeof(connect), - "*** Client connecting: %s (%s@%s) [%s] %s", newuser->name, - newuser->user->username, newuser->user->realhost, newuser->ip ? newuser->ip : "0", - get_connect_extinfo(newuser)); - } - else - { - ircsnprintf(connect, sizeof(connect), "*** Client exiting: %s (%s@%s) [%s] (%s)", - newuser->name, newuser->user->username, newuser->user->realhost, - newuser->ip ? newuser->ip : "0", comment); - } - - list_for_each_entry(acptr, &oper_list, special_node) - { - if (acptr->user->snomask & SNO_FCLIENT) - sendto_one(acptr, NULL, ":%s NOTICE %s :%s", newuser->user->server, acptr->name, connect); - } -} - /** Introduce user to all other servers, except the one to skip. * @param one Server to skip (can be NULL) * @param client Client to introduce * @param umodes User modes of client */ -void sendto_serv_butone_nickcmd(Client *one, Client *client, char *umodes) +void sendto_serv_butone_nickcmd(Client *one, MessageTag *mtags, Client *client, const char *umodes) { Client *acptr; @@ -1133,7 +925,7 @@ void sendto_serv_butone_nickcmd(Client *one, Client *client, char *umodes) if (one && acptr == one->direction) continue; - sendto_one_nickcmd(acptr, client, umodes); + sendto_one_nickcmd(acptr, mtags, client, umodes); } } @@ -1142,9 +934,10 @@ void sendto_serv_butone_nickcmd(Client *one, Client *client, char *umodes) * @param client Client to introduce * @param umodes User modes of client */ -void sendto_one_nickcmd(Client *server, Client *client, char *umodes) +void sendto_one_nickcmd(Client *server, MessageTag *mtags, Client *client, const char *umodes) { char *vhost; + char mtags_generated = 0; if (!*umodes) umodes = "+"; @@ -1164,13 +957,22 @@ void sendto_one_nickcmd(Client *server, Client *client, char *umodes) vhost = "*"; } - sendto_one(server, NULL, + if (mtags == NULL) + { + moddata_add_s2s_mtags(client, &mtags); + mtags_generated = 1; + } + + sendto_one(server, mtags, ":%s UID %s %d %lld %s %s %s %s %s %s %s %s :%s", - client->srvptr->id, client->name, client->hopcount, + client->uplink->id, client->name, client->hopcount, (long long)client->lastnick, client->user->username, client->user->realhost, client->id, - client->user->svid, umodes, vhost, getcloak(client), + client->user->account, umodes, vhost, getcloak(client), encode_ip(client->ip), client->info); + + if (mtags_generated) + safe_free_message_tags(mtags); } /* sidenote: sendnotice() and sendtxtnumeric() assume no client or server @@ -1205,36 +1007,6 @@ void sendnotice_multiline(Client *client, MultiLine *m) sendnotice(client, "%s", m->line); } - -/** Send numeric message to a client. - * @param to The recipient - * @param numeric The numeric, one of RPL_* or ERR_*, see src/numeric.c - * @param ... The parameters for the numeric - * @note Be sure to provide the correct number and type of parameters that belong to the numeric. Check src/numeric.c when in doubt! - * @section sendnumeric_examples Examples - * @subsection sendnumeric_permission_denied Send "Permission Denied" numeric - * This numeric has no parameter, so is simple: - * @code - * sendnumeric(client, ERR_NOPRIVILEGES); - * @endcode - * @subsection sendnumeric_notenoughparameters Send "Not enough parameters" numeric - * This numeric requires 1 parameter: the name of the command. - * @code - * sendnumeric(client, ERR_NEEDMOREPARAMS, "SOMECOMMAND"); - * @endcode - */ -void sendnumeric(Client *to, int numeric, ...) -{ - va_list vl; - char pattern[512]; - - snprintf(pattern, sizeof(pattern), ":%s %.3d %s %s", me.name, numeric, to->name[0] ? to->name : "*", rpl_str(numeric)); - - va_start(vl, numeric); - vsendto_one(to, NULL, pattern, vl); - va_end(vl); -} - /** Send numeric message to a client - format to user specific needs. * This will ignore the numeric definition of src/numeric.c and always send ":me.name numeric clientname " * followed by the pattern and format string you choose. @@ -1276,6 +1048,56 @@ void sendtxtnumeric(Client *to, FORMAT_STRING(const char *pattern), ...) va_end(vl); } +/** Build buffer in order to send a numeric message to a client - rarely used. + * @param buf The buffer that should be used + * @param buflen The size of the buffer + * @param to The recipient + * @param numeric The numeric, one of RPL_* or ERR_*, see src/numeric.c + * @param pattern The format string / pattern to use. + * @param ... Format string parameters. + */ +void buildnumericfmt(char *buf, size_t buflen, Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + char realpattern[512]; + + snprintf(realpattern, sizeof(realpattern), ":%s %.3d %s %s", me.name, numeric, to->name[0] ? to->name : "*", pattern); + + va_start(vl, pattern); + /* Need to ignore -Wformat-nonliteral here */ +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + vsnprintf(buf, buflen, realpattern, vl); +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + va_end(vl); +} + +void add_nvplist_numeric_fmt(NameValuePrioList **lst, int priority, const char *name, Client *to, int numeric, FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + char realpattern[512], buf[512]; + + snprintf(realpattern, sizeof(realpattern), ":%s %.3d %s %s", me.name, numeric, to->name[0] ? to->name : "*", pattern); + + va_start(vl, pattern); + /* Need to ignore -Wformat-nonliteral here */ +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + vsnprintf(buf, sizeof(buf), realpattern, vl); +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + va_end(vl); + + add_nvplist(lst, priority, name, buf); +} + /* Send raw data directly to socket, bypassing everything. * Looks like an interesting function to call? NO! STOP! * Don't use this function. It may only be used by the initial @@ -1291,14 +1113,14 @@ void sendtxtnumeric(Client *to, FORMAT_STRING(const char *pattern), ...) * By the way, did I already mention that you SHOULD NOT USE THIS * FUNCTION? ;) */ -void send_raw_direct(Client *user, FORMAT_STRING(FORMAT_STRING(const char *pattern)), ...) +void send_raw_direct(Client *user, FORMAT_STRING(const char *pattern), ...) { va_list vl; int sendlen; *sendbuf = '\0'; va_start(vl, pattern); - sendlen = vmakebuf_local_withprefix(sendbuf, sizeof sendbuf, user, pattern, vl); + sendlen = vmakebuf_local_withprefix(sendbuf, sizeof(sendbuf), user, pattern, vl); va_end(vl); (void)send(user->local->fd, sendbuf, sendlen, 0); } diff --git a/src/serv.c b/src/serv.c index a58b417..6fc1377 100644 --- a/src/serv.c +++ b/src/serv.c @@ -53,14 +53,17 @@ int MODVAR spamf_ugly_vchanoverride = 0; void read_motd(const char *filename, MOTDFile *motd); void do_read_motd(const char *filename, MOTDFile *themotd); -#ifdef USE_LIBCURL -void read_motd_async_downloaded(const char *url, const char *filename, const char *errorbuf, int cached, MOTDDownload *motd_download); -#endif extern MOTDLine *find_file(char *, short); void reread_motdsandrules(); +#if defined(__GNUC__) +/* Temporarily ignore for this function. FIXME later!!! */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + /** Send a message upstream if necessary and check if it's for us. * @param client The sender * @param mtags Message tags associated with this message @@ -68,13 +71,24 @@ void reread_motdsandrules(); * @param server This indicates parv[server] contains the destination * @param parc Parameter count (MAX 8!!) * @param parv Parameter values (MAX 8!!) - * @note Command can have only max 8 parameters (parv[8]) - * @note parv[server] is replaced with the name of the matched client. + * @note While sending parv[server] is replaced with the name of the matched client + * (virtually, as parv[] is not actually written to) */ -int hunt_server(Client *client, MessageTag *mtags, char *command, int server, int parc, char *parv[]) +int hunt_server(Client *client, MessageTag *mtags, const char *command, int server, int parc, const char *parv[]) { Client *acptr; - char *saved; + const char *saved; + int i; + char buf[1024]; + + if (strchr(command, '%') || strchr(command, ' ')) + { + unreal_log(ULOG_ERROR, "main", "BUG_HUNT_SERVER", client, + "[BUG] hunt_server called with command '$command' but it may not contain " + "spaces or percentage signs nowadays, it must be ONLY the command.", + log_data_string("command", command)); + abort(); + } /* This would be strange and bad. Previous version assumed "it's for me". Hmm.. okay. */ if (parc <= server || BadPtr(parv[server])) @@ -102,21 +116,46 @@ int hunt_server(Client *client, MessageTag *mtags, char *command, int server, in return HUNTED_NOSUCH; } - /* Replace "server" part with actual servername (eg: 'User' -> 'x.y.net') - * Ugly. Previous version didn't even restore the state, now we do. + /* This puts all parv[] arguments in 'buf' + * Taken from concat_params() but this one is + * with parv[server] magic replacement. */ - saved = parv[server]; - parv[server] = acptr->id; + *buf = '\0'; + for (i = 1; i < parc; i++) + { + const char *param = parv[i]; - sendto_one(acptr, mtags, command, client->id, - parv[1], parv[2], parv[3], parv[4], - parv[5], parv[6], parv[7], parv[8]); + if (!param) + break; - parv[server] = saved; + /* The magic parv[server] replacement: + * this replaces eg 'User' with '001' in S2S traffic. + */ + if (i == server) + param = acptr->id; + + if (*buf) + strlcat(buf, " ", sizeof(buf)); + + if (strchr(param, ' ') || (*param == ':')) + { + /* Last parameter, with : */ + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, parv[i], sizeof(buf)); + break; + } + strlcat(buf, parv[i], sizeof(buf)); + } + + sendto_one(acptr, mtags, ":%s %s %s", client->id, command, buf); return HUNTED_PASS; } +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #ifndef _WIN32 /** Grab operating system name on Windows (outdated) */ char *getosname(void) @@ -146,12 +185,17 @@ char *getosname(void) #endif /** Helper function to send version strings */ -void send_version(Client *client, int reply) +void send_version(Client *client, int remote) { int i; for (i = 0; ISupportStrings[i]; i++) - sendnumeric(client, reply, ISupportStrings[i]); + { + if (remote) + sendnumeric(client, RPL_REMOTEISUPPORT, ISupportStrings[i]); + else + sendnumeric(client, RPL_ISUPPORT, ISupportStrings[i]); + } } /** VERSION command: @@ -162,11 +206,11 @@ CMD_FUNC(cmd_version) /* Only allow remote VERSIONs if registered -- Syzop */ if (!IsUser(client) && !IsServer(client)) { - send_version(client, RPL_ISUPPORT); + send_version(client, 0); return; } - if (hunt_server(client, recv_mtags, ":%s VERSION :%s", 1, parc, parv) == HUNTED_ISME) + if (hunt_server(client, recv_mtags, "VERSION", 1, parc, parv) == HUNTED_ISME) { sendnumeric(client, RPL_VERSION, version, debugmode, me.name, (ValidatePermissionsForPath("server:info",client,NULL,NULL,NULL) ? serveropts : "0"), @@ -185,9 +229,9 @@ CMD_FUNC(cmd_version) sendnotice(client, "%s", pcre2_version()); } if (MyUser(client)) - send_version(client,RPL_ISUPPORT); + send_version(client,0); else - send_version(client,RPL_REMOTEISUPPORT); + send_version(client,1); } } @@ -206,14 +250,14 @@ void send_proto(Client *client, ConfigItem_link *aconf) */ /* First line */ - sendto_one(client, NULL, "PROTOCTL NOQUIT NICKv2 SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID %s %s", + sendto_one(client, NULL, "PROTOCTL NOQUIT NICKv2 SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID NEXTBANS %s %s", iConf.ban_setter_sync ? "SJSBY" : "", ClientCapabilityFindReal("message-tags") ? "MTAGS" : ""); /* Second line */ - sendto_one(client, NULL, "PROTOCTL CHANMODES=%s%s,%s%s,%s%s,%s%s USERMODES=%s BOOTED=%lld PREFIX=%s SID=%s MLOCK TS=%lld EXTSWHOIS", - CHPAR1, EXPAR1, CHPAR2, EXPAR2, CHPAR3, EXPAR3, CHPAR4, EXPAR4, - umodestring, (long long)me.local->since, prefix->value, + sendto_one(client, NULL, "PROTOCTL CHANMODES=%s%s,%s,%s,%s USERMODES=%s BOOTED=%lld PREFIX=%s SID=%s MLOCK TS=%lld EXTSWHOIS", + CHPAR1, EXPAR1, EXPAR2, EXPAR3, EXPAR4, + umodestring, (long long)me.local->fake_lag, prefix->value, me.id, (long long)TStime()); /* Third line */ @@ -227,7 +271,7 @@ void send_proto(Client *client, ConfigItem_link *aconf) #endif /** Special filter for remote commands */ -int remotecmdfilter(Client *client, int parc, char *parv[]) +int remotecmdfilter(Client *client, int parc, const char *parv[]) { /* no remote requests permitted from non-ircops */ if (MyUser(client) && !ValidatePermissionsForPath("server:remote",client,NULL,NULL,NULL) && !BadPtr(parv[1])) @@ -252,6 +296,7 @@ char *unrealinfo[] = "* Bram Matthys (Syzop) ", "", "Coders:", + "* Krzysztof Beresztant (k4be) ", "* Gottem ", "* i ", "", @@ -286,7 +331,7 @@ void cmd_info_send(Client *client) sendnumericfmt(client, RPL_INFO, ":| UnrealIRCd Homepage: https://www.unrealircd.org"); sendnumericfmt(client, RPL_INFO, ":============================================"); sendnumericfmt(client, RPL_INFO, ":Birth Date: %s, compile # %s", creation, generation); - sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->firsttime)); + sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->creationtime)); sendnumericfmt(client, RPL_INFO, ":ReleaseID (%s)", buildid); sendnumeric(client, RPL_ENDOFINFO); } @@ -299,7 +344,7 @@ CMD_FUNC(cmd_info) if (remotecmdfilter(client, parc, parv)) return; - if (hunt_server(client, recv_mtags, ":%s INFO :%s", 1, parc, parv) == HUNTED_ISME) + if (hunt_server(client, recv_mtags, "INFO", 1, parc, parv) == HUNTED_ISME) cmd_info_send(client); } @@ -313,7 +358,7 @@ CMD_FUNC(cmd_license) if (remotecmdfilter(client, parc, parv)) return; - if (hunt_server(client, recv_mtags, ":%s LICENSE :%s", 1, parc, parv) == HUNTED_ISME) + if (hunt_server(client, recv_mtags, "LICENSE", 1, parc, parv) == HUNTED_ISME) { while (*text) sendnumeric(client, RPL_INFO, *text++); @@ -333,20 +378,20 @@ CMD_FUNC(cmd_credits) if (remotecmdfilter(client, parc, parv)) return; - if (hunt_server(client, recv_mtags, ":%s CREDITS :%s", 1, parc, parv) == HUNTED_ISME) + if (hunt_server(client, recv_mtags, "CREDITS", 1, parc, parv) == HUNTED_ISME) { while (*text) sendnumeric(client, RPL_INFO, *text++); sendnumeric(client, RPL_INFO, ""); sendnumericfmt(client, RPL_INFO, ":Birth Date: %s, compile # %s", creation, generation); - sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->firsttime)); + sendnumericfmt(client, RPL_INFO, ":On-line since %s", myctime(me.local->creationtime)); sendnumeric(client, RPL_ENDOFINFO); } } -/** Return flags for a client (connection), eg 's' for SSL/TLS - used in STATS L/l */ -char *get_client_status(Client *client) +/** Return flags for a client (connection), eg 's' for TLS - used in STATS L/l */ +const char *get_client_status(Client *client) { static char buf[10]; char *p = buf; @@ -371,28 +416,7 @@ char *get_client_status(Client *client) } *p++ = ']'; *p++ = '\0'; - return (buf); -} - -/** Used to blank out ports -- Barubary - only used in STATS l/L */ -char *get_client_name2(Client *client, int showports) -{ - char *pointer = get_client_name(client, TRUE); - - if (!pointer) - return NULL; - if (showports) - return pointer; - if (!strrchr(pointer, '.')) - return NULL; - /* - * This may seem like wack but remind this is only used - * in rows of get_client_name2's, so it's perfectly fair - * - */ - strcpy(strrchr(pointer, '.'), ".0]"); - - return pointer; + return buf; } /** ERROR command - used by servers to indicate errors. @@ -400,29 +424,24 @@ char *get_client_name2(Client *client, int showports) */ CMD_FUNC(cmd_error) { - char *para; + const char *para; if (!MyConnect(client)) return; para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>"; - /* Errors from untrusted sources only go to the junk snomask - * (which is only for debugging issues and such). - * This to prevent flooding and confusing IRCOps by - * malicious users. + /* Errors from untrusted sources are ignored as any + * malicious user can send these, confusing IRCOps etc. + * One can always see the errors from the other side anyway. */ - if (!IsServer(client) && !client->serv) - { - sendto_snomask(SNO_JUNK, "ERROR from server %s: %s", - get_client_name(client, FALSE), para); + if (!IsServer(client) && !client->server) return; - } - sendto_umode_global(UMODE_OPER, "ERROR from server %s: %s", - get_client_name(client, FALSE), para); - ircd_log(LOG_ERROR, "ERROR from server %s: %s", - get_client_name(client, FALSE), para); + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_MESSAGE", client, + "Error from $client: $error_message", + log_data_string("error_message", para), + client->server->conf ? log_data_link_block(client->server->conf) : NULL); } /** Save the tunefile (such as: highest seen connection count) */ @@ -433,11 +452,11 @@ EVENT(save_tunefile) tunefile = fopen(conf_files->tune_file, "w"); if (!tunefile) { -#if !defined(_WIN32) && !defined(_AMIGA) - sendto_ops("Unable to write tunefile.. %s", strerror(errno)); -#else - sendto_ops("Unable to write tunefile.."); -#endif + char *errstr = strerror(errno); + unreal_log(ULOG_WARNING, "config", "WRITE_TUNE_FILE_FAILED", NULL, + "Unable to write tunefile '$filename': $system_error", + log_data_string("filename", conf_files->tune_file), + log_data_string("system_error", errstr)); return; } fprintf(tunefile, "0\n"); @@ -454,14 +473,15 @@ void load_tunefile(void) tunefile = fopen(conf_files->tune_file, "r"); if (!tunefile) return; - fprintf(stderr, "Loading tunefile..\n"); - if (!fgets(buf, sizeof(buf), tunefile)) - fprintf(stderr, "Warning: error while reading the timestamp offset from the tunefile%s%s\n", - errno? ": ": "", errno? strerror(errno): ""); - - if (!fgets(buf, sizeof(buf), tunefile)) - fprintf(stderr, "Warning: error while reading the peak user count from the tunefile%s%s\n", - errno? ": ": "", errno? strerror(errno): ""); + /* We ignore the first line, hence the weird looking double fgets here... */ + if (!fgets(buf, sizeof(buf), tunefile) || !fgets(buf, sizeof(buf), tunefile)) + { + char *errstr = strerror(errno); + unreal_log(ULOG_WARNING, "config", "READ_TUNE_FILE_FAILED", NULL, + "Unable to read tunefile '$filename': $system_error", + log_data_string("filename", conf_files->tune_file), + log_data_string("system_error", errstr)); + } irccounts.me_max = atol(buf); fclose(tunefile); } @@ -501,7 +521,7 @@ extern void reinit_resolver(Client *client); */ CMD_FUNC(cmd_rehash) { - int x = 0; + int x; /* This is one of the (few) commands that cannot be handled * by labeled-response accurately in all circumstances. @@ -521,13 +541,13 @@ CMD_FUNC(cmd_rehash) if (parv[1] && (parv[1][0] == '-')) x = HUNTED_ISME; else - x = hunt_server(client, recv_mtags, ":%s REHASH :%s", 1, parc, parv); + x = hunt_server(client, recv_mtags, "REHASH", 1, parc, parv); } else { if (match_simple("-glob*", parv[1])) /* This is really ugly... hack to make /rehash -global -something work */ { x = HUNTED_ISME; } else { - x = hunt_server(client, NULL, ":%s REHASH %s :%s", 1, parc, parv); + x = hunt_server(client, NULL, "REHASH", 1, parc, parv); } } if (x != HUNTED_ISME) @@ -557,17 +577,14 @@ CMD_FUNC(cmd_rehash) #endif if (parv[2] == NULL) { - if (loop.ircd_rehashing) + if (loop.rehashing) { sendnotice(client, "A rehash is already in progress"); return; } - sendto_umode_global(UMODE_OPER, "%s is remotely rehashing server %s config file", client->name, me.name); + unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", client, "Rehashing server configuration file [by: $client.details]"); remote_rehash_client = client; - reread_motdsandrules(); - // TODO: clean this next line up, wtf man. - rehash(client, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0); - return; + /* fallthrough... so we deal with this the same way as local rehashes */ } parv[1] = parv[2]; } else { @@ -583,16 +600,6 @@ CMD_FUNC(cmd_rehash) parv[1] = parv[2]; parv[2] = NULL; parc--; - /* Only netadmins may use /REHASH -global, which is because: - * a) it makes sense - * b) remote servers don't support remote rehashes by non-netadmins - */ - if (!ValidatePermissionsForPath("server:rehash",client,NULL,NULL,NULL)) - { - sendnumeric(client, ERR_NOPRIVILEGES); - sendnotice(client, "'/REHASH -global' requires you to have server::rehash permissions"); - return; - } if (parv[1] && *parv[1] != '-') { sendnotice(client, "You cannot specify a server name after /REHASH -global, for obvious reasons"); @@ -614,19 +621,12 @@ CMD_FUNC(cmd_rehash) if (!BadPtr(parv[1]) && strcasecmp(parv[1], "-all")) { - - if (!ValidatePermissionsForPath("server:rehash",client,NULL,NULL,NULL)) - { - sendnumeric(client, ERR_NOPRIVILEGES); - return; - } - if (*parv[1] == '-') { if (!strncasecmp("-gar", parv[1], 4)) { loop.do_garbage_collect = 1; - RunHook2(HOOKTYPE_REHASHFLAG, client, parv[1]); + RunHook(HOOKTYPE_REHASHFLAG, client, parv[1]); return; } if (!strncasecmp("-dns", parv[1], 4)) @@ -636,58 +636,27 @@ CMD_FUNC(cmd_rehash) } if (match_simple("-ssl*", parv[1]) || match_simple("-tls*", parv[1])) { - reinit_ssl(client); + unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD_TLS", client, "Reloading all TLS related data. [by: $client.details]"); + reinit_tls(); return; } - if (match_simple("-o*motd", parv[1])) - { - if (MyUser(client)) - sendto_ops("Rehashing OPERMOTD on request of %s", client->name); - else - sendto_umode_global(UMODE_OPER, "Remotely rehashing OPERMOTD on request of %s", client->name); - read_motd(conf_files->opermotd_file, &opermotd); - RunHook2(HOOKTYPE_REHASHFLAG, client, parv[1]); - return; - } - if (match_simple("-b*motd", parv[1])) - { - if (MyUser(client)) - sendto_ops("Rehashing BOTMOTD on request of %s", client->name); - else - sendto_umode_global(UMODE_OPER, "Remotely rehashing BOTMOTD on request of %s", client->name); - read_motd(conf_files->botmotd_file, &botmotd); - RunHook2(HOOKTYPE_REHASHFLAG, client, parv[1]); - return; - } - if (!strncasecmp("-motd", parv[1], 5) || !strncasecmp("-rules", parv[1], 6)) - { - if (MyUser(client)) - sendto_ops("Rehashing all MOTDs and RULES on request of %s", client->name); - else - sendto_umode_global(UMODE_OPER, "Remotely rehasing all MOTDs and RULES on request of %s", client->name); - rehash_motdrules(); - RunHook2(HOOKTYPE_REHASHFLAG, client, parv[1]); - return; - } - RunHook2(HOOKTYPE_REHASHFLAG, client, parv[1]); + RunHook(HOOKTYPE_REHASHFLAG, client, parv[1]); return; } } else { - if (loop.ircd_rehashing) + if (loop.rehashing) { - sendnotice(client, "A rehash is already in progress"); + sendnotice(client, "ERROR: A rehash is already in progress"); return; } - sendto_ops("%s is rehashing server config file", client->name); + unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", client, "Rehashing server configuration file [by: $client.details]"); } /* Normal rehash, rehash motds&rules too, just like the on in the tld block will :p */ sendnumeric(client, RPL_REHASHING, configfile); - // TODO: fix next line - occurence #2 - x = rehash(client, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0); - reread_motdsandrules(); + request_rehash(client); } /** RESTART command - restart the server (discouraged command) @@ -696,7 +665,7 @@ CMD_FUNC(cmd_rehash) */ CMD_FUNC(cmd_restart) { - char *reason = parv[1]; + const char *reason = parv[1]; Client *acptr; if (!MyUser(client)) @@ -731,7 +700,6 @@ CMD_FUNC(cmd_restart) reason = parv[2]; } } - sendto_ops("Server is Restarting by request of %s", client->name); list_for_each_entry(acptr, &lclient_list, lclient_node) { @@ -806,122 +774,11 @@ void short_motd(Client *client) sendnumeric(client, RPL_ENDOFMOTD); } -/* - * A merge from ircu and bahamut, and some extra stuff added by codemastr - * we can now use 1 function for multiple files -- codemastr - * Merged read_motd/read_rules stuff into this -- Syzop - */ - /** Read motd-like file, used for rules/motd/botmotd/opermotd/etc. - * Multiplexes to either directly reading the MOTD or downloading it asynchronously. * @param filename Filename of file to read or URL. NULL is accepted and causes the *motd to be free()d. * @param motd Reference to motd pointer (used for freeing if needed and for asynchronous remote MOTD support) */ void read_motd(const char *filename, MOTDFile *themotd) -{ -#ifdef USE_LIBCURL - time_t modtime; - MOTDDownload *motd_download; -#endif - - /* TODO: if themotd points to a tld's motd, - could a rehash disrupt this pointer?*/ -#ifdef USE_LIBCURL - if(themotd->motd_download) - { - themotd->motd_download->themotd = NULL; - /* - * It is not our job to free() motd_download, the - * read_motd_async_downloaded() function will do that - * when it sees that ->themod == NULL. - */ - themotd->motd_download = NULL; - } - - /* if filename is NULL, do_read_motd will catch it */ - if(filename && url_is_valid(filename)) - { - /* prepare our payload for read_motd_async_downloaded() */ - motd_download = safe_alloc(sizeof(MOTDDownload)); - motd_download->themotd = themotd; - themotd->motd_download = motd_download; - - modtime = unreal_getfilemodtime(unreal_mkcache(filename)); - - download_file_async(filename, modtime, (vFP)read_motd_async_downloaded, motd_download); - return; - } -#endif /* USE_LIBCURL */ - - do_read_motd(filename, themotd); - - return; -} - -#ifdef USE_LIBCURL -/** Callback for download_file_async() called from read_motd() below. - * @param url the URL curl groked or NULL if the MOTD is stored locally. - * @param filename the path to the local copy of the MOTD or NULL if either cached=1 or there's an error. - * @param errorbuf NULL or an errorstring if there was an error while downloading the MOTD. - * @param cached 0 if the URL was downloaded freshly or 1 if the last download was canceled and the local copy should be used. - */ -void read_motd_async_downloaded(const char *url, const char *filename, const char *errorbuf, int cached, MOTDDownload *motd_download) -{ - MOTDFile *themotd; - - themotd = motd_download->themotd; - /* - check if the download was soft-canceled. See struct.h's docs on - struct MOTDDownload for details. - */ - if(!themotd) - { - safe_free(motd_download); - return; - } - - /* errors -- check for specialcached version if applicable */ - if(!cached && !filename) - { - if(has_cached_version(url)) - { - config_warn("Error downloading MOTD file from \"%s\": %s -- using cached version instead.", displayurl(url), errorbuf); - filename = unreal_mkcache(url); - } else { - config_error("Error downloading MOTD file from \"%s\": %s", displayurl(url), errorbuf); - - /* remove reference to this chunk of memory about to be freed. */ - motd_download->themotd->motd_download = NULL; - safe_free(motd_download); - return; - } - } - - /* - * We need to move our newly downloaded file to its cache file - * if it isn't there already. - */ - if(!cached) - { - /* create specialcached version for later */ - unreal_copyfileex(filename, unreal_mkcache(url), 1); - } else { - /* - * The file is cached. Thus we must look for it at the - * cache location where we placed it earlier. - */ - filename = unreal_mkcache(url); - } - - do_read_motd(filename, themotd); - safe_free(motd_download); -} -#endif /* USE_LIBCURL */ - - -/** The actual reading of the MOTD - used by read_motd() and read_motd_async_downloaded() - */ -void do_read_motd(const char *filename, MOTDFile *themotd) { FILE *fd; struct tm *tm_tmp; @@ -934,7 +791,7 @@ void do_read_motd(const char *filename, MOTDFile *themotd) free_motd(themotd); - if(!filename) + if (!filename) return; fd = fopen(filename, "r"); @@ -960,7 +817,7 @@ void do_read_motd(const char *filename, MOTDFile *themotd) temp = safe_alloc(sizeof(MOTDLine)); safe_strdup(temp->line, line); - if(last) + if (last) last->next = temp; else /* handle the special case of the first line */ @@ -969,7 +826,7 @@ void do_read_motd(const char *filename, MOTDFile *themotd) last = temp; } /* the file could be zero bytes long? */ - if(last) + if (last) last->next = NULL; fclose(fd); @@ -986,7 +843,7 @@ void free_motd(MOTDFile *themotd) { MOTDLine *next, *motdline; - if(!themotd) + if (!themotd) return; for (motdline = themotd->lines; motdline; motdline = next) @@ -998,11 +855,6 @@ void free_motd(MOTDFile *themotd) themotd->lines = NULL; memset(&themotd->last_modified, '\0', sizeof(struct tm)); - -#ifdef USE_LIBCURL - /* see struct.h for more information about motd_download */ - themotd->motd_download = NULL; -#endif } /** DIE command - terminate the server @@ -1036,7 +888,8 @@ CMD_FUNC(cmd_die) } /* Let the +s know what is going on */ - sendto_ops("Server Terminating by request of %s", client->name); + unreal_log(ULOG_INFO, "main", "UNREALIRCD_STOP", client, + "Terminating server by request of $client.details"); list_for_each_entry(acptr, &lclient_list, lclient_node) { @@ -1055,23 +908,29 @@ CMD_FUNC(cmd_die) PendingNet *pendingnet = NULL; /** Add server list (network) from 'client' connection */ -void add_pending_net(Client *client, char *str) +void add_pending_net(Client *client, const char *str) { PendingNet *net; PendingServer *srv; char *p, *name; + char buf[512]; if (BadPtr(str) || !client) return; + /* Skip any * at the beginning (indicating a reply), + * and work on a copy. + */ + if (*str == '*') + strlcpy(buf, str+1, sizeof(buf)); + else + strlcpy(buf, str, sizeof(buf)); + /* Allocate */ net = safe_alloc(sizeof(PendingNet)); net->client = client; - /* Fill in */ - if (*str == '*') - str++; - for (name = strtoken(&p, str, ","); name; name = strtoken(&p, NULL, ",")) + for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ",")) { if (!*name) continue; @@ -1108,7 +967,7 @@ void free_pending_net(Client *client) } /** Find SID in any server list (network) that is pending, except 'exempt' */ -PendingNet *find_pending_net_by_sid_butone(char *sid, Client *exempt) +PendingNet *find_pending_net_by_sid_butone(const char *sid, Client *exempt) { PendingNet *net; PendingServer *srv; @@ -1181,7 +1040,7 @@ Client *find_non_pending_net_duplicates(Client *client) } /** Parse CHANMODES= in PROTOCTL */ -void parse_chanmodes_protoctl(Client *client, char *str) +void parse_chanmodes_protoctl(Client *client, const char *str) { char *modes, *p; char copy[256]; @@ -1191,19 +1050,19 @@ void parse_chanmodes_protoctl(Client *client, char *str) modes = strtoken(&p, copy, ","); if (modes) { - safe_strdup(client->serv->features.chanmodes[0], modes); + safe_strdup(client->server->features.chanmodes[0], modes); modes = strtoken(&p, NULL, ","); if (modes) { - safe_strdup(client->serv->features.chanmodes[1], modes); + safe_strdup(client->server->features.chanmodes[1], modes); modes = strtoken(&p, NULL, ","); if (modes) { - safe_strdup(client->serv->features.chanmodes[2], modes); + safe_strdup(client->server->features.chanmodes[2], modes); modes = strtoken(&p, NULL, ","); if (modes) { - safe_strdup(client->serv->features.chanmodes[3], modes); + safe_strdup(client->server->features.chanmodes[3], modes); } } } @@ -1218,9 +1077,9 @@ static int previous_langsinuse_ready = 0; */ void charsys_check_for_changes(void) { - char *langsinuse = charsys_get_current_languages(); + const char *langsinuse = charsys_get_current_languages(); /* already called by charsys_finish() */ - safe_strdup(me.serv->features.nickchars, langsinuse); + safe_strdup(me.server->features.nickchars, langsinuse); if (!previous_langsinuse_ready) { @@ -1231,10 +1090,10 @@ void charsys_check_for_changes(void) if (strcmp(langsinuse, previous_langsinuse)) { - ircd_log(LOG_ERROR, "Permitted nick characters changed at runtime: %s -> %s", - previous_langsinuse, langsinuse); - sendto_realops("Permitted nick characters changed at runtime: %s -> %s", - previous_langsinuse, langsinuse); + unreal_log(ULOG_INFO, "charsys", "NICKCHARS_CHANGED", NULL, + "Permitted nick characters changed at runtime: $old_nickchars -> $new_nickchars", + log_data_string("old_nickchars", previous_langsinuse), + log_data_string("new_nickchars", langsinuse)); /* Broadcast change to all (locally connected) servers */ sendto_server(NULL, 0, 0, NULL, "PROTOCTL NICKCHARS=%s", langsinuse); } @@ -1243,25 +1102,21 @@ void charsys_check_for_changes(void) } /** Check if supplied server name is valid, that is: does not contain forbidden characters etc */ -int valid_server_name(char *name) +int valid_server_name(const char *name) { - char *p; + const char *p; - if (strlen(name) >= HOSTLEN) - return 0; - - for (p = name; *p; p++) - if ((*p <= ' ') || (*p > '~')) - return 0; + if (!valid_host(name, 0)) + return 0; /* invalid hostname */ if (!strchr(name, '.')) - return 0; + return 0; /* no dot */ return 1; } /** Check if the supplied name is a valid SID, as in: syntax. */ -int valid_sid(char *name) +int valid_sid(const char *name) { if (strlen(name) != 3) return 0; @@ -1274,6 +1129,31 @@ int valid_sid(char *name) return 1; } +/** Check if the supplied name is a valid UID, as in: syntax. */ +int valid_uid(const char *name) +{ + const char *p; + + /* Enforce at least some minimum length */ + if (strlen(name) < 6) + return 0; + + /* UID cannot be larger than IDLEN or it would be cut off later */ + if (strlen(name) > IDLEN) + return 0; + + /* Must start with a digit */ + if (!isdigit(*name)) + return 0; + + /* For all the remaining characters: digit or uppercase character */ + for (p = name+1; *p; p++) + if (!isdigit(*p) && !isupper(*p)) + return 0; + + return 1; +} + /** Initialize the TKL subsystem */ void tkl_init(void) { @@ -1284,28 +1164,96 @@ void tkl_init(void) /** Called when a server link is lost. * Used for logging only, API users can use the HOOKTYPE_SERVER_QUIT hook. */ -void lost_server_link(Client *serv, FORMAT_STRING(const char *fmt), ...) +void lost_server_link(Client *client, const char *tls_error_string) { - va_list vl; - static char buf[1024], buf2[512]; - - va_start(vl, fmt); - vsnprintf(buf2, sizeof(buf2), fmt, vl); - va_end(vl); - - if (IsServer(serv)) + if (IsServer(client)) { - /* An already established link is now lost. Broadcast this to all opers. */ - snprintf(buf, sizeof(buf), "Lost server link to %s: %s", - get_client_name(serv, FALSE), buf2); - sendto_umode_global(UMODE_OPER, "%s", buf); + /* An already established link is now lost. */ + // FIXME: we used to broadcast this GLOBALLY.. not anymore since the U6 rewrite.. is that what we want? + if (tls_error_string) + { + /* TLS */ + unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, + "Lost server link to $client [$client.ip]: $tls_error_string", + log_data_string("tls_error_string", tls_error_string), + client->server->conf ? log_data_link_block(client->server->conf) : NULL); + } else { + /* NON-TLS */ + unreal_log(ULOG_ERROR, "link", "LINK_DISCONNECTED", client, + "Lost server link to $client [$client.ip]: $socket_error", + log_data_socket_error(client->local->fd), + client->server->conf ? log_data_link_block(client->server->conf) : NULL); + } } else { - /* A link attempt failed. Only send this to local opers (can be noisy every xx seconds). */ - snprintf(buf, sizeof(buf), "Unable to link with server %s: %s", - get_client_name(serv, FALSE), buf2); - sendto_umode(UMODE_OPER, "%s", buf); + /* A link attempt failed (it was never a fully connected server) */ + /* We send these to local ops only */ + if (tls_error_string) + { + /* TLS */ + if (client->server->conf) + { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, + "Unable to link with server $client [$link_block.ip:$link_block.port]: $tls_error_string", + log_data_string("tls_error_string", tls_error_string), + log_data_link_block(client->server->conf)); + } else { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, + "Unable to link with server $client: $tls_error_string", + log_data_string("tls_error_string", tls_error_string)); + } + } else { + /* non-TLS */ + if (client->server->conf) + { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, + "Unable to link with server $client [$link_block.ip:$link_block.port]: $socket_error", + log_data_socket_error(client->local->fd), + log_data_link_block(client->server->conf)); + } else { + unreal_log(ULOG_ERROR, "link", "LINK_ERROR_CONNECT", client, + "Unable to link with server $client: $socket_error", + log_data_socket_error(client->local->fd)); + } + } + } + SetServerDisconnectLogged(client); +} + +/** Reject an insecure (outgoing) server link that isn't TLS. + * This function is void and not int because it can be called from other void functions + */ +void reject_insecure_server(Client *client) +{ + unreal_log(ULOG_ERROR, "link", "SERVER_STARTTLS_FAILED", client, + "Could not link with server $client with TLS enabled. " + "Please check logs on the other side of the link. " + "If you insist with insecure linking then you can set link::options::outgoing::insecure " + "(NOT recommended!)."); + dead_socket(client, "Rejected server link without TLS"); +} + +/** Start server handshake - called after the outgoing connection has been established. + * @param client The remote server + */ +void start_server_handshake(Client *client) +{ + ConfigItem_link *aconf = client->server ? client->server->conf : NULL; + + if (!aconf) + { + /* Should be impossible. */ + unreal_log(ULOG_ERROR, "link", "BUG_LOST_CONFIGURATION_ON_HANDSHAKE", client, + "Lost configuration while connecting to $client.details"); + return; } - /* Always log! */ - ircd_log(LOG_ERROR, "%s", buf); + RunHook(HOOKTYPE_SERVER_HANDSHAKE_OUT, client); + + sendto_one(client, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); + + send_protoctl_servers(client, 0); + send_proto(client, aconf); + /* Sending SERVER message moved to cmd_protoctl, so it's send after the first PROTOCTL + * that we receive from the remote server. -- Syzop + */ } diff --git a/src/socket.c b/src/socket.c index b256ad3..cb4d508 100644 --- a/src/socket.c +++ b/src/socket.c @@ -40,10 +40,6 @@ char zlinebuf[BUFSIZE]; extern char *version; MODVAR time_t last_allinuse = 0; -#ifdef USE_LIBCURL -extern void url_do_transfers_async(void); -#endif - void start_of_normal_client_handshake(Client *client); void proceed_normal_client_handshake(Client *client, struct hostent *he); @@ -85,91 +81,6 @@ void close_connections(void) #endif } -/** Report an error to the log and also send to all local opers. - * @param text Format string for outputting the error. - * It must contain only two '%s'. The first - * one is replaced by the sockhost from the - * client, and the latter will be the error - * message from strerror(errno). - * @param client The client - ALWAYS locally connected. - */ -void report_error(char *text, Client *client) -{ - int errtmp = ERRNO, origerr = ERRNO; - char *host, xbuf[256]; - int err, len = sizeof(err), n; - - host = (client) ? get_client_name(client, FALSE) : ""; - - Debug((DEBUG_ERROR, text, host, STRERROR(errtmp))); - - /* - * Get the *real* error from the socket (well try to anyway..). - * This may only work when SO_DEBUG is enabled but its worth the - * gamble anyway. - */ -#ifdef SO_ERROR - if (client && !IsMe(client) && client->local->fd >= 0) - if (!getsockopt(client->local->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &len)) - if (err) - errtmp = err; -#endif - if (origerr != errtmp) { - /* Socket error is different than original error, - * some tricks are needed because of 2x strerror() (or at least - * according to the man page) -- Syzop. - */ - snprintf(xbuf, 200, "[syserr='%s'", STRERROR(origerr)); - n = strlen(xbuf); - snprintf(xbuf+n, 256-n, ", sockerr='%s']", STRERROR(errtmp)); - sendto_snomask(SNO_JUNK, text, host, xbuf); - ircd_log(LOG_ERROR, text, host, xbuf); - } else { - sendto_snomask(SNO_JUNK, text, host, STRERROR(errtmp)); - ircd_log(LOG_ERROR, text,host,STRERROR(errtmp)); - } - return; -} - -/** Report a BAD error to the log and also send to all local opers. - * TODO: Document the difference between report_error() and report_baderror() - * @param text Format string for outputting the error. - * It must contain only two '%s'. The first - * one is replaced by the sockhost from the - * client, and the latter will be the error - * message from strerror(errno). - * @param client The client - ALWAYS locally connected. - */ -void report_baderror(char *text, Client *client) -{ -#ifndef _WIN32 - int errtmp = errno; /* debug may change 'errno' */ -#else - int errtmp = WSAGetLastError(); /* debug may change 'errno' */ -#endif - char *host; - int err, len = sizeof(err); - - host = (client) ? get_client_name(client, FALSE) : ""; - - Debug((DEBUG_ERROR, text, host, STRERROR(errtmp))); - - /* - * Get the *real* error from the socket (well try to anyway..). - * This may only work when SO_DEBUG is enabled but its worth the - * gamble anyway. - */ -#ifdef SO_ERROR - if (client && !IsMe(client) && client->local->fd >= 0) - if (!getsockopt(client->local->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &len)) - if (err) - errtmp = err; -#endif - sendto_umode(UMODE_OPER, text, host, STRERROR(errtmp)); - ircd_log(LOG_ERROR, text, host, STRERROR(errtmp)); - return; -} - /** Accept an incoming connection. * @param listener_fd The file descriptor of a listen() socket. * @param data The listen { } block configuration data. @@ -190,8 +101,10 @@ 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. */ - report_baderror("Cannot accept connections %s:%s", NULL); - sendto_realops("[BUG] Restarting listener on %s:%d due to fatal errors (see previous message)", listener->ip, listener->port); + 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(); } @@ -207,7 +120,9 @@ static void listener_accept(int listener_fd, int revents, void *data) ircstats.is_ref++; if (last_allinuse < TStime() - 15) { - sendto_ops_and_log("All connections in use. ([@%s/%u])", listener->ip, listener->port); + 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(); } @@ -252,13 +167,20 @@ int unreal_listen(ConfigItem_listen *listener, char *ip, int port, int ipv6) listener->fd = fd_socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0, "Listener socket"); if (listener->fd < 0) { - report_baderror("Cannot open stream socket() %s:%s", NULL); + unreal_log(ULOG_FATAL, "listen", "LISTEN_SOCKET_ERROR", NULL, + "Could not listen on IP \"$listen_ip\" on port $listen_port: $socket_error", + log_data_socket_error(-1), + log_data_string("listen_ip", ip), + log_data_integer("listen_port", port)); return -1; } if (++OpenFiles >= maxclients) { - sendto_ops_and_log("No more connections allowed (%s)", listener->ip); + unreal_log(ULOG_FATAL, "listen", "LISTEN_ERROR_MAXCLIENTS", NULL, + "Could not listen on IP \"$listen_ip\" on port $listen_port: all connections in use", + log_data_string("listen_ip", ip), + log_data_integer("listen_port", port)); fd_close(listener->fd); listener->fd = -1; --OpenFiles; @@ -269,10 +191,11 @@ int unreal_listen(ConfigItem_listen *listener, char *ip, int port, int ipv6) if (!unreal_bind(listener->fd, ip, port, ipv6)) { - char buf[512]; - ircsnprintf(buf, sizeof(buf), "Error binding stream socket to IP %s port %d", ip, port); - strlcat(buf, " - %s:%s", sizeof(buf)); - report_baderror(buf, NULL); + unreal_log(ULOG_FATAL, "listen", "LISTEN_BIND_ERROR", NULL, + "Could not listen on IP \"$listen_ip\" on port $listen_port: $socket_error", + log_data_socket_error(listener->fd), + log_data_string("listen_ip", ip), + log_data_integer("listen_port", port)); fd_close(listener->fd); listener->fd = -1; --OpenFiles; @@ -281,7 +204,11 @@ int unreal_listen(ConfigItem_listen *listener, char *ip, int port, int ipv6) if (listen(listener->fd, LISTEN_SIZE) < 0) { - report_error("listen failed for %s:%s", NULL); + unreal_log(ULOG_FATAL, "listen", "LISTEN_LISTEN_ERROR", NULL, + "Could not listen on IP \"$listen_ip\" on port $listen_port: $socket_error", + log_data_socket_error(listener->fd), + log_data_string("listen_ip", ip), + log_data_integer("listen_port", port)); fd_close(listener->fd); listener->fd = -1; --OpenFiles; @@ -344,17 +271,17 @@ void close_listener(ConfigItem_listen *listener) { if (listener->fd >= 0) { - ircd_log(LOG_ERROR, "IRCd no longer listening on %s:%d (%s)%s", - listener->ip, listener->port, - listener->ipv6 ? "IPv6" : "IPv4", - listener->options & LISTENER_TLS ? " (SSL/TLS)" : ""); + unreal_log(ULOG_INFO, "listen", "LISTEN_REMOVED", NULL, + "UnrealIRCd is now no longer listening on $listen_ip:$listen_port", + log_data_string("listen_ip", listener->ip), + log_data_integer("listen_port", listener->port)); fd_close(listener->fd); --OpenFiles; } listener->options &= ~LISTENER_BOUND; listener->fd = -1; - /* We can already free the SSL/TLS context, since it is only + /* We can already free the TLS context, since it is only * used for new connections, which we no longer accept. */ if (listener->ssl_ctx) @@ -498,63 +425,6 @@ void close_std_descriptors(void) #endif } -/** Write PID file */ -void write_pidfile(void) -{ -#ifdef IRCD_PIDFILE - int fd; - char buff[20]; - if ((fd = open(conf_files->pid_file, O_CREAT | O_WRONLY, 0600)) < 0) - { - ircd_log(LOG_ERROR, "Error writing to pid file %s: %s", conf_files->pid_file, strerror(ERRNO)); - return; - } - ircsnprintf(buff, sizeof(buff), "%5d\n", (int)getpid()); - if (write(fd, buff, strlen(buff)) < 0) - ircd_log(LOG_ERROR, "Error writing to pid file %s: %s", conf_files->pid_file, strerror(ERRNO)); - if (close(fd) < 0) - ircd_log(LOG_ERROR, "Error writing to pid file %s: %s", conf_files->pid_file, strerror(ERRNO)); -#endif -} - -/** Reject an insecure (outgoing) server link that isn't SSL/TLS. - * This function is void and not int because it can be called from other void functions - */ -void reject_insecure_server(Client *client) -{ - sendto_umode(UMODE_OPER, "Could not link with server %s with SSL/TLS enabled. " - "Please check logs on the other side of the link. " - "If you insist with insecure linking then you can set link::options::outgoing::insecure " - "(NOT recommended!).", - client->name); - dead_socket(client, "Rejected link without SSL/TLS"); -} - -/** Start server handshake - called after the outgoing connection has been established. - * @param client The remote server - */ -void start_server_handshake(Client *client) -{ - ConfigItem_link *aconf = client->serv ? client->serv->conf : NULL; - - if (!aconf) - { - /* Should be impossible. */ - sendto_ops_and_log("Lost configuration for %s in start_server_handshake()", get_client_name(client, FALSE)); - return; - } - - RunHook(HOOKTYPE_SERVER_HANDSHAKE_OUT, client); - - sendto_one(client, NULL, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); - - send_protoctl_servers(client, 0); - send_proto(client, aconf); - /* Sending SERVER message moved to cmd_protoctl, so it's send after the first PROTOCTL - * that we receive from the remote server. -- Syzop - */ -} - /** Do an ident lookup if necessary. * @param client The incoming client */ @@ -563,7 +433,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->serv && IsHandshake(client))) + if ((IDENT_CHECK == 0) || (client->server && IsHandshake(client))) { ClearIdentLookupSent(client); ClearIdentLookup(client); @@ -579,11 +449,11 @@ void consider_ident_lookup(Client *client) void completed_connection(int fd, int revents, void *data) { Client *client = data; - ConfigItem_link *aconf = client->serv ? client->serv->conf : NULL; + ConfigItem_link *aconf = client->server ? client->server->conf : NULL; if (IsHandshake(client)) { - /* Due to delayed ircd_SSL_connect call */ + /* Due to delayed unreal_tls_connect call */ start_server_handshake(client); fd_setselect(fd, FD_SELECT_READ, read_packet, client); return; @@ -593,7 +463,8 @@ void completed_connection(int fd, int revents, void *data) if (!aconf) { - sendto_ops_and_log("Lost configuration for %s", get_client_name(client, FALSE)); + unreal_log(ULOG_ERROR, "link", "BUG_LOST_CONFIGURATION_ON_CONNECT", client, + "Lost configuration while connecting to $client.details"); return; } @@ -623,40 +494,12 @@ void close_connection(Client *client) if (IsServer(client)) { ircstats.is_sv++; - ircstats.is_sbs += client->local->sendB; - ircstats.is_sbr += client->local->receiveB; - ircstats.is_sks += client->local->sendK; - ircstats.is_skr += client->local->receiveK; - ircstats.is_sti += TStime() - client->local->firsttime; - if (ircstats.is_sbs > 1023) - { - ircstats.is_sks += (ircstats.is_sbs >> 10); - ircstats.is_sbs &= 0x3ff; - } - if (ircstats.is_sbr > 1023) - { - ircstats.is_skr += (ircstats.is_sbr >> 10); - ircstats.is_sbr &= 0x3ff; - } + ircstats.is_sti += TStime() - client->local->creationtime; } else if (IsUser(client)) { ircstats.is_cl++; - ircstats.is_cbs += client->local->sendB; - ircstats.is_cbr += client->local->receiveB; - ircstats.is_cks += client->local->sendK; - ircstats.is_ckr += client->local->receiveK; - ircstats.is_cti += TStime() - client->local->firsttime; - if (ircstats.is_cbs > 1023) - { - ircstats.is_cks += (ircstats.is_cbs >> 10); - ircstats.is_cbs &= 0x3ff; - } - if (ircstats.is_cbr > 1023) - { - ircstats.is_ckr += (ircstats.is_cbr >> 10); - ircstats.is_cbr &= 0x3ff; - } + ircstats.is_cti += TStime() - client->local->creationtime; } else ircstats.is_ni++; @@ -727,13 +570,21 @@ void set_sock_opts(int fd, Client *client, int ipv6) #ifdef SO_REUSEADDR opt = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)) < 0) - report_error("setsockopt(SO_REUSEADDR) %s:%s", client); + { + 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) - report_error("setsockopt(SO_USELOOPBACK) %s:%s", client); + { + 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 @@ -747,14 +598,18 @@ void set_sock_opts(int fd, Client *client, int ipv6) { if (client) { - report_error("fcntl(fd, F_GETFL) failed for %s:%s", client); + unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client, + "Could not get socket options (F_GETFL): $socket_error", + log_data_socket_error(-1)); } } else if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) { if (client) { - report_error("fcntl(fd, F_SETL, nonb) failed for %s:%s", client); + unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client, + "Could not get socket options (F_SETFL): $socket_error", + log_data_socket_error(-1)); } } #else @@ -763,7 +618,9 @@ void set_sock_opts(int fd, Client *client, int ipv6) { if (client) { - report_error("ioctlsocket(fd,FIONBIO) failed for %s:%s", client); + unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client, + "Could not ioctlsocket FIONBIO: $socket_error", + log_data_socket_error(-1)); } } #endif @@ -796,7 +653,7 @@ int is_loopback_ip(char *ip) * @param port Remote port (will be written) * @returns The IP address */ -char *getpeerip(Client *client, int fd, int *port) +const char *getpeerip(Client *client, int fd, int *port) { static char ret[HOSTLEN+1]; @@ -859,7 +716,7 @@ static int check_too_many_unknown_connections(Client *client) Client *add_connection(ConfigItem_listen *listener, int fd) { Client *client; - char *ip; + const char *ip; int port = 0; client = make_client(NULL, &me); @@ -877,7 +734,11 @@ Client *add_connection(ConfigItem_listen *listener, int fd) */ if (ERRNO != P_ENOTCONN) { - report_error("Failed to accept new client %s :%s", client); + unreal_log(ULOG_ERROR, "listen", "ACCEPT_ERROR", NULL, + "Failed to accept new client: unable to get IP address: $socket_error", + log_data_socket_error(fd), + log_data_string("listen_ip", listener->ip), + log_data_integer("listen_port", listener->port)); } refuse_client: ircstats.is_ref++; @@ -932,7 +793,6 @@ refuse_client: if (ctx) { SetTLSAcceptHandshake(client); - Debug((DEBUG_DEBUG, "Starting TLS accept handshake for %s", client->local->sockhost)); if ((client->local->ssl = SSL_new(ctx)) == NULL) { goto refuse_client; @@ -940,10 +800,9 @@ refuse_client: SetTLS(client); SSL_set_fd(client->local->ssl, fd); SSL_set_nonblocking(client->local->ssl); - SSL_set_ex_data(client->local->ssl, ssl_client_index, client); - if (!ircd_SSL_accept(client, fd)) + SSL_set_ex_data(client->local->ssl, tls_client_index, client); + if (!unreal_tls_accept(client, fd)) { - Debug((DEBUG_DEBUG, "Failed TLS accept handshake in instance 1: %s", client->local->sockhost)); SSL_set_shutdown(client->local->ssl, SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(client->local->ssl); SSL_free(client->local->ssl); @@ -956,12 +815,10 @@ refuse_client: return client; } -static int dns_special_flag = 0; /* This is for an "interesting" race condition very ugly. */ - /** Start of normal client handshake - DNS and ident lookups, etc. * @param client The client * @note This is called directly after accept() -> add_connection() for plaintext. - * For SSL/TLS connections this is called after the SSL/TLS handshake is completed. + * For TLS connections this is called after the TLS handshake is completed. */ void start_of_normal_client_handshake(Client *client) { @@ -975,9 +832,7 @@ void start_of_normal_client_handshake(Client *client) { if (should_show_connect_info(client)) sendto_one(client, NULL, ":%s %s", me.name, REPORT_DO_DNS); - dns_special_flag = 1; he = unrealdns_doclient(client); - dns_special_flag = 0; if (client->local->hostp) goto doauth; /* Race condition detected, DNS has been done, continue with auth */ @@ -1040,7 +895,7 @@ void read_packet(int fd, int revents, void *data) fd_setselect(fd, FD_SELECT_READ, read_packet, client); /* Restore handling of writes towards send_queued_cb(), since * it may be overwritten in an earlier call to read_packet(), - * to handle (SSL) writes by read_packet(), see below under + * to handle (TLS) writes by read_packet(), see below under * SSL_ERROR_WANT_WRITE. */ fd_setselect(fd, FD_SELECT_WRITE, send_queued_cb, client); @@ -1090,16 +945,16 @@ void read_packet(int fd, int revents, void *data) if (length < 0 && ((ERRNO == P_EWOULDBLOCK) || (ERRNO == P_EAGAIN) || (ERRNO == P_EINTR))) return; - if (IsServer(client) || client->serv) /* server or outgoing connection */ - lost_server_link(client, "Read error or connection closed."); + if (IsServer(client) || client->server) /* server or outgoing connection */ + lost_server_link(client, NULL); - exit_client(client, NULL, "Read error"); + exit_client(client, NULL, ERRNO ? "Read error" : "Connection closed"); return; } - client->local->lasttime = now; - if (client->local->lasttime > client->local->since) - client->local->since = client->local->lasttime; + client->local->last_msg_received = now; + if (client->local->last_msg_received > client->local->fake_lag) + client->local->fake_lag = client->local->last_msg_received; /* FIXME: Is this correct? I have my doubts. */ ClearPingSent(client); @@ -1174,191 +1029,28 @@ void process_clients(void) } while(&client->lclient_node != &unknown_list); } -/** Returns 4 if 'str' is a valid IPv4 address - * and 6 if 'str' is a valid IPv6 IP address. - * Zero (0) is returned in any other case (eg: hostname). +/** Check if 'ip' is a valid IP address, and if so what type. + * @param ip The IP address + * @retval 4 Valid IPv4 address + * @retval 6 Valid IPv6 address + * @retval 0 Invalid IP address (eg: a hostname) */ -int is_valid_ip(char *str) +int is_valid_ip(const char *ip) { char scratch[64]; - if (inet_pton(AF_INET, str, scratch) == 1) + if (BadPtr(ip)) + return 0; + + if (inet_pton(AF_INET, ip, scratch) == 1) return 4; /* IPv4 */ - if (inet_pton(AF_INET6, str, scratch) == 1) + if (inet_pton(AF_INET6, ip, scratch) == 1) return 6; /* IPv6 */ return 0; /* not an IP address */ } -static int connect_server_helper(ConfigItem_link *, Client *); - -/** Start an outgoing connection to a server, for server linking. - * @param aconf Configuration attached to this server - * @param by The user initiating the connection (can be NULL) - * @param hp The address to connect to. - * @returns <0 on error, 0 on success. Rather confusing. - */ -int connect_server(ConfigItem_link *aconf, Client *by, struct hostent *hp) -{ - Client *client; - -#ifdef DEBUGMODE - sendto_realops("connect_server() called with aconf %p, refcount: %d, TEMP: %s", - aconf, aconf->refcount, aconf->flag.temporary ? "YES" : "NO"); -#endif - - if (!aconf->outgoing.hostname) - return -1; /* This is an incoming-only link block. Caller shouldn't call us. */ - - if (!hp) - { - /* Remove "cache" */ - safe_free(aconf->connect_ip); - } - /* - * If we dont know the IP# for this host and itis a hostname and - * not a ip# string, then try and find the appropriate host record. - */ - if (!aconf->connect_ip) - { - if (is_valid_ip(aconf->outgoing.hostname)) - { - /* link::outgoing::hostname is an IP address. No need to resolve host. */ - safe_strdup(aconf->connect_ip, aconf->outgoing.hostname); - } else - { - /* It's a hostname, let the resolver look it up. */ - int ipv4_explicit_bind = 0; - - if (aconf->outgoing.bind_ip && (is_valid_ip(aconf->outgoing.bind_ip) == 4)) - ipv4_explicit_bind = 1; - - /* We need this 'aconf->refcount++' or else there's a race condition between - * starting resolving the host and the result of the resolver (we could - * REHASH in that timeframe) leading to an invalid (freed!) 'aconf'. - * -- Syzop, bug #0003689. - */ - aconf->refcount++; - unrealdns_gethostbyname_link(aconf->outgoing.hostname, aconf, ipv4_explicit_bind); - return -2; - } - } - client = make_client(NULL, &me); - client->local->hostp = hp; - /* - * Copy these in so we have something for error detection. - */ - strlcpy(client->name, aconf->servername, sizeof(client->name)); - strlcpy(client->local->sockhost, aconf->outgoing.hostname, HOSTLEN + 1); - - if (!connect_server_helper(aconf, client)) - { - int errtmp = ERRNO; - report_error("Connect to host %s failed: %s", client); - if (by && IsUser(by) && !MyUser(by)) - sendnotice(by, "*** Connect to host %s failed.", client->name); - fd_close(client->local->fd); - --OpenFiles; - client->local->fd = -2; - free_client(client); - SET_ERRNO(errtmp); - if (ERRNO == P_EINTR) - SET_ERRNO(P_ETIMEDOUT); - return -1; - } - /* The socket has been connected or connect is in progress. */ - make_server(client); - client->serv->conf = aconf; - client->serv->conf->refcount++; -#ifdef DEBUGMODE - sendto_realops("connect_server() CONTINUED (%s:%d), aconf %p, refcount: %d, TEMP: %s", - __FILE__, __LINE__, aconf, aconf->refcount, aconf->flag.temporary ? "YES" : "NO"); -#endif - Debug((DEBUG_ERROR, "reference count for %s (%s) is now %d", - client->name, client->serv->conf->servername, client->serv->conf->refcount)); - if (by && IsUser(by)) - strlcpy(client->serv->by, by->name, sizeof(client->serv->by)); - else - strlcpy(client->serv->by, "AutoConn.", sizeof client->serv->by); - client->serv->up = me.name; - SetConnecting(client); - SetOutgoing(client); - irccounts.unknown++; - list_add(&client->lclient_node, &unknown_list); - set_sockhost(client, aconf->outgoing.hostname); - add_client_to_list(client); - - if (aconf->outgoing.options & CONNECT_TLS) - { - SetTLSConnectHandshake(client); - fd_setselect(client->local->fd, FD_SELECT_WRITE, ircd_SSL_client_handshake, client); - } - else - fd_setselect(client->local->fd, FD_SELECT_WRITE, completed_connection, client); - - return 0; -} - -/** Helper function for connect_server() to prepare the actual bind()'ing and connect(). - * @param aconf Configuration entry of the server. - * @param client The client entry that we will use and fill in. - * @returns 1 on success, 0 on failure. - */ -static int connect_server_helper(ConfigItem_link *aconf, Client *client) -{ - char *bindip; - char buf[BUFSIZE]; - - if (!aconf->connect_ip) - return 0; /* handled upstream or shouldn't happen */ - - if (strchr(aconf->connect_ip, ':')) - SetIPV6(client); - - safe_strdup(client->ip, aconf->connect_ip); - - snprintf(buf, sizeof buf, "Outgoing connection: %s", get_client_name(client, TRUE)); - client->local->fd = fd_socket(IsIPV6(client) ? AF_INET6 : AF_INET, SOCK_STREAM, 0, buf); - if (client->local->fd < 0) - { - if (ERRNO == P_EMFILE) - { - sendto_realops("opening stream socket to server %s: No more sockets", - get_client_name(client, TRUE)); - return 0; - } - report_baderror("opening stream socket to server %s:%s", client); - return 0; - } - if (++OpenFiles >= maxclients) - { - sendto_ops_and_log("No more connections allowed (%s)", client->name); - return 0; - } - - set_sockhost(client, aconf->outgoing.hostname); - - if (!aconf->outgoing.bind_ip && iConf.link_bindip) - bindip = iConf.link_bindip; - else - bindip = aconf->outgoing.bind_ip; - - if (bindip && strcmp("*", bindip)) - { - if (!unreal_bind(client->local->fd, bindip, 0, IsIPV6(client))) - { - report_baderror("Error binding to local port for %s:%s -- " - "Your link::outgoing::bind-ip is probably incorrect.", client); - return 0; - } - } - - set_sock_opts(client->local->fd, client, IsIPV6(client)); - - return unreal_connect(client->local->fd, client->ip, aconf->outgoing.port, IsIPV6(client)); -} - /** Checks if the system is IPv6 capable. * IPv6 is always available at compile time (libs, headers), but the OS may * not have IPv6 enabled (or ipv6 kernel module not loaded). So we better check.. @@ -1375,11 +1067,11 @@ int ipv6_capable(void) /** Attempt to deliver data to a client. * This function is only called from send_queued() and will deal - * with sending to the SSL/TLS or plaintext connection. + * with sending to the TLS or plaintext connection. * @param cptr The client * @param str The string to send * @param len The length of the string - * @param want_read In case of SSL/TLS it may happen that SSL_write() + * @param want_read In case of TLS it may happen that SSL_write() * needs to READ data. If this happens then this * function will set *want_read to 1. * The upper layer should then call us again when @@ -1399,16 +1091,10 @@ int deliver_it(Client *client, char *str, int len, int *want_read) *want_read = 0; - if (IsDeadSocket(client) || (!IsServer(client) && !IsUser(client) - && !IsHandshake(client) - && !IsTLSHandshake(client) - - && !IsUnknown(client))) + if (IsDeadSocket(client) || + (!IsServer(client) && !IsUser(client) && !IsHandshake(client) && + !IsTLSHandshake(client) && !IsUnknown(client))) { - str[len] = '\0'; - sendto_ops - ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s", - client->name, client->status, IsDeadSocket(client) ? "DEAD" : "", str); return -1; } @@ -1459,25 +1145,15 @@ int deliver_it(Client *client, char *str, int len, int *want_read) if (retval > 0) { - client->local->sendB += retval; - me.local->sendB += retval; - if (client->local->sendB > 1023) - { - client->local->sendK += (client->local->sendB >> 10); - client->local->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ - } - if (me.local->sendB > 1023) - { - me.local->sendK += (me.local->sendB >> 10); - me.local->sendB &= 0x03ff; - } + client->local->traffic.bytes_sent += retval; + me.local->traffic.bytes_sent += retval; } return (retval); } /** Initiate an outgoing connection, the actual connect() call. */ -int unreal_connect(int fd, char *ip, int port, int ipv6) +int unreal_connect(int fd, const char *ip, int port, int ipv6) { int n; @@ -1513,7 +1189,7 @@ int unreal_connect(int fd, char *ip, int port, int ipv6) /** Bind to an IP/port (port may be 0 for auto). * @returns 0 on failure, other on success. */ -int unreal_bind(int fd, char *ip, int port, int ipv6) +int unreal_bind(int fd, const char *ip, int port, int ipv6) { if (ipv6) { diff --git a/src/support.c b/src/support.c index 6207d93..cf88e94 100644 --- a/src/support.c +++ b/src/support.c @@ -34,7 +34,7 @@ extern void outofmemory(); #define is_enabled match /** Convert integer to string */ -char *my_itoa(int i) +const char *my_itoa(int i) { static char buf[128]; ircsnprintf(buf, sizeof(buf), "%d", i); @@ -50,9 +50,7 @@ char *my_itoa(int i) * @section Ex1 Example * @code * for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ",")) - * { - * ircd_log(LOG_ERROR, "Got: %s", name); - * } + * unreal_log(ULOG_INFO, "test", "TEST", "Got: $name", log_data_string(name)); * @endcode */ char *strtoken(char **save, char *str, char *fs) @@ -92,7 +90,7 @@ char *strtoken(char **save, char *str, char *fs) * @returns IP address as a string (IPv4 or IPv6, in case of the latter: * always the uncompressed form without ::) */ -char *inetntop(int af, const void *in, char *out, size_t size) +const char *inetntop(int af, const void *in, char *out, size_t size) { char tmp[MYDUMMY_SIZE]; @@ -140,7 +138,6 @@ char *inetntop(int af, const void *in, char *out, size_t size) if (*(op - 1) == ':') *op++ = '0'; *op = '\0'; - Debug((DEBUG_DNS, "Expanding `%s' -> `%s'", tmp, out)); } return out; } @@ -180,6 +177,27 @@ size_t strlcpy(char *dst, const char *src, size_t size) } #endif +#ifndef HAVE_STRLNCPY +/** BSD'ish strlncpy() - similar to strlcpy but never copies more then n characters. + */ +size_t strlncpy(char *dst, const char *src, size_t size, size_t n) +{ + size_t len = strlen(src); + size_t ret = len; + + if (size <= 0) + return 0; + if (len > n) + len = n; + if (len >= size) + len = size - 1; + memcpy(dst, src, len); + dst[len] = 0; + + return ret; +} +#endif + #ifndef HAVE_STRLCAT /* BSD'ish strlcat(). * The strlcat() function appends the NUL-terminated string src to the end of @@ -233,6 +251,16 @@ size_t strlncat(char *dst, const char *src, size_t size, size_t n) } #endif +/** Like strlcpy but concat one letter */ +void strlcat_letter(char *buf, char c, size_t buflen) +{ + int n = strlen(buf); + if (!buflen || (n >= buflen-1)) + return; + buf[n] = c; + buf[n+1] = '\0'; +} + /** Copies a string and ensure the new buffer is at most 'max' size, including NUL. * The syntax is pretty much identical to strlcpy() except that * the buffer is newly allocated. @@ -715,11 +743,11 @@ void outofmemory(size_t bytes) if (log_attempt) { + /* This will probably fail, but we can try... */ + unreal_log(ULOG_ERROR, "main", "OUT_OF_MEMORY", NULL, + "Out of memory while trying to allocate $bytes bytes!", + log_data_integer("bytes", bytes)); log_attempt = 0; - if (bytes) - ircd_log(LOG_ERROR, "Out of memory while trying to allocate %lld bytes!", (long long)bytes); - else - ircd_log(LOG_ERROR, "Out of memory"); } exit(7); } @@ -778,9 +806,9 @@ char *unreal_mktemp(const char *dir, const char *suffix) /** Returns the path portion of the given path/file * in the specified location (must be at least PATH_MAX bytes). */ -char *unreal_getpathname(char *filepath, char *path) +char *unreal_getpathname(const char *filepath, char *path) { - char *end = filepath+strlen(filepath); + const char *end = filepath+strlen(filepath); while (*end != '\\' && *end != '/' && end > filepath) end--; @@ -803,31 +831,35 @@ char *unreal_getpathname(char *filepath, char *path) /** Returns the filename portion of the given path. * The original string is not modified */ -char *unreal_getfilename(char *path) +const char *unreal_getfilename(const char *path) { - int len = strlen(path); - char *end; - if (!len) - return NULL; - end = path+len-1; + int len = strlen(path); + const char *end; + + if (!len) + return NULL; + + end = path+len-1; if (*end == '\\' || *end == '/') return NULL; - while (end > path) - { - if (*end == '\\' || *end == '/') - { - end++; - break; - } - end--; - } - return end; + + while (end > path) + { + if (*end == '\\' || *end == '/') + { + end++; + break; + } + end--; + } + + return end; } /** Returns the special module tmp name for a given path. * The original string is not modified. */ -char *unreal_getmodfilename(char *path) +const char *unreal_getmodfilename(const char *path) { static char ret[512]; char buf[512]; @@ -874,12 +906,12 @@ char *unreal_getmodfilename(char *path) /* Returns a consistent filename for the cache/ directory. * Returned value will be like: cache/ */ -char *unreal_mkcache(const char *url) +const char *unreal_mkcache(const char *url) { static char tempbuf[PATH_MAX+1]; - char tmp2[33]; + char tmp2[128]; - snprintf(tempbuf, PATH_MAX, "%s/%s", CACHEDIR, md5hash(tmp2, url, strlen(url))); + snprintf(tempbuf, PATH_MAX, "%s/%s", CACHEDIR, sha256hash(tmp2, url, strlen(url))); return tempbuf; } @@ -892,9 +924,9 @@ int has_cached_version(const char *url) /** Used to blow away result of bad copy or cancel file copy */ void cancel_copy(int srcfd, int destfd, const char *dest) { - close(srcfd); - close(destfd); - unlink(dest); + close(srcfd); + close(destfd); + unlink(dest); } /** Copys the contents of the src file to the dest file. @@ -1034,7 +1066,7 @@ time_t unreal_getfilemodtime(const char *filename) #endif /** Encode an IP string (eg: "1.2.3.4") to a BASE64 encoded value for S2S traffic */ -char *encode_ip(char *ip) +const char *encode_ip(const char *ip) { static char retbuf[25]; /* returned string */ char addrbuf[16]; @@ -1067,7 +1099,7 @@ char *encode_ip(char *ip) } /** Decode a BASE64 encoded string to an IP address string. Used for S2S traffic. */ -char *decode_ip(char *buf) +const char *decode_ip(const char *buf) { int n; char targ[25]; @@ -1161,7 +1193,7 @@ struct u_WSA_errors WSAErrors[] = { }; /** Get socket error string */ -char *sock_strerror(int error) +const char *sock_strerror(int error) { static char unkerr[64]; int start = 0; @@ -1277,7 +1309,7 @@ literal: } /** Return the PCRE2 library version in use */ -char *pcre2_version(void) +const char *pcre2_version(void) { static char buf[256]; @@ -1322,8 +1354,13 @@ int get_terminal_width(void) #endif } +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + /** Like strftime() but easier. */ -char *unreal_strftime(char *str) +char *unreal_strftime(const char *str) { time_t t; struct tm *tmp; @@ -1332,12 +1369,20 @@ char *unreal_strftime(char *str) t = time(NULL); tmp = localtime(&t); if (!tmp || !strftime(buf, sizeof(buf), str, tmp)) - return str; + { + /* If anything fails bigtime, then return the format string */ + strlcpy(buf, str, sizeof(buf)); + return buf; + } return buf; } -/** Convert a string to lowercase */ -void strtolower_safe(char *dst, char *src, int size) +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +/** Convert a string to lowercase - with separate input/output buffer */ +void strtolower_safe(char *dst, const char *src, int size) { if (!size) return; /* size of 0 is unworkable */ @@ -1350,3 +1395,32 @@ void strtolower_safe(char *dst, char *src, int size) } *dst = '\0'; } + +/** Convert a string to lowercase - modifying existing string */ +void strtolower(char *str) +{ + for (; *str; str++) + *str = tolower(*str); +} + +/** Convert a string to uppercase - with separate input/output buffer */ +void strtoupper_safe(char *dst, const char *src, int size) +{ + if (!size) + return; /* size of 0 is unworkable */ + size--; /* for \0 */ + + for (; *src && size; src++) + { + *dst++ = toupper(*src); + size--; + } + *dst = '\0'; +} + +/** Convert a string to uppercase - modifying existing string */ +void strtoupper(char *str) +{ + for (; *str; str++) + *str = toupper(*str); +} diff --git a/src/tls.c b/src/tls.c index 961e1a2..c84ef02 100644 --- a/src/tls.c +++ b/src/tls.c @@ -19,7 +19,7 @@ */ /** @file - * @brief SSL/TLS functions + * @brief TLS functions */ #include "unrealircd.h" @@ -31,33 +31,32 @@ extern HINSTANCE hInst; extern HWND hwIRCDWnd; #endif -#define SAFE_SSL_READ 1 -#define SAFE_SSL_WRITE 2 -#define SAFE_SSL_ACCEPT 3 -#define SAFE_SSL_CONNECT 4 +#define FUNC_TLS_READ 1 +#define FUNC_TLS_WRITE 2 +#define FUNC_TLS_ACCEPT 3 +#define FUNC_TLS_CONNECT 4 /* Forward declarations */ -static int fatal_ssl_error(int ssl_error, int where, int my_errno, Client *client); +static int fatal_tls_error(int ssl_error, int where, int my_errno, Client *client); int cipher_check(SSL_CTX *ctx, char **errstr); int certificate_quality_check(SSL_CTX *ctx, char **errstr); -/* The SSL structures */ +/* The TLS structures */ SSL_CTX *ctx_server; SSL_CTX *ctx_client; -char *SSLKeyPasswd; +char *TLSKeyPasswd; typedef struct { int *size; char **buffer; } StreamIO; -MODVAR int ssl_client_index = 0; +MODVAR int tls_client_index = 0; -#define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); } #ifdef _WIN32 -/** Ask SSL private key password (Windows GUI mode only) */ -LRESULT SSLPassDLG(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam) +/** Ask private key password (Windows GUI mode only) */ +LRESULT TLS_key_passwd_dialog(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam) { static StreamIO *stream; switch (Message) { @@ -88,7 +87,7 @@ LRESULT SSLPassDLG(HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam) * @param my_errno The value of errno to use in case we want to call strerror(). * @returns Error string, only valid until next call to this function. */ -char *ssl_error_str(int err, int my_errno) +const char *ssl_error_str(int err, int my_errno) { static char ssl_errbuf[256]; char *ssl_errstr = NULL; @@ -96,7 +95,7 @@ char *ssl_error_str(int err, int my_errno) switch(err) { case SSL_ERROR_NONE: - ssl_errstr = "SSL: No error"; + ssl_errstr = "OpenSSL: No error"; break; case SSL_ERROR_SSL: ssl_errstr = "Internal OpenSSL error or protocol error"; @@ -126,27 +125,8 @@ char *ssl_error_str(int err, int my_errno) return ssl_errstr; } -/** Write official OpenSSL error string to ircd log / sendto_realops, using config_status. - * Note that you are expected to announce earlier that you actually encountered an SSL error. - * Also note that multiple error strings may be written out (with a slight chance of including - * irrelevent ones[?]). - */ -void config_report_ssl_error() -{ -unsigned long e; -char buf[512]; - - do { - e = ERR_get_error(); - if (e == 0) - break; /* no (more) errors */ - ERR_error_string_n(e, buf, sizeof(buf)); - config_status(" %s", buf); - } while(e); -} - -/** Ask SSL private key password (rare) */ -int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *password) +/** Ask certificate private key password (rare) */ +int TLS_key_passwd_cb(char *buf, int size, int rwflag, void *password) { char *pass; static int before = 0; @@ -162,19 +142,19 @@ int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *password) return strlen(buf); } #ifndef _WIN32 - pass = getpass("Password for SSL private key: "); + pass = getpass("Password for TLS private key: "); #else pass = passbuf; stream.buffer = &pass; stream.size = &passsize; - DialogBoxParam(hInst, "SSLPass", hwIRCDWnd, (DLGPROC)SSLPassDLG, (LPARAM)&stream); + DialogBoxParam(hInst, "TLSKey", hwIRCDWnd, (DLGPROC)TLS_key_passwd_dialog, (LPARAM)&stream); #endif if (pass) { strlcpy(buf, pass, size); strlcpy(beforebuf, pass, sizeof(beforebuf)); before = 1; - SSLKeyPasswd = beforebuf; + TLSKeyPasswd = beforebuf; return (strlen(buf)); } return 0; @@ -192,7 +172,7 @@ static int ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) /** Get Client pointer by SSL pointer */ Client *get_client_by_ssl(SSL *ssl) { - return SSL_get_ex_data(ssl, ssl_client_index); + return SSL_get_ex_data(ssl, tls_client_index); } /** Set requested server name as indicated by SNI */ @@ -218,20 +198,7 @@ static int ssl_hostname_callback(SSL *ssl, int *unk, void *arg) return SSL_TLSEXT_ERR_OK; } -/** Special logging function for SSL/TLS (? make more generic?) */ -static void mylog(char *fmt, ...) -{ - va_list vl; - static char buf[2048]; - - va_start(vl, fmt); - ircvsnprintf(buf, sizeof(buf), fmt, vl); - va_end(vl); - sendto_realops("[SSL rehash] %s", buf); - ircd_log(LOG_ERROR, "%s", buf); -} - -/** Disable SSL/TLS protocols as set by config */ +/** Disable TLS protocols as set by config */ void disable_ssl_protocols(SSL_CTX *ctx, TLSOptions *tlsoptions) { /* OpenSSL has three mechanisms for protocol version control... */ @@ -255,7 +222,7 @@ void disable_ssl_protocols(SSL_CTX *ctx, TLSOptions *tlsoptions) /* The remaining two mechanisms are: * The old way, which is most flexible, is to use: * SSL_CTX_set_options(... SSL_OP_NO_) which allows - * you to disable each and every specific SSL/TLS version. + * you to disable each and every specific TLS version. * * And the new way, which only allows setting a * minimum and maximum protocol version, using: @@ -305,10 +272,10 @@ void disable_ssl_protocols(SSL_CTX *ctx, TLSOptions *tlsoptions) #endif } -/** Initialize SSL/TLS context +/** Initialize TLS context * @param tlsoptions The ::tls-options configuration * @param server Set to 1 if we are initializing a server, 0 for client. - * @returns The SSL/TLS context (SSL_CTX) or NULL in case of error. + * @returns The TLS context (SSL_CTX) or NULL in case of error. */ SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server) { @@ -322,19 +289,20 @@ SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server) if (!ctx) { - config_error("Failed to do SSL CTX new"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_LOAD_FAILED", NULL, + "Failed to do SSL_CTX_new() !?\n$tls_error.all", + log_data_tls_error()); return NULL; } disable_ssl_protocols(ctx, tlsoptions); - SSL_CTX_set_default_passwd_cb(ctx, ssl_pem_passwd_cb); + SSL_CTX_set_default_passwd_cb(ctx, TLS_key_passwd_cb); if (server && !(tlsoptions->options & TLSFLAG_DISABLECLIENTCERT)) { /* We tell OpenSSL/LibreSSL to verify the certificate and set our callback. * Our callback will always accept the certificate since actual checking * will take place elsewhere. Why? Because certificate is (often) delayed - * until after the SSL handshake. Such as in the case of link blocks where + * until after the TLS handshake. Such as in the case of link blocks where * _verify_link() will take care of it only after we learned what server * we are dealing with (and if we should verify certificates for that server). */ @@ -346,71 +314,70 @@ SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server) #endif SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); - if (!tlsoptions->certificate_file) - { - config_error("No SSL certificate configured (set::options::ssl::certificate or in a listen block)"); - config_report_ssl_error(); - goto fail; - } - if (SSL_CTX_use_certificate_chain_file(ctx, tlsoptions->certificate_file) <= 0) { - config_error("Failed to load SSL certificate %s", tlsoptions->certificate_file); - config_report_ssl_error(); - goto fail; - } - - if (!tlsoptions->key_file) - { - config_error("No SSL key configured (set::options::ssl::key or in a listen block)"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_LOAD_FAILED", NULL, + "Failed to load TLS certificate $filename\n$tls_error.all", + log_data_string("filename", tlsoptions->certificate_file), + log_data_tls_error()); goto fail; } if (SSL_CTX_use_PrivateKey_file(ctx, tlsoptions->key_file, SSL_FILETYPE_PEM) <= 0) { - config_error("Failed to load SSL private key %s", tlsoptions->key_file); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_LOAD_FAILED", NULL, + "Failed to load TLS private key $filename\n$tls_error.all", + log_data_string("filename", tlsoptions->key_file), + log_data_tls_error()); goto fail; } if (!SSL_CTX_check_private_key(ctx)) { - config_error("Failed to check SSL private key"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_LOAD_FAILED", NULL, + "Check for TLS private key failed $filename\n$tls_error.all", + log_data_string("filename", tlsoptions->key_file), + log_data_tls_error()); goto fail; } if (SSL_CTX_set_cipher_list(ctx, tlsoptions->ciphers) == 0) { - config_error("Failed to set SSL cipher list"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_INVALID_CIPHERS_LIST", NULL, + "Failed to set TLS cipher list '$tls_ciphers_list'\n$tls_error.all", + log_data_string("tls_ciphers_list", tlsoptions->ciphers), + log_data_tls_error()); goto fail; } #ifdef SSL_OP_NO_TLSv1_3 if (SSL_CTX_set_ciphersuites(ctx, tlsoptions->ciphersuites) == 0) { - config_error("Failed to set SSL ciphersuites list"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_INVALID_CIPHERSUITES_LIST", NULL, + "Failed to set TLS ciphersuites list '$tls_ciphers_list'\n$tls_error.all", + log_data_string("tls_ciphersuites_list", tlsoptions->ciphersuites), + log_data_tls_error()); goto fail; } #endif if (!cipher_check(ctx, &errstr)) { - config_error("There is a problem with your SSL/TLS 'ciphers' configuration setting: %s", errstr); - config_error("Remove the ciphers setting from your configuration file to use safer defaults, or change the cipher setting."); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_CIPHER_CHECK_FAILED", NULL, + "There is a problem with your TLS 'ciphers' configuration setting: $quality_check_error\n" + "Remove the ciphers setting from your configuration file to use safer defaults, or change the cipher setting.", + log_data_string("quality_check_error", errstr)); goto fail; } if (!certificate_quality_check(ctx, &errstr)) { - config_error("There is a problem with your SSL/TLS certificate: %s. Please use another certificate/keypair.", errstr); - config_error("If you use the standard UnrealIRCd certificates then you can simply run 'make pem' and 'make install' " - "from your UnrealIRCd source directory (eg: ~/unrealircd-5.X.Y/) to create and install new certificates"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_CERTIFICATE_CHECK_FAILED", NULL, + "There is a problem with your TLS certificate '$filename': $quality_check_error\n" + "If you use the standard UnrealIRCd certificates then you can simply run 'make pem' and 'make install' " + "from your UnrealIRCd source directory (eg: ~/unrealircd-6.X.Y/) to create and install new certificates", + log_data_string("filename", tlsoptions->certificate_file), + log_data_string("quality_check_error", errstr)); goto fail; } @@ -421,8 +388,10 @@ SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server) { if (!SSL_CTX_load_verify_locations(ctx, tlsoptions->trusted_ca_file, NULL)) { - config_error("Failed to load Trusted CA's from %s", tlsoptions->trusted_ca_file); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_LOAD_FAILED", NULL, + "Failed to load trusted-ca-file $filename\n$tls_error.all", + log_data_string("filename", tlsoptions->trusted_ca_file), + log_data_tls_error()); goto fail; } } @@ -450,22 +419,22 @@ SSL_CTX *init_ctx(TLSOptions *tlsoptions, int server) #ifdef HAS_SSL_CTX_SET1_CURVES_LIST if (!SSL_CTX_set1_curves_list(ctx, tlsoptions->ecdh_curves)) { - config_error("Failed to apply ecdh-curves '%s'. " - "To get a list of supported curves with the " - "appropriate names, run " - "'openssl ecparam -list_curves' on the server. " - "Separate multiple curves by colon, " - "for example: ecdh-curves \"secp521r1:secp384r1\".", - tlsoptions->ecdh_curves); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_INVALID_ECDH_CURVES_LIST", NULL, + "Failed to set ecdh-curves '$ecdh_curves_list'\n$tls_error.all\n" + "HINT: o get a list of supported curves with the appropriate names, " + "run 'openssl ecparam -list_curves' on the server. " + "Separate multiple curves by colon, for example: " + "ecdh-curves \"secp521r1:secp384r1\".", + log_data_string("ecdh_curves_list", tlsoptions->ecdh_curves), + log_data_tls_error()); goto fail; } #else /* We try to avoid this in the config code, but better have * it here too than be sorry if someone screws up: */ - config_error("ecdh-curves specified but not supported by library -- BAD!"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "BUG_ECDH_CURVES", NULL, + "ecdh-curves specified but not supported by library -- BAD!"); goto fail; #endif } @@ -487,23 +456,51 @@ fail: return NULL; } -/** Early initalization of SSL/TLS subsystem - called on startup */ -int early_init_ssl(void) +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +MODVAR EVP_MD *sha256_function; /**< SHA256 function for EVP_DigestInit_ex() call */ +MODVAR EVP_MD *sha1_function; /**< SHA1 function for EVP_DigestInit_ex() call */ +MODVAR EVP_MD *md5_function; /**< MD5 function for EVP_DigestInit_ex() call */ +#endif + +/** Early initalization of TLS subsystem - called on startup */ +int early_init_tls(void) { SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); /* This is used to track (SSL *) <--> (Client *) relationships: */ - ssl_client_index = SSL_get_ex_new_index(0, "ssl_client", NULL, NULL, NULL); + tls_client_index = SSL_get_ex_new_index(0, "tls_client", NULL, NULL, NULL); + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + sha256_function = EVP_MD_fetch(NULL, "SHA2-256", NULL); + if (!sha256_function) + { + fprintf(stderr, "Could not find SHA256 algorithm in TLS library\n"); + exit(6); + } + + sha1_function = EVP_MD_fetch(NULL, "SHA1", NULL); + if (!sha1_function) + { + fprintf(stderr, "Could not find SHA1 algorithm in TLS library\n"); + exit(6); + } + + md5_function = EVP_MD_fetch(NULL, "MD5", NULL); + if (!md5_function) + { + fprintf(stderr, "Could not find MD5 algorithm in TLS library\n"); + exit(6); + } +#endif return 1; } /** Initialize the server and client contexts. * This is only possible after reading the configuration file. */ -int init_ssl(void) +int init_tls(void) { - /* SSL preliminaries. We keep the certificate and key with the context. */ ctx_server = init_ctx(iConf.tls_options, 1); if (!ctx_server) return 0; @@ -513,29 +510,20 @@ int init_ssl(void) return 1; } -/** Reinitialize SSL/TLS server and client contexts - after REHASH -tls +/** Reinitialize TLS server and client contexts - after REHASH -tls */ -void reinit_ssl(Client *client) +void reinit_tls(void) { SSL_CTX *tmp; ConfigItem_listen *listen; ConfigItem_sni *sni; ConfigItem_link *link; - if (!client) - mylog("Reloading all SSL related data (./unrealircd reloadtls)"); - else if (IsUser(client)) - mylog("%s (%s@%s) requested a reload of all SSL related data (/rehash -tls)", - client->name, client->user->username, client->user->realhost); - else - mylog("%s requested a reload of all SSL related data (/rehash -tls)", - client->name); - tmp = init_ctx(iConf.tls_options, 1); if (!tmp) { - config_error("SSL Reload failed."); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, + "TLS Reload failed. See previous errors."); return; } if (ctx_server) @@ -545,8 +533,8 @@ void reinit_ssl(Client *client) tmp = init_ctx(iConf.tls_options, 0); if (!tmp) { - config_error("SSL Reload partially failed. Server context is reloaded, client context failed"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, + "TLS Reload failed at client context. See previous errors."); return; } if (ctx_client) @@ -561,8 +549,8 @@ void reinit_ssl(Client *client) tmp = init_ctx(listen->tls_options, 1); if (!tmp) { - config_error("SSL Reload partially failed. listen::tls-options error, see above"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, + "TLS Reload failed at listen::tls-options. See previous errors."); return; } if (listen->ssl_ctx) @@ -579,8 +567,8 @@ void reinit_ssl(Client *client) tmp = init_ctx(sni->tls_options, 1); if (!tmp) { - config_error("SSL Reload partially failed. sni::tls-options error, see above"); - config_report_ssl_error(); + unreal_log(ULOG_ERROR, "config", "TLS_RELOAD_FAILED", NULL, + "TLS Reload failed at sni::tls-options. See previous errors."); return; } if (sni->ssl_ctx) @@ -597,9 +585,9 @@ void reinit_ssl(Client *client) tmp = init_ctx(link->tls_options, 0); if (!tmp) { - config_error("SSL Reload partially failed. link::outgoing::tls-options error in link %s { }, see above", - link->servername); - config_report_ssl_error(); + 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; } if (link->ssl_ctx) @@ -616,15 +604,23 @@ void SSL_set_nonblocking(SSL *s) BIO_set_nbio(SSL_get_wbio(s),1); } -/** Get SSL/TLS ciphersuite */ -char *tls_get_cipher(SSL *ssl) +/** Get TLS ciphersuite */ +const char *tls_get_cipher(Client *client) { static char buf[256]; - + const char *cached; + + cached = moddata_client_get(client, "tls_cipher"); + if (cached) + return cached; + + if (!MyConnect(client) || !client->local->ssl) + return NULL; + buf[0] = '\0'; - strlcpy(buf, SSL_get_version(ssl), sizeof(buf)); + strlcpy(buf, SSL_get_version(client->local->ssl), sizeof(buf)); strlcat(buf, "-", sizeof(buf)); - strlcat(buf, SSL_get_cipher(ssl), sizeof(buf)); + strlcat(buf, SSL_get_cipher(client->local->ssl), sizeof(buf)); return buf; } @@ -636,30 +632,34 @@ TLSOptions *get_tls_options_for_client(Client *client) { if (!client->local) return NULL; - if (client->serv && client->serv->conf && client->serv->conf->tls_options) - return client->serv->conf->tls_options; + if (client->server && client->server->conf && client->server->conf->tls_options) + return client->server->conf->tls_options; if (client->local && client->local->listener && client->local->listener->tls_options) return client->local->listener->tls_options; return iConf.tls_options; } -/** Outgoing SSL connect (read: handshake) to another server. */ -void ircd_SSL_client_handshake(int fd, int revents, void *data) +/** Outgoing TLS connect (read: handshake) to another server. */ +void unreal_tls_client_handshake(int fd, int revents, void *data) { Client *client = data; - SSL_CTX *ctx = (client->serv && client->serv->conf && client->serv->conf->ssl_ctx) ? client->serv->conf->ssl_ctx : ctx_client; + SSL_CTX *ctx = (client->server && client->server->conf && client->server->conf->ssl_ctx) ? client->server->conf->ssl_ctx : ctx_client; TLSOptions *tlsoptions = get_tls_options_for_client(client); if (!ctx) { - sendto_realops("Could not start SSL client handshake: SSL was not loaded correctly on this server (failed to load cert or key)"); + unreal_log(ULOG_ERROR, "config", "TLS_CREATE_SESSION_FAILED", NULL, + "Could not start TLS client handshake (no ctx?): TLS was possibly not loaded correctly on this server!?\n$tls_error.all", + log_data_tls_error()); return; } client->local->ssl = SSL_new(ctx); if (!client->local->ssl) { - sendto_realops("Failed to SSL_new(ctx)"); + unreal_log(ULOG_ERROR, "config", "TLS_CREATE_SESSION_FAILED", NULL, + "Could not start TLS client handshake: TLS was possibly not loaded correctly on this server!?\n$tls_error.all", + log_data_tls_error()); return; } @@ -679,15 +679,15 @@ void ircd_SSL_client_handshake(int fd, int revents, void *data) BIO_set_ssl_renegotiate_timeout(SSL_get_wbio(client->local->ssl), tlsoptions->renegotiate_timeout); } - if (client->serv && client->serv->conf) + if (client->server && client->server->conf) { /* Client: set hostname for SNI */ - SSL_set_tlsext_host_name(client->local->ssl, client->serv->conf->servername); + SSL_set_tlsext_host_name(client->local->ssl, client->server->conf->servername); } SetTLS(client); - switch (ircd_SSL_connect(client, fd)) + switch (unreal_tls_connect(client, fd)) { case -1: fd_close(fd); @@ -695,11 +695,9 @@ void ircd_SSL_client_handshake(int fd, int revents, void *data) --OpenFiles; return; case 0: - Debug((DEBUG_DEBUG, "SetTLSConnectHandshake(%s)", get_client_name(client, TRUE))); SetTLSConnectHandshake(client); return; case 1: - Debug((DEBUG_DEBUG, "SSL_init_finished should finish this job (%s)", get_client_name(client, TRUE))); return; default: return; @@ -707,15 +705,15 @@ void ircd_SSL_client_handshake(int fd, int revents, void *data) } -/** Called by I/O engine to (re)try accepting an SSL/TLS connection */ -static void ircd_SSL_accept_retry(int fd, int revents, void *data) +/** Called by I/O engine to (re)try accepting an TLS connection */ +static void unreal_tls_accept_retry(int fd, int revents, void *data) { Client *client = data; - ircd_SSL_accept(client, fd); + unreal_tls_accept(client, fd); } -/** Accept an SSL/TLS connection - that is: do the TLS handshake */ -int ircd_SSL_accept(Client *client, int fd) +/** Accept an TLS connection - that is: do the TLS handshake */ +int unreal_tls_accept(Client *client, int fd) { int ssl_err; @@ -730,26 +728,26 @@ int ircd_SSL_accept(Client *client, int fd) { char buf[512]; snprintf(buf, sizeof(buf), - "ERROR :STARTTLS received but this is an SSL-only port. Check your connect settings. " - "If this is a server linking in then add 'ssl' in your link::outgoing::options block.\r\n"); + "ERROR :STARTTLS received but this is a TLS-only port. Check your connect settings. " + "If this is a server linking in then add 'tls' in your link::outgoing::options block.\r\n"); (void)send(fd, buf, strlen(buf), 0); - return fatal_ssl_error(SSL_ERROR_SSL, SAFE_SSL_ACCEPT, ERRNO, client); + return fatal_tls_error(SSL_ERROR_SSL, FUNC_TLS_ACCEPT, ERRNO, client); } if ((n >= 4) && (!strncmp(buf, "USER", 4) || !strncmp(buf, "NICK", 4) || !strncmp(buf, "PASS", 4) || !strncmp(buf, "CAP ", 4))) { char buf[512]; snprintf(buf, sizeof(buf), - "ERROR :NON-SSL command received on SSL-only port. Check your connection settings.\r\n"); + "ERROR :NON-TLS command received on TLS-only port. Check your connection settings.\r\n"); (void)send(fd, buf, strlen(buf), 0); - return fatal_ssl_error(SSL_ERROR_SSL, SAFE_SSL_ACCEPT, ERRNO, client); + return fatal_tls_error(SSL_ERROR_SSL, FUNC_TLS_ACCEPT, ERRNO, client); } if ((n >= 8) && (!strncmp(buf, "PROTOCTL", 8) || !strncmp(buf, "SERVER", 6))) { char buf[512]; snprintf(buf, sizeof(buf), - "ERROR :NON-SSL command received on SSL-only port. Check your connection settings.\r\n"); + "ERROR :NON-TLS command received on TLS-only port. Check your connection settings.\r\n"); (void)send(fd, buf, strlen(buf), 0); - return fatal_ssl_error(SSL_ERROR_SSL, SAFE_SSL_ACCEPT, ERRNO, client); + return fatal_tls_error(SSL_ERROR_SSL, FUNC_TLS_ACCEPT, ERRNO, client); } if (n > 0) SetNextCall(client); @@ -764,17 +762,17 @@ int ircd_SSL_accept(Client *client, int fd) { return 1; } - return fatal_ssl_error(ssl_err, SAFE_SSL_ACCEPT, ERRNO, client); + return fatal_tls_error(ssl_err, FUNC_TLS_ACCEPT, ERRNO, client); case SSL_ERROR_WANT_READ: - fd_setselect(fd, FD_SELECT_READ, ircd_SSL_accept_retry, client); + fd_setselect(fd, FD_SELECT_READ, unreal_tls_accept_retry, client); fd_setselect(fd, FD_SELECT_WRITE, NULL, client); return 1; case SSL_ERROR_WANT_WRITE: fd_setselect(fd, FD_SELECT_READ, NULL, client); - fd_setselect(fd, FD_SELECT_WRITE, ircd_SSL_accept_retry, client); + fd_setselect(fd, FD_SELECT_WRITE, unreal_tls_accept_retry, client); return 1; default: - return fatal_ssl_error(ssl_err, SAFE_SSL_ACCEPT, ERRNO, client); + return fatal_tls_error(ssl_err, FUNC_TLS_ACCEPT, ERRNO, client); } /* NOTREACHED */ return -1; @@ -786,14 +784,14 @@ int ircd_SSL_accept(Client *client, int fd) } /** Called by the I/O engine to (re)try to connect to a remote host */ -static void ircd_SSL_connect_retry(int fd, int revents, void *data) +static void unreal_tls_connect_retry(int fd, int revents, void *data) { Client *client = data; - ircd_SSL_connect(client, fd); + unreal_tls_connect(client, fd); } /** Connect to a remote host - that is: connect and do the TLS handshake */ -int ircd_SSL_connect(Client *client, int fd) +int unreal_tls_connect(Client *client, int fd) { int ssl_err; @@ -805,23 +803,23 @@ int ircd_SSL_connect(Client *client, int fd) case SSL_ERROR_SYSCALL: if (ERRNO == P_EINTR || ERRNO == P_EWOULDBLOCK || ERRNO == P_EAGAIN) { - /* Hmmm. This implementation is different than in ircd_SSL_accept(). + /* Hmmm. This implementation is different than in unreal_tls_accept(). * One of them must be wrong -- better check! (TODO) */ - fd_setselect(fd, FD_SELECT_READ|FD_SELECT_WRITE, ircd_SSL_connect_retry, client); + fd_setselect(fd, FD_SELECT_READ|FD_SELECT_WRITE, unreal_tls_connect_retry, client); return 0; } - return fatal_ssl_error(ssl_err, SAFE_SSL_CONNECT, ERRNO, client); + return fatal_tls_error(ssl_err, FUNC_TLS_CONNECT, ERRNO, client); case SSL_ERROR_WANT_READ: - fd_setselect(fd, FD_SELECT_READ, ircd_SSL_connect_retry, client); + fd_setselect(fd, FD_SELECT_READ, unreal_tls_connect_retry, client); fd_setselect(fd, FD_SELECT_WRITE, NULL, client); return 0; case SSL_ERROR_WANT_WRITE: fd_setselect(fd, FD_SELECT_READ, NULL, client); - fd_setselect(fd, FD_SELECT_WRITE, ircd_SSL_connect_retry, client); + fd_setselect(fd, FD_SELECT_WRITE, unreal_tls_connect_retry, client); return 0; default: - return fatal_ssl_error(ssl_err, SAFE_SSL_CONNECT, ERRNO, client); + return fatal_tls_error(ssl_err, FUNC_TLS_CONNECT, ERRNO, client); } /* NOTREACHED */ return -1; @@ -833,7 +831,7 @@ int ircd_SSL_connect(Client *client, int fd) return 1; } -/** Shutdown a SSL/TLS connection (gracefully) */ +/** Shutdown a TLS connection (gracefully) */ int SSL_smart_shutdown(SSL *ssl) { char i; @@ -848,50 +846,54 @@ int SSL_smart_shutdown(SSL *ssl) } /** - * Report a fatal SSL error and disconnect the associated client. + * Report a fatal TLS error and disconnect the associated client. * * @param ssl_error The error as from OpenSSL. * @param where The location, one of the SAFE_SSL_* defines. * @param my_errno A preserved value of errno to pass to ssl_error_str(). * @param client The client the error is associated with. */ -static int fatal_ssl_error(int ssl_error, int where, int my_errno, Client *client) +static int fatal_tls_error(int ssl_error, int where, int my_errno, Client *client) { /* don`t alter ERRNO */ int errtmp = ERRNO; - char *ssl_errstr, *ssl_func; + const char *ssl_errstr, *ssl_func; unsigned long additional_errno = ERR_get_error(); char additional_info[256]; + char buf[512]; const char *one, *two; if (IsDeadSocket(client)) - { -#ifdef DEBUGMODE - /* This is quite possible I guess.. especially if we don't pay attention upstream :p */ - ircd_log(LOG_ERROR, "Warning: fatal_ssl_error() called for already-dead-socket (%d/%s)", - client->local->fd, client->name); -#endif return -1; - } switch(where) { - case SAFE_SSL_READ: + case FUNC_TLS_READ: ssl_func = "SSL_read()"; break; - case SAFE_SSL_WRITE: + case FUNC_TLS_WRITE: ssl_func = "SSL_write()"; break; - case SAFE_SSL_ACCEPT: + case FUNC_TLS_ACCEPT: ssl_func = "SSL_accept()"; break; - case SAFE_SSL_CONNECT: + case FUNC_TLS_CONNECT: ssl_func = "SSL_connect()"; break; default: ssl_func = "undefined SSL func"; } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + /* Fetch additional error information from OpenSSL 3.0.0+ */ + two = ERR_reason_error_string(additional_errno); + if (two && *two) + { + snprintf(additional_info, sizeof(additional_info), ": %s", two); + } else { + *additional_info = '\0'; + } +#else /* Fetch additional error information from OpenSSL. This is new as of Nov 2017 (4.0.16+) */ one = ERR_func_error_string(additional_errno); two = ERR_reason_error_string(additional_errno); @@ -901,20 +903,18 @@ static int fatal_ssl_error(int ssl_error, int where, int my_errno, Client *clien } else { *additional_info = '\0'; } +#endif ssl_errstr = ssl_error_str(ssl_error, my_errno); - /* if we reply() something here, we might just trigger another - * fatal_ssl_error() call and loop until a stack overflow... - * the client won`t get the ERROR : ... string, but this is - * the only way to do it. - * IRC protocol wasn`t SSL enabled .. --vejeta - */ SetDeadSocket(client); - sendto_snomask(SNO_JUNK, "Exiting ssl client %s: %s: %s%s", - get_client_name(client, TRUE), ssl_func, ssl_errstr, additional_info); + unreal_log(ULOG_DEBUG, "tls", "DEBUG_TLS_FATAL_ERROR", client, + "Exiting TLS client $client.details: $tls_function: $tls_error_string: $tls_additional_info", + log_data_string("tls_function", ssl_func), + log_data_string("tls_error_string", ssl_errstr), + log_data_string("tls_additional_info", additional_info)); - if (where == SAFE_SSL_CONNECT) + if (where == FUNC_TLS_CONNECT) { char extra[256]; *extra = '\0'; @@ -922,15 +922,17 @@ static int fatal_ssl_error(int ssl_error, int where, int my_errno, Client *clien { snprintf(extra, sizeof(extra), ". Please verify that listen::options::ssl is enabled on port %d in %s's configuration file.", - (client->serv && client->serv->conf) ? client->serv->conf->outgoing.port : -1, + (client->server && client->server->conf) ? client->server->conf->outgoing.port : -1, client->name); } - lost_server_link(client, "%s: %s%s%s", ssl_func, ssl_errstr, additional_info, extra); + snprintf(buf, sizeof(buf), "%s: %s%s%s", ssl_func, ssl_errstr, additional_info, extra); + lost_server_link(client, buf); } else - if (IsServer(client) || (client->serv && client->serv->conf)) + if (IsServer(client) || (client->server && client->server->conf)) { /* Either a trusted fully established server (incoming) or an outgoing server link (established or not) */ - lost_server_link(client, "%s: %s%s", ssl_func, ssl_errstr, additional_info); + snprintf(buf, sizeof(buf), "%s: %s%s", ssl_func, ssl_errstr, additional_info); + lost_server_link(client, buf); } if (errtmp) @@ -949,7 +951,7 @@ static int fatal_ssl_error(int ssl_error, int where, int my_errno, Client *clien return -1; } -/** Do a SSL/TLS handshake after a STARTTLS, as a client */ +/** Do a TLS handshake after a STARTTLS, as a client */ int client_starttls(Client *client) { if ((client->local->ssl = SSL_new(ctx_client)) == NULL) @@ -960,15 +962,14 @@ int client_starttls(Client *client) SSL_set_fd(client->local->ssl, client->local->fd); SSL_set_nonblocking(client->local->ssl); - if (client->serv && client->serv->conf) + if (client->server && client->server->conf) { /* Client: set hostname for SNI */ - SSL_set_tlsext_host_name(client->local->ssl, client->serv->conf->servername); + SSL_set_tlsext_host_name(client->local->ssl, client->server->conf->servername); } - if (ircd_SSL_connect(client, client->local->fd) < 0) + if (unreal_tls_connect(client, client->local->fd) < 0) { - Debug((DEBUG_DEBUG, "Failed SSL connect handshake in instance 1: %s", client->name)); SSL_set_shutdown(client->local->ssl, SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(client->local->ssl); SSL_free(client->local->ssl); @@ -987,7 +988,7 @@ fail_starttls: } /** Find the appropriate TLSOptions structure for a client. - * NOTE: The default global SSL options will be returned if not found, + * NOTE: The default global TLS options will be returned if not found, * or NULL if no such options are available (unlikely, but possible?). */ TLSOptions *FindTLSOptionsForUser(Client *client) @@ -1020,7 +1021,7 @@ TLSOptions *FindTLSOptionsForUser(Client *client) * @param errstr: Error will be stored in here (optional) * @returns Returns 1 on success and 0 on error. */ -int verify_certificate(SSL *ssl, char *hostname, char **errstr) +int verify_certificate(SSL *ssl, const char *hostname, char **errstr) { static char buf[512]; X509 *cert; @@ -1033,10 +1034,10 @@ int verify_certificate(SSL *ssl, char *hostname, char **errstr) if (!ssl) { - strlcpy(buf, "Not using SSL/TLS", sizeof(buf)); + strlcpy(buf, "Not using TLS", sizeof(buf)); if (errstr) *errstr = buf; - return 0; /* Cannot verify a non-SSL connection */ + return 0; /* Cannot verify a non-TLS connection */ } if (SSL_get_verify_result(ssl) != X509_V_OK) @@ -1085,7 +1086,7 @@ int verify_certificate(SSL *ssl, char *hostname, char **errstr) } /** Grab the certificate name */ -char *certificate_name(SSL *ssl) +const char *certificate_name(SSL *ssl) { static char buf[384]; X509 *cert; @@ -1128,7 +1129,7 @@ int cipher_check(SSL_CTX *ctx, char **errstr) ssl = SSL_new(ctx); if (!ssl) { - snprintf(errbuf, sizeof(errbuf), "Could not create SSL structure"); + snprintf(errbuf, sizeof(errbuf), "Could not create TLS structure"); return 0; } @@ -1169,6 +1170,8 @@ int cipher_check(SSL_CTX *ctx, char **errstr) /** Check if a certificate (or actually: key) is weak */ int certificate_quality_check(SSL_CTX *ctx, char **errstr) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L + // FIXME: this only works on OpenSSL <3.0.0 SSL *ssl; X509 *cert; EVP_PKEY *public_key; @@ -1185,14 +1188,14 @@ int certificate_quality_check(SSL_CTX *ctx, char **errstr) ssl = SSL_new(ctx); if (!ssl) { - snprintf(errbuf, sizeof(errbuf), "Could not create SSL structure"); + snprintf(errbuf, sizeof(errbuf), "Could not create TLS structure"); return 0; } cert = SSL_get_certificate(ssl); if (!cert) { - snprintf(errbuf, sizeof(errbuf), "Could not retrieve SSL/TLS certificate"); + snprintf(errbuf, sizeof(errbuf), "Could not retrieve TLS certificate"); SSL_free(ssl); return 0; } @@ -1221,14 +1224,15 @@ int certificate_quality_check(SSL_CTX *ctx, char **errstr) if (key_length < 2048) { - snprintf(errbuf, sizeof(errbuf), "Your SSL/TLS certificate key is only %d bits, which is insecure", key_length); + snprintf(errbuf, sizeof(errbuf), "Your TLS certificate key is only %d bits, which is insecure", key_length); return 0; } +#endif return 1; } -char *spki_fingerprint_ex(X509 *x509_cert); +const char *spki_fingerprint_ex(X509 *x509_cert); /** Return the SPKI Fingerprint for a client. * @@ -1237,10 +1241,10 @@ char *spki_fingerprint_ex(X509 *x509_cert); * openssl dgst -sha256 -binary public.key | openssl enc -base64 * ( from https://tools.ietf.org/html/draft-ietf-websec-key-pinning-21#appendix-A ) */ -char *spki_fingerprint(Client *cptr) +const char *spki_fingerprint(Client *cptr) { X509 *x509_cert = NULL; - char *ret; + const char *ret; if (!MyConnect(cptr) || !cptr->local->ssl) return NULL; @@ -1253,12 +1257,11 @@ char *spki_fingerprint(Client *cptr) return ret; } -char *spki_fingerprint_ex(X509 *x509_cert) +const char *spki_fingerprint_ex(X509 *x509_cert) { unsigned char *der_cert = NULL, *p; int der_cert_len, n; static char retbuf[256]; - SHA256_CTX ckctx; unsigned char checksum[SHA256_DIGEST_LENGTH]; memset(retbuf, 0, sizeof(retbuf)); @@ -1274,9 +1277,7 @@ char *spki_fingerprint_ex(X509 *x509_cert) /* The DER encoded SPKI is stored in 'der_cert' with length 'der_cert_len'. * Now we need to create an SHA256 hash out of it. */ - SHA256_Init(&ckctx); - SHA256_Update(&ckctx, der_cert, der_cert_len); - SHA256_Final(checksum, &ckctx); + sha256hash_binary(checksum, der_cert, der_cert_len); /* And convert the binary to a base64 string... */ n = b64_encode(checksum, SHA256_DIGEST_LENGTH, retbuf, sizeof(retbuf)); @@ -1317,7 +1318,7 @@ int outdated_tls_client(Client *client) } /** Returns the expanded string used for set::outdated-tls-policy::user-message etc. */ -char *outdated_tls_client_build_string(char *pattern, Client *client) +const char *outdated_tls_client_build_string(const char *pattern, Client *client) { static char buf[512]; const char *name[3], *value[3]; @@ -1404,8 +1405,10 @@ void check_certificate_expiry_tlsoptions_and_warn(TLSOptions *tlsoptions) if (check_certificate_expiry_ctx(ctx, &errstr)) { - sendto_umode_global(UMODE_OPER, "Warning: TLS certificate '%s': %s", tlsoptions->certificate_file, errstr); - ircd_log(LOG_ERROR, "[warning] TLS certificate '%s': %s", tlsoptions->certificate_file, errstr); + unreal_log(ULOG_ERROR, "tls", "TLS_CERT_EXPIRING", NULL, + "Warning: TLS certificate '$filename': $error_string", + log_data_string("filename", tlsoptions->certificate_file), + log_data_string("error_string", errstr)); } SSL_CTX_free(ctx); } diff --git a/src/unrealdb.c b/src/unrealdb.c index e8a7226..6b77c21 100644 --- a/src/unrealdb.c +++ b/src/unrealdb.c @@ -40,15 +40,15 @@ * and I/O speeds of the underlying hardware. */ -/* In UnrealIRCd 5.2.0 we don't write the v1 header yet for unencrypted - * database files, this so users using unencrypted can easily downgrade - * to 5.0.9 and lower should there be any need to do so. +/* In UnrealIRCd 5.2.x we didn't write the v1 header yet for unencrypted + * database files, this so users using unencrypted could easily downgrade + * to version 5.0.9 and older. * We DO support READING encypted, unencrypted v1, and unencrypted raw (v0) - * in 5.2.0, though. - * Presumably in 2022 or so we will stop writing v0 by default and change - * this #undef to a #define to write v1. + * in 5.2.0 onwards, though. + * Starting with UnrealIRCd 6 we now write the header, so people can only + * downgrade from UnrealIRCd 6 to 5.2.0 and later (not 5.0.9). */ -#undef UNREALDB_WRITE_V1 +#define UNREALDB_WRITE_V1 /* If a key is specified, it must be this size */ #define UNREALDB_KEY_LEN crypto_secretstream_xchacha20poly1305_KEYBYTES @@ -68,6 +68,7 @@ /* Forward declarations - only used for internal (static) functions, of course */ static SecretCache *find_secret_cache(Secret *secr, UnrealDBConfig *cfg); static void unrealdb_add_to_secret_cache(Secret *secr, UnrealDBConfig *cfg); +static void unrealdb_set_error(UnrealDB *c, UnrealDBError errcode, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,3,4))); UnrealDBError unrealdb_last_error_code; static char *unrealdb_last_error_string = NULL; @@ -136,7 +137,7 @@ static int unrealdb_kdf(UnrealDB *c, Secret *secr) * For programmatically checking of error conditions * use unrealdb_get_error_code() instead. */ -char *unrealdb_get_error_string(void) +const char *unrealdb_get_error_string(void) { return unrealdb_last_error_string; } @@ -321,12 +322,16 @@ UnrealDB *unrealdb_open(const char *filename, UnrealDBMode mode, char *secret_bl if (cached) { #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[UnrealDB] unrealdb_open(): Cache hit for '%s' while writing", secr->name); + unreal_log(ULOG_DEBUG, "unrealdb", "DEBUG_UNREALDB_CACHE_HIT", NULL, + "Cache hit for '$secret_block' while writing", + log_data_string("secret_block", secr->name)); #endif } else { #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[UnrealDB] unrealdb_open(): Need to run argon2 '%s' while writing", secr->name); + unreal_log(ULOG_DEBUG, "unrealdb", "DEBUG_UNREALDB_CACHE_MISS", NULL, + "Cache miss for '$secret_block' while writing, need to run argon2", + log_data_string("secret_block", secr->name)); #endif if (!unrealdb_kdf(c, secr)) { @@ -414,11 +419,15 @@ UnrealDB *unrealdb_open(const char *filename, UnrealDBMode mode, char *secret_bl /* Use cached key, no need to run expensive argon2.. */ memcpy(c->config->key, dbcache->config->key, c->config->keylen); #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[UnrealDB] unrealdb_open(): Cache hit for '%s' while reading", secr->name); + unreal_log(ULOG_DEBUG, "unrealdb", "DEBUG_UNREALDB_CACHE_HIT", NULL, + "Cache hit for '$secret_block' while reading", + log_data_string("secret_block", secr->name)); #endif } else { #ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[UnrealDB] unrealdb_open(): Need to run argon2 for '%s' while reading", secr->name); + unreal_log(ULOG_DEBUG, "unrealdb", "DEBUG_UNREALDB_CACHE_MISS", NULL, + "Cache miss for '$secret_block' while reading, need to run argon2", + log_data_string("secret_block", secr->name)); #endif if (!unrealdb_kdf(c, secr)) { @@ -563,11 +572,11 @@ char *unrealdb_test_db(const char *filename, char *secret_block) * unrealdb_write_int64(), unrealdb_write_int32(), unrealdb_write_int16(), * unrealdb_write_char(), unrealdb_write_str(). */ -static int unrealdb_write(UnrealDB *c, void *wbuf, int len) +static int unrealdb_write(UnrealDB *c, const void *wbuf, int len) { char buf_out[UNREALDB_CRYPT_FILE_CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; unsigned long long out_len; - char *buf = wbuf; + const char *buf = wbuf; if (c->error_code) return 0; @@ -643,7 +652,7 @@ static int unrealdb_write(UnrealDB *c, void *wbuf, int len) * Note that 'x' can safely be NULL. * @returns 1 on success, 0 on failure. */ -int unrealdb_write_str(UnrealDB *c, char *x) +int unrealdb_write_str(UnrealDB *c, const char *x) { uint16_t len; @@ -934,6 +943,7 @@ int unrealdb_read_char(UnrealDB *c, char *t) /** @} */ +#if 0 void fatal_error(FORMAT_STRING(const char *pattern), ...) { va_list vl; @@ -1045,10 +1055,11 @@ void unrealdb_test(void) fprintf(stderr, "**** TESTING UNENCRYPTED ****\n"); unrealdb_test_speed(NULL); } +#endif /** TODO: document and implement */ -char *unrealdb_test_secret(char *name) +const char *unrealdb_test_secret(const char *name) { // FIXME: check if exists, if not then return an error, with a nice FAQ reference etc. return NULL; /* no error */ diff --git a/src/updconf.c b/src/updconf.c deleted file mode 100644 index 8294960..0000000 --- a/src/updconf.c +++ /dev/null @@ -1,1730 +0,0 @@ -/* - * Configuration file updater - upgrade from 3.2.x to 4.x - * (C) Copyright 2015 Bram Matthys and the UnrealIRCd team - * - * License: GPLv2 - */ - -#include "unrealircd.h" - -extern void config_free(ConfigFile *cfptr); - -char configfiletmp[512]; - -struct Upgrade -{ - char *locop_host; - char *oper_host; - char *coadmin_host; - char *admin_host; - char *sadmin_host; - char *netadmin_host; - int host_on_oper_up; -}; - -struct Upgrade upgrade; - -typedef struct FlagMapping FlagMapping; -struct FlagMapping -{ - char shortflag; - char *longflag; -}; - -static FlagMapping FlagMappingTable[] = { - { 'o', "local" }, - { 'O', "global" }, - { 'r', "can_rehash" }, - { 'D', "can_die" }, - { 'R', "can_restart" }, - { 'w', "can_wallops" }, - { 'g', "can_globops" }, - { 'c', "can_localroute" }, - { 'L', "can_globalroute" }, - { 'K', "can_globalkill" }, - { 'b', "can_kline" }, - { 'B', "can_unkline" }, - { 'n', "can_localnotice" }, - { 'G', "can_globalnotice" }, - { 'A', "admin" }, - { 'a', "services-admin" }, - { 'N', "netadmin" }, - { 'C', "coadmin" }, - { 'z', "can_zline" }, - { 'W', "get_umodew" }, - { 'H', "get_host" }, - { 't', "can_gkline" }, - { 'Z', "can_gzline" }, - { 'v', "can_override" }, - { 'q', "can_setq" }, - { 'd', "can_dccdeny" }, - { 'T', "can_tsctl" }, - { 0, NULL }, -}; - -int needs_modules_default_conf = 1; -int needs_operclass_default_conf = 1; - -static void die() -{ -#ifdef _WIN32 - win_error(); /* ? */ -#endif - exit(0); -} - -#define CFGBUFSIZE 1024 -void modify_file(int start, char *ins, int stop) -{ - char configfiletmp2[512]; - FILE *fdi, *fdo; - char *rdbuf = NULL, *wbuf; - int n; - int first = 1; - - snprintf(configfiletmp2, sizeof(configfiletmp2), "%s.tmp", configfiletmp); // .tmp.tmp :D - -#ifndef _WIN32 - fdi = fopen(configfiletmp, "r"); - fdo = fopen(configfiletmp2, "w"); -#else - fdi = fopen(configfiletmp, "rb"); - fdo = fopen(configfiletmp2, "wb"); -#endif - - if (!fdi || !fdo) - { - config_error("could not read/write to %s/%s", configfiletmp, configfiletmp2); - die(); - } - - rdbuf = safe_alloc(start); - - if ((n = fread(rdbuf, 1, start, fdi)) != start) - { - config_error("read error in remove_section(%d,%d): %d", start, stop, n); - die(); - } - - fwrite(rdbuf, 1, start, fdo); - - safe_free(rdbuf); - - if (ins) - fwrite(ins, 1, strlen(ins), fdo); /* insert this piece */ - - if (stop > 0) - { - if (fseek(fdi, stop+1, SEEK_SET) != 0) - goto end; /* end of file we hope.. */ - } - - // read the remaining stuff - rdbuf = safe_alloc(CFGBUFSIZE); - - while(1) - { - n = fread(rdbuf, 1, CFGBUFSIZE, fdi); - if (n <= 0) - break; // done - - wbuf = rdbuf; - - if (first && (stop > 0)) - { - if ((n > 0) && (*wbuf == '\r')) - { - wbuf++; - n--; - } - if ((n > 0) && (*wbuf == '\n')) - { - wbuf++; - n--; - } - first = 0; - if (n <= 0) - break; /* we are done (EOF) */ - } - - fwrite(wbuf, 1, n, fdo); - } - -end: - fclose(fdi); - fclose(fdo); - - safe_free(rdbuf); - // todo: handle write errors and such.. - - unlink(configfiletmp); - if (rename(configfiletmp2, configfiletmp) < 0) - { - config_error("Could not rename '%s' to '%s': %s", configfiletmp2, configfiletmp, strerror(errno)); - die(); - } -} - -void remove_section(int start, int stop) -{ - modify_file(start, NULL, stop); -} - -void insert_section(int start, char *buf) -{ -#ifdef _WIN32 -static char realbuf[16384]; -char *i, *o; - - if (strlen(buf) > ((sizeof(realbuf)/2)-2)) - abort(); /* damn lazy you !!! */ - - for (i = buf, o = realbuf; *i; i++) - { - if (*i == '\n') - { - *o++ = '\r'; - *o++ = '\n'; - } else - { - *o++ = *i; - } - } - *o = '\0'; - - modify_file(start, realbuf, 0); -#else - modify_file(start, buf, 0); -#endif -} - -void replace_section(ConfigEntry *ce, char *buf) -{ - remove_section(ce->ce_fileposstart, ce->ce_fileposend); - insert_section(ce->ce_fileposstart, buf); -} - -static char buf[8192]; - -int upgrade_me_block(ConfigEntry *ce) -{ - ConfigEntry *cep; - char *name = NULL; - char *info = NULL; - int numeric = 0; - - char sid[16]; - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "sid")) - return 0; /* no upgrade needed */ - else if (!cep->ce_vardata) - { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "me", cep->ce_varname); - return 0; - } - else if (!strcmp(cep->ce_varname, "name")) - name = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "info")) - info = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "numeric")) - numeric = atoi(cep->ce_vardata); - } - - if (!name || !info || !numeric) - { - /* Invalid block as it does not contain the 3.2.x mandatory items */ - return 0; - } - - snprintf(sid, sizeof(sid), "%.3d", numeric); - - snprintf(buf, sizeof(buf), - "me {\n" - "\tname %s;\n" - "\tinfo \"%s\";\n" - "\tsid %s;\n" - "};\n", - name, - info, - sid); - - replace_section(ce, buf); - - config_status("- me block upgraded"); - return 1; -} - -int upgrade_link_block(ConfigEntry *ce) -{ - ConfigEntry *cep, *cepp; - char *bind_ip = NULL; - char *username = NULL; - char *hostname = NULL; - char *port = NULL; - char *password_receive = NULL; - char *password_connect = NULL; - char *class = NULL; - int options_ssl = 0; - int options_autoconnect = 0; - int options_nohostcheck = 0; - int options_quarantine = 0; - /* options_nodnscache is deprecated, always now.. */ - char *hub = NULL; - char *leaf = NULL; - int leaf_depth = -1; - char *ciphers = NULL; - char *password_receive_authmethod = NULL; - int need_incoming = 0, need_outgoing = 0; - - /* ripped from test_link */ - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "incoming") || !strcmp(cep->ce_varname, "outgoing")) - return 0; /* no upgrade needed */ - else if (!strcmp(cep->ce_varname, "options")) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (!strcmp(cepp->ce_varname, "ssl")) - options_ssl = 1; - if (!strcmp(cepp->ce_varname, "autoconnect")) - options_autoconnect = 1; - if (!strcmp(cepp->ce_varname, "nohostcheck")) - options_nohostcheck = 1; - if (!strcmp(cepp->ce_varname, "quarantine")) - options_quarantine = 1; - } - } - else if (!cep->ce_vardata) - { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "link", cep->ce_varname); - return 0; - } - else if (!strcmp(cep->ce_varname, "username")) - username = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "hostname")) - hostname = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "bind-ip")) - bind_ip = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "port")) - port = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "password-receive")) - { - password_receive = cep->ce_vardata; - if (cep->ce_entries) - password_receive_authmethod = cep->ce_entries->ce_varname; - } - else if (!strcmp(cep->ce_varname, "password-connect")) - password_connect = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "class")) - class = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "hub")) - hub = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "leaf")) - leaf = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "leafdepth")) - leaf_depth = atoi(cep->ce_vardata); - else if (!strcmp(cep->ce_varname, "ciphers")) - ciphers = cep->ce_vardata; - } - - if (!username || !hostname || !class || !password_receive || - !password_connect || !port) - { - /* Invalid link block as it does not contain the 3.2.x mandatory items */ - return 0; - } - - if (strchr(hostname, '?') || strchr(hostname, '*')) - { - /* Wildcards in hostname: incoming only */ - need_incoming = 1; - need_outgoing = 0; - } - else - { - /* IP (or hostname with nohostcheck) */ - need_incoming = 1; - need_outgoing = 1; - } - - snprintf(buf, sizeof(buf), "link %s {\n", ce->ce_vardata); - - if (need_incoming) - { - char upg_mask[HOSTLEN+USERLEN+8]; - - if (options_nohostcheck) - { - strlcpy(upg_mask, "*", sizeof(upg_mask)); - } - else - { - if (!strcmp(username, "*")) - strlcpy(upg_mask, hostname, sizeof(upg_mask)); /* just host */ - else - snprintf(upg_mask, sizeof(upg_mask), "%s@%s", username, hostname); /* user@host */ - } - - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tincoming {\n\t\tmask %s;\n\t};\n", upg_mask); - } - - if (need_outgoing) - { - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), - "\toutgoing {\n" - "\t\tbind-ip %s;\n" - "\t\thostname %s;\n" - "\t\tport %s;\n" - "\t\toptions { %s%s};\n" - "\t};\n", - bind_ip, - hostname, - port, - options_ssl ? "ssl; " : "", - options_autoconnect ? "autoconnect; " : ""); - } - - if (strcasecmp(password_connect, password_receive)) - { - if (!password_receive_authmethod) - { - /* Prompt user ? */ - config_warn("Link block '%s' has a different connect/receive password. " - "This is no longer supported in UnrealIRCd 4.x", - ce->ce_vardata); - - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), - "\tpassword \"%s\"; /* WARNING: password changed due to 4.x upgrade */\n", - options_autoconnect ? password_connect : password_receive); - } else - { - /* sslcertificate or sslcertficatefp */ - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), - "\tpassword \"%s\" { %s; };\n", - password_receive, - password_receive_authmethod); - } - } else { - /* identical connect & receive passwords. easy. */ - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), - "\tpassword \"%s\";\n", password_receive); - } - - if (hub) - { - if (strcmp(hub, "*")) // only if it's something other than *, as * is the default anyway.. - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\thub %s;\n", hub); - } else - if (leaf) - { - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tleaf %s;\n", leaf); - if (leaf_depth) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tleaf-depth %d;\n", leaf_depth); - } - - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tclass %s;\n", class); - - if (ciphers) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tciphers %s;\n", ciphers); - - if (options_quarantine) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\toptions { quarantine; };\n"); - - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "};\n"); /* end */ - - replace_section(ce, buf); - - config_status("- link block '%s' upgraded", ce->ce_vardata); - return 1; -} - -/** oper::from::userhost becomes oper::mask & vhost::from::userhost becomes vhost::mask */ -#define MAXFROMENTRIES 100 -int upgrade_from_subblock(ConfigEntry *ce) -{ - ConfigEntry *cep; - char *list[MAXFROMENTRIES]; - int listcnt = 0; - - memset(list, 0, sizeof(list)); - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!cep->ce_vardata) - continue; - else if (!strcmp(cep->ce_varname, "userhost")) - { - if (listcnt == MAXFROMENTRIES) - break; // no room, sorry. - list[listcnt++] = cep->ce_vardata; - } - } - - if (listcnt == 0) - return 0; /* invalid block. strange. */ - - if (listcnt == 1) - { - char *m = !strncmp(list[0], "*@", 2) ? list[0]+2 : list[0]; /* skip or don't skip the user@ part */ - snprintf(buf, sizeof(buf), "mask %s;\n", m); - } else - { - /* Multiple (list of masks) */ - int i; - snprintf(buf, sizeof(buf), "mask {\n"); - - for (i=0; i < listcnt; i++) - { - char *m = !strncmp(list[i], "*@", 2) ? list[i]+2 : list[i]; /* skip or don't skip the user@ part */ - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\t%s;\n", m); - } - - strlcat(buf, "\t};\n", sizeof(buf)); - } - - replace_section(ce, buf); - - config_status("- %s::from::userhost sub-block upgraded", ce->ce_prevlevel ? ce->ce_prevlevel->ce_varname : "???"); - return 1; -} - -int upgrade_loadmodule(ConfigEntry *ce) -{ - char *file = ce->ce_vardata; - char tmp[512], *p, *newfile; - - if (!file) - return 0; - - if (our_strcasestr(file, "commands.dll") || our_strcasestr(file, "/commands.so")) - { - snprintf(buf, sizeof(buf), "include \"modules.default.conf\";\n"); - needs_modules_default_conf = 0; - if (needs_operclass_default_conf) - { - /* This is a nice place :) */ - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "include \"operclass.default.conf\";\n"); - needs_operclass_default_conf = 0; - } - replace_section(ce, buf); - config_status("- loadmodule for '%s' replaced with an include \"modules.default.conf\"", file); - return 1; - } - - if (our_strcasestr(file, "cloak.dll") || our_strcasestr(file, "/cloak.so")) - { - replace_section(ce, "/* NOTE: cloaking module is included in modules.default.conf */"); - config_status("- loadmodule for '%s' removed as this is now in modules.default.conf", file); - return 1; - } - - /* All other loadmodule commands... */ - - strlcpy(tmp, file, sizeof(tmp)); - p = strstr(tmp, ".so"); - if (p) - *p = '\0'; - p = our_strcasestr(tmp, ".dll"); - if (p) - *p = '\0'; - - newfile = !strncmp(tmp, "src/", 4) ? tmp+4 : tmp; - - newfile = !strncmp(newfile, "modules/", 8) ? newfile+8 : newfile; - - if (!strcmp(newfile, file)) - return 0; /* no change */ - - snprintf(buf, sizeof(buf), "loadmodule \"%s\";\n", newfile); - replace_section(ce, buf); - config_status("- loadmodule line converted to new syntax"); - return 1; -} - -int upgrade_include(ConfigEntry *ce) -{ - char *file = ce->ce_vardata; - static int badwords_upgraded_already = 0; - - if (!file) - return 0; - - if (!strstr(file, "help/") && match_simple("help*.conf", file)) - { - snprintf(buf, sizeof(buf), "include \"help/%s\";\n", file); - replace_section(ce, buf); - config_status("- include for '%s' replaced with 'help/%s'", file, file); - return 1; - } - - if (!strcmp("badwords.quit.conf", file)) - { - *buf = '\0'; - replace_section(ce, buf); - config_status("- include for '%s' removed (now in badwords.conf)", file); - return 1; - } - - if (match_simple("badwords.*.conf", file)) - { - if (badwords_upgraded_already) - { - *buf = '\0'; - config_status("- include for '%s' removed (now in badwords.conf)", file); - } else { - strcpy(buf, "/* all badwords are now in badwords.conf */\ninclude \"badwords.conf\";\n"); - badwords_upgraded_already = 1; - config_status("- include for '%s' replaced with 'badwords.conf'", file); - } - replace_section(ce, buf); - return 1; - } - - return 0; -} - -#define MAXSPFTARGETS 32 -int upgrade_spamfilter_block(ConfigEntry *ce) -{ - ConfigEntry *cep, *cepp; - char *reason = NULL; - char *regex = NULL; - char *action = NULL; - char *ban_time = NULL; - char *target[MAXSPFTARGETS]; - char targets[512]; - int targetcnt = 0; - char *match_type = NULL; - char *p; - - memset(target, 0, sizeof(target)); - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "match") || !strcmp(cep->ce_varname, "match-type")) - return 0; /* no upgrade needed */ - else if (!strcmp(cep->ce_varname, "target")) - { - if (cep->ce_vardata) - { - target[0] = cep->ce_vardata; - } - else if (cep->ce_entries) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (targetcnt == MAXSPFTARGETS) - break; - target[targetcnt++] = cepp->ce_varname; - } - } - } - else if (!cep->ce_vardata) - continue; /* invalid */ - else if (!strcmp(cep->ce_varname, "regex")) - { - regex = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "action")) - { - action = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "reason")) - { - reason = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "ban-time")) - { - ban_time = cep->ce_vardata; - } - } - - if (!regex || !target[0] || !action) - return 0; /* invalid spamfilter block */ - - /* build target(s) list */ - if (targetcnt > 1) - { - int i; - - strlcpy(targets, "{ ", sizeof(targets)); - - for (i=0; i < targetcnt; i++) - { - snprintf(targets+strlen(targets), sizeof(targets)-strlen(targets), - "%s; ", target[i]); - } - strlcat(targets, "}", sizeof(target)); - } else { - strlcpy(targets, target[0], sizeof(targets)); - } - - /* Determine match-type, fallback to 'posix' (=3.2.x regex engine) */ - - match_type = "simple"; - for (p = regex; *p; p++) - if (!isalnum(*p) && !isspace(*p)) - { - match_type = "posix"; - break; - } - - snprintf(buf, sizeof(buf), "spamfilter {\n" - "\tmatch-type %s;\n" - "\tmatch \"%s\";\n" - "\ttarget %s;\n" - "\taction %s;\n", - match_type, - unreal_add_quotes(regex), - targets, - action); - - /* optional: reason */ - if (reason) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\treason \"%s\";\n", unreal_add_quotes(reason)); - - /* optional: ban-time */ - if (ban_time) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tban-time \"%s\";\n", ban_time); - - strlcat(buf, "};\n", sizeof(buf)); - - replace_section(ce, buf); - config_status("- spamfilter block converted to new syntax"); - return 1; -} - -#define MAXOPTIONS 32 -int upgrade_allow_block(ConfigEntry *ce) -{ - ConfigEntry *cep, *cepp; - char *hostname = NULL; - char *ip = NULL; - char *maxperip = NULL; - char *ipv6_clone_mask = NULL; - char *password = NULL; - char *password_type = NULL; - char *class = NULL; - char *redirect_server = NULL; - int redirect_port = 0; - char *options[MAXOPTIONS]; - int optionscnt = 0; - char options_str[512], comment[512]; - - memset(options, 0, sizeof(options)); - *comment = *options_str = '\0'; - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "options")) - { - if (cep->ce_vardata) - { - options[0] = cep->ce_vardata; - optionscnt = 1; - } - else if (cep->ce_entries) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (optionscnt == MAXOPTIONS) - break; - options[optionscnt++] = cepp->ce_varname; - } - } - } - else if (!cep->ce_vardata) - continue; /* invalid */ - else if (!strcmp(cep->ce_varname, "hostname")) - { - hostname = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "ip")) - { - ip = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "maxperip")) - { - maxperip = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "ipv6-clone-mask")) - { - ipv6_clone_mask = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "password")) - { - password = cep->ce_vardata; - if (cep->ce_entries) - password_type = cep->ce_entries->ce_varname; - } - else if (!strcmp(cep->ce_varname, "class")) - { - class = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "redirect-server")) - { - redirect_server = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "redirect-port")) - { - redirect_port = atoi(cep->ce_vardata); - } - } - - if (!ip || !hostname || !class) - return 0; /* missing 3.2.x items in allow block, upgraded already! (or invalid) */ - - /* build target(s) list */ - if (optionscnt == 0) - { - *options_str = '\0'; - } - else - { - int i; - - for (i=0; i < optionscnt; i++) - { - snprintf(options_str+strlen(options_str), sizeof(options_str)-strlen(options_str), - "%s; ", options[i]); - } - } - - /* drop either 'ip' or 'hostname' */ - if (!strcmp(ip, "*@*") && !strcmp(hostname, "*@*")) - hostname = NULL; /* just ip */ - else if (strstr(ip, "NOMATCH")) - ip = NULL; - else if (strstr(hostname, "NOMATCH")) - hostname = NULL; - else if (!strchr(hostname, '.') && strcmp(hostname, "localhost")) - hostname = NULL; - else if (!strchr(ip, '.')) - ip = NULL; - else - { - /* very rare case -- let's bet on IP */ - snprintf(comment, sizeof(comment), "/* CHANGED BY 3.2.x TO 4.x CONF UPGRADE!! Was: ip %s; hostname %s; */\n", ip, hostname); - hostname = NULL; - } - - snprintf(buf, sizeof(buf), "allow {\n"); - - if (*comment) - strlcat(buf, comment, sizeof(buf)); - - if (ip) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tip \"%s\";\n", ip); - - if (hostname) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\thostname \"%s\";\n", hostname); - - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tclass %s;\n", class); - - /* maxperip: optional in 3.2.x, mandatory in 4.x */ - if (maxperip) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tmaxperip %s;\n", maxperip); - else - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tmaxperip 3; /* CHANGED BY 3.2.x TO 4.x CONF UPGRADE! */\n"); - - if (ipv6_clone_mask) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tipv6-clone-mask %s;\n", ipv6_clone_mask); - - if (password) - { - if (password_type) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tpassword \"%s\" { %s; };\n", password, password_type); - else - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tpassword \"%s\";\n", password); - } - - if (redirect_server) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tredirect-server %s;\n", redirect_server); - - if (redirect_port) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tredirect-port %d;\n", redirect_port); - - if (*options_str) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\toptions { %s};\n", options_str); - - strlcat(buf, "};\n", sizeof(buf)); - - replace_section(ce, buf); - config_status("- allow block converted to new syntax"); - return 1; -} - -/* Pick out the ip address and the port number from a string. - * The string syntax is: ip:port. ip must be enclosed in brackets ([]) if its an ipv6 - * address because they contain colon (:) separators. The ip part is optional. If the string - * contains a single number its assumed to be a port number. - * - * Returns with ip pointing to the ip address (if one was specified), a "*" (if only a port - * was specified), or an empty string if there was an error. port is returned pointing to the - * port number if one was specified, otherwise it points to a empty string. - */ -void ipport_separate(char *string, char **ip, char **port) -{ - char *f; - - /* assume failure */ - *ip = *port = ""; - - /* sanity check */ - if (string && strlen(string) > 0) - { - /* handle ipv6 type of ip address */ - if (*string == '[') - { - if ((f = strrchr(string, ']'))) - { - *ip = string + 1; /* skip [ */ - *f = '\0'; /* terminate the ip string */ - /* next char must be a : if a port was specified */ - if (*++f == ':') - { - *port = ++f; - } - } - } - /* handle ipv4 and port */ - else if ((f = strchr(string, ':'))) - { - /* we found a colon... we may have ip:port or just :port */ - if (f == string) - { - /* we have just :port */ - *ip = "*"; - } - else - { - /* we have ip:port */ - *ip = string; - *f = '\0'; - } - *port = ++f; - } - /* no ip was specified, just a port number */ - else if (!strcmp(string, my_itoa(atoi(string)))) - { - *ip = "*"; - *port = string; - } - } -} - -int upgrade_listen_block(ConfigEntry *ce) -{ - ConfigEntry *cep, *cepp; - char *ip = NULL; - char *port = NULL; - char *options[MAXOPTIONS]; - int optionscnt = 0; - char options_str[512]; - char copy[128]; - - memset(options, 0, sizeof(options)); - *options_str = '\0'; - - if (!ce->ce_vardata) - return 0; /* already upgraded */ - - strlcpy(copy, ce->ce_vardata, sizeof(copy)); - ipport_separate(copy, &ip, &port); - if (!ip || !*ip || !port || !*port) - return 0; /* invalid conf */ - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "options")) - { - if (cep->ce_entries) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (optionscnt == MAXOPTIONS) - break; - options[optionscnt++] = cepp->ce_varname; - } - } - } - } - - /* build options list */ - if (optionscnt == 0) - { - *options_str = '\0'; - } - else - { - int i; - - for (i=0; i < optionscnt; i++) - { - snprintf(options_str+strlen(options_str), sizeof(options_str)-strlen(options_str), - "%s; ", options[i]); - } - } - - snprintf(buf, sizeof(buf), "listen {\n" - "\tip %s;\n" - "\tport %s;\n", - ip, - port); - - if (*options_str) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\toptions { %s};\n", options_str); - - strlcat(buf, "};\n", sizeof(buf)); - - replace_section(ce, buf); - config_status("- listen block converted to new syntax"); - return 1; -} - -int upgrade_cgiirc_block(ConfigEntry *ce) -{ - ConfigEntry *cep; - char *type = NULL; - char *username = NULL; - char *hostname = NULL; - char *password = NULL, *password_type = NULL; - char mask[USERLEN+HOSTLEN+8]; - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!cep->ce_vardata) - { - config_error_empty(cep->ce_fileptr->cf_filename, cep->ce_varlinenum, - "cgiirc", cep->ce_varname); - return 0; - } - else if (!strcmp(cep->ce_varname, "type")) - type = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "username")) - username = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "hostname")) - hostname = cep->ce_vardata; - else if (!strcmp(cep->ce_varname, "password")) - { - password = cep->ce_vardata; - if (cep->ce_entries) - password_type = cep->ce_entries->ce_varname; - } - } - - if (!type || !hostname) - { - /* Invalid block as it does not contain the 3.2.x mandatory items */ - return 0; - } - - if (username) - snprintf(mask, sizeof(mask), "%s@%s", username, hostname); - else - strlcpy(mask, hostname, sizeof(mask)); - - if (!strcmp(type, "old")) - { - snprintf(buf, sizeof(buf), - "webirc {\n" - "\ttype old;\n" - "\tmask %s;\n", - mask); - } else - { - if (password_type) - { - snprintf(buf, sizeof(buf), - "webirc {\n" - "\tmask %s;\n" - "\tpassword \"%s\" { %s; };\n" - "};\n", - mask, - password, - password_type); - } else - { - snprintf(buf, sizeof(buf), - "webirc {\n" - "\tmask %s;\n" - "\tpassword \"%s\";\n" - "};\n", - mask, - password); - } - } - - replace_section(ce, buf); - - config_status("- cgiirc block upgraded and renamed to webirc"); - return 1; -} - -int contains_flag(char **flags, int flagscnt, char *needle) -{ - int i; - - for (i = 0; i < flagscnt; i++) - if (!strcmp(flags[i], needle)) - return 1; - - return 0; -} - -int upgrade_oper_block(ConfigEntry *ce) -{ - ConfigEntry *cep, *cepp; - char *name = NULL; - char *password = NULL; - char *password_type = NULL; - char *require_modes = NULL; - char *class = NULL; - char *flags[MAXOPTIONS]; - int flagscnt = 0; - char *swhois = NULL; - char *snomask = NULL; - char *modes = NULL; - int maxlogins = -1; - char *fromlist[MAXFROMENTRIES]; - int fromlistcnt = 0; - char maskbuf[1024]; - char *operclass = NULL; /* set by us, not read from conf */ - char *vhost = NULL; /* set by us, not read from conf */ - int i; - char silly[64]; - - memset(flags, 0, sizeof(flags)); - *maskbuf = '\0'; - - name = ce->ce_vardata; - - if (!name) - return 0; /* oper block without a name = invalid */ - - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "operclass")) - return 0; /* already 4.x conf */ - else if (!strcmp(cep->ce_varname, "flags")) - { - if (cep->ce_vardata) /* short options (flag letters) */ - { - char *p; - for (p = cep->ce_vardata; *p; p++) - { - if (flagscnt == MAXOPTIONS) - break; - for (i = 0; FlagMappingTable[i].shortflag; i++) - { - if (FlagMappingTable[i].shortflag == *p) - { - flags[flagscnt] = FlagMappingTable[i].longflag; - flagscnt++; - break; - } - } - } - } - else if (cep->ce_entries) /* long options (flags written out) */ - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (flagscnt == MAXOPTIONS) - break; - flags[flagscnt++] = cepp->ce_varname; - } - } - } - else if (!strcmp(cep->ce_varname, "from")) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (!strcmp(cepp->ce_varname, "userhost") && cepp->ce_vardata) - { - if (fromlistcnt == MAXFROMENTRIES) - break; // no room, sorry. - fromlist[fromlistcnt++] = cepp->ce_vardata; - } - } - } - else if (!strcmp(cep->ce_varname, "mask")) - { - /* processing mask here means we can also upgrade 3.4-alphaX oper blocks.. */ - if (cep->ce_vardata) - { - if (fromlistcnt == MAXFROMENTRIES) - break; // no room, sorry. - fromlist[fromlistcnt++] = cep->ce_vardata; - } else - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (fromlistcnt == MAXFROMENTRIES) - break; // no room, sorry. - fromlist[fromlistcnt++] = cepp->ce_varname; - } - } - } - else if (!cep->ce_vardata) - continue; /* invalid */ - else if (!strcmp(cep->ce_varname, "password")) - { - password = cep->ce_vardata; - if (cep->ce_entries) - password_type = cep->ce_entries->ce_varname; - } - else if (!strcmp(cep->ce_varname, "require-modes")) - { - require_modes = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "class")) - { - class = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "swhois")) - { - swhois = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "snomasks")) - { - snomask = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "modes")) - { - modes = cep->ce_vardata; - } - else if (!strcmp(cep->ce_varname, "maxlogins")) - { - maxlogins = atoi(cep->ce_vardata); - } - } - - if ((fromlistcnt == 0) || !password || !class) - return 0; /* missing 3.2.x items in allow block (invalid or upgraded already) */ - - /* build oper::mask list in 'maskbuf' (includes variable name) */ - if (fromlistcnt == 1) - { - char *m = !strncmp(fromlist[0], "*@", 2) ? fromlist[0]+2 : fromlist[0]; /* skip or don't skip the user@ part */ - snprintf(maskbuf, sizeof(maskbuf), "mask %s;\n", m); - } else - { - /* Multiple (list of masks) */ - int i; - snprintf(maskbuf, sizeof(maskbuf), "mask {\n"); - - for (i=0; i < fromlistcnt; i++) - { - char *m = !strncmp(fromlist[i], "*@", 2) ? fromlist[i]+2 : fromlist[i]; /* skip or don't skip the user@ part */ - snprintf(maskbuf+strlen(maskbuf), sizeof(maskbuf)-strlen(maskbuf), "\t%s;\n", m); - } - strlcat(maskbuf, "\t};\n", sizeof(maskbuf)); - } - - /* Figure out which default operclass looks most suitable (="find highest rank") */ - if (contains_flag(flags, flagscnt, "netadmin")) - operclass = "netadmin"; - else if (contains_flag(flags, flagscnt, "services-admin")) - operclass = "services-admin"; - else if (contains_flag(flags, flagscnt, "coadmin")) - operclass = "coadmin"; - else if (contains_flag(flags, flagscnt, "admin")) - operclass = "admin"; - else if (contains_flag(flags, flagscnt, "global")) - operclass = "globop"; - else if (contains_flag(flags, flagscnt, "local")) - operclass = "locop"; - else - { - /* Hmm :) */ - config_status("WARNING: I have trouble converting oper block '%s' to the new system. " - "I made it use the locop operclass. Feel free to change", name); - operclass = "locop"; - } - - if (contains_flag(flags, flagscnt, "get_host") || upgrade.host_on_oper_up) - { - if (!strcmp(operclass, "netadmin")) - vhost = upgrade.netadmin_host; - else if (!strcmp(operclass, "services-admin")) - vhost = upgrade.sadmin_host; - else if (!strcmp(operclass, "coadmin")) - vhost = upgrade.coadmin_host; - else if (!strcmp(operclass, "admin")) - vhost = upgrade.admin_host; - else if (!strcmp(operclass, "globop")) - vhost = upgrade.oper_host; - else if (!strcmp(operclass, "locop")) - vhost = upgrade.locop_host; - } - - /* If no swhois is set, then set a title. Just because people are used to it. */ - if (!swhois) - { - if (!strcmp(operclass, "netadmin")) - swhois = "is a Network Administrator"; - else if (!strcmp(operclass, "services-admin")) - swhois = "is a Services Administrator"; - else if (!strcmp(operclass, "coadmin")) - swhois = "is a Co Administrator"; - else if (!strcmp(operclass, "admin")) - swhois = "is a Server Administrator"; - } - - /* The 'coadmin' operclass is actually 'admin'. There's no difference in privileges. */ - if (!strcmp(operclass, "coadmin")) - operclass = "admin"; - - /* convert globop and above w/override to operclassname-with-override */ - if (contains_flag(flags, flagscnt, "can_override") && strcmp(operclass, "locop")) - { - snprintf(silly, sizeof(silly), "%s-with-override", operclass); - operclass = silly; - } - - /* Ok, we got everything we need. Now we will write out the actual new oper block! */ - - /* oper block header & oper::mask */ - snprintf(buf, sizeof(buf), "oper %s {\n" - "\t%s", - name, - maskbuf); - - /* oper::password */ - if (password_type) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tpassword \"%s\" { %s; };\n", password, password_type); - else - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tpassword \"%s\";\n", password); - - /* oper::require-modes */ - if (require_modes) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\trequire-modes \"%s\";\n", require_modes); - - /* oper::maxlogins */ - if (maxlogins != -1) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tmaxlogins %d;\n", maxlogins); - - /* oper::class */ - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tclass %s;\n", class); - - /* oper::operclass */ - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\toperclass %s;\n", operclass); - - /* oper::modes */ - if (modes) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tmodes \"%s\";\n", modes); - - /* oper::snomask */ - if (snomask) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tsnomask \"%s\";\n", snomask); - - /* oper::vhost */ - if (vhost) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tvhost \"%s\";\n", vhost); - - /* oper::swhois */ - if (swhois) - snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\tswhois \"%s\";\n", swhois); - - strlcat(buf, "};\n", sizeof(buf)); - - replace_section(ce, buf); - config_status("- oper block (%s) converted to new syntax", name); - return 1; -} - -void update_read_settings(char *cfgfile) -{ - ConfigFile *cf = NULL; - ConfigEntry *ce = NULL, *cep, *cepp; - - cf = config_load(cfgfile, NULL); - if (!cf) - return; - - if (strstr(cfgfile, "modules.default.conf")) - needs_modules_default_conf = 0; - else if (strstr(cfgfile, "operclass.default.conf")) - needs_operclass_default_conf = 0; - - /* This needs to be read early, as the rest may depend on it */ - for (ce = cf->cf_entries; ce; ce = ce->ce_next) - { - if (!strcmp(ce->ce_varname, "set")) - { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "hosts")) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (!cepp->ce_vardata) - continue; - if (!strcmp(cepp->ce_varname, "local")) { - safe_strdup(upgrade.locop_host, cepp->ce_vardata); - } - else if (!strcmp(cepp->ce_varname, "global")) { - safe_strdup(upgrade.oper_host, cepp->ce_vardata); - } - else if (!strcmp(cepp->ce_varname, "coadmin")) { - safe_strdup(upgrade.coadmin_host, cepp->ce_vardata); - } - else if (!strcmp(cepp->ce_varname, "admin")) { - safe_strdup(upgrade.admin_host, cepp->ce_vardata); - } - else if (!strcmp(cepp->ce_varname, "servicesadmin")) { - safe_strdup(upgrade.sadmin_host, cepp->ce_vardata); - } - else if (!strcmp(cepp->ce_varname, "netadmin")) { - safe_strdup(upgrade.netadmin_host, cepp->ce_vardata); - } - else if (!strcmp(cepp->ce_varname, "host-on-oper-up")) { - upgrade.host_on_oper_up = config_checkval(cepp->ce_vardata,CFG_YESNO); - } - } - } - } - } - } - - config_free(cf); -} - - -int update_conf_file(void) -{ - ConfigFile *cf = NULL; - ConfigEntry *ce = NULL, *cep, *cepp; - int update_conf_runs = 0; - -again: - if (update_conf_runs++ > 100) - { - config_error("update conf re-run overflow. whoops! upgrade failed! sorry!"); - return 0; - } - - if (cf) - { - config_free(cf); - cf = NULL; - } - - cf = config_load(configfiletmp, NULL); - if (!cf) - { - config_error("could not load configuration file '%s'", configfile); - return 0; - } - - for (ce = cf->cf_entries; ce; ce = ce->ce_next) - { - /*printf("%s%s%s\n", - ce->ce_varname, - ce->ce_vardata ? " " : "", - ce->ce_vardata ? ce->ce_vardata : ""); */ - - if (!strcmp(ce->ce_varname, "loadmodule")) - { - if (upgrade_loadmodule(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "include")) - { - if (upgrade_include(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "me")) - { - if (upgrade_me_block(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "link")) - { - if (upgrade_link_block(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "oper")) - { - if (upgrade_oper_block(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "vhost")) - { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "from")) - { - if (upgrade_from_subblock(cep)) - goto again; - } - } - } - if (!strcmp(ce->ce_varname, "spamfilter")) - { - if (upgrade_spamfilter_block(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "allow") && !ce->ce_vardata) /* 'allow' block for clients, not 'allow channel' etc.. */ - { - if (upgrade_allow_block(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "listen")) - { - if (upgrade_listen_block(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "cgiirc")) - { - if (upgrade_cgiirc_block(ce)) - goto again; - } - if (!strcmp(ce->ce_varname, "set")) - { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "throttle")) - { - int n = 0, t = 0; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (!cepp->ce_vardata) - continue; - if (!strcmp(cepp->ce_varname, "period")) - t = config_checkval(cepp->ce_vardata, CFG_TIME); - else if (!strcmp(cepp->ce_varname, "connections")) - n = atoi(cepp->ce_vardata); - } - - remove_section(cep->ce_fileposstart, cep->ce_fileposend); - snprintf(buf, sizeof(buf), "anti-flood { connect-flood %d:%d; };\n", - n, t); - - insert_section(cep->ce_fileposstart, buf); - goto again; - } else - if (!strcmp(cep->ce_varname, "hosts")) - { - config_status("- removed set::hosts. we now use oper::vhost for this."); - remove_section(cep->ce_fileposstart, cep->ce_fileposend); /* hmm something is wrong here */ - goto again; - } else - if (!strcmp(cep->ce_varname, "dns")) - { - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - if (!strcmp(cepp->ce_varname, "nameserver") || - !strcmp(cepp->ce_varname, "timeout") || - !strcmp(cepp->ce_varname, "retries")) - { - config_status("- removed set::dns::%s. this option is never used.", cepp->ce_varname); - remove_section(cepp->ce_fileposstart, cepp->ce_fileposend); - goto again; - } - } - } - } - - } - - if (cf) - config_free(cf); - - return (update_conf_runs > 1) ? 1 : 0; -} - -static int already_included(char *fname, ConfigFile *cf) -{ - for (; cf; cf = cf->cf_next) - if (!strcmp(cf->cf_filename, fname)) - return 1; - - return 0; -} - -static void add_include_list(char *fname, ConfigFile **cf) -{ - ConfigFile *n = safe_alloc(sizeof(ConfigFile)); - -// config_status("INCLUDE: %s", fname); - safe_strdup(n->cf_filename, fname); - n->cf_next = *cf; - *cf = n; -} - -void build_include_list_ex(char *fname, ConfigFile **cf_list) -{ - ConfigFile *cf; - ConfigEntry *ce; - - if (strstr(fname, "://")) - return; /* Remote include - ignored */ - - add_include_list(fname, cf_list); - - cf = config_load(fname, NULL); - if (!cf) - return; - - for (ce = cf->cf_entries; ce; ce = ce->ce_next) - if (!strcmp(ce->ce_varname, "include")) - { - if ((ce->ce_vardata[0] != '/') && (ce->ce_vardata[0] != '\\') && strcmp(ce->ce_vardata, CPATH)) - { - char *str = safe_alloc(strlen(ce->ce_vardata) + strlen(CONFDIR) + 4); - sprintf(str, "%s/%s", CONFDIR, ce->ce_vardata); - safe_free(ce->ce_vardata); - ce->ce_vardata = str; - } - if (!already_included(ce->ce_vardata, *cf_list)) - build_include_list_ex(ce->ce_vardata, cf_list); - } - - config_free(cf); -} - -ConfigFile *build_include_list(char *fname) -{ - ConfigFile *cf_list = NULL; - - build_include_list_ex(fname, &cf_list); - return cf_list; -} - -void update_conf(void) -{ - ConfigFile *files; - ConfigFile *cf; - char *mainconf = configfile; - int upgraded_files = 0; - char answerbuf[128], *answer; - - config_status("You have requested to upgrade your configuration files."); - config_status("If you are upgrading from 4.x to 5.x then DO NOT run this script. This script does NOT update config files from 4.x -> 5.x."); - config_status("UnrealIRCd 4.2.x configuration files should work OK on 5.x, with only some warnings printed when you boot the IRCd."); - config_status("See https://www.unrealircd.org/docs/Upgrading_from_4.x#Configuration_changes"); - config_status("This upgrade-conf script is only useful if you are upgrading from 3.2.x."); - config_status(""); -#ifndef _WIN32 - do - { - printf("Continue upgrading 3.2.x to 4.x configuration file format? (Y/N): "); - *answerbuf = '\0'; - answer = fgets(answerbuf, sizeof(answerbuf), stdin); - if (answer && (toupper(*answer) == 'N')) - { - printf("Configuration unchanged.\n"); - return; - } - if (answer && (toupper(*answer) == 'Y')) - { - break; - } - printf("Invalid response. Please enter either Y or N\n\n"); - } while(1); -#endif - - strlcpy(me.name, "", sizeof(me.name)); - memset(&upgrade, 0, sizeof(upgrade)); - - files = build_include_list(mainconf); - - /* We need to read some original settings first, before we touch anything... */ - for (cf = files; cf; cf = cf->cf_next) - { - update_read_settings(cf->cf_filename); - } - - /* Now go upgrade... */ - for (cf = files; cf; cf = cf->cf_next) - { - if (!file_exists(cf->cf_filename)) - continue; /* skip silently. errors were already shown earlier by build_include_list anyway. */ - configfile = cf->cf_filename; - config_status("Checking '%s'...", cf->cf_filename); - snprintf(configfiletmp, sizeof(configfiletmp), "%s.tmp", configfile); - unlink(configfiletmp); - if (!unreal_copyfileex(configfile, configfiletmp, 0)) - { - config_error("Could not create temp file for processing!"); - die(); - } - if (update_conf_file()) - { - char buf[512]; - snprintf(buf, sizeof(buf), "%s.old", configfile); - if (file_exists(buf)) - { - int i; - for (i=0; i<100; i++) - { - snprintf(buf, sizeof(buf), "%s.old.%d", configfile, i); - if (!file_exists(buf)) - break; - } - } - /* rename original config file to ... */ - if (rename(configfile, buf) < 0) - { - config_error("Could not rename original conf '%s' to '%s'", configfile, buf); - die(); - } - - /* Rename converted conf to config file */ -#ifdef _WIN32 - /* "If newpath already exists it will be atomically replaced".. - * well.. not on Windows! Error: "File exists"... - */ - unlink(configfile); -#endif - if (rename(configfiletmp, configfile) < 0) - { - config_error("Could not rename converted configuration file '%s' to '%s' -- please rename this file yourself!", - configfiletmp, configfile); - die(); - } - - config_status("File '%s' upgrade complete.", configfile); - upgraded_files++; - } else { - unlink(configfiletmp); - config_status("File '%s' left unchanged (no upgrade necessary)", configfile); - } - } - configfile = mainconf; /* restore */ - - if (needs_operclass_default_conf) - { - /* There's a slight chance we never added this include, and you get mysterious - * oper permissions errors if you try to use such an operclass and it's missing. - */ - FILE *fd = fopen(mainconf, "a"); - if (fd) - { - fprintf(fd, "\ninclude \"operclass.default.conf\";\n"); - fclose(fd); - config_status("Oh wait, %s needs an include for operclass.default.conf. Added.", mainconf); - upgraded_files++; - } - } - - if (upgraded_files > 0) - { - config_status(""); - config_status("%d configuration file(s) upgraded. You can now boot UnrealIRCd with your freshly converted conf's!", upgraded_files); - config_status("You should probably take a look at the converted configuration files now or at a later time."); - config_status("See also https://www.unrealircd.org/docs/Upgrading_from_3.2.x and the sections in there (eg: Oper block)"); - config_status(""); - } else { - config_status(""); - config_status("No configuration files were changed. No upgrade was needed. If this is incorrect then please report on https://bugs.unrealircd.org/ !"); - config_status(""); - } -} - diff --git a/src/url.c b/src/url_curl.c similarity index 51% rename from src/url.c rename to src/url_curl.c index 8535651..7ff7cdb 100644 --- a/src/url.c +++ b/src/url_curl.c @@ -1,6 +1,7 @@ /* * Unreal Internet Relay Chat Daemon, src/url.c * (C) 2003 Dominick Meglio and the UnrealIRCd Team + * (C) 2004-2021 Bram Matthys * (C) 2012 William Pitcock * * This program is free software; you can redistribute it and/or modify @@ -19,110 +20,35 @@ */ #include "unrealircd.h" +#include "dns.h" -extern char *SSLKeyPasswd; - -#ifndef _WIN32 -extern uid_t irc_uid; -extern gid_t irc_gid; -#endif - -CURLM *multihandle; +extern char *TLSKeyPasswd; /* Stores information about the async transfer. * Used to maintain information about the transfer * to trigger the callback upon completion. */ -typedef struct +typedef struct Download Download; + +struct Download { vFP callback; void *callback_data; - FILE *fd; + FILE *file_fd; /**< File open for writing (otherwise NULL) */ char filename[PATH_MAX]; char *url; /*< must be free()d by url_do_transfers_async() */ char errorbuf[CURL_ERROR_SIZE]; time_t cachetime; -} FileHandle; +}; -/* - * Determines if the given string is a valid URL. Since libcurl - * supports telnet, ldap, and dict such strings are treated as - * invalid URLs here since we don't want them supported in - * unreal. - */ -int url_is_valid(const char *string) +CURLM *multihandle = NULL; + +void url_free_handle(Download *handle) { - if (strstr(string, "telnet://") == string || - strstr(string, "ldap://") == string || - strstr(string, "dict://") == string) - { - return 0; - } - return (strstr(string, "://") != NULL); -} - -/** A displayable URL for in error messages and such. - * This leaves out any authentication information (user:pass) - * the URL may contain. - */ -const char *displayurl(const char *url) -{ - static char buf[512]; - char *proto, *rest; - - /* protocol://user:pass@host/etc.. */ - rest = strchr(url, '@'); - - if (!rest) - return url; /* contains no auth information */ - - rest++; /* now points to the rest (remainder) of the URL */ - - proto = strstr(url, "://"); - if (!proto || (proto > rest) || (proto == url)) - return url; /* incorrectly formatted, just show entire URL. */ - - /* funny, we don't ship strlncpy.. */ - *buf = '\0'; - strlncat(buf, url, sizeof(buf), proto - url); - strlcat(buf, "://***:***@", sizeof(buf)); - strlcat(buf, rest, sizeof(buf)); - - return buf; -} - -/* - * Returns the filename portion of the URL. The returned string - * is malloc()'ed and must be freed by the caller. If the specified - * URL does not contain a filename, a '-' is allocated and returned. - */ -char *url_getfilename(const char *url) -{ - const char *c, *start; - - if ((c = strstr(url, "://"))) - c += 3; - else - c = url; - - while (*c && *c != '/') - c++; - - if (*c == '/') - { - c++; - if (!*c || *c == '?') - return raw_strdup("-"); - start = c; - while (*c && *c != '?') - c++; - if (!*c) - return raw_strdup(start); - else - return raw_strldup(start, c-start+1); - - } - return raw_strdup("-"); + if (handle->file_fd) + fclose(handle->file_fd); + safe_free(handle->url); + safe_free(handle); } /* @@ -132,15 +58,15 @@ char *url_getfilename(const char *url) static void set_curl_tls_options(CURL *curl) { char buf[512]; - + #if 0 /* This would only be necessary if you use client certificates over HTTPS and such. * But this information is not known yet since the configuration file has not been * parsed yet at this point. */ curl_easy_setopt(curl, CURLOPT_SSLCERT, iConf.tls_options->certificate_file); - if (SSLKeyPasswd) - curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, SSLKeyPasswd); + if (TLSKeyPasswd) + curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, TLSKeyPasswd); curl_easy_setopt(curl, CURLOPT_SSLKEY, iConf.tls_options->key_file); #endif @@ -157,91 +83,6 @@ static size_t do_download(void *ptr, size_t size, size_t nmemb, void *stream) return fwrite(ptr, size, nmemb, (FILE *)stream); } -/* - * Handles synchronous downloading of a file. This function allows - * a download to be made transparently without the caller having any - * knowledge of how libcurl works. If the function succeeds, the - * filename the file was downloaded to is returned. Otherwise NULL - * is returned and the string pointed to by error contains the error - * message. The returned filename is malloc'ed and must be freed by - * the caller. - */ -char *download_file(const char *url, char **error) -{ - static char errorbuf[CURL_ERROR_SIZE]; - CURL *curl = curl_easy_init(); - CURLcode res; - char *file = url_getfilename(url); - char *filename = unreal_getfilename(file); - char *tmp = unreal_mktemp(TMPDIR, filename ? filename : "download.conf"); - FILE *fd; - - - if (!curl) - { - safe_free(file); - strlcpy(errorbuf, "curl_easy_init() failed", sizeof(errorbuf)); - *error = errorbuf; - return NULL; - } - - fd = fopen(tmp, "wb"); - if (!fd) - { - snprintf(errorbuf, CURL_ERROR_SIZE, "Cannot write to %s: %s", tmp, strerror(errno)); - safe_free(file); - *error = errorbuf; - return NULL; - } - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, fd); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, do_download); - curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); - curl_easy_setopt(curl, CURLOPT_FILETIME, 1); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 45); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); -#if LIBCURL_VERSION_NUM >= 0x070f01 - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1); -#endif - /* We need to set CURLOPT_FORBID_REUSE because otherwise libcurl does not - * notify us (or not in time) about FD close/opens, thus we end up closing and - * screwing up another innocent FD, like a listener (BAD!). In my view a bug, but - * mailing list archives seem to indicate curl devs have a different opinion - * on these matters... - * Actually I don't know for sure if this option alone fixes 100% of the cases - * but at least I can't crash my server anymore. - * As a side-effect we also fix useless CLOSE_WAIT connections. - */ - curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1); - - set_curl_tls_options(curl); - memset(errorbuf, 0, CURL_ERROR_SIZE); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorbuf); - res = curl_easy_perform(curl); - fclose(fd); - safe_free(file); - if (res == CURLE_OK) - { - long last_mod; - - curl_easy_getinfo(curl, CURLINFO_FILETIME, &last_mod); - curl_easy_cleanup(curl); - - if (last_mod != -1) - unreal_setfilemodtime(tmp, last_mod); - return raw_strdup(tmp); - } - else - { - curl_easy_cleanup(curl); - remove(tmp); - *error = errorbuf; - return NULL; - } -} - /* * Interface for new-style evented I/O. * @@ -260,7 +101,7 @@ static void url_check_multi_handles(void) { if (msg->msg == CURLMSG_DONE) { - FileHandle *handle; + Download *handle; long code; long last_mod; CURL *easyhand = msg->easy_handle; @@ -268,7 +109,8 @@ static void url_check_multi_handles(void) curl_easy_getinfo(easyhand, CURLINFO_RESPONSE_CODE, &code); curl_easy_getinfo(easyhand, CURLINFO_PRIVATE, (char **) &handle); curl_easy_getinfo(easyhand, CURLINFO_FILETIME, &last_mod); - fclose(handle->fd); + fclose(handle->file_fd); + handle->file_fd = NULL; if (msg->data.result == CURLE_OK) { @@ -292,8 +134,7 @@ static void url_check_multi_handles(void) remove(handle->filename); } - safe_free(handle->url); - safe_free(handle); + url_free_handle(handle); curl_multi_remove_handle(multihandle, easyhand); /* NOTE: after curl_multi_remove_handle() you cannot use @@ -321,22 +162,22 @@ static void url_socket_pollcb(int fd, int revents, void *data) static int url_socket_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { - Debug((DEBUG_DEBUG, "url_socket_cb: %d (%s)", (int)s, (what == CURL_POLL_REMOVE)?"remove":"add-or-modify")); if (what == CURL_POLL_REMOVE) { - /* Socket is going to be closed *BY CURL*.. so don't call fd_close() but fd_unmap(). - * Otherwise we (or actually, they) may end up closing the wrong fd. - */ - fd_unmap(s); + fd_close(s); } else { FDEntry *fde = &fd_table[s]; int flags = 0; - + if (!fde->is_open) { - fd_open(s, "CURL transfer"); + /* NOTE: We use FDCLOSE_NONE here because cURL will take + * care of the closing of the socket. So *WE* must never + * close the socket ourselves. + */ + fd_open(s, "CURL transfer", FDCLOSE_NONE); } if (what == CURL_POLL_IN || what == CURL_POLL_INOUT) @@ -352,7 +193,7 @@ static int url_socket_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *so } /* Handle timeouts. */ -static EVENT(curl_socket_timeout) +EVENT(url_socket_timeout) { int dummy; @@ -360,7 +201,7 @@ static EVENT(curl_socket_timeout) url_check_multi_handles(); } -static Event *curl_socket_timeout_hdl = NULL; +static Event *url_socket_timeout_hdl = NULL; /* * Initializes the URL system @@ -371,7 +212,7 @@ void url_init(void) multihandle = curl_multi_init(); curl_multi_setopt(multihandle, CURLMOPT_SOCKETFUNCTION, url_socket_cb); - curl_socket_timeout_hdl = EventAdd(NULL, "curl_socket_timeout", curl_socket_timeout, NULL, 500, 0); + url_socket_timeout_hdl = EventAdd(NULL, "url_socket_timeout", url_socket_timeout, NULL, 500, 0); } /* @@ -395,64 +236,80 @@ void url_init(void) * than 10 seconds to happen and the config file can be rehashed * multiple times during that time. */ -void download_file_async(const char *url, time_t cachetime, vFP callback, void *callback_data) +void download_file_async(const char *url, time_t cachetime, vFP callback, void *callback_data, char *original_url, int maxredirects) { static char errorbuf[CURL_ERROR_SIZE]; - CURL *curl = curl_easy_init(); - if (curl) - { - char *file = url_getfilename(url); - char *filename = unreal_getfilename(file); - char *tmp = unreal_mktemp(TMPDIR, filename ? filename : "download.conf"); - FileHandle *handle = safe_alloc(sizeof(FileHandle)); - handle->fd = fopen(tmp, "wb"); - if (!handle->fd) - { - snprintf(errorbuf, sizeof(errorbuf), "Cannot create '%s': %s", tmp, strerror(ERRNO)); - callback(url, NULL, errorbuf, 0, callback_data); - safe_free(file); - safe_free(handle); - return; - } - handle->callback = callback; - handle->callback_data = callback_data; - handle->cachetime = cachetime; - safe_strdup(handle->url, url); - strlcpy(handle->filename, tmp, sizeof(handle->filename)); - safe_free(file); + char user_agent[256]; + CURL *curl; + char *file; + const char *filename; + char *tmp; + Download *handle; - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, do_download); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)handle->fd); - curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); - set_curl_tls_options(curl); - memset(handle->errorbuf, 0, CURL_ERROR_SIZE); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handle->errorbuf); - curl_easy_setopt(curl, CURLOPT_PRIVATE, (char *)handle); - curl_easy_setopt(curl, CURLOPT_FILETIME, 1); - /* We need to set CURLOPT_FORBID_REUSE because otherwise libcurl does not - * notify us (or not in time) about FD close/opens, thus we end up closing and - * screwing up another innocent FD, like a listener (BAD!). In my view a bug, but - * mailing list archives seem to indicate curl devs have a different opinion - * on these matters... - * Actually I don't know for sure if this option alone fixes 100% of the cases - * but at least I can't crash my server anymore. - * As a side-effect we also fix useless CLOSE_WAIT connections. - */ - curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1); - if (cachetime) - { - curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); - curl_easy_setopt(curl, CURLOPT_TIMEVALUE, cachetime); - } - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 45); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); + curl = curl_easy_init(); + if (!curl) + { + unreal_log(ULOG_ERROR, "main", "CURL_INTERNAL_FAILURE", NULL, + "Could not initialize curl handle. Maybe out of memory/resources?"); + snprintf(errorbuf, sizeof(errorbuf), "Could not initialize curl handle"); + return; + } + + file = url_getfilename(url); + filename = unreal_getfilename(file); + tmp = unreal_mktemp(TMPDIR, filename ? filename : "download.conf"); + + handle = safe_alloc(sizeof(Download)); + handle->file_fd = fopen(tmp, "wb"); + if (!handle->file_fd) + { + snprintf(errorbuf, sizeof(errorbuf), "Cannot create '%s': %s", tmp, strerror(ERRNO)); + callback(url, NULL, errorbuf, 0, callback_data); + safe_free(file); + safe_free(handle); + return; + } + + handle->callback = callback; + handle->callback_data = callback_data; + handle->cachetime = cachetime; + safe_strdup(handle->url, url); + strlcpy(handle->filename, tmp, sizeof(handle->filename)); + safe_free(file); + + curl_easy_setopt(curl, CURLOPT_URL, url); + snprintf(user_agent, sizeof(user_agent), "UnrealIRCd %s", VERSIONONLY); + curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, do_download); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)handle->file_fd); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); + set_curl_tls_options(curl); + memset(handle->errorbuf, 0, CURL_ERROR_SIZE); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handle->errorbuf); + curl_easy_setopt(curl, CURLOPT_PRIVATE, (char *)handle); + curl_easy_setopt(curl, CURLOPT_FILETIME, 1); + /* We need to set CURLOPT_FORBID_REUSE because otherwise libcurl does not + * notify us (or not in time) about FD close/opens, thus we end up closing and + * screwing up another innocent FD, like a listener (BAD!). In my view a bug, but + * mailing list archives seem to indicate curl devs have a different opinion + * on these matters... + * Actually I don't know for sure if this option alone fixes 100% of the cases + * but at least I can't crash my server anymore. + * As a side-effect we also fix useless CLOSE_WAIT connections. + */ + curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1); + if (cachetime) + { + curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); + curl_easy_setopt(curl, CURLOPT_TIMEVALUE, cachetime); + } + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, DOWNLOAD_TRANSFER_TIMEOUT); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DOWNLOAD_CONNECT_TIMEOUT); #if LIBCURL_VERSION_NUM >= 0x070f01 - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, maxredirects); #endif - curl_multi_add_handle(multihandle, curl); - } + curl_multi_add_handle(multihandle, curl); } diff --git a/src/url_unreal.c b/src/url_unreal.c new file mode 100644 index 0000000..f176a95 --- /dev/null +++ b/src/url_unreal.c @@ -0,0 +1,1068 @@ +/* + * Unreal Internet Relay Chat Daemon, src/url.c + * (C) 2021 Bram Matthys and the UnrealIRCd team + * + * 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. + */ + +#include "unrealircd.h" +#include "dns.h" + +/* Structs */ + +typedef enum TransferEncoding { + TRANSFER_ENCODING_NONE=0, + TRANSFER_ENCODING_CHUNKED=1 +} TransferEncoding; + +/* Stores information about the async transfer. + * Used to maintain information about the transfer + * to trigger the callback upon completion. + */ +typedef struct Download Download; + +struct Download +{ + Download *prev, *next; + vFP callback; + void *callback_data; + FILE *file_fd; /**< File open for writing (otherwise NULL) */ + char filename[PATH_MAX]; + char *url; /*< must be free()d by url_do_transfers_async() */ + char errorbuf[512]; + time_t cachetime; + char *hostname; /**< Parsed hostname (from 'url') */ + int port; /**< Parsed port (from 'url') */ + char *username; + char *password; + char *document; /**< Parsed document (from 'url') */ + char *ip; /**< Resolved IP */ + int ipv6; + SSL *ssl; + int fd; /**< Socket */ + int connected; + int got_response; + int http_status_code; + char *lefttoparse; + long long lefttoparselen; /* size of data in lefttoparse (note: not used for first header parsing) */ + time_t last_modified; + time_t download_started; + int dns_refcnt; + TransferEncoding transfer_encoding; + long chunk_remaining; + /* for redirects: */ + int redirects_remaining; + char *redirect_new_location; + char *redirect_original_url; +}; + +/* Variables */ +Download *downloads = NULL; +SSL_CTX *https_ctx = NULL; + +/* Forward declarations */ +void url_resolve_cb(void *arg, int status, int timeouts, struct hostent *he); +void unreal_https_initiate_connect(Download *handle); +int url_parse(const char *url, char **host, int *port, char **username, char **password, char **document); +SSL_CTX *https_new_ctx(void); +void unreal_https_connect_handshake(int fd, int revents, void *data); +int https_connect(Download *handle); +int https_fatal_tls_error(int ssl_error, int my_errno, Download *handle); +void https_connect_send_header(Download *handle); +void https_receive_response(int fd, int revents, void *data); +int https_handle_response_header(Download *handle, char *readbuf, int n); +int https_handle_response_file(Download *handle, char *readbuf, int n); +void https_done(Download *handle); +void https_done_cached(Download *handle); +void https_redirect(Download *handle); +int https_parse_header(char *buffer, int len, char **key, char **value, char **lastloc, int *end_of_request); +char *url_find_end_of_request(char *header, int totalsize, int *remaining_bytes); +void https_cancel(Download *handle, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,2,3))); + +void url_free_handle(Download *handle) +{ + DelListItem(handle, downloads); + if (handle->fd > 0) + { + fd_close(handle->fd); + fd_unnotify(handle->fd); + } + if (handle->file_fd) + fclose(handle->file_fd); + safe_free(handle->url); + safe_free(handle->hostname); + safe_free(handle->username); + safe_free(handle->password); + safe_free(handle->document); + safe_free(handle->ip); + if (handle->ssl) + SSL_free(handle->ssl); + safe_free(handle->lefttoparse); + safe_free(handle->redirect_new_location); + safe_free(handle->redirect_original_url); + safe_free(handle); +} + +void https_cancel(Download *handle, FORMAT_STRING(const char *pattern), ...) +{ + va_list vl; + va_start(vl, pattern); + vsnprintf(handle->errorbuf, sizeof(handle->errorbuf), pattern, vl); + va_end(vl); + handle->callback(handle->url, NULL, handle->errorbuf, 0, handle->callback_data); + url_free_handle(handle); +} + +void download_file_async(const char *url, time_t cachetime, vFP callback, void *callback_data, char *original_url, int maxredirects) +{ + char *file; + const char *filename; + char *tmp; + Download *handle = NULL; + int ipv6 = 0; + char *host; + int port; + char *username; + char *password; + char *document; + + handle = safe_alloc(sizeof(Download)); + handle->download_started = TStime(); + handle->callback = callback; + handle->callback_data = callback_data; + handle->cachetime = cachetime; + safe_strdup(handle->url, url); + safe_strdup(handle->redirect_original_url, original_url); + handle->redirects_remaining = maxredirects; + AddListItem(handle, downloads); + + if (strncmp(url, "https://", 8)) + { + https_cancel(handle, "Only https:// is supported (either rebuild UnrealIRCd with curl support or use https)"); + return; + } + if (!url_parse(url, &host, &port, &username, &password, &document)) + { + https_cancel(handle, "Failed to parse HTTP url"); + return; + } + + safe_strdup(handle->hostname, host); + handle->port = port; + safe_strdup(handle->username, username); + safe_strdup(handle->password, password); + safe_strdup(handle->document, document); + + file = url_getfilename(url); + filename = unreal_getfilename(file); + tmp = unreal_mktemp(TMPDIR, filename ? filename : "download.conf"); + + handle->file_fd = fopen(tmp, "wb"); + if (!handle->file_fd) + { + https_cancel(handle, "Cannot create '%s': %s", tmp, strerror(ERRNO)); + safe_free(file); + return; + } + + strlcpy(handle->filename, tmp, sizeof(handle->filename)); + safe_free(file); + + + // todo: allocate handle, select en weetikt allemaal + // add to some global struct linkedlist, for timeouts + // register in i/o + + if (is_valid_ip(handle->hostname)) + { + /* Nothing to resolve, eg https://127.0.0.1/ */ + safe_strdup(handle->ip, handle->hostname); + unreal_https_initiate_connect(handle); + } else { + /* Hostname, so start resolving... */ + handle->dns_refcnt++; + ares_gethostbyname(resolver_channel, handle->hostname, AF_INET, url_resolve_cb, handle); + // TODO: check return value? + } +} + +void url_resolve_cb(void *arg, int status, int timeouts, struct hostent *he) +{ + Download *handle = (Download *)arg; + int n; + struct hostent *he2; + char ipbuf[HOSTLEN+1]; + const char *ip = NULL; + + handle->dns_refcnt--; + + if ((status != 0) || !he->h_addr_list || !he->h_addr_list[0]) + { + https_cancel(handle, "Unable to resolve hostname '%s'", handle->hostname); + return; + } + + if (!he->h_addr_list[0] || (he->h_length != (handle->ipv6 ? 16 : 4)) || + !(ip = inetntop(handle->ipv6 ? AF_INET6 : AF_INET, he->h_addr_list[0], ipbuf, sizeof(ipbuf)))) + { + /* Illegal response -- fatal */ + https_cancel(handle, "Unable to resolve hostname '%s'", handle->hostname); + return; + } + + /* Ok, since we got here, it seems things were actually succesfull */ + + safe_strdup(handle->ip, ip); + + unreal_https_initiate_connect(handle); +} + +void unreal_https_initiate_connect(Download *handle) +{ + // todo: allocate handle, select en weetikt allemaal + // add to some global struct linkedlist, for timeouts + // register in i/o + + if (!handle->ip) + { + https_cancel(handle, "No IP address found to connect to"); + return; + } + + handle->fd = fd_socket(handle->ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0, "HTTPS"); + if (handle->fd < 0) + { + https_cancel(handle, "Could not create socket: %s", strerror(ERRNO)); + return; + } + set_sock_opts(handle->fd, NULL, handle->ipv6); + if (!unreal_connect(handle->fd, handle->ip, handle->port, handle->ipv6)) + { + https_cancel(handle, "Could not connect: %s", strerror(ERRNO)); + return; + } + + fd_setselect(handle->fd, FD_SELECT_WRITE, unreal_https_connect_handshake, handle); +} + +// based on unreal_tls_client_handshake() +void unreal_https_connect_handshake(int fd, int revents, void *data) +{ + Download *handle = data; + handle->ssl = SSL_new(https_ctx); + if (!handle->ssl) + { + https_cancel(handle, "Failed to setup SSL"); + return; + } + SSL_set_fd(handle->ssl, handle->fd); + SSL_set_connect_state(handle->ssl); + SSL_set_nonblocking(handle->ssl); + SSL_set_tlsext_host_name(handle->ssl, handle->hostname); + + if (https_connect(handle) < 0) + { + /* Some fatal error already */ + https_cancel(handle, "TLS_connect() failed early"); + return; + } + + /* Is now connecting... */ +} + +SSL_CTX *https_new_ctx(void) +{ + SSL_CTX *ctx_client; + char buf1[512], buf2[512]; + char *curl_ca_bundle = buf1; + + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + ctx_client = SSL_CTX_new(SSLv23_client_method()); + if (!ctx_client) + return NULL; +#ifdef HAS_SSL_CTX_SET_MIN_PROTO_VERSION + SSL_CTX_set_min_proto_version(ctx_client, TLS1_2_VERSION); +#endif + SSL_CTX_set_options(ctx_client, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1); + + /* Verify peer certificate */ + snprintf(buf1, sizeof(buf1), "%s/tls/curl-ca-bundle.crt", CONFDIR); + if (!file_exists(buf1)) + { + snprintf(buf2, sizeof(buf2), "%s/doc/conf/tls/curl-ca-bundle.crt", BUILDDIR); + if (!file_exists(buf2)) + { + unreal_log(ULOG_ERROR, "url", "CA_BUNDLE_NOT_FOUND", NULL, + "Neither $filename1 nor $filename2 exist.\n" + "Cannot use built-in https client without curl-ca-bundle.crt\n", + log_data_string("filename1", buf1), + log_data_string("filename2", buf2)); + exit(-1); + } + curl_ca_bundle = buf2; + } + SSL_CTX_load_verify_locations(ctx_client, curl_ca_bundle, NULL); + SSL_CTX_set_verify(ctx_client, SSL_VERIFY_PEER, NULL); + + /* Limit ciphers as well */ + SSL_CTX_set_cipher_list(ctx_client, UNREALIRCD_DEFAULT_CIPHERS); + + return ctx_client; +} + +// Based on unreal_tls_connect_retry +void https_connect_retry(int fd, int revents, void *data) +{ + Download *handle = data; + https_connect(handle); +} + +// Based on unreal_tls_connect() +int https_connect(Download *handle) +{ + int ssl_err; + char *errstr; + + if ((ssl_err = SSL_connect(handle->ssl)) <= 0) + { + ssl_err = SSL_get_error(handle->ssl, ssl_err); + switch(ssl_err) + { + case SSL_ERROR_SYSCALL: + if (ERRNO == P_EINTR || ERRNO == P_EWOULDBLOCK || ERRNO == P_EAGAIN) + { + /* Hmmm. This implementation is different than in unreal_tls_accept(). + * One of them must be wrong -- better check! (TODO) + */ + fd_setselect(handle->fd, FD_SELECT_READ|FD_SELECT_WRITE, https_connect_retry, handle); + return 0; + } + return https_fatal_tls_error(ssl_err, ERRNO, handle); + case SSL_ERROR_WANT_READ: + fd_setselect(handle->fd, FD_SELECT_READ, https_connect_retry, handle); + fd_setselect(handle->fd, FD_SELECT_WRITE, NULL, handle); + return 0; + case SSL_ERROR_WANT_WRITE: + fd_setselect(handle->fd, FD_SELECT_READ, NULL, handle); + fd_setselect(handle->fd, FD_SELECT_WRITE, https_connect_retry, handle); + return 0; + default: + return https_fatal_tls_error(ssl_err, ERRNO, handle); + } + /* NOTREACHED */ + return -1; + } + + /* We are connected now. */ + + if (!verify_certificate(handle->ssl, handle->hostname, &errstr)) + { + https_cancel(handle, "TLS Certificate error for server: %s", errstr); + return -1; + } + https_connect_send_header(handle); + return 1; +} + +/** + * Report a fatal TLS error and terminate the download. + * + * @param ssl_error The error as from OpenSSL. + * @param where The location, one of the SAFE_SSL_* defines. + * @param my_errno A preserved value of errno to pass to ssl_error_str(). + * @param client The client the error is associated with. + */ +int https_fatal_tls_error(int ssl_error, int my_errno, Download *handle) +{ + const char *ssl_errstr; + unsigned long additional_errno = ERR_get_error(); + char additional_info[256]; + const char *one, *two; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + /* Fetch additional error information from OpenSSL 3.0.0+ */ + two = ERR_reason_error_string(additional_errno); + if (two && *two) + { + snprintf(additional_info, sizeof(additional_info), ": %s", two); + } else { + *additional_info = '\0'; + } +#else + /* Fetch additional error information from OpenSSL. This is new as of Nov 2017 (4.0.16+) */ + one = ERR_func_error_string(additional_errno); + two = ERR_reason_error_string(additional_errno); + if (one && *one && two && *two) + { + snprintf(additional_info, sizeof(additional_info), ": %s: %s", one, two); + } else { + *additional_info = '\0'; + } +#endif + + ssl_errstr = ssl_error_str(ssl_error, my_errno); + + https_cancel(handle, "%s [%s]", ssl_errstr, additional_info); + return -1; +} + +// copied 100% from modulemanager parse_url() +int url_parse(const char *url, char **hostname, int *port, char **username, char **password, char **document) +{ + char *p, *p2; + static char hostbuf[256]; + static char documentbuf[512]; + + *hostname = *username = *password = *document = NULL; + *port = 443; + + if (strncmp(url, "https://", 8)) + return 0; + url += 8; /* skip over https:// part */ + + p = strchr(url, '/'); + if (!p) + return 0; + + strlncpy(hostbuf, url, sizeof(hostbuf), p - url); + + strlcpy(documentbuf, p, sizeof(documentbuf)); + + *hostname = hostbuf; + *document = documentbuf; + + /* Actually we may still need to extract the port */ + p = strchr(hostbuf, '@'); + if (p) + { + *p++ = '\0'; + + *username = hostbuf; + p2 = strchr(hostbuf, ':'); + if (p2) + { + *p2++ = '\0'; + *password = p2; + } + *hostname = p; + } + p = strchr(*hostname, ':'); + if (p) + { + *p++ = '\0'; + *port = atoi(p); + } + + return 1; +} + +void https_connect_send_header(Download *handle) +{ + char buf[1024]; + char hostandport[512]; + int ssl_err; + char *host; + int port; + char *document; + + handle->connected = 1; + snprintf(hostandport, sizeof(hostandport), "%s:%d", handle->hostname, handle->port); + + /* Prepare the header */ + snprintf(buf, sizeof(buf), "GET %s HTTP/1.1\r\n" + "User-Agent: UnrealIRCd %s\r\n" + "Host: %s\r\n" + "Connection: close\r\n", + handle->document, + VERSIONONLY, + hostandport); + if (handle->username && handle->password) + { + char wbuf[128]; + char obuf[256]; + char header[512]; + + snprintf(wbuf, sizeof(wbuf), "%s:%s", handle->username, handle->password); + if (b64_encode(wbuf, strlen(wbuf), obuf, sizeof(obuf)-1) > 0) + { + snprintf(header, sizeof(header), "Authorization: Basic %s\r\n", obuf); + strlcat(buf, header, sizeof(buf)); + } + } + if (handle->cachetime > 0) + { + const char *datestr = rfc2616_time(handle->cachetime); + if (datestr) + { + // snprintf_append... + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), + "If-Modified-Since: %s\r\n", datestr); + } + } + strlcat(buf, "\r\n", sizeof(buf)); + + ssl_err = SSL_write(handle->ssl, buf, strlen(buf)); + if (ssl_err < 0) + { + https_fatal_tls_error(ssl_err, ERRNO, handle); + return; + } + fd_setselect(handle->fd, FD_SELECT_WRITE, NULL, handle); + fd_setselect(handle->fd, FD_SELECT_READ, https_receive_response, handle); +} + +void https_receive_response(int fd, int revents, void *data) +{ + Download *handle = data; + int n; + char readbuf[2048]; + + n = SSL_read(handle->ssl, readbuf, sizeof(readbuf)-1); + if (n == 0) + { + /* Graceful close */ + https_done(handle); + return; + } + if (n < 0) + { + int ssl_err = SSL_get_error(handle->ssl, n); + switch (ssl_err) + { + case SSL_ERROR_WANT_WRITE: + fd_setselect(fd, FD_SELECT_READ, NULL, handle); + fd_setselect(fd, FD_SELECT_WRITE, https_receive_response, handle); + return; + case SSL_ERROR_WANT_READ: + /* Wants to read more data; let it call us next time again */ + return; + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + default: + https_fatal_tls_error(ssl_err, ERRNO, handle); + return; + } + return; + } + readbuf[n] = '\0'; + + //fprintf(stderr, "Got: '%s'\n", readbuf); + + if (!handle->got_response) + { + https_handle_response_header(handle, readbuf, n); + return; + } else + if (handle->got_response) + { + if (!https_handle_response_file(handle, readbuf, n)) + return; /* handle is already freed! */ + } +} + +// Based on websocket_handle_handshake() +int https_handle_response_header(Download *handle, char *readbuf, int n) +{ + char *key, *value; + int r, end_of_request; + char netbuf[4096], netbuf2[4096]; + char *lastloc = NULL; + int maxcopy, nprefix=0; + int totalsize; + + /* Yeah, totally paranoid: */ + memset(netbuf, 0, sizeof(netbuf)); + memset(netbuf2, 0, sizeof(netbuf2)); + + /** Frame re-assembling starts here **/ + *netbuf = '\0'; + if (handle->lefttoparse) + { + strlcpy(netbuf, handle->lefttoparse, sizeof(netbuf)); + nprefix = strlen(netbuf); + } + maxcopy = sizeof(netbuf) - nprefix - 1; + /* (Need to some manual checking here as strlen() can't be safely used + * on readbuf. Same is true for strlncat since it uses strlen().) + */ + if (n > maxcopy) + n = maxcopy; + if (n <= 0) + { + https_cancel(handle, "Oversized line in HTTP response"); + return 0; + } + memcpy(netbuf+nprefix, readbuf, n); /* SAFE: see checking above */ + totalsize = n + nprefix; + netbuf[totalsize] = '\0'; + memcpy(netbuf2, netbuf, totalsize+1); // copy, including the "always present \0 at the end just in case we use strstr etc". + safe_free(handle->lefttoparse); + + /** Now step through the lines.. **/ + for (r = https_parse_header(netbuf, strlen(netbuf), &key, &value, &lastloc, &end_of_request); + r; + r = https_parse_header(NULL, 0, &key, &value, &lastloc, &end_of_request)) + { + // do something actually with the header here ;) + if (!strcasecmp(key, "RESPONSE")) + { + handle->http_status_code = atoi(value); + if (handle->http_status_code == 304) + { + /* 304 Not Modified: cache hit */ + https_done_cached(handle); + return 0; + } + else if ((handle->http_status_code >= 301) && (handle->http_status_code <= 308)) + { + /* Redirect */ + if (handle->redirects_remaining == 0) + { + https_cancel(handle, "Too many HTTP redirects (%d)", DOWNLOAD_MAX_REDIRECTS); + return 0; + } + /* Let it continue.. we handle it later, as we need to + * receive the "Location" header as well. + */ + } + else if (handle->http_status_code != 200) + { + /* HTTP Failure code */ + https_cancel(handle, "HTTP Error: %s", value); + return 0; + } + } else + if (!strcasecmp(key, "Last-Modified") && value) + { + handle->last_modified = rfc2616_time_to_unix_time(value); + } else + if (!strcasecmp(key, "Location") && value) + { + safe_strdup(handle->redirect_new_location, value); + } else + if (!strcasecmp(key, "Transfer-Encoding") && value) + { + if (value && !strcasecmp(value, "chunked")) + handle->transfer_encoding = TRANSFER_ENCODING_CHUNKED; + } + //fprintf(stderr, "\nHEADER '%s'\n\n", key); + } + + if (end_of_request) + { + int remaining_bytes = 0; + char *nextframe; + + safe_free(handle->lefttoparse); + handle->got_response = 1; + + if (handle->http_status_code == 0) + { + https_cancel(handle, "Invalid HTTP response"); + return 0; + } + if (handle->http_status_code != 200) + { + if (handle->redirect_new_location) + { + https_redirect(handle); + return 0; /* this old request dies */ + } else { + https_cancel(handle, "HTTP Redirect encountered but no URL specified!?"); + return 0; + } + } + + nextframe = url_find_end_of_request(netbuf2, totalsize, &remaining_bytes); + if (nextframe) + { + if (!https_handle_response_file(handle, nextframe, remaining_bytes)) + return 0; + } + } + + if (lastloc) + { + /* Last line was cut somewhere, save it for next round. */ + safe_strdup(handle->lefttoparse, lastloc); + } + + return 1; +} + +int https_handle_response_file(Download *handle, char *readbuf, int pktsize) +{ + char *buf; + long long n; + char *free_this_buffer = NULL; + + // TODO we fail to check for write errors ;) + // TODO: Makes sense to track if we got everything? :D + + if (handle->transfer_encoding == TRANSFER_ENCODING_NONE) + { + /* Ohh.. so easy! */ + fwrite(readbuf, 1, pktsize, handle->file_fd); + return 1; + } + + /* Fill 'buf' nd set 'buflen' with what we had + what we have now. + * Makes things easy. + */ + if (handle->lefttoparse) + { + n = handle->lefttoparselen + pktsize; + free_this_buffer = buf = safe_alloc(n); + memcpy(buf, handle->lefttoparse, handle->lefttoparselen); + memcpy(buf+handle->lefttoparselen, readbuf, pktsize); + safe_free(handle->lefttoparse); + handle->lefttoparselen = 0; + } else { + n = pktsize; + buf = readbuf; + } + + /* Chunked transfers.. yayyyy.. */ + while (n > 0) + { + if (handle->chunk_remaining > 0) + { + /* Eat it */ + int eat = MIN(handle->chunk_remaining, n); + fwrite(buf, 1, eat, handle->file_fd); + n -= eat; + buf += eat; + handle->chunk_remaining -= eat; + } else + { + int gotlf = 0; + int i; + + /* First check if it is a (trailing) empty line, + * eg from a previous chunk. Skip over. + */ + if ((n >= 2) && !strncmp(buf, "\r\n", 2)) + { + buf += 2; + n -= 2; + } else + if ((n >= 1) && !strncmp(buf, "\n", 1)) + { + buf++; + n--; + } + + /* Now we are (possibly) at the chunk size line, + * this is or example '7f' + newline. + * So first, check if we have a newline at all. + */ + for (i=0; i < n; i++) + { + if (buf[i] == '\n') + { + gotlf = 1; + break; + } + } + if (!gotlf) + { + /* The line telling us the chunk size is incomplete, + * as it does not contain an \n. Wait for more data + * from the network socket. + */ + if (n > 0) + { + /* Store what we have first.. */ + handle->lefttoparselen = n; + handle->lefttoparse = safe_alloc(n); + memcpy(handle->lefttoparse, buf, n); + } + safe_free(free_this_buffer); + return 1; /* WE WANT MORE! */ + } + buf[i] = '\0'; /* cut at LF */ + i++; /* point to next data */ + handle->chunk_remaining = strtol(buf, NULL, 16); + if (handle->chunk_remaining < 0) + { + https_cancel(handle, "Negative chunk encountered (%ld)", handle->chunk_remaining); + safe_free(free_this_buffer); + return 0; + } + if (handle->chunk_remaining == 0) + { + https_done(handle); + safe_free(free_this_buffer); + return 0; + } + buf += i; + n -= i; + } + } + + safe_free(free_this_buffer); + return 1; +} + +void https_done(Download *handle) +{ + char *url = handle->redirect_original_url ? handle->redirect_original_url : handle->url; + + fclose(handle->file_fd); + handle->file_fd = NULL; + + if (!handle->got_response) + handle->callback(url, NULL, "HTTPS response not received", 0, handle->callback_data); + else + { + if (handle->last_modified > 0) + unreal_setfilemodtime(handle->filename, handle->last_modified); + handle->callback(url, handle->filename, NULL, 0, handle->callback_data); + } + url_free_handle(handle); + return; +} + +void https_done_cached(Download *handle) +{ + char *url = handle->redirect_original_url ? handle->redirect_original_url : handle->url; + + fclose(handle->file_fd); + handle->file_fd = NULL; + handle->callback(url, NULL, NULL, 1, handle->callback_data); + url_free_handle(handle); +} + +void https_redirect(Download *handle) +{ + if (handle->redirects_remaining == 0) + { + https_cancel(handle, "Too many HTTP redirects (%d)", DOWNLOAD_MAX_REDIRECTS); + return; + } + handle->redirects_remaining--; + + download_file_async(handle->redirect_new_location, handle->cachetime, handle->callback, handle->callback_data, + handle->url, handle->redirects_remaining); + /* Don't call the hook, just free this, the new redirect from above will call the hook later */ + url_free_handle(handle); +} + +/** Helper function to parse the HTTP header consisting of multiple 'Key: value' pairs */ +int https_parse_header(char *buffer, int len, char **key, char **value, char **lastloc, int *end_of_request) +{ + static char buf[4096], *nextptr; + char *p; + char *k = NULL, *v = NULL; + int foundlf = 0; + + if (buffer) + { + /* Initialize */ + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + + memcpy(buf, buffer, len); + buf[len] = '\0'; + nextptr = buf; + } + + *end_of_request = 0; + + p = nextptr; + + if (!p) + { + *key = *value = NULL; + return 0; /* done processing data */ + } + + if (!strncmp(p, "\n", 1) || !strncmp(p, "\r\n", 2)) + { + *key = *value = NULL; + *end_of_request = 1; + // new compared to websocket handling: + if (*p == '\n') + *lastloc = p + 1; + else + *lastloc = p + 2; + return 0; + } + + /* Note: p *could* point to the NUL byte ('\0') */ + + /* Special handling for response line itself. */ + if (!strncmp(p, "HTTP/1", 6) && (strlen(p)>=13)) + { + k = "RESPONSE"; + p += 9; + v = p; /* SET VALUE */ + nextptr = NULL; /* set to "we are done" in case next for loop fails */ + for (; *p; p++) + { + if (*p == '\r') + { + *p = '\0'; /* eat silently, but don't consider EOL */ + } + else if (*p == '\n') + { + *p = '\0'; + nextptr = p+1; /* safe, there is data or at least a \0 there */ + break; + } + } + *key = k; + *value = v; + return 1; + } + + /* Header parsing starts here. + * Example line "Host: www.unrealircd.org" + */ + k = p; /* SET KEY */ + + /* First check if the line contains a terminating \n. If not, don't process it + * as it may have been a cut header. + */ + for (; *p; p++) + { + if (*p == '\n') + { + foundlf = 1; + break; + } + } + + if (!foundlf) + { + *key = *value = NULL; + *lastloc = k; + return 0; + } + + p = k; + + for (; *p; p++) + { + if ((*p == '\n') || (*p == '\r')) + { + /* Reached EOL but 'value' not found */ + *p = '\0'; + break; + } + if (*p == ':') + { + *p++ = '\0'; + if (*p++ != ' ') + break; /* missing mandatory space after ':' */ + + v = p; /* SET VALUE */ + nextptr = NULL; /* set to "we are done" in case next for loop fails */ + for (; *p; p++) + { + if (*p == '\r') + { + *p = '\0'; /* eat silently, but don't consider EOL */ + } + else if (*p == '\n') + { + *p = '\0'; + nextptr = p+1; /* safe, there is data or at least a \0 there */ + break; + } + } + /* A key-value pair was succesfully parsed, return it */ + *key = k; + *value = v; + return 1; + } + } + + /* Fatal parse error */ + *key = *value = NULL; + return 0; +} + +/** Check if there is any data at the end of the request */ +char *url_find_end_of_request(char *header, int totalsize, int *remaining_bytes) +{ + char *nextframe1; + char *nextframe2; + char *nextframe = NULL; + + // find first occurance, yeah this is just stupid, but it works. + nextframe1 = strstr(header, "\r\n\r\n"); // = +4 + nextframe2 = strstr(header, "\n\n"); // = +2 + if (nextframe1 && nextframe2) + { + if (nextframe1 < nextframe2) + { + nextframe = nextframe1 + 4; + } else { + nextframe = nextframe2 + 2; + } + } else + if (nextframe1) + { + nextframe = nextframe1 + 4; + } else + if (nextframe2) + { + nextframe = nextframe2 + 2; + } + if (nextframe) + { + *remaining_bytes = totalsize - (nextframe - header); + if (*remaining_bytes > 0) + return nextframe; + } + return NULL; +} + +/* Handle timeouts. */ +EVENT(url_socket_timeout) +{ + Download *d, *d_next; + for (d = downloads; d; d = d_next) + { + d_next = d->next; + if (d->dns_refcnt) + continue; /* can't touch this... */ + if (!d->connected && (TStime() - d->download_started > DOWNLOAD_CONNECT_TIMEOUT)) + { + https_cancel(d, "Connect or DNS timeout after %ld seconds", (long)DOWNLOAD_CONNECT_TIMEOUT); + continue; + } + if (d->connected && (TStime() - d->download_started > DOWNLOAD_TRANSFER_TIMEOUT)) + { + https_cancel(d, "Download timeout after %ld seconds", (long)DOWNLOAD_TRANSFER_TIMEOUT); + continue; + } + } +} + +void url_init(void) +{ + https_ctx = https_new_ctx(); + if (!https_ctx) + { + unreal_log(ULOG_ERROR, "url", "HTTPS_NEW_CTX_FAILED", NULL, + "Unable to initialize SSL context"); + exit(-1); + } + EventAdd(NULL, "url_socket_timeout", url_socket_timeout, NULL, 500, 0); +} diff --git a/src/user.c b/src/user.c index f8bd291..7a34c92 100644 --- a/src/user.c +++ b/src/user.c @@ -59,7 +59,7 @@ MODVAR int non_utf8_nick_chars_in_use = 0; * @param client The client (user) * @param host The new vhost */ -void iNAH_host(Client *client, char *host) +void iNAH_host(Client *client, const char *host) { if (!client->user) return; @@ -80,45 +80,45 @@ void iNAH_host(Client *client, char *host) * @param umode The user mode string * @returns the user mode value (long) */ -long set_usermode(char *umode) +long set_usermode(const char *umode) { - int newumode; - int what; - char *m; - int i; + Umode *um; + int newumode; + int what; + const char *m; newumode = 0; what = MODE_ADD; for (m = umode; *m; m++) + { switch (*m) { - case '+': - what = MODE_ADD; - break; - case '-': - what = MODE_DEL; - break; - case ' ': - case '\n': - case '\r': - case '\t': - break; - default: - for (i = 0; i <= Usermode_highest; i++) - { - if (!Usermode_Table[i].flag) - continue; - if (*m == Usermode_Table[i].flag) - { - if (what == MODE_ADD) - newumode |= Usermode_Table[i].mode; - else - newumode &= ~Usermode_Table[i].mode; - } - } + case '+': + what = MODE_ADD; + break; + case '-': + what = MODE_DEL; + break; + case ' ': + case '\n': + case '\r': + case '\t': + break; + default: + for (um = usermodes; um; um = um->next) + { + if (um->letter == *m) + { + if (what == MODE_ADD) + newumode |= um->mode; + else + newumode &= ~um->mode; + } + } } + } - return (newumode); + return newumode; } /** Convert a target pointer to an 8 bit hash, used for target limiting. */ @@ -177,10 +177,10 @@ int target_limit_exceeded(Client *client, void *target, const char *name) { /* Target limit reached */ client->local->nexttarget += 2; /* punish them some more */ - client->local->since += 2; /* lag them up as well */ + add_fake_lag(client, 2000); /* lag them up as well */ flood_limit_exceeded_log(client, "max-concurrent-conversations"); - sendnumeric(client, ERR_TARGETTOOFAST, name, client->local->nexttarget - TStime()); + sendnumeric(client, ERR_TARGETTOOFAST, name, (long long)(client->local->nexttarget - TStime())); return 1; } @@ -207,9 +207,10 @@ int target_limit_exceeded(Client *client, void *target, const char *name) * @param buffer Input string * @returns The new de-duplicated buffer (temporary storage, only valid until next canonize call) */ -char *canonize(char *buffer) +char *canonize(const char *buffer) { static char cbuf[2048]; + char tbuf[2048]; char *s, *t, *cp = cbuf; int l = 0; char *p = NULL, *p2; @@ -219,11 +220,8 @@ char *canonize(char *buffer) if (!buffer) return NULL; - /* Ohh.. so lazy. But then again, this should never happen with a 2K buffer anyway. */ - if (strlen(buffer) >= sizeof(cbuf)) - buffer[sizeof(cbuf)-1] = '\0'; - - for (s = strtoken(&p, buffer, ","); s; s = strtoken(&p, NULL, ",")) + strlcpy(tbuf, buffer, sizeof(tbuf)); + for (s = strtoken(&p, tbuf, ","); s; s = strtoken(&p, NULL, ",")) { if (l) { @@ -252,99 +250,98 @@ char *canonize(char *buffer) return cbuf; } -/** Get snomasks as a string. - * @param client The client - * @returns string of snomasks (temporary storage) - */ -char *get_snomask_string(Client *client) -{ - int i; - char *m; - - m = buf; - - *m++ = '+'; - for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++) - if (Snomask_Table[i].flag && client->user->snomask & Snomask_Table[i].mode) - *m++ = Snomask_Table[i].flag; - *m = 0; - return buf; -} - /** Get user modes as a string. * @param client The client * @returns string of user modes (temporary storage) */ -char *get_usermode_string(Client *client) +const char *get_usermode_string(Client *client) { - int i; - char *m; + static char buf[128]; + Umode *um; + + strlcpy(buf, "+", sizeof(buf)); + for (um = usermodes; um; um = um->next) + if (client->umodes & um->mode) + strlcat_letter(buf, um->letter, sizeof(buf)); - m = buf; - *m++ = '+'; - for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++) - if (Usermode_Table[i].flag && (client->umodes & Usermode_Table[i].mode)) - *m++ = Usermode_Table[i].flag; - *m = '\0'; return buf; } +/** Get user modes as a string - buffer is specified. + * @param client The client + * @param buf The buffer to write to + * @param buflen The size of the buffer + * @returns string of user modes (buf) + */ +const char *get_usermode_string_r(Client *client, char *buf, size_t buflen) +{ + Umode *um; + + strlcpy(buf, "+", buflen); + for (um = usermodes; um; um = um->next) + if (client->umodes & um->mode) + strlcat_letter(buf, um->letter, buflen); + + return buf; +} /** Get user modes as a string - this one does not work on 'client' but directly on 'umodes'. * @param umodes The user modes that are set * @returns string of user modes (temporary storage) */ -char *get_usermode_string_raw(long umodes) +const char *get_usermode_string_raw(long umodes) { - int i; - char *m; + static char buf[128]; + Umode *um; + + strlcpy(buf, "+", sizeof(buf)); + for (um = usermodes; um; um = um->next) + if (umodes & um->mode) + strlcat_letter(buf, um->letter, sizeof(buf)); - m = buf; - *m++ = '+'; - for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++) - - if (Usermode_Table[i].flag && (umodes & Usermode_Table[i].mode)) - *m++ = Usermode_Table[i].flag; - *m = '\0'; return buf; } -/** Get snomasks as a string - this one does not work on 'client' but directly on 'sno'. - * @param sno The snomasks that are set - * @returns string of snomasks (temporary storage) +/** Get user modes as a string - this one does not work on 'client' but directly on 'umodes'. + * @param umodes The user modes that are set + * @param buf The buffer to write to + * @param buflen The size of the buffer + * @returns string of user modes (buf) */ -char *get_snomask_string_raw(long sno) +const char *get_usermode_string_raw_r(long umodes, char *buf, size_t buflen) { - int i; - char *m; + Umode *um; - m = buf; + strlcpy(buf, "+", buflen); + for (um = usermodes; um; um = um->next) + if (umodes & um->mode) + strlcat_letter(buf, um->letter, buflen); - *m++ = '+'; - for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++) - if (Snomask_Table[i].flag && sno & Snomask_Table[i].mode) - *m++ = Snomask_Table[i].flag; - *m = 0; return buf; } + /** Set a new snomask on the user. * The user is not informed of the change by this function. * @param client The client * @param snomask The snomask to add or delete (eg: "+k-c") */ -void set_snomask(Client *client, char *snomask) +void set_snomask(Client *client, const char *snomask) { int what = MODE_ADD; /* keep this an int. -- Syzop */ - char *p; + const char *p; int i; - if (snomask == NULL) { - client->user->snomask = 0; + + if (snomask == NULL) + { + remove_all_snomasks(client); return; } - for (p = snomask; p && *p; p++) { - switch (*p) { + for (p = snomask; p && *p; p++) + { + switch (*p) + { case '+': what = MODE_ADD; break; @@ -352,22 +349,20 @@ void set_snomask(Client *client, char *snomask) what = MODE_DEL; break; default: - for (i = 0; i <= Snomask_highest; i++) - { - if (!Snomask_Table[i].flag) - continue; - if (*p == Snomask_Table[i].flag) - { - if (Snomask_Table[i].allowed && !Snomask_Table[i].allowed(client,what)) + if (what == MODE_ADD) + { + if (!isalpha(*p) || !is_valid_snomask(*p)) continue; - if (what == MODE_ADD) - client->user->snomask |= Snomask_Table[i].mode; - else - client->user->snomask &= ~Snomask_Table[i].mode; - } - } + addlettertodynamicstringsorted(&client->user->snomask, *p); + } else { + delletterfromstring(client->user->snomask, *p); + } + break; } } + /* If the snomask becomes empty ("") then set it to NULL and user mode -s */ + if (client->user->snomask && !*client->user->snomask) + remove_all_snomasks(client); } /** Build the MODE line with (modified) user modes for this user. @@ -375,7 +370,7 @@ void set_snomask(Client *client, char *snomask) */ void build_umode_string(Client *client, long old, long sendmask, char *umode_buf) { - int i; + Umode *um; long flag; char *m; int what = MODE_NULL; @@ -386,33 +381,31 @@ void build_umode_string(Client *client, long old, long sendmask, char *umode_buf */ m = umode_buf; *m = '\0'; - for (i = 0; i <= Usermode_highest; i++) + for (um = usermodes; um; um = um->next) { - if (!Usermode_Table[i].flag) - continue; - flag = Usermode_Table[i].mode; + flag = um->mode; if (MyUser(client) && !(flag & sendmask)) continue; if ((flag & old) && !(client->umodes & flag)) { if (what == MODE_DEL) - *m++ = Usermode_Table[i].flag; + *m++ = um->letter; else { what = MODE_DEL; *m++ = '-'; - *m++ = Usermode_Table[i].flag; + *m++ = um->letter; } } else if (!(flag & old) && (client->umodes & flag)) { if (what == MODE_ADD) - *m++ = Usermode_Table[i].flag; + *m++ = um->letter; else { what = MODE_ADD; *m++ = '+'; - *m++ = Usermode_Table[i].flag; + *m++ = um->letter; } } } @@ -485,7 +478,7 @@ static void maxtarget_add_sorted(MaxTarget *n) } /** Find a maxtarget structure for a cmd (internal) */ -MaxTarget *findmaxtarget(char *cmd) +MaxTarget *findmaxtarget(const char *cmd) { MaxTarget *m; @@ -496,17 +489,14 @@ MaxTarget *findmaxtarget(char *cmd) } /** Set a maximum targets per command restriction */ -void setmaxtargets(char *cmd, int limit) +void setmaxtargets(const char *cmd, int limit) { MaxTarget *m = findmaxtarget(cmd); if (!m) { - char cmdupper[64], *i, *o; - if (strlen(cmd) > 63) - cmd[63] = '\0'; - for (i=cmd,o=cmdupper; *i; i++) - *o++ = toupper(*i); - *o = '\0'; + char cmdupper[64]; + strlcpy(cmdupper, cmd, sizeof(cmdupper)); + strtoupper(cmdupper); m = safe_alloc(sizeof(MaxTarget)); safe_strdup(m->cmd, cmdupper); maxtarget_add_sorted(m); @@ -529,7 +519,7 @@ void freemaxtargets(void) } /** Return the maximum number of targets permitted for a command */ -int max_targets_for_command(char *cmd) +int max_targets_for_command(const char *cmd) { MaxTarget *m = findmaxtarget(cmd); if (m) @@ -626,7 +616,7 @@ int is_handshake_finished(Client *client) int should_show_connect_info(Client *client) { if (SHOWCONNECTINFO && - !client->serv && + !client->server && !IsServersOnlyListener(client->local->listener) && !client->local->listener->websocket_options) { @@ -672,7 +662,7 @@ const char *uid_get(void) } /** Get cloaked host for user */ -char *getcloak(Client *client) +const char *getcloak(Client *client) { if (!*client->user->cloakedhost) { @@ -689,9 +679,11 @@ char *getcloak(Client *client) * @param buf Buffer to store the new cloaked host in * @param buflen Length of the buffer (should be HOSTLEN+1) */ -void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen) +void make_cloakedhost(Client *client, const char *curr, char *buf, size_t buflen) { - char host[256], *mask, *p, *q; + const char *p; + char host[256], *q; + const char *mask; /* Convert host to lowercase and cut off at 255 bytes just to be sure */ for (p = curr, q = host; *p && (q < host+sizeof(host)-1); p++, q++) @@ -700,9 +692,9 @@ void make_cloakedhost(Client *client, char *curr, char *buf, size_t buflen) /* Call the cloaking layer */ if (RCallbacks[CALLBACKTYPE_CLOAK_EX] != NULL) - mask = RCallbacks[CALLBACKTYPE_CLOAK_EX]->func.pcharfunc(client, host); + mask = RCallbacks[CALLBACKTYPE_CLOAK_EX]->func.stringfunc(client, host); else if (RCallbacks[CALLBACKTYPE_CLOAK] != NULL) - mask = RCallbacks[CALLBACKTYPE_CLOAK]->func.pcharfunc(host); + mask = RCallbacks[CALLBACKTYPE_CLOAK]->func.stringfunc(host); else mask = curr; @@ -718,7 +710,7 @@ void user_account_login(MessageTag *recv_mtags, Client *client) if (find_tkline_match(client, 0) && IsDead(client)) return; } - RunHook2(HOOKTYPE_ACCOUNT_LOGIN, client, recv_mtags); + RunHook(HOOKTYPE_ACCOUNT_LOGIN, client, recv_mtags); } /** Should we hide the idle time of 'target' to user 'client'? @@ -751,11 +743,13 @@ int hide_idle_time(Client *client, Client *target) * @param name The name of the group * @returns 1 if name is valid, 0 if not (eg: illegal characters) */ -int security_group_valid_name(char *name) +int security_group_valid_name(const char *name) { - char *p; + const char *p; + if (strlen(name) > SECURITYGROUPLEN) return 0; /* Too long */ + for (p = name; *p; p++) { if (!isalnum(*p) && !strchr("_-", *p)) @@ -768,7 +762,7 @@ int security_group_valid_name(char *name) * @param name The name of the security group * @returns A SecurityGroup struct, or NULL if not found. */ -SecurityGroup *find_security_group(char *name) +SecurityGroup *find_security_group(const char *name) { SecurityGroup *s; for (s = securitygroups; s; s = s->next) @@ -782,7 +776,7 @@ SecurityGroup *find_security_group(char *name) * @param name The name of the security group * @returns 1 if it exists, 0 if not */ -int security_group_exists(char *name) +int security_group_exists(const char *name) { if (!strcmp(name, "unknown-users") || find_security_group(name)) return 1; @@ -793,7 +787,7 @@ int security_group_exists(char *name) * @param name The name of the security group * @returns A SecurityGroup struct (already added to the 'securitygroups' linked list) */ -SecurityGroup *add_security_group(char *name, int priority) +SecurityGroup *add_security_group(const char *name, int priority) { SecurityGroup *s = find_security_group(name); @@ -812,9 +806,7 @@ SecurityGroup *add_security_group(char *name, int priority) /** Free a SecurityGroup struct */ void free_security_group(SecurityGroup *s) { - /* atm there is nothing else to free, - * but who knows this may change in the future - */ + unreal_delete_masks(s->include_mask); safe_free(s); } @@ -866,7 +858,9 @@ int user_allowed_by_security_group(Client *client, SecurityGroup *s) return 1; if (s->reputation_score && (GetReputation(client) >= s->reputation_score)) return 1; - if (s->tls && (IsSecureConnect(client) || IsSecure(client))) + if (s->tls && (IsSecureConnect(client) || (MyConnect(client) && IsSecure(client)))) + return 1; + if (s->include_mask && unreal_mask_match(client, s->include_mask)) return 1; return 0; } @@ -876,7 +870,7 @@ int user_allowed_by_security_group(Client *client, SecurityGroup *s) * @param secgroupname The name of the security-group to check against * @retval 1 if user is allowed by security-group, 0 if not. */ -int user_allowed_by_security_group_name(Client *client, char *secgroupname) +int user_allowed_by_security_group_name(Client *client, const char *secgroupname) { SecurityGroup *s; @@ -897,33 +891,81 @@ int user_allowed_by_security_group_name(Client *client, char *secgroupname) return user_allowed_by_security_group(client, s); } +/** Get comma separated list of matching security groups for 'client'. + * This is usually only used for displaying purposes. + * @returns string like "unknown-users,tls-users" from a static buffer. + */ +const char *get_security_groups(Client *client) +{ + SecurityGroup *s; + static char buf[512]; + + *buf = '\0'; + + /* We put known-users or unknown-users at the beginning. + * The latter is special and doesn't actually exist + * in the linked list, hence the special code here, + * and again later in the for loop to skip it. + */ + if (user_allowed_by_security_group_name(client, "known-users")) + strlcat(buf, "known-users,", sizeof(buf)); + else + strlcat(buf, "unknown-users,", sizeof(buf)); + + for (s = securitygroups; s; s = s->next) + { + if (strcmp(s->name, "known-users") && + user_allowed_by_security_group(client, s)) + { + strlcat(buf, s->name, sizeof(buf)); + strlcat(buf, ",", sizeof(buf)); + } + } + + if (*buf) + buf[strlen(buf)-1] = '\0'; + return buf; +} + /** Return extended information about user for the "Client connecting" line. * @returns A string such as "[secure] [reputation: 5]", never returns NULL. */ -char *get_connect_extinfo(Client *client) +const char *get_connect_extinfo(Client *client) { static char retbuf[512]; char tmp[512]; + const char *s; + const char *secgroups; NameValuePrioList *list = NULL, *e; /* From modules... */ - RunHook2(HOOKTYPE_CONNECT_EXTINFO, client, &list); + RunHook(HOOKTYPE_CONNECT_EXTINFO, client, &list); /* And some built-in: */ - /* "class": this should be first */ + /* "vhost": this should be first */ + if (IsHidden(client)) + add_nvplist(&list, -1000000, "vhost", client->user->virthost); + + /* "class": second */ if (MyUser(client) && client->local->class) add_nvplist(&list, -100000, "class", client->local->class->name); - /* "secure": SSL/TLS */ - if (MyUser(client) && IsSecure(client)) - add_nvplist(&list, -1000, "secure", tls_get_cipher(client->local->ssl)); - else if (!MyUser(client) && IsSecureConnect(client)) - add_nvplist(&list, -1000, "secure", NULL); + /* "secure": TLS */ + s = tls_get_cipher(client); + if (s) + add_nvplist(&list, -1000, "secure", s); + else if (IsSecure(client) || IsSecureConnect(client)) + add_nvplist(&list, -1000, "secure", NULL); /* old server or otherwise no details (eg: fake secure) */ /* services account? */ if (IsLoggedIn(client)) - add_nvplist(&list, -500, "account", client->user->svid); + add_nvplist(&list, -500, "account", client->user->account); + + /* security groups */ + secgroups = get_security_groups(client); + if (secgroups) + add_nvplist(&list, 100, "security-groups", secgroups); *retbuf = '\0'; for (e = list; e; e = e->next) @@ -949,18 +991,13 @@ char *get_connect_extinfo(Client *client) * @param client The client to check flood for (local user) * @param opt The flood option (eg FLD_AWAY) */ -void flood_limit_exceeded_log(Client *client, char *floodname) +void flood_limit_exceeded_log(Client *client, const char *floodname) { char buf[1024]; - snprintf(buf, sizeof(buf), "Flood blocked (%s) from %s!%s@%s [%s]", - floodname, - client->name, - client->user->username, - client->user->realhost, - GetIP(client)); - ircd_log(LOG_FLOOD, "%s", buf); - sendto_snomask_global(SNO_FLOOD, "%s", buf); + unreal_log(ULOG_INFO, "flood", "FLOOD_BLOCKED", client, + "Flood blocked ($flood_type) from $client.details [$client.ip]", + log_data_string("flood_type", floodname)); } /** Is the flood limit exceeded for an option? eg for away-flood. @@ -980,9 +1017,6 @@ int flood_limit_exceeded(Client *client, FloodOption opt) if (f->limit[opt] <= 0) return 0; /* No limit set or unlimited */ - ircd_log(LOG_ERROR, "Checking flood_limit_exceeded() for '%s', type %d with max %d:%ld...", - client->name, (int)opt, (int)f->limit[opt], (long)f->period[opt]); - /* Ok, let's do the flood check */ if ((client->local->flood[opt].t + f->period[opt]) <= timeofday) { @@ -1041,12 +1075,76 @@ FloodSettings *get_floodsettings_for_user(Client *client, FloodOption opt) return f; } -MODVAR char *floodoption_names[] = { +MODVAR const char *floodoption_names[] = { "nick-flood", "join-flood", "away-flood", "invite-flood", "knock-flood", "max-concurrent-conversations", + "lag-penalty", NULL }; + +/** Lookup GEO information for an IP address. + * @param ip The IP to lookup + * @returns A struct containing all the details. Must be freed by caller! + */ +GeoIPResult *geoip_lookup(const char *ip) +{ + if (!RCallbacks[CALLBACKTYPE_GEOIP_LOOKUP]) + return NULL; + return RCallbacks[CALLBACKTYPE_GEOIP_LOOKUP]->func.pvoidfunc(ip); +} + +void free_geoip_result(GeoIPResult *r) +{ + if (!r) + return; + safe_free(r->country_code); + safe_free(r->country_name); + safe_free(r); +} + +/** Grab geoip information for client */ +GeoIPResult *geoip_client(Client *client) +{ + ModData *m = moddata_client_get_raw(client, "geoip"); + if (!m) + return NULL; + return m->ptr; /* can still be NULL */ +} + +/** Get the oper block that was used to become OPER. + * @param client The client to fetch the info for + * @returns the oper block name (eg: "OpEr") or NULL. + */ +const char *get_operlogin(Client *client) +{ + if (client->user->operlogin) + return client->user->operlogin; + return moddata_client_get(client, "operlogin"); +} + +/** Get the operclass of the IRCOp. + * @param client The client to fetch the info for + * @returns the operclass name or NULL + */ +const char *get_operclass(Client *client) +{ + const char *operlogin = NULL; + + if (MyUser(client) && client->user->operlogin) + { + ConfigItem_oper *oper; + operlogin = client->user->operlogin; + oper = find_oper(operlogin); + if (oper && oper->operclass) + return oper->operclass; + } + + /* Remote user or locally no longer available + * (eg oper block removed but user is still oper) + */ + return moddata_client_get(client, "operclass"); +} diff --git a/src/utf8.c b/src/utf8.c index 701479b..fb9fa56 100644 --- a/src/utf8.c +++ b/src/utf8.c @@ -140,38 +140,51 @@ char *unrl_utf8_find_prev_char (const char *begin, const char *p) } /** Return a valid UTF8 string based on the input. - * @param str The input string, with a maximum of 1024 bytes. - * @retval Returns a valid UTF8 string (which may be sanitized - * or simply the original string if it was OK already) + * @param str The input string + * @param outputbuf The output buffer + * @param outputbuflen Length of the output buffer + * @param strictlen If set to 1 we never return more than + * outputbuflen-1 characters. + * If set to 0, we may do that, if the + * input string was already 100% valid UTF8. + * @retval Returns a valid UTF8 string, either the input buffer + * (if it was already valid UTF8) or the output buffer. + * NULL is returned if either 'str' was NULL or outputlen is zero. + * @notes The 'outputbuf' is unused if the string is already valid UTF8. + * So don't rely on it being always set, use the returned string. */ -char *unrl_utf8_make_valid(const char *str) +char *unrl_utf8_make_valid(const char *str, char *outputbuf, size_t outputbuflen, int strictlen) { - static char string[4096]; /* crazy, but lazy, max amplification is x3, so x4 is safe. */ const char *remainder, *invalid; int remaining_bytes, valid_bytes, len; int replaced = 0; /**< UTF8 string needed replacement (was invalid) */ - if (!str) + if (!str || !outputbuflen) return NULL; len = strlen(str); - if (len >= 1024) - abort(); /* better safe than sorry */ - - *string = '\0'; + *outputbuf = '\0'; remainder = str; remaining_bytes = len; while (remaining_bytes != 0) { if (unrl_utf8_validate(remainder, &invalid)) + { + if (!replaced && strictlen) + { + /* Caller wants us to go through the 'replaced' branch */ + strlcpy(outputbuf, str, outputbuflen); + replaced = 1; + } break; + } replaced = 1; valid_bytes = invalid - remainder; - strlncat(string, remainder, sizeof(string), valid_bytes); /*g_string_append_len(string, remainder, valid_bytes);*/ - strlcat(string, "\357\277\275", sizeof(string)); + strlncat(outputbuf, remainder, outputbuflen, valid_bytes); /*g_string_append_len(string, remainder, valid_bytes);*/ + strlcat(outputbuf, "\357\277\275", outputbuflen); remaining_bytes -= valid_bytes + 1; remainder = invalid + 1; @@ -180,21 +193,25 @@ char *unrl_utf8_make_valid(const char *str) if (!replaced) return (char *)str; /* return original string (no changes needed) */ - /* If output size is too much for an IRC message then cut the string at - * the appropriate place (as in: not to cause invalid UTF8 due to - * cutting half-way a byte sequence). + /* If we took up all the space, then backtrack one character and cut + * things off from there. This to ensure that we don't end up with + * invalid UTF8 due to cutting half-way a UTF8 byte sequence. + * NOTE: This may cause us to remove 1 character needlessly at the + * end even though there was still (some) space. So be it. */ - if (strlen(string) >= 510) + if (strlen(outputbuf) == outputbuflen-1) { - char *cut_at = unrl_utf8_find_prev_char(string, string+509); + char *cut_at = unrl_utf8_find_prev_char(outputbuf, outputbuf+outputbuflen-1); if (cut_at) *cut_at = '\0'; } - if (!unrl_utf8_validate(string, NULL)) +#ifdef DEBUGMODE + if (!unrl_utf8_validate(outputbuf, NULL)) abort(); /* this should never happen, it means our conversion resulted in an invalid UTF8 string */ +#endif - return string; + return outputbuf; } /**************** END OF UTF8 HELPER FUNCTIONS *****************/ @@ -206,12 +223,14 @@ void utf8_test(void) char *res; int cnt = 0; char *heapbuf; /* for strict OOB testing with ASan */ + char *workbuf = safe_alloc(500); + size_t workbuflen = 500; while ((fgets(buf, sizeof(buf), stdin))) { stripcrlf(buf); heapbuf = strdup(buf); - res = unrl_utf8_make_valid(heapbuf); + res = unrl_utf8_make_valid(heapbuf, workbuf, workbuflen, 1); if (heapbuf == res) { printf(" %s\n", res); @@ -220,4 +239,5 @@ void utf8_test(void) } free(heapbuf); } + safe_free(workbuf); } diff --git a/src/version.c.SH b/src/version.c.SH index f499120..38a0439 100644 --- a/src/version.c.SH +++ b/src/version.c.SH @@ -4,7 +4,10 @@ echo "Extracting src/version.c..." #id=`grep '$Id: Changes,v' ../Changes` #id=`echo $id |sed 's/.* Changes\,v \(.*\) .* Exp .*/\1/'` -id="5.2.0.1" +if [ -d ../.git ]; then + SUFFIX="-$(git rev-parse --short HEAD)" +fi +id="6.0.1.1$SUFFIX" echo "$id" if test -r version.c @@ -19,7 +22,9 @@ generation=`expr $generation + 1` export LANG=C export LC_TIME=C export LC_ALL=C -creation=`date | \ +SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}" +BUILD_DATE=$(date -u -d "@$SOURCE_DATE_EPOCH" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" 2>/dev/null || date -u) +creation=`echo "$BUILD_DATE" | \ awk '{if (NF == 6) \ { print $1 " " $2 " " $3 " " $6 " at " $4 " " $5 } \ else \ @@ -143,7 +148,8 @@ char *unrealcredits[] = "officially joining the staff.", " ", "===========================[ Coders ]=============================", - "Syzop - Head Coder / Project leader", + "Bram Matthys (Syzop) - Head Coder / Project leader", + "Krzysztof Beresztant (k4be) - Coder", "Gottem - Coder", "i - Coder", " ", diff --git a/src/whowas.c b/src/whowas.c index d9f9ed0..6a4c6f7 100644 --- a/src/whowas.c +++ b/src/whowas.c @@ -24,19 +24,19 @@ // Some users may not want to load cmd_whowas at all. /* internally defined function */ -static void add_whowas_to_clist(aWhowas **, aWhowas *); -static void del_whowas_from_clist(aWhowas **, aWhowas *); -static void add_whowas_to_list(aWhowas **, aWhowas *); -static void del_whowas_from_list(aWhowas **, aWhowas *); +static void add_whowas_to_clist(WhoWas **, WhoWas *); +static void del_whowas_from_clist(WhoWas **, WhoWas *); +static void add_whowas_to_list(WhoWas **, WhoWas *); +static void del_whowas_from_list(WhoWas **, WhoWas *); -aWhowas MODVAR WHOWAS[NICKNAMEHISTORYLENGTH]; -aWhowas MODVAR *WHOWASHASH[WHOWAS_HASH_TABLE_SIZE]; +WhoWas MODVAR WHOWAS[NICKNAMEHISTORYLENGTH]; +WhoWas MODVAR *WHOWASHASH[WHOWAS_HASH_TABLE_SIZE]; MODVAR int whowas_next = 0; void add_history(Client *client, int online) { - aWhowas *new; + WhoWas *new; new = &WHOWAS[whowas_next]; @@ -87,7 +87,7 @@ void add_history(Client *client, int online) void off_history(Client *client) { - aWhowas *temp, *next; + WhoWas *temp, *next; for (temp = client->user->whowas; temp; temp = next) { @@ -97,9 +97,9 @@ void off_history(Client *client) } } -Client *get_history(char *nick, time_t timelimit) +Client *get_history(const char *nick, time_t timelimit) { - aWhowas *temp; + WhoWas *temp; int blah; timelimit = TStime() - timelimit; @@ -118,7 +118,7 @@ Client *get_history(char *nick, time_t timelimit) void count_whowas_memory(int *wwu, u_long *wwum) { - aWhowas *tmp; + WhoWas *tmp; int i; int u = 0; u_long um = 0; @@ -129,7 +129,7 @@ void count_whowas_memory(int *wwu, u_long *wwum) if (tmp->hashv != -1) { u++; - um += sizeof(aWhowas); + um += sizeof(WhoWas); } *wwu = u; *wwum = um; @@ -142,14 +142,14 @@ void initwhowas() for (i = 0; i < NICKNAMEHISTORYLENGTH; i++) { - memset(&WHOWAS[i], 0, sizeof(aWhowas)); + memset(&WHOWAS[i], 0, sizeof(WhoWas)); WHOWAS[i].hashv = -1; } for (i = 0; i < WHOWAS_HASH_TABLE_SIZE; i++) WHOWASHASH[i] = NULL; } -static void add_whowas_to_clist(aWhowas ** bucket, aWhowas * whowas) +static void add_whowas_to_clist(WhoWas ** bucket, WhoWas * whowas) { whowas->cprev = NULL; if ((whowas->cnext = *bucket) != NULL) @@ -157,7 +157,7 @@ static void add_whowas_to_clist(aWhowas ** bucket, aWhowas * whowas) *bucket = whowas; } -static void del_whowas_from_clist(aWhowas ** bucket, aWhowas * whowas) +static void del_whowas_from_clist(WhoWas ** bucket, WhoWas * whowas) { if (whowas->cprev) whowas->cprev->cnext = whowas->cnext; @@ -167,7 +167,7 @@ static void del_whowas_from_clist(aWhowas ** bucket, aWhowas * whowas) whowas->cnext->cprev = whowas->cprev; } -static void add_whowas_to_list(aWhowas ** bucket, aWhowas * whowas) +static void add_whowas_to_list(WhoWas ** bucket, WhoWas * whowas) { whowas->prev = NULL; if ((whowas->next = *bucket) != NULL) @@ -175,7 +175,7 @@ static void add_whowas_to_list(aWhowas ** bucket, aWhowas * whowas) *bucket = whowas; } -static void del_whowas_from_list(aWhowas ** bucket, aWhowas * whowas) +static void del_whowas_from_list(WhoWas ** bucket, WhoWas * whowas) { if (whowas->prev) whowas->prev->next = whowas->next; diff --git a/src/windows/UnrealIRCd.exe.manifest b/src/windows/UnrealIRCd.exe.manifest index d6fb05d..d45baa8 100644 --- a/src/windows/UnrealIRCd.exe.manifest +++ b/src/windows/UnrealIRCd.exe.manifest @@ -2,8 +2,8 @@ Internet Relay Chat Daemon @@ -12,7 +12,7 @@ next) - { - if (inc->flag.type & INCLUDE_NOTLOADED) - continue; -#ifdef USE_LIBCURL - if (inc->flag.type & INCLUDE_REMOTE) - AppendMenu(hConfig, MF_STRING, i++, inc->url); - else #endif - AppendMenu(hConfig, MF_STRING, i++, inc->file); - } - AppendMenu(hConfig, MF_SEPARATOR, 0, NULL); - } if (conf_files) { AppendMenu(hConfig, MF_STRING, IDM_MOTD, conf_files->motd_file); @@ -458,6 +448,7 @@ LRESULT CALLBACK MainDLG(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) hLogs = CreatePopupMenu(); AppendMenu(hConfig, MF_STRING, IDM_CONF, CPATH); +#if 0 if (conf_log) { ConfigItem_log *logs; @@ -468,22 +459,7 @@ LRESULT CALLBACK MainDLG(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) } } AppendMenu(hConfig, MF_SEPARATOR, 0, NULL); - - if (conf_include) - { - ConfigItem_include *inc; - for (inc = conf_include; inc; inc = inc->next) - { -#ifdef USE_LIBCURL - if (inc->flag.type & INCLUDE_REMOTE) - AppendMenu(hConfig, MF_STRING, i++, inc->url); - else #endif - AppendMenu(hConfig, MF_STRING, i++, inc->file); - } - AppendMenu(hConfig, MF_SEPARATOR, 0, NULL); - } - if (conf_files) { AppendMenu(hConfig, MF_STRING, IDM_MOTD, conf_files->motd_file); @@ -520,7 +496,16 @@ LRESULT CALLBACK MainDLG(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) return 0; } else if ((p.x >= 336) && (p.x <= 411) && (p.y >= TOOLBAR_START) && (p.y <= TOOLBAR_STOP)) - return CloseUnreal(hDlg); + return AskCloseUnreal(hDlg); + } + case WM_SYSCOMMAND: + { + if (wParam == SC_CLOSE) + { + AskCloseUnreal(hDlg); + return 1; + } + break; } case WM_COMMAND: { @@ -533,20 +518,13 @@ LRESULT CALLBACK MainDLG(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) else { GetMenuString(hConfig,LOWORD(wParam), path, MAX_PATH, MF_BYCOMMAND); -#ifdef USE_LIBCURL - if (url_is_valid(path)) - { - char *file = find_loaded_remote_include(path); - DialogBoxParam(hInst, "FromVar", hDlg, (DLGPROC)FromFileReadDLG, (LPARAM)file); - } - else -#endif + if (!url_is_valid(path)) DialogBoxParam(hInst, "FromFile", hDlg, (DLGPROC)FromFileDLG, (LPARAM)path); } return FALSE; } - if (!loop.ircd_booted) + if (!loop.booted) { MessageBox(NULL, "UnrealIRCd not booted due to configuration errors. " "Check other window for error details. Then close that window, " @@ -561,34 +539,28 @@ LRESULT CALLBACK MainDLG(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) ShowDialog(&hStatusWnd, hInst, "Status", hDlg,StatusDLG); break; case IDM_SHUTDOWN: - return CloseUnreal(hDlg); + return AskCloseUnreal(hDlg); case IDM_RHALL: MessageBox(NULL, "Rehashing all files", "Rehashing", MB_OK); - sendto_realops("Rehashing all files via the console"); - rehash(&me,0); - reread_motdsandrules(); + request_rehash(NULL); break; case IDM_RHCONF: MessageBox(NULL, "Rehashing the Config file", "Rehashing", MB_OK); - sendto_realops("Rehashing the Config file via the console"); - rehash(&me,0); + request_rehash(NULL); break; case IDM_RHMOTD: { MessageBox(NULL, "Rehashing all MOTD and Rules files", "Rehashing", MB_OK); rehash_motdrules(); - sendto_realops("Rehashing all MOTD and Rules files via the console"); break; } case IDM_RHOMOTD: MessageBox(NULL, "Rehashing the OperMOTD", "Rehashing", MB_OK); read_motd(conf_files->opermotd_file, &opermotd); - sendto_realops("Rehashing the OperMOTD via the console"); break; case IDM_RHBMOTD: MessageBox(NULL, "Rehashing the BotMOTD", "Rehashing", MB_OK); read_motd(conf_files->botmotd_file, &botmotd); - sendto_realops("Rehashing the BotMOTD via the console"); break; case IDM_LICENSE: DialogBox(hInst, "FromVar", hDlg, (DLGPROC)LicenseDLG); @@ -1016,7 +988,7 @@ void win_map(Client *server, HWND hwTreeView, short remap) for (lp = Servers; lp; lp = lp->next) { acptr = lp->value.client; - if (acptr->srvptr != server) + if (acptr->uplink != server) continue; win_map(acptr, hwTreeView, 0); } @@ -1076,27 +1048,6 @@ void win_error() { if (errors && !IsService) DialogBox(hInst, "ConfigError", hwIRCDWnd, (DLGPROC)ConfigErrorDLG); - if (need_34_upgrade) - { - need_34_upgrade = 0; /* anti-recursion. yes, is needed. */ - if (MessageBox(NULL, - "Shall I try to upgrade your configuration files to UnrealIRCd 4 format?", - "3.2.x configuration detected", - MB_YESNO|MB_ICONQUESTION) == IDNO) - { - return; - } - else - { - update_conf(); - MessageBox(NULL, - "Configuration file(s) upgraded! In next screen you can see what I did (just for reference). " - "After that, simply try to start UnrealIRCd again and see if it loads.", - "Configuration upgrade", - MB_OK); - DialogBox(hInst, "ConfigError", hwIRCDWnd, (DLGPROC)ConfigErrorDLG); - } - } } LRESULT CALLBACK ConfigErrorDLG(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) diff --git a/src/windows/service.c b/src/windows/service.c index dc76411..7db873d 100644 --- a/src/windows/service.c +++ b/src/windows/service.c @@ -77,8 +77,7 @@ VOID WINAPI IRCDCtrlHandler(DWORD opcode) /* Rehash */ else if (opcode == IRCD_SERVICE_CONTROL_REHASH) { - rehash(&me,0); - reread_motdsandrules(); + request_rehash(NULL); } SetServiceStatus(IRCDStatusHandle, &IRCDStatus); @@ -112,7 +111,7 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) chdir(path); /* Go one level up, since we are currently in the bin\ subdir - * and we want to be in (f.e.) "C:\Program Files\UnrealIRCd 5" + * and we want to be in (f.e.) "C:\Program Files\UnrealIRCd 6" */ chdir(".."); diff --git a/src/windows/unrealinst.iss b/src/windows/unrealinst.iss old mode 100644 new mode 100755 index c071af5..c7800b5 --- a/src/windows/unrealinst.iss +++ b/src/windows/unrealinst.iss @@ -5,15 +5,15 @@ #define USE_CURL [Setup] -AppName=UnrealIRCd 5 -AppVerName=UnrealIRCd 5.2.0.1 +AppName=UnrealIRCd 6 +AppVerName=UnrealIRCd 6.0.1.1 AppPublisher=UnrealIRCd Team AppPublisherURL=https://www.unrealircd.org AppSupportURL=https://www.unrealircd.org AppUpdatesURL=https://www.unrealircd.org AppMutex=UnrealMutex,Global\UnrealMutex -DefaultDirName={pf}\UnrealIRCd 5 -DefaultGroupName=UnrealIRCd 5 +DefaultDirName={pf}\UnrealIRCd 6 +DefaultGroupName=UnrealIRCd 6 AllowNoIcons=yes LicenseFile=src\windows\gplplusssl.rtf Compression=lzma @@ -26,8 +26,11 @@ UninstallFilesDir={app}\bin\uninstaller DisableWelcomePage=no ArchitecturesInstallIn64BitMode=x64 ArchitecturesAllowed=x64 +;These are set only on release: +;SignedUninstaller=yes +;SignTool=signtool -; !!! Make sure to update SSL/TLS validation (WizardForm.TasksList.Checked[9]) if tasks are added/removed !!! +; !!! Make sure to update TLS validation (WizardForm.TasksList.Checked[9]) if tasks are added/removed !!! [Tasks] Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:" Name: "quicklaunchicon"; Description: "Create a &Quick Launch icon"; GroupDescription: "Additional icons:"; Flags: unchecked @@ -35,13 +38,42 @@ Name: "installservice"; Description: "Install as a &service (not for beginners)" Name: "installservice/startboot"; Description: "S&tart UnrealIRCd when Windows starts"; GroupDescription: "Service support:"; MinVersion: 0,4.0; Flags: exclusive unchecked Name: "installservice/startdemand"; Description: "Start UnrealIRCd on &request"; GroupDescription: "Service support:"; MinVersion: 0,4.0; Flags: exclusive unchecked Name: "installservice/crashrestart"; Description: "Restart UnrealIRCd if it &crashes"; GroupDescription: "Service support:"; Flags: unchecked; MinVersion: 0,5.0; -Name: "makecert"; Description: "&Create certificate"; GroupDescription: "SSL/TLS options:"; +Name: "makecert"; Description: "&Create certificate"; GroupDescription: "TLS options:"; Name: "fixperm"; Description: "Make UnrealIRCd folder writable by current user"; [Files] -Source: "UnrealIRCd.exe"; DestDir: "{app}\bin"; Flags: ignoreversion +; UnrealIRCd binaries +Source: "UnrealIRCd.exe"; DestDir: "{app}\bin"; Flags: ignoreversion signonce Source: "UnrealIRCd.pdb"; DestDir: "{app}\bin"; Flags: ignoreversion +Source: "unrealsvc.exe"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +; TLS certificate generation helpers +Source: "src\windows\makecert.bat"; DestDir: "{app}\bin"; Flags: ignoreversion +Source: "extras\tls.cnf"; DestDir: "{app}\bin"; Flags: ignoreversion + +; UnrealIRCd modules +Source: "src\modules\*.dll"; DestDir: "{app}\modules"; Flags: ignoreversion signonce +Source: "src\modules\chanmodes\*.dll"; DestDir: "{app}\modules\chanmodes"; Flags: ignoreversion signonce +Source: "src\modules\usermodes\*.dll"; DestDir: "{app}\modules\usermodes"; Flags: ignoreversion signonce +Source: "src\modules\extbans\*.dll"; DestDir: "{app}\modules\extbans"; Flags: ignoreversion signonce +Source: "src\modules\third\*.dll"; DestDir: "{app}\modules\third"; Flags: ignoreversion skipifsourcedoesntexist signonce + +; Libraries +Source: "c:\dev\unrealircd-6-libs\pcre2\bin\pcre*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\argon2\vs2015\build\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\libsodium\bin\x64\Release\v142\dynamic\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\jansson\bin\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\c-ares\msvc\cares\dll-release\cares.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\libressl\bin\openssl.exe"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\libressl\bin\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\GeoIP\libGeoIP\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +Source: "c:\dev\unrealircd-6-libs\setacl.exe"; DestDir: "{app}\tmp"; Flags: ignoreversion signonce +#ifdef USE_CURL +Source: "c:\dev\unrealircd-6-libs\curl\builds\libcurl-vc-x64-release-dll-ssl-dll-cares-dll-ipv6-obj-lib\libcurl.dll"; DestDir: "{app}\bin"; Flags: ignoreversion signonce +#endif +Source: "doc\conf\tls\curl-ca-bundle.crt"; DestDir: "{app}\conf\tls"; Flags: ignoreversion + +; Config files Source: "doc\conf\*.default.conf"; DestDir: "{app}\conf"; Flags: ignoreversion Source: "doc\conf\*.optional.conf"; DestDir: "{app}\conf"; Flags: ignoreversion Source: "doc\conf\spamfilter.conf"; DestDir: "{app}\conf"; Flags: onlyifdoesntexist @@ -50,38 +82,13 @@ Source: "doc\conf\dccallow.conf"; DestDir: "{app}\conf"; Flags: onlyifdoesntexis Source: "doc\conf\aliases\*.conf"; DestDir: "{app}\conf\aliases"; Flags: ignoreversion Source: "doc\conf\help\*.conf"; DestDir: "{app}\conf\help"; Flags: ignoreversion Source: "doc\conf\examples\*.conf"; DestDir: "{app}\conf\examples"; Flags: ignoreversion - -Source: "doc\Donation"; DestDir: "{app}\doc"; DestName: "Donation.txt"; Flags: ignoreversion -Source: "LICENSE"; DestDir: "{app}\doc"; DestName: "LICENSE.txt"; Flags: ignoreversion - -Source: "doc\*.*"; DestDir: "{app}\doc"; Flags: ignoreversion -Source: "doc\technical\*.*"; DestDir: "{app}\doc\technical"; Flags: ignoreversion Source: "doc\conf\aliases\*"; DestDir: "{app}\conf\aliases"; Flags: ignoreversion -Source: "unrealsvc.exe"; DestDir: "{app}\bin"; Flags: ignoreversion; MinVersion: 0,4.0 - -Source: "src\windows\makecert.bat"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "extras\tls.cnf"; DestDir: "{app}\bin"; Flags: ignoreversion - -Source: "src\modules\*.dll"; DestDir: "{app}\modules"; Flags: ignoreversion -Source: "src\modules\chanmodes\*.dll"; DestDir: "{app}\modules\chanmodes"; Flags: ignoreversion -Source: "src\modules\usermodes\*.dll"; DestDir: "{app}\modules\usermodes"; Flags: ignoreversion -Source: "src\modules\snomasks\*.dll"; DestDir: "{app}\modules\snomasks"; Flags: ignoreversion -Source: "src\modules\extbans\*.dll"; DestDir: "{app}\modules\extbans"; Flags: ignoreversion -Source: "src\modules\third\*.dll"; DestDir: "{app}\modules\third"; Flags: ignoreversion skipifsourcedoesntexist - -Source: "c:\dev\unrealircd-5-libs\pcre2\bin\pcre*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "c:\dev\unrealircd-5-libs\argon2\vs2015\build\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "c:\dev\unrealircd-5-libs\libsodium\bin\x64\Release\v142\dynamic\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "c:\dev\unrealircd-5-libs\c-ares\msvc\cares\dll-release\cares.dll"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "c:\dev\unrealircd-5-libs\libressl\bin\openssl.exe"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "c:\dev\unrealircd-5-libs\libressl\bin\*.dll"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "c:\dev\unrealircd-5-libs\setacl.exe"; DestDir: "{app}\tmp"; Flags: ignoreversion - -#ifdef USE_CURL -Source: "c:\dev\unrealircd-5-libs\curl\builds\libcurl-vc-x64-release-dll-ssl-dll-cares-dll-ipv6-obj-lib\libcurl.dll"; DestDir: "{app}\bin"; Flags: ignoreversion -Source: "doc\conf\tls\curl-ca-bundle.crt"; DestDir: "{app}\conf\tls"; Flags: ignoreversion -#endif +; Documentation etc. +Source: "doc\Donation"; DestDir: "{app}\doc"; DestName: "Donation.txt"; Flags: ignoreversion +Source: "LICENSE"; DestDir: "{app}\doc"; DestName: "LICENSE.txt"; Flags: ignoreversion +Source: "doc\*.*"; DestDir: "{app}\doc"; Flags: ignoreversion +Source: "doc\technical\*.*"; DestDir: "{app}\doc\technical"; Flags: ignoreversion [Dirs] Name: "{app}\tmp" @@ -156,7 +163,7 @@ if CurStep = ssPostInstall then end; //********************************************************************************* -// Checks if SSL/TLS cert file exists +// Checks if TLS cert file exists //********************************************************************************* procedure CurPageChanged(CurPage: Integer); diff --git a/src/windows/unrealsvc.c b/src/windows/unrealsvc.c index adb6d82..ed360ec 100644 --- a/src/windows/unrealsvc.c +++ b/src/windows/unrealsvc.c @@ -83,11 +83,11 @@ int main(int argc, char *argv[]) { uChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &info); CloseServiceHandle(hService); printf("\n[!!!] IMPORTANT: By default the network service user cannot write to the \n" - "UnrealIRCd 5 folder and this will make UnrealIRCd fail to boot without\n" + "UnrealIRCd 6 folder and this will make UnrealIRCd fail to boot without\n" "writing any meaningful error to the log files.\n" "You have two options:\n" "1) Manually grant FULL permissions to NT AUTHORITY\\NetworkService\n" - " for the UnrealIRCd 5 folder, all its subfolders and files.\n" + " for the UnrealIRCd 6 folder, all its subfolders and files.\n" "OR, easier and recommended:\n" "2) just re-run the UnrealIRCd installer and select 'Install as a service',\n" " which sets all the necessary permissions automatically.\n"); diff --git a/src/windows/win.c b/src/windows/win.c index 5047bb7..2cfa918 100644 --- a/src/windows/win.c +++ b/src/windows/win.c @@ -143,7 +143,7 @@ int GetOSName(char *pszOS) osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) + if ( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) return -1; // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. @@ -151,7 +151,7 @@ int GetOSName(char *pszOS) pGNSI = (PGNSI) GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); - if(NULL != pGNSI) + if (NULL != pGNSI) pGNSI(&si); else GetSystemInfo(&si); @@ -164,14 +164,14 @@ int GetOSName(char *pszOS) if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1 ) { - if( osvi.wProductType == VER_NT_WORKSTATION ) + if ( osvi.wProductType == VER_NT_WORKSTATION ) StringCchCat(pszOS, OSVER_SIZE, TEXT("Windows 7 ")); else StringCchCat(pszOS, OSVER_SIZE, TEXT("Windows Server 2008 R2 " )); } if ( osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0 ) { - if( osvi.wProductType == VER_NT_WORKSTATION ) + if ( osvi.wProductType == VER_NT_WORKSTATION ) StringCchCat(pszOS, OSVER_SIZE, TEXT("Windows Vista ")); else StringCchCat(pszOS, OSVER_SIZE, TEXT("Windows Server 2008 " )); @@ -243,13 +243,13 @@ int GetOSName(char *pszOS) if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) { - if( GetSystemMetrics(SM_SERVERR2) ) + if ( GetSystemMetrics(SM_SERVERR2) ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Windows Server 2003 R2, ")); else if ( osvi.wSuiteMask==VER_SUITE_STORAGE_SERVER ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Windows Storage Server 2003")); else if ( osvi.wSuiteMask==VER_SUITE_WH_SERVER ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Windows Home Server")); - else if( osvi.wProductType == VER_NT_WORKSTATION && + else if ( osvi.wProductType == VER_NT_WORKSTATION && si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) { StringCchCat(pszOS, OSVER_SIZE, TEXT( "Windows XP Professional x64 Edition")); @@ -261,17 +261,17 @@ int GetOSName(char *pszOS) { if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 ) { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + if ( osvi.wSuiteMask & VER_SUITE_DATACENTER ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Datacenter Edition for Itanium-based Systems" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Enterprise Edition for Itanium-based Systems" )); } else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + if ( osvi.wSuiteMask & VER_SUITE_DATACENTER ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Datacenter x64 Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Enterprise x64 Edition" )); else StringCchCat(pszOS, OSVER_SIZE, TEXT( "Standard x64 Edition" )); } @@ -280,9 +280,9 @@ int GetOSName(char *pszOS) { if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Compute Cluster Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + else if ( osvi.wSuiteMask & VER_SUITE_DATACENTER ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Datacenter Edition" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Enterprise Edition" )); else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Web Edition" )); @@ -294,7 +294,7 @@ int GetOSName(char *pszOS) if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) { StringCchCat(pszOS, OSVER_SIZE, TEXT("Windows XP ")); - if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) + if ( osvi.wSuiteMask & VER_SUITE_PERSONAL ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Home Edition" )); else StringCchCat(pszOS, OSVER_SIZE, TEXT( "Professional" )); } @@ -309,9 +309,9 @@ int GetOSName(char *pszOS) } else { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + if ( osvi.wSuiteMask & VER_SUITE_DATACENTER ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Datacenter Server" )); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + else if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) StringCchCat(pszOS, OSVER_SIZE, TEXT( "Advanced Server" )); else StringCchCat(pszOS, OSVER_SIZE, TEXT( "Server" )); } @@ -319,7 +319,7 @@ int GetOSName(char *pszOS) // Include service pack (if any) and build number. - if( _tcslen(osvi.szCSDVersion) > 0 ) + if ( _tcslen(osvi.szCSDVersion) > 0 ) { StringCchCat(pszOS, OSVER_SIZE, TEXT(" ") ); StringCchCat(pszOS, OSVER_SIZE, osvi.szCSDVersion); diff --git a/src/windows/wingui.rc b/src/windows/wingui.rc index ec3c091..c3023be 100644 --- a/src/windows/wingui.rc +++ b/src/windows/wingui.rc @@ -143,9 +143,9 @@ BEGIN CONTROL "",IDC_GRAY,"Button",BS_OWNERDRAW | WS_TABSTOP,137,21,13,12 END -SSLPASS DIALOG 0, 0, 174, 57 +TLSKEY DIALOG 0, 0, 174, 57 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "SSL Private Key Password" +CAPTION "TLS Private Key Password" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,33,35,50,14 @@ -261,7 +261,7 @@ BEGIN BOTTOMMARGIN, 30 END - "SSLPASS", DIALOG + "TLSKEY", DIALOG BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 167 diff --git a/unrealircd.in b/unrealircd.in index ef03bb4..bdfdc82 100644 --- a/unrealircd.in +++ b/unrealircd.in @@ -2,6 +2,11 @@ PID_FILE="@PIDFILE@" PID_BACKUP="@PIDFILE@.bak" + +# When built with --with-asan, ASan does not dump core by default because +# older gcc/clang might dump a 16TB core file. We explicitly enable it here. +export ASAN_OPTIONS="abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1:log_path=@TMPDIR@/unrealircd_asan:detect_leaks=0" + if [ ! -f @BINDIR@/unrealircd ]; then echo "ERROR: Could not find the IRCd binary (@BINDIR@/unrealircd)" echo "This could mean two things:" @@ -14,9 +19,6 @@ if [ "$1" = "start" ] ; then if [ -r $PID_FILE ] ; then mv -f $PID_FILE $PID_BACKUP fi - # When built with --with-asan, ASan does not dump core by default because - # older gcc/clang might dump a 16TB core file. We explicitly enable it here. - export ASAN_OPTIONS="abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1:log_path=@TMPDIR@/unrealircd_asan:detect_leaks=0" # Check if ~/Unrealxxx/unrealircd.conf exists but the file # ~/unrealircd/conf/unrealircd.conf does not. @@ -132,8 +134,6 @@ elif [ "$1" = "version" ] ; then @BINDIR@/unrealircd -v elif [ "$1" = "gencloak" ] ; then @BINDIR@/unrealircd -k -elif [ "$1" = "upgrade-conf" ] ; then - @BINDIR@/unrealircd -U elif [ "$1" = "backtrace" ] ; then cd @TMPDIR@ @@ -227,9 +227,15 @@ elif [ "$1" = "spki" -o "$1" = "spkifp" ] ; then CERT="@CONFDIR@/tls/server.cert.pem" if [ "$2" != "" ]; then CERT="$2" + else + echo "NOTE: This script uses the default certificate location (any set::tls settings" + echo "are ignored). If this is not what you want then specify a certificate" + echo "explicitly like this: ./unrealircd spkifp conf/tls/example.pem" + echo "" fi if [ ! -f "$CERT" ]; then echo "Could not open certificate: $CERT" + echo "You can specify a certificate like this: ./unrealircd spkifp conf/tls/example.pem" exit 1 fi openssl x509 -noout -in "$CERT" -pubkey | openssl asn1parse -noout -inform pem -out @TMPDIR@/tmp.public.key @@ -295,6 +301,7 @@ elif [ "$1" = "hot-patch" -o "$1" = "cold-patch" ] ; then fi elif [ "$1" = "upgrade" ] ; then @BINDIR@/unrealircd-upgrade-script $* + exit elif [ "$1" = "genlinkblock" ] ; then @BINDIR@/unrealircd -L else