.gitignore | 2 + AUTHORS | 15 +- ChangeLog | 40 +- Makefile.am | 26 +- NEWS | 20 + autogen.sh | 27 +- config.guess | 36 +- config.sub | 52 ++- configure.in | 63 ++- contrib/Debian/.gitignore | 9 + contrib/Debian/Makefile.am | 6 +- contrib/Debian/changelog | 21 + contrib/Debian/control | 13 +- contrib/Debian/ngircd.init | 79 ++- contrib/Debian/rules | 10 +- contrib/MacOSX/.gitignore | 1 + contrib/MacOSX/Makefile.am | 11 +- contrib/MacOSX/de.barton.ngircd.plist.tmpl | 4 + contrib/MacOSX/ngIRCd.pmdoc/01ngircd-contents.xml | 1 + contrib/MacOSX/ngIRCd.pmdoc/01ngircd.xml | 1 + contrib/MacOSX/ngIRCd.pmdoc/02de-contents.xml | 1 + contrib/MacOSX/ngIRCd.pmdoc/02de.xml | 1 + contrib/MacOSX/ngIRCd.pmdoc/Makefile.am | 18 + contrib/MacOSX/ngIRCd.pmdoc/index.xml | 188 ++++++ contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj | 5 +- contrib/MacOSX/postinstall.sh | 42 ++ contrib/MacOSX/preinstall.sh | 25 + contrib/Makefile.am | 17 +- contrib/ngircd.spec | 2 +- doc/FAQ.txt | 13 +- doc/Makefile.am | 5 +- doc/Platforms.txt | 25 +- doc/Protocol.txt | 7 +- doc/SSL.txt | 72 ++- doc/Services.txt | 70 ++ doc/sample-ngircd.conf | 59 ++- doc/src/Makefile.am | 6 +- man/ngircd.8.tmpl | 4 +- man/ngircd.conf.5.tmpl | 53 ++- src/.gitignore | 1 + src/ipaddr/ng_ipaddr.h | 1 + src/ngircd/.gitignore | 2 - src/ngircd/Makefile.am | 12 +- src/ngircd/array.c | 8 + src/ngircd/channel.c | 243 +++++--- src/ngircd/channel.h | 21 +- src/ngircd/client.c | 122 +++-- src/ngircd/client.h | 11 +- src/ngircd/conf-ssl.h | 47 ++ src/ngircd/conf.c | 184 ++++-- src/ngircd/conf.h | 32 +- src/ngircd/conn-func.c | 13 +- src/ngircd/conn-func.h | 15 +- src/ngircd/conn-ssl.c | 727 +++++++++++++++++++++ src/ngircd/conn-ssl.h | 28 + src/ngircd/conn.c | 517 +++++++++++----- src/ngircd/conn.h | 24 +- src/ngircd/defines.h | 2 +- src/ngircd/irc-channel.c | 166 ++++-- src/ngircd/irc-info.c | 245 +++++--- src/ngircd/irc-info.h | 1 + src/ngircd/irc-login.c | 419 +++++++++--- src/ngircd/irc-login.h | 19 +- src/ngircd/irc-mode.c | 33 +- src/ngircd/irc-op.c | 179 ++++-- src/ngircd/irc-server.c | 19 +- src/ngircd/irc-write.c | 47 +- src/ngircd/irc-write.h | 35 +- src/ngircd/irc.c | 322 +++++++-- src/ngircd/irc.h | 21 +- src/ngircd/log.c | 33 +- src/ngircd/match.c | 12 + src/ngircd/match.h | 1 + src/ngircd/messages.h | 13 +- src/ngircd/ngircd.c | 57 ++- src/ngircd/numeric.c | 175 ++++-- src/ngircd/parse.c | 28 +- src/ngircd/resolve.c | 19 +- src/ngircd/resolve.h | 1 - src/portab/Makefile.am | 6 +- src/portab/portab.h | 12 + src/portab/strtok_r.c | 27 + src/portab/waitpid.c | 31 + src/testsuite/.gitignore | 15 +- src/testsuite/Makefile.am | 62 ++- src/testsuite/README | 65 ++- src/testsuite/getpid.sh | 4 +- src/testsuite/invite-test.e | 113 ++++ src/testsuite/join-test.e | 68 ++ src/testsuite/kick-test.e | 112 ++++ src/testsuite/message-test.e | 133 ++++ src/testsuite/misc-test.e | 127 ++++- src/testsuite/ngircd-test.conf | 16 - src/testsuite/ngircd-test1.conf | 44 ++ src/testsuite/ngircd-test2.conf | 26 + src/testsuite/opless-channel-test.e | 32 + src/testsuite/server-link-test.e | 52 ++ src/testsuite/start-server.sh | 26 +- src/testsuite/start-server1 | 7 + src/testsuite/start-server2 | 7 + src/testsuite/stop-server.sh | 13 +- src/testsuite/stop-server1 | 7 + src/testsuite/stop-server2 | 7 + src/testsuite/who-test.e | 38 +- src/tool/tool.c | 39 +- src/tool/tool.h | 6 +- 106 files changed, 4831 insertions(+), 1169 deletions(-) diff --git a/.gitignore b/.gitignore index 9c344a9..6a7ccc8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ missing .deps *.a *.o +debian +build-stamp-ngircd* diff --git a/AUTHORS b/AUTHORS index 24845fd..bb7035f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,9 +24,16 @@ Florian Westphal, (fw) Contributors ~~~~~~~~~~~~ -Goetz Hoffart, (goetz) -Ilja Osthoff, (ilja) +Ali Shemiran, Benjamin Pineau, +Brandon Beresini, +Bryan Caldwell, +Dana Dahlstrom, +Eric Grunow, +Goetz Hoffart, +Ilja Osthoff, +Rolf Eike Beer, +Scott Perry, Sean Reifschneider, @@ -35,7 +42,3 @@ Code snippets J. Kercheval: pattern matching functions Patrick Powell, : snprintf()-function Andrew Tridgell & Martin Pool: strl{cpy|cat}()-functions - - --- -$Id: AUTHORS,v 1.13 2007/10/04 15:18:48 alex Exp $ diff --git a/ChangeLog b/ChangeLog index 18cadd7..8d730bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,37 @@ -- ChangeLog -- +ngIRCd Release 13 (2008-12-25) + + - Updated documentation, especially doc/Services.txt and doc/SSL.txt. + - Make the test suite work on OpenSolaris. + + ngIRCd 13~rc1 (2008-11-21): + - New version numer scheme :-) + - Initial support for IRC services, using a RFC1459 styel interface, + tested with IRCServices (http://www.ircservices.za.net/) version 5.1.13. + For this to work, ngIRCd now supports server-server links conforming + to RFC 1459. New ngircd.conf(5) option: ServiceMask. + - Support for SSL-encrypted server-server and client-server links using + OpenSSL (configure: --with-openssl) or GNUTLS (configure: --with-gnutls). + New ngircd.conf(5) options: SSLPorts, SSLKeyFile, SSLKeyFilePassword, + SSLCertFile, SSLDHFile, and SSLConnect. + - Server local channels have been implemented, prefix "&", that are only + visible to users of the same server and are not visible in the network. + In addition ngIRCd creates a "special" channel &SERVER on startup and logs + all the messages to it that a user with mode +s receives. + - New make target "osxpkg" to build a Mac OS X installer package. + - Debug mode: enable support for GNU libc memory tracing (see mtrace(3)). + - SysV init script: use LSB logging functions, if available. + - Added some more FAQ entries (regarding logging and IRC operators). + - Allow IRC operators to overwrite channel limits. + - Support for enhanced PRIVMSG and NOTICE message targets. + - More tests have been added to the test-suite ("make check"), and two + servers are started for testing server-server linking. + - Added a timestamp to log messages to the console. + - New configuration option "NoIdent" to disable IDENT lookups even if the + daemon is compiled with IDENT support. + ngIRCd 0.12.1 (2008-07-09) - Allow mixed line terminations (CR+LF/CR/LF) in non-RFC-compliant mode @@ -69,7 +100,7 @@ ngIRCd 0.11.0 (2008-01-15) ngIRCd 0.11.0-pre2 (2008-01-07) - SECURITY: IRC_PART could reference invalid memory, causing - ngircd to crash [from HEAD]. + ngircd to crash [from HEAD]. (CVE-2008-0285) ngIRCd 0.11.0-pre1 (2008-01-02) - Use dotted-decimal IP address if hostname is >= 64. @@ -103,12 +134,13 @@ ngIRCd 0.11.0 (2008-01-15) ngIRCd 0.10.4 (2008-01-07) - SECURITY: IRC_PART could reference invalid memory, causing - ngircd to crash [from HEAD]. + ngircd to crash [from HEAD]. (CVE-2008-0285) ngIRCd 0.10.3 (2007-08-01) - SECURITY: Fixed a severe bug in handling JOIN commands, which could cause the server to crash. Thanks to Sebastian Vesper, . + (CVE-2007-6062) ngIRCd 0.10.2 (2007-06-08) @@ -243,13 +275,13 @@ ngIRCd 0.8.3 (2005-02-03) - Fixed a bug that could case a root exploit when the daemon is compiled to do IDENT lookups and is logging to syslog. Bug discovered by CoKi, , thanks a lot! - (http://www.nosystem.com.ar/advisories/advisory-11.txt) + (CVE-2005-0226; http://www.nosystem.com.ar/advisories/advisory-11.txt) ngIRCd 0.8.2 (2005-01-26) - Added doc/SSL.txt to distribution. - Fixed a buffer overflow that could cause the daemon to crash. Bug found - by Florian Westphal, . + by Florian Westphal, . (CVE-2005-0199) - Fixed a possible buffer underrun when reading the MOTD file. Thanks to Florian Westphal, . - Fixed detection of IRC lines which are too long to send. Detected by diff --git a/Makefile.am b/Makefile.am index d678f17..701bf6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ SUBDIRS = doc src man contrib clean-local: rm -f build-stamp* + rm -rf ngircd.dest maintainer-clean-local: rm -rf autom4te.cache @@ -43,6 +44,29 @@ rpm: distcheck deb: [ -f debian/rules ] || ln -s contrib/Debian debian - dpkg-buildpackage -rfakeroot + dpkg-buildpackage -rfakeroot -i + +osxpkg: + @packagemaker >/dev/null 2>&1; [ $$? -ge 1 ] \ + || ( echo; echo "Error: \"packagemaker\" not found!"; echo; exit 2) + make clean + ./configure --prefix=/opt/ngircd + make xcode + make -C contrib/MacOSX de.barton.ngircd.plist + mkdir -p ngircd.dest/opt/ngircd/sbin + DESTDIR="$$PWD/ngircd.dest" make -C doc install + DESTDIR="$$PWD/ngircd.dest" make -C contrib install + DESTDIR="$$PWD/ngircd.dest" make -C man install + cp contrib/MacOSX/build/Default/ngIRCd \ + ngircd.dest/opt/ngircd/sbin/ngircd + rm ngircd.dest/opt/ngircd/etc/ngircd.conf + echo "Have a nice day IRCing!" >ngircd.dest/opt/ngircd/etc/ngircd.motd + chmod -R a-s,og-w,a+rX ngircd.dest + cd contrib/MacOSX && packagemaker \ + --doc ngIRCd.pmdoc \ + --out ../../$(distdir).mpkg + rm -f $(distdir).mpkg.zip + zip -ro9 $(distdir).mpkg.zip $(distdir).mpkg + rm -rf ngircd.dest $(distdir).mpkg # -eof- diff --git a/NEWS b/NEWS index 7607aef..92a6b35 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,26 @@ -- NEWS -- +ngIRCd Release 13 (2008-12-25) + + ngIRCd 13~rc1 (2008-11-21): + - New version numer scheme :-) + - Initial support for IRC services, using a RFC1459 styel interface, + tested with IRCServices (http://www.ircservices.za.net/) version 5.1.13. + For this to work, ngIRCd now supports server-server links conforming + to RFC 1459. New ngircd.conf(5) option: ServiceMask. + - Support for SSL-encrypted server-server and client-server links using + OpenSSL (configure: --with-openssl) or GNUTLS (configure: --with-gnutls). + New ngircd.conf(5) options: SSLPorts, SSLKeyFile, SSLKeyFilePassword, + SSLCertFile, SSLDHFile, and SSLConnect. + - Server local channels have been implemented, prefix "&", that are only + visible to users of the same server and are not visible in the network. + In addition ngIRCd creates a "special" channel &SERVER on startup and logs + all the messages to it that a user with mode +s receives. + - New make target "osxpkg" to build a Mac OS X installer package. + - New configuration option "NoIdent" to disable IDENT lookups even if the + daemon is compiled with IDENT support. + ngIRCd 0.12.1 (2008-07-09) - Add option aliases -V (for --version) and -h (for --help). diff --git a/autogen.sh b/autogen.sh index dfb1622..bd438cd 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,7 +1,7 @@ #!/bin/sh # # ngIRCd -- The Next Generation IRC Daemon -# Copyright (c)2001-2004 Alexander Barton +# Copyright (c)2001-2008 Alexander Barton # # 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 @@ -9,11 +9,8 @@ # (at your option) any later version. # Please read the file COPYING, README and AUTHORS for more information. # -# $Id: autogen.sh,v 1.15 2007/10/07 13:02:15 alex Exp $ -# - -# -# Usage: [VAR=] ./autogen.sh [] +# Usage: +# [VAR=] ./autogen.sh [] # # This script generates the ./configure script using GNU automake and # GNU autoconf. It tries to be smart in finding the correct/usable/available @@ -124,20 +121,6 @@ if [ -z "$EXIST" ]; then fi [ "$VERBOSE" = "1" ] && echo "Using \"$EXIST\" to test for tools." -# We want to use GNU automake 1.9, if available (WANT_AUTOMAKE is used by -# the wrapper scripts of Gentoo Linux, AUTOMAKE_VERSION is used by OpenBSD); -# same applies for GNU autoconf, we want to use version 2.59. -- But only -# set these preferences if not already set! -if [ -z "$AUTOMAKE_VERSION" -a -z "$WANT_AUTOMAKE" ]; then - AUTOMAKE_VERSION=1.9 - WANT_AUTOMAKE=1.9 -fi -if [ -z "$AUTOCONF_VERSION" -a -z "$WANT_AUTOCONF" ]; then - AUTOCONF_VERSION=2.59 - WANT_AUTOCONF=2.59 -fi -export AUTOMAKE_VERSION WANT_AUTOMAKE AUTOCONF_VERSION WANT_AUTOCONF - # Try to detect the needed tools when no environment variable already # specifies one: echo "Searching tools ..." @@ -155,12 +138,12 @@ echo "Searching tools ..." [ -z "$GO" -a $# -gt 0 ] && GO=1 # Verify that all tools have been found -[ -z "$AUTOCONF" ] && Notfound autoconf +[ -z "$ACLOCAL" ] && Notfound aclocal [ -z "$AUTOHEADER" ] && Notfound autoheader [ -z "$AUTOMAKE" ] && Notfound automake [ -z "$AUTOCONF" ] && Notfound autoconf -export AUTOCONF AUTOHEADER AUTOMAKE AUTOCONF +export ACLOCAL AUTOHEADER AUTOMAKE AUTOCONF # Generate files echo "Generating files ..." diff --git a/config.guess b/config.guess index 0f0fe71..f32079a 100644 --- a/config.guess +++ b/config.guess @@ -1,10 +1,10 @@ #! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, -# Inc. +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# Free Software Foundation, Inc. -timestamp='2007-03-06' +timestamp='2008-01-23' # 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 @@ -56,8 +56,8 @@ version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 -Free Software Foundation, Inc. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 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." @@ -330,7 +330,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; - i86pc:SunOS:5.*:*) + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) @@ -532,7 +532,7 @@ EOF echo rs6000-ibm-aix3.2 fi exit ;; - *:AIX:*:[45]) + *:AIX:*:[456]) 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 IBM_ARCH=rs6000 @@ -793,12 +793,15 @@ EOF exit ;; *:Interix*:[3456]*) case ${UNAME_MACHINE} in - x86) + x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; EM64T | authenticamd) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks @@ -833,7 +836,14 @@ EOF echo ${UNAME_MACHINE}-pc-minix exit ;; arm*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + eval $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-gnu + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu @@ -954,8 +964,8 @@ EOF x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit ;; - xtensa:Linux:*:*) - echo xtensa-unknown-linux-gnu + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so @@ -1474,9 +1484,9 @@ This script, last modified $timestamp, has failed to recognize the operating system you are using. It is advised that you download the most up to date version of the config scripts from - http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD and - http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD If the version you run ($0) is already up to date, please send the following data and any information you think might be diff --git a/config.sub b/config.sub index 5defff6..6759825 100644 --- a/config.sub +++ b/config.sub @@ -1,10 +1,10 @@ #! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, -# Inc. +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 +# Free Software Foundation, Inc. -timestamp='2007-01-18' +timestamp='2008-01-16' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -72,8 +72,8 @@ Report bugs and patches to ." version="\ GNU config.sub ($timestamp) -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 -Free Software Foundation, Inc. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006, 2007, 2008 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." @@ -369,10 +369,14 @@ case $basic_machine in | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ - | xstormy16-* | xtensa-* \ + | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-*) ;; + # 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) @@ -443,6 +447,14 @@ case $basic_machine in basic_machine=ns32k-sequent os=-dynix ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; c90) basic_machine=c90-cray os=-unicos @@ -475,8 +487,8 @@ case $basic_machine in basic_machine=craynv-cray os=-unicosmp ;; - cr16c) - basic_machine=cr16c-unknown + cr16) + basic_machine=cr16-unknown os=-elf ;; crds | unos) @@ -668,6 +680,14 @@ case $basic_machine in basic_machine=m68k-isi os=-sysv ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; m88k-omron*) basic_machine=m88k-omron ;; @@ -683,6 +703,10 @@ case $basic_machine in basic_machine=i386-pc os=-mingw32 ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; miniframe) basic_machine=m68000-convergent ;; @@ -809,6 +833,14 @@ case $basic_machine in 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 ;; @@ -1017,6 +1049,10 @@ case $basic_machine in basic_machine=tic6x-unknown os=-coff ;; + tile*) + basic_machine=tile-unknown + os=-linux-gnu + ;; tx39) basic_machine=mipstx39-unknown ;; diff --git a/configure.in b/configure.in index f7a321f..be03945 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ # -- Initialisation -- AC_PREREQ(2.50) -AC_INIT(ngircd, 0.12.1) +AC_INIT(ngircd, 13) AC_CONFIG_SRCDIR(src/ngircd/ngircd.c) AC_CANONICAL_TARGET AM_INIT_AUTOMAKE(1.6) @@ -136,11 +136,10 @@ AC_FUNC_FORK AC_FUNC_STRFTIME AC_CHECK_FUNCS([ \ - bind gethostbyaddr gethostbyname gethostname inet_ntoa malloc memmove \ - memset realloc setsid setsockopt socket strcasecmp strchr strcspn strerror \ - strstr waitpid],,AC_MSG_ERROR([required function missing!])) + bind gethostbyaddr gethostbyname gethostname inet_ntoa \ + setsid setsockopt socket strcasecmp waitpid],,AC_MSG_ERROR([required function missing!])) -AC_CHECK_FUNCS(inet_aton isdigit sigaction snprintf vsnprintf strdup strlcpy strlcat) +AC_CHECK_FUNCS(inet_aton isdigit sigaction snprintf vsnprintf strdup strlcpy strlcat strtok_r) # -- Configuration options -- @@ -310,6 +309,52 @@ if test "$x_io_backend" = "none"; then AC_MSG_ERROR([No useabe IO API activated/found!?]) fi +# use SSL? + +AC_ARG_WITH(openssl, + [ --with-openssl enable SSL support using OpenSSL], + [ if test "$withval" = "yes"; then + if test "$withval" != "yes"; then + CFLAGS="-I$withval/include $CFLAGS" + CPPFLAGS="-I$withval/include $CPPFLAGS" + LDFLAGS="-L$withval/lib $LDFLAGS" + fi + AC_CHECK_LIB(crypto, BIO_s_mem) + AC_CHECK_LIB(ssl, SSL_library_init) + AC_CHECK_FUNCS(SSL_library_init, x_ssl_openssl=yes, + AC_MSG_ERROR([Can't enable openssl]) + ) + fi + ] +) + +AC_ARG_WITH(gnutls, + [ --with-gnutls enable SSL support using gnutls], + [ if test "$withval" = "yes"; then + if test "$withval" != "yes"; then + CFLAGS="-I$withval/include $CFLAGS" + CPPFLAGS="-I$withval/include $CPPFLAGS" + LDFLAGS="-L$withval/lib $LDFLAGS" + fi + AC_CHECK_LIB(gnutls, gnutls_global_init) + AC_CHECK_FUNCS(gnutls_global_init, x_ssl_gnutls=yes, + AC_MSG_ERROR([Can't enable gnutls]) + ) + fi + ] +) + +x_ssl_lib="no" +if test "$x_ssl_gnutls" = "yes"; then + if test "$x_ssl_openssl" = "yes";then + AC_MSG_ERROR([Cannot enable both gnutls and openssl]) + fi + x_ssl_lib=gnutls +fi +if test "$x_ssl_openssl" = "yes"; then + x_ssl_lib=openssl +fi + # use TCP wrappers? x_tcpwrap_on=no @@ -458,6 +503,7 @@ AC_ARG_ENABLE(debug, if test "$x_debug_on" = "yes"; then AC_DEFINE(DEBUG, 1) test "$GCC" = "yes" && CFLAGS="-pedantic $CFLAGS" + AC_CHECK_FUNCS(mtrace) fi # enable "strict RFC rules"? @@ -498,6 +544,7 @@ AC_OUTPUT([ \ contrib/Debian/Makefile \ contrib/MacOSX/Makefile \ contrib/MacOSX/ngIRCd.xcodeproj/Makefile \ + contrib/MacOSX/ngIRCd.pmdoc/Makefile \ ]) type dpkg >/dev/null 2>&1 @@ -587,7 +634,11 @@ echo $ECHO_N " I/O backend: $ECHO_C" echo "\"$x_io_backend\"" echo $ECHO_N " IPv6 protocol: $ECHO_C" -echo "$x_ipv6_on" +echo $ECHO_N "$x_ipv6_on $ECHO_C" + +echo $ECHO_N " SSL support: $ECHO_C" +echo "$x_ssl_lib" + echo # -eof- diff --git a/contrib/Debian/.gitignore b/contrib/Debian/.gitignore new file mode 100644 index 0000000..f2a4c05 --- /dev/null +++ b/contrib/Debian/.gitignore @@ -0,0 +1,9 @@ +*.log +*.debhelper +*.substvars +files +ngircd/ +ngircd-full/ +ngircd-full.default +ngircd-full.init +ngircd-full.postinst diff --git a/contrib/Debian/Makefile.am b/contrib/Debian/Makefile.am index 151f405..f3d02a5 100644 --- a/contrib/Debian/Makefile.am +++ b/contrib/Debian/Makefile.am @@ -1,6 +1,6 @@ # # ngIRCd -- The Next Generation IRC Daemon -# Copyright (c)2001-2003 by Alexander Barton (alex@barton.de) +# Copyright (c)2001-2008 Alexander Barton (alex@barton.de) # # 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 @@ -8,11 +8,9 @@ # (at your option) any later version. # Please read the file COPYING, README and AUTHORS for more information. # -# $Id: Makefile.am,v 1.4 2004/05/11 00:32:31 alex Exp $ -# EXTRA_DIST = rules changelog compat control copyright \ - ngircd.init ngircd.postinst + ngircd.init ngircd.default ngircd.postinst maintainer-clean-local: rm -f Makefile Makefile.in diff --git a/contrib/Debian/changelog b/contrib/Debian/changelog index a1647c4..1e1b0e0 100644 --- a/contrib/Debian/changelog +++ b/contrib/Debian/changelog @@ -1,3 +1,24 @@ +ngircd (13-0ab1) unstable; urgency=low + + * New "upstream" release: ngIRCd 13. + + -- Alexander Barton Thu, 25 Dec 2008 23:09:58 +0100 + +ngircd (13~rc1-0ab1) unstable; urgency=low + + * New "upstream" release candidate 1 for ngIRCd Release 13. + + -- Alexander Barton Fri, 21 Nov 2008 22:04:41 +0100 + +ngircd (0.12.1-0ab1+dev) unstable; urgency=low + + * Update package for testing the new "upstream" features: + - Support for IRC services (see http://www.ircservices.za.net), + - Encrypted connections using GNU TLS (ngircd-full), + - Support for the IPv6 protocol (ngircd-full). + + -- Alexander Barton Fri, 03 Oct 2008 16:22:55 +0200 + ngircd (0.12.1-0ab1) unstable; urgency=low * New "upstream" release ngIRCd 0.12.1. diff --git a/contrib/Debian/control b/contrib/Debian/control index 23ab280..543c5ad 100644 --- a/contrib/Debian/control +++ b/contrib/Debian/control @@ -2,8 +2,8 @@ Source: ngircd Section: net Priority: optional Maintainer: Alexander Barton -Build-Depends: debhelper (>> 4.0.0), libz-dev, libwrap-dev, libident-dev -Standards-Version: 3.7.2.1 +Build-Depends: debhelper (>> 4.0.0), libz-dev, libwrap0-dev, libident-dev, libgnutls-dev +Standards-Version: 3.8.0 Package: ngircd Architecture: any @@ -15,7 +15,9 @@ Description: A lightweight daemon for the Internet Relay Chat (IRC) IRCd like many others. . This package contains the "standard distribution", including support for - syslog logging and compressed server-links using zlib. + syslog logging and compressed server-links using zlib. Please have a look + at the "ngircd-full" package if you need advanced functionality like support + for IPv6 or SSL. . Advantages of ngIRCd: - no problems with servers using changing/non-static IP addresses. @@ -36,8 +38,9 @@ Description: A lightweight daemon for the Internet Relay Chat (IRC) network. It is written from scratch and is not based upon the original IRCd like many others. . - This package includes support for TCP wrappers and IDENT requests in - addition to the features of the "standard package". + In addition to the features of the "standard package", this package + includes support for TCP wrappers, IDENT requests, the IPv6 protocol and + SSL encrypted client and server links. . Advantages of ngIRCd: - no problems with servers using changing/non-static IP addresses. diff --git a/contrib/Debian/ngircd.init b/contrib/Debian/ngircd.init index c4a1479..7262429 100755 --- a/contrib/Debian/ngircd.init +++ b/contrib/Debian/ngircd.init @@ -1,16 +1,15 @@ #!/bin/sh # # ngIRCd start and stop script for Debian-based systems -# -# $Id: ngircd.init,v 1.7 2006/12/26 14:43:46 alex Exp $ +# Copyright 2008 Alexander Barton # ### BEGIN INIT INFO # Provides: ircd -# Required-Start: $local_fs -# Required-Stop: $local_fs -# Should-Start: $syslog $network -# Should-Stop: $syslog $network +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Should-Start: $syslog +# Should-Stop: $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Next Generation IRC Server @@ -25,54 +24,76 @@ PARAMS="" test -h "$0" && me=`readlink $0` || me="$0" BASENAME=`basename $me` -test -f /etc/default/$BASENAME && . /etc/default/$BASENAME +test -r /etc/default/$BASENAME && . /etc/default/$BASENAME test -x $DAEMON || exit 0 +# LSB compatibility functions that become used if there is no local +# include file available. +log_daemon_msg() { + echo -n "$*" +} +log_end_msg() { + [ "$1" == "0" ] && echo "." || echo " failed!" +} +log_failure_msg() { + echo "$*" +} + +# Include LSB functions, if available: +test -r /lib/lsb/init-functions && . /lib/lsb/init-functions + Check_Config() { + # Make sure that the configuration of ngIRCd is valid: $DAEMON --configtest >/dev/null 2>&1 if [ $? -ne 0 ]; then - echo "Configuration of $NAME is not valide, won't (re)start!" - echo "Please run \"$DAEMON --configtest\" manually and fix it up ..." + log_failure_msg "Configuration of $NAME is not valid, won't (re)start!" + log_failure_msg "Run \"$DAEMON --configtest\" and fix it up ..." exit 1 fi -} - -Try_Start() -{ - [ ! -d /var/run/ircd ] || chown irc:irc /var/run/ircd - start-stop-daemon --start --quiet --exec $DAEMON -- $PARAMS - if [ $? -ne 0 ]; then - echo "$NAME failed!" - exit 1 + # Make sure the PID file directory exists and is writable: + if [ ! -d /var/run/ircd ]; then + mkdir -p /var/run/ircd fi - echo "$NAME." + chown irc:irc /var/run/ircd } case "$1" in start) Check_Config - echo -n "Starting $DESC: " - Try_Start + log_daemon_msg "Starting $DESC" "$NAME" + start-stop-daemon --start \ + --quiet --exec $DAEMON -- $PARAMS + log_end_msg $? ;; stop) - echo -n "Stopping $DESC: " - start-stop-daemon --stop --quiet --pidfile /var/run/ircd/ngircd.pid --exec $DAEMON \ - && echo "$NAME." \ - || echo "(none running)" + log_daemon_msg "Stopping $DESC" "$NAME" + [ -r /var/run/ircd/ngircd.pid ] \ + && PIDFILE="--pidfile /var/run/ircd/ngircd.pid" \ + || PIDFILE="" + start-stop-daemon --stop \ + --quiet --oknodo --exec $DAEMON $PIDFILE + log_end_msg $? ;; reload|force-reload) Check_Config - echo "Reloading $DESC configuration files." + log_daemon_msg "Reloading $DESC" "$NAME" start-stop-daemon --stop --signal 1 --quiet --exec $DAEMON + log_end_msg $? ;; restart) Check_Config - echo -n "Restarting $DESC: " - start-stop-daemon --stop --quiet --oknodo --exec $DAEMON + log_daemon_msg "Restarting $DESC" "$NAME" + [ -r /var/run/ircd/ngircd.pid ] \ + && PIDFILE="--pidfile /var/run/ircd/ngircd.pid" \ + || PIDFILE="" + start-stop-daemon --stop \ + --quiet --oknodo --exec $DAEMON $PIDFILE sleep 1 - Try_Start + start-stop-daemon --start \ + --quiet --exec $DAEMON -- $PARAMS + log_end_msg $? ;; *) N=/etc/init.d/$NAME diff --git a/contrib/Debian/rules b/contrib/Debian/rules index fabf6a3..5648846 100755 --- a/contrib/Debian/rules +++ b/contrib/Debian/rules @@ -1,7 +1,7 @@ #!/usr/bin/make -f # # ngIRCd -- The Next Generation IRC Daemon -# Copyright (c)2001-2003 by Alexander Barton (alex@barton.de) +# Copyright (c)2001-2008 Alexander Barton # # 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 @@ -11,8 +11,6 @@ # # debian/rules for ngIRCd # -# $Id: rules,v 1.3 2005/02/07 23:09:31 alex Exp $ -# # Based on the sample debian/rules that uses debhelper, # GNU copyright 1997 to 1999 by Joey Hess. # @@ -54,7 +52,9 @@ configure-ngircd-full: configure --prefix=/usr \ --sysconfdir=/etc/ngircd \ --mandir=\$${prefix}/share/man \ - --with-syslog --with-zlib --with-tcp-wrappers --with-ident + --with-syslog --with-zlib \ + --with-gnutls --with-ident --with-tcp-wrappers \ + --enable-ipv6 build: dh_clean -k @@ -88,7 +88,7 @@ clean: rm -f $(CURDIR)/debian/ngircd-full.postinst # Add here commands to clean up after the build process: - -$(MAKE) clean + [ ! -f Makefile ] || $(MAKE) distclean ifneq "$(wildcard /usr/share/misc/config.sub)" "" cp -f /usr/share/misc/config.sub config.sub diff --git a/contrib/MacOSX/.gitignore b/contrib/MacOSX/.gitignore index 378eac2..260a9fd 100644 --- a/contrib/MacOSX/.gitignore +++ b/contrib/MacOSX/.gitignore @@ -1 +1,2 @@ build +de.barton.ngircd.plist diff --git a/contrib/MacOSX/Makefile.am b/contrib/MacOSX/Makefile.am index 306e41f..abd799c 100644 --- a/contrib/MacOSX/Makefile.am +++ b/contrib/MacOSX/Makefile.am @@ -8,12 +8,10 @@ # (at your option) any later version. # Please read the file COPYING, README and AUTHORS for more information. # -# $Id: Makefile.am,v 1.3 2008/02/17 15:31:15 alex Exp $ -# -SUBDIRS = ngIRCd.xcodeproj +SUBDIRS = ngIRCd.xcodeproj ngIRCd.pmdoc -EXTRA_DIST = de.barton.ngircd.plist.tmpl config.h cvs-version.h +EXTRA_DIST = de.barton.ngircd.plist.tmpl config.h preinstall.sh postinstall.sh SUFFIXES = .tmpl . @@ -36,7 +34,9 @@ install-sys-darwin: fi install-sys-darwin-root: de.barton.ngircd.plist - install -c -m 644 -b -o root -g wheel de.barton.ngircd.plist /Library/LaunchDaemons/de.barton.ngircd.plist + install -d -m 755 -o root -g wheel $(DESTDIR)/Library/LaunchDaemons + install -c -m 644 -b -o root -g wheel de.barton.ngircd.plist \ + $(DESTDIR)/Library/LaunchDaemons/de.barton.ngircd.plist @echo @echo " ** \"/Library/LaunchDaemons/de.barton.ngircd.plist\" has been installed," @echo " ** but is disabled. Use launchctl(8) to enable/run ngIRCd on Darwin/Mac OS X." @@ -44,6 +44,7 @@ install-sys-darwin-root: de.barton.ngircd.plist clean-local: rm -rf build + rm -f de.barton.ngircd.plist maintainer-clean-local: rm -f Makefile Makefile.in diff --git a/contrib/MacOSX/cvs-version.h b/contrib/MacOSX/cvs-version.h deleted file mode 100644 index e69de29..0000000 diff --git a/contrib/MacOSX/de.barton.ngircd.plist.tmpl b/contrib/MacOSX/de.barton.ngircd.plist.tmpl index f54333d..7f38d11 100644 --- a/contrib/MacOSX/de.barton.ngircd.plist.tmpl +++ b/contrib/MacOSX/de.barton.ngircd.plist.tmpl @@ -15,5 +15,9 @@ RunAtLoad + StandardErrorPath + /Library/Logs/ngIRCd.log + StandardOutPath + /Library/Logs/ngIRCd.log diff --git a/contrib/MacOSX/ngIRCd.pmdoc/01ngircd-contents.xml b/contrib/MacOSX/ngIRCd.pmdoc/01ngircd-contents.xml new file mode 100644 index 0000000..bc1e5a7 --- /dev/null +++ b/contrib/MacOSX/ngIRCd.pmdoc/01ngircd-contents.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contrib/MacOSX/ngIRCd.pmdoc/01ngircd.xml b/contrib/MacOSX/ngIRCd.pmdoc/01ngircd.xml new file mode 100644 index 0000000..66dbd1f --- /dev/null +++ b/contrib/MacOSX/ngIRCd.pmdoc/01ngircd.xml @@ -0,0 +1 @@ +de.barton.ngircd.daemon.pkg1../../ngircd.dest/parentscripts.postinstall.pathscripts.postinstall.isRelativeTypescripts.preupgrade.pathinstallFrom.isRelativeTypeversionscripts.preinstall.isRelativeTypeidentifierinstallTopreinstall.shpreinstall.sh \ No newline at end of file diff --git a/contrib/MacOSX/ngIRCd.pmdoc/02de-contents.xml b/contrib/MacOSX/ngIRCd.pmdoc/02de-contents.xml new file mode 100644 index 0000000..bc1e5a7 --- /dev/null +++ b/contrib/MacOSX/ngIRCd.pmdoc/02de-contents.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/contrib/MacOSX/ngIRCd.pmdoc/02de.xml b/contrib/MacOSX/ngIRCd.pmdoc/02de.xml new file mode 100644 index 0000000..59b5202 --- /dev/null +++ b/contrib/MacOSX/ngIRCd.pmdoc/02de.xml @@ -0,0 +1 @@ +de.barton.ngircd.launchscript.pkg1de.barton.ngircd.plist/Library/LaunchDaemonsparentscripts.postinstall.pathscripts.postupgrade.pathinstallFrom.isRelativeTypeinstallTo.pathinstallToidentifierpostinstall.shpostinstall.sh \ No newline at end of file diff --git a/contrib/MacOSX/ngIRCd.pmdoc/Makefile.am b/contrib/MacOSX/ngIRCd.pmdoc/Makefile.am new file mode 100644 index 0000000..e11eeb1 --- /dev/null +++ b/contrib/MacOSX/ngIRCd.pmdoc/Makefile.am @@ -0,0 +1,18 @@ +# +# ngIRCd -- The Next Generation IRC Daemon +# Copyright (c)2001-2008 Alexander Barton +# +# 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 2 of the License, or +# (at your option) any later version. +# Please read the file COPYING, README and AUTHORS for more information. +# + +EXTRA_DIST = index.xml \ + 01ngircd-contents.xml 01ngircd.xml 02de-contents.xml 02de.xml + +maintainer-clean-local: + rm -f Makefile Makefile.in + +# -eof- diff --git a/contrib/MacOSX/ngIRCd.pmdoc/index.xml b/contrib/MacOSX/ngIRCd.pmdoc/index.xml new file mode 100644 index 0000000..553e305 --- /dev/null +++ b/contrib/MacOSX/ngIRCd.pmdoc/index.xml @@ -0,0 +1,188 @@ +ngIRCd/Users/alex/Desktop/ngIRCd.mpkgde.barton.ngircdngIRCd – next generation IRC (Internet Relay Chat) server daemon//Library/LaunchDaemons01ngircd.xml02de.xmlproperties.customizeOptionproperties.titledescriptionproperties.anywhereDomainproperties.systemDomain \ No newline at end of file diff --git a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj index f557738..0d19fd6 100644 --- a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj +++ b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj @@ -188,7 +188,6 @@ FA322DAE0CEF7538001761B3 /* ngircd.8.tmpl */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngircd.8.tmpl; sourceTree = ""; }; FA322DAF0CEF7538001761B3 /* ngircd.conf.5.tmpl */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngircd.conf.5.tmpl; sourceTree = ""; }; FA322DB10CEF7565001761B3 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; - FA322DBB0CEF773C001761B3 /* cvs-version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "cvs-version.h"; sourceTree = ""; }; FA322DC00CEF77CB001761B3 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = ""; }; FA407F2B0DB159F400271AF1 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = Makefile.am; path = ipaddr/Makefile.am; sourceTree = ""; }; FA407F2C0DB159F400271AF1 /* ng_ipaddr.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = ng_ipaddr.c; path = ipaddr/ng_ipaddr.c; sourceTree = ""; }; @@ -413,7 +412,6 @@ FA322D8D0CEF7523001761B3 /* Makefile.am */, FA322D8E0CEF7523001761B3 /* ngIRCd.xcodeproj */, FA322DB10CEF7565001761B3 /* config.h */, - FA322DBB0CEF773C001761B3 /* cvs-version.h */, ); path = MacOSX; sourceTree = ""; @@ -655,6 +653,7 @@ GCC_WARN_UNUSED_VALUE = YES; INSTALL_PATH = /usr/local/bin; PRODUCT_NAME = ngIRCd; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; }; name = Default; }; @@ -668,7 +667,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; }; name = Default; }; diff --git a/contrib/MacOSX/postinstall.sh b/contrib/MacOSX/postinstall.sh new file mode 100755 index 0000000..9cca7b4 --- /dev/null +++ b/contrib/MacOSX/postinstall.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# ngIRCd Mac OS X postinstall/postupgrade script + +LDPLIST="/Library/LaunchDaemons/de.barton.ngircd.plist" + +if [ ! -e /etc/ngircd ]; then + echo "Creating symlink: /opt/ngircd/etc -> /etc/ngircd" + ln -s /opt/ngircd/etc /etc/ngircd || exit 1 +else + echo "/etc/ngircd already exists. Don't create symlink." +fi + +if [ ! -e /opt/ngircd/etc/ngircd.conf ]; then + echo "Creating default configuration: /opt/ngircd/etc/ngircd.conf" + cp /opt/ngircd/share/doc/ngircd/sample-ngircd.conf \ + /opt/ngircd/etc/ngircd.conf || exit 1 +else + echo "/opt/ngircd/etc/ngircd.conf exists. Don't copy sample file." +fi +chmod o-rwx /opt/ngircd/etc/ngircd.conf + +if [ -f "$LDPLIST" ]; then + echo "Fixing ownership and permissions of LaunchDaemon script ..." + chown root:wheel "$LDPLIST" || exit 1 + chmod 644 "$LDPLIST" || exit 1 +fi + +if [ -f /tmp/ngircd_needs_restart ]; then + echo "ngIRCd should be (re-)started ..." + if [ -r "$LDPLIST" ]; then + echo "LaunchDaemon script found, starting daemon ..." + launchctl load -w "$LDPLIST" || exit 1 + echo "OK, LaunchDaemon script loaded successfully." + else + echo "LaunchDaemon script not installed. Can't start daemon." + fi +else + echo "Not loading LaunchDaemon script." +fi +rm -f /tmp/ngircd_needs_restart + +# -eof- diff --git a/contrib/MacOSX/preinstall.sh b/contrib/MacOSX/preinstall.sh new file mode 100755 index 0000000..0e13bac --- /dev/null +++ b/contrib/MacOSX/preinstall.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# ngIRCd Mac OS X preinstall/preupgrade script + +LDPLIST="/Library/LaunchDaemons/de.barton.ngircd.plist" + +rm -f /tmp/ngircd_needs_restart || exit 1 +if [ -r "$LDPLIST" ]; then + echo "LaunchDaemon script found, checking status ..." + launchctl list | fgrep "de.barton.ngIRCd" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + # ngIRCd is already running; stop it and touch a + # "stamp file" so that we know that we have to + # restart it after installation/upgrade. + echo "ngIRCd is already running; stop it ..." + launchctl unload "$LDPLIST" || exit 1 + echo "Daemon has been stopped." + touch /tmp/ngircd_needs_restart || exit 1 + else + echo "ngIRCd is not running." + fi +else + echo "LaunchDaemon script not found." +fi + +# -eof- diff --git a/contrib/Makefile.am b/contrib/Makefile.am index e1f51c2..2125c10 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,20 +1,17 @@ # # ngIRCd -- The Next Generation IRC Daemon -# Copyright (c)2001-2004 Alexander Barton +# Copyright (c)2001-2008 Alexander Barton (alex@barton.de) # -# Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen -# der GNU General Public License (GPL), wie von der Free Software Foundation -# herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2 -# der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version. -# Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste -# der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS. -# -# $Id: Makefile.am,v 1.4 2004/04/28 12:18:02 alex Exp $ +# 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 2 of the License, or +# (at your option) any later version. +# Please read the file COPYING, README and AUTHORS for more information. # SUBDIRS = Debian MacOSX -EXTRA_DIST = README ngircd.spec systrace.policy +EXTRA_DIST = README ngircd.spec systrace.policy ngindent ngircd.sh maintainer-clean-local: rm -f Makefile Makefile.in diff --git a/contrib/ngircd.spec b/contrib/ngircd.spec index f18f13a..be6a9c0 100644 --- a/contrib/ngircd.spec +++ b/contrib/ngircd.spec @@ -1,5 +1,5 @@ %define name ngircd -%define version 0.12.1 +%define version 13 %define release 1 %define prefix %{_prefix} diff --git a/doc/FAQ.txt b/doc/FAQ.txt index edd73ae..bf3628c 100644 --- a/doc/FAQ.txt +++ b/doc/FAQ.txt @@ -54,6 +54,10 @@ A: Most probably you are using version 1.5 of GNU automake which seems to be III. Runtime ~~~~~~~~~~~~ +Q: Where is the log file located? +A: ngIRCd does not write its own log file. Instead, ngIRCd uses + syslog(3). Check the files in /var/log/ and/or consult the + documentation for your system logger daemon. Q: I cannot connect to remote peers when I use the chroot option, the following is logged: "Can't resolve example.com: unknown error!". A: On Linux/glibc with chroot enabled you need to put some libraries inside @@ -61,7 +65,14 @@ A: On Linux/glibc with chroot enabled you need to put some libraries inside linking ngircd statically does not help this. The only known workaround is to either disable chroot support or to link against dietlibc instead of glibc. (tnx to Sebastian Siewior) - +Q: I have added an [Oper] section, how do i log on as IRC operator? +A: You can use the /OPER command in your IRC client to become an IRC operator. + ngIRCd will also log all OPER requests (using syslog), if OPER fails you + can look there to determine why it did not work (bad password, unauthorized + host mask, etc.) +Q: I am an IRC operator, but MODE doesn't work! +A: You need to set 'OperCanUseMode = yes' in ngircd.conf to enable MODE for IRC + operators. IV. Bugs!? ~~~~~~~~~~ diff --git a/doc/Makefile.am b/doc/Makefile.am index 394c89b..eba97d9 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,6 @@ # # ngIRCd -- The Next Generation IRC Daemon -# Copyright (c)2001-2008 by Alexander Barton (alex@barton.de) +# Copyright (c)2001-2008 Alexander Barton (alex@barton.de) # # Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen # der GNU General Public License (GPL), wie von der Free Software Foundation @@ -13,7 +13,8 @@ SUBDIRS = src EXTRA_DIST = FAQ.txt GIT.txt Protocol.txt Platforms.txt README-AUX.txt \ - README-BeOS.txt RFC.txt SSL.txt Zeroconf.txt sample-ngircd.conf + README-BeOS.txt RFC.txt Services.txt SSL.txt Zeroconf.txt \ + sample-ngircd.conf maintainer-clean-local: rm -f Makefile Makefile.in diff --git a/doc/Platforms.txt b/doc/Platforms.txt index 57c0551..a77cc88 100644 --- a/doc/Platforms.txt +++ b/doc/Platforms.txt @@ -1,7 +1,7 @@ ngIRCd - Next Generation IRC Server - (c)2001-2006 Alexander Barton + (c)2001-2008 Alexander Barton alex@barton.de, http://www.barton.de/ ngIRCd is free software and published under the @@ -29,20 +29,27 @@ Platform Compiler ngIRCd Date Tester C M T R See alpha/unknown/netbsd3.0 gcc 3.3.3 CVSHEAD 06-05-07 fw Y Y Y Y (3) hppa/unknown/openbsd3.5 gcc 2.95.3 CVSHEAD 04-05-25 alex Y Y Y Y hppa1.1/unknown/linux-gnu gcc 3.3.3 0.8.0 04-05-30 alex Y Y Y Y -hppa2.0/unknown/linux-gnu gcc 3.3.5 0.9.x-CVS 05-06-27 alex Y Y Y Y +hppa2.0/unknown/linux-gnu gcc 3.3.5 13~rc1 08-12-02 alex Y Y Y Y +i386/apple/darwin9.5.1 gcc 4.0.1 13~rc1 08-12-02 alex Y Y Y Y (3) i386/pc/solaris2.9 gcc 3.2.2 CVSHEAD 04-02-24 alex Y Y Y Y -i386/pc/solaris2.11 gcc 3.4.3 CVSHEAD 06-08-04 alex Y Y Y Y +i386/pc/solaris2.11 gcc 3.4.3 13~rc1 08-12-03 alex Y Y Y Y (4) i386/unknown/freebsd5.2.1 gcc 3.3.3 0.8.0 04-05-30 alex Y Y Y Y i386/unknown/freebsd6.0 gcc 3.4.4 0.10.0-p1 06-08-04 alex Y Y Y Y (3) i386/unknown/freebsd6.1 gcc 3.4.4 CVSHEAD 06-05-07 fw Y Y Y Y (3) +i386/unknown/freebsd6.2 gcc 3.4.6 13~rc1 08-12-04 alex Y Y Y Y (3) +i386/unknown/freebsd7.0 gcc 4.2.1 13~rc1 08-12-04 alex Y Y Y Y (3) i386/unknown/gnu0.3 gcc 3.3.3 0.8.0 04-05-30 alex Y Y n Y +i686/unknown/gnu0.3 gcc 4.3.1 13~rc1 08-12-05 alex Y Y Y Y i386/unknown/netbsdelf1.6.1 gcc 2.95.3 CVSHEAD 04-02-24 alex Y Y Y Y i386/unknown/netbsdelf3.0.1 gcc 3.3.3 0.10.0-p1 06-08-30 alex Y Y Y Y (3) +i386/unknown/netbsdelf4.0 gcc 4.1.2 13~rc1 08-12-05 alex Y Y Y Y (3) i386/unknown/openbsd3.9 gcc 3.3.5 0.10.0-p1 06-08-30 alex Y Y Y Y (3) +i386/unknown/openbsd4.1 gcc 3.3.5 13~rc1 08-12-05 alex Y Y Y Y (3) i686/pc/cygwin gcc 3.3.1 0.8.0 04-05-30 alex Y Y n Y i686/pc/linux-gnu gcc 2.95.4 0.8.0 04-05-30 alex Y Y Y Y (1) -i686/pc/linux-gnu gcc 3.3.3 0.8.0 04-05-30 alex Y Y Y Y (1) -i386/pc/linux-gnu gcc 4.1.2 0.10.0-p1 06-08-30 alex Y Y Y Y (1) +i686/pc/linux-gnu gcc 3.3.5 13~rc1 08-12-05 alex Y Y Y Y (1) +i386/pc/linux-gnu gcc 4.1.2 13~rc1 08-12-05 alex Y Y Y Y (1) +i386/pc/linux-gnu gcc 4.3.2 13~rc1 08-12-05 alex Y Y Y Y (1) m68k/apple/aux3.1.1 Orig. A/UX 0.7.x-CVS 03-04-22 alex Y Y Y Y (2) m68k/hp/hp-ux9.10 Orig. HPUX 0.7.x-CVS 03-04-30 goetz Y Y Y Y m88k/dg/dgux5.4R3.10 gcc 2.5.8 CVSHEAD 04-03-15 alex Y Y ? ? @@ -55,6 +62,7 @@ powerpc/unknown/openbsd3.6 gcc 2.95.3 0.10.0 06-10-08 alex Y Y n Y sparc/sun/solaris2.6 gcc 2.95.3 0.7.x-CVS 03-04-22 alex Y Y Y Y sparc/sun/solaris2.7 gcc 3.3 0.8.0 04-05-30 alex Y Y Y Y sparc/unkn./netbsdelf1.6.1 gcc 2.95.3 0.8.0 04-05-30 alex Y Y Y Y +x86_64/unknown/linux-gnu 4.3.2 13~rc1 08-12-05 alex Y Y Y Y (5) Notes @@ -64,7 +72,7 @@ Notes ngIRCd has been tested with various Linux distributions, such as SuSE, RedHat, Debian, and Gentoo using Kernels 2.2.x, 2.4.x and 2.6.x with various versions of the GNU C compiler (starting with 2.95.x and up to - version 4.1.x). The eldest glibc used was glibc-2.0.7. ngIRCd compiled + version 4.3.x). The eldest glibc used was glibc-2.0.7. ngIRCd compiled and run on all these systems without problems. Actual Linux kernels (2.6.x) and glic's support the epoll() IO interface. @@ -73,5 +81,6 @@ Notes (3) Using the kqueue() IO interface. --- -$Id: Platforms.txt,v 1.18 2006/10/08 14:09:16 alex Exp $ +(4) Using the /dev/poll IO interface. + +(5) Using the epoll() IO interface. diff --git a/doc/Protocol.txt b/doc/Protocol.txt index c81143e..354b814 100644 --- a/doc/Protocol.txt +++ b/doc/Protocol.txt @@ -1,7 +1,7 @@ ngIRCd - Next Generation IRC Server - (c)2001-2007 Alexander Barton, + (c)2001-2008 Alexander Barton, alex@barton.de, http://www.barton.de/ ngIRCd is free software and published under the @@ -85,6 +85,8 @@ The following are defined at the moment: - o: IRC operators are allowed to change channel- and channel-user-modes even if they aren't channel-operator of the affected channel. +- S: The server supports the SERVICE command (on this link). + - Z: Compressed server links are supported by the server. Example for a complete string: "ngircd|0.7.5:CZ". @@ -159,6 +161,3 @@ and therefore can't be omitted. The parameter must be ignored when a channel has no user limit (the parameter doesn't list the "l" channel mode). In this case should be "0". - --- -$Id: Protocol.txt,v 1.14 2007/11/21 12:16:35 alex Exp $ diff --git a/doc/SSL.txt b/doc/SSL.txt index 7578ad8..6b590b8 100644 --- a/doc/SSL.txt +++ b/doc/SSL.txt @@ -1,7 +1,7 @@ ngIRCd - Next Generation IRC Server - (c)2001-2004 by Alexander Barton, + (c)2001-2008 Alexander Barton, alex@barton.de, http://www.barton.de/ ngIRCd is free software and published under the @@ -10,17 +10,70 @@ -- SSL.txt -- -ngIRCd actually doesn't support secure connections for client-server or -server-server links using SSL, the Secure Socket Layer, by itself. But you can -use the stunnel(8) command to make this work. +ngIRCd supports SSL/TLSv1 encrypted connections using the OpenSSL or GnuTLS +libraries. Both encrypted server-server links as well as client-server links +are supported. + +SSL is a compile-time option which is disabled by default. Use one of these +options of the ./configure script to enable it: + + --with-openssl enable SSL support using OpenSSL + --with-gnutls enable SSL support using GnuTLS + +You need a SSL certificate, see below for how to create a self-signed one. + + +Configuration +~~~~~~~~~~~~~ + +To enable SSL connections a separate port must be configured: it is NOT +possible to handle unencrypted and encrypted connections on the same port! +This is a limitation of the IRC protocol ... + +You have to set (at least) the following configuration variables in the +[GLOBAL] section of ngircd.conf(5): SSLPorts, SSLKeyFile, and SSLCertFile. + +Now IRC clients are able to connect using SSL on the configured port(s). +(Using port 6697 for encrypted connections is common.) + +To enable encrypted server-server links, you have to additionally set +SSLConnect to "yes" in the corresponding [SERVER] section. + + +Creating a self-signed certificate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +OpenSSL: + +Creating a self-signed certificate and key: + $ openssl req -newkey rsa:2048 -x509 -keyout server-key.pem \ + -out server-cert.pem -days 1461 +Create DH parameters (optional): + $ openssl dhparam -2 -out dhparams.pem 2048 + +GnuTLS: + +Creating a self-signed certificate and key: + $ certtool --generate-privkey --bits 2048 --outfile server-key.pem + $ certtool --generate-self-signed --load-privkey server-key.pem \ + --outfile server-cert.pem +Create DH parameters (optional): + $ certtool --generate-dh-params --bits 2048 --outfile dhparams.pem + + +Alternate approach using stunnel(1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Alternatively (or if you are using ngIRCd without compiled without support +for GnuTLS/OpenSSL), you can use external programs/tools like stunnel(1) to +get SSL encrypted connections: -Stefan Sperling (stefan at binarchy dot net) mailed me the following text as a +Stefan Sperling (stefan at binarchy dot net) mailed the following text as a short "how-to", thanks Stefan! - === snip === ! This guide applies to stunnel 4.x ! @@ -49,10 +102,3 @@ short "how-to", thanks Stefan! That's it. Don't forget to activate ssl support in your irc client ;) === snip === - - -Probably ngIRCd will include support for SSL in the future ... - - --- -$Id: SSL.txt,v 1.2 2004/12/27 01:11:40 alex Exp $ diff --git a/doc/Services.txt b/doc/Services.txt new file mode 100644 index 0000000..67053b1 --- /dev/null +++ b/doc/Services.txt @@ -0,0 +1,70 @@ + + ngIRCd - Next Generation IRC Server + + (c)2001-2008 Alexander Barton, + alex@barton.de, http://www.barton.de/ + + ngIRCd is free software and published under the + terms of the GNU General Public License. + + -- Services.txt -- + + +At the moment, ngIRCd doesn't implement a "special IRC services interface". +But services acting as a "regular server" are supported, either using the IRC +protocol defined in RFC 1459 or RFC 2812. + +Services have been tested using "IRC Services" version 5.x by Andrew Church, +homepage: . This document describes setting up +ngIRCd and these services. + + +Setting up ngIRCd +~~~~~~~~~~~~~~~~~ + +The "pseudo server" handling the IRC services is configured as a regular +remote server in the ngircd.conf(5). In addition the variable "ServiceMask" +should be set, enabling this ngIRCd to recognize the "pseudo users" as IRC +services instead of regular IRC users. + +Example: + + [SERVER] + Name = services.irc.net + MyPassword = 123abc + PeerPassword = 123abc + ServiceMask = *Serv + + +Setting up IRC Services 5.1.x +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +IRC Services 5.1.3 and above can be used with ngIRCd using the "rfc1459" +protocol module. + +Please note that versions up to and including 5.1.3 contain a bug that +sometimes causes IRC Services to hang on startup. There are two workarounds: + a) send the services process a HUP signal ("killall -HUP ircservices") + b) apply this patch to the IRC Services source tree: + + +At least the following settings have to be tweaked, in addition to all the +settings marked as required by IRC Services: + +In ircservices.conf: + + Variable Example value + + RemoteServer server.irc.net 6667 "123abc" + ServerName "services.irc.net" + LoadModule protocol/rfc1459 + +In modules.conf: + + Module protocol/rfc1459 + +The documentation of IRC Services can be found here: + + +Please let us know if you are successfully using other IRC service packages or +which problems you encounter, thanks! diff --git a/doc/sample-ngircd.conf b/doc/sample-ngircd.conf index 87a94d9..041542e 100644 --- a/doc/sample-ngircd.conf +++ b/doc/sample-ngircd.conf @@ -1,18 +1,19 @@ -# $Id: sample-ngircd.conf,v 1.44 2008/01/07 23:02:29 alex Exp $ - # -# This is a sample configuration file for the ngIRCd, which must be adepted -# to the local preferences and needs. +# This is a sample configuration file for the ngIRCd IRC daemon, which must +# be customized to the local preferences and needs. # # Comments are started with "#" or ";". # # A lot of configuration options in this file start with a ";". You have # to remove the ";" in front of each variable to actually set a value! -# The disabled variables are shown with example values for completeness. +# The disabled variables are shown with example values for completeness only +# and the daemon is using compiled-in default settings. # # Use "ngircd --configtest" (see manual page ngircd(8)) to validate that the # server interprets the configuration file as expected! # +# Please see ngircd.conf(5) for a complete list of configuration options. +# [Global] # The [Global] section of this file is used to define the main @@ -40,6 +41,21 @@ # one port, separated with ",". (Default: 6667) ;Ports = 6667, 6668, 6669 + # Additional Listen Ports that expect SSL/TLS encrypted connections + ;SSLPorts = 9999,6668 + + # SSL Server Key + ;SSLKeyFile = /usr/local/etc/ngircd/ssl/server-key.pem + + # password to decrypt SSLKeyFile (OpenSSL only) + ;SSLKeyFilePassword = secret + + # SSL Server Key Certificate + ;SSLCertFile = /usr/local/etc/ngircd/ssl/server-cert.pem + + # Diffie-Hellman parameters + ;SSLDHFile = /usr/local/etc/ngircd/ssl/dhparams.pem + # comma seperated list of IP addresses on which the server should # listen. Default values are: # "0.0.0.0" or (if compiled with IPv6 support) "::,0.0.0.0" @@ -105,12 +121,16 @@ # Don't do any DNS lookups when a client connects to the server. ;NoDNS = no + # Don't do any IDENT lookups, even if ngIRCd has been compiled + # with support for it. + ;NoIdent = no + # try to connect to other irc servers using ipv4 and ipv6, if possible ;ConnectIPv6 = yes ;ConnectIPv4 = yes - # Maximum number of simultaneous connection the server is allowed - # to accept (0: unlimited): + # Maximum number of simultaneous in- and outbound connections the + # server is allowed to accept (0: unlimited): ;MaxConnections = 0 # Maximum number of simultaneous connections from a single IP address @@ -158,13 +178,13 @@ # IRC name of the remote server, must match the "Name" variable in # the [Global] section of the other server (when using ngIRCd). ;Name = irc2.the.net - + # Internet host name or IP address of the peer (only required when # this server should establish the connection). ;Host = connect-to-host.the.net - # IP address to use as _source_ address for the connection. if unspecified, - # ngircd will let the operating system pick an address. + # IP address to use as _source_ address for the connection. if + # unspecified, ngircd will let the operating system pick an address. ;Bind = 10.0.0.1 # Port of the server to which the ngIRCd should connect. If you @@ -184,11 +204,24 @@ # Set the "Passive" option to "yes" if you don't want this ngIRCd to # connect to the configured peer (same as leaving the "Port" variable - # empty). The advantage of this option is that you can actually configure - # a port an use the IRC command CONNECT more easily to manually connect - # this specific server later. + # empty). The advantage of this option is that you can actually + # configure a port an use the IRC command CONNECT more easily to + # manually connect this specific server later. ;Passive = no + # Connect to the remote server using TLS/SSL (Default: false) + ;SSLConnect = yes + + # Define a (case insensitive) mask matching nick names that sould be + # treated as IRC services when introduced via this remote server. + # REGULAR SERVERS DON'T NEED this parameter, so leave it empty + # (which is the default). + # When you are connecting IRC services which mask as a IRC server + # and which use "virtual users" to communicate with, for example + # "NickServ" amd "ChanServ", you should set this parameter to + # something like "*Serv". + ;ServiceMask = *Serv + [Server] # More [Server] sections, if you like ... diff --git a/doc/src/Makefile.am b/doc/src/Makefile.am index 8ff2723..0eaa80d 100644 --- a/doc/src/Makefile.am +++ b/doc/src/Makefile.am @@ -1,6 +1,6 @@ # # ngIRCd -- The Next Generation IRC Daemon -# Copyright (c)2001-2006 Alexander Barton (alex@barton.de) +# Copyright (c)2001-2008 Alexander Barton (alex@barton.de) # # 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 @@ -8,8 +8,8 @@ # (at your option) any later version. # Please read the file COPYING, README and AUTHORS for more information. # -# $Id: Makefile.am,v 1.3 2006/12/28 14:04:28 alex Exp $ -# + +EXTRA_DIST = Doxyfile header.inc.html footer.inc.html ngircd-doc.css maintainer-clean-local: rm -f Makefile Makefile.in diff --git a/man/ngircd.8.tmpl b/man/ngircd.8.tmpl index 692c188..2fc7a9b 100644 --- a/man/ngircd.8.tmpl +++ b/man/ngircd.8.tmpl @@ -1,7 +1,7 @@ .\" -.\" $Id: ngircd.8.tmpl,v 1.2 2007/11/15 01:03:29 fw Exp $ +.\" ngircd(8) manual page template .\" -.TH ngircd 8 "May 2008" ngircd "ngIRCd Manual" +.TH ngircd 8 "Dec 2008" ngircd "ngIRCd Manual" .SH NAME ngIRCd \- the next generation IRC daemon .SH SYNOPSIS diff --git a/man/ngircd.conf.5.tmpl b/man/ngircd.conf.5.tmpl index 7c9ce31..3c6c278 100644 --- a/man/ngircd.conf.5.tmpl +++ b/man/ngircd.conf.5.tmpl @@ -1,7 +1,7 @@ .\" -.\" $Id: ngircd.conf.5.tmpl,v 1.7 2007/11/23 16:26:03 fw Exp $ +.\" ngircd.conf(5) manual page template .\" -.TH ngircd.conf 5 "May 2008" ngircd "ngIRCd Manual" +.TH ngircd.conf 5 "Dec 2008" ngircd "ngIRCd Manual" .SH NAME ngircd.conf \- configuration file of ngIRCd .SH SYNOPSIS @@ -72,6 +72,28 @@ command. Ports on which the server should listen. There may be more than one port, separated with ','. Default: 6667. .TP +\fBSSLPorts\fR +Same as \fBPorts\fR , except that ngircd will expect incoming connections +to be SSL/TLS encrypted. Default: None +.TP +\fBSSLKeyFile\fR +Filename of SSL Server Key to be used for SSL connections. This is required for +SSL/TLS support. +.TP +\fBSSLKeyFilePassword\fR +(OpenSSL only:) Password to decrypt private key. +.TP +\fBSSLCertFile\fR +Certificate of the private key +.TP +\fBSSLDHFile\fR +Name of the Diffie-Hellman Parameter file. Can be created with gnutls +"certtool \-\-generate-dh-params" or "openssl dhparam". +If this file is not present, it will be generated on startup when ngircd +was compiled with gnutls support (this may take some time). If ngircd +was compiled with OpenSSL, then (Ephemeral)-Diffie-Hellman Key Exchanges and several +Cipher Suites will not be available. +.TP \fBListen\fR A comma seperated list of IP address on which the server should listen. If unset, the defaults value is "0.0.0.0", or, if ngircd was compiled @@ -157,10 +179,15 @@ the config file. Default: No. .TP \fBNoDNS\fR -If enabled, ngircd will not make DNS lookups when clients connect. +If set to true, ngircd will not make DNS lookups when clients connect. If you configure ngircd to connect to other servers, ngircd may still perform a DNS lookup if required. -Default: No. +Default: false. +.TP +\fBNoIdent\fR +If ngircd is compiled with IDENT support this can be used to disable IDENT +lookups at run time. +Default: false. .TP \fBConnectIPv4\fR Set this to no if you do not want ngircd to connect to other irc servers using ipv4. @@ -172,8 +199,8 @@ Set this to no if you do not want ngircd to connect to other irc servers using i Default: Yes. .TP \fBMaxConnections\fR -Maximum number of simultaneous connection the server is allowed to accept -(0: unlimited). Default: 0. +Maximum number of simultaneous in- and outbound connections the server is +allowed to accept (0: unlimited). Default: 0. .TP \fBMaxConnectionsIP\fR Maximum number of simultaneous connections from a single IP address that @@ -188,6 +215,8 @@ Default: 10. Maximum length of an user nick name (Default: 9, as in RFC 2812). Please note that all servers in an IRC network MUST use the same maximum nick name length! +\fBSSLConnect\fR +Connect to the remote server using TLS/SSL (Default: false) .SH [OPERATOR] .I [Operator] sections are used to define IRC Operators. There may be more than one @@ -250,6 +279,16 @@ Group of this server (optional). \fBPassive\fR Disable automatic connection even if port value is specified. Default: false. You can use the IRC Operator command CONNECT later on to create the link. +.TP +\fBServiceMask\fR +Define a (case insensitive) mask matching nick names that sould be treated as +IRC services when introduced via this remote server. REGULAR SERVERS DON'T NEED +this parameter, so leave it empty (which is the default). +.PP +.RS +When you are connecting IRC services which mask as a IRC server and which use +"virtual users" to communicate with, for example "NickServ" amd "ChanServ", +you should set this parameter to something like "*Serv". .SH [CHANNEL] Pre-defined channels can be configured in .I [Channel] @@ -278,7 +317,7 @@ Sets initial channel key (only relevant if mode k is set). \fBMaxUsers\fR Set maximum user limit for this channel (only relevant if mode l is set). .SH HINTS -It's wise to use "ngircd --configtest" to validate the configuration file +It's wise to use "ngircd \-\-configtest" to validate the configuration file after changing it. See .BR ngircd (8) for details. diff --git a/src/.gitignore b/src/.gitignore index a9cca37..5f069df 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,4 @@ config.h config.h.in +config.h.in~ stamp-h1 diff --git a/src/ipaddr/ng_ipaddr.h b/src/ipaddr/ng_ipaddr.h index 9c45d2f..6490a07 100644 --- a/src/ipaddr/ng_ipaddr.h +++ b/src/ipaddr/ng_ipaddr.h @@ -115,3 +115,4 @@ ng_ipaddr_tostr_r(const ng_ipaddr_t *addr, char *d) #endif /* -eof- */ + diff --git a/src/ngircd/.gitignore b/src/ngircd/.gitignore index e02dd40..c25ba5e 100644 --- a/src/ngircd/.gitignore +++ b/src/ngircd/.gitignore @@ -1,5 +1,3 @@ check-help check-version -cvs-version.h -cvs-version.new ngircd diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.am index 1a5119f..fca531d 100644 --- a/src/ngircd/Makefile.am +++ b/src/ngircd/Makefile.am @@ -21,7 +21,7 @@ LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \ sbin_PROGRAMS = ngircd ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.c \ - conn-zip.c hash.c io.c irc.c irc-channel.c irc-info.c irc-login.c \ + conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c irc-info.c irc-login.c \ irc-mode.c irc-op.c irc-oper.c irc-server.c irc-write.c lists.c log.c \ match.c numeric.c parse.c rendezvous.c resolve.c @@ -29,11 +29,11 @@ ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr ngircd_LDADD = -lngportab -lngtool -lngipaddr -noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conn.h conn-func.h \ - conn-zip.h hash.h io.h irc.h irc-channel.h irc-info.h irc-login.h \ - irc-mode.h irc-op.h irc-oper.h irc-server.h irc-write.h lists.h log.h \ - match.h numeric.h parse.h rendezvous.h resolve.h \ - defines.h messages.h +noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conf-ssl.h conn.h \ + conn-func.h conn-ssl.h conn-zip.h hash.h io.h irc.h irc-channel.h \ + irc-info.h irc-login.h irc-mode.h irc-op.h irc-oper.h irc-server.h \ + irc-write.h lists.h log.h match.h numeric.h parse.h rendezvous.h \ + resolve.h defines.h messages.h clean-local: rm -f check-version check-help lint.out diff --git a/src/ngircd/array.c b/src/ngircd/array.c index ff7f02c..1e56b71 100644 --- a/src/ngircd/array.c +++ b/src/ngircd/array.c @@ -281,6 +281,14 @@ array_free(array * a) a->used = 0; } +void +array_free_wipe(array *a) +{ + size_t bytes = a->allocated; + if (bytes) + memset(a->mem, 0, bytes); + array_free(a); +} void * array_start(const array * const a) diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c index 32f911a..609bbf5 100644 --- a/src/ngircd/channel.c +++ b/src/ngircd/channel.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2005 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 by Alexander Barton (alex@barton.de) * * 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 @@ -17,8 +17,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: channel.c,v 1.65 2008/02/05 16:31:35 fw Exp $"; - #include "imp.h" #include #include @@ -65,8 +63,16 @@ static bool Delete_Channel PARAMS(( CHANNEL *Chan )); GLOBAL void Channel_Init( void ) { + CHANNEL *sc; + My_Channels = NULL; My_Cl2Chan = NULL; + + sc = Channel_Create("&SERVER"); + if (sc) { + Channel_SetModes(sc, "mnPt"); + Channel_SetTopic(sc, Client_ThisServer(), "Server Messages"); + } } /* Channel_Init */ @@ -89,18 +95,18 @@ Channel_GetListInvites(CHANNEL *c) GLOBAL void Channel_InitPredefined( void ) { - /* Vordefinierte persistente Channels erzeugen */ + /* Generate predefined persistent channels */ CHANNEL *chan; char *c; unsigned int i; - + for( i = 0; i < Conf_Channel_Count; i++ ) { - /* Ist ein Name konfiguriert? */ + /* Check for Name configuration */ if( ! Conf_Channel[i].name[0] ) continue; - /* Gueltiger Channel-Name? */ + /* Check for invalid channel name */ if( ! Channel_IsValidName( Conf_Channel[i].name )) { Log( LOG_ERR, "Can't create pre-defined channel: invalid name: \"%s\"!", Conf_Channel[i].name ); @@ -108,7 +114,7 @@ Channel_InitPredefined( void ) continue; } - /* Gibt es den Channel bereits? */ + /* Check if the channel name is already in use */ chan = Channel_Search( Conf_Channel[i].name ); if( chan ) { @@ -149,7 +155,7 @@ Channel_Exit( void ) CHANNEL *c, *c_next; CL2CHAN *cl2chan, *cl2chan_next; - /* Channel-Strukturen freigeben */ + /* free struct Channel */ c = My_Channels; while( c ) { @@ -159,7 +165,7 @@ Channel_Exit( void ) c = c_next; } - /* Channel-Zuordnungstabelle freigeben */ + /* Free Channel allocation table */ cl2chan = My_Cl2Chan; while( c ) { @@ -170,40 +176,53 @@ Channel_Exit( void ) } /* Channel_Exit */ +/** + * Join Channel + * This function lets a client join a channel. First, the function + * checks that the specified channel name is valid and that the client + * isn't already a member. If the specified channel doesn't exist, + * a new channel is created. Client is added to channel by function + * Add_Client(). + */ GLOBAL bool Channel_Join( CLIENT *Client, char *Name ) { CHANNEL *chan; - - assert( Client != NULL ); - assert( Name != NULL ); - if( ! Channel_IsValidName( Name )) { - IRC_WriteStrClient( Client, ERR_NOSUCHCHANNEL_MSG, Client_ID( Client ), Name ); + assert(Client != NULL); + assert(Name != NULL); + + /* Check that the channel name is valid */ + if (! Channel_IsValidName(Name)) { + IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG, + Client_ID(Client), Name); return false; } - chan = Channel_Search( Name ); - if( chan ) { - /* Ist der Client bereits Mitglied? */ - if( Get_Cl2Chan( chan, Client )) return false; - } - else - { - /* Gibt es noch nicht? Dann neu anlegen: */ - chan = Channel_Create( Name ); - if (!chan) return false; + chan = Channel_Search(Name); + if(chan) { + /* Check if the client is already in the channel */ + if (Get_Cl2Chan(chan, Client)) + return false; + } else { + /* If the specified channel does not exist, the channel + * is now created */ + chan = Channel_Create(Name); + if (!chan) + return false; } - /* User dem Channel hinzufuegen */ - if( ! Add_Client( chan, Client )) return false; - else return true; + /* Add user to Channel */ + if (! Add_Client(chan, Client)) + return false; + + return true; } /* Channel_Join */ /** - * Remove client from channel. - * This function lets a client lead a channel. First, the function checks + * Part client from channel. + * This function lets a client part from a channel. First, the function checks * if the channel exists and the client is a member of it and sends out * appropriate error messages if not. The real work is done by the function * Remove_Client(). @@ -217,18 +236,22 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea assert(Name != NULL); assert(Reason != NULL); + /* Check that specified channel exists */ chan = Channel_Search(Name); if (!chan) { IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG, Client_ID(Client), Name); return false; } + + /* Check that the client is in the channel */ if (!Get_Cl2Chan(chan, Client)) { IRC_WriteStrClient(Client, ERR_NOTONCHANNEL_MSG, Client_ID(Client), Name); return false; } + /* Part client from channel */ if (!Remove_Client(REMOVE_PART, chan, Client, Origin, Reason, true)) return false; else @@ -236,16 +259,22 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea } /* Channel_Part */ +/** + * Kick user from Channel + */ GLOBAL void -Channel_Kick( CLIENT *Client, CLIENT *Origin, char *Name, char *Reason ) +Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name, + const char *Reason ) { CHANNEL *chan; - assert( Client != NULL ); - assert( Origin != NULL ); - assert( Name != NULL ); - assert( Reason != NULL ); + assert(Peer != NULL); + assert(Target != NULL); + assert(Origin != NULL); + assert(Name != NULL); + assert(Reason != NULL); + /* Check that channel exists */ chan = Channel_Search( Name ); if( ! chan ) { @@ -253,27 +282,32 @@ Channel_Kick( CLIENT *Client, CLIENT *Origin, char *Name, char *Reason ) return; } - if( ! Channel_IsMemberOf( chan, Origin )) - { - IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, Client_ID( Origin ), Name ); - return; - } + if (Client_Type(Peer) != CLIENT_SERVER && + Client_Type(Origin) != CLIENT_SERVICE) { + /* Check that user is on the specified channel */ + if (!Channel_IsMemberOf(chan, Origin)) { + IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG, + Client_ID(Origin), Name); + return; + } - /* Is User Channel-Operator? */ - if( ! strchr( Channel_UserModes( chan, Origin ), 'o' )) - { - IRC_WriteStrClient( Origin, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Origin ), Name); - return; + /* Check if user has operator status */ + if (!strchr(Channel_UserModes(chan, Origin), 'o')) { + IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG, + Client_ID(Origin), Name); + return; + } } - /* Ist the kickED User member of channel? */ - if( ! Channel_IsMemberOf( chan, Client )) - { - IRC_WriteStrClient( Origin, ERR_USERNOTINCHANNEL_MSG, Client_ID( Origin ), Client_ID( Client ), Name ); + /* Check that the client to be kicked is on the specified channel */ + if (!Channel_IsMemberOf(chan, Target)) { + IRC_WriteStrClient(Origin, ERR_USERNOTINCHANNEL_MSG, + Client_ID(Origin), Client_ID(Target), Name ); return; } - Remove_Client( REMOVE_KICK, chan, Client, Origin, Reason, true); + /* Kick Client from channel */ + Remove_Client( REMOVE_KICK, chan, Target, Origin, Reason, true); } /* Channel_Kick */ @@ -302,7 +336,7 @@ Channel_Count( void ) { CHANNEL *c; unsigned long count = 0; - + c = My_Channels; while( c ) { @@ -338,9 +372,9 @@ Channel_CountForUser( CLIENT *Client ) CL2CHAN *cl2chan; int count = 0; - + assert( Client != NULL ); - + cl2chan = My_Cl2Chan; while( cl2chan ) { @@ -352,7 +386,6 @@ Channel_CountForUser( CLIENT *Client ) } /* Channel_CountForUser */ - GLOBAL const char * Channel_Name( const CHANNEL *Chan ) { @@ -403,8 +436,8 @@ Channel_Next( CHANNEL *Chan ) GLOBAL CHANNEL * Channel_Search( const char *Name ) { - /* Channel-Struktur suchen */ - + /* Search channel structure */ + CHANNEL *c; UINT32 search_hash; @@ -416,7 +449,7 @@ Channel_Search( const char *Name ) { if( search_hash == c->hash ) { - /* lt. Hash-Wert: Treffer! */ + /* hash hit */ if( strcasecmp( Name, c->name ) == 0 ) return c; } c = c->next; @@ -480,7 +513,14 @@ Channel_IsValidName( const char *Name ) { assert( Name != NULL ); - if(( Name[0] != '#' ) || ( strlen( Name ) >= CHANNEL_NAME_LEN )) return false; +#ifdef STRICT_RFC + if (strlen(Name) <= 1) + return false; +#endif + if (strchr("#&+", Name[0]) == NULL) + return false; + if (strlen(Name) >= CHANNEL_NAME_LEN) + return false; return Name[strcspn(Name, " ,:\007")] == 0; } /* Channel_IsValidName */ @@ -628,7 +668,7 @@ Channel_Topic( CHANNEL *Chan ) return ret ? ret : ""; } /* Channel_Topic */ - + #ifndef STRICT_RFC GLOBAL unsigned int @@ -715,6 +755,10 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) is_member = has_voice = is_op = false; + /* The server itself always can send messages :-) */ + if (Client_ThisServer() == From) + return true; + if (Channel_IsMemberOf(Chan, From)) { is_member = true; if (strchr(Channel_UserModes(Chan, From), 'v')) @@ -743,30 +787,21 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From) GLOBAL bool -Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Text) -{ - if (!Can_Send_To_Channel(Chan, From)) - return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG, Client_ID(From), Channel_Name(Chan)); - - if (Client_Conn(From) > NONE) - Conn_UpdateIdle(Client_Conn(From)); - - return IRC_WriteStrChannelPrefix(Client, Chan, From, true, - "PRIVMSG %s :%s", Channel_Name(Chan), Text); -} - - -GLOBAL bool -Channel_Notice(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Text) +Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command, + bool SendErrors, const char *Text) { - if (!Can_Send_To_Channel(Chan, From)) - return true; /* no error, see RFC 2812 */ + if (!Can_Send_To_Channel(Chan, From)) { + if (! SendErrors) + return CONNECTED; /* no error, see RFC 2812 */ + return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG, + Client_ID(From), Channel_Name(Chan)); + } if (Client_Conn(From) > NONE) Conn_UpdateIdle(Client_Conn(From)); return IRC_WriteStrChannelPrefix(Client, Chan, From, true, - "NOTICE %s :%s", Channel_Name(Chan), Text); + "%s %s :%s", Command, Channel_Name(Chan), Text); } @@ -820,7 +855,7 @@ Add_Client( CHANNEL *Chan, CLIENT *Client ) assert( Chan != NULL ); assert( Client != NULL ); - /* neue CL2CHAN-Struktur anlegen */ + /* Create new CL2CHAN structure */ cl2chan = (CL2CHAN *)malloc( sizeof( CL2CHAN )); if( ! cl2chan ) { @@ -831,7 +866,7 @@ Add_Client( CHANNEL *Chan, CLIENT *Client ) cl2chan->client = Client; strcpy( cl2chan->modes, "" ); - /* Verketten */ + /* concatenate */ cl2chan->next = My_Cl2Chan; My_Cl2Chan = cl2chan; @@ -846,12 +881,17 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch { CL2CHAN *cl2chan, *last_cl2chan; CHANNEL *c; - + assert( Chan != NULL ); assert( Client != NULL ); assert( Origin != NULL ); assert( Reason != NULL ); + /* Do not inform other servers if the channel is local to this server, + * regardless of what the caller requested! */ + if(InformServer) + InformServer = !Channel_IsLocal(Chan); + last_cl2chan = NULL; cl2chan = My_Cl2Chan; while( cl2chan ) @@ -865,7 +905,7 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch c = cl2chan->channel; assert( c != NULL ); - /* Aus Verkettung loesen und freigeben */ + /* maintain cl2chan list */ if( last_cl2chan ) last_cl2chan->next = cl2chan->next; else My_Cl2Chan = cl2chan->next; free( cl2chan ); @@ -873,14 +913,16 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch switch( Type ) { case REMOVE_QUIT: - /* QUIT: other servers have already been notified, see Client_Destroy(); - * so only inform other clients in same channel. */ + /* QUIT: other servers have already been notified, + * see Client_Destroy(); so only inform other clients + * in same channel. */ assert( InformServer == false ); LogDebug("User \"%s\" left channel \"%s\" (%s).", Client_Mask( Client ), c->name, Reason ); break; case REMOVE_KICK: - /* User was KICKed: inform other servers and all users in channel */ + /* User was KICKed: inform other servers (public + * channels) and all users in the channel */ if( InformServer ) IRC_WriteStrServersPrefix( Client_NextHop( Origin ), Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason); @@ -892,7 +934,7 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch IRC_WriteStrClientPrefix(Client, Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason); } - LogDebug("User \"%s\" has been kicked of \"%s\" by \"%s\": %s.", + LogDebug("User \"%s\" has been kicked off \"%s\" by \"%s\": %s.", Client_Mask( Client ), c->name, Client_ID(Origin), Reason); break; default: /* PART */ @@ -911,7 +953,7 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch } } - /* Wenn Channel nun leer und nicht pre-defined: loeschen */ + /* When channel is empty and is not pre-defined, delete */ if( ! strchr( Channel_Modes( Chan ), 'P' )) { if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan ); @@ -983,6 +1025,26 @@ Channel_ShowInvites( CLIENT *Client, CHANNEL *Channel ) } +/** + * Log a message to the local &SERVER channel, if it exists. + */ +GLOBAL void +Channel_LogServer(char *msg) +{ + CHANNEL *sc; + CLIENT *c; + + assert(msg != NULL); + + sc = Channel_Search("&SERVER"); + if (!sc) + return; + + c = Client_ThisServer(); + Channel_Write(sc, c, c, "PRIVMSG", false, msg); +} /* Channel_LogServer */ + + static CL2CHAN * Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan ) { @@ -996,7 +1058,7 @@ Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel ) CL2CHAN *cl2chan; assert( Client != NULL || Channel != NULL ); - + cl2chan = Start; while( cl2chan ) { @@ -1011,7 +1073,7 @@ Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel ) static bool Delete_Channel( CHANNEL *Chan ) { - /* Channel-Struktur loeschen */ + /* delete channel structure */ CHANNEL *chan, *last_chan; @@ -1027,11 +1089,11 @@ Delete_Channel( CHANNEL *Chan ) Log( LOG_DEBUG, "Freed channel structure for \"%s\".", Chan->name ); - /* Invite- und Ban-Lists aufraeumen */ + /* free invite and ban lists */ Lists_Free( &chan->list_bans ); Lists_Free( &chan->list_invites ); - /* Neu verketten und freigeben */ + /* maintain channel list */ if( last_chan ) last_chan->next = chan->next; else My_Channels = chan->next; free( chan ); @@ -1039,5 +1101,4 @@ Delete_Channel( CHANNEL *Chan ) return true; } /* Delete_Channel */ - /* -eof- */ diff --git a/src/ngircd/channel.h b/src/ngircd/channel.h index 8bf526b..91d1e21 100644 --- a/src/ngircd/channel.h +++ b/src/ngircd/channel.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 by Alexander Barton (alex@barton.de) * * 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 @@ -8,8 +8,6 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: channel.h,v 1.35 2008/02/05 16:31:35 fw Exp $ - * * Channel management (header) */ @@ -46,7 +44,7 @@ typedef struct _CLIENT2CHAN struct _CLIENT2CHAN *next; CLIENT *client; CHANNEL *channel; - char modes[CHANNEL_MODE_LEN]; /* User-Modes in dem Channel */ + char modes[CHANNEL_MODE_LEN]; /* User-Modes in Channel */ } CL2CHAN; #else @@ -68,7 +66,8 @@ GLOBAL bool Channel_Part PARAMS(( CLIENT *Client, CLIENT *Origin, const char *Na GLOBAL void Channel_Quit PARAMS(( CLIENT *Client, char *Reason )); -GLOBAL void Channel_Kick PARAMS(( CLIENT *Client, CLIENT *Origin, char *Name, char *Reason )); +GLOBAL void Channel_Kick PARAMS((CLIENT *Peer, CLIENT *Target, CLIENT *Origin, + const char *Name, const char *Reason)); GLOBAL unsigned long Channel_Count PARAMS(( void )); GLOBAL unsigned long Channel_MemberCount PARAMS(( CHANNEL *Chan )); @@ -109,8 +108,9 @@ GLOBAL char *Channel_UserModes PARAMS(( CHANNEL *Chan, CLIENT *Client )); GLOBAL bool Channel_IsMemberOf PARAMS(( CHANNEL *Chan, CLIENT *Client )); -GLOBAL bool Channel_Write PARAMS(( CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Text )); -GLOBAL bool Channel_Notice PARAMS(( CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Text)); +GLOBAL bool Channel_Write PARAMS((CHANNEL *Chan, CLIENT *From, CLIENT *Client, + const char *Command, bool SendErrors, + const char *Text)); GLOBAL CHANNEL *Channel_Create PARAMS(( char *Name )); @@ -124,5 +124,12 @@ GLOBAL bool Channel_AddBan PARAMS((CHANNEL *c, const char *Mask )); GLOBAL bool Channel_ShowBans PARAMS((CLIENT *client, CHANNEL *c)); GLOBAL bool Channel_ShowInvites PARAMS((CLIENT *client, CHANNEL *c)); + +GLOBAL void Channel_LogServer PARAMS((char *msg)); + +#define Channel_IsLocal(c) (Channel_Name(c)[0] == '&') + + #endif + /* -eof- */ diff --git a/src/ngircd/client.c b/src/ngircd/client.c index 24b24c7..1d01f78 100644 --- a/src/ngircd/client.c +++ b/src/ngircd/client.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2005 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -17,8 +17,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: client.c,v 1.98 2008/04/04 19:30:01 fw Exp $"; - #include "imp.h" #include #include @@ -66,8 +64,13 @@ static void Generate_MyToken PARAMS(( CLIENT *Client )); static void Adjust_Counters PARAMS(( CLIENT *Client )); static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer, - CLIENT *TopServer, int Type, char *ID, char *User, char *Hostname, - char *Info, int Hops, int Token, char *Modes, bool Idented)); + CLIENT *TopServer, int Type, char *ID, + char *User, char *Hostname, char *Info, + int Hops, int Token, char *Modes, + bool Idented)); + +static void Destroy_UserOrService PARAMS((CLIENT *Client, char *Txt, char *FwdMsg, + bool SendQuit)); GLOBAL void @@ -260,41 +263,8 @@ Client_Destroy( CLIENT *Client, char *LogMsg, char *FwdMsg, bool SendQuit ) if( last ) last->next = c->next; else My_Clients = (CLIENT *)c->next; - if( c->type == CLIENT_USER ) - { - if( c->conn_id != NONE ) - { - /* Ein lokaler User */ - Log( LOG_NOTICE, "User \"%s\" unregistered (connection %d): %s", Client_Mask( c ), c->conn_id, txt ); - - if( SendQuit ) - { - /* Alle andere Server informieren! */ - if( FwdMsg ) IRC_WriteStrServersPrefix( NULL, c, "QUIT :%s", FwdMsg ); - else IRC_WriteStrServersPrefix( NULL, c, "QUIT :" ); - } - } - else - { - /* Remote User */ - Log( LOG_DEBUG, "User \"%s\" unregistered: %s", Client_Mask( c ), txt ); - - if( SendQuit ) - { - /* Andere Server informieren, ausser denen, die "in - * Richtung dem liegen", auf dem der User registriert - * ist. Von denen haben wir das QUIT ja wohl bekommen. */ - if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "QUIT :%s", FwdMsg ); - else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "QUIT :" ); - } - } - - /* Unregister client from channels */ - Channel_Quit( c, FwdMsg ? FwdMsg : c->id ); - - /* Register client in My_Whowas structure */ - Client_RegisterWhowas( c ); - } + if(c->type == CLIENT_USER || c->type == CLIENT_SERVICE) + Destroy_UserOrService(c, txt, FwdMsg, SendQuit); else if( c->type == CLIENT_SERVER ) { if( c != This_Server ) @@ -432,7 +402,8 @@ Client_SetAway( CLIENT *Client, char *Txt ) assert( Txt != NULL ); strlcpy( Client->away, Txt, sizeof( Client->away )); - Log( LOG_DEBUG, "User \"%s\" is away: %s", Client_Mask( Client ), Txt ); + LogDebug("%s \"%s\" is away: %s", Client_TypeText(Client), + Client_Mask(Client), Txt); } /* Client_SetAway */ @@ -530,7 +501,7 @@ Client_ModeDel( CLIENT *Client, char Mode ) GLOBAL CLIENT * -Client_Search( char *Nick ) +Client_Search( const char *Nick ) { /* return Client-Structure that has the corresponding Nick. * If none is found, return NULL. @@ -1142,4 +1113,71 @@ Client_RegisterWhowas( CLIENT *Client ) } /* Client_RegisterWhowas */ +GLOBAL char * +Client_TypeText(CLIENT *Client) +{ + assert(Client != NULL); + switch (Client_Type(Client)) { + case CLIENT_USER: + return "User"; + break; + case CLIENT_SERVICE: + return "Service"; + break; + case CLIENT_SERVER: + return "Server"; + break; + default: + return "Client"; + } +} /* Client_TypeText */ + + +/** + * Destroy user or service client. + */ +static void +Destroy_UserOrService(CLIENT *Client, char *Txt, char *FwdMsg, bool SendQuit) +{ + if(Client->conn_id != NONE) { + /* Local (directly connected) client */ + Log(LOG_NOTICE, + "%s \"%s\" unregistered (connection %d): %s", + Client_TypeText(Client), Client_Mask(Client), + Client->conn_id, Txt); + + if (SendQuit) { + /* Inforam all the other servers */ + if (FwdMsg) + IRC_WriteStrServersPrefix(NULL, + Client, "QUIT :%s", FwdMsg ); + else + IRC_WriteStrServersPrefix(NULL, + Client, "QUIT :"); + } + } else { + /* Remote client */ + LogDebug("%s \"%s\" unregistered: %s", + Client_TypeText(Client), Client_Mask(Client), Txt); + + if(SendQuit) { + /* Inform all the other servers, but the ones in the + * direction we got the QUIT from */ + if(FwdMsg) + IRC_WriteStrServersPrefix(Client_NextHop(Client), + Client, "QUIT :%s", FwdMsg ); + else + IRC_WriteStrServersPrefix(Client_NextHop(Client), + Client, "QUIT :" ); + } + } + + /* Unregister client from channels */ + Channel_Quit(Client, FwdMsg ? FwdMsg : Client->id); + + /* Register client in My_Whowas structure */ + Client_RegisterWhowas(Client); +} /* Destroy_UserOrService */ + + /* -eof- */ diff --git a/src/ngircd/client.h b/src/ngircd/client.h index d211d23..6fcf7b6 100644 --- a/src/ngircd/client.h +++ b/src/ngircd/client.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -8,16 +8,12 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: client.h,v 1.46 2007/01/23 16:07:19 alex Exp $ - * * Client management (header) */ - #ifndef __client_h__ #define __client_h__ - #define CLIENT_UNKNOWN 1 /* connection of unknown type */ #define CLIENT_GOTPASS 2 /* client did send PASS */ #define CLIENT_GOTNICK 4 /* client did send NICK */ @@ -26,6 +22,7 @@ #define CLIENT_SERVER 32 /* client is a server */ #define CLIENT_SERVICE 64 /* client is a service */ #define CLIENT_UNKNOWNSERVER 128 /* unregistered server connection */ +#define CLIENT_GOTPASS_2813 256 /* client did send PASS, RFC 2813 style */ #define CLIENT_TYPE int @@ -85,7 +82,7 @@ GLOBAL CLIENT *Client_ThisServer PARAMS(( void )); GLOBAL CLIENT *Client_GetFromToken PARAMS(( CLIENT *Client, int Token )); -GLOBAL CLIENT *Client_Search PARAMS(( char *ID )); +GLOBAL CLIENT *Client_Search PARAMS(( const char *ID )); GLOBAL CLIENT *Client_First PARAMS(( void )); GLOBAL CLIENT *Client_Next PARAMS(( CLIENT *c )); @@ -149,8 +146,8 @@ GLOBAL int Client_GetLastWhowasIndex PARAMS(( void )); GLOBAL void Client_RegisterWhowas PARAMS(( CLIENT *Client )); +GLOBAL char * Client_TypeText PARAMS((CLIENT *Client)); #endif - /* -eof- */ diff --git a/src/ngircd/conf-ssl.h b/src/ngircd/conf-ssl.h new file mode 100644 index 0000000..e88d3e8 --- /dev/null +++ b/src/ngircd/conf-ssl.h @@ -0,0 +1,47 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * SSL defines. + */ + +#ifndef conf_ssl_h +#define conf_ssl_h + +#ifdef HAVE_LIBSSL +#define SSL_SUPPORT +#include +#endif +#ifdef HAVE_LIBGNUTLS +#define SSL_SUPPORT +#include +#ifndef LIBGNUTLS_VERSION_MAJOR +#define gnutls_certificate_credentials_t gnutls_certificate_credentials +#define gnutls_cipher_algorithm_t gnutls_cipher_algorithm +#define gnutls_datum_t gnutls_datum +#define gnutls_dh_params_t gnutls_dh_params +#define gnutls_session_t gnutls_session +#define gnutls_transport_ptr_t gnutls_transport_ptr +#endif +#endif + +#ifdef SSL_SUPPORT +struct ConnSSL_State { +#ifdef HAVE_LIBSSL + SSL *ssl; +#endif +#ifdef HAVE_LIBGNUTLS + gnutls_session gnutls_session; + void *cookie; /* pointer to server configuration structure + (for outgoing connections), or NULL. */ +#endif +}; + +bool +ConnSSL_InitLibrary(void); +#else +static inline bool +ConnSSL_InitLibrary(void) { return true; } +#endif /* SSL_SUPPORT */ + +#endif /* conf_ssl_h */ + +/* -eof- */ diff --git a/src/ngircd/conf.c b/src/ngircd/conf.c index 5d4c695..fe05938 100644 --- a/src/ngircd/conf.c +++ b/src/ngircd/conf.c @@ -14,8 +14,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: conf.c,v 1.105 2008/03/18 20:12:47 fw Exp $"; - #include "imp.h" #include #include @@ -44,6 +42,7 @@ static char UNUSED id[] = "$Id: conf.c,v 1.105 2008/03/18 20:12:47 fw Exp $"; #include "client.h" #include "defines.h" #include "log.h" +#include "match.h" #include "resolve.h" #include "tool.h" @@ -56,18 +55,6 @@ static CONF_SERVER New_Server; static int New_Server_Idx; -#ifdef WANT_IPV6 -/* - * these options appeared in ngircd 0.12; they are here - * for backwards compatibility. They should be removed - * in the future. Instead of setting these options, - * the "Listen" option should be set accordingly. - */ -static bool Conf_ListenIPv6; -static bool Conf_ListenIPv4; -#endif - - static void Set_Defaults PARAMS(( bool InitServers )); static bool Read_Config PARAMS(( bool ngircd_starting )); static bool Validate_Config PARAMS(( bool TestOnly, bool Rehash )); @@ -84,6 +71,44 @@ static void Config_Error_TooLong PARAMS(( const int LINE, const char *Value )); static void Init_Server_Struct PARAMS(( CONF_SERVER *Server )); +#ifdef WANT_IPV6 +#define DEFAULT_LISTEN_ADDRSTR "::,0.0.0.0" +#else +#define DEFAULT_LISTEN_ADDRSTR "0.0.0.0" +#endif + +#ifdef SSL_SUPPORT +struct SSLOptions Conf_SSLOptions; + +static void +ConfSSL_Init(void) +{ + free(Conf_SSLOptions.KeyFile); + Conf_SSLOptions.KeyFile = NULL; + + free(Conf_SSLOptions.CertFile); + Conf_SSLOptions.CertFile = NULL; + + free(Conf_SSLOptions.DHFile); + Conf_SSLOptions.DHFile = NULL; + array_free_wipe(&Conf_SSLOptions.KeyFilePassword); +} + + +static void +ConfSSL_Puts(void) +{ + if (Conf_SSLOptions.KeyFile) + printf( " SSLKeyFile = %s\n", Conf_SSLOptions.KeyFile); + if (Conf_SSLOptions.CertFile) + printf( " SSLCertFile = %s\n", Conf_SSLOptions.CertFile); + if (Conf_SSLOptions.DHFile) + printf( " SSLDHFile = %s\n", Conf_SSLOptions.DHFile); + if (array_bytes(&Conf_SSLOptions.KeyFilePassword)) + puts(" SSLKeyFilePassword = " ); + array_free_wipe(&Conf_SSLOptions.KeyFilePassword); +} +#endif static char * strdup_warn(const char *str) @@ -211,10 +236,16 @@ Conf_Test( void ) printf( " MotdPhrase = %s\n", Conf_MotdPhrase ); printf( " ChrootDir = %s\n", Conf_Chroot ); printf( " PidFile = %s\n", Conf_PidFile); + printf(" Listen = %s\n", Conf_ListenAddress); fputs(" Ports = ", stdout); ports_puts(&Conf_ListenPorts); - printf(" Listen = %s\n", Conf_ListenAddress); +#ifdef SSL_SUPPORT + fputs(" SSLPorts = ", stdout); + ports_puts(&Conf_SSLOptions.ListenPorts); + ConfSSL_Puts(); +#endif + pwd = getpwuid( Conf_UID ); if( pwd ) printf( " ServerUID = %s\n", pwd->pw_name ); else printf( " ServerUID = %ld\n", (long)Conf_UID ); @@ -228,13 +259,9 @@ Conf_Test( void ) printf( " OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode)); printf( " PredefChannelsOnly = %s\n", yesno_to_str(Conf_PredefChannelsOnly)); printf( " NoDNS = %s\n", yesno_to_str(Conf_NoDNS)); + printf( " NoIdent = %s\n", yesno_to_str(Conf_NoIdent)); #ifdef WANT_IPV6 - /* both are deprecated, only mention them if their default value changed. */ - if (!Conf_ListenIPv6) - puts(" ListenIPv6 = no"); - if (!Conf_ListenIPv4) - puts(" ListenIPv4 = no"); printf(" ConnectIPv4 = %s\n", yesno_to_str(Conf_ConnectIPv6)); printf(" ConnectIPv6 = %s\n", yesno_to_str(Conf_ConnectIPv4)); #endif @@ -262,8 +289,12 @@ Conf_Test( void ) printf( " Name = %s\n", Conf_Server[i].name ); printf( " Host = %s\n", Conf_Server[i].host ); printf( " Port = %u\n", (unsigned int)Conf_Server[i].port ); +#ifdef SSL_SUPPORT + printf( " SSLConnect = %s\n", Conf_Server[i].SSLConnect?"yes":"no"); +#endif printf( " MyPassword = %s\n", Conf_Server[i].pwd_in ); printf( " PeerPassword = %s\n", Conf_Server[i].pwd_out ); + printf( " ServiceMask = %s\n", Conf_Server[i].svs_mask); printf( " Group = %d\n", Conf_Server[i].group ); printf( " Passive = %s\n\n", Conf_Server[i].flags & CONF_SFLAG_DISABLED ? "yes" : "no"); } @@ -441,6 +472,16 @@ Conf_AddServer( char *Name, UINT16 Port, char *Host, char *MyPwd, char *PeerPwd } /* Conf_AddServer */ +/** + * Check if the given nick name is an service + */ +GLOBAL bool +Conf_IsService(int ConfServer, char *Nick) +{ + return MatchCaseInsensitive(Conf_Server[ConfServer].svs_mask, Nick); +} /* Conf_IsService */ + + static void Set_Defaults( bool InitServers ) { @@ -479,15 +520,13 @@ Set_Defaults( bool InitServers ) Conf_OperCanMode = false; Conf_NoDNS = false; + Conf_NoIdent = false; Conf_PredefChannelsOnly = false; Conf_OperServerMode = false; Conf_ConnectIPv4 = true; Conf_ConnectIPv6 = true; -#ifdef WANT_IPV6 - Conf_ListenIPv4 = true; - Conf_ListenIPv6 = true; -#endif + Conf_MaxConnections = 0; Conf_MaxConnectionsIP = 5; Conf_MaxJoins = 10; @@ -560,7 +599,9 @@ Read_Config( bool ngircd_starting ) strcpy( section, "" ); Init_Server_Struct( &New_Server ); New_Server_Idx = NONE; - +#ifdef SSL_SUPPORT + ConfSSL_Init(); +#endif /* Read configuration file */ while( true ) { if( ! fgets( str, LINE_LEN, fd )) break; @@ -669,18 +710,9 @@ Read_Config( bool ngircd_starting ) } } - if (!Conf_ListenAddress) { - /* no Listen addresses configured, use default */ -#ifdef WANT_IPV6 - /* Conf_ListenIPv6/4 should no longer be used */ - if (Conf_ListenIPv6 && Conf_ListenIPv4) - Conf_ListenAddress = strdup_warn("::,0.0.0.0"); - else if (Conf_ListenIPv6) - Conf_ListenAddress = strdup_warn("::"); - else -#endif - Conf_ListenAddress = strdup_warn("0.0.0.0"); - } + if (!Conf_ListenAddress) + Conf_ListenAddress = strdup_warn(DEFAULT_LISTEN_ADDRSTR); + if (!Conf_ListenAddress) { Config_Error(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME); exit(1); @@ -873,29 +905,21 @@ Handle_GLOBAL( int Line, char *Var, char *Arg ) Conf_NoDNS = Check_ArgIsTrue( Arg ); return; } -#ifdef WANT_IPV6 - /* the default setting for all the WANT_IPV6 special options is 'true' */ - if (strcasecmp(Var, "ListenIPv6") == 0) { /* DEPRECATED, option appeared in 0.12.0 */ - /* - * listen on ipv6 sockets, if available? - * Deprecated use "Listen = 0.0.0.0" (or, rather, do not list "::") - */ - Conf_ListenIPv6 = Check_ArgIsTrue( Arg ); - Config_Error(LOG_WARNING, "%s, line %d: %s=%s is deprecated, %sinclude '::' in \"Listen =\" option instead", - NGIRCd_ConfFile, Line, Var, yesno_to_str(Conf_ListenIPv6), Conf_ListenIPv6 ? " ":"do not "); - return; - } - if (strcasecmp(Var, "ListenIPv4") == 0) { /* DEPRECATED, option appeared in 0.12.0 */ - /* - * listen on ipv4 sockets, if available? - * this allows "ipv6-only" setups - * Deprecated use "Listen = ::" (or, rather, do not list "0.0.0.0") - */ - Conf_ListenIPv4 = Check_ArgIsTrue( Arg ); - Config_Error(LOG_WARNING, "%s, line %d: %s=%s is deprecated, %sinclude '0.0.0.0' in \"Listen =\" option instead", - NGIRCd_ConfFile, Line, Var, yesno_to_str(Conf_ListenIPv4), Conf_ListenIPv4 ? " ":"do not "); + if (strcasecmp(Var, "NoIdent") == 0) { + /* don't do IDENT lookups when clients connect? */ + Conf_NoIdent = Check_ArgIsTrue(Arg); +#ifndef IDENTAUTH + if (!Conf_NoIdent) { + /* user has enabled ident lookups explicitly, but ... */ + Config_Error(LOG_WARNING, + "%s: line %d: NoIdent=False, but ngircd was built without IDENT support", + NGIRCd_ConfFile, Line); + } +#endif return; } +#ifdef WANT_IPV6 + /* the default setting for all the WANT_IPV6 special options is 'true' */ if( strcasecmp( Var, "ConnectIPv6" ) == 0 ) { /* connect to other hosts using ipv6, if they have an AAAA record? */ Conf_ConnectIPv6 = Check_ArgIsTrue( Arg ); @@ -970,6 +994,37 @@ Handle_GLOBAL( int Line, char *Var, char *Arg ) } return; } + +#ifdef SSL_SUPPORT + if( strcasecmp( Var, "SSLPorts" ) == 0 ) { + ports_parse(&Conf_SSLOptions.ListenPorts, Line, Arg); + return; + } + + if( strcasecmp( Var, "SSLKeyFile" ) == 0 ) { + assert(Conf_SSLOptions.KeyFile == NULL ); + Conf_SSLOptions.KeyFile = strdup_warn(Arg); + return; + } + if( strcasecmp( Var, "SSLCertFile" ) == 0 ) { + assert(Conf_SSLOptions.CertFile == NULL ); + Conf_SSLOptions.CertFile = strdup_warn(Arg); + return; + } + + if( strcasecmp( Var, "SSLKeyFilePassword" ) == 0 ) { + assert(array_bytes(&Conf_SSLOptions.KeyFilePassword) == 0); + if (!array_copys(&Conf_SSLOptions.KeyFilePassword, Arg)) + Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Could not copy %s: %s!", + NGIRCd_ConfFile, Line, Var, strerror(errno)); + return; + } + if( strcasecmp( Var, "SSLDHFile" ) == 0 ) { + assert(Conf_SSLOptions.DHFile == NULL); + Conf_SSLOptions.DHFile = strdup_warn( Arg ); + return; + } +#endif Config_Error(LOG_ERR, "%s, line %d (section \"Global\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var); } /* Handle_GLOBAL */ @@ -1079,6 +1134,12 @@ Handle_SERVER( int Line, char *Var, char *Arg ) NGIRCd_ConfFile, Line, port ); return; } +#ifdef SSL_SUPPORT + if( strcasecmp( Var, "SSLConnect" ) == 0 ) { + New_Server.SSLConnect = Check_ArgIsTrue(Arg); + return; + } +#endif if( strcasecmp( Var, "Group" ) == 0 ) { /* Server group */ #ifdef HAVE_ISDIGIT @@ -1094,6 +1155,13 @@ Handle_SERVER( int Line, char *Var, char *Arg ) New_Server.flags |= CONF_SFLAG_DISABLED; return; } + if (strcasecmp(Var, "ServiceMask") == 0) { + len = strlcpy(New_Server.svs_mask, ngt_LowerStr(Arg), + sizeof(New_Server.svs_mask)); + if (len >= sizeof(New_Server.svs_mask)) + Config_Error_TooLong(Line, Var); + return; + } Config_Error( LOG_ERR, "%s, line %d (section \"Server\"): Unknown variable \"%s\"!", NGIRCd_ConfFile, Line, Var ); diff --git a/src/ngircd/conf.h b/src/ngircd/conf.h index 6ec5bce..5328465 100644 --- a/src/ngircd/conf.h +++ b/src/ngircd/conf.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -8,8 +8,6 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: conf.h,v 1.49 2008/03/18 20:12:47 fw Exp $ - * * Configuration management (header) */ @@ -24,6 +22,9 @@ #include "portab.h" #include "tool.h" #include "ng_ipaddr.h" +#include "resolve.h" +#include "conf-ssl.h" + typedef struct _Conf_Oper { @@ -44,10 +45,28 @@ typedef struct _Conf_Server RES_STAT res_stat; /* Status of the resolver */ int flags; /* Flags */ CONN_ID conn_id; /* ID of server connection or NONE */ - ng_ipaddr_t bind_addr; /* source address to use for outgoing connections */ + ng_ipaddr_t bind_addr; /* source address to use for outgoing + connections */ ng_ipaddr_t dst_addr[2]; /* list of addresses to connect to */ +#ifdef SSL_SUPPORT + bool SSLConnect; /* connect() using SSL? */ +#endif + char svs_mask[CLIENT_ID_LEN]; /* Mask of nick names that are + services */ } CONF_SERVER; + +#ifdef SSL_SUPPORT +struct SSLOptions { + char *KeyFile; + char *CertFile; + char *DHFile; + array ListenPorts; + array KeyFilePassword; +}; +#endif + + typedef struct _Conf_Channel { char name[CHANNEL_NAME_LEN]; /* Name of the channel */ @@ -124,6 +143,9 @@ GLOBAL bool Conf_OperCanMode; /* Disable all DNS functions? */ GLOBAL bool Conf_NoDNS; +/* Disable IDENT lookups, even when compiled with support for it */ +GLOBAL bool Conf_NoIdent; + /* * try to connect to remote systems using the ipv6 protocol, * if they have an ipv6 address? (default yes) @@ -163,6 +185,8 @@ GLOBAL bool Conf_EnablePassiveServer PARAMS((const char *Name)); GLOBAL bool Conf_DisableServer PARAMS(( char *Name )); GLOBAL bool Conf_AddServer PARAMS(( char *Name, UINT16 Port, char *Host, char *MyPwd, char *PeerPwd )); +GLOBAL bool Conf_IsService PARAMS((int ConfServer, char *Nick)); + #endif diff --git a/src/ngircd/conn-func.c b/src/ngircd/conn-func.c index 2f15786..8da62ae 100644 --- a/src/ngircd/conn-func.c +++ b/src/ngircd/conn-func.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -176,6 +176,17 @@ Conn_Options( CONN_ID Idx ) /** + * Set connection option. + */ +GLOBAL void +Conn_SetOption(CONN_ID Idx, int Option) +{ + assert(Idx > NONE); + Conn_OPTION_ADD(&My_Connections[Idx], Option); +} /* Conn_SetOption */ + + +/** * Get the start time of the connection. * The result is the start time in seconds since 1970-01-01, as reported * by the C function time(NULL). diff --git a/src/ngircd/conn-func.h b/src/ngircd/conn-func.h index e762e5c..9a1859d 100644 --- a/src/ngircd/conn-func.h +++ b/src/ngircd/conn-func.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -8,8 +8,6 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: conn-func.h,v 1.7 2007/10/04 15:03:56 alex Exp $ - * * Connection management: Global functions (header) */ @@ -22,7 +20,11 @@ * containing connection handling functions. So other modules must only * include this conn-func.h header. */ #ifndef CONN_MODULE -# include "conn.h" +#include "conn.h" +#else +#define Conn_OPTION_ADD( x, opt ) ( (x)->options |= (opt) ) +#define Conn_OPTION_DEL( x, opt ) ( (x)->options &= ~(opt) ) +#define Conn_OPTION_ISSET( x, opt ) ( ((x)->options & (opt)) != 0) #endif @@ -49,14 +51,11 @@ GLOBAL CONN_ID Conn_First PARAMS(( void )); GLOBAL CONN_ID Conn_Next PARAMS(( CONN_ID Idx )); GLOBAL UINT16 Conn_Options PARAMS(( CONN_ID Idx )); +GLOBAL void Conn_SetOption PARAMS(( CONN_ID Idx, int Option )); GLOBAL void Conn_ResetWCounter PARAMS(( void )); GLOBAL long Conn_WCounter PARAMS(( void )); -#define Conn_OPTION_ADD( x, opt ) ( (x)->options |= (opt) ) -#define Conn_OPTION_DEL( x, opt ) ( (x)->options &= ~(opt) ) -#define Conn_OPTION_ISSET( x, opt ) ( ((x)->options & (opt)) != 0) - #endif diff --git a/src/ngircd/conn-ssl.c b/src/ngircd/conn-ssl.c new file mode 100644 index 0000000..dc165d7 --- /dev/null +++ b/src/ngircd/conn-ssl.c @@ -0,0 +1,727 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * + * SSL wrapper functions. + * Copyright (c) 2005-2008 Florian Westphal + */ + +#include "portab.h" +#include "imp.h" +#include "conf-ssl.h" + +#ifdef SSL_SUPPORT + +#include "io.h" +#include +#include +#include +#include +#include + +#define CONN_MODULE +#include "conn.h" +#include "conf.h" +#include "conn-func.h" +#include "conn-ssl.h" +#include "log.h" + +#include "exp.h" +#include "defines.h" + +extern struct SSLOptions Conf_SSLOptions; + +#ifdef HAVE_LIBSSL +#include +#include + +static SSL_CTX * ssl_ctx; +static DH *dh_params; + +static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c )); +#endif + +#ifdef HAVE_LIBGNUTLS +#include +#include +#include +#include +#include + +#define DH_BITS 1024 +static gnutls_certificate_credentials_t x509_cred; +static gnutls_dh_params_t dh_params; + +static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void )); +#endif + +static bool ConnSSL_Init_SSL PARAMS(( CONNECTION *c )); +static int ConnectAccept PARAMS(( CONNECTION *c, bool connect )); +static int ConnSSL_HandleError PARAMS(( CONNECTION *c, const int code, const char *fname )); + +#ifdef HAVE_LIBGNUTLS +static char * openreadclose(const char *name, size_t *len) +{ + char *buf = NULL; + struct stat s; + ssize_t br; + int fd = open(name, O_RDONLY); + if (fd < 0) { + Log(LOG_ERR, "Could not open %s: %s", name, strerror(errno)); + return NULL; + } + if (fstat(fd, &s)) { + Log(LOG_ERR, "Could not fstat %s: %s", name, strerror(errno)); + goto out; + } + if (!S_ISREG(s.st_mode)) { + Log(LOG_ERR, "%s: Not a regular file", name); + goto out; + } + if (s.st_size <= 0) { + Log(LOG_ERR, "%s: invalid file length (size %ld <= 0)", name, (long) s.st_size); + goto out; + } + buf = malloc(s.st_size); + if (!buf) { + Log(LOG_ERR, "Could not malloc %lu bytes for file %s: %s", s.st_size, name, strerror(errno)); + goto out; + } + br = read(fd, buf, s.st_size); + if (br != (ssize_t)s.st_size) { + Log(LOG_ERR, "Could not read file %s: read returned %ld, expected %ld: %s", + name, (long) br, (long) s.st_size, br == -1 ? strerror(errno):"short read?!"); + memset(buf, 0, s.st_size); + free(buf); + buf = NULL; + } else { + *len = br; + } +out: + close(fd); + return buf; +} +#endif + + +#ifdef HAVE_LIBSSL +static void +LogOpenSSLError( const char *msg, const char *msg2 ) +{ + unsigned long err = ERR_get_error(); + char * errmsg = err ? ERR_error_string(err, NULL) : "Unable to determine error"; + + if (!msg) msg = "SSL Error"; + if (msg2) + Log( LOG_ERR, "%s: %s: %s", msg, msg2, errmsg); + else + Log( LOG_ERR, "%s: %s", msg, errmsg); +} + + +static int +pem_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + array *pass = password; + int passlen; + + (void)rwflag; /* rwflag is unused if DEBUG is not set. */ + assert(rwflag == 0); /* 0 -> callback used for decryption. + * See SSL_CTX_set_default_passwd_cb(3) */ + + passlen = (int) array_bytes(pass); + + LogDebug("pem_passwd_cb buf size %d, array size %d", size, passlen); + assert(passlen >= 0); + if (passlen <= 0) { + Log(LOG_ERR, "pem_passwd_cb: password required, but not set"); + return 0; + } + size = passlen > size ? size : passlen; + memcpy(buf, (char *)(array_start(pass)), size); + return size; +} +#endif + + +static bool +Load_DH_params(void) +{ +#ifdef HAVE_LIBSSL + FILE *fp; + bool ret = true; + + if (!Conf_SSLOptions.DHFile) { + Log(LOG_NOTICE, "Configuration option \"SSLDHFile\" not set!"); + return false; + } + fp = fopen(Conf_SSLOptions.DHFile, "r"); + if (!fp) { + Log(LOG_ERR, "%s: %s", Conf_SSLOptions.DHFile, strerror(errno)); + return false; + } + dh_params = PEM_read_DHparams(fp, NULL, NULL, NULL); + if (!dh_params) { + Log(LOG_ERR, "%s: PEM_read_DHparams failed!", + Conf_SSLOptions.DHFile); + ret = false; + } + fclose(fp); + return ret; +#endif +#ifdef HAVE_LIBGNUTLS + bool need_dhgenerate = true; + int err; + gnutls_dh_params_t tmp_dh_params; + + err = gnutls_dh_params_init(&tmp_dh_params); + if (err < 0) { + Log(LOG_ERR, "gnutls_dh_params_init: %s", gnutls_strerror(err)); + return false; + } + if (Conf_SSLOptions.DHFile) { + gnutls_datum_t dhparms; + size_t size; + dhparms.data = (unsigned char *) openreadclose(Conf_SSLOptions.DHFile, &size); + if (dhparms.data) { + dhparms.size = size; + err = gnutls_dh_params_import_pkcs3(tmp_dh_params, &dhparms, GNUTLS_X509_FMT_PEM); + if (err == 0) + need_dhgenerate = false; + else + Log(LOG_ERR, "gnutls_dh_params_init: %s", gnutls_strerror(err)); + + memset(dhparms.data, 0, size); + free(dhparms.data); + } + } + if (need_dhgenerate) { + Log(LOG_WARNING, + "SSLDHFile not set, generating %u bit DH parameters. This may take a while ...", + DH_BITS); + err = gnutls_dh_params_generate2(tmp_dh_params, DH_BITS); + if (err < 0) { + Log(LOG_ERR, "gnutls_dh_params_generate2: %s", gnutls_strerror(err)); + return false; + } + } + dh_params = tmp_dh_params; + return true; +#endif +} + + +void ConnSSL_Free(CONNECTION *c) +{ +#ifdef HAVE_LIBSSL + SSL *ssl = c->ssl_state.ssl; + if (ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + c->ssl_state.ssl = NULL; + } +#endif +#ifdef HAVE_LIBGNUTLS + gnutls_session_t sess = c->ssl_state.gnutls_session; + if (Conn_OPTION_ISSET(c, CONN_SSL)) { + gnutls_bye(sess, GNUTLS_SHUT_RDWR); + gnutls_deinit(sess); + } +#endif + assert(Conn_OPTION_ISSET(c, CONN_SSL)); + Conn_OPTION_DEL(c, (CONN_SSL_CONNECT|CONN_SSL|CONN_SSL_WANT_WRITE)); +} + + +bool +ConnSSL_InitLibrary( void ) +{ +#ifdef HAVE_LIBSSL + SSL_CTX *newctx; + + if (!ssl_ctx) { + SSL_library_init(); + SSL_load_error_strings(); + } + + if (!RAND_status()) { + Log(LOG_ERR, "OpenSSL PRNG not seeded: /dev/urandom missing?"); + /* + * it is probably best to fail and let the user install EGD or a similar program if no kernel random device is available. + * According to OpenSSL RAND_egd(3): "The automatic query of /var/run/egd-pool et al was added in OpenSSL 0.9.7"; + * so it makes little sense to deal with PRNGD seeding ourselves. + */ + return false; + } + + newctx = SSL_CTX_new(SSLv23_method()); + if (!newctx) { + LogOpenSSLError("SSL_CTX_new()", NULL); + return false; + } + + if (!ConnSSL_LoadServerKey_openssl(newctx)) + goto out; + + SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + SSL_CTX_free(ssl_ctx); + ssl_ctx = newctx; + Log(LOG_INFO, "%s initialized.", SSLeay_version(SSLEAY_VERSION)); + return true; +out: + SSL_CTX_free(newctx); + return false; +#endif +#ifdef HAVE_LIBGNUTLS + int err; + static bool initialized; + if (initialized) /* TODO: cannot reload gnutls keys: can't simply free x509 context -- it may still be in use */ + return false; + + err = gnutls_global_init(); + if (err) { + Log(LOG_ERR, "gnutls_global_init(): %s", gnutls_strerror(err)); + return false; + } + if (!ConnSSL_LoadServerKey_gnutls()) + return false; + Log(LOG_INFO, "gnutls %s initialized.", gnutls_check_version(NULL)); + initialized = true; + return true; +#endif +} + + +#ifdef HAVE_LIBGNUTLS +static bool +ConnSSL_LoadServerKey_gnutls(void) +{ + int err; + const char *cert_file; + + err = gnutls_certificate_allocate_credentials(&x509_cred); + if (err < 0) { + Log(LOG_ERR, "gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err)); + return false; + } + + cert_file = Conf_SSLOptions.CertFile ? Conf_SSLOptions.CertFile:Conf_SSLOptions.KeyFile; + if (!cert_file) { + Log(LOG_NOTICE, "No SSL server key configured, SSL disabled."); + return false; + } + + if (array_bytes(&Conf_SSLOptions.KeyFilePassword)) + Log(LOG_WARNING, + "Ignoring KeyFilePassword: Not supported by GNUTLS."); + + if (!Load_DH_params()) + return false; + + gnutls_certificate_set_dh_params(x509_cred, dh_params); + err = gnutls_certificate_set_x509_key_file(x509_cred, cert_file, Conf_SSLOptions.KeyFile, GNUTLS_X509_FMT_PEM); + if (err < 0) { + Log(LOG_ERR, "gnutls_certificate_set_x509_key_file (cert %s, key %s): %s", + cert_file, Conf_SSLOptions.KeyFile ? Conf_SSLOptions.KeyFile : "(NULL)", gnutls_strerror(err)); + return false; + } + return true; +} +#endif + + +#ifdef HAVE_LIBSSL +static bool +ConnSSL_LoadServerKey_openssl(SSL_CTX *ctx) +{ + char *cert_key; + + assert(ctx); + if (!Conf_SSLOptions.KeyFile) { + Log(LOG_NOTICE, "No SSL server key configured, SSL disabled."); + return false; + } + + SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ctx, &Conf_SSLOptions.KeyFilePassword); + + if (SSL_CTX_use_PrivateKey_file(ctx, Conf_SSLOptions.KeyFile, SSL_FILETYPE_PEM) != 1) { + array_free_wipe(&Conf_SSLOptions.KeyFilePassword); + LogOpenSSLError("SSL_CTX_use_PrivateKey_file", Conf_SSLOptions.KeyFile); + return false; + } + + cert_key = Conf_SSLOptions.CertFile ? Conf_SSLOptions.CertFile:Conf_SSLOptions.KeyFile; + if (SSL_CTX_use_certificate_chain_file(ctx, cert_key) != 1) { + array_free_wipe(&Conf_SSLOptions.KeyFilePassword); + LogOpenSSLError("SSL_CTX_use_certificate_file", cert_key); + return false; + } + + array_free_wipe(&Conf_SSLOptions.KeyFilePassword); + + if (!SSL_CTX_check_private_key(ctx)) { + LogOpenSSLError("Server Private Key does not match certificate", NULL); + return false; + } + if (Load_DH_params()) { + if (SSL_CTX_set_tmp_dh(ctx, dh_params) != 1) + LogOpenSSLError("Error setting DH Parameters", Conf_SSLOptions.DHFile); + /* don't return false here: the non-DH modes will still work */ + DH_free(dh_params); + dh_params = NULL; + } + return true; +} + + +#endif +static bool +ConnSSL_Init_SSL(CONNECTION *c) +{ + int ret; + assert(c != NULL); +#ifdef HAVE_LIBSSL + assert(ssl_ctx); + if (!ssl_ctx) /* NULL when library initialization failed */ + return false; + + assert(c->ssl_state.ssl == NULL); + + c->ssl_state.ssl = SSL_new(ssl_ctx); + if (!c->ssl_state.ssl) { + LogOpenSSLError("SSL_new()", NULL); + return false; + } + + ret = SSL_set_fd(c->ssl_state.ssl, c->sock); + if (ret != 1) { + LogOpenSSLError("SSL_set_fd()", NULL); + ConnSSL_Free(c); + return false; + } +#endif +#ifdef HAVE_LIBGNUTLS + ret = gnutls_set_default_priority(c->ssl_state.gnutls_session); + if (ret < 0) { + Log(LOG_ERR, "gnutls_set_default_priority: %s", gnutls_strerror(ret)); + ConnSSL_Free(c); + } + /* + * The intermediate (long) cast is here to avoid a warning like: + * "cast to pointer from integer of different size" on 64-bit platforms. + * There doesn't seem to be an alternate GNUTLS API we could use instead, see e.g. + * http://www.mail-archive.com/help-gnutls@gnu.org/msg00286.html + */ + gnutls_transport_set_ptr(c->ssl_state.gnutls_session, (gnutls_transport_ptr_t) (long) c->sock); + ret = gnutls_credentials_set(c->ssl_state.gnutls_session, GNUTLS_CRD_CERTIFICATE, x509_cred); + if (ret < 0) { + Log(LOG_ERR, "gnutls_credentials_set: %s", gnutls_strerror(ret)); + ConnSSL_Free(c); + } + gnutls_dh_set_prime_bits(c->ssl_state.gnutls_session, DH_BITS); +#endif + Conn_OPTION_ADD(c, CONN_SSL); + return true; +} + + +bool +ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s) +{ + bool ret; +#ifdef HAVE_LIBGNUTLS + int err; +#endif + assert(c != NULL); + assert(s != NULL); +#ifdef HAVE_LIBGNUTLS + err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_CLIENT); + if (err) { + Log(LOG_ERR, "gnutls_init: %s", gnutls_strerror(err)); + return false; + } +#endif + ret = ConnSSL_Init_SSL(c); + if (!ret) + return false; + Conn_OPTION_ADD(c, CONN_SSL_CONNECT); +#ifdef HAVE_LIBSSL + assert(c->ssl_state.ssl); + SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_NONE, NULL); +#endif + return true; +} + + +/* + Check an Handle Error return code after failed calls to ssl/tls functions. + OpenSSL: + SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or SSL_write() on ssl. + GNUTLS: + gnutlsssl_read(), gnutls_write() or gnutls_handshake(). + Return: -1 on fatal error, 0 if we can try again later. + */ +static int +ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname ) +{ +#ifdef HAVE_LIBSSL + int ret = SSL_ERROR_SYSCALL; + unsigned long sslerr; + int real_errno = errno; + + assert( fname ); + + ret = SSL_get_error(c->ssl_state.ssl, code); + switch (ret) { + case SSL_ERROR_WANT_READ: + io_event_del(c->sock, IO_WANTWRITE); + Conn_OPTION_ADD(c, CONN_SSL_WANT_READ); + return 0; /* try again later */ + case SSL_ERROR_WANT_WRITE: + io_event_del(c->sock, IO_WANTREAD); + Conn_OPTION_ADD(c, CONN_SSL_WANT_WRITE); /* fall through */ + case SSL_ERROR_NONE: + return 0; /* try again later */ + case SSL_ERROR_ZERO_RETURN: /* TLS/SSL Connection was shut down */ + LogOpenSSLError("TLS/SSL Connection shutdown", fname); + break; + /* + SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, SSL_ERROR_WANT_X509_LOOKUP + */ + case SSL_ERROR_SYSCALL: + sslerr = ERR_get_error(); + if (sslerr) { + Log( LOG_ERR, "%s: %s", fname, ERR_error_string(sslerr, NULL )); + } else { + + switch (code) { /* EOF that violated protocol */ + case 0: + Log(LOG_ERR, "%s: Client Disconnected", fname ); + break; + case -1: /* low level socket I/O error, check errno */ + Log(LOG_ERR, "%s: %s", fname, strerror(real_errno)); + } + } + break; + case SSL_ERROR_SSL: + LogOpenSSLError("TLS/SSL Protocol Error", fname); + break; + default: + Log( LOG_ERR, "%s: Unknown error %d!", fname, ret); + } + ConnSSL_Free(c); + return -1; +#endif +#ifdef HAVE_LIBGNUTLS + switch (code) { + case GNUTLS_E_AGAIN: + case GNUTLS_E_INTERRUPTED: + if (gnutls_record_get_direction(c->ssl_state.gnutls_session)) { /* need write */ + io_event_del(c->sock, IO_WANTREAD); + Conn_OPTION_ADD(c, CONN_SSL_WANT_WRITE); /* fall through */ + break; + } else { /* need read */ + io_event_del(c->sock, IO_WANTWRITE); + Conn_OPTION_ADD(c, CONN_SSL_WANT_READ); + break; + } + default: + assert(code < 0); + if (gnutls_error_is_fatal(code)) { + Log(LOG_ERR, "%s: %s", fname, gnutls_strerror(code)); + ConnSSL_Free(c); + return -1; + } + } + return 0; +#endif +} + + +static void +ConnSSL_LogCertInfo( CONNECTION *c ) +{ +#ifdef HAVE_LIBSSL + SSL *ssl = c->ssl_state.ssl; + + assert( c ); + assert( ssl ); + + Log(LOG_INFO, "New %s connection using cipher %s on socket %d.", + SSL_get_version(ssl), SSL_get_cipher(ssl), c->sock); +#endif +#ifdef HAVE_LIBGNUTLS + gnutls_session_t sess = c->ssl_state.gnutls_session; + gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(sess); + + Log(LOG_INFO, "New %s connection using cipher %s-%s on socket %d.", + gnutls_protocol_get_name(gnutls_protocol_get_version(sess)), + gnutls_cipher_get_name(cipher), + gnutls_mac_get_name(gnutls_mac_get(sess)), c->sock); +#endif +} + + +/* + Accept incoming SSL connection. + Return Values: + 1: SSL Connection established + 0: try again + -1: SSL Connection not established due to fatal error. +*/ +int +ConnSSL_Accept( CONNECTION *c ) +{ + assert(c != NULL); +#ifdef HAVE_LIBSSL + if (!c->ssl_state.ssl) { +#endif +#ifdef HAVE_LIBGNUTLS + if (!Conn_OPTION_ISSET(c, CONN_SSL)) { + int err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_SERVER); + if (err) { + Log(LOG_ERR, "gnutls_init: %s", gnutls_strerror(err)); + return false; + } +#endif + LogDebug("ConnSSL_Accept: Initializing SSL data"); + if (!ConnSSL_Init_SSL(c)) + return -1; + } + return ConnectAccept(c, false ); +} + + +int +ConnSSL_Connect( CONNECTION *c ) +{ + assert(c != NULL); +#ifdef HAVE_LIBSSL + assert(c->ssl_state.ssl); +#endif +#ifdef HAVE_LIBGNUTLS + assert(Conn_OPTION_ISSET(c, CONN_SSL)); +#endif + return ConnectAccept(c, true); +} + + +/* accept/connect wrapper. if connect is true, connect to peer, otherwise wait for incoming connection */ +static int +ConnectAccept( CONNECTION *c, bool connect) +{ + int ret; +#ifdef HAVE_LIBSSL + SSL *ssl = c->ssl_state.ssl; + assert(ssl != NULL); + + ret = connect ? SSL_connect(ssl) : SSL_accept(ssl); + if (1 != ret) + return ConnSSL_HandleError(c, ret, connect ? "SSL_connect": "SSL_accept"); +#endif +#ifdef HAVE_LIBGNUTLS + (void) connect; + assert(Conn_OPTION_ISSET(c, CONN_SSL)); + ret = gnutls_handshake(c->ssl_state.gnutls_session); + if (ret) + return ConnSSL_HandleError(c, ret, "gnutls_handshake"); +#endif /* _GNUTLS */ + Conn_OPTION_DEL(c, (CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_CONNECT)); + ConnSSL_LogCertInfo(c); + return 1; +} + + +ssize_t +ConnSSL_Write(CONNECTION *c, const void *buf, size_t count) +{ + ssize_t bw; + + Conn_OPTION_DEL(c, CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ); + + assert(count > 0); +#ifdef HAVE_LIBSSL + bw = (ssize_t) SSL_write(c->ssl_state.ssl, buf, count); +#endif +#ifdef HAVE_LIBGNUTLS + bw = gnutls_write(c->ssl_state.gnutls_session, buf, count); +#endif + if ( bw > 0 ) return bw; + if (ConnSSL_HandleError( c, bw, "ConnSSL_Write") == 0) + errno = EAGAIN; /* try again */ + return -1; +} + + +ssize_t +ConnSSL_Read(CONNECTION *c, void * buf, size_t count) +{ + ssize_t br; + + Conn_OPTION_DEL(c, CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ); +#ifdef HAVE_LIBSSL + br = (ssize_t) SSL_read(c->ssl_state.ssl, buf, count); + if (br > 0) /* on EOF we have to call ConnSSL_HandleError(), see SSL_read(3) */ + return br; +#endif +#ifdef HAVE_LIBGNUTLS + br = gnutls_read(c->ssl_state.gnutls_session, buf, count); + if (br >= 0) /* on EOF we must _not_ call ConnSSL_HandleError, see gnutls_record_recv(3) */ + return br; +#endif + /* error on read: switch ConnSSL_HandleError() return values -> 0 is "try again", so return -1 and set EAGAIN */ + if (ConnSSL_HandleError(c, br, "ConnSSL_Read") == 0) { + errno = EAGAIN; + return -1; + } + return 0; +} + + +bool +ConnSSL_GetCipherInfo(CONNECTION *c, char *buf, size_t len) +{ +#ifdef HAVE_LIBSSL + char *nl; + + SSL *ssl; + assert(c != NULL); + assert(len >= 128); + ssl = c->ssl_state.ssl; + if (!ssl) + return false; + *buf = 0; + SSL_CIPHER_description(SSL_get_current_cipher(ssl), buf, len); + nl = strchr(buf, '\n'); + if (nl) + *nl = 0; + return true; +#endif +#ifdef HAVE_LIBGNUTLS + assert(c != NULL); + assert(len >= 128); + if (Conn_OPTION_ISSET(c, CONN_SSL)) { + const char *name_cipher, *name_mac, *name_proto, *name_keyexchange; + unsigned keysize; + + gnutls_session_t sess = c->ssl_state.gnutls_session; + gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(sess); + name_cipher = gnutls_cipher_get_name(cipher); + name_mac = gnutls_mac_get_name(gnutls_mac_get(sess)); + keysize = gnutls_cipher_get_key_size(cipher) * 8; + name_proto = gnutls_protocol_get_name(gnutls_protocol_get_version(sess)); + name_keyexchange = gnutls_kx_get_name(gnutls_kx_get(sess)); + + return snprintf(buf, len, "%s-%s%15s Kx=%s Enc=%s(%u) Mac=%s", + name_cipher, name_mac, name_proto, name_keyexchange, name_cipher, keysize, name_mac) > 0; + } + return false; +#endif +} + + +#endif /* SSL_SUPPORT */ +/* -eof- */ + + diff --git a/src/ngircd/conn-ssl.h b/src/ngircd/conn-ssl.h new file mode 100644 index 0000000..4cb1bde --- /dev/null +++ b/src/ngircd/conn-ssl.h @@ -0,0 +1,28 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * SSL wrapper functions. (header) + */ + +#ifndef conn_ssl_h +#define conn_ssl_h + +#include "conf-ssl.h" +#include "conn.h" +#include "conf.h" + +#ifdef SSL_SUPPORT +GLOBAL void ConnSSL_Free PARAMS(( CONNECTION *c )); + +GLOBAL bool ConnSSL_PrepareConnect PARAMS(( CONNECTION *c, CONF_SERVER *s )); + +GLOBAL int ConnSSL_Accept PARAMS(( CONNECTION *c )); +GLOBAL int ConnSSL_Connect PARAMS(( CONNECTION *c )); + +GLOBAL ssize_t ConnSSL_Write PARAMS(( CONNECTION *c, const void *buf, size_t count)); +GLOBAL ssize_t ConnSSL_Read PARAMS(( CONNECTION *c, void *buf, size_t count)); + +GLOBAL bool ConnSSL_GetCipherInfo PARAMS(( CONNECTION *c, char *buf, size_t len )); +#endif /* SSL_SUPPORT */ +#endif /* conn_ssl_h */ + +/* -eof- */ diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c index 7efc5b8..bd1a5bd 100644 --- a/src/ngircd/conn.c +++ b/src/ngircd/conn.c @@ -15,10 +15,9 @@ #define CONN_MODULE #include "portab.h" +#include "conf-ssl.h" #include "io.h" -static char UNUSED id[] = "$Id: conn.c,v 1.221 2008/02/26 22:04:17 fw Exp $"; - #include "imp.h" #include #ifdef PROTOTYPES @@ -60,6 +59,7 @@ static char UNUSED id[] = "$Id: conn.c,v 1.221 2008/02/26 22:04:17 fw Exp $"; #include "ngircd.h" #include "client.h" #include "conf.h" +#include "conn-ssl.h" #include "conn-zip.h" #include "conn-func.h" #include "log.h" @@ -81,7 +81,7 @@ static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len )); static int New_Connection PARAMS(( int Sock )); static CONN_ID Socket2Index PARAMS(( int Sock )); static void Read_Request PARAMS(( CONN_ID Idx )); -static bool Handle_Buffer PARAMS(( CONN_ID Idx )); +static void Handle_Buffer PARAMS(( CONN_ID Idx )); static void Check_Connections PARAMS(( void )); static void Check_Servers PARAMS(( void )); static void Init_Conn_Struct PARAMS(( CONN_ID Idx )); @@ -92,6 +92,7 @@ static int NewListener PARAMS(( const char *listen_addr, UINT16 Port )); static array My_Listeners; static array My_ConnArray; +static size_t NumConnections; #ifdef TCPWRAP int allow_severity = LOG_INFO; @@ -100,6 +101,11 @@ int deny_severity = LOG_ERR; static void server_login PARAMS((CONN_ID idx)); +#ifdef SSL_SUPPORT +extern struct SSLOptions Conf_SSLOptions; +static void cb_connserver_login_ssl PARAMS((int sock, short what)); +static void cb_clientserver_ssl PARAMS((int sock, short what)); +#endif static void cb_Read_Resolver_Result PARAMS(( int sock, UNUSED short what)); static void cb_Connect_to_Server PARAMS(( int sock, UNUSED short what)); static void cb_clientserver PARAMS((int sock, short what)); @@ -108,10 +114,29 @@ static void cb_listen(int sock, short irrelevant) { (void) irrelevant; - New_Connection( sock ); + if (New_Connection( sock ) >= 0) + NumConnections++; + LogDebug("Total number of connections now %ld.", NumConnections); } +#ifdef SSL_SUPPORT +static void +cb_listen_ssl(int sock, short irrelevant) +{ + int fd; + (void) irrelevant; + fd = New_Connection(sock); + if (fd < 0) + return; + + NumConnections++; + LogDebug("Total number of connections now %ld.", NumConnections); + io_event_setcb(My_Connections[fd].sock, cb_clientserver_ssl); +} +#endif + + static void cb_connserver(int sock, UNUSED short what) { @@ -166,6 +191,13 @@ cb_connserver(int sock, UNUSED short what) if (res >= 0) /* connect succeeded, remove all additional addresses */ memset(&Conf_Server[res].dst_addr, 0, sizeof(&Conf_Server[res].dst_addr)); Conn_OPTION_DEL( &My_Connections[idx], CONN_ISCONNECTING ); +#ifdef SSL_SUPPORT + if ( Conn_OPTION_ISSET( &My_Connections[idx], CONN_SSL_CONNECT )) { + io_event_setcb( sock, cb_connserver_login_ssl ); + io_event_add( sock, IO_WANTWRITE|IO_WANTREAD ); + return; + } +#endif server_login(idx); } @@ -185,24 +217,88 @@ server_login(CONN_ID idx) } +#ifdef SSL_SUPPORT +static void +cb_connserver_login_ssl(int sock, short unused) +{ + CONN_ID idx = Socket2Index(sock); + + assert(idx >= 0); + if (idx < 0) { + io_close(sock); + return; + } + (void) unused; + switch (ConnSSL_Connect( &My_Connections[idx])) { + case 1: break; + case 0: LogDebug("ConnSSL_Connect: not ready"); + return; + case -1: + Log(LOG_ERR, "SSL connection on socket %d failed!", sock); + Conn_Close(idx, "Can't connect!", NULL, false); + return; + } + + Log( LOG_INFO, "SSL connection %d with \"%s:%d\" established.", idx, + My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port ); + + server_login(idx); +} +#endif + + static void cb_clientserver(int sock, short what) { - CONN_ID idx = Socket2Index( sock ); - if (idx <= NONE) { -#ifdef DEBUG - Log(LOG_WARNING, "WTF: cb_clientserver wants to write on unknown socket?!"); + CONN_ID idx = Socket2Index(sock); + + assert(idx >= 0); + + if (idx < 0) { + io_close(sock); + return; + } +#ifdef SSL_SUPPORT + if (what & IO_WANTREAD || (Conn_OPTION_ISSET(&My_Connections[idx], CONN_SSL_WANT_WRITE))) + Read_Request( idx ); /* if TLS layer needs to write additional data, call Read_Request instead so SSL/TLS can continue */ +#else + if (what & IO_WANTREAD) + Read_Request( idx ); #endif + if (what & IO_WANTWRITE) + Handle_Write( idx ); +} + + +#ifdef SSL_SUPPORT +static void +cb_clientserver_ssl(int sock, short what) +{ + CONN_ID idx = Socket2Index(sock); + + assert(idx >= 0); + + if (idx < 0) { io_close(sock); return; } + switch (ConnSSL_Accept(&My_Connections[idx])) { + case 1: break; /* OK */ + case 0: return; /* EAGAIN: this callback will be invoked again by the io layer */ + default: + Conn_Close( idx, "Socket closed!", "SSL accept error", false ); + return; + } if (what & IO_WANTREAD) - Read_Request( idx ); + Read_Request(idx); if (what & IO_WANTWRITE) - Handle_Write( idx ); + Handle_Write(idx); + + io_event_setcb(sock, cb_clientserver); /* SSL handshake completed */ } +#endif GLOBAL void @@ -214,12 +310,10 @@ Conn_Init( void ) /* Speicher fuer Verbindungs-Pool anfordern */ Pool_Size = CONNECTION_POOL; - if( Conf_MaxConnections > 0 ) - { - /* konfiguriertes Limit beachten */ - if( Pool_Size > Conf_MaxConnections ) Pool_Size = Conf_MaxConnections; - } - + if ((Conf_MaxConnections > 0) && + (Pool_Size > Conf_MaxConnections)) + Pool_Size = Conf_MaxConnections; + if (!array_alloc(&My_ConnArray, sizeof(CONNECTION), (size_t)Pool_Size)) { Log( LOG_EMERG, "Can't allocate memory! [Conn_Init]" ); exit( 1 ); @@ -252,11 +346,9 @@ Conn_Exit( void ) CONN_ID idx; - LogDebug("Shutting down all connections ..." ); - Conn_ExitListeners(); - /* Sockets schliessen */ + LogDebug("Shutting down all connections ..." ); for( idx = 0; idx < Pool_Size; idx++ ) { if( My_Connections[idx].sock > NONE ) { Conn_Close( idx, NULL, NGIRCd_SignalRestart ? @@ -325,8 +417,12 @@ Conn_InitListeners( void ) while (listen_addr) { ngt_TrimStr(listen_addr); - if (*listen_addr) + if (*listen_addr) { created += ports_initlisteners(&Conf_ListenPorts, listen_addr, cb_listen); +#ifdef SSL_SUPPORT + created += ports_initlisteners(&Conf_SSLOptions.ListenPorts, listen_addr, cb_listen_ssl); +#endif + } listen_addr = strtok(NULL, ","); } @@ -353,7 +449,8 @@ Conn_ExitListeners( void ) #endif arraylen = array_length(&My_Listeners, sizeof (int)); - Log( LOG_INFO, "Shutting down all listening sockets (%d total)...", arraylen ); + Log(LOG_INFO, + "Shutting down all listening sockets (%d total) ...", arraylen); fd = array_start(&My_Listeners); while(arraylen--) { assert(fd != NULL); @@ -479,105 +576,157 @@ NewListener(const char *listen_addr, UINT16 Port) return sock; } /* NewListener */ +#ifdef SSL_SUPPORT +/* + * SSL/TLS connections require extra treatment: + * When either CONN_SSL_WANT_WRITE or CONN_SSL_WANT_READ is set, we + * need to take care of that first, before checking read/write buffers. + * For instance, while we might have data in our write buffer, the + * TLS/SSL protocol might need to read internal data first for TLS/SSL + * writes to succeed. + * + * If this function returns true, such a condition is met and we have + * to reverse the condition (check for read even if we've data to write, + * do not check for read but writeability even if write-buffer is empty). + */ +static bool +SSL_WantRead(const CONNECTION *c) +{ + if (Conn_OPTION_ISSET(c, CONN_SSL_WANT_READ)) { + io_event_add(c->sock, IO_WANTREAD); + return true; + } + return false; +} +static bool +SSL_WantWrite(const CONNECTION *c) +{ + if (Conn_OPTION_ISSET(c, CONN_SSL_WANT_WRITE)) { + io_event_add(c->sock, IO_WANTWRITE); + return true; + } + return false; +} +#else +static inline bool +SSL_WantRead(UNUSED const CONNECTION *c) { return false; } +static inline bool +SSL_WantWrite(UNUSED const CONNECTION *c) { return false; } +#endif + +/** + * "Main Loop": Loop until shutdown or restart is signalled. + * This function loops until a shutdown or restart of ngIRCd is signalled and + * calls io_dispatch() to check for readable and writable sockets every second. + * It checks for status changes on pending connections (e. g. when a hostname + * has been resolved), checks for "penalties" and timeouts, and handles the + * input buffers. + */ GLOBAL void -Conn_Handler( void ) +Conn_Handler(void) { - /* "Main Loop.": Loop until a signal (for shutdown or restart) arrives. - * Call io_dispatch() to check for read/writeable sockets every second - * Wait for status change on pending connections (e.g: when the hostname has been resolved) - * check for penalty/timeouts - * handle input buffers - */ int i; unsigned int wdatalen; struct timeval tv; time_t t; - bool timeout; - while(( ! NGIRCd_SignalQuit ) && ( ! NGIRCd_SignalRestart )) { - timeout = true; + while (!NGIRCd_SignalQuit && !NGIRCd_SignalRestart) { + t = time(NULL); #ifdef ZEROCONF - Rendezvous_Handler( ); + Rendezvous_Handler(); #endif /* Should the configuration be reloaded? */ - if (NGIRCd_SignalRehash) { - NGIRCd_Rehash( ); - } + if (NGIRCd_SignalRehash) + NGIRCd_Rehash(); /* Check configured servers and established links */ - Check_Servers( ); - Check_Connections( ); - - t = time( NULL ); - - /* noch volle Lese-Buffer suchen */ - for( i = 0; i < Pool_Size; i++ ) { - if(( My_Connections[i].sock > NONE ) && ( array_bytes(&My_Connections[i].rbuf) > 0 ) && - ( My_Connections[i].delaytime < t )) - { - /* Kann aus dem Buffer noch ein Befehl extrahiert werden? */ - if (Handle_Buffer( i )) timeout = false; + Check_Servers(); + Check_Connections(); + + /* Look for non-empty read buffers ... */ + for (i = 0; i < Pool_Size; i++) { + if ((My_Connections[i].sock > NONE) + && (array_bytes(&My_Connections[i].rbuf) > 0) + && (My_Connections[i].delaytime < t)) { + /* ... and try to handle the received data */ + Handle_Buffer(i); } } - /* noch volle Schreib-Puffer suchen */ - for( i = 0; i < Pool_Size; i++ ) { - if ( My_Connections[i].sock <= NONE ) + /* Look for non-empty write buffers ... */ + for (i = 0; i < Pool_Size; i++) { + if (My_Connections[i].sock <= NONE) continue; wdatalen = (unsigned int)array_bytes(&My_Connections[i].wbuf); - #ifdef ZLIB - if (( wdatalen > 0 ) || ( array_bytes(&My_Connections[i].zip.wbuf)> 0 )) + if (wdatalen > 0 || + array_bytes(&My_Connections[i].zip.wbuf) > 0) #else - if ( wdatalen > 0 ) + if (wdatalen > 0) #endif { - /* Socket der Verbindung in Set aufnehmen */ - io_event_add( My_Connections[i].sock, IO_WANTWRITE ); + if (SSL_WantRead(&My_Connections[i])) + continue; + io_event_add(My_Connections[i].sock, + IO_WANTWRITE); } } - /* von welchen Sockets koennte gelesen werden? */ - for (i = 0; i < Pool_Size; i++ ) { - if ( My_Connections[i].sock <= NONE ) + /* Check from which sockets we possibly could read ... */ + for (i = 0; i < Pool_Size; i++) { + if (My_Connections[i].sock <= NONE) continue; - +#ifdef SSL_SUPPORT + if (SSL_WantWrite(&My_Connections[i])) + continue; /* TLS/SSL layer needs to write data; deal with this first */ +#endif if (Resolve_INPROGRESS(&My_Connections[i].res_stat)) { - /* wait for completion of Resolver Sub-Process */ - io_event_del( My_Connections[i].sock, IO_WANTREAD ); + /* Wait for completion of resolver sub-process ... */ + io_event_del(My_Connections[i].sock, + IO_WANTREAD); continue; } - if ( Conn_OPTION_ISSET( &My_Connections[i], CONN_ISCONNECTING )) - continue; /* wait for completion of connect() */ + if (Conn_OPTION_ISSET(&My_Connections[i], CONN_ISCONNECTING)) + /* Wait for completion of connect() ... */ + continue; - if( My_Connections[i].delaytime > t ) { - /* Fuer die Verbindung ist eine "Penalty-Zeit" gesetzt */ - io_event_del( My_Connections[i].sock, IO_WANTREAD ); + if (My_Connections[i].delaytime > t) { + /* There is a "penalty time" set: ignore socket! */ + io_event_del(My_Connections[i].sock, + IO_WANTREAD); continue; } - io_event_add( My_Connections[i].sock, IO_WANTREAD ); + io_event_add(My_Connections[i].sock, IO_WANTREAD); } - /* (re-)set timeout - tv_sec/usec are undefined after io_dispatch() returns */ + /* Set the timeout for reading from the network to 1 second, + * which is the granularity with witch we handle "penalty + * times" for example. + * Note: tv_sec/usec are undefined(!) after io_dispatch() + * returns, so we have to set it beforce each call to it! */ tv.tv_usec = 0; - tv.tv_sec = timeout ? 1 : 0; - - /* wait for activity */ - i = io_dispatch( &tv ); - if (i == -1 && errno != EINTR ) { - Log(LOG_EMERG, "Conn_Handler(): io_dispatch(): %s!", strerror(errno)); - Log(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME); - exit( 1 ); + tv.tv_sec = 1; + + /* Wait for activity ... */ + i = io_dispatch(&tv); + if (i == -1 && errno != EINTR) { + Log(LOG_EMERG, "Conn_Handler(): io_dispatch(): %s!", + strerror(errno)); + Log(LOG_ALERT, "%s exiting due to fatal errors!", + PACKAGE_NAME); + exit(1); } } - if( NGIRCd_SignalQuit ) Log( LOG_NOTICE|LOG_snotice, "Server going down NOW!" ); - else if( NGIRCd_SignalRestart ) Log( LOG_NOTICE|LOG_snotice, "Server restarting NOW!" ); + if (NGIRCd_SignalQuit) + Log(LOG_NOTICE | LOG_snotice, "Server going down NOW!"); + else if (NGIRCd_SignalRestart) + Log(LOG_NOTICE | LOG_snotice, "Server restarting NOW!"); } /* Conn_Handler */ @@ -808,7 +957,12 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) /* Search client, if any (re-check!) */ c = Conn_GetClient( Idx ); - +#ifdef SSL_SUPPORT + if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) { + Log(LOG_INFO, "SSL connection %d shutting down ...", Idx); + ConnSSL_Free(&My_Connections[Idx]); + } +#endif /* Shut down socket */ if (! io_close(My_Connections[Idx].sock)) { /* Oops, we can't close the socket!? This is ... ugly! */ @@ -879,7 +1033,11 @@ Conn_Close( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool InformClient ) /* Clean up connection structure (=free it) */ Init_Conn_Struct( Idx ); - LogDebug("Shutdown of connection %d completed.", Idx ); + assert(NumConnections > 0); + if (NumConnections) + NumConnections--; + LogDebug("Shutdown of connection %d completed, %ld connection%s left.", + Idx, NumConnections, NumConnections != 1 ? "s" : ""); } /* Conn_Close */ @@ -955,9 +1113,15 @@ Handle_Write( CONN_ID Idx ) ("Handle_Write() called for connection %d, %ld bytes pending ...", Idx, wdatalen); - len = write(My_Connections[Idx].sock, - array_start(&My_Connections[Idx].wbuf), wdatalen ); - +#ifdef SSL_SUPPORT + if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) { + len = ConnSSL_Write(&My_Connections[Idx], array_start(&My_Connections[Idx].wbuf), wdatalen); + } else +#endif + { + len = write(My_Connections[Idx].sock, + array_start(&My_Connections[Idx].wbuf), wdatalen ); + } if( len < 0 ) { if (errno == EAGAIN || errno == EINTR) return true; @@ -1002,7 +1166,7 @@ New_Connection( int Sock ) #endif ng_ipaddr_t new_addr; char ip_str[NG_INET_ADDRSTRLEN]; - int new_sock, new_sock_len, new_Pool_Size; + int new_sock, new_sock_len, identsock; CLIENT *c; long cnt; @@ -1021,6 +1185,7 @@ New_Connection( int Sock ) Log(LOG_CRIT, "fd %d: Can't convert IP address!", new_sock); Simple_Message(new_sock, "ERROR :Internal Server Error"); close(new_sock); + return -1; } #ifdef TCPWRAP @@ -1049,18 +1214,16 @@ New_Connection( int Sock ) return -1; } - if( new_sock >= Pool_Size ) { - new_Pool_Size = new_sock + 1; - /* No free Connection Structures, check if we may accept further connections */ - if ((( Conf_MaxConnections > 0) && Pool_Size >= Conf_MaxConnections) || - (new_Pool_Size < Pool_Size)) - { - Log( LOG_ALERT, "Can't accept connection: limit (%d) reached!", Pool_Size ); - Simple_Message( new_sock, "ERROR :Connection limit reached" ); - close( new_sock ); - return -1; - } + if ((Conf_MaxConnections > 0) && + (NumConnections >= (size_t) Conf_MaxConnections)) + { + Log( LOG_ALERT, "Can't accept connection: limit (%d) reached!", Conf_MaxConnections); + Simple_Message( new_sock, "ERROR :Connection limit reached" ); + close( new_sock ); + return -1; + } + if( new_sock >= Pool_Size ) { if (!array_alloc(&My_ConnArray, sizeof(CONNECTION), (size_t)new_sock)) { Log( LOG_EMERG, "Can't allocate memory! [New_Connection]" ); @@ -1073,7 +1236,7 @@ New_Connection( int Sock ) /* Adjust pointer to new block */ My_Connections = array_start(&My_ConnArray); - while (Pool_Size < new_Pool_Size) + while (Pool_Size <= new_sock) Init_Conn_Struct(Pool_Size++); } @@ -1106,10 +1269,14 @@ New_Connection( int Sock ) Client_SetHostname(c, My_Connections[new_sock].host); + identsock = new_sock; +#ifdef IDENTAUTH + if (Conf_NoIdent) + identsock = -1; +#endif if (!Conf_NoDNS) Resolve_Addr(&My_Connections[new_sock].res_stat, &new_addr, - My_Connections[new_sock].sock, cb_Read_Resolver_Result); - + identsock, cb_Read_Resolver_Result); Conn_SetPenalty(new_sock, 4); return new_sock; } /* New_Connection */ @@ -1160,6 +1327,11 @@ Read_Request( CONN_ID Idx ) return; } +#ifdef SSL_SUPPORT + if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL)) + len = ConnSSL_Read( &My_Connections[Idx], readbuf, sizeof(readbuf)); + else +#endif len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf)); if (len == 0) { Log(LOG_INFO, "%s:%u (%s) is closing the connection ...", @@ -1221,42 +1393,55 @@ Read_Request( CONN_ID Idx ) } /* Read_Request */ -static bool -Handle_Buffer( CONN_ID Idx ) +/** + * Handle all data in the connection read-buffer. + * All data is precessed until no complete command is left. When a fatal + * error occurs, the connection is shut down. + */ +static void +Handle_Buffer(CONN_ID Idx) { - /* Handle Data in Connections Read-Buffer. - * Return true if a reuqest was handled, false otherwise (also returned on errors). */ #ifndef STRICT_RFC char *ptr1, *ptr2, *first_eol; #endif char *ptr; size_t len, delta; - bool result; time_t starttime; #ifdef ZLIB bool old_z; #endif starttime = time(NULL); - result = false; for (;;) { /* Check penalty */ - if( My_Connections[Idx].delaytime > starttime) return result; + if (My_Connections[Idx].delaytime > starttime) + return; #ifdef ZLIB - /* unpack compressed data */ - if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ZIP )) - if( ! Unzip_Buffer( Idx )) return false; + /* Unpack compressed data, if compression is in use */ + if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ZIP)) { + /* When unzipping fails, Unzip_Buffer() shuts + * down the connection itself */ + if (!Unzip_Buffer(Idx)) + return; + } #endif if (0 == array_bytes(&My_Connections[Idx].rbuf)) - break; + return; - if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) /* make sure buf is NULL terminated */ - return false; + /* Make sure that the buffer is NULL terminated */ + if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) { + Conn_Close(Idx, NULL, + "Can't allocate memory [Handle_Buffer]", + true); + return; + } - /* A Complete Request end with CR+LF, see RFC 2812. */ + /* RFC 2812, section "2.3 Messages", 5th paragraph: + * "IRC messages are always lines of characters terminated + * with a CR-LF (Carriage Return - Line Feed) pair [...]". */ delta = 2; - ptr = strstr( array_start(&My_Connections[Idx].rbuf), "\r\n" ); + ptr = strstr(array_start(&My_Connections[Idx].rbuf), "\r\n"); #ifndef STRICT_RFC /* Check for non-RFC-compliant request (only CR or LF)? @@ -1284,58 +1469,67 @@ Handle_Buffer( CONN_ID Idx ) } #endif - if( ! ptr ) - break; + if (!ptr) + return; - /* End of request found */ + /* Complete (=line terminated) request found, handle it! */ *ptr = '\0'; - len = ( ptr - (char*) array_start(&My_Connections[Idx].rbuf)) + delta; + len = ptr - (char *)array_start(&My_Connections[Idx].rbuf) + delta; - if( len > ( COMMAND_LEN - 1 )) { - /* Request must not exceed 512 chars (incl. CR+LF!), see - * RFC 2812. Disconnect Client if this happens. */ - Log( LOG_ERR, "Request too long (connection %d): %d bytes (max. %d expected)!", - Idx, array_bytes(&My_Connections[Idx].rbuf), COMMAND_LEN - 1 ); - Conn_Close( Idx, NULL, "Request too long", true ); - return false; + if (len > (COMMAND_LEN - 1)) { + /* Request must not exceed 512 chars (incl. CR+LF!), + * see RFC 2812. Disconnect Client if this happens. */ + Log(LOG_ERR, + "Request too long (connection %d): %d bytes (max. %d expected)!", + Idx, array_bytes(&My_Connections[Idx].rbuf), + COMMAND_LEN - 1); + Conn_Close(Idx, NULL, "Request too long", true); + return; } - if (len <= 2) { /* request was empty (only '\r\n') */ - array_moveleft(&My_Connections[Idx].rbuf, 1, delta); /* delta is either 1 or 2 */ - break; + if (len <= delta) { + /* Request is empty (only '\r\n', '\r' or '\n'); + * delta is 2 ('\r\n') or 1 ('\r' or '\n'), see above */ + array_moveleft(&My_Connections[Idx].rbuf, 1, len); + return; } + #ifdef ZLIB /* remember if stream is already compressed */ old_z = My_Connections[Idx].options & CONN_ZIP; #endif My_Connections[Idx].msg_in++; - if (!Parse_Request(Idx, (char*)array_start(&My_Connections[Idx].rbuf) )) - return false; - - result = true; + if (!Parse_Request + (Idx, (char *)array_start(&My_Connections[Idx].rbuf))) + return; array_moveleft(&My_Connections[Idx].rbuf, 1, len); LogDebug("Connection %d: %d bytes left in read buffer.", - Idx, array_bytes(&My_Connections[Idx].rbuf)); + Idx, array_bytes(&My_Connections[Idx].rbuf)); #ifdef ZLIB - if(( ! old_z ) && ( My_Connections[Idx].options & CONN_ZIP ) && - ( array_bytes(&My_Connections[Idx].rbuf) > 0 )) - { - /* The last Command activated Socket-Compression. - * Data that was read after that needs to be copied to Unzip-buf - * for decompression */ - if (!array_copy( &My_Connections[Idx].zip.rbuf, &My_Connections[Idx].rbuf )) - return false; + if ((!old_z) && (My_Connections[Idx].options & CONN_ZIP) && + (array_bytes(&My_Connections[Idx].rbuf) > 0)) { + /* The last command activated socket compression. + * Data that was read after that needs to be copied + * to the unzip buffer for decompression: */ + if (!array_copy + (&My_Connections[Idx].zip.rbuf, + &My_Connections[Idx].rbuf)) { + Conn_Close(Idx, NULL, + "Can't allocate memory [Handle_Buffer]", + true); + return; + } array_trunc(&My_Connections[Idx].rbuf); - LogDebug("Moved already received data (%u bytes) to uncompression buffer.", - array_bytes(&My_Connections[Idx].zip.rbuf)); + LogDebug + ("Moved already received data (%u bytes) to uncompression buffer.", + array_bytes(&My_Connections[Idx].zip.rbuf)); } -#endif /* ZLIB */ +#endif } - return result; } /* Handle_Buffer */ @@ -1519,9 +1713,19 @@ New_Server( int Server , ng_ipaddr_t *dest) Init_Conn_Struct( new_sock ); Conf_Server[Server].conn_id = NONE; } - - LogDebug("Registered new connection %d on socket %d.", - new_sock, My_Connections[new_sock].sock ); +#ifdef SSL_SUPPORT + if (Conf_Server[Server].SSLConnect && !ConnSSL_PrepareConnect( &My_Connections[new_sock], + &Conf_Server[Server] )) + { + Log(LOG_ALERT, "Could not initialize SSL for outgoing connection"); + Conn_Close( new_sock, "Could not initialize SSL for outgoing connection", NULL, false ); + Init_Conn_Struct( new_sock ); + Conf_Server[Server].conn_id = NONE; + } +#endif + NumConnections++; + LogDebug("Registered new connection %d on socket %d (%ld in total).", + new_sock, My_Connections[new_sock].sock, NumConnections); Conn_OPTION_ADD( &My_Connections[new_sock], CONN_ISCONNECTING ); } /* New_Server */ @@ -1620,8 +1824,8 @@ cb_Connect_to_Server(int fd, UNUSED short events) len -= sizeof(ng_ipaddr_t); if (len > sizeof(&Conf_Server[i].dst_addr)) { len = sizeof(&Conf_Server[i].dst_addr); - Log(LOG_NOTICE, "Notice: Resolver returned more IP Addresses for host than we can handle," - " additional addresses dropped"); + Log(LOG_NOTICE, + "Notice: Resolver returned more IP Addresses for host than we can handle, additional addresses dropped."); } memcpy(&Conf_Server[i].dst_addr, &dest_addrs[1], len); } @@ -1741,4 +1945,19 @@ Conn_GetClient( CONN_ID Idx ) return c ? c->client : NULL; } +#ifdef SSL_SUPPORT +/* we cannot access My_Connections in irc-info.c */ +GLOBAL bool +Conn_GetCipherInfo(CONN_ID Idx, char *buf, size_t len) +{ + return ConnSSL_GetCipherInfo(&My_Connections[Idx], buf, len); +} + + +GLOBAL bool +Conn_UsesSSL(CONN_ID Idx) +{ + return Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL); +} +#endif /* -eof- */ diff --git a/src/ngircd/conn.h b/src/ngircd/conn.h index 3bb76ab..08f6dde 100644 --- a/src/ngircd/conn.h +++ b/src/ngircd/conn.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 by Alexander Barton (alex@barton.de) * * 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 @@ -8,8 +8,6 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: conn.h,v 1.46 2008/02/26 22:04:17 fw Exp $ - * * Connection management (header) */ @@ -23,12 +21,19 @@ #define CONN_ISCLOSING 1 /* Conn_Close() already called */ #define CONN_ISCONNECTING 2 /* connect() in progress */ - +#define CONN_RFC1459 4 /* RFC 1459 compatibility mode */ #ifdef ZLIB -#define CONN_ZIP 4 /* zlib compressed link */ +#define CONN_ZIP 8 /* zlib compressed link */ #endif +#include "conf-ssl.h" +#ifdef SSL_SUPPORT +#define CONN_SSL_CONNECT 8 /* wait for ssl connect to finish */ +#define CONN_SSL 16 /* this connection is SSL encrypted */ +#define CONN_SSL_WANT_WRITE 32 /* SSL/TLS library needs to write protocol data */ +#define CONN_SSL_WANT_READ 64 /* SSL/TLS library needs to read protocol data */ +#endif typedef int CONN_ID; #include "client.h" @@ -74,6 +79,9 @@ typedef struct _Connection #ifdef ZLIB ZIPDATA zip; /* Compression information */ #endif /* ZLIB */ +#ifdef SSL_SUPPORT + struct ConnSSL_State ssl_state; /* SSL/GNUTLS state information */ +#endif } CONNECTION; GLOBAL CONNECTION *My_Connections; @@ -98,6 +106,12 @@ GLOBAL void Conn_Close PARAMS(( CONN_ID Idx, char *LogMsg, char *FwdMsg, bool In GLOBAL void Conn_SyncServerStruct PARAMS(( void )); GLOBAL CLIENT* Conn_GetClient PARAMS((CONN_ID i)); +#ifdef SSL_SUPPORT +GLOBAL bool Conn_GetCipherInfo PARAMS((CONN_ID Idx, char *buf, size_t len)); +GLOBAL bool Conn_UsesSSL PARAMS((CONN_ID Idx)); +#else +static inline bool Conn_UsesSSL(UNUSED CONN_ID Idx) { return false; } +#endif #endif /* -eof- */ diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h index cccf48b..7abe641 100644 --- a/src/ngircd/defines.h +++ b/src/ngircd/defines.h @@ -82,7 +82,7 @@ protocol, see doc/Protocol.txt */ #ifdef IRCPLUS -# define IRCPLUSFLAGS "CHL" /* Standard IRC+ flags */ +# define IRCPLUSFLAGS "CHLS" /* Standard IRC+ flags */ #endif #define STARTUP_DELAY 1 /* Delay outgoing connections n seconds diff --git a/src/ngircd/irc-channel.c b/src/ngircd/irc-channel.c index c678cee..27414d3 100644 --- a/src/ngircd/irc-channel.c +++ b/src/ngircd/irc-channel.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -14,8 +14,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: irc-channel.c,v 1.45 2008/02/24 18:57:38 fw Exp $"; - #include "imp.h" #include #include @@ -26,6 +24,7 @@ static char UNUSED id[] = "$Id: irc-channel.c,v 1.45 2008/02/24 18:57:38 fw Exp #include "conn.h" #include "client.h" #include "channel.h" +#include "conn-func.h" #include "lists.h" #include "log.h" #include "match.h" @@ -70,6 +69,10 @@ join_allowed(CLIENT *Client, CLIENT *target, CHANNEL *chan, bool is_invited, is_banned; const char *channel_modes; + /* Allow IRC operators to overwrite channel limits */ + if (strchr(Client_Modes(Client), 'o')) + return true; + is_banned = Lists_Check(Channel_GetListBans(chan), target); is_invited = Lists_Check(Channel_GetListInvites(chan), target); @@ -118,26 +121,59 @@ join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags) static void +cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data) +{ + CONN_ID conn; + char str[COMMAND_LEN], *ptr = NULL; + + strlcpy(str, (char *)Data, sizeof(str)); + conn = Client_Conn(To); + + if (Conn_Options(conn) & CONN_RFC1459) { + /* RFC 1459 compatibility mode, appended modes are NOT + * supported, so strip them off! */ + ptr = strchr(str, 0x7); + if (ptr) + *ptr++ = '\0'; + } + + IRC_WriteStrClientPrefix(To, Prefix, "JOIN %s", str); + if (ptr && *ptr) + IRC_WriteStrClientPrefix(To, Prefix, "MODE %s +%s %s", str, ptr, + Client_ID(Prefix)); +} /* cb_join_forward */ + + +static void join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan, const char *channame) { - char modes[8]; + char modes[CHANNEL_MODE_LEN], str[COMMAND_LEN]; strlcpy(&modes[1], Channel_UserModes(chan, target), sizeof(modes) - 1); - if (modes[1]) modes[0] = 0x7; else modes[0] = '\0'; - /* forward to other servers */ - IRC_WriteStrServersPrefix(Client, target, "JOIN :%s%s", channame, modes); + + /* forward to other servers (if it is not a local channel) */ + if (!Channel_IsLocal(chan)) { + snprintf(str, sizeof(str), "%s%s", channame, modes); + IRC_WriteStrServersPrefixFlag_CB(Client, target, '\0', + cb_join_forward, str); + } /* tell users in this channel about the new client */ - IRC_WriteStrChannelPrefix(Client, chan, target, false, "JOIN :%s", channame); - if (modes[1]) - IRC_WriteStrChannelPrefix(Client, chan, target, false, "MODE %s +%s %s", - channame, &modes[1], Client_ID(target)); -} + IRC_WriteStrChannelPrefix(Client, chan, target, false, + "JOIN :%s", channame); + + /* syncronize channel modes */ + if (modes[1]) { + IRC_WriteStrChannelPrefix(Client, chan, target, false, + "MODE %s +%s %s", channame, + &modes[1], Client_ID(target)); + } +} /* join_forward */ static bool @@ -177,7 +213,7 @@ join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan, GLOBAL bool IRC_JOIN( CLIENT *Client, REQUEST *Req ) { - char *channame, *channame_ptr, *key, *key_ptr, *flags; + char *channame, *key = NULL, *flags, *lastkey = NULL, *lastchan = NULL; CLIENT *target; CHANNEL *chan; @@ -203,16 +239,16 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) return part_from_all_channels(Client, target); /* Are channel keys given? */ - if (Req->argc > 1) { - key = Req->argv[1]; - key_ptr = strchr(key, ','); - if (key_ptr) *key_ptr = '\0'; - } else { - key = key_ptr = NULL; - } + if (Req->argc > 1) + key = strtok_r(Req->argv[1], ",", &lastkey); + channame = Req->argv[0]; - channame_ptr = strchr(channame, ','); - if (channame_ptr) *channame_ptr = '\0'; + channame = strtok_r(channame, ",", &lastchan); + + /* Make sure that "channame" is not the empty string ("JOIN :") */ + if (! channame) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); while (channame) { flags = NULL; @@ -239,11 +275,19 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) if ((Conf_MaxJoins > 0) && (Channel_CountForUser(Client) >= Conf_MaxJoins)) return IRC_WriteStrClient(Client, ERR_TOOMANYCHANNELS_MSG, Client_ID(Client), channame); - if (!chan) /* New Channel: first user will be channel operator */ - flags = "o"; - else + if (!chan) { + /* + * New Channel: first user will be channel operator + * unless this is a modeless channel. + */ + if (*channame != '+') + flags = "o"; + } else if (!join_allowed(Client, target, chan, channame, key)) break; + + /* Local client: update idle time */ + Conn_UpdateIdle(Client_Conn(Client)); } else { /* Remote server: we don't need to know whether the * client is invited or not, but we have to make sure @@ -257,8 +301,14 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) if (!Channel_Join(target, channame)) break; - if (!chan) /* channel is new; it has been created above */ + if (!chan) { /* channel is new; it has been created above */ chan = Channel_Search(channame); + assert(chan != NULL); + if (*channame == '+') { /* modeless channel... */ + Channel_ModeAdd(chan, 't'); /* /TOPIC not allowed */ + Channel_ModeAdd(chan, 'n'); /* no external msgs */ + } + } assert(chan != NULL); join_set_channelmodes(chan, target, flags); @@ -269,18 +319,9 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req ) break; /* write error */ /* next channel? */ - channame = channame_ptr; - if (channame) { - channame++; - channame_ptr = strchr(channame, ','); - if (channame_ptr) *channame_ptr = '\0'; - - if (key_ptr) { - key = ++key_ptr; - key_ptr = strchr(key, ','); - if (key_ptr) *key_ptr = '\0'; - } - } + channame = strtok_r(NULL, ",", &lastchan); + if (channame && key) + key = strtok_r(NULL, ",", &lastkey); } return CONNECTED; } /* IRC_JOIN */ @@ -313,11 +354,22 @@ IRC_PART(CLIENT * Client, REQUEST * Req) /* Loop over all the given channel names */ chan = strtok(Req->argv[0], ","); + + /* Make sure that "chan" is not the empty string ("PART :") */ + if (! chan) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + while (chan) { Channel_Part(target, Client, chan, Req->argc > 1 ? Req->argv[1] : Client_ID(target)); chan = strtok(NULL, ","); } + + /* Update idle time, if local client */ + if (Client_Conn(Client) > NONE) + Conn_UpdateIdle(Client_Conn(Client)); + return CONNECTED; } /* IRC_PART */ @@ -333,8 +385,8 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) assert( Client != NULL ); assert( Req != NULL ); - /* Falsche Anzahl Parameter? */ - if(( Req->argc < 1 ) || ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + if ((Req->argc < 1) || (Req->argc > 2)) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix ); else from = Client; @@ -375,16 +427,22 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req ) /* Set new topic */ Channel_SetTopic(chan, from, Req->argv[1]); - Log(LOG_DEBUG, "User \"%s\" set topic on \"%s\": %s", - Client_Mask(from), Channel_Name(chan), - Req->argv[1][0] ? Req->argv[1] : ""); - - /* im Channel bekannt machen und an Server weiterleiten */ - IRC_WriteStrServersPrefix( Client, from, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); - IRC_WriteStrChannelPrefix( Client, chan, from, false, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); - - if( Client_Type( Client ) == CLIENT_USER ) return IRC_WriteStrClientPrefix( Client, Client, "TOPIC %s :%s", Req->argv[0], Req->argv[1] ); - else return CONNECTED; + LogDebug("%s \"%s\" set topic on \"%s\": %s", + Client_TypeText(from), Client_Mask(from), Channel_Name(chan), + Req->argv[1][0] ? Req->argv[1] : ""); + + /* Update channel and forward new topic to other servers */ + if (!Channel_IsLocal(chan)) + IRC_WriteStrServersPrefix(Client, from, "TOPIC %s :%s", + Req->argv[0], Req->argv[1]); + IRC_WriteStrChannelPrefix(Client, chan, from, false, "TOPIC %s :%s", + Req->argv[0], Req->argv[1]); + + if (Client_Type(Client) == CLIENT_USER) + return IRC_WriteStrClientPrefix(Client, Client, "TOPIC %s :%s", + Req->argv[0], Req->argv[1]); + else + return CONNECTED; } /* IRC_TOPIC */ @@ -439,7 +497,7 @@ IRC_LIST( CLIENT *Client, REQUEST *Req ) Req->argv[1] ); } } - + while( pattern ) { /* Loop through all the channels */ @@ -463,14 +521,14 @@ IRC_LIST( CLIENT *Client, REQUEST *Req ) } chan = Channel_Next( chan ); } - + /* Get next name ... */ if( Req->argc > 0 ) pattern = strtok( NULL, "," ); else pattern = NULL; } - + return IRC_WriteStrClient( from, RPL_LISTEND_MSG, Client_ID( from )); } /* IRC_LIST */ @@ -539,7 +597,7 @@ IRC_CHANINFO( CLIENT *Client, REQUEST *Req ) } ptr++; } - + /* Inform members of this channel */ IRC_WriteStrChannelPrefix( Client, chan, from, false, "MODE %s +%s%s", Req->argv[0], Channel_Modes( chan ), modes_add ); } diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c index a3661f5..4ac2a47 100644 --- a/src/ngircd/irc-info.c +++ b/src/ngircd/irc-info.c @@ -269,6 +269,44 @@ IRC_LUSERS( CLIENT *Client, REQUEST *Req ) } /* IRC_LUSERS */ +/** + * Handler for the IRC command "SERVLIST". + * List registered services, see RFC 2811, section 3.5.1: the syntax is + * "SERVLIST [ []]". + */ +GLOBAL bool +IRC_SERVLIST(CLIENT *Client, REQUEST *Req) +{ + CLIENT *c; + + assert(Client != NULL); + assert(Req != NULL); + + if (Req->argc > 2) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + + if (Req->argc < 2 || strcmp(Req->argv[1], "0") == 0) { + for (c = Client_First(); c!= NULL; c = Client_Next(c)) { + if (Client_Type(c) != CLIENT_SERVICE) + continue; + if (Req->argc > 0 && !MatchCaseInsensitive(Req->argv[0], + Client_ID(c))) + continue; + if (!IRC_WriteStrClient(Client, RPL_SERVLIST_MSG, + Client_ID(Client), Client_Mask(c), + Client_Mask(Client_Introducer(c)), "*", + 0, Client_Hops(c), Client_Info(c))) + return DISCONNECTED; + } + } + + return IRC_WriteStrClient(Client, RPL_SERVLISTEND_MSG, Client_ID(Client), + Req->argc > 0 ? Req->argv[0] : "*", + Req->argc > 1 ? Req->argv[1] : "0"); +} /* IRC_SERVLIST */ + + GLOBAL bool IRC_MOTD( CLIENT *Client, REQUEST *Req ) { @@ -399,13 +437,12 @@ IRC_NAMES( CLIENT *Client, REQUEST *Req ) static unsigned int -t_diff(time_t *t, const time_t div) +t_diff(time_t *t, const time_t d) { time_t diff, remain; - diff = *t / div; - - remain = diff * div; + diff = *t / d; + remain = diff * d; *t -= remain; return diff; @@ -746,20 +783,6 @@ IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps) } /* IRC_Send_WHO */ - -static bool -MatchCaseInsensitive(const char *pattern, const char *searchme) -{ - char haystack[COMMAND_LEN]; - - strlcpy(haystack, searchme, sizeof(haystack)); - - ngt_LowerStr(haystack); - - return Match(pattern, haystack); -} - - GLOBAL bool IRC_WHO( CLIENT *Client, REQUEST *Req ) { @@ -927,7 +950,14 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req ) cl2chan = Channel_NextChannelOf( c, cl2chan ); /* Secret channel? */ - if( strchr( Channel_Modes( chan ), 's' ) && ! Channel_IsMemberOf( chan, Client )) continue; + if (strchr(Channel_Modes(chan), 's') + && !Channel_IsMemberOf(chan, Client)) + continue; + + /* Local channel and request is not from a user? */ + if (Client_Type(Client) == CLIENT_SERVER + && Channel_IsLocal(chan)) + continue; /* Concatenate channel names */ if( str[strlen( str ) - 1] != ':' ) strlcat( str, " ", sizeof( str )); @@ -974,6 +1004,22 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req ) } /* IRC_WHOIS */ +static bool +WHOWAS_EntryWrite(CLIENT *prefix, WHOWAS *entry) +{ + char t_str[60]; + + (void)strftime(t_str, sizeof(t_str), "%a %b %d %H:%M:%S %Y", + localtime(&entry->time)); + + if (!IRC_WriteStrClient(prefix, RPL_WHOWASUSER_MSG, Client_ID(prefix), + entry->id, entry->user, entry->host, entry->info)) + return DISCONNECTED; + + return IRC_WriteStrClient(prefix, RPL_WHOISSERVER_MSG, Client_ID(prefix), + entry->id, entry->server, t_str); +} + /** * IRC "WHOWAS" function. * This function implements the IRC command "WHOWHAS". It handles local @@ -984,40 +1030,41 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req ) { CLIENT *target, *prefix; WHOWAS *whowas; - int max, last, count, i; - char t_str[60]; - + char tok_buf[COMMAND_LEN]; + int max, last, count, i, nc; + const char *nick; + assert( Client != NULL ); assert( Req != NULL ); /* Wrong number of parameters? */ - if(( Req->argc < 1 ) || ( Req->argc > 3 )) - return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, - Client_ID( Client ), Req->command ); + if (Req->argc > 3) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + if (Req->argc < 1) + return IRC_WriteStrClient(Client, ERR_NONICKNAMEGIVEN_MSG, Client_ID(Client)); - /* Search taget */ - if( Req->argc == 3 ) - target = Client_Search( Req->argv[2] ); + /* Search target */ + if (Req->argc == 3) + target = Client_Search(Req->argv[2]); else - target = Client_ThisServer( ); + target = Client_ThisServer(); /* Get prefix */ - if( Client_Type( Client ) == CLIENT_SERVER ) - prefix = Client_Search( Req->prefix ); + if (Client_Type(Client) == CLIENT_SERVER) + prefix = Client_Search(Req->prefix); else prefix = Client; - if( ! prefix ) - return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, - Client_ID( Client ), Req->prefix ); + if (!prefix) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->prefix); /* Forward to other server? */ - if( target != Client_ThisServer( )) - { - if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) - return IRC_WriteStrClient( prefix, ERR_NOSUCHSERVER_MSG, - Client_ID( prefix ), - Req->argv[2] ); + if (target != Client_ThisServer()) { + if (!target || (Client_Type(target) != CLIENT_SERVER)) + return IRC_WriteStrClient(prefix, ERR_NOSUCHSERVER_MSG, + Client_ID(prefix), Req->argv[2]); /* Forward */ IRC_WriteStrClientPrefix( target, prefix, "WHOWAS %s %s %s", @@ -1025,58 +1072,52 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req ) Req->argv[2] ); return CONNECTED; } - + whowas = Client_GetWhowas( ); last = Client_GetLastWhowasIndex( ); - if( last < 0 ) last = 0; - - if( Req->argc > 1 ) - { - max = atoi( Req->argv[1] ); - if( max < 1 ) max = MAX_WHOWAS; + if (last < 0) + last = 0; + + max = DEFAULT_WHOWAS; + if (Req->argc > 1) { + max = atoi(Req->argv[1]); + if (max < 1) + max = MAX_WHOWAS; } - else - max = DEFAULT_WHOWAS; - - i = last; - count = 0; - do - { - /* Used entry? */ - if( whowas[i].time > 0 && - strcasecmp( Req->argv[0], whowas[i].id ) == 0 ) - { - (void)strftime( t_str, sizeof(t_str), - "%a %b %d %H:%M:%S %Y", - localtime( &whowas[i].time )); - - if( ! IRC_WriteStrClient( prefix, RPL_WHOWASUSER_MSG, - Client_ID( prefix ), - whowas[i].id, - whowas[i].user, - whowas[i].host, - whowas[i].info )) - return DISCONNECTED; - - if( ! IRC_WriteStrClient( prefix, RPL_WHOISSERVER_MSG, - Client_ID( prefix ), - whowas[i].id, - whowas[i].server, t_str )) - return DISCONNECTED; - - count++; - if( count >= max ) break; - } - - /* previos entry */ - i--; - /* "underflow", wrap around */ - if( i < 0 ) i = MAX_WHOWAS - 1; - } while( i != last ); - - return IRC_WriteStrClient( prefix, RPL_ENDOFWHOWAS_MSG, - Client_ID( prefix ), Req->argv[0] ); + /* + * Break up the nick argument into a list of nicks, if applicable + * Can't modify Req->argv[0] because we need it for RPL_ENDOFWHOWAS_MSG. + */ + strlcpy(tok_buf, Req->argv[0], sizeof(tok_buf)); + nick = strtok(tok_buf, ","); + + for (i=last, count=0; nick != NULL ; nick = strtok(NULL, ",")) { + nc = 0; + do { + /* Used entry? */ + if (whowas[i].time > 0 && strcasecmp(nick, whowas[i].id) == 0) { + if (!WHOWAS_EntryWrite(prefix, &whowas[i])) + return DISCONNECTED; + nc++; + count++; + } + /* previous entry */ + i--; + + /* "underflow", wrap around */ + if (i < 0) + i = MAX_WHOWAS - 1; + + if (nc && count >= max) + break; + } while (i != last); + + if (nc == 0 && !IRC_WriteStrClient(prefix, ERR_WASNOSUCHNICK_MSG, + Client_ID(prefix), nick)) + return DISCONNECTED; + } + return IRC_WriteStrClient(prefix, RPL_ENDOFWHOWAS_MSG, Client_ID(prefix), Req->argv[0]); } /* IRC_WHOWAS */ @@ -1151,6 +1192,23 @@ Show_MOTD_End(CLIENT *Client) return IRC_WriteStrClient( Client, RPL_ENDOFMOTD_MSG, Client_ID( Client )); } +#ifdef SSL_SUPPORT +static bool Show_MOTD_SSLInfo(CLIENT *Client) +{ + bool ret = true; + char buf[COMMAND_LEN] = "Connected using Cipher "; + + if (!Conn_GetCipherInfo(Client_Conn(Client), buf + 23, sizeof buf - 23)) + return true; + + if (!Show_MOTD_Sendline(Client, buf)) + ret = false; + + return ret; +} +#else +static inline bool Show_MOTD_SSLInfo(UNUSED CLIENT *c) { return true; } +#endif GLOBAL bool IRC_Show_MOTD( CLIENT *Client ) @@ -1165,13 +1223,17 @@ IRC_Show_MOTD( CLIENT *Client ) return DISCONNECTED; if (!Show_MOTD_Sendline(Client, Conf_MotdPhrase)) return DISCONNECTED; - - return Show_MOTD_End(Client); + goto out; } fd = fopen( Conf_MotdFile, "r" ); if( ! fd ) { Log( LOG_WARNING, "Can't read MOTD file \"%s\": %s", Conf_MotdFile, strerror( errno )); + if (Conn_UsesSSL(Client_Conn(Client))) { + if (!Show_MOTD_Start(Client)) + return DISCONNECTED; + goto out; + } return IRC_WriteStrClient( Client, ERR_NOMOTD_MSG, Client_ID( Client ) ); } @@ -1189,6 +1251,9 @@ IRC_Show_MOTD( CLIENT *Client ) } } fclose(fd); +out: + if (!Show_MOTD_SSLInfo(Client)) + return DISCONNECTED; return Show_MOTD_End(Client); } /* IRC_Show_MOTD */ diff --git a/src/ngircd/irc-info.h b/src/ngircd/irc-info.h index faef75a..fbebacc 100644 --- a/src/ngircd/irc-info.h +++ b/src/ngircd/irc-info.h @@ -30,6 +30,7 @@ GLOBAL bool IRC_SUMMON PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_TIME PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_USERHOST PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_USERS PARAMS(( CLIENT *Client, REQUEST *Req )); +GLOBAL bool IRC_SERVLIST PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_VERSION PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_WHO PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_WHOIS PARAMS(( CLIENT *Client, REQUEST *Req )); diff --git a/src/ngircd/irc-login.c b/src/ngircd/irc-login.c index dd43619..943612e 100644 --- a/src/ngircd/irc-login.c +++ b/src/ngircd/irc-login.c @@ -40,6 +40,9 @@ static bool Hello_User PARAMS(( CLIENT *Client )); static void Kill_Nick PARAMS(( char *Nick, char *Reason )); +static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client, int Type)); +static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix, + void *i)); /** @@ -59,12 +62,12 @@ IRC_PASS( CLIENT *Client, REQUEST *Req ) if (Client_Conn(Client) <= NONE) return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG, Client_ID(Client), Req->command); - + if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) { /* Not yet registered "unknown" connection, PASS with one * argument: either a regular client, service, or server * using the old RFC 1459 section 4.1.1 syntax. */ - LogDebug("Connection %d: got PASS command ...", + LogDebug("Connection %d: got PASS command (RFC 1459) ...", Client_Conn(Client)); } else if ((Client_Type(Client) == CLIENT_UNKNOWN || Client_Type(Client) == CLIENT_UNKNOWNSERVER) && @@ -72,7 +75,7 @@ IRC_PASS( CLIENT *Client, REQUEST *Req ) /* Not yet registered "unknown" connection or outgoing server * link, PASS with three or four argument: server using the * RFC 2813 section 4.1.1 syntax. */ - LogDebug("Connection %d: got PASS command (new server link) ...", + LogDebug("Connection %d: got PASS command (RFC 2813, new server link) ...", Client_Conn(Client)); } else if (Client_Type(Client) == CLIENT_UNKNOWN || Client_Type(Client) == CLIENT_UNKNOWNSERVER) { @@ -86,7 +89,6 @@ IRC_PASS( CLIENT *Client, REQUEST *Req ) } Client_SetPassword(Client, Req->argv[0]); - Client_SetType(Client, CLIENT_GOTPASS); /* Protocol version */ if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) { @@ -99,18 +101,22 @@ IRC_PASS( CLIENT *Client, REQUEST *Req ) protolow = atoi(&Req->argv[1][2]); Req->argv[1][2] = '\0'; protohigh = atoi(Req->argv[1]); - + Req->argv[1][2] = c2; Req->argv[1][4] = c4; - } else + + Client_SetType(Client, CLIENT_GOTPASS_2813); + } else { protohigh = protolow = 0; + Client_SetType(Client, CLIENT_GOTPASS); + } /* Protocol type, see doc/Protocol.txt */ if (Req->argc >= 2 && strlen(Req->argv[1]) > 4) type = &Req->argv[1][4]; else type = NULL; - + /* Protocol flags/options */ if (Req->argc >= 4) orig_flags = Req->argv[3]; @@ -168,27 +174,23 @@ GLOBAL bool IRC_NICK( CLIENT *Client, REQUEST *Req ) { CLIENT *intr_c, *target, *c; - char *modes; + char *nick, *user, *hostname, *modes, *info; + int token, hops; assert( Client != NULL ); assert( Req != NULL ); -#ifndef STRICT_RFC /* Some IRC clients, for example BitchX, send the NICK and USER * commands in the wrong order ... */ - if( Client_Type( Client ) == CLIENT_UNKNOWN - || Client_Type( Client ) == CLIENT_GOTPASS - || Client_Type( Client ) == CLIENT_GOTNICK - || Client_Type( Client ) == CLIENT_GOTUSER - || Client_Type( Client ) == CLIENT_USER - || ( Client_Type( Client ) == CLIENT_SERVER && Req->argc == 1 )) -#else - if( Client_Type( Client ) == CLIENT_UNKNOWN - || Client_Type( Client ) == CLIENT_GOTPASS - || Client_Type( Client ) == CLIENT_GOTNICK - || Client_Type( Client ) == CLIENT_USER - || ( Client_Type( Client ) == CLIENT_SERVER && Req->argc == 1 )) + if(Client_Type(Client) == CLIENT_UNKNOWN + || Client_Type(Client) == CLIENT_GOTPASS + || Client_Type(Client) == CLIENT_GOTNICK +#ifndef STRICT_RFC + || Client_Type(Client) == CLIENT_GOTUSER #endif + || Client_Type(Client) == CLIENT_USER + || Client_Type(Client) == CLIENT_SERVICE + || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1)) { /* User registration or change of nickname */ @@ -236,9 +238,9 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) return CONNECTED; } - if(( Client_Type( target ) != CLIENT_USER ) - && ( Client_Type( target ) != CLIENT_SERVER )) - { + if (Client_Type(target) != CLIENT_USER && + Client_Type(target) != CLIENT_SERVICE && + Client_Type(target) != CLIENT_SERVER) { /* New client */ Log( LOG_DEBUG, "Connection %d: got valid NICK command ...", Client_Conn( Client )); @@ -252,25 +254,22 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) return Hello_User( Client ); else Client_SetType( Client, CLIENT_GOTNICK ); - } - else - { + } else { /* Nickname change */ - if( Client_Conn( target ) > NONE ) - { + if (Client_Conn(target) > NONE) { /* Local client */ - Log( LOG_INFO, - "User \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".", - Client_Mask( target ), Client_Conn( target ), - Client_ID( target ), Req->argv[0] ); - } - else - { + Log(LOG_INFO, + "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".", + Client_TypeText(target), Client_Mask(target), + Client_Conn(target), Client_ID(target), + Req->argv[0]); + Conn_UpdateIdle(Client_Conn(target)); + } else { /* Remote client */ - Log( LOG_DEBUG, - "User \"%s\" changed nick: \"%s\" -> \"%s\".", - Client_Mask( target ), Client_ID( target ), - Req->argv[0] ); + LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".", + Client_TypeText(target), + Client_Mask(target), Client_ID(target), + Req->argv[0]); } /* Inform all users and servers (which have to know) @@ -294,18 +293,38 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) } return CONNECTED; - } - else if( Client_Type( Client ) == CLIENT_SERVER ) - { - /* Server introduces new client */ - - /* Falsche Anzahl Parameter? */ - if( Req->argc != 7 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + } else if(Client_Type(Client) == CLIENT_SERVER || + Client_Type(Client) == CLIENT_SERVICE) { + /* Server or service introduces new client */ + + /* Bad number of parameters? */ + if (Req->argc != 2 && Req->argc != 7) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + + if (Req->argc >= 7) { + /* RFC 2813 compatible syntax */ + nick = Req->argv[0]; + hops = atoi(Req->argv[1]); + user = Req->argv[2]; + hostname = Req->argv[3]; + token = atoi(Req->argv[4]); + modes = Req->argv[5] + 1; + info = Req->argv[6]; + } else { + /* RFC 1459 compatible syntax */ + nick = Req->argv[0]; + hops = 1; + user = Req->argv[0]; + hostname = Client_ID(Client); + token = atoi(Req->argv[1]); + modes = ""; + info = Req->argv[0]; + } /* Nick ueberpruefen */ - c = Client_Search( Req->argv[0] ); - if( c ) - { + c = Client_Search(nick); + if(c) { /* Der neue Nick ist auf diesem Server bereits registriert: * sowohl der neue, als auch der alte Client muessen nun * disconnectiert werden. */ @@ -315,7 +334,7 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) } /* Server, zu dem der Client connectiert ist, suchen */ - intr_c = Client_GetFromToken( Client, atoi( Req->argv[4] )); + intr_c = Client_GetFromToken(Client, token); if( ! intr_c ) { Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] ); @@ -324,7 +343,8 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) } /* Neue Client-Struktur anlegen */ - c = Client_NewRemoteUser( intr_c, Req->argv[0], atoi( Req->argv[1] ), Req->argv[2], Req->argv[3], atoi( Req->argv[4] ), Req->argv[5] + 1, Req->argv[6], true); + c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname, + token, modes, info, true); if( ! c ) { /* Eine neue Client-Struktur konnte nicht angelegt werden. @@ -335,12 +355,16 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) return CONNECTED; } - modes = Client_Modes( c ); - if( *modes ) Log( LOG_DEBUG, "User \"%s\" (+%s) registered (via %s, on %s, %d hop%s).", Client_Mask( c ), modes, Client_ID( Client ), Client_ID( intr_c ), Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" ); - else Log( LOG_DEBUG, "User \"%s\" registered (via %s, on %s, %d hop%s).", Client_Mask( c ), Client_ID( Client ), Client_ID( intr_c ), Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" ); - - /* Andere Server, ausser dem Introducer, informieren */ - IRC_WriteStrServersPrefix( Client, Client, "NICK %s %d %s %s %d %s :%s", Req->argv[0], atoi( Req->argv[1] ) + 1, Req->argv[2], Req->argv[3], Client_MyToken( intr_c ), Req->argv[5], Req->argv[6] ); + /* RFC 2813: client is now fully registered, inform all the + * other servers about the new user. + * RFC 1459: announce the new client only after receiving the + * USER command, first we need more information! */ + if (Req->argc < 7) { + LogDebug("Client \"%s\" is beeing registered (RFC 1459) ...", + Client_Mask(c)); + Client_SetType(c, CLIENT_GOTNICK); + } else + Introduce_Client(Client, c, CLIENT_USER); return CONNECTED; } @@ -348,60 +372,204 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) } /* IRC_NICK */ +/** + * Handler for the IRC command "USER". + */ GLOBAL bool -IRC_USER( CLIENT *Client, REQUEST *Req ) +IRC_USER(CLIENT * Client, REQUEST * Req) { + CLIENT *c; #ifdef IDENTAUTH char *ptr; #endif - assert( Client != NULL ); - assert( Req != NULL ); + assert(Client != NULL); + assert(Req != NULL); + if (Client_Type(Client) == CLIENT_GOTNICK || #ifndef STRICT_RFC - if( Client_Type( Client ) == CLIENT_GOTNICK || Client_Type( Client ) == CLIENT_GOTPASS || Client_Type( Client ) == CLIENT_UNKNOWN ) -#else - if( Client_Type( Client ) == CLIENT_GOTNICK || Client_Type( Client ) == CLIENT_GOTPASS ) + Client_Type(Client) == CLIENT_UNKNOWN || #endif + Client_Type(Client) == CLIENT_GOTPASS) { - /* Wrong number of parameters? */ - if( Req->argc != 4 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + /* New connection */ + if (Req->argc != 4) + return IRC_WriteStrClient(Client, + ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), + Req->command); /* User name */ #ifdef IDENTAUTH - ptr = Client_User( Client ); - if( ! ptr || ! *ptr || *ptr == '~' ) Client_SetUser( Client, Req->argv[0], false ); + ptr = Client_User(Client); + if (!ptr || !*ptr || *ptr == '~') + Client_SetUser(Client, Req->argv[0], false); #else - Client_SetUser( Client, Req->argv[0], false ); + Client_SetUser(Client, Req->argv[0], false); #endif - /* "Real name" or user info text: Don't set it to the empty string, the original ircd - * can't deal with such "real names" (e. g. "USER user * * :") ... */ - if( *Req->argv[3] ) Client_SetInfo( Client, Req->argv[3] ); - else Client_SetInfo( Client, "-" ); + /* "Real name" or user info text: Don't set it to the empty + * string, the original ircd can't deal with such "real names" + * (e. g. "USER user * * :") ... */ + if (*Req->argv[3]) + Client_SetInfo(Client, Req->argv[3]); + else + Client_SetInfo(Client, "-"); - Log( LOG_DEBUG, "Connection %d: got valid USER command ...", Client_Conn( Client )); - if( Client_Type( Client ) == CLIENT_GOTNICK ) return Hello_User( Client ); - else Client_SetType( Client, CLIENT_GOTUSER ); + LogDebug("Connection %d: got valid USER command ...", + Client_Conn(Client)); + if (Client_Type(Client) == CLIENT_GOTNICK) + return Hello_User(Client); + else + Client_SetType(Client, CLIENT_GOTUSER); return CONNECTED; + + } else if (Client_Type(Client) == CLIENT_SERVER || + Client_Type(Client) == CLIENT_SERVICE) { + /* Server/service updating an user */ + if (Req->argc != 4) + return IRC_WriteStrClient(Client, + ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), + Req->command); + c = Client_Search(Req->prefix); + if (!c) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), + Req->prefix); + + Client_SetUser(c, Req->argv[0], true); + Client_SetHostname(c, Req->argv[1]); + Client_SetInfo(c, Req->argv[3]); + + LogDebug("Connection %d: got valid USER command for \"%s\".", + Client_Conn(Client), Client_Mask(c)); + + /* RFC 1459 style user registration? + * Introduce client to network: */ + if (Client_Type(c) == CLIENT_GOTNICK) + Introduce_Client(Client, c, CLIENT_USER); + + return CONNECTED; + } else if (Client_Type(Client) == CLIENT_USER) { + /* Already registered connection */ + return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG, + Client_ID(Client)); + } else { + /* Unexpected/invalid connection state? */ + return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG, + Client_ID(Client)); } - else if( Client_Type( Client ) == CLIENT_USER || Client_Type( Client ) == CLIENT_SERVER || Client_Type( Client ) == CLIENT_SERVICE ) - { - return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client )); - } - else return IRC_WriteStrClient( Client, ERR_NOTREGISTERED_MSG, Client_ID( Client )); } /* IRC_USER */ +/** + * Handler for the IRC command "SERVICE". + * This function implements IRC Services registration using the SERVICE command + * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4. + * At the moment ngIRCd doesn't support directly linked services, so this + * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been + * received from a peer server. + */ +GLOBAL bool +IRC_SERVICE(CLIENT *Client, REQUEST *Req) +{ + CLIENT *c, *intr_c; + char *nick, *user, *host, *info, *modes, *ptr; + int token, hops; + + assert(Client != NULL); + assert(Req != NULL); + + if (Client_Type(Client) != CLIENT_GOTPASS && + Client_Type(Client) != CLIENT_SERVER) + return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG, + Client_ID(Client)); + + if (Req->argc != 6) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + + if (Client_Type(Client) != CLIENT_SERVER) + return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG, + Client_ID(Client), Req->argv[0]); + + /* Bad number of parameters? */ + if (Req->argc != 6) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + + nick = Req->argv[0]; + user = NULL; host = NULL; + token = atoi(Req->argv[1]); + hops = atoi(Req->argv[4]); + info = Req->argv[5]; + + /* Validate service name ("nick name") */ + c = Client_Search(nick); + if(c) { + /* Nick name collission: disconnect (KILL) both clients! */ + Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!", + Client_ID(Client), nick); + Kill_Nick(nick, "Nick collision"); + return CONNECTED; + } + + /* Get the server to which the service is connected */ + intr_c = Client_GetFromToken(Client, token); + if (! intr_c) { + Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?", + Client_ID(Client), nick); + Kill_Nick(nick, "Unknown server"); + return CONNECTED; + } + + /* Get user and host name */ + ptr = strchr(nick, '@'); + if (ptr) { + *ptr = '\0'; + host = ++ptr; + } + if (!host) + host = Client_Hostname(intr_c); + ptr = strchr(nick, '!'); + if (ptr) { + *ptr = '\0'; + user = ++ptr; + } + if (!user) + user = nick; + + /* According to RFC 2812/2813 parameter 4 "is currently reserved + * for future usage"; but we use it to transfer the modes and check + * that the first character is a '+' sign and ignore it otherwise. */ + modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : ""; + + c = Client_NewRemoteUser(intr_c, nick, hops, user, host, + token, modes, info, true); + if (! c) { + /* Couldn't create client structure, so KILL the service to + * keep network status consistent ... */ + Log(LOG_ALERT, "Can't create client structure! (on connection %d)", + Client_Conn(Client)); + Kill_Nick(nick, "Server error"); + return CONNECTED; + } + + Introduce_Client(Client, c, CLIENT_SERVICE); + return CONNECTED; +} /* IRC_SERVICE */ + + GLOBAL bool IRC_QUIT( CLIENT *Client, REQUEST *Req ) { CLIENT *target; char quitmsg[LINE_LEN]; - + assert( Client != NULL ); assert( Req != NULL ); - + /* Wrong number of arguments? */ if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); @@ -435,7 +603,7 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req ) /* User, Service, oder noch nicht registriert */ Conn_Close( Client_Conn( Client ), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true); - + return DISCONNECTED; } } /* IRC_QUIT */ @@ -578,20 +746,13 @@ Hello_User(CLIENT * Client) if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) { /* Bad password! */ Log(LOG_ERR, - "User \"%s\" rejected (connection %d): Bad password!", + "Client \"%s\" rejected (connection %d): Bad password!", Client_Mask(Client), Client_Conn(Client)); Conn_Close(Client_Conn(Client), NULL, "Bad password", true); return DISCONNECTED; } - Log(LOG_NOTICE, "User \"%s\" registered (connection %d).", - Client_Mask(Client), Client_Conn(Client)); - - /* Inform other servers */ - IRC_WriteStrServers(NULL, "NICK %s 1 %s %s 1 +%s :%s", - Client_ID(Client), Client_User(Client), - Client_Hostname(Client), Client_Modes(Client), - Client_Info(Client)); + Introduce_Client(NULL, Client, CLIENT_USER); if (!IRC_WriteStrClient (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client))) @@ -615,8 +776,6 @@ Hello_User(CLIENT * Client) if (!IRC_Send_ISUPPORT(Client)) return DISCONNECTED; - Client_SetType(Client, CLIENT_USER); - if (!IRC_Send_LUSERS(Client)) return DISCONNECTED; if (!IRC_Show_MOTD(Client)) @@ -647,4 +806,74 @@ Kill_Nick( char *Nick, char *Reason ) } /* Kill_Nick */ +static void +Introduce_Client(CLIENT *From, CLIENT *Client, int Type) +{ + /* Set client type (user or service) */ + Client_SetType(Client, Type); + + if (From) { + if (Conf_IsService(Conf_GetServer(Client_Conn(From)), + Client_ID(Client))) + Client_SetType(Client, CLIENT_SERVICE); + LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).", + Client_TypeText(Client), Client_Mask(Client), + Client_Modes(Client), Client_ID(From), + Client_ID(Client_Introducer(Client)), + Client_Hops(Client), Client_Hops(Client) > 1 ? "s": ""); + } else + Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).", + Client_TypeText(Client), Client_Mask(Client), + Client_Conn(Client)); + + /* Inform other servers */ + IRC_WriteStrServersPrefixFlag_CB(From, + From != NULL ? From : Client_ThisServer(), + '\0', cb_introduceClient, (void *)Client); +} /* Introduce_Client */ + + +static void +cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data) +{ + CLIENT *c = (CLIENT *)data; + CONN_ID conn; + char *modes, *user, *host; + + modes = Client_Modes(c); + user = Client_User(c) ? Client_User(c) : "-"; + host = Client_Hostname(c) ? Client_Hostname(c) : "-"; + + conn = Client_Conn(To); + if (Conn_Options(conn) & CONN_RFC1459) { + /* RFC 1459 mode: separate NICK and USER commands */ + Conn_WriteStr(conn, "NICK %s :%d", Client_ID(c), + Client_Hops(c) + 1); + Conn_WriteStr(conn, ":%s USER %s %s %s :%s", + Client_ID(c), user, host, + Client_ID(Client_Introducer(c)), Client_Info(c)); + if (modes[0]) + Conn_WriteStr(conn, ":%s MODE %s +%s", + Client_ID(c), Client_ID(c), modes); + } else { + /* RFC 2813 mode: one combined NICK or SERVICE command */ + if (Client_Type(c) == CLIENT_SERVICE + && strchr(Client_Flags(To), 'S')) + IRC_WriteStrClientPrefix(To, Prefix, + "SERVICE %s %d * +%s %d :%s", + Client_Mask(c), + Client_MyToken(Client_Introducer(c)), + Client_Modes(c), Client_Hops(c) + 1, + Client_Info(c)); + else + IRC_WriteStrClientPrefix(To, Prefix, + "NICK %s %d %s %s %d +%s :%s", + Client_ID(c), Client_Hops(c) + 1, + user, host, + Client_MyToken(Client_Introducer(c)), + modes, Client_Info(c)); + } +} /* cb_introduceClient */ + + /* -eof- */ diff --git a/src/ngircd/irc-login.h b/src/ngircd/irc-login.h index 28df23e..0b92038 100644 --- a/src/ngircd/irc-login.h +++ b/src/ngircd/irc-login.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -8,8 +8,6 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: irc-login.h,v 1.6 2005/03/19 18:43:48 fw Exp $ - * * Login and logout (header) */ @@ -17,14 +15,13 @@ #ifndef __irc_login_h__ #define __irc_login_h__ - -GLOBAL bool IRC_PASS PARAMS((CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_NICK PARAMS((CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_USER PARAMS((CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_PING PARAMS((CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_PONG PARAMS((CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_QUIT PARAMS((CLIENT *Client, REQUEST *Req )); - +GLOBAL bool IRC_PASS PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_NICK PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_USER PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_SERVICE PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_PING PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_PONG PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_QUIT PARAMS((CLIENT *Client, REQUEST *Req)); #endif diff --git a/src/ngircd/irc-mode.c b/src/ngircd/irc-mode.c index 3786e39..ed70a9b 100644 --- a/src/ngircd/irc-mode.c +++ b/src/ngircd/irc-mode.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2005 Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -229,7 +229,9 @@ client_exit: ok = IRC_WriteStrClientPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes ); IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes ); } - Log( LOG_DEBUG, "User \"%s\": Mode change, now \"%s\".", Client_Mask( Target ), Client_Modes( Target )); + LogDebug("%s \"%s\": Mode change, now \"%s\".", + Client_TypeText(Target), Client_Mask(Target), + Client_Modes(Target)); } IRC_SetPenalty( Client, 1 ); @@ -286,6 +288,11 @@ Channel_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel ) long l; size_t len; + /* Are modes allowed on channel? */ + if (Channel_Name(Channel)[0] == '+') + return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG, + Client_ID(Client), Channel_Name(Channel)); + /* Mode request: let's answer it :-) */ if (Req->argc <= 1) return Channel_Mode_Answer_Request(Origin, Channel); @@ -551,7 +558,15 @@ chan_exit: the_modes[len] = '\0'; if (Client_Type(Client) == CLIENT_SERVER) { - /* Forward mode changes to channel users and other servers */ + /* MODE requests for local channels from other servers + * are definitely invalid! */ + if (Channel_IsLocal(Channel)) { + Log(LOG_ALERT, "Got remote MODE command for local channel!? Ignored."); + return CONNECTED; + } + + /* Forward mode changes to channel users and all the + * other remote servers: */ IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s", Channel_Name( Channel ), the_modes, the_args); IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); } else { @@ -560,10 +575,14 @@ chan_exit: /* Send reply to client and inform other servers and channel users */ ok = IRC_WriteStrClientPrefix(Client, Origin, "MODE %s %s%s", Channel_Name(Channel), the_modes, the_args); - IRC_WriteStrServersPrefix(Client, Origin, "MODE %s %s%s", - Channel_Name(Channel), the_modes, the_args); - IRC_WriteStrChannelPrefix(Client, Channel, Origin, false, "MODE %s %s%s", - Channel_Name(Channel), the_modes, the_args); + /* Only forward requests for non-local channels */ + if (!Channel_IsLocal(Channel)) + IRC_WriteStrServersPrefix(Client, Origin, + "MODE %s %s%s", Channel_Name(Channel), + the_modes, the_args); + IRC_WriteStrChannelPrefix(Client, Channel, Origin, + false, "MODE %s %s%s", Channel_Name(Channel), + the_modes, the_args); } } diff --git a/src/ngircd/irc-op.c b/src/ngircd/irc-op.c index ac47779..6cd0104 100644 --- a/src/ngircd/irc-op.c +++ b/src/ngircd/irc-op.c @@ -14,8 +14,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: irc-op.c,v 1.17 2006/12/07 17:57:20 fw Exp $"; - #include "imp.h" #include #include @@ -35,90 +33,187 @@ static char UNUSED id[] = "$Id: irc-op.c,v 1.17 2006/12/07 17:57:20 fw Exp $"; #include "irc-op.h" +static bool +try_kick(CLIENT *peer, CLIENT* from, const char *nick, const char *channel, + const char *reason) +{ + CLIENT *target = Client_Search(nick); + + if (!target) + return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG, Client_ID(from), nick); + + Channel_Kick(peer, target, from, channel, reason); + return true; +} + + GLOBAL bool -IRC_KICK( CLIENT *Client, REQUEST *Req ) +IRC_KICK(CLIENT *Client, REQUEST *Req) { - CLIENT *target, *from; - + CLIENT *from; + char *itemList = Req->argv[0]; + const char* currentNick, *currentChannel, *reason; + unsigned int channelCount = 1; + unsigned int nickCount = 1; + assert( Client != NULL ); assert( Req != NULL ); - /* Falsche Anzahl Parameter? */ - if(( Req->argc < 2) || ( Req->argc > 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + if ((Req->argc < 2) || (Req->argc > 3)) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); - if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix ); - else from = Client; - if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); - - /* Ziel-User suchen */ - target = Client_Search( Req->argv[1] ); - if( ! target ) return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( from ), Req->argv[1] ); + while (*itemList) { + if (*itemList == ',') { + *itemList = '\0'; + channelCount++; + } + itemList++; + } - Channel_Kick( target, from, Req->argv[0], Req->argc == 3 ? Req->argv[2] : Client_ID( from )); - return CONNECTED; -} /* IRC_KICK */ + itemList = Req->argv[1]; + while (*itemList) { + if (*itemList == ',') { + *itemList = '\0'; + nickCount++; + } + itemList++; + } + + if (Client_Type(Client) == CLIENT_SERVER) + from = Client_Search(Req->prefix); + else + from = Client; + + if (!from) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->prefix); + + reason = Req->argc == 3 ? Req->argv[2] : Client_ID(from); + currentNick = Req->argv[1]; + currentChannel = Req->argv[0]; + if (channelCount == 1) { + while (nickCount > 0) { + if (!try_kick(Client, from, currentNick, + currentChannel, reason)) + return false; + + while (*currentNick) + currentNick++; + + currentNick++; + nickCount--; + } + } else if (channelCount == nickCount) { + while (nickCount > 0) { + if (!try_kick(Client, from, currentNick, + currentChannel, reason)) + return false; + + while (*currentNick) + currentNick++; + + while (*currentChannel) + currentChannel++; + + currentNick++; + currentChannel++; + nickCount--; + } + } else { + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + } + return true; +} /* IRC_KICK */ GLOBAL bool -IRC_INVITE( CLIENT *Client, REQUEST *Req ) +IRC_INVITE(CLIENT *Client, REQUEST *Req) { CHANNEL *chan; CLIENT *target, *from; + const char *colon_if_necessary; bool remember = false; assert( Client != NULL ); assert( Req != NULL ); - /* Wrong number of parameters? */ - if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + if (Req->argc != 2) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); - if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix ); - else from = Client; - if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); - - /* Search user */ - target = Client_Search( Req->argv[0] ); - if(( ! target ) || ( Client_Type( target ) != CLIENT_USER )) return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] ); + if (Client_Type(Client) == CLIENT_SERVER) + from = Client_Search(Req->prefix); + else + from = Client; + if (!from) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->prefix); - chan = Channel_Search( Req->argv[1] ); + /* Search user */ + target = Client_Search(Req->argv[0]); + if (!target || (Client_Type(target) != CLIENT_USER)) + return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->argv[0]); - if( chan ) - { + chan = Channel_Search(Req->argv[1]); + if (chan) { /* Channel exists. Is the user a valid member of the channel? */ - if( ! Channel_IsMemberOf( chan, from )) return IRC_WriteStrClient( from, ERR_NOTONCHANNEL_MSG, Client_ID( Client ), Req->argv[1] ); + if (!Channel_IsMemberOf(chan, from)) + return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG, Client_ID(Client), Req->argv[1]); /* Is the channel "invite-only"? */ - if( strchr( Channel_Modes( chan ), 'i' )) - { + if (strchr(Channel_Modes(chan), 'i')) { /* Yes. The user must be channel operator! */ - if( ! strchr( Channel_UserModes( chan, from ), 'o' )) return IRC_WriteStrClient( from, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( from ), Channel_Name( chan )); + if (!strchr(Channel_UserModes(chan, from), 'o')) + return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG, + Client_ID(from), Channel_Name(chan)); remember = true; } /* Is the target user already member of the channel? */ - if( Channel_IsMemberOf( chan, target )) return IRC_WriteStrClient( from, ERR_USERONCHANNEL_MSG, Client_ID( from ), Req->argv[0], Req->argv[1] ); + if (Channel_IsMemberOf(chan, target)) + return IRC_WriteStrClient(from, ERR_USERONCHANNEL_MSG, + Client_ID(from), Req->argv[0], Req->argv[1]); /* If the target user is banned on that channel: remember invite */ - if( Lists_Check(Channel_GetListBans(chan), target )) remember = true; + if (Lists_Check(Channel_GetListBans(chan), target)) + remember = true; if (remember) { /* We must remember this invite */ - if( ! Channel_AddInvite(chan, Client_Mask( target ), true)) + if (!Channel_AddInvite(chan, Client_Mask(target), true)) return CONNECTED; } } LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from), Req->argv[0], Req->argv[1]); + + /* + * RFC 2812 says: + * 'There is no requirement that the channel [..] must exist or be a valid channel' + * The problem with this is that this allows the "channel" to contain spaces, + * in which case we must prefix its name with a colon to make it clear that + * it is only a single argument. + */ + colon_if_necessary = strchr(Req->argv[1], ' ') ? ":":""; /* Inform target client */ - IRC_WriteStrClientPrefix( target, from, "INVITE %s %s", Req->argv[0], Req->argv[1] ); + IRC_WriteStrClientPrefix(target, from, "INVITE %s %s%s", Req->argv[0], + colon_if_necessary, Req->argv[1]); - if( Client_Conn( target ) > NONE ) - { + if (Client_Conn(target) > NONE) { /* The target user is local, so we have to send the status code */ - if( ! IRC_WriteStrClientPrefix( from, target, RPL_INVITING_MSG, Client_ID( from ), Req->argv[0], Req->argv[1] )) return DISCONNECTED; + if (!IRC_WriteStrClientPrefix(from, target, RPL_INVITING_MSG, + Client_ID(from), Req->argv[0], colon_if_necessary, Req->argv[1])) + return DISCONNECTED; + + if (strchr(Client_Modes(target), 'a') && + !IRC_WriteStrClient(from, RPL_AWAY_MSG, Client_ID(from), + Client_ID(target), Client_Away(target))) + return DISCONNECTED; } - return CONNECTED; } /* IRC_INVITE */ diff --git a/src/ngircd/irc-server.c b/src/ngircd/irc-server.c index d342ffa..f1b817c 100644 --- a/src/ngircd/irc-server.c +++ b/src/ngircd/irc-server.c @@ -14,8 +14,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: irc-server.c,v 1.46 2007/11/21 12:16:36 alex Exp $"; - #include "imp.h" #include #include @@ -26,6 +24,7 @@ static char UNUSED id[] = "$Id: irc-server.c,v 1.46 2007/11/21 12:16:36 alex Exp #include "defines.h" #include "resolve.h" #include "conn.h" +#include "conn-func.h" #include "conn-zip.h" #include "conf.h" #include "client.h" @@ -64,7 +63,8 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req ) return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG, Client_ID(Client), Req->command); - if (Client_Type(Client) == CLIENT_GOTPASS) { + if (Client_Type(Client) == CLIENT_GOTPASS || + Client_Type(Client) == CLIENT_GOTPASS_2813) { /* We got a PASS command from the peer, and now a SERVER * command: the peer tries to register itself as a server. */ LogDebug("Connection %d: got SERVER command (new server link) ...", @@ -124,7 +124,18 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req ) /* Mark this connection as belonging to an configured server */ Conf_SetServer(i, con); - + + /* Check protocol level */ + if (Client_Type(Client) == CLIENT_GOTPASS) { + /* We got a "simple" PASS command, so the peer is + * using the protocol as defined in RFC 1459. */ + if (! (Conn_Options(con) & CONN_RFC1459)) + Log(LOG_INFO, + "Switching connection %d (\"%s\") to RFC 1459 compatibility mode.", + con, Client_ID(Client)); + Conn_SetOption(con, CONN_RFC1459); + } + Client_SetType(Client, CLIENT_UNKNOWNSERVER); #ifdef ZLIB diff --git a/src/ngircd/irc-write.c b/src/ngircd/irc-write.c index 61131b6..8239c15 100644 --- a/src/ngircd/irc-write.c +++ b/src/ngircd/irc-write.c @@ -39,7 +39,9 @@ static char UNUSED id[] = "$Id: irc-write.c,v 1.21 2006/08/12 11:56:24 fw Exp $" #define SEND_TO_SERVER 2 -static char *Get_Prefix PARAMS(( CLIENT *Target, CLIENT *Client )); +static char *Get_Prefix PARAMS((CLIENT *Target, CLIENT *Client)); +static void cb_writeStrServersPrefixFlag PARAMS((CLIENT *Client, + CLIENT *Prefix, void *Buffer)); #ifdef PROTOTYPES @@ -187,7 +189,7 @@ va_dcl else if( Client_Type( c ) == CLIENT_SERVER ) c = NULL; } if( c ) c = Client_NextHop( c ); - + if( c && ( c != Client )) { /* Ok, anderer Client */ @@ -272,7 +274,7 @@ va_dcl IRC_WriteStrServersPrefixFlag( ExceptOf, Prefix, '\0', "%s", buffer ); } /* IRC_WriteStrServersPrefix */ - + #ifdef PROTOTYPES GLOBAL void @@ -288,9 +290,8 @@ va_dcl #endif { char buffer[1000]; - CLIENT *c; va_list ap; - + assert( Format != NULL ); assert( Prefix != NULL ); @@ -301,16 +302,27 @@ va_dcl #endif vsnprintf( buffer, 1000, Format, ap ); va_end( ap ); - - c = Client_First( ); - while( c ) - { - if(( Client_Type( c ) == CLIENT_SERVER ) && ( Client_Conn( c ) > NONE ) && ( c != Client_ThisServer( )) && ( c != ExceptOf )) - { - /* Ziel-Server gefunden. Nun noch pruefen, ob Flags stimmen */ - if(( Flag == '\0' ) || ( strchr( Client_Flags( c ), Flag ) != NULL )) IRC_WriteStrClientPrefix( c, Prefix, "%s", buffer ); + + IRC_WriteStrServersPrefixFlag_CB(ExceptOf, Prefix, Flag, + cb_writeStrServersPrefixFlag, buffer); +} /* IRC_WriteStrServersPrefixFlag */ + + +GLOBAL void +IRC_WriteStrServersPrefixFlag_CB(CLIENT *ExceptOf, CLIENT *Prefix, char Flag, + void (*callback)(CLIENT *, CLIENT *, void *), void *cb_data) +{ + CLIENT *c; + + c = Client_First(); + while(c) { + if (Client_Type(c) == CLIENT_SERVER && Client_Conn(c) > NONE && + c != Client_ThisServer() && c != ExceptOf) { + /* Found a target server, do the flags match? */ + if (Flag == '\0' || strchr(Client_Flags(c), Flag)) + callback(c, Prefix, cb_data); } - c = Client_Next( c ); + c = Client_Next(c); } } /* IRC_WriteStrServersPrefixFlag */ @@ -426,4 +438,11 @@ Get_Prefix( CLIENT *Target, CLIENT *Client ) } /* Get_Prefix */ +static void +cb_writeStrServersPrefixFlag(CLIENT *Client, CLIENT *Prefix, void *Buffer) +{ + IRC_WriteStrClientPrefix(Client, Prefix, "%s", Buffer); +} /* cb_writeStrServersPrefixFlag */ + + /* -eof- */ diff --git a/src/ngircd/irc-write.h b/src/ngircd/irc-write.h index 40169b1..51c8f0c 100644 --- a/src/ngircd/irc-write.h +++ b/src/ngircd/irc-write.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -8,32 +8,35 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: irc-write.h,v 1.8 2006/05/10 21:24:01 alex Exp $ - * * Sending IRC commands over the network (header) */ - #ifndef __irc_write_h__ #define __irc_write_h__ +GLOBAL bool IRC_WriteStrClient PARAMS((CLIENT *Client, char *Format, ...)); +GLOBAL bool IRC_WriteStrClientPrefix PARAMS((CLIENT *Client, CLIENT *Prefix, + char *Format, ...)); -GLOBAL bool IRC_WriteStrClient PARAMS(( CLIENT *Client, char *Format, ... )); -GLOBAL bool IRC_WriteStrClientPrefix PARAMS(( CLIENT *Client, CLIENT *Prefix, char *Format, ... )); - -GLOBAL bool IRC_WriteStrChannel PARAMS(( CLIENT *Client, CHANNEL *Chan, bool Remote, char *Format, ... )); -GLOBAL bool IRC_WriteStrChannelPrefix PARAMS(( CLIENT *Client, CHANNEL *Chan, CLIENT *Prefix, bool Remote, char *Format, ... )); +GLOBAL bool IRC_WriteStrChannel PARAMS((CLIENT *Client, CHANNEL *Chan, + bool Remote, char *Format, ...)); +GLOBAL bool IRC_WriteStrChannelPrefix PARAMS((CLIENT *Client, CHANNEL *Chan, + CLIENT *Prefix, bool Remote, char *Format, ...)); -GLOBAL void IRC_WriteStrServers PARAMS(( CLIENT *ExceptOf, char *Format, ... )); -GLOBAL void IRC_WriteStrServersPrefix PARAMS(( CLIENT *ExceptOf, CLIENT *Prefix, char *Format, ... )); -GLOBAL void IRC_WriteStrServersPrefixFlag PARAMS(( CLIENT *ExceptOf, CLIENT *Prefix, char Flag, char *Format, ... )); +GLOBAL void IRC_WriteStrServers PARAMS((CLIENT *ExceptOf, char *Format, ...)); +GLOBAL void IRC_WriteStrServersPrefix PARAMS((CLIENT *ExceptOf, CLIENT *Prefix, + char *Format, ...)); +GLOBAL void IRC_WriteStrServersPrefixFlag PARAMS((CLIENT *ExceptOf, + CLIENT *Prefix, char Flag, char *Format, ...)); +GLOBAL void IRC_WriteStrServersPrefixFlag_CB PARAMS((CLIENT *ExceptOf, + CLIENT *Prefix, char Flag, + void (*callback)(CLIENT *, CLIENT *, void *), void *cb_data)); -GLOBAL bool IRC_WriteStrRelatedPrefix PARAMS(( CLIENT *Client, CLIENT *Prefix, bool Remote, char *Format, ... )); - -GLOBAL void IRC_SetPenalty PARAMS(( CLIENT *Client, time_t Seconds )); +GLOBAL bool IRC_WriteStrRelatedPrefix PARAMS((CLIENT *Client, CLIENT *Prefix, + bool Remote, char *Format, ...)); +GLOBAL void IRC_SetPenalty PARAMS((CLIENT *Client, time_t Seconds)); #endif - /* -eof- */ diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c index 86f5852..47f8652 100644 --- a/src/ngircd/irc.c +++ b/src/ngircd/irc.c @@ -30,14 +30,21 @@ static char UNUSED id[] = "$Id: irc.c,v 1.132 2008/01/15 22:28:14 fw Exp $"; #include "defines.h" #include "irc-write.h" #include "log.h" +#include "match.h" #include "messages.h" #include "parse.h" +#include "tool.h" #include "exp.h" #include "irc.h" -static char *Option_String PARAMS(( CONN_ID Idx )); +static char *Option_String PARAMS((CONN_ID Idx)); +static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType, + bool SendErrors)); +static bool Send_Message_Mask PARAMS((CLIENT *from, char *command, + char *targetMask, char *message, + bool SendErrors)); GLOBAL bool @@ -166,82 +173,34 @@ IRC_KILL( CLIENT *Client, REQUEST *Req ) } /* IRC_KILL */ +/** + * Handler for the IRC command NOTICE. + */ GLOBAL bool -IRC_NOTICE( CLIENT *Client, REQUEST *Req ) +IRC_NOTICE(CLIENT *Client, REQUEST *Req) { - CLIENT *to, *from; - CHANNEL *chan; - - assert( Client != NULL ); - assert( Req != NULL ); - - if(( Client_Type( Client ) != CLIENT_USER ) && ( Client_Type( Client ) != CLIENT_SERVER )) return CONNECTED; - - /* Falsche Anzahl Parameter? */ - if( Req->argc != 2 ) return CONNECTED; - - if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix ); - else from = Client; - if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); - - to = Client_Search( Req->argv[0] ); - if(( to ) && ( Client_Type( to ) == CLIENT_USER )) - { - /* Okay, Ziel ist ein User */ - return IRC_WriteStrClientPrefix( to, from, "NOTICE %s :%s", Client_ID( to ), Req->argv[1] ); - } - else - { - chan = Channel_Search(Req->argv[0]); - if (chan) - return Channel_Notice(chan, from, Client, Req->argv[1]); - } - - return CONNECTED; + return Send_Message(Client, Req, CLIENT_USER, false); } /* IRC_NOTICE */ +/** + * Handler for the IRC command PRIVMSG. + */ GLOBAL bool -IRC_PRIVMSG( CLIENT *Client, REQUEST *Req ) +IRC_PRIVMSG(CLIENT *Client, REQUEST *Req) { - CLIENT *cl, *from; - CHANNEL *chan; - - assert( Client != NULL ); - assert( Req != NULL ); - - /* Falsche Anzahl Parameter? */ - if( Req->argc == 0 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command ); - if( Req->argc == 1 ) return IRC_WriteStrClient( Client, ERR_NOTEXTTOSEND_MSG, Client_ID( Client )); - if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - - if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix ); - else from = Client; - if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); - - cl = Client_Search( Req->argv[0] ); - if( cl ) - { - /* Okay, Ziel ist ein Client. Aber ist es auch ein User? */ - if( Client_Type( cl ) != CLIENT_USER ) return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( from ), Req->argv[0] ); - - /* Okay, Ziel ist ein User */ - if(( Client_Type( Client ) != CLIENT_SERVER ) && ( strchr( Client_Modes( cl ), 'a' ))) - { - /* Ziel-User ist AWAY: Meldung verschicken */ - if( ! IRC_WriteStrClient( from, RPL_AWAY_MSG, Client_ID( from ), Client_ID( cl ), Client_Away( cl ))) return DISCONNECTED; - } - - /* Text senden */ - if( Client_Conn( from ) > NONE ) Conn_UpdateIdle( Client_Conn( from )); - return IRC_WriteStrClientPrefix( cl, from, "PRIVMSG %s :%s", Client_ID( cl ), Req->argv[1] ); - } + return Send_Message(Client, Req, CLIENT_USER, true); +} /* IRC_PRIVMSG */ - chan = Channel_Search( Req->argv[0] ); - if( chan ) return Channel_Write( chan, from, Client, Req->argv[1] ); - return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( from ), Req->argv[0] ); -} /* IRC_PRIVMSG */ +/** + * Handler for the IRC command SQUERY. + */ +GLOBAL bool +IRC_SQUERY(CLIENT *Client, REQUEST *Req) +{ + return Send_Message(Client, Req, CLIENT_SERVICE, true); +} /* IRC_SQUERY */ GLOBAL bool @@ -351,4 +310,231 @@ Option_String( CONN_ID Idx ) } /* Option_String */ +static bool +Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) +{ + CLIENT *cl, *from; + CHANNEL *chan; + char *currentTarget = Req->argv[0]; + char *lastCurrentTarget = NULL; + + assert(Client != NULL); + assert(Req != NULL); + + if (Req->argc == 0) { + if (!SendErrors) + return CONNECTED; + return IRC_WriteStrClient(Client, ERR_NORECIPIENT_MSG, + Client_ID(Client), Req->command); + } + if (Req->argc == 1) { + if (!SendErrors) + return CONNECTED; + return IRC_WriteStrClient(Client, ERR_NOTEXTTOSEND_MSG, + Client_ID(Client)); + } + if (Req->argc > 2) { + if (!SendErrors) + return CONNECTED; + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + } + + if (Client_Type(Client) == CLIENT_SERVER) + from = Client_Search(Req->prefix); + else + from = Client; + if (!from) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->prefix); + + /* handle msgtarget = msgto *("," msgto) */ + currentTarget = strtok_r(currentTarget, ",", &lastCurrentTarget); + ngt_UpperStr(Req->command); + + while (currentTarget) { + /* Check for and handle valid of form: + * RFC 2812 2.3.1: + * msgto = channel / ( user [ "%" host ] "@" servername ) + * msgto =/ ( user "%" host ) / targetmask + * msgto =/ nickname / ( nickname "!" user "@" host ) + */ + if (strchr(currentTarget, '!') == NULL) + /* nickname */ + cl = Client_Search(currentTarget); + else + cl = NULL; + + if (cl == NULL) { + /* If currentTarget isn't a nickname check for: + * user ["%" host] "@" servername + * user "%" host + * nickname "!" user "@" host + */ + char target[COMMAND_LEN]; + char * nick = NULL; + char * user = NULL; + char * host = NULL; + char * server = NULL; + + strlcpy(target, currentTarget, COMMAND_LEN); + server = strchr(target, '@'); + if (server) { + *server = '\0'; + server++; + } + host = strchr(target, '%'); + if (host) { + *host = '\0'; + host++; + } + user = strchr(target, '!'); + if (user) { + /* msgto form: nick!user@host */ + *user = '\0'; + user++; + nick = target; + host = server; /* not "@server" but "@host" */ + } else { + user = target; + } + + for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) { + if (Client_Type(cl) != CLIENT_USER && + Client_Type(cl) != CLIENT_SERVICE) + continue; + if (nick != NULL && host != NULL) { + if (strcmp(nick, Client_ID(cl)) == 0 && + strcmp(user, Client_User(cl)) == 0 && + strcasecmp(host, Client_Hostname(cl)) == 0) + break; + else + continue; + } + if (strcasecmp(user, Client_User(cl)) != 0) + continue; + if (host != NULL && strcasecmp(host, + Client_Hostname(cl)) != 0) + continue; + if (server != NULL && strcasecmp(server, + Client_ID(Client_Introducer(cl))) != 0) + continue; + break; + } + } + + if (cl) { + /* Target is a user, enforce type */ +#ifndef STRICT_RFC + if (Client_Type(cl) != ForceType && + !(ForceType == CLIENT_USER && + (Client_Type(cl) == CLIENT_USER || + Client_Type(cl) == CLIENT_SERVICE))) { +#else + if (Client_Type(cl) != ForceType) { +#endif + if (!SendErrors) + return CONNECTED; + return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG, + Client_ID(from), + currentTarget); + } + +#ifndef STRICT_RFC + if (ForceType == CLIENT_SERVICE && + (Conn_Options(Client_Conn(Client_NextHop(cl))) + & CONN_RFC1459)) { + /* SQUERY command but RFC 1459 link: convert + * request to PRIVMSG command */ + Req->command = "PRIVMSG"; + } +#endif + + if (SendErrors && (Client_Type(Client) != CLIENT_SERVER) + && strchr(Client_Modes(cl), 'a')) { + /* Target is away */ + if (!IRC_WriteStrClient(from, RPL_AWAY_MSG, + Client_ID(from), + Client_ID(cl), + Client_Away(cl))) + return DISCONNECTED; + } + if (Client_Conn(from) > NONE) { + Conn_UpdateIdle(Client_Conn(from)); + } + if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s", + Req->command, Client_ID(cl), + Req->argv[1])) + return DISCONNECTED; + } else if (ForceType != CLIENT_SERVICE + && strchr("$#", currentTarget[0]) + && strchr(currentTarget, '.')) { + /* targetmask */ + if (!Send_Message_Mask(from, Req->command, currentTarget, + Req->argv[1], SendErrors)) + return DISCONNECTED; + } else if (ForceType != CLIENT_SERVICE + && (chan = Channel_Search(currentTarget))) { + /* channel */ + if (!Channel_Write(chan, from, Client, Req->command, + SendErrors, Req->argv[1])) + return DISCONNECTED; + } else { + if (!SendErrors) + return CONNECTED; + if (!IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG, + Client_ID(from), currentTarget)) + return DISCONNECTED; + } + + currentTarget = strtok_r(NULL, ",", &lastCurrentTarget); + } + + return CONNECTED; +} /* Send_Message */ + + +static bool +Send_Message_Mask(CLIENT * from, char * command, char * targetMask, + char * message, bool SendErrors) +{ + CLIENT *cl; + bool client_match; + char *mask = targetMask + 1; + + cl = NULL; + + if (strchr(Client_Modes(from), 'o') == NULL) { + if (!SendErrors) + return true; + return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG, + Client_ID(from)); + } + + if (targetMask[0] == '#') { + for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) { + if (Client_Type(cl) != CLIENT_USER) + continue; + client_match = MatchCaseInsensitive(mask, Client_Hostname(cl)); + if (client_match) + if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s", + command, Client_ID(cl), message)) + return false; + } + } else { + for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) { + if (Client_Type(cl) != CLIENT_USER) + continue; + client_match = MatchCaseInsensitive(mask, + Client_ID(Client_Introducer(cl))); + if (client_match) + if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s", + command, Client_ID(cl), message)) + return false; + } + } + return CONNECTED; +} /* Send_Message_Mask */ + + /* -eof- */ diff --git a/src/ngircd/irc.h b/src/ngircd/irc.h index a96c8e4..358d91a 100644 --- a/src/ngircd/irc.h +++ b/src/ngircd/irc.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -8,25 +8,20 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: irc.h,v 1.39 2005/03/19 18:43:49 fw Exp $ - * * IRC commands (header) */ - #ifndef __irc_h__ #define __irc_h__ - -GLOBAL bool IRC_ERROR PARAMS(( CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_KILL PARAMS(( CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_NOTICE PARAMS(( CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_PRIVMSG PARAMS(( CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_TRACE PARAMS(( CLIENT *Client, REQUEST *Req )); -GLOBAL bool IRC_HELP PARAMS(( CLIENT *Client, REQUEST *Req )); - +GLOBAL bool IRC_ERROR PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_KILL PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_NOTICE PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_PRIVMSG PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_SQUERY PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_TRACE PARAMS((CLIENT *Client, REQUEST *Req)); +GLOBAL bool IRC_HELP PARAMS((CLIENT *Client, REQUEST *Req)); #endif - /* -eof- */ diff --git a/src/ngircd/log.c b/src/ngircd/log.c index 9c4daf5..d450bd0 100644 --- a/src/ngircd/log.c +++ b/src/ngircd/log.c @@ -61,7 +61,7 @@ Log_Init( bool Daemon_Mode ) Is_Daemon = Daemon_Mode; #ifdef SYSLOG -#ifndef LOG_CONS /* Kludge: mips-dec-ultrix4.5 has no LOG_CONS/LOG_LOCAL5 */ +#ifndef LOG_CONS /* Kludge: mips-dec-ultrix4.5 has no LOG_CONS/LOG_LOCAL5 */ #define LOG_CONS 0 #endif #ifndef LOG_LOCAL5 @@ -202,6 +202,8 @@ va_dcl * Logging function of ngIRCd. * This function logs messages to the console and/or syslog, whichever is * suitable for the mode ngIRCd is running in (daemon vs. non-daemon). + * If LOG_snotice is set, the log messages goes to all user with the mode +s + * set and the local &SERVER channel, too. * Please note: you sould use LogDebug(...) for debug messages! * @param Level syslog level (LOG_xxx) * @param Format Format string like printf(). @@ -218,7 +220,6 @@ const char *Format; va_dcl #endif { - /* Eintrag in Logfile(s) schreiben */ char msg[MAX_LOG_MSG_LEN]; bool snotice; va_list ap; @@ -239,7 +240,6 @@ va_dcl if( Level == LOG_DEBUG ) return; #endif - /* String mit variablen Argumenten zusammenbauen ... */ #ifdef PROTOTYPES va_start( ap, Format ); #else @@ -248,11 +248,11 @@ va_dcl vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap ); va_end( ap ); - if( ! Is_Daemon ) - { - /* auf Konsole ausgeben */ - fprintf( stdout, "[%d:%d] %s\n", (int)getpid( ), Level, msg ); - fflush( stdout ); + if (!Is_Daemon) { + /* log to console */ + fprintf(stdout, "[%d:%d %4ld] %s\n", (int)getpid( ), Level, + time(NULL) - NGIRCd_Start, msg); + fflush(stdout); } #ifdef SYSLOG else @@ -269,10 +269,11 @@ va_dcl fflush( stderr ); } - if( snotice ) - { - /* NOTICE an lokale User mit "s"-Mode */ - Wall_ServerNotice( msg ); + if (snotice) { + /* Send NOTICE to all local users with mode +s and to the + * local &SERVER channel */ + Wall_ServerNotice(msg); + Channel_LogServer(msg); } } /* Log */ @@ -334,11 +335,11 @@ va_dcl vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap ); va_end( ap ); - if( ! Is_Daemon ) - { + if (!Is_Daemon) { /* Output to console */ - fprintf( stdout, "[%d:%d] %s\n", (int)getpid( ), Level, msg ); - fflush( stdout ); + fprintf(stdout, "[%d:%d %4ld] %s\n", (int)getpid( ), Level, + time(NULL) - NGIRCd_Start, msg); + fflush(stdout); } #ifdef SYSLOG else syslog( Level, "%s", msg ); diff --git a/src/ngircd/match.c b/src/ngircd/match.c index 8f2fa2a..5e97e71 100644 --- a/src/ngircd/match.c +++ b/src/ngircd/match.c @@ -22,6 +22,8 @@ static char UNUSED id[] = "$Id: match.c,v 1.5 2006/10/06 21:23:47 fw Exp $"; #include "exp.h" #include "match.h" +#include "defines.h" +#include "tool.h" /* @@ -53,6 +55,16 @@ Match( const char *Pattern, const char *String ) } /* Match */ +GLOBAL bool +MatchCaseInsensitive(const char *pattern, const char *searchme) +{ + char haystack[COMMAND_LEN]; + + strlcpy(haystack, searchme, sizeof(haystack)); + return Match(pattern, ngt_LowerStr(haystack)); +} /* MatchCaseInsensitive */ + + static int Matche( const char *p, const char *t ) { diff --git a/src/ngircd/match.h b/src/ngircd/match.h index ac1aa96..0e8df74 100644 --- a/src/ngircd/match.h +++ b/src/ngircd/match.h @@ -19,6 +19,7 @@ GLOBAL bool Match PARAMS(( const char *Pattern, const char *String )); +GLOBAL bool MatchCaseInsensitive PARAMS(( const char *Pattern, const char *searchme )); #endif diff --git a/src/ngircd/messages.h b/src/ngircd/messages.h index 4f01ac5..562f778 100644 --- a/src/ngircd/messages.h +++ b/src/ngircd/messages.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2004 Alexander Barton + * Copyright (c)2001-2008 Alexander Barton * * 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 @@ -8,8 +8,6 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: messages.h,v 1.75 2008/02/17 13:26:42 alex Exp $ - * * IRC numerics (Header) */ @@ -22,7 +20,7 @@ #define RPL_YOURHOST_MSG "002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)" #define RPL_CREATED_MSG "003 %s :This server has been started %s" #define RPL_MYINFO_MSG "004 %s %s ngircd-%s %s %s" -#define RPL_ISUPPORT1_MSG "005 %s RFC2812 CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=# CHANMODES=bI,k,l,imnPst CHANLIMIT=#:%d :are supported on this server" +#define RPL_ISUPPORT1_MSG "005 %s RFC2812 CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=bI,k,l,imnPst CHANLIMIT=#&+:%d :are supported on this server" #define RPL_ISUPPORT2_MSG "005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d PENALTY :are supported on this server" #define RPL_TRACELINK_MSG "200 %s Link %s-%s %s %s V%s %ld %d %d" @@ -32,6 +30,9 @@ #define RPL_STATSCOMMANDS_MSG "212 %s %s %ld %ld %ld" #define RPL_ENDOFSTATS_MSG "219 %s %c :End of STATS report" #define RPL_UMODEIS_MSG "221 %s +%s" +#define RPL_SERVLIST_MSG "234 %s %s %s %s %d %d :%s" +#define RPL_SERVLISTEND_MSG "235 %s %s %s :End of service listing" + #define RPL_STATSUPTIME "242 %s :Server Up %u days %u:%02u:%02u" #define RPL_LUSERCLIENT_MSG "251 %s :There are %ld users and %ld services on %ld servers" #define RPL_LUSEROP_MSG "252 %s %lu :operator(s) online" @@ -65,7 +66,7 @@ #define RPL_NOTOPIC_MSG "331 %s %s :No topic is set" #define RPL_TOPIC_MSG "332 %s %s :%s" #define RPL_TOPICSETBY_MSG "333 %s %s %s %u" -#define RPL_INVITING_MSG "341 %s %s %s" +#define RPL_INVITING_MSG "341 %s %s %s%s" #define RPL_INVITELIST_MSG "346 %s %s %s" #define RPL_ENDOFINVITELIST_MSG "347 %s %s :End of channel invite list" #define RPL_VERSION_MSG "351 %s %s-%s.%s %s :%s" @@ -97,6 +98,7 @@ #define ERR_NOTEXTTOSEND_MSG "412 %s :No text to send" #define ERR_UNKNOWNCOMMAND_MSG "421 %s %s :Unknown command" #define ERR_NOMOTD_MSG "422 %s :MOTD file is missing" +#define ERR_NONICKNAMEGIVEN_MSG "431 %s :No nickname given" #define ERR_ERRONEUSNICKNAME_MSG "432 %s %s :Erroneous nickname" #define ERR_NICKNAMEINUSE_MSG "433 %s %s :Nickname already in use" #define ERR_USERNOTINCHANNEL_MSG "441 %s %s %s :They aren't on that channel" @@ -114,6 +116,7 @@ #define ERR_INVITEONLYCHAN_MSG "473 %s %s :Cannot join channel (+i)" #define ERR_BANNEDFROMCHAN_MSG "474 %s %s :Cannot join channel (+b)" #define ERR_BADCHANNELKEY_MSG "475 %s %s :Cannot join channel (+k)" +#define ERR_NOCHANMODES_MSG "477 %s %s :Channel doesn't support modes" #define ERR_NOPRIVILEGES_MSG "481 %s :Permission denied" #define ERR_CHANOPRIVSNEEDED_MSG "482 %s %s :You are not channel operator" #define ERR_CANTKILLSERVER_MSG "483 %s :You can't kill a server!" diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c index 45487bc..b564295 100644 --- a/src/ngircd/ngircd.c +++ b/src/ngircd/ngircd.c @@ -34,9 +34,14 @@ #include #include +#if defined(DEBUG) && defined(HAVE_MTRACE) +#include +#endif + #include "defines.h" #include "resolve.h" #include "conn.h" +#include "conf-ssl.h" #include "client.h" #include "channel.h" #include "conf.h" @@ -84,6 +89,12 @@ main( int argc, const char *argv[] ) int i; size_t n; +#if defined(DEBUG) && defined(HAVE_MTRACE) + /* enable GNU libc memory tracing when running in debug mode + * and functionality available */ + mtrace(); +#endif + umask( 0077 ); NGIRCd_SignalQuit = NGIRCd_SignalRestart = NGIRCd_SignalRehash = false; @@ -357,6 +368,10 @@ Fill_Version( void ) strlcat( NGIRCd_VersionAddition, "ZLIB", sizeof NGIRCd_VersionAddition ); #endif +#ifdef SSL_SUPPORT + if ( NGIRCd_VersionAddition[0] ) strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition ); + strlcat( NGIRCd_VersionAddition, "SSL", sizeof NGIRCd_VersionAddition ); +#endif #ifdef TCPWRAP if( NGIRCd_VersionAddition[0] ) strlcat( NGIRCd_VersionAddition, "+", sizeof NGIRCd_VersionAddition ); @@ -455,7 +470,10 @@ NGIRCd_Rehash( void ) /* Create new pre-defined channels */ Channel_InitPredefined( ); - + + if (!ConnSSL_InitLibrary()) + Log(LOG_WARNING, "Re-Initializing SSL failed, using old keys"); + /* Start listening on sockets */ Conn_InitListeners( ); @@ -676,6 +694,19 @@ NGIRCd_getNobodyID(uid_t *uid, gid_t *gid ) { struct passwd *pwd; +#ifdef __CYGWIN__ + /* Cygwin kludge. + * It can return EINVAL instead of EPERM + * so, if we are already unprivileged, + * use id of current user. + */ + if (geteuid() && getuid()) { + *uid = getuid(); + *gid = getgid(); + return true; + } +#endif + pwd = getpwnam("nobody"); if (!pwd) return false; @@ -703,6 +734,10 @@ NGIRCd_Init( bool NGIRCd_NoDaemon ) if (initialized) return true; + if (!ConnSSL_InitLibrary()) + Log(LOG_WARNING, + "Warning: Error during SSL initialization, continuing ..."); + if( Conf_Chroot[0] ) { if( chdir( Conf_Chroot ) != 0 ) { Log( LOG_ERR, "Can't chdir() in ChrootDir (%s): %s", Conf_Chroot, strerror( errno )); @@ -771,7 +806,11 @@ NGIRCd_Init( bool NGIRCd_NoDaemon ) } /* New child process */ +#ifndef NeXT (void)setsid( ); +#else + setpgrp(0, getpid()); +#endif chdir( "/" ); /* Detach stdin, stdout and stderr */ @@ -789,16 +828,16 @@ NGIRCd_Init( bool NGIRCd_NoDaemon ) pwd = getpwuid( Conf_UID ); grp = getgrgid( Conf_GID ); - Log( LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.", - pwd ? pwd->pw_name : "unknown", Conf_UID, - grp ? grp->gr_name : "unknown", Conf_GID, pid); + Log(LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.", + pwd ? pwd->pw_name : "unknown", (long)Conf_UID, + grp ? grp->gr_name : "unknown", (long)Conf_GID, (long)pid); - if ( chrooted ) { - Log( LOG_INFO, "Running chrooted, chrootdir \"%s\".", Conf_Chroot ); + if (chrooted) { + Log(LOG_INFO, "Running with root directory \"%s\".", + Conf_Chroot ); return true; - } else { - Log( LOG_INFO, "Not running chrooted." ); - } + } else + Log(LOG_INFO, "Not running with changed root directory."); /* Change working directory to home directory of the user * we are running as (only when running in daemon mode and not in chroot) */ diff --git a/src/ngircd/numeric.c b/src/ngircd/numeric.c index dd24016..74c5c12 100644 --- a/src/ngircd/numeric.c +++ b/src/ngircd/numeric.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2007 Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -13,8 +13,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: numeric.c,v 1.1 2007/11/21 12:20:32 alex Exp $"; - #include "imp.h" #include #include @@ -26,6 +24,7 @@ static char UNUSED id[] = "$Id: numeric.c,v 1.1 2007/11/21 12:20:32 alex Exp $"; #include "conn.h" #include "conf.h" #include "conn.h" +#include "conn-func.h" #include "client.h" #include "channel.h" #include "irc-write.h" @@ -39,6 +38,77 @@ static char UNUSED id[] = "$Id: numeric.c,v 1.1 2007/11/21 12:20:32 alex Exp $"; /** + * Announce a channel and its users in the network. + */ +static bool +Announce_Channel(CLIENT *Client, CHANNEL *Chan) +{ + CL2CHAN *cl2chan; + CLIENT *cl; + char str[LINE_LEN], *ptr; + bool njoin; + + if (Conn_Options(Client_Conn(Client)) & CONN_RFC1459) + njoin = false; + else + njoin = true; + + /* Get all the members of this channel */ + cl2chan = Channel_FirstMember(Chan); + snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(Chan)); + while (cl2chan) { + cl = Channel_GetClient(cl2chan); + assert(cl != NULL); + + if (njoin) { + /* RFC 2813: send NJOIN with nick names and modes + * (if user is channel operator or has voice) */ + if (str[strlen(str) - 1] != ':') + strlcat(str, ",", sizeof(str)); + if (strchr(Channel_UserModes(Chan, cl), 'v')) + strlcat(str, "+", sizeof(str)); + if (strchr(Channel_UserModes(Chan, cl), 'o')) + strlcat(str, "@", sizeof(str)); + strlcat(str, Client_ID(cl), sizeof(str)); + + /* Send the data if the buffer is "full" */ + if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 8)) { + if (!IRC_WriteStrClient(Client, "%s", str)) + return DISCONNECTED; + snprintf(str, sizeof(str), "NJOIN %s :", + Channel_Name(Chan)); + } + } else { + /* RFC 1459: no NJOIN, send JOIN and MODE */ + if (!IRC_WriteStrClientPrefix(Client, cl, "JOIN %s", + Channel_Name(Chan))) + return DISCONNECTED; + ptr = Channel_UserModes(Chan, cl); + while (*ptr) { + if (!IRC_WriteStrClientPrefix(Client, cl, + "MODE %s +%c %s", + Channel_Name(Chan), ptr[0], + Client_ID(cl))) + return DISCONNECTED; + ptr++; + } + } + + cl2chan = Channel_NextMember(Chan, cl2chan); + } + + /* Data left in the buffer? */ + if (str[strlen(str) - 1] != ':') { + /* Yes, send it ... */ + if (!IRC_WriteStrClient(Client, "%s", str)) + return DISCONNECTED; + } + + return CONNECTED; +} /* Announce_Channel */ + + +/** * Announce new server in the network * @param Client New server * @param Server Existing server in the network @@ -78,10 +148,45 @@ Announce_Server(CLIENT * Client, CLIENT * Server) static bool Announce_User(CLIENT * Client, CLIENT * User) { - return IRC_WriteStrClient(Client, "NICK %s %d %s %s %d +%s :%s", - Client_ID(User), Client_Hops(User) + 1, Client_User(User), - Client_Hostname(User), Client_MyToken(Client_Introducer(User)), - Client_Modes(User), Client_Info(User)); + CONN_ID conn; + char *modes; + + conn = Client_Conn(Client); + if (Conn_Options(conn) & CONN_RFC1459) { + /* RFC 1459 mode: separate NICK and USER commands */ + if (! Conn_WriteStr(conn, "NICK %s :%d", + Client_ID(User), Client_Hops(User) + 1)) + return DISCONNECTED; + if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s", + Client_ID(User), Client_User(User), + Client_Hostname(User), + Client_ID(Client_Introducer(User)), + Client_Info(User))) + return DISCONNECTED; + modes = Client_Modes(User); + if (modes[0]) { + return Conn_WriteStr(conn, ":%s MODE %s +%s", + Client_ID(User), Client_ID(User), + modes); + } + return CONNECTED; + } else { + /* RFC 2813 mode: one combined NICK or SERVICE command */ + if (Client_Type(User) == CLIENT_SERVICE + && strchr(Client_Flags(Client), 'S')) + return IRC_WriteStrClient(Client, + "SERVICE %s %d * +%s %d :%s", Client_Mask(User), + Client_MyToken(Client_Introducer(User)), + Client_Modes(User), Client_Hops(User) + 1, + Client_Info(User)); + else + return IRC_WriteStrClient(Client, + "NICK %s %d %s %s %d +%s :%s", + Client_ID(User), Client_Hops(User) + 1, + Client_User(User), Client_Hostname(User), + Client_MyToken(Client_Introducer(User)), + Client_Modes(User), Client_Info(User)); + } } /* Announce_User */ @@ -142,20 +247,20 @@ Send_CHANINFO(CLIENT * Client, CHANNEL * Chan) { char *modes, *topic; bool has_k, has_l; - + #ifdef DEBUG Log(LOG_DEBUG, "Sending CHANINFO commands ..."); #endif - + modes = Channel_Modes(Chan); topic = Channel_Topic(Chan); - + if (!*modes && !*topic) return CONNECTED; - + has_k = strchr(modes, 'k') != NULL; has_l = strchr(modes, 'l') != NULL; - + /* send CHANINFO */ if (!has_k && !has_l) { if (!*topic) { @@ -185,11 +290,9 @@ Send_CHANINFO(CLIENT * Client, CHANNEL * Chan) GLOBAL bool IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req) { - char str[LINE_LEN]; int max_hops, i; - CLIENT *c, *cl; + CLIENT *c; CHANNEL *chan; - CL2CHAN *cl2chan; Client_SetType(Client, CLIENT_SERVER); @@ -227,7 +330,8 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req) /* Announce all the users to the new server */ c = Client_First(); while (c) { - if (Client_Type(c) == CLIENT_USER) { + if (Client_Type(c) == CLIENT_USER || + Client_Type(c) == CLIENT_SERVICE) { if (!Announce_User(Client, c)) return DISCONNECTED; } @@ -237,6 +341,10 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req) /* Announce all channels to the new server */ chan = Channel_First(); while (chan) { + if (Channel_IsLocal(chan)) { + chan = Channel_Next(chan); + continue; + } #ifdef IRCPLUS /* Send CHANINFO if the peer supports it */ if (strchr(Client_Flags(Client), 'C')) { @@ -245,39 +353,8 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req) } #endif - /* Get all the members of this channel */ - cl2chan = Channel_FirstMember(chan); - snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(chan)); - while (cl2chan) { - cl = Channel_GetClient(cl2chan); - assert(cl != NULL); - - /* Nick name, with modes (if applicable) */ - if (str[strlen(str) - 1] != ':') - strlcat(str, ",", sizeof(str)); - if (strchr(Channel_UserModes(chan, cl), 'v')) - strlcat(str, "+", sizeof(str)); - if (strchr(Channel_UserModes(chan, cl), 'o')) - strlcat(str, "@", sizeof(str)); - strlcat(str, Client_ID(cl), sizeof(str)); - - /* Send the data if the buffer is "full" */ - if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 8)) { - if (!IRC_WriteStrClient(Client, "%s", str)) - return DISCONNECTED; - snprintf(str, sizeof(str), "NJOIN %s :", - Channel_Name(chan)); - } - - cl2chan = Channel_NextMember(chan, cl2chan); - } - - /* Data left in the buffer? */ - if (str[strlen(str) - 1] != ':') { - /* Yes, send it ... */ - if (!IRC_WriteStrClient(Client, "%s", str)) - return DISCONNECTED; - } + if (!Announce_Channel(Client, chan)) + return DISCONNECTED; /* Get next channel ... */ chan = Channel_Next(chan); diff --git a/src/ngircd/parse.c b/src/ngircd/parse.c index 409231a..ec856a0 100644 --- a/src/ngircd/parse.c +++ b/src/ngircd/parse.c @@ -89,6 +89,9 @@ static COMMAND My_Commands[] = { "REHASH", IRC_REHASH, CLIENT_USER, 0, 0, 0 }, { "RESTART", IRC_RESTART, CLIENT_USER, 0, 0, 0 }, { "SERVER", IRC_SERVER, 0xFFFF, 0, 0, 0 }, + { "SERVICE", IRC_SERVICE, 0xFFFF, 0, 0, 0 }, + { "SERVLIST", IRC_SERVLIST, CLIENT_USER, 0, 0, 0 }, + { "SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "SQUIT", IRC_SQUIT, CLIENT_SERVER, 0, 0, 0 }, { "STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, @@ -335,17 +338,22 @@ Validate_Command( UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed ) static bool +#ifdef STRICT_RFC Validate_Args(CONN_ID Idx, REQUEST *Req, bool *Closed) +#else +Validate_Args(UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed) +#endif { #ifdef STRICT_RFC int i; #endif - assert( Idx >= 0 ); - assert( Req != NULL ); *Closed = false; #ifdef STRICT_RFC + assert( Idx >= 0 ); + assert( Req != NULL ); + /* CR and LF are never allowed in command parameters. * But since we do accept lines terminated only with CR or LF in * "non-RFC-compliant mode" (besides the correct CR+LF combination), @@ -373,7 +381,8 @@ static bool Handle_Numeric(CLIENT *client, REQUEST *Req) { static const struct _NUMERIC Numerics[] = { - { 005, IRC_Num_ISUPPORT }, + { 5, IRC_Num_ISUPPORT }, + { 20, NULL }, { 376, IRC_Num_ENDOFMOTD } }; int i, num; @@ -381,8 +390,12 @@ Handle_Numeric(CLIENT *client, REQUEST *Req) CLIENT *prefix, *target = NULL; /* Determine target */ - if (Req->argc > 0) - target = Client_Search(Req->argv[0]); + if (Req->argc > 0) { + if (strcmp(Req->argv[0], "*") != 0) + target = Client_Search(Req->argv[0]); + else + target = Client_ThisServer(); + } if (!target) { /* Status code without target!? */ @@ -401,8 +414,11 @@ Handle_Numeric(CLIENT *client, REQUEST *Req) num = atoi(Req->command); for (i = 0; i < (int) ARRAY_SIZE(Numerics); i++) { - if (num == Numerics[i].numeric) + if (num == Numerics[i].numeric) { + if (!Numerics[i].function) + return CONNECTED; return Numerics[i].function(client, Req); + } } LogDebug("Ignored status code %s from \"%s\".", diff --git a/src/ngircd/resolve.c b/src/ngircd/resolve.c index 041c156..999ef99 100644 --- a/src/ngircd/resolve.c +++ b/src/ngircd/resolve.c @@ -175,13 +175,12 @@ Do_IdentQuery(int identsock, array *resolved_addr) #ifdef IDENTAUTH char *res; - assert(identsock >= 0); + if (identsock < 0) + return; #ifdef DEBUG Log_Resolver(LOG_DEBUG, "Doing IDENT lookup on socket %d ...", identsock); #endif - if (identsock < 0) - return; res = ident_id( identsock, 10 ); #ifdef DEBUG Log_Resolver(LOG_DEBUG, "Ok, IDENT lookup on socket %d done: \"%s\"", @@ -271,19 +270,21 @@ static bool ForwardLookup(const char *hostname, array *IpAddr) { ng_ipaddr_t addr; + #ifdef HAVE_GETADDRINFO int res; struct addrinfo *a, *ai_results; - static struct addrinfo hints = { + static struct addrinfo hints; + #ifndef WANT_IPV6 - .ai_family = AF_INET, + hints.ai_family = AF_INET; #endif #ifdef AI_ADDRCONFIG /* glibc has this, but not e.g. netbsd 4.0 */ - .ai_flags = AI_ADDRCONFIG, + hints.ai_flags = AI_ADDRCONFIG; #endif - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP - }; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + #ifdef WANT_IPV6 assert(Conf_ConnectIPv6 || Conf_ConnectIPv4); diff --git a/src/ngircd/resolve.h b/src/ngircd/resolve.h index 9fd16be..8ff88c8 100644 --- a/src/ngircd/resolve.h +++ b/src/ngircd/resolve.h @@ -20,7 +20,6 @@ #include "array.h" #include "tool.h" #include "ng_ipaddr.h" -#include /* This struct must not be accessed directly */ typedef struct _Res_Stat { diff --git a/src/portab/Makefile.am b/src/portab/Makefile.am index 06c6bae..a57ea49 100644 --- a/src/portab/Makefile.am +++ b/src/portab/Makefile.am @@ -9,14 +9,12 @@ # Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste # der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS. # -# $Id: Makefile.am,v 1.8 2005/04/16 09:23:01 fw Exp $ -# AUTOMAKE_OPTIONS = ansi2knr noinst_LIBRARIES = libngportab.a -libngportab_a_SOURCES = strlcpy.c strdup.c vsnprintf.c +libngportab_a_SOURCES = strdup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c check_PROGRAMS = portabtest @@ -26,7 +24,7 @@ portabtest_LDFLAGS = -L. portabtest_LDADD = -lngportab -noinst_HEADERS = imp.h exp.h portab.h +noinst_HEADERS = imp.h exp.h portab.h splint.h maintainer-clean-local: rm -f Makefile Makefile.in diff --git a/src/portab/portab.h b/src/portab/portab.h index 2a133ef..56d4249 100644 --- a/src/portab/portab.h +++ b/src/portab/portab.h @@ -99,6 +99,14 @@ typedef unsigned char bool; #endif #endif +#ifdef NeXT +#define S_IRUSR 0000400 /* read permission, owner */ +#define S_IWUSR 0000200 /* write permission, owner */ +#define S_IRGRP 0000040 /* read permission, group */ +#define S_IROTH 0000004 /* read permission, other */ +#define ssize_t int +#endif + #undef GLOBAL #define GLOBAL @@ -148,6 +156,10 @@ extern size_t strlcpy PARAMS(( char *dst, const char *src, size_t size )); extern char * strdup PARAMS(( const char *s )); #endif +#ifndef HAVE_STRTOK_R +extern char * strtok_r PARAMS((char *str, const char *delim, char **saveptr)); +#endif + #ifndef HAVE_VSNPRINTF #include extern int vsnprintf PARAMS(( char *str, size_t count, const char *fmt, va_list args )); diff --git a/src/portab/strtok_r.c b/src/portab/strtok_r.c new file mode 100644 index 0000000..852c8f7 --- /dev/null +++ b/src/portab/strtok_r.c @@ -0,0 +1,27 @@ +#include "portab.h" +#include + +#ifndef HAVE_STRTOK_R + +char * +strtok_r(char *str, const char *delim, char **saveptr) +{ + char *tmp; + + if (!str) + str = *saveptr; + str += strspn(str, delim); + if (*str == 0) + return NULL; + + tmp = str + strcspn(str, delim); /* get end of token */ + if (*tmp) { /* another delimiter */ + *tmp = 0; + tmp++; + } + *saveptr = tmp; + return str; +} + +#endif + diff --git a/src/portab/waitpid.c b/src/portab/waitpid.c new file mode 100644 index 0000000..0c16960 --- /dev/null +++ b/src/portab/waitpid.c @@ -0,0 +1,31 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * + * waitpid() implementation. Public domain. + * Written by Steven D. Blackford for the NeXT system. + * + */ + +#include "portab.h" + +#include "imp.h" +#include +#include +#include + +#include "exp.h" + +#ifndef HAVE_WAITPID + +GLOBAL int +waitpid(pid, stat_loc, options) +int pid, *stat_loc, options; +{ + for (;;) { + int wpid = wait(stat_loc); + if (wpid == pid || wpid == -1) + return wpid; + } +} + +#endif diff --git a/src/testsuite/.gitignore b/src/testsuite/.gitignore index 611b082..fd628e3 100644 --- a/src/testsuite/.gitignore +++ b/src/testsuite/.gitignore @@ -1,10 +1,19 @@ -T-ngircd +T-ngircd1 +T-ngircd2 channel-test connect-test +invite-test +join-test +kick-test +message-test misc-test mode-test +opless-channel-test +server-link-test who-test -ngircd-test.log -ngircd-test.motd +ngircd-test1.log +ngircd-test2.log +ngircd-test1.motd +ngircd-test2.motd logs tests diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am index 6512aaa..f72453f 100644 --- a/src/testsuite/Makefile.am +++ b/src/testsuite/Makefile.am @@ -1,6 +1,6 @@ # # ngIRCd -- The Next Generation IRC Daemon -# Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) +# Copyright (c)2001-2008 Alexander Barton (alex@barton.de) # # Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen # der GNU General Public License (GPL), wie von der Free Software Foundation @@ -9,8 +9,6 @@ # Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste # der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS. # -# $Id: Makefile.am,v 1.18 2008/02/17 13:26:42 alex Exp $ -# AUTOMAKE_OPTIONS = ../portab/ansi2knr @@ -20,15 +18,18 @@ EXTRA_DIST = \ README functions.inc getpid.sh \ start-server.sh stop-server.sh tests.sh stress-server.sh \ test-loop.sh wait-tests.sh \ - channel-test.e connect-test.e check-idle.e misc-test.e mode-test.e \ - who-test.e stress-A.e stress-B.e \ - ngircd-test.conf + channel-test.e connect-test.e check-idle.e invite-test.e \ + join-test.e kick-test.e message-test.e misc-test.e mode-test.e \ + opless-channel-test.e server-link-test.e who-test.e \ + stress-A.e stress-B.e \ + start-server1 stop-server1 ngircd-test1.conf \ + start-server2 stop-server2 ngircd-test2.conf all: clean-local: - rm -rf logs tests *-test ngircd-test.log ngircd-test.motd \ - T-ngircd procs.tmp + rm -rf logs tests *-test ngircd-test*.log procs.tmp \ + T-ngircd1 ngircd-test1.motd T-ngircd2 ngircd-test2.motd maintainer-clean-local: rm -f Makefile Makefile.in @@ -36,7 +37,8 @@ maintainer-clean-local: check_SCRIPTS = ngircd-TEST-Binary tests.sh ngircd-TEST-Binary: - cp ../ngircd/ngircd T-ngircd + cp ../ngircd/ngircd T-ngircd1 + cp ../ngircd/ngircd T-ngircd2 [ -f getpid.sh ] || ln -s $(srcdir)/getpid.sh . connect-test: tests.sh @@ -47,9 +49,21 @@ channel-test: tests.sh rm -f channel-test ln -s $(srcdir)/tests.sh channel-test -who-test: tests.sh - rm -f who-test - ln -s $(srcdir)/tests.sh who-test +invite-test: tests.sh + rm -f invite-test + ln -s $(srcdir)/tests.sh invite-test + +join-test: tests.sh + rm -f join-test + ln -s $(srcdir)/tests.sh join-test + +kick-test: tests.sh + rm -f kick-test + ln -s $(srcdir)/tests.sh kick-test + +message-test: tests.sh + rm -f message-test + ln -s $(srcdir)/tests.sh message-test misc-test: tests.sh rm -f misc-test @@ -59,13 +73,33 @@ mode-test: tests.sh rm -f mode-test ln -s $(srcdir)/tests.sh mode-test -TESTS = start-server.sh \ +opless-channel-test: tests.sh + rm -f opless-channel-test + ln -s $(srcdir)/tests.sh opless-channel-test + +server-link-test: tests.sh + rm -f server-link-test + ln -s $(srcdir)/tests.sh server-link-test + +who-test: tests.sh + rm -f who-test + ln -s $(srcdir)/tests.sh who-test + +TESTS = start-server1 \ connect-test \ + start-server2 \ channel-test \ + invite-test \ + join-test \ + kick-test \ + message-test \ misc-test \ mode-test \ + opless-channel-test \ who-test \ + server-link-test \ + stop-server2 \ stress-server.sh \ - stop-server.sh + stop-server1 # -eof- diff --git a/src/testsuite/README b/src/testsuite/README index 2a39be0..8b695a1 100644 --- a/src/testsuite/README +++ b/src/testsuite/README @@ -1,7 +1,7 @@ ngIRCd - Next Generation IRC Server - (c)2001-2004 by Alexander Barton, + (c)2001-2008 Alexander Barton, alex@barton.de, http://www.barton.de/ ngIRCd is free software and published under the @@ -17,14 +17,20 @@ The purpose of the "test suite" contained in this directory is to detect bugs and incompatibilities in ngIRCd introduced during coding and after building ngIRCd on a specific platform. -To run the "standard" tests call "make check". It will build ngIRCd (if -required) and run some tests on it. These tests should be portable and run -on all supported platforms without errors. +To run the "standard" tests call "make check" (which runs "make check" in +all the source directories, testing the "portab" library as well for example) +or "make testsuite" (which only runs the tests in this directory). Both will +build ngIRCd (if required) and run some tests on it. These tests should be +portable and run on all supported platforms without errors. -Please note: most tests of this suite depend on the external tools expect(1) +NOTE #1: most tests of this suite depend on the external tools expect(1) and telnet(1), so make sure you have them installed. If not, the tests will not fail but simply be skipped. +NOTE #2: the two test servers started by this test suite are configured to +run on port 6789 and 6790; so it will fail if one or both of these ports +are already used by some other daemons! + II. Shell Scripts ~~~~~~~~~~~~~~~~ @@ -35,26 +41,43 @@ getpid.sh the given name in a portable manner. The result is echoed on the console. It is a helper script for some other scripts of this suite. -start-server.sh +start-server.sh [] - start-server.sh starts up the test binary, "T-ngircd". It makes sure - that getpid.sh is available and working, and that no other instance - of the test binary is already running. + start-server.sh starts up the test binary, "T-ngircd" (the default + for is 1) with configuration file "ngircd-test.conf" and the + console output redirected to "ngircd-test.log". + The script first makes sure that getpid.sh is available and working, + and that no other instance of the test binary is already running. The exit code is 0 if the test binary could be started. -stop-server.sh +stop-server.sha [] - This script uses getpid.sh to detect a running test binary "T-ngircd" - and then shuts it down using the TERM signal. + This script uses getpid.sh to detect a running test binary + "T-ngircd" and then shuts it down using the TERM signal. The exit code is 0 if the test binary could be stopped. -stress-server.sh +stress-server.sh [ []] - ... + stress-server.sh starts clients that "stress" the + running test server (id 1); but no more than clients + are started at the same moment. tests.sh - ... + Most of the tests scripts are symlinked to tests.sh, which in turn + uses expect(1) to run the respective script .e and checks + its exit code. + +test-loop.sh [ []] + + This script runs all the tests times (default: 5) and pauses + seconds (default: 5) betweed runs. + It isn't used by "make check" or "make testsuite". + +wait-tests.sh [] + + stress-server.sh uses this script to ensure that no more than + clients are connected to the test server (id 1). III. Scripts for expect(1) @@ -63,10 +86,14 @@ III. Scripts for expect(1) channel-test.e check-idle.e connect-test.e +invite-test.e +join-test.e +kick-test.e +message-test.e +misc-test.e mode-test.e +opless-channel-test.e +server-link-test.e stress-A.e stress-B.e - - --- -$Id: README,v 1.1 2004/09/04 13:58:31 alex Exp $ +who-test.e diff --git a/src/testsuite/getpid.sh b/src/testsuite/getpid.sh index 0d94519..5e16917 100755 --- a/src/testsuite/getpid.sh +++ b/src/testsuite/getpid.sh @@ -7,12 +7,14 @@ # detect flags for "ps" and "head" UNAME=`uname` -if [ $UNAME = "FreeBSD" -o $UNAME = "SunOS" ]; then +if [ $UNAME = "FreeBSD" ]; then PS_FLAGS="-a"; PS_PIDCOL="1"; HEAD_FLAGS="-n 1" elif [ $UNAME = "A/UX" ]; then PS_FLAGS="-ae"; PS_PIDCOL="1"; HEAD_FLAGS="-1" elif [ $UNAME = "GNU" ]; then PS_FLAGS="-ax"; PS_PIDCOL="2"; HEAD_FLAGS="-n 1" +elif [ $UNAME = "SunOS" ]; then + PS_FLAGS="-af"; PS_PIDCOL=2; HEAD_FLAGS="-n 1" else PS_FLAGS="-f"; PS_PIDCOL="2"; HEAD_FLAGS="-n 1" ps $PS_FLAGS > /dev/null 2>&1 diff --git a/src/testsuite/invite-test.e b/src/testsuite/invite-test.e new file mode 100644 index 0000000..cc7015a --- /dev/null +++ b/src/testsuite/invite-test.e @@ -0,0 +1,113 @@ +spawn telnet localhost 6789 +expect { + timeout { exit 1 } + "Connected" +} + +send "nick nick\r" +send "user user . . :User\r" +expect { + timeout { exit 1 } + "376" +} + +send "invite\r" +expect { + timeout { exit 1 } + "461" +} + +send "invite nick\r" +expect { + timeout { exit 1 } + "461" +} + +send "invite nick #channel\r" +expect { + timeout { exit 1 } + -re "INVITE nick :?#channel" +} +expect { + timeout { exit 1 } + -re "341 nick nick :?#channel" +} + +send "invite nosuchnic #TopicChannel\r" +expect { + timeout { exit 1 } + "401 nick nosuchnic :No such nick or channel name" +} + +send "invite nick #TopicChannel\r" +expect { + timeout { exit 1 } + "442 nick #TopicChannel :You are not on that channel" +} + +send "join #channel\r" +expect { + timeout { exit 1 } + -re "JOIN :?#channel" +} + +send "invite nick #channel\r" +expect { + timeout { exit 1 } + "443 nick nick #channel :is already on channel" +} + +send "mode #channel +i\r" +expect { + timeout { exit 1 } + "MODE #channel +i" +} + +send "mode #channel -o nick\r" +expect { + timeout { exit 1 } + "MODE #channel -o nick" +} + +send "invite nick #channel\r" +expect { + timeout { exit 1 } + "482 nick #channel :You are not channel operator" + #it would be reasonable to expect 443 here instead +} + +send "part #channel\r" +expect { + timeout { exit 1} + -re "PART #channel :?nick" +} + +send "invite nick :parameter with spaces\r" +expect { + timeout { exit 1 } + "INVITE nick :parameter with spaces" +} +expect { + timeout { exit 1 } + "341 nick nick :parameter with spaces" +} + +send "away message\r" +expect { + timeout { exit 1 } + "306 nick :You have been marked as being away" +} + +send "INVITE nick #channel\r" +expect { + timeout { exit 1 } + -re "301 nick nick :?message" +} + +send "quit\r" +expect { + timeout { exit 1 } + "Connection closed" +} + +# -eof- diff --git a/src/testsuite/join-test.e b/src/testsuite/join-test.e new file mode 100644 index 0000000..41a5168 --- /dev/null +++ b/src/testsuite/join-test.e @@ -0,0 +1,68 @@ +spawn telnet localhost 6789 +expect { + timeout { exit 1 } + "Connected" +} + +send "nick nick\r" +send "user user . . :User\r" +expect { + timeout { exit 1 } + "376" +} + +send "JOIN\r" +expect { + timeout { exit 1} + "461" +} + +send "JOIN #InviteChannel\r" +expect { + timeout { exit 1 } + "473" +} + +send "JOIN #FullKeyed\r" +expect { + timeout { exit 1 } + "475" +} + +send "JOIN #FullKeyed WrongKey\r" +expect { + timeout { exit 1 } + "475" +} + +send "JOIN #FullKeyed Secret\r" +expect { + timeout { exit 1 } + "471" +} + +send "JOIN #TopicChannel\r" +expect { + timeout { exit 1 } + "@* JOIN :#TopicChannel" +} +expect { + timeout { exit 1 } + "332" +} + +send "JOIN 0\r" +send "JOIN #1,#2,#3,#4\r" +send "JOIN #5\r" +expect { + timeout { exit 1 } + "405" +} + +send "quit\r" +expect { + timeout { exit 1 } + "Connection closed" +} + +# -eof- diff --git a/src/testsuite/kick-test.e b/src/testsuite/kick-test.e new file mode 100644 index 0000000..9412d32 --- /dev/null +++ b/src/testsuite/kick-test.e @@ -0,0 +1,112 @@ +spawn telnet localhost 6789 +expect { + timeout { exit 1 } + "Connected" +} + +send "nick nick\r" +send "user user . . :User\r" +expect { + timeout { exit 1 } + "376" +} + +send "kick #Channel nick\r" +expect { + timeout { exit 1 } + "403" +} + +send "join #Channel\r" + +send "kick #Channel nick\r" +expect { + timeout { exit 1 } + "@* KICK #Channel nick :nick" +} + +send "join #Channel\r" + +send "kick #Channel nick :reason\r" +expect { + timeout { exit 1 } + "@* KICK #Channel nick :reason" +} + +send "join #Channel,#Channel2\r" + +send "kick #Channel,#Channel2 nick\r" +expect { + timeout { exit 1 } + "461" +} + +send "kick #Channel,#Channel2,#NoExists,#NoExists nick1,nick,nick3,nick :reason\r" +expect { + timeout { exit 1 } + "401" +} +expect { + timeout { exit 1 } + "@* KICK #Channel2 nick :reason" +} +expect { + timeout { exit 1 } + "401" +} +expect { + timeout { exit 1 } + "403" +} + +send "kick #Channel nick2,nick,nick3\r" +expect { + timeout { exit 1 } + "401" +} +expect { + timeout { exit 1 } + "@* KICK #Channel nick :nick" +} +expect { + timeout { exit 1 } + "401" +} + +send "kick #Channel ,,\r" +expect { + timeout { exit 1 } + "401" +} +expect { + timeout { exit 1 } + "401" +} + +send "kick ,, ,,,\r" +expect { + timeout { exit 1 } + "461" +} + +send "kick ,, ,,\r" +expect { + timeout { exit 1 } + "401" +} +expect { + timeout { exit 1 } + "401" +} +expect { + timeout { exit 1 } + "401" +} + +send "quit\r" +expect { + timeout { exit 1 } + "Connection closed" +} + +# -eof- diff --git a/src/testsuite/message-test.e b/src/testsuite/message-test.e new file mode 100644 index 0000000..eb26ac4 --- /dev/null +++ b/src/testsuite/message-test.e @@ -0,0 +1,133 @@ +spawn telnet localhost 6789 +expect { + timeout { exit 1 } + "Connected" +} + +send "nick nick\r" +send "user user . . :User\r" +expect { + timeout { exit 1 } + "376" +} + +send "privmsg nick :test\r" +expect { + timeout { exit 1 } + "@* PRIVMSG nick :test" +} + +send "privmsg nick\r" +expect { + timeout { exit 1 } + "412" +} + +send "privmsg\r" +expect { + timeout { exit 1 } + "411" +} + +send "privmsg nick,nick :test\r" +expect { + timeout { exit 1 } + "@* PRIVMSG nick :test\r*@* PRIVMSG nick :test" +} + +send "privmsg nick,#testChannel,nick :test\r" +expect { + timeout { exit 1 } + "@* PRIVMSG nick :test\r*401*@* PRIVMSG nick :test" +} + +send "privmsg doesnotexist :test\r" +expect { + timeout { exit 1 } + "401" +} + +send "privmsg ~user@ngircd.test.server :test\r" +expect { + timeout { exit 1 } + "@* PRIVMSG nick :test" +} + +# The following two tests using "localhost" as host name +# had to be disabled, because there are operating systems +# out there, that use "localhost." as host name +# for 127.0.0.1 instead of just "localhost". +# (for example OpenBSD 4, OpenSolaris, ...) +# +#send "privmsg ~user\%localhost :test\r" +#expect { +# timeout { exit 1 } +# "@* PRIVMSG nick :test" +#} +# +#send "privmsg nick!~user@localhost :test\r" +#expect { +# timeout { exit 1 } +# "@* PRIVMSG nick :test" +# "401" +#} + +send "away :away\r" +expect { + timeout { exit 1 } + "306" +} + +send "privmsg nick :test\r" +expect { + timeout { exit 1 } + "301" +} + +send "away\r" +expect { + timeout { exit 1 } + "305" +} + +send "privmsg \$ngircd.test.server :test\r" +expect { + timeout { exit 1 } + "481" +} + +send "privmsg #*.de :test\r" +expect { + timeout { exit 1 } + "481" +} + +send "oper TestOp 123\r" + +send "privmsg \$ngircd.test.server :test\r" +expect { + timeout { exit 1 } + "@* PRIVMSG nick :test" +} + +send "privmsg \$*.test*.server :test\r" +expect { + timeout { exit 1 } + "@* PRIVMSG nick :test" +} + +send "privmsg \$noDotServer :test\r" +expect { + timeout { exit 1 } + "401" +} + +#cannot test host mask since localhost has no '.' as RFC requires + +send "quit\r" +expect { + timeout { exit 1 } + "Connection closed" +} + +# -eof- diff --git a/src/testsuite/misc-test.e b/src/testsuite/misc-test.e index 4b88379..0623024 100644 --- a/src/testsuite/misc-test.e +++ b/src/testsuite/misc-test.e @@ -13,26 +13,147 @@ expect { "376" } +# RFC 2812 Section 3.4.1 + +send "motd\r" +expect { + timeout { exit 1 } + "375" +} +expect { + timeout { exit 1 } + "372" +} +expect { + timeout { exit 1 } + "376" +} + +send "motd ngircd.test.server\r" +expect { + timeout { exit 1 } + "375" +} +expect { + timeout { exit 1 } + "372" +} +expect { + timeout { exit 1 } + "376" +} + +send "motd doesnotexist\r" +expect { + timeout { exit 1 } + "402" +# note this is not specified in RFC 2812, but probably should be +} + +# RFC 2812 Section 3.4.3 + +send "version\r" +expect { + timeout { exit 1 } + "351" +} + +send "version ngircd.test.server\r" +expect { + timeout { exit 1 } + "351" +} + +send "version doesnotexist\r" +expect { + timeout { exit 1 } + "402" +} + +# RFC 2812 Section 3.4.6 + +send "time\r" +expect { + timeout { exit 1 } + "391" +} + +send "time ngircd.test.server\r" +expect { + timeout { exit 1 } + "391" +} + +send "time doesnotexist\r" +expect { + timeout { exit 1 } + "402" +} + +# RFC 2812 Section 3.4.10 + +send "info\r" +expect { + timeout { exit 1 } + "371" +} +expect { + timeout { exit 1 } + "374" +} + +# RFC 2812 Section 4.5 + send "summon\r" expect { timeout { exit 1 } "445" } +# RFC 2812 Section 4.6 + send "users\r" expect { timeout { exit 1 } "446" } -send "info\r" +# RFC 2812 Section 4.8 + +send "userhost\r" expect { timeout { exit 1 } - "371" + "461" } + +send "userhost nick\r" expect { timeout { exit 1 } - "374" + -re ":ngircd.test.server 302 nick :?nick=+.*@(localhos.*|127.0.0.1)" +} + +send "userhost doesnotexist\r" +expect { + timeout { exit 1 } + ":ngircd.test.server 302 nick :\r" +} + +send "userhost nick doesnotexist nick doesnotexist\r" +expect { + timeout { exit 1 } + -re ":ngircd.test.server 302 nick :nick=+.*@(localhos.*|127.0.0.1) nick=+.*@(localhos.*|127.0.0.1)" +} + +send "away :testing\r" +expect { + timeout { exit 1 } + "306 nick" +} + +send "userhost nick nick nick nick nick nick\r" +expect { + timeout { exit 1 } + -re ":ngircd.test.server 302 nick :nick=-.*@(localhos.*|127.0.0.1) nick=-.*@(localhos.*|127.0.0.1) nick=-.*@(localhos.*|127.0.0.1) nick=-.*@(localhos.*|127.0.0.1) nick=-.*@(localhos.*|127.0.0.1)\r" } send "quit\r" diff --git a/src/testsuite/ngircd-test.conf b/src/testsuite/ngircd-test.conf deleted file mode 100644 index 0bec96d..0000000 --- a/src/testsuite/ngircd-test.conf +++ /dev/null @@ -1,16 +0,0 @@ -# $Id: ngircd-test.conf,v 1.6 2008/02/17 00:00:13 fw Exp $ - -[Global] - Name = ngircd.test.server - Info = ngIRCd Test-Server - Ports = 6789 - MotdFile = ngircd-test.motd - AdminEMail = admin@irc.server - MaxConnectionsIP = 0 - OperCanUseMode = yes - -[Operator] - Name = TestOp - Password = 123 - -# -eof- diff --git a/src/testsuite/ngircd-test1.conf b/src/testsuite/ngircd-test1.conf new file mode 100644 index 0000000..a12873f --- /dev/null +++ b/src/testsuite/ngircd-test1.conf @@ -0,0 +1,44 @@ +# ngIRCd test suite +# configuration file for test server #2 + +[Global] + Name = ngircd.test.server + Info = ngIRCd Test-Server 1 + Ports = 6789 + MotdFile = ngircd-test1.motd + AdminEMail = admin@irc.server + MaxConnectionsIP = 0 + OperCanUseMode = yes + MaxJoins = 4 + NoIdent = yes + +[Operator] + Name = TestOp + Password = 123 + +[Server] + Name = ngircd.test.server2 + MyPassword = pwd1 + PeerPassword = pwd2 + +[Channel] + Name = #InviteChannel + Modes = i + +[Channel] + Name = #FullKeyed + Modes = lk + MaxUsers = 0 + Key = Secret + +[Channel] + Name = #TopicChannel + Modes = t + Topic = the topic + +[Channel] + Name = #SecretChannel + Modes = s + Topic = A secret Channel + +# -eof- diff --git a/src/testsuite/ngircd-test2.conf b/src/testsuite/ngircd-test2.conf new file mode 100644 index 0000000..e6d1696 --- /dev/null +++ b/src/testsuite/ngircd-test2.conf @@ -0,0 +1,26 @@ +# ngIRCd test suite +# configuration file for test server #2 + +[Global] + Name = ngircd.test.server2 + Info = ngIRCd Test-Server 2 + Ports = 6790 + MotdFile = ngircd-test2.motd + AdminEMail = admin@irc.server2 + MaxConnectionsIP = 0 + OperCanUseMode = yes + MaxJoins = 4 + NoIdent = yes + +[Operator] + Name = TestOp + Password = 123 + +[Server] + Name = ngircd.test.server + Host = localhost + Port = 6789 + MyPassword = pwd2 + PeerPassword = pwd1 + +# -eof- diff --git a/src/testsuite/opless-channel-test.e b/src/testsuite/opless-channel-test.e new file mode 100644 index 0000000..35d109e --- /dev/null +++ b/src/testsuite/opless-channel-test.e @@ -0,0 +1,32 @@ +spawn telnet localhost 6789 +expect { + timeout { exit 1 } + "Connected" +} + +send "nick nick\r" +send "user user . . :User\r" +expect { + timeout { exit 1 } + "376" +} + +send "JOIN +Channel\r" +expect { + timeout { exit 1 } + "@* JOIN :+Channel" +} + +send "mode +Channel +t\r" +expect { + timeout { exit 1 } + "477" +} + +send "quit\r" +expect { + timeout { exit 1 } + "Connection closed" +} + +# -eof- diff --git a/src/testsuite/server-link-test.e b/src/testsuite/server-link-test.e new file mode 100644 index 0000000..3a1c232 --- /dev/null +++ b/src/testsuite/server-link-test.e @@ -0,0 +1,52 @@ +# ngIRCd test suite +# server-server link test + +spawn telnet localhost 6790 +expect { + timeout { exit 1 } + "Connected" +} + +send "nick nick\r" +send "user user . . :User\r" +expect { + timeout { exit 1 } + "376" +} + +send "version ngircd.test.server2\r" +expect { + timeout { exit 1 } + ":ngircd.test.server2 351" +} +send "version ngircd.test.server\r" +expect { + timeout { exit 1 } + ":ngircd.test.server 351" +} + +send "whois ngircd.test.server nick\r" +expect { + timeout { exit 1 } + ":ngircd.test.server 318" +} + +send "admin ngircd.test.server\r" +expect { + timeout { exit 1 } + ":ngircd.test.server 259 nick :admin@irc.server" +} + +send "links\r" +expect { + timeout { exit 1 } + "364 nick ngircd.test.server ngircd.test.server2 :1" +} + +send "quit\r" +expect { + timeout { exit 1 } + "ERROR" +} + +# -eof- diff --git a/src/testsuite/start-server.sh b/src/testsuite/start-server.sh index 8d6c650..90832a3 100755 --- a/src/testsuite/start-server.sh +++ b/src/testsuite/start-server.sh @@ -1,16 +1,21 @@ #!/bin/sh # ngIRCd Test Suite -# $Id: start-server.sh,v 1.14 2004/09/06 22:04:06 alex Exp $ [ -z "$srcdir" ] && srcdir=`dirname $0` # read in functions . ${srcdir}/functions.inc -echo_n " starting server ..." +if [ -n "$1" ]; then + id="$1"; shift +else + id="1" +fi + +echo_n " starting server ${id} ..." -# remove old logfiles -rm -rf logs *.log +# remove old logfiles, if this is the first server (ID 1) +[ "$id" = "1" ] && rm -rf logs *.log # check weather getpid.sh returns valid PIDs. If not, don't start up the # test-server, because we won't be able to kill it at the end of the test. @@ -21,24 +26,25 @@ if [ $? -ne 0 ]; then fi # check if there is a test-server already running -./getpid.sh T-ngircd > /dev/null 2>&1 +./getpid.sh T-ngircd${id} >/dev/null 2>&1 if [ $? -eq 0 ]; then - echo " failure: test-server already running!" + echo " failure: test-server ${id} already running!" exit 1 fi # generate MOTD for test-server -echo "This is an ngIRCd Test Server" > ngircd-test.motd +echo "This is an ngIRCd Test Server" > ngircd-test${id}.motd # starting up test-server ... -./T-ngircd -np -f ${srcdir}/ngircd-test.conf $* > ngircd-test.log 2>&1 & +./T-ngircd${id} -n -f ${srcdir}/ngircd-test${id}.conf $* \ + >ngircd-test${id}.log 2>&1 & sleep 1 # validate running test-server -pid=`./getpid.sh T-ngircd` +pid=`./getpid.sh T-ngircd${id}` [ -n "$pid" ] && kill -0 $pid > /dev/null 2>&1; r=$? [ $r -eq 0 ] && echo " ok." || echo " failure!" -exit +exit $r # -eof- diff --git a/src/testsuite/start-server1 b/src/testsuite/start-server1 new file mode 100755 index 0000000..7fb4e94 --- /dev/null +++ b/src/testsuite/start-server1 @@ -0,0 +1,7 @@ +#!/bin/sh +# ngIRCd Test Suite + +[ -z "$srcdir" ] && srcdir=`dirname $0` +${srcdir}/start-server.sh 1 + +# -eof- diff --git a/src/testsuite/start-server2 b/src/testsuite/start-server2 new file mode 100755 index 0000000..2c4ffa6 --- /dev/null +++ b/src/testsuite/start-server2 @@ -0,0 +1,7 @@ +#!/bin/sh +# ngIRCd Test Suite + +[ -z "$srcdir" ] && srcdir=`dirname $0` +${srcdir}/start-server.sh 2 + +# -eof- diff --git a/src/testsuite/stop-server.sh b/src/testsuite/stop-server.sh index 256d512..7e6ee4f 100755 --- a/src/testsuite/stop-server.sh +++ b/src/testsuite/stop-server.sh @@ -1,16 +1,21 @@ #!/bin/sh # ngIRCd Test Suite -# $Id: stop-server.sh,v 1.13 2004/09/06 22:04:06 alex Exp $ [ -z "$srcdir" ] && srcdir=`dirname $0` # read in functions . ${srcdir}/functions.inc -echo_n " stopping server ..." +if [ -n "$1" ]; then + id="$1"; shift +else + id="1" +fi + +echo_n " stopping server ${id} ..." # stop test-server ... -pid=`./getpid.sh T-ngircd` +pid=`./getpid.sh T-ngircd${id}` if [ -z "$pid" ]; then echo " failure: no running server found!?" exit 1 @@ -26,7 +31,7 @@ for i in 1 2 3 4 5; do fi sleep 1 done -echo " failure: server still running!?" +echo " failure: server ${id} still running!?" exit 1 # -eof- diff --git a/src/testsuite/stop-server1 b/src/testsuite/stop-server1 new file mode 100755 index 0000000..9b56207 --- /dev/null +++ b/src/testsuite/stop-server1 @@ -0,0 +1,7 @@ +#!/bin/sh +# ngIRCd Test Suite + +[ -z "$srcdir" ] && srcdir=`dirname $0` +${srcdir}/stop-server.sh 1 + +# -eof- diff --git a/src/testsuite/stop-server2 b/src/testsuite/stop-server2 new file mode 100755 index 0000000..304d174 --- /dev/null +++ b/src/testsuite/stop-server2 @@ -0,0 +1,7 @@ +#!/bin/sh +# ngIRCd Test Suite + +[ -z "$srcdir" ] && srcdir=`dirname $0` +${srcdir}/stop-server.sh 2 + +# -eof- diff --git a/src/testsuite/who-test.e b/src/testsuite/who-test.e index c54a190..54c1992 100644 --- a/src/testsuite/who-test.e +++ b/src/testsuite/who-test.e @@ -14,7 +14,7 @@ expect { send "who\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick \* ~user localhost ngircd.test.server nick H :0 Real Name" + ":ngircd.test.server 352 nick \* * ngircd.test.server nick H :0 Real Name" } send "join #channel\r" @@ -26,7 +26,7 @@ expect { send "who 0\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick #channel ~user localhost ngircd.test.server nick H@ :0 Real Name" + ":ngircd.test.server 352 nick #channel * ngircd.test.server nick H@ :0 Real Name" } send "away :testing\r" @@ -38,7 +38,7 @@ expect { send "who *\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick #channel ~user localhost ngircd.test.server nick G@ :0 Real Name" + ":ngircd.test.server 352 nick #channel * ngircd.test.server nick G@ :0 Real Name" } send "mode #channel +v nick\r" @@ -47,10 +47,10 @@ expect { "@* MODE #channel +v nick\r" } -send "who localhost\r" +send "who localhos*\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick #channel ~user localhost ngircd.test.server nick G@ :0 Real Name" + ":ngircd.test.server 352 nick #channel * ngircd.test.server nick G@ :0 Real Name" } send "mode #channel -o nick\r" @@ -62,7 +62,7 @@ expect { send "who ngircd.test.server\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick #channel ~user localhost ngircd.test.server nick G+ :0 Real Name" + ":ngircd.test.server 352 nick #channel * ngircd.test.server nick G+ :0 Real Name" } send "part #channel\r" @@ -74,7 +74,7 @@ expect { send "who Real?Name\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick \* ~user localhost ngircd.test.server nick G :0 Real Name" + ":ngircd.test.server 352 nick \* * ngircd.test.server nick G :0 Real Name" } send "oper TestOp 123\r" @@ -90,7 +90,7 @@ expect { send "who 0 o\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick \* ~user localhost ngircd.test.server nick G* :0 Real Name" + ":ngircd.test.server 352 nick \* * ngircd.test.server nick G* :0 Real Name" } send "away\r" @@ -99,10 +99,10 @@ expect { "305 nick" } -send "who *cal*ho??\r" +send "who ??cal*ho*\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick \* ~user localhost ngircd.test.server nick H* :0 Real Name" + ":ngircd.test.server 352 nick \* * ngircd.test.server nick H* :0 Real Name" } send "join #opers\r" @@ -114,7 +114,7 @@ expect { send "who #opers\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick #opers ~user localhost ngircd.test.server nick H*@ :0 Real Name" + ":ngircd.test.server 352 nick #opers * ngircd.test.server nick H*@ :0 Real Name" } send "mode #opers -o nick\r" @@ -126,7 +126,7 @@ expect { send "who *.server\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick #opers ~user localhost ngircd.test.server nick H* :0 Real Name" + ":ngircd.test.server 352 nick #opers * ngircd.test.server nick H* :0 Real Name" } send "mode #opers +v nick\r" @@ -138,7 +138,7 @@ expect { send "who Real*me\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick #opers ~user localhost ngircd.test.server nick H*+ :0 Real Name" + ":ngircd.test.server 352 nick #opers * ngircd.test.server nick H*+ :0 Real Name" } send "mode #opers +s\r" @@ -150,7 +150,17 @@ expect { send "who n?c?\r" expect { timeout { exit 1 } - ":ngircd.test.server 352 nick \* ~user localhost ngircd.test.server nick H* :0 Real Name" + ":ngircd.test.server 352 nick \* * ngircd.test.server nick H* :0 Real Name" +} +expect { + timeout { exit 1 } + "315" +} + +send "who #SecretChannel\r" +expect { + timeout { exit 1 } + "315" } send "quit\r" diff --git a/src/tool/tool.c b/src/tool/tool.c index 1e72377..a24c160 100644 --- a/src/tool/tool.c +++ b/src/tool/tool.c @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001-2005 Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 Alexander Barton (alex@barton.de) * * 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 @@ -14,8 +14,6 @@ #include "portab.h" -static char UNUSED id[] = "$Id: tool.c,v 1.9 2008/02/26 22:04:18 fw Exp $"; - #include "imp.h" #include #include @@ -65,25 +63,40 @@ ngt_TrimStr(char *String) } /* ngt_TrimStr */ +/** + * Convert a string to uppercase letters. + */ GLOBAL char * -ngt_LowerStr( char *String ) +ngt_UpperStr(char *String) { - /* String in Kleinbuchstaben konvertieren. Der uebergebene - * Speicherbereich wird durch das Ergebnis ersetzt, zusaetzlich - * wird dieser auch als Pointer geliefert. */ + char *ptr; + + assert(String != NULL); + + ptr = String; + while(*ptr) { + *ptr = toupper(*ptr); + ptr++; + } + return String; +} /* ngt_UpperStr */ + +/** + * Convert a string to lowercase letters. + */ +GLOBAL char * +ngt_LowerStr(char *String) +{ char *ptr; - assert( String != NULL ); + assert(String != NULL); - /* Zeichen konvertieren */ ptr = String; - while( *ptr ) - { - *ptr = tolower( *ptr ); + while(*ptr) { + *ptr = tolower(*ptr); ptr++; } - return String; } /* ngt_LowerStr */ diff --git a/src/tool/tool.h b/src/tool/tool.h index 9bb7983..d17e787 100644 --- a/src/tool/tool.h +++ b/src/tool/tool.h @@ -1,6 +1,6 @@ /* * ngIRCd -- The Next Generation IRC Daemon - * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de) + * Copyright (c)2001-2008 by Alexander Barton (alex@barton.de) * * 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 @@ -8,8 +8,6 @@ * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. * - * $Id: tool.h,v 1.6 2008/02/26 22:04:18 fw Exp $ - * * Tool functions (Header) */ @@ -28,7 +26,9 @@ GLOBAL void ngt_TrimLastChr PARAMS((char *String, const char Chr )); GLOBAL void ngt_TrimStr PARAMS((char *String )); +GLOBAL char *ngt_UpperStr PARAMS((char *String )); GLOBAL char *ngt_LowerStr PARAMS((char *String )); + #endif