--- /dev/null
+makefile
+Makefile
--- /dev/null
+--leave-preprocessor-space
+--dont-break-procedure-type
+--no-space-after-function-call-names
+--brace-indent0
+--indent-level2
+--dont-line-up-parentheses
+--continuation-indentation4
+--case-indentation2
+--no-space-after-casts
+--blank-lines-after-procedures
+--no-blank-lines-after-declarations
+--braces-on-struct-decl-line
+--paren-indentation0
+--case-brace-indentation0
+--line-length80
+--declaration-indentation4
+-T size_t
+-T aClass
+-T aClient
+-T aServer
+-T anUser
+-T aChannel
+-T Mode
+-T aConfItem
+-T aMessage
+-T aMessageTree
+-T aGline
+-T aListingArgs
+-T snomask_t
+-T n_short
+-T n_long
+-T n_time
+-T u_char
+-T u_short
+-T u_long
+-T u_int
+-T dbuf
+-T dbufbuf
+-T aHashEntry
+-T Link
+-T Dlink
+-T VOIDSIG
+-T aHostent
+-T ResRQ
+-T aCache
+-T CacheTable
+-T cainfo
+-T reinfo
+-T RETSIGTYPE
+-T OPT_TYPE
--- /dev/null
+ircu2.10.07+
--- /dev/null
+#
+# $Id: BUGS,v 1.1.1.1 1999-11-16 05:13:14 codercom Exp $
+#
+# Undernet ircu BUGS File
+# Please put information about known bugs here, if the bug has been resolved
+# please comment the entry with "(fixed)"
+#
--- /dev/null
+#
+# ChangeLog for Undernet ircu Servers
+#
+# $Id: ChangeLog,v 1.1.1.1 1999-11-16 05:13:14 codercom Exp $
+#
+# Please insert new entries on the top of the list, a one or two line comment
+# is sufficient. Please include your name on the entries we know who to blame.
+# Please keep lines < 80 chars.
+#-------------------------------------------------------------------------------
+* ChangeLog (file): added ChangeLog, and BUGS files, import to ircu2.10 --Bleep
+
--- /dev/null
+ INSTALL file by Run <carlo@runaway.xs4all.nl>
+
+
+This is the UnderNet IRC daemon.
+
+The installation of the IRC daemon (ircd) exists of the following steps:
+
+1) Untar the package.
+2) cd into the base directory.
+3 )`./configure'
+4) `make config'
+5) `make'
+6) `make install'
+
+1) Untar the package
+====================
+
+The name of the package is something like `ircu2.x.y.z.tgz', where
+"x.y.z" is the current release (at the time of writing we have
+ircu2.10.00.beta3.tgz).
+
+You need `gzip', the GNU unzip command, to uncompress this package.
+You can download this from every GNU ftp site for almost any Operating system.
+
+If you have GNU tar, type:
+
+tar xzf ircu2.x.y.z.tgz
+
+where "ircu2.x.y.z.tgz" is the name of the package.
+
+If your tar doesn't support the 'z' flag, you can type alternatively:
+
+gzip -dc ircu2.x.y.z.tgz | tar xf -
+
+Both methods result in a directory "ircu2.x.y.z" in your current directory.
+
+2) cd into the base directory
+=============================
+
+Make this directory your current directory by typing:
+
+cd ircu2.x.y.z
+
+where "ircu2.x.y.z" is the name of the unpacked directory.
+
+3) `./configure'
+==============
+
+This will generate 'config/setup.h', your Operating System dependend
+configuration.
+
+4) `make config'
+================
+
+This will (re)generate the include/config.h file. You can run this
+as often as you like and it will use your last values as defaults.
+At every question you can type a '?' (followed by a return) to get
+extensive help, or a 'c' to continue using your old values by default
+(quickly finishing the script).
+
+5) `make'
+=========
+
+Type:
+
+make
+
+in the base directory. It should compile without errors or warnings.
+Please mail any problem to the maintainer, but only AFTER you made sure
+you did everything the right way. If you want your Operating System
+to be supported in future releases, you best make a patch that
+actually fixes the problem.
+
+6) `make install'
+=================
+
+This should install the ircd and the man page. Please recheck the
+permissions of the binary.
+You need to create some of the logfiles that you have chosen by hand
+(for instance with 'touch') before the ircd starts writing to them.
+Of course, you need a syntactically correct ircd.conf in DPATH. See the
+docs for some info on this. Also create an ircd.motd with the text of
+your MOTD. And finally create a remote.motd with three lines of text
+as the remote MOTD. Again, all of these files should be readable by the
+ircd, and the logfiles should be writeable.
+
+
+In case of problems
+===================
+
+If you have problems configuring the server you might consider installing
+GNU make in your PATH. In some cases a brain-dead /bin/sh is causing the
+problem, in which case I suggest to install 'bash' and use that (as sh -> bash).
+Finally, any other compile problem should be solved when you install gcc.
+
+If you have problems with starting the ircd, run 'make config' again
+and define DEBUGMODE. Recompile the ircd, and run it by hand as:
+
+ircd -t -x9
+
+This will write debug output to your screen, probably showing why it
+doesn't start.
+
+Don't use a server with DEBUGMODE defined on a production net.
--- /dev/null
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The license agreements of most software companies try to keep users
+at the mercy of those companies. By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must tell them their rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License. The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications. Each
+licensee is addressed as "you".
+
+ 1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program. You may charge a fee for the physical act of
+transferring a copy.
+
+ 2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+ a) cause the modified files to carry prominent notices stating that
+ you changed the files and the date of any change; and
+
+ b) cause the whole of any work that you distribute or publish, that
+ in whole or in part contains the Program or any part thereof, either
+ with or without modifications, to be licensed at no charge to all
+ third parties under the terms of this General Public License (except
+ that you may choose to grant warranty protection to some or all
+ third parties, at your option).
+
+ c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use
+ in the simplest and most usual way, to print or display an
+ announcement including an appropriate copyright notice and a notice
+ that there is no warranty (or else, saying that you provide a
+ warranty) and that users may redistribute the program under these
+ conditions, and telling the user how to view a copy of this General
+ Public License.
+
+ d) You may charge a fee for the physical act of transferring a
+ copy, and you may at your option offer warranty protection in
+ exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+\f
+ 3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+ a) accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ b) accompany it with a written offer, valid for at least three
+ years, to give any third party free (except for a nominal charge
+ for the cost of distribution) a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of
+ Paragraphs 1 and 2 above; or,
+
+ c) accompany it with the information you received as to where the
+ corresponding source code may be obtained. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it. For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+ 4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License. However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+ 5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions. You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+\f
+ 7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+ 8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+ To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19xx name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License. Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ program `Gnomovision' (a program to direct compilers to make passes
+ at assemblers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+# Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+
+# 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, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+srcdir=@srcdir@
+VPATH=@srcdir@
+
+SHELL=@SHPROG@
+RM=@RMPROG@
+AWK=@AWK@
+@SET_MAKE@
+#### End of system configuration section. ####
+
+all: build
+
+.PHONY: server build depend install config update diff patch export
+# Some versions of make give a warning when this is empty:
+.SUFFIXES: .dummy
+
+build:
+ @if [ ! -f config/config.h ]; then \
+ echo "Run 'make config' to configure the server"; \
+ else \
+ for i in config ircd; do \
+ echo "Building $$i..."; \
+ cd $$i; ${MAKE} build; cd ..; \
+ done; \
+ fi
+
+root-clean:
+ @for i in '*.orig' '.*.orig' '\#*' '*~' '.*~' '*.bak' '.*.bak' core; do\
+ echo "Removing $$i"; \
+ REMOVE_FILES="`find . -name "$$i" -print`"; \
+ test -n "$$REMOVE_FILES" && ${RM} -f $$REMOVE_FILES; \
+ done || true
+
+clean: root-clean
+ @for i in ircd config; do \
+ echo "Cleaning $$i..."; \
+ cd $$i; ${MAKE} clean; cd ..;\
+ done
+
+root-distclean: root-clean
+ @for i in '*.rej'; do \
+ echo "Removing $$i"; \
+ REMOVE_FILES="`find . -name "$$i" -print`"; \
+ test -n "$$REMOVE_FILES" && ${RM} -f $$REMOVE_FILES; \
+ done || true
+ ${RM} -f Makefile
+
+distclean: root-distclean
+ @for i in doc ircd config; do \
+ echo "Dist-cleaning $$i..."; \
+ cd $$i; ${MAKE} distclean; cd ..;\
+ done
+
+maintainer-clean: root-distclean
+ @for i in doc ircd config; do \
+ echo "maintainer-cleaning $$i..."; \
+ cd $$i; ${MAKE} maintainer-clean; cd ..;\
+ done
+
+depend:
+ @for i in ircd; do \
+ echo "Making dependencies in $$i..."; \
+ cd $$i; ${MAKE} depend; cd ..; \
+ done
+
+install:
+ @if [ -f ircd/ircd ]; then \
+ for i in ircd doc; do \
+ echo "Installing $$i..."; \
+ cd $$i; ${MAKE} install; cd ..; \
+ done \
+ else \
+ echo "First run 'make'"; \
+ fi
+
+uninstall:
+ @for i in doc ircd; do \
+ echo "Uninstalling $$i..."; \
+ cd $$i; ${MAKE} uninstall; cd ..; \
+ done
+
+config: FORCE
+ @cd config; ${MAKE} config
+ @echo
+ @echo "The Undernet IRC daemon is now hopefully configured for your setup."
+ @echo "Next run 'make' to build the server."
+ @echo
+
+# Coders: You need GNU make for this to work.
+Makefile: config/config.status Makefile.in
+ @echo "recreating Makefile"
+ @cd config; \
+ CONFIG_FILES=../Makefile CONFIG_HEADERS= ./config.status > /dev/null
+
+config/config.status:
+ @cd config; ${MAKE} config.status
+
+# Some versions of 'make' do not support the .PHONY target :
+FORCE:
+
+# Indent all headers and source files:
+indent:
+ @test "`indent --version`" = "GNU indent 2.1.0" || \
+ (echo "You need GNU indent 2.1.0; See doc/readme.indent" && exit -1);
+ VERSION_CONTROL=none indent include/*.h ircd/*.c
--- /dev/null
+Makefile
+config.cache
+config.log
+config.status
+setup.h
+stamp-h
+config-sh
+Configure
+config.h
+.config
+.tmpconfig.h
--- /dev/null
+# @configure_input@
+#
+# This script is used to configure the Undernet Daemon.
+# It was copied from the linux kernel distribution and only slightly
+# changed.
+#
+# 170897 (carlo@runaway.xs4all.nl) -
+# Changed the default configuration file to be defconfig.
+# Changed the location of autoconf.h to be 'include/'.
+# Changed the final text printed at the end.
+# Changed replacement to only match 'is not set' (and not 'is not').
+# Added function string() and define_string().
+# Added function define_macro()
+#
+# 180897 (carlo@runaway.xs4all.nl) -
+# Changed bool() so it can take a default argument.
+# Introduced hard link ../.config, to inherit config files over new
+# distributions.
+# Got rid of 'is not set' and using '=n' instead.
+# Changed configuration help file to be doc/Configure.help.
+# Changed DEFAULT into USE_DEFAULT, which is asked for in config.in.
+#
+# 210897 (carlo@runaway.xs4all.nl) -
+# Changed the location of autoconf.h and .config to be 'configure/'.
+# Fixed the script to work with a general 'sh' and not only with bash.
+# (removing 'function', using `` instead of $(), using \\$ inside
+# that instead of \$, using 'eval set -- $choices' rather then
+# 'set -- $choices', introduction of function 'expr' instead of
+# using external executable 'expr', using set -fh rather then
+# set -f -h, using "" around reg.exp rather then '' and removing
+# unnecessary backslashes from it).
+# Removed tristate and hex functions.
+# Making the hardlink to ../.config only when configure/.config doesn't exist.
+#
+# 280897 (carlo@runaway.xs4all.nl) -
+# Added CONFIG_NEW: Ask if one wants to use this configuration as main
+# configuration.
+# Changed /bin/rm into $RM, so now this environment variable must be set!
+#
+# 290897 (carlo@runaway.xs4all.nl) -
+# Added support for the solaris Born shell.
+# Changed the directory configure/ to config/ in order not to collide with
+# GNU autoconf in the future.
+#
+# 020997 (carlo@runaway.xs4all.nl) -
+# The future is here... renamed 'config.in' to 'config-sh', reserving
+# 'file.in' for input files of autoconfs' 'configure' script.
+# 'autoconf.h' has been renamed to 'config.h', because it would be confusing
+# otherwise with autoconf.
+# Changed Configure to be run from within config/.
+#
+# 050997 (carlo@runaway.xs4all.nl) -
+# Don't use 'def={$old:-$3}' but instead the equivalent ': ${def=$3}' because
+# some sh don't understand the first expression.
+# Pressing a 'c' at any question continues in 'batch' mode.
+#
+# 220199 (carlo@runaway.xs4all.nl) -
+# Put MACRO name at front, making it easier to find it when scanning
+# through the defaults to change just one.
+# Make pressing 'c' stop at every paragraph, and pressing 'C' finish the
+# whole script.
+#
+# -----------------------------------------------------------------------------
+#
+# This script is used to configure the linux kernel.
+#
+# It was inspired by the challenge in the original Configure script
+# to ``do something better'', combined with the actual need to ``do
+# something better'' because the old configure script wasn't flexible
+# enough.
+#
+# Please send comments / questions / bug fixes to raymondc@microsoft.com.
+#
+# ***** IMPORTANT COMPATIBILITY NOTE ****
+# If configuration changes are made which might adversely effect
+# Menuconfig or xconfig, please notify the respective authors so that
+# those utilities can be updated in parallel.
+#
+# Menuconfig: <roadcapw@cfw.com>
+# xconfig: <apenwarr@foxnet.net> <eric@aib.com>
+# ****************************************
+#
+# Each line in the config file is a command.
+#
+# 050793 - use IFS='@' to get around a bug in a pre-version of bash-1.13
+# with an empty IFS.
+#
+# 030995 (storner@osiris.ping.dk) - added support for tri-state answers,
+# for selecting modules to compile.
+#
+# 180995 Bernhard Kaindl (bkaindl@ping.at) - added dummy functions for
+# use with a config.in modified for make menuconfig.
+#
+# 301195 (boldt@math.ucsb.edu) - added help text support
+#
+# 281295 Paul Gortmaker - make tri_state functions collapse to boolean
+# if module support is not enabled.
+#
+# 010296 Aaron Ucko (ucko@vax1.rockhurst.edu) - fix int and hex to accept
+# arbitrary ranges
+#
+# 150296 Dick Streefland (dicks@tasking.nl) - report new configuration
+# items and ask for a value even when doing a "make oldconfig"
+#
+# 200396 Tom Dyas (tdyas@eden.rutgers.edu) - when the module option is
+# chosen for an item, define the macro <option_name>_MODULE
+#
+# 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular
+# expressions for GNU expr since version 1.15 and up use \? and \+.
+
+#### Start of system configuration section. ####
+
+SHELL=@SHPROG@
+RM=@RMPROG@
+LN_S="@LN_S@"
+unet_cv_sys_set_h=@unet_cv_sys_set_h@
+
+#### End of system configuration section. ####
+
+# Disable filename globbing
+set -f
+
+if test $unet_cv_sys_set_h = yes; then
+ set -h
+fi
+
+# Figure out how to do 'echo' without newline:
+c=''
+n=''
+if [ "`eval echo -n 'a'`" = "-n a" ] ; then
+ c='\c'
+else
+ n='-n'
+fi
+
+# Check if ${VAR:-def} works
+DUMMY_VAR=""
+DUMMY_VAR2="some value"
+DUMMY_VAR=${DUMMY_VAR:-test}
+DUMMY_VAR2=${DUMMY_VAR2:-test}
+if [ "$DUMMY_VAR" != "test" -o "$DUMMY_VAR2" != "some value" ]; then
+ echo "You /bin/sh doesn't understand '\${VAR:-default}'"
+ exit 1
+fi
+
+#
+# Dummy functions for use with a config.in modified for menuconf
+#
+mainmenu_option () {
+ :
+}
+mainmenu_name () {
+ :
+}
+endmenu () {
+ :
+}
+
+#
+# help prints the corresponding help text from Configure.help to stdout
+#
+# help variable
+#
+help () {
+ if [ -f ../doc/Configure.help ]
+ then
+ #first escape regexp special characters in the argument:
+ var=`echo "$1"|sed 's/[][\/.^$*]/\\&/g'`
+ #now pick out the right help text:
+ text=`sed -n "/^$var[ ]*\\$/,\\${
+ /^$var[ ]*\\$/b
+ /^#.*/b
+ /^[ ]*\\$/q
+ p
+ }" ../doc/Configure.help`
+ if [ -z "$text" ]
+ then
+ echo; echo " Sorry, no help available for this option yet.";echo
+ else
+ (echo; echo "$text"; echo) | ${PAGER:-more}
+ fi
+ else
+ echo;
+ echo " Can't access the file doc/Configure.help which"
+ echo " should contain the help texts."
+ echo
+ fi
+}
+
+
+#
+# readln reads a line into $ans.
+#
+# readln macro prompt default oldval
+#
+readln () {
+ echo $1 | awk '{ printf("%-20.20s: ", $1); }'
+ if [ "$USE_DEFAULT" != "n" -a -n "$4" ]; then
+ echo $2
+ ans=$3
+ else
+ echo $n "$2$c"
+ [ -z "$4" ] && echo $n "(NEW) $c"
+ IFS='@' read ans </dev/tty || exit 1
+ [ -z "$ans" ] && ans=$3
+ if [ "$ans" = "c" -o "$ans" = "C" ]; then
+ USE_DEFAULT=$ans
+ ans=$3
+ fi
+ fi
+}
+
+#
+# comment does some pretty-printing
+#
+# comment 'xxx'
+#
+comment () {
+ echo ""
+ echo "*-----------------------------------------------------------------------------";
+ echo "* $1" ;
+ echo "*"
+ (echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG
+ (echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H
+ if [ "$USE_DEFAULT" = "c" ]; then
+ USE_DEFAULT=n
+ fi
+}
+
+#
+# define_bool sets the value of a boolean argument
+#
+# define_bool define value
+#
+define_bool () {
+ case "$2" in
+ "y")
+ echo "$1=y" >>$CONFIG
+ echo "#define $1" >>$CONFIG_H
+ ;;
+
+ "n")
+ echo "$1=n" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ ;;
+ esac
+ eval "$1=$2"
+}
+
+#
+# expr determines if string matches a regular expression
+#
+# expr string reg_exp
+#
+expr () {
+ MATCH=`echo $1 | egrep -e $2`
+ if [ -z "$MATCH" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+#
+# bool processes a boolean argument
+#
+# bool question define
+#
+bool () {
+ old=`eval echo "\\${$2}"`
+ if [ -z "$old" -a -n "$3" ]; then
+ def=$3
+ else
+ def=${old:-n}
+ fi
+ case "$def" in
+ "y")
+ defprompt="Y/n/?"
+ def="y"
+ ;;
+
+ "n")
+ defprompt="N/y/?"
+ ;;
+ esac
+ while :; do
+ readln $2 "$1 [$defprompt] " "$def" "$old"
+ case "$ans" in
+ [yY] | [yY]es )
+ define_bool "$2" "y"
+ break
+ ;;
+
+ [nN] | [nN]o )
+ define_bool "$2" "n"
+ break
+ ;;
+
+ * )
+ help "$2"
+ ;;
+ esac
+ done
+}
+
+#
+# define_int sets the value of a integer argument
+#
+# define_int define value
+#
+define_int () {
+ echo "$1=$2" >>$CONFIG
+ echo "#define $1 ($2)" >>$CONFIG_H
+ eval "$1=$2"
+}
+
+#
+# int processes an integer argument
+#
+# int question define default
+#
+int () {
+ old=`eval echo "\\${$2}"`
+ def=${old:-$3}
+ while :; do
+ readln $2 "$1 [$def] " "$def" "$old"
+ if expr "$ans" "^0$|^(-[1-9]|[1-9])[0-9]*$"; then
+ define_int "$2" "$ans"
+ break
+ else
+ help "$2"
+ fi
+ done
+}
+
+#
+# choice processes a choice list (1-out-of-n)
+#
+# choice question choice-list default
+#
+# The choice list has a syntax of:
+# NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE }
+# The user may enter any unique prefix of one of the NAMEs and
+# choice will define VALUE as if it were a boolean option.
+# VALUE must be in all uppercase. Normally, VALUE is of the
+# form CONFIG_<something>. Thus, if the user selects <something>,
+# the CPP symbol CONFIG_<something> will be defined and the
+# shell variable CONFIG_<something> will be set to "y".
+#
+choice () {
+ question="$1"
+ choices="$2"
+ old=
+ def=$3
+
+ # determine default answer:
+ names=""
+ eval set -- $choices
+ firstvar=$2
+ while [ -n "$2" ]; do
+ if [ -n "$names" ]; then
+ names="$names, $1"
+ else
+ names="$1"
+ fi
+ if [ "`eval echo "\\${$2}"`" = "y" ]; then
+ old=$1
+ def=$1
+ fi
+ shift; shift
+ done
+
+ val=""
+ while [ -z "$val" ]; do
+ ambg=n
+ readln $names "$question [$def] " "$def" "$old"
+ ans=`echo $ans | tr a-z A-Z`
+ eval set -- $choices
+ while [ -n "$1" ]; do
+ name=`echo $1 | tr a-z A-Z`
+ case "$name" in
+ "$ans"* )
+ if [ "$name" = "$ans" ]; then
+ val="$2"
+ break # stop on exact match
+ fi
+ if [ -n "$val" ]; then
+ echo;
+ echo \
+ " Sorry, \"$ans\" is ambiguous; please enter a longer string."
+ echo
+ val=""
+ ambg=y
+ break
+ else
+ val="$2"
+ fi
+ ;;
+ esac
+ shift; shift
+ done
+ if [ "$val" = "" -a "$ambg" = "n" ]; then
+ help "$firstvar"
+ fi
+ done
+ eval set -- $choices
+ while [ -n "$2" ]; do
+ if [ "$2" = "$val" ]; then
+ echo " defined $val"
+ define_bool "$2" "y"
+ else
+ define_bool "$2" "n"
+ fi
+ shift; shift
+ done
+}
+
+#
+# define_macro sets the value of a macro argument
+#
+# define_macro define value
+#
+define_macro () {
+ if [ -n "$2" ]; then
+ echo "$1=\"$2\"" >>$CONFIG
+ echo "#define $1 $2" >>$CONFIG_H
+ fi
+ eval "$1=\"$2\""
+}
+
+#
+# define_string sets the value of a string argument
+#
+# define_string define value
+#
+define_string () {
+ if [ -n "$2" ]; then
+ echo "$1=\"$2\"" >>$CONFIG
+ echo "#define $1 \"$2\"" >>$CONFIG_H
+ fi
+ eval "$1=\"$2\""
+}
+
+#
+# string processes a string argument
+#
+# string question define default
+#
+string () {
+ old=`eval echo "\\${$2}"`
+ def=${old:-$3}
+ while :; do
+ readln $2 "$1 [$def] " "$def" "$old"
+ if expr "$ans" "^[^\"].*[^\"]$"; then
+ define_string "$2" "$ans"
+ break
+ else
+ help "$2"
+ fi
+ done
+}
+
+CONFIG=.tmpconfig
+CONFIG_H=.tmpconfig.h
+trap "$RM -f $CONFIG $CONFIG_H ; exit 1" 1 2 15
+
+#
+# Make sure we start out with a clean slate.
+#
+echo "#" > $CONFIG
+echo "# Configuration default generated for:" > $CONFIG
+echo '# `pwd`' > $CONFIG
+echo "#" > $CONFIG
+echo "# Automatically generated by 'make config': don't edit" >> $CONFIG
+echo "#" >> $CONFIG
+
+echo "/*" > $CONFIG_H
+echo " * Automatically generated C config: don't edit" >> $CONFIG_H
+echo " */" >> $CONFIG_H
+echo "#define AUTOCONF_INCLUDED" >> $CONFIG_H
+
+CONFIG_IN=./config-sh
+if [ "$1" != "" ] ; then
+ CONFIG_IN=$1
+fi
+
+DEFAULTS=none
+if [ -r .config ]; then
+ DEFAULTS=./.config
+else
+ if [ -r ../../.config ]; then
+ echo "*"
+ echo "* WARNING: Inheriting .config from previous version!"
+ DEFAULTS=../../.config
+ fi
+fi
+
+if [ -r $DEFAULTS ]; then
+ echo "*"
+ echo "* Using defaults found in" $DEFAULTS
+ echo "* If you type a 'C' the script will finish using all defaults."
+ echo "* If you type a 'c' the script will skip to the next paragraph."
+ echo "*"
+ . $DEFAULTS
+else
+ echo "*"
+ echo "* No defaults found"
+ echo "*"
+fi
+
+. $CONFIG_IN
+
+mv $CONFIG_H config.h
+$RM -f .config.old
+if [ -f .config ]; then
+ # Keep the inode of .config (hardlink to ../../.config) intact:
+ cp .config .config.old
+ cat $CONFIG > .config
+ $RM -f $CONFIG
+fi
+if test ! -f .config -o ! -r ../../.config; then
+ if [ "$CONFIG_BATCH" = "y" ]; then
+ CONFIG_NEW=n
+ else
+ CONFIG_NEW=y
+ USE_DEFAULT=n
+ echo "*"
+ echo "*"
+ echo "*"
+ bool 'Use .config of THIS source tree as your upgrade default' CONFIG_NEW
+ fi
+ if [ ! -f .config ]; then
+ mv $CONFIG .config
+ fi
+ if [ "$CONFIG_NEW" = "y" ]; then
+ CONFIG_CURDIR=`pwd`
+ cd ../..
+ $RM -f .config .config.cache
+ ln $CONFIG_CURDIR/.config .config
+ ln $CONFIG_CURDIR/config.cache .config.cache
+ echo
+ echo "NOTE: Linking ../.config to THIS source tree's configuration !"
+ echo "(The configuration of this source tree will be used as default when you upgrade)"
+ fi
+fi
+
+echo
+
+exit 0
--- /dev/null
+# config/Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+
+# 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, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+SHELL=@SHPROG@
+RM=@RMPROG@
+TOUCH=touch
+@SET_MAKE@
+#### End of system configuration section. ####
+
+# Some versions of make give a warning when this is empty:
+.SUFFIXES: .dummy
+
+# These files need to be up to date when we are building the ircd
+build: config.h setup.h
+
+clean:
+ ${RM} -f .tmpconfig.h .tmpconfig
+
+distclean: clean
+ ${RM} -f .config setup.h stamp-h config.h .config.old \
+ config.cache config.log config.status Makefile config-sh \
+ Configure
+
+maintainer-clean: distclean
+ ${RM} -f aclocal.m4 setup.h.in stamp-h.in configure
+ @echo Generating aclocal.m4...
+ aclocal
+ @echo Generating setup.h.in...
+ autoheader
+ @echo timestamp > stamp-h.in
+ @echo Generating configure...
+ autoconf
+
+config: configure setup.h.in config.status Configure FORCE
+ @CONFIG_BATCH=n ${SHELL} ./Configure || exit 1
+ @# Allow the use of non-GNU make with 'make config':
+ @cd ../ircd; ${MAKE} Makefile
+ @cd ../doc; ${MAKE} Makefile
+
+config.h: Configure config-sh
+ @echo Generating config.h...
+ @CONFIG_BATCH=y ${SHELL} ./Configure || exit 1
+ @cd ../ircd; ${MAKE} Makefile
+
+FORCE:
+
+#==============================================================================
+# Rules to automatically remake Configuration :
+
+Makefile: config.status Makefile.in
+ @echo "recreating config/Makefile"
+ @CONFIG_FILES=Makefile CONFIG_HEADERS= ./config.status > /dev/null
+
+aclocal.m4: acinclude.m4
+ @echo Generating aclocal.m4...
+ @echo aclocal
+ @aclocal || ( echo "NOT recreating 'aclocal.m4', you don't have 'aclocal'"; touch aclocal.m4 )
+
+configure: configure.in aclocal.m4
+ @echo Generating configure...
+ @echo autoconf
+ @autoconf || ( echo "NOT recreating 'configure', you don't have 'autoconf'"; touch configure )
+
+setup.h.in: stamp-h.in
+stamp-h.in: configure.in acconfig.h setup.h.top aclocal.m4 #setup.h.bot
+ @echo Generating setup.h.in...
+ @echo autoheader
+ @autoheader || ( echo "NOT recreating 'setup.h.in', you don't have 'autoheader'"; touch setup.h.in )
+ @echo timestamp > stamp-h.in
+
+setup.h: stamp-h
+stamp-h: stamp-h.in config.status
+ @echo Generating setup.h...
+ @CONFIG_FILES= CONFIG_HEADERS=setup.h ./config.status
+
+config.status: configure
+ @echo Generating config.status...
+ @CONFIG_FILES= CONFIG_HEADERS= ./config.status --recheck || \
+ (./configure && ${TOUCH} ../doc/stamp-m ../ircd/stamp-m)
+
+config-sh: config-sh.in config.status
+ @echo Generating config-sh...
+ @CONFIG_FILES=config-sh CONFIG_HEADERS= ./config.status
+
+Configure: Configure.in config.status
+ @echo Generating Configure...
+ @CONFIG_FILES=Configure CONFIG_HEADERS= ./config.status
--- /dev/null
+This directory contains NOTHING for the admin to configure.
+
+If you want to configure the IRC server, go to the top level directory and
+type: 'make config'.
+
+-------------------------------------------------------------------------------
+
+If you are a coder and are adding a feature to the server that you need
+automatic or manual configuration for, then this is where you need to
+change things.
+
+If you touch any of the following files, then you need to have 'autoconf'
+(and 'autoheader') installed - you might also need GNU m4 and perl then:
+
+configure.in
+acconfig.h
+setup.h.top
+
+Never touch setup.h.in and stamp-h.in.
+Don't run 'make maintainer-clean' when you don't have autoconf etc. installed.
+
+If you touch any of the following files, then you need to have GNU make
+in order to have the Makefiles correctly rebuild automatically:
+
+parse.none
+gen.ircd.Makefile
+../ircd/Makefile.in
+../doc/Makefile.in
+../Makefile.in
+
+Note that after running 'make config' special added code in config/Makefile
+will remake the Makefiles as needed to allow the admins to use a non-GNU make.
+But coders should use GNU make.
+
+Here is a sheme of which files are needed to generate which files:
+
+ /> (config.log)
+ /-> (config.cache)
+configure.in --(autoconf)--> configure --> config.status
+ \
+acconfig.h --(autoheader)--> setup.h.in --(config.status)--> setup.h
+setup.h.top / (stamp-h.in) (stamp-h)
+
+config-sh.in --(config.status)--> config-sh --(Configure)--> config.h
+ (.config) -/ \-> (.config)
+ \> (.config.old)
+Makefile.in --(config.status)--> Makefile
+
+The exectuable scripts (configure, config.status and Configure) should only be
+called from Makefile (automatically, when needed, by means of makefile rules).
+
+Further more, config.status is also used to (re)generate doc/Makefile and
+ircd/Makefile (from respectively doc/Makefile.in and ircd/Makefile.in).
+However, ircd/Makefile is not functional then, first also gen.ircd.Makefile
+must be called. All of this is done automatically by the Makefile rules
+inside doc/Makefile and ircd/Makefile respectively. Finally, gen.ircd.Makefile
+also calls parse.none. So:
+
+../ircd/Makefile.in --\
+gen.ircd.Makefile ----(config.status)--> ../ircd/Makefile
+.config --/
+parse.none -/
+
+../doc/Makefile.in --\
+gen.doc.Makefile ----(config.status)--> ../doc/Makefile
+.config --/
+parse.none -/
+
+../Makefile.in ----(config.status)--> ../Makefile
+
+'Configure' uses the file ../doc/Configure.help for the help texts.
+
+-------------------------------------------------------------------------------
+
+Adding an 'autoconf' check.
+
+1) Install GNU m4, autoconf & autoheader.
+2) Add your check to configure.in, if needed also add an entry to acconfig.h
+3) Add the #ifdefs to the source code.
+
+Adding a manual configuration.
+
+1) Edit config-sh.in and add the question.
+2) Edit ../doc/Configure.help and add the help text !
+3) If necessary, add something to parse.none to allow the use of the
+ value 'none'.
+4) If necessary edit gen.ircd.Makefile and ../ircd/Makefile to get your
+ variable in ../ircd/Makefile. (It will already be in config.h).
+5) Add the #ifdefs to the source code.
+
+In both cases, all needed files will be automatically (re) generated when
+you run 'make' from the top level directory (you need GNU make though).
+
+Carlo Wood
--- /dev/null
+/* Define if you have the resolv library (-lresolv) */
+#undef HAVE_LIB_RESOLV
+
+/* Define if you have the setrlimit function */
+#undef HAVE_SETRLIMIT
+
+/* Define one of these, depending on wether you have
+ POSIX, BSD or SYSV non-blocking stuff */
+#undef NBLOCK_POSIX
+#undef NBLOCK_BSD
+#undef NBLOCK_SYSV
+
+/* Define on of these, depending on wether you have
+ POSIX, BSD or SYSV signal handling */
+#undef POSIX_SIGNALS
+#undef BSD_RELIABLE_SIGNALS
+#undef SYSV_UNRELIABLE_SIGNALS
+
+/* Define these to be unsigned integral internal types,
+ * of respecitvely 2 and 4 bytes in size, when not already
+ * defined in <sys/types.h>, <stdlib.h> or <stddef.h> */
+#undef u_int16_t
+#undef u_int32_t
+
+/* Define this to the printf format for size_t */
+#undef SIZE_T_FMT
+
+/* Define this to the printf format for time_t */
+#undef TIME_T_FMT
+
+/* Define this to the printf signed format for time_t */
+#undef STIME_T_FMT
--- /dev/null
+dnl
+dnl Macro: unet_PIPE_CFLAGS
+dnl
+dnl If the compiler understands -pipe, add it to CFLAGS if not already
+dnl there.
+dnl
+AC_DEFUN(unet_PIPE_CFLAGS,
+[AC_MSG_CHECKING([if the compiler understands -pipe])
+unet_cv_pipe_flags="$ac_cv_prog_gcc"
+if test "$ac_cv_prog_gcc" = no; then
+ OLDCFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -pipe"
+ AC_TRY_COMPILE(,,unet_cv_pipe_flags=yes,)
+ CFLAGS="$OLDCFLAGS"
+fi
+AC_MSG_RESULT($unet_cv_pipe_flags)
+if test "$unet_cv_pipe_flags" = yes ; then
+ x=`echo $CFLAGS | grep 'pipe' 2>/dev/null`
+ if test "$x" = "" ; then
+ CFLAGS="$CFLAGS -pipe"
+ fi
+fi
+])
+
+dnl
+dnl Macro: unet_CHECK_LIB_RESOLV
+dnl
+dnl Check for res_mkquery in -lresolv and add that to LIBS when needed.
+dnl
+AC_DEFUN(unet_CHECK_LIB_RESOLV,
+[AC_CACHE_CHECK([for res_mkquery in -lresolv], unet_cv_lib_resolv,
+[AC_TRY_LINK([struct rrec;
+extern int res_mkquery(int, const char *, int, int, const char *,
+ int, struct rrec *, unsigned char *, int);],
+[int op;
+const char *dname;
+int class, type;
+const char *data;
+int datalen;
+struct rrec *newrr;
+unsigned char *buf;
+int buflen;
+res_mkquery(op,dname,class,type,data,datalen,newrr,buf,buflen)],
+unet_cv_lib_resolv=no, [OLD_LIBS="$LIBS"
+LIBS="$LIBS -lresolv"
+AC_TRY_LINK([extern char *_res;], [*_res=0],
+unet_cv_lib_resolv=yes, unet_cv_lib_resolv=no)
+LIBS="$OLD_LIBS"])])
+if test $unet_cv_lib_resolv = yes; then
+ AC_DEFINE(HAVE_LIB_RESOLV)
+ LIBS="$LIBS -lresolv"
+fi])
+
+dnl
+dnl Macro: unet_NONBLOCKING
+dnl
+dnl Check whether we have posix, bsd or sysv non-blocking sockets and
+dnl define respectively NBLOCK_POSIX, NBLOCK_BSD or NBLOCK_SYSV.
+dnl
+AC_DEFUN(unet_NONBLOCKING,
+[dnl Do we have posix, bsd or sysv non-blocking stuff ?
+AC_CACHE_CHECK([for posix non-blocking], unet_cv_sys_nonblocking_posix,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+ char b[12];
+ struct sockaddr x;
+ size_t l = sizeof(x);
+ int f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f >= 0 && !(fcntl(f, F_SETFL, O_NONBLOCK)))
+ {
+ signal(SIGALRM, alarmed);
+ alarm(2);
+ recvfrom(f, b, 12, 0, &x, &l);
+ alarm(0);
+ exit(0);
+ }
+ exit(1);
+}], unet_cv_sys_nonblocking_posix=yes, unet_cv_sys_nonblocking_posix=no)])
+if test $unet_cv_sys_nonblocking_posix = yes; then
+ AC_DEFINE(NBLOCK_POSIX)
+else
+AC_CACHE_CHECK([for bsd non-blocking], unet_cv_sys_nonblocking_bsd,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+ char b[12];
+ struct sockaddr x;
+ size_t l = sizeof(x);
+ int f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f >= 0 && !(fcntl(f, F_SETFL, O_NDELAY)))
+ {
+ signal(SIGALRM, alarmed);
+ alarm(2);
+ recvfrom(f, b, 12, 0, &x, &l);
+ alarm(0);
+ exit(0);
+ }
+ exit(1);
+}], unet_cv_sys_nonblocking_bsd=yes, unet_cv_sys_nonblocking_bsd=no)])
+if test $unet_cv_sys_nonblocking_bsd = yes; then
+ AC_DEFINE(NBLOCK_BSD)
+else
+ AC_DEFINE(NBLOCK_SYSV)
+fi
+fi])
+
+dnl
+dnl Macro: unet_SIGNALS
+dnl
+dnl Check if we have posix signals, reliable bsd signals or
+dnl unreliable sysv signals and define respectively POSIX_SIGNALS,
+dnl BSD_RELIABLE_SIGNALS or SYSV_UNRELIABLE_SIGNALS.
+dnl
+AC_DEFUN(unet_SIGNALS,
+[dnl Do we have posix signals, reliable bsd signals or unreliable sysv signals ?
+AC_CACHE_CHECK([for posix signals], unet_cv_sys_signal_posix,
+[AC_TRY_COMPILE([#include <signal.h>],
+[sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L)],
+unet_cv_sys_signal_posix=yes, unet_cv_sys_signal_posix=no)])
+if test $unet_cv_sys_signal_posix = yes; then
+ AC_DEFINE(POSIX_SIGNALS)
+else
+AC_CACHE_CHECK([for bsd reliable signals], unet_cv_sys_signal_bsd,
+[AC_TRY_RUN([#include <signal.h>
+int calls = 0;
+$ac_cv_type_signal handler()
+{
+ if (calls) return;
+ calls++;
+ kill(getpid(), SIGTERM);
+ sleep(1);
+}
+int main(void)
+{
+ signal(SIGTERM, handler);
+ kill(getpid(), SIGTERM);
+ exit (0);
+}], unet_cv_sys_signal_bsd=yes, unet_cv_sys_signal_bsd=no)])
+if test $unet_cv_sys_signal_bsd = yes; then
+ AC_DEFINE(BSD_RELIABLE_SIGNALS)
+else
+ AC_DEFINE(SYSV_UNRELIABLE_SIGNALS)
+fi
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_SIZE_T_FMT
+dnl
+dnl Define SIZE_T_FMT to be "%u" or "%lu", whichever seems more appropriate.
+dnl
+AC_DEFUN(unet_DEFINE_SIZE_T_FMT,
+[dnl Make educated guess :/, if size_t is a long or not
+AC_CHECK_SIZEOF(size_t)dnl
+AC_MSG_CHECKING(printf format of size_t)
+if test "$ac_cv_sizeof_size_t" = 4 ; then
+ AC_MSG_RESULT("%u")
+ AC_DEFINE(SIZE_T_FMT, "%u")
+else
+ AC_MSG_RESULT("%lu")
+ AC_DEFINE(SIZE_T_FMT, "%lu")
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_TIME_T_FMT
+dnl
+dnl Try to figure out if time_t is an int or not, and if so define
+dnl TIME_T_FMT to be "%u", otherwise define it to be "%lu".
+dnl Likewise define STIME_T_FMT for the signed format.
+dnl
+AC_DEFUN(unet_DEFINE_TIME_T_FMT,
+[dnl Make educated guess :/, if time_t is a long or not
+AC_MSG_CHECKING(size of time_t)
+AC_CACHE_VAL(unet_cv_sizeof_time_t,
+[AC_TRY_RUN([#include <stdio.h>
+#include <sys/types.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(time_t));
+ exit(0);
+}], unet_cv_sizeof_time_t=`cat conftestval`,
+unet_cv_sizeof_time_t=0, unet_cv_sizeof_time_t=0)])
+if test "$unet_cv_sizeof_time_t" = 0 ; then
+ AC_MSG_RESULT(unknown)
+ AC_DEFINE(TIME_T_FMT, "%lu")
+ AC_DEFINE(STIME_T_FMT, "%ld")
+else
+ AC_MSG_RESULT([$unet_cv_sizeof_time_t])
+ AC_MSG_CHECKING(printf format of time_t)
+ if test "$unet_cv_sizeof_time_t" = "$ac_cv_sizeof_long" ; then
+ AC_MSG_RESULT("%lu")
+ AC_DEFINE(TIME_T_FMT, "%lu")
+ AC_DEFINE(STIME_T_FMT, "%ld")
+ else
+ AC_MSG_RESULT("%u")
+ AC_DEFINE(TIME_T_FMT, "%u")
+ AC_DEFINE(STIME_T_FMT, "%d")
+ fi
+fi])
+
+dnl
+dnl Macro: unet_CHECK_TYPE_SIZES
+dnl
+dnl Check the size of several types and define a valid int16_t and int32_t.
+dnl
+AC_DEFUN(unet_CHECK_TYPE_SIZES,
+[dnl Check type sizes
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+if test "$ac_cv_sizeof_int" = 2 ; then
+ AC_CHECK_TYPE(int16_t, int)
+ AC_CHECK_TYPE(u_int16_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 2 ; then
+ AC_CHECK_TYPE(int16_t, short)
+ AC_CHECK_TYPE(u_int16_t, unsigned short)
+else
+ AC_MSG_ERROR([Cannot find a type with size of 16 bits])
+fi
+if test "$ac_cv_sizeof_int" = 4 ; then
+ AC_CHECK_TYPE(int32_t, int)
+ AC_CHECK_TYPE(u_int32_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 4 ; then
+ AC_CHECK_TYPE(int32_t, short)
+ AC_CHECK_TYPE(u_int32_t, unsigned short)
+elif test "$ac_cv_sizeof_long" = 4 ; then
+ AC_CHECK_TYPE(int32_t, long)
+ AC_CHECK_TYPE(u_int32_t, unsigned long)
+else
+ AC_MSG_ERROR([Cannot find a type with size of 32 bits])
+fi])
+
+dnl
+dnl Macro: unet_FUNC_POLL_SYSCALL
+dnl
+dnl Try to figure out if we have a system call poll (not if it is emulated).
+dnl Manical laughter...
+dnl
+AC_DEFUN(unet_FUNC_POLL_SYSCALL,
+[AC_CHECK_HEADERS(poll.h)dnl
+if test -z "$unet_cv_func_poll_syscall" ; then
+ AC_MSG_CHECKING([if poll is a system call (please wait)])
+else
+ AC_MSG_CHECKING([if poll is a system call])
+fi
+AC_CACHE_VAL(unet_cv_func_poll_syscall,
+[unet_cv_func_poll_syscall=no
+dnl No need to go through the trouble when we don't have poll.h:
+changequote(, )dnl
+if test "$ac_cv_header_poll_h" = yes; then
+ unet_dirs=`find /usr/include/sys -type f -name '*.h' -exec egrep '^#include <[^/]*/.*>' {} \; | sed -e 's/^.*<//' -e 's%/.*$%%' | sort | uniq`
+ for i in $unet_dirs ; do
+ if test "$unet_cv_func_poll_syscall" = no ; then
+ unet_files=`ls /usr/include/$i/*.h 2> /dev/null`
+ if test -n "$unet_files" ; then
+ for j in $unet_files ; do
+ if test "$unet_cv_func_poll_syscall" = no ; then
+ unet_line=`egrep '^#define[[:space:]]+[[:alnum:]_]*[Pp][Oo][Ll][Ll]' $j`
+ if test -n "$unet_line" ; then
+ unet_sig=`echo "$unet_line" | sed -e 's/poll/fork/g' -e 's/POLL/FORK/g' -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g'`
+ unet_set=`for k in "$unet_sig" ; do echo $k; done | sed -e 's% %|%g'`
+ unet_match=`sed -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g' $j | egrep "$unet_set"`
+ if test -n "$unet_match" ; then
+ unet_cv_func_poll_syscall=yes
+ fi
+ fi
+ fi
+ done
+ fi
+ fi
+ done
+fi
+changequote([, ])dnl
+])
+AC_MSG_RESULT([$unet_cv_func_poll_syscall])
+])
--- /dev/null
+dnl aclocal.m4 generated automatically by aclocal 1.4a
+
+dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+dnl PARTICULAR PURPOSE.
+
+dnl
+dnl Macro: unet_PIPE_CFLAGS
+dnl
+dnl If the compiler understands -pipe, add it to CFLAGS if not already
+dnl there.
+dnl
+AC_DEFUN(unet_PIPE_CFLAGS,
+[AC_MSG_CHECKING([if the compiler understands -pipe])
+unet_cv_pipe_flags="$ac_cv_prog_gcc"
+if test "$ac_cv_prog_gcc" = no; then
+ OLDCFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -pipe"
+ AC_TRY_COMPILE(,,unet_cv_pipe_flags=yes,)
+ CFLAGS="$OLDCFLAGS"
+fi
+AC_MSG_RESULT($unet_cv_pipe_flags)
+if test "$unet_cv_pipe_flags" = yes ; then
+ x=`echo $CFLAGS | grep 'pipe' 2>/dev/null`
+ if test "$x" = "" ; then
+ CFLAGS="$CFLAGS -pipe"
+ fi
+fi
+])
+
+dnl
+dnl Macro: unet_CHECK_LIB_RESOLV
+dnl
+dnl Check for res_mkquery in -lresolv and add that to LIBS when needed.
+dnl
+AC_DEFUN(unet_CHECK_LIB_RESOLV,
+[AC_CACHE_CHECK([for res_mkquery in -lresolv], unet_cv_lib_resolv,
+[AC_TRY_LINK([struct rrec;
+extern int res_mkquery(int, const char *, int, int, const char *,
+ int, struct rrec *, unsigned char *, int);],
+[int op;
+const char *dname;
+int class, type;
+const char *data;
+int datalen;
+struct rrec *newrr;
+unsigned char *buf;
+int buflen;
+res_mkquery(op,dname,class,type,data,datalen,newrr,buf,buflen)],
+unet_cv_lib_resolv=no, [OLD_LIBS="$LIBS"
+LIBS="$LIBS -lresolv"
+AC_TRY_LINK([extern char *_res;], [*_res=0],
+unet_cv_lib_resolv=yes, unet_cv_lib_resolv=no)
+LIBS="$OLD_LIBS"])])
+if test $unet_cv_lib_resolv = yes; then
+ AC_DEFINE(HAVE_LIB_RESOLV)
+ LIBS="$LIBS -lresolv"
+fi])
+
+dnl
+dnl Macro: unet_NONBLOCKING
+dnl
+dnl Check whether we have posix, bsd or sysv non-blocking sockets and
+dnl define respectively NBLOCK_POSIX, NBLOCK_BSD or NBLOCK_SYSV.
+dnl
+AC_DEFUN(unet_NONBLOCKING,
+[dnl Do we have posix, bsd or sysv non-blocking stuff ?
+AC_CACHE_CHECK([for posix non-blocking], unet_cv_sys_nonblocking_posix,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+ char b[12];
+ struct sockaddr x;
+ size_t l = sizeof(x);
+ int f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f >= 0 && !(fcntl(f, F_SETFL, O_NONBLOCK)))
+ {
+ signal(SIGALRM, alarmed);
+ alarm(2);
+ recvfrom(f, b, 12, 0, &x, &l);
+ alarm(0);
+ exit(0);
+ }
+ exit(1);
+}], unet_cv_sys_nonblocking_posix=yes, unet_cv_sys_nonblocking_posix=no)])
+if test $unet_cv_sys_nonblocking_posix = yes; then
+ AC_DEFINE(NBLOCK_POSIX)
+else
+AC_CACHE_CHECK([for bsd non-blocking], unet_cv_sys_nonblocking_bsd,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+ char b[12];
+ struct sockaddr x;
+ size_t l = sizeof(x);
+ int f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f >= 0 && !(fcntl(f, F_SETFL, O_NDELAY)))
+ {
+ signal(SIGALRM, alarmed);
+ alarm(2);
+ recvfrom(f, b, 12, 0, &x, &l);
+ alarm(0);
+ exit(0);
+ }
+ exit(1);
+}], unet_cv_sys_nonblocking_bsd=yes, unet_cv_sys_nonblocking_bsd=no)])
+if test $unet_cv_sys_nonblocking_bsd = yes; then
+ AC_DEFINE(NBLOCK_BSD)
+else
+ AC_DEFINE(NBLOCK_SYSV)
+fi
+fi])
+
+dnl
+dnl Macro: unet_SIGNALS
+dnl
+dnl Check if we have posix signals, reliable bsd signals or
+dnl unreliable sysv signals and define respectively POSIX_SIGNALS,
+dnl BSD_RELIABLE_SIGNALS or SYSV_UNRELIABLE_SIGNALS.
+dnl
+AC_DEFUN(unet_SIGNALS,
+[dnl Do we have posix signals, reliable bsd signals or unreliable sysv signals ?
+AC_CACHE_CHECK([for posix signals], unet_cv_sys_signal_posix,
+[AC_TRY_COMPILE([#include <signal.h>],
+[sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L)],
+unet_cv_sys_signal_posix=yes, unet_cv_sys_signal_posix=no)])
+if test $unet_cv_sys_signal_posix = yes; then
+ AC_DEFINE(POSIX_SIGNALS)
+else
+AC_CACHE_CHECK([for bsd reliable signals], unet_cv_sys_signal_bsd,
+[AC_TRY_RUN([#include <signal.h>
+int calls = 0;
+$ac_cv_type_signal handler()
+{
+ if (calls) return;
+ calls++;
+ kill(getpid(), SIGTERM);
+ sleep(1);
+}
+int main(void)
+{
+ signal(SIGTERM, handler);
+ kill(getpid(), SIGTERM);
+ exit (0);
+}], unet_cv_sys_signal_bsd=yes, unet_cv_sys_signal_bsd=no)])
+if test $unet_cv_sys_signal_bsd = yes; then
+ AC_DEFINE(BSD_RELIABLE_SIGNALS)
+else
+ AC_DEFINE(SYSV_UNRELIABLE_SIGNALS)
+fi
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_SIZE_T_FMT
+dnl
+dnl Define SIZE_T_FMT to be "%u" or "%lu", whichever seems more appropriate.
+dnl
+AC_DEFUN(unet_DEFINE_SIZE_T_FMT,
+[dnl Make educated guess :/, if size_t is a long or not
+AC_CHECK_SIZEOF(size_t)dnl
+AC_MSG_CHECKING(printf format of size_t)
+if test "$ac_cv_sizeof_size_t" = 4 ; then
+ AC_MSG_RESULT("%u")
+ AC_DEFINE(SIZE_T_FMT, "%u")
+else
+ AC_MSG_RESULT("%lu")
+ AC_DEFINE(SIZE_T_FMT, "%lu")
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_TIME_T_FMT
+dnl
+dnl Try to figure out if time_t is an int or not, and if so define
+dnl TIME_T_FMT to be "%u", otherwise define it to be "%lu".
+dnl Likewise define STIME_T_FMT for the signed format.
+dnl
+AC_DEFUN(unet_DEFINE_TIME_T_FMT,
+[dnl Make educated guess :/, if time_t is a long or not
+AC_MSG_CHECKING(size of time_t)
+AC_CACHE_VAL(unet_cv_sizeof_time_t,
+[AC_TRY_RUN([#include <stdio.h>
+#include <sys/types.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(time_t));
+ exit(0);
+}], unet_cv_sizeof_time_t=`cat conftestval`,
+unet_cv_sizeof_time_t=0, unet_cv_sizeof_time_t=0)])
+if test "$unet_cv_sizeof_time_t" = 0 ; then
+ AC_MSG_RESULT(unknown)
+ AC_DEFINE(TIME_T_FMT, "%lu")
+ AC_DEFINE(STIME_T_FMT, "%ld")
+else
+ AC_MSG_RESULT([$unet_cv_sizeof_time_t])
+ AC_MSG_CHECKING(printf format of time_t)
+ if test "$unet_cv_sizeof_time_t" = "$ac_cv_sizeof_long" ; then
+ AC_MSG_RESULT("%lu")
+ AC_DEFINE(TIME_T_FMT, "%lu")
+ AC_DEFINE(STIME_T_FMT, "%ld")
+ else
+ AC_MSG_RESULT("%u")
+ AC_DEFINE(TIME_T_FMT, "%u")
+ AC_DEFINE(STIME_T_FMT, "%d")
+ fi
+fi])
+
+dnl
+dnl Macro: unet_CHECK_TYPE_SIZES
+dnl
+dnl Check the size of several types and define a valid int16_t and int32_t.
+dnl
+AC_DEFUN(unet_CHECK_TYPE_SIZES,
+[dnl Check type sizes
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+if test "$ac_cv_sizeof_int" = 2 ; then
+ AC_CHECK_TYPE(int16_t, int)
+ AC_CHECK_TYPE(u_int16_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 2 ; then
+ AC_CHECK_TYPE(int16_t, short)
+ AC_CHECK_TYPE(u_int16_t, unsigned short)
+else
+ AC_MSG_ERROR([Cannot find a type with size of 16 bits])
+fi
+if test "$ac_cv_sizeof_int" = 4 ; then
+ AC_CHECK_TYPE(int32_t, int)
+ AC_CHECK_TYPE(u_int32_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 4 ; then
+ AC_CHECK_TYPE(int32_t, short)
+ AC_CHECK_TYPE(u_int32_t, unsigned short)
+elif test "$ac_cv_sizeof_long" = 4 ; then
+ AC_CHECK_TYPE(int32_t, long)
+ AC_CHECK_TYPE(u_int32_t, unsigned long)
+else
+ AC_MSG_ERROR([Cannot find a type with size of 32 bits])
+fi])
+
+dnl
+dnl Macro: unet_FUNC_POLL_SYSCALL
+dnl
+dnl Try to figure out if we have a system call poll (not if it is emulated).
+dnl Manical laughter...
+dnl
+AC_DEFUN(unet_FUNC_POLL_SYSCALL,
+[AC_CHECK_HEADERS(poll.h)dnl
+if test -z "$unet_cv_func_poll_syscall" ; then
+ AC_MSG_CHECKING([if poll is a system call (please wait)])
+else
+ AC_MSG_CHECKING([if poll is a system call])
+fi
+AC_CACHE_VAL(unet_cv_func_poll_syscall,
+[unet_cv_func_poll_syscall=no
+dnl No need to go through the trouble when we don't have poll.h:
+changequote(, )dnl
+if test "$ac_cv_header_poll_h" = yes; then
+ unet_dirs=`find /usr/include/sys -type f -name '*.h' -exec egrep '^#include <[^/]*/.*>' {} \; | sed -e 's/^.*<//' -e 's%/.*$%%' | sort | uniq`
+ for i in $unet_dirs ; do
+ if test "$unet_cv_func_poll_syscall" = no ; then
+ unet_files=`ls /usr/include/$i/*.h 2> /dev/null`
+ if test -n "$unet_files" ; then
+ for j in $unet_files ; do
+ if test "$unet_cv_func_poll_syscall" = no ; then
+ unet_line=`egrep '^#define[[:space:]]+[[:alnum:]_]*[Pp][Oo][Ll][Ll]' $j`
+ if test -n "$unet_line" ; then
+ unet_sig=`echo "$unet_line" | sed -e 's/poll/fork/g' -e 's/POLL/FORK/g' -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g'`
+ unet_set=`for k in "$unet_sig" ; do echo $k; done | sed -e 's% %|%g'`
+ unet_match=`sed -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g' $j | egrep "$unet_set"`
+ if test -n "$unet_match" ; then
+ unet_cv_func_poll_syscall=yes
+ fi
+ fi
+ fi
+ done
+ fi
+ fi
+ done
+fi
+changequote([, ])dnl
+])
+AC_MSG_RESULT([$unet_cv_func_poll_syscall])
+])
+
+
+# serial 1
+
+# @defmac AC_PROG_CC_STDC
+# @maindex PROG_CC_STDC
+# @ovindex CC
+# If the C compiler in not in ANSI C mode by default, try to add an option
+# to output variable @code{CC} to make it so. This macro tries various
+# options that select ANSI C on some system or another. It considers the
+# compiler to be in ANSI C mode if it handles function prototypes correctly.
+#
+# If you use this macro, you should check after calling it whether the C
+# compiler has been set to accept ANSI C; if not, the shell variable
+# @code{am_cv_prog_cc_stdc} is set to @samp{no}. If you wrote your source
+# code in ANSI C, you can make an un-ANSIfied copy of it by using the
+# program @code{ansi2knr}, which comes with Ghostscript.
+# @end defmac
+
+AC_DEFUN(AM_PROG_CC_STDC,
+[AC_REQUIRE([AC_PROG_CC])
+AC_BEFORE([$0], [AC_C_INLINE])
+AC_BEFORE([$0], [AC_C_CONST])
+dnl Force this before AC_PROG_CPP. Some cpp's, eg on HPUX, require
+dnl a magic option to avoid problems with ANSI preprocessor commands
+dnl like #elif.
+dnl FIXME: can't do this because then AC_AIX won't work due to a
+dnl circular dependency.
+dnl AC_BEFORE([$0], [AC_PROG_CPP])
+AC_MSG_CHECKING(for ${CC-cc} option to accept ANSI C)
+AC_CACHE_VAL(am_cv_prog_cc_stdc,
+[am_cv_prog_cc_stdc=no
+ac_save_CC="$CC"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ AC_TRY_COMPILE(
+[#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+], [
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+],
+[am_cv_prog_cc_stdc="$ac_arg"; break])
+done
+CC="$ac_save_CC"
+])
+if test -z "$am_cv_prog_cc_stdc"; then
+ AC_MSG_RESULT([none needed])
+else
+ AC_MSG_RESULT($am_cv_prog_cc_stdc)
+fi
+case "x$am_cv_prog_cc_stdc" in
+ x|xno) ;;
+ *) CC="$CC $am_cv_prog_cc_stdc" ;;
+esac
+])
+
--- /dev/null
+# @configure_input@
+#
+# By Carlo Wood (carlo@runaway.xs4all.nl)
+#
+# For a description of the syntax of this configuration file,
+# see the config/Configure script.
+#
+# TO CONFIGURE THE SERVER, TYPE: 'make config' in the top level directory !
+#
+
+#### Start of system configuration section. ####
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+mandir=@mandir@
+CC_DEFAULT="@CC@"
+CFLAGS_DEFAULT="@CFLAGS@"
+CPPFLAGS_DEFAULT="@CPPFLAGS@"
+LDFLAGS_DEFAULT="@LDFLAGS@"
+LIBS="@LIBS@"
+ac_cv_header_poll_h=@ac_cv_header_poll_h@
+unet_cv_func_poll_syscall=@unet_cv_func_poll_syscall@
+ac_cv_header_syslog_h=@ac_cv_header_syslog_h@
+
+#### End of system configuration section. ####
+
+if [ -z "$CONFIG_BATCH" ]; then
+ echo "**************************************************************************"
+ echo "Error: Please run 'make config' that resides in the top level directory!"
+ echo "**************************************************************************"
+ exit -1
+fi
+
+mainmenu_name "Undernet IRC Daemon Configuration"
+mainmenu_option next_comment
+ if [ "$CONFIG_BATCH" = "n" ]; then
+ echo "* Welcome to the Undernet IRC Daemon Configuration script."
+ echo "* You can restart this configuration at any time by typing 'make config'."
+ echo "* If you need help with your decision on any question, type a '?'"
+ echo "*"
+ USE_DEFAULT=n
+ else
+ USE_DEFAULT=C
+ fi
+ CHANGE_CONFIG=n
+ if [ "$DEFAULTS" != "none" ]; then
+ bool 'Do you want to change your previous configuration' CHANGE_CONFIG
+ if [ "$CHANGE_CONFIG" = "n" ]; then
+ echo "Ok, I will only prompt you for NEW parameters."
+ USE_DEFAULT=C
+ else
+ USE_DEFAULT=n
+ fi
+ else
+ USE_DEFAULT=n
+ fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Compile stuff'
+ if [ "$prefix" = "NONE" ]; then
+ prefix=/usr/local
+ fi
+ if [ "$exec_prefix" = "NONE" ]; then
+ eval exec_prefix="$prefix"
+ fi
+ string 'Which compiler do you want to use' CC "$CC_DEFAULT"
+ echo "* For the following four questions, specify 'none' when you want it to be empty."
+ if [ -z "$cflags_O3_remark" -a -n "$CFLAGS" ]; then
+ CFLAGS_DEFAULT=`echo "$CFLAGS" | sed -e 's%-O2%-O3%'`
+ if [ "$CFLAGS_DEFAULT" != "$CFLAGS" ]; then
+ echo "You are highly advised to use -O3 instead of -O2 if you're short in cpu cycles!"
+ echo "Please read documentation (press '?'):"
+ CFLAGS=
+ fi
+ fi
+ eval string "'What flags should I pass to $CC [none]'" CFLAGS "'$CFLAGS_DEFAULT'"
+ define_macro cflags_O3_remark done
+ string 'Do you need extra include directories [none]' EXTRA_INCLUDEDIRS none
+ if [ -z "$LDFLAGS_DEFAULT" ]; then
+ LDFLAGS_DEFAULT=none
+ else
+ eval LDFLAGS_DEFAULT="$LDFLAGS_DEFAULT"
+ fi
+ string 'Which linker flags do you need [none]' LDFLAGS "$LDFLAGS_DEFAULT"
+ if [ -z "$LIBS" ]; then
+ LIBS=none
+ fi
+ string 'Which extra libraries do you need [none]' IRCDLIBS "$LIBS"
+ eval bindir="$bindir"
+ string 'In which directory should I install the ircd binary' BINDIR $bindir
+ if [ ! -d "$BINDIR" ]; then
+ echo "$BINDIR : No such directory"
+ fi
+ string 'What should the name of the installed symbolic link to the exectuable be' SYMLINK ircd
+ string 'Which permissions do you want the binary to have' IRCDMODE 711
+ string 'Which owner do you want the binary to have' IRCDOWN "`id | sed -e 's/.*uid=[0-9]*(//' -e 's/).*//' 2> /dev/null`"
+ string 'Which group do you want the binary to have' IRCDGRP "`id | sed -e 's/.*gid=[0-9]*(//' -e 's/).*//' 2> /dev/null`"
+ eval mandir=$mandir
+ string 'Where should I install the man page' MANDIR $mandir
+ if [ "$CFLAGS" = "none" ]; then
+ CFLAGS=""
+ fi
+ if [ "$EXTRA_INCLUDEDIRS" = "none" ]; then
+ EXTRA_INCLUDEDIRS=""
+ fi
+ if [ "$LDFLAGS" = "none" ]; then
+ LDFLAGS=""
+ fi
+ if [ "$IRCDLIBS" = "none" ]; then
+ IRCDLIBS=""
+ fi
+ EXTRA_CPPFLAGS=""
+ if [ -n "$EXTRA_INCLUDEDIRS" ]; then
+ for i in $EXTRA_INCLUDEDIRS; do
+ if [ -z "$EXTRA_CPPFLAGS" ]; then
+ EXTRA_CPPFLAGS=-I$i
+ else
+ EXTRA_CPPFLAGS="$EXTRA_CPPFLAGS -I$i"
+ fi
+ done
+ fi
+ if [ -z "$EXTRA_CPPFLAGS" ]; then
+ CPPFLAGS=-I../include
+ else
+ CPPFLAGS="-I../include $EXTRA_CPPFLAGS"
+ fi
+ echo "EXTRA_CPPFLAGS=\"$EXTRA_CPPFLAGS\"" >>$CONFIG
+ echo "CPPFLAGS=\"$CPPFLAGS\"" >>$CONFIG
+ bool 'Use inlining for a few crucial functions' FORCEINLINE y
+endmenu
+
+if [ "$ac_cv_header_poll_h" = "yes" ]; then
+ if [ "$unet_cv_func_poll_syscall" = "yes" ]; then
+ define_bool USE_POLL y
+ else
+ mainmenu_option next_comment
+ comment 'Operating System specific defines.'
+ bool 'You have poll(), but do you want to use it' USE_POLL n
+ endmenu
+ fi
+fi
+
+mainmenu_option next_comment
+comment 'Host specific defines'
+ if [ -f /etc/resolv.conf ]; then
+ DOMAINNAME_DEFAULT="`awk '/^domain/ { print $2; exit }' /etc/resolv.conf`"
+ fi
+ string 'What is the domain name of your network' DOMAINNAME $DOMAINNAME_DEFAULT
+ if [ -z "$DOMAINNAME" ]; then
+ DOMAINNAME=none
+ fi
+ string 'Please give a random seed of eight characters' RANDOM_SEED 12345678
+ bool 'Does your host have a reliable clock' RELIABLE_CLOCK
+endmenu
+
+mainmenu_option next_comment
+comment 'General defines'
+ bool 'Change root ('/') after start of daemon' CHROOTDIR
+ bool 'Do you want the daemon set its own uid/gid' CONFIG_SETUGID
+ if [ "$CONFIG_SETUGID" = "y" ]; then
+ int ' UID of irc daemon' IRC_UID
+ int ' GID of irc daemon' IRC_GID
+ else
+ define_int IRC_UID $IRC_UID
+ define_int IRC_GID $IRC_GID
+ bool 'Allow to specify configuration file on command line' CMDLINE_CONFIG
+ if [ "$CMDLINE_CONFIG" = "y" ]; then
+ echo " SECURITY: Then don't install the daemon SUID or SGID !"
+ fi
+ fi
+ bool 'Set up a Unix domain socket to connect clients/servers' UNIXPORT
+ bool 'Do you need virtual hosting' VIRTUAL_HOST
+ PREV_HUB=$HUB
+ bool 'Will you connect to more then one server at a time' HUB
+ if [ "$PREV_HUB" != "$HUB" ]; then
+ BUFFERPOOL=
+ fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Debugging (do not define this on production servers)'
+ bool 'Do you want to enable debugging output' DEBUGMODE
+ bool 'Do you want memory- allocation and/or leak checking' DEBUGMALLOC
+ if [ "$DEBUGMALLOC" = "y" ]; then
+ bool 'Do you want to have boundary checking' MEMMAGICNUMS
+ bool 'Do you want memory leak testing (stats M)' MEMLEAKSTATS y
+ if [ "$MEMLEAKSTATS" = "y" ]; then
+ if [ "$MEMMAGICNUMS" = "y" ]; then
+ echo "You will have extra info on allocated sizes too (MEMSIZESTATS)"
+ define_bool MEMSIZESTATS $MEMSIZESTATS
+ else
+ bool 'Do you want extra info on allocated sizes' MEMSIZESTATS y
+ fi
+ bool 'Do you want support for a time interval with /stats M' MEMTIMESTATS y
+ fi
+ else
+ define_bool MEMMAGICNUMS $MEMMAGICNUMS
+ define_bool MEMLEAKSTATS $MEMLEAKSTATS
+ define_bool MEMSIZESTATS $MEMSIZESTATS
+ define_bool MEMTIMESTATS $MEMTIMESTATS
+ fi
+ bool 'Are you testing on a host without DNS' NODNS
+endmenu
+
+mainmenu_option next_comment
+comment 'Paths and files'
+ eval DPATH_DEFAULT="${prefix}/lib/ircd"
+ string 'Directory where all ircd stuff resides' DPATH $DPATH_DEFAULT
+ define_string SPATH "$BINDIR/ircd"
+ echo "The following filenames are either full paths or files within DPATH"
+ string 'Server configuration file' CPATH 'ircd.conf'
+ string 'Server MOTD file' MPATH 'ircd.motd'
+ string 'Server remote MOTD file (3 lines max)' RPATH 'remote.motd'
+ if [ "$DEBUGMODE" = "y" ]; then
+ string 'Debug file if DEBUGMODE' LPATH '/tmp/ircd.log'
+ else
+ define_string LPATH "$LPATH"
+ fi
+ string 'File for server pid' PPATH 'ircd.pid'
+endmenu
+
+mainmenu_option next_comment
+comment 'Logging (filenames are either full paths or files within DPATH)'
+ bool 'Do you want to log the use of /WHO x% (recommended)' CONFIG_LOG_WHOX y
+ if [ "$CONFIG_LOG_WHOX" = "y" ]; then
+ string ' Give the path and(or) filename of this log file' WPATH 'whox.log'
+ fi
+ bool 'Do you want to log G-lines to a separate file' CONFIG_LOG_GLINES
+ if [ "$CONFIG_LOG_GLINES" = "y" ]; then
+ string ' Give the path and(or) filename of this log file' GPATH 'gline.log'
+ fi
+ bool 'Do you want to log connecting users to a separate file' CONFIG_LOG_USERS
+ if [ "$CONFIG_LOG_USERS" = "y" ]; then
+ string ' Give the path and(or) filename of this log file' FNAME_USERLOG $DPATH/users
+ fi
+ bool 'Do you want to log Opers to a separate file' CONFIG_LOG_OPERS
+ if [ "$CONFIG_LOG_OPERS" = "y" ]; then
+ string ' Give the path and(or) filename of this log file' FNAME_OPERLOG $DPATH/opers
+ fi
+ if [ "$ac_cv_header_syslog_h" = "yes" ]; then
+ bool 'Do you want to use syslog' USE_SYSLOG
+ else
+ USE_SYSLOG=n
+ fi
+ if [ "$USE_SYSLOG" = "y" ]; then
+ bool ' Log all operator kills to syslog' SYSLOG_KILL
+ bool ' Log all remote squits for all servers to syslog' SYSLOG_SQUIT
+ bool ' Log remote connect messages for other all servs' SYSLOG_CONNECT
+ bool ' Log all users who successfully become an Oper' SYSLOG_OPER
+ bool ' Send userlog stuff to syslog' SYSLOG_USERS
+ if [ "$SYSLOG_KILL" = "n" -a "$SYSLOG_SQUIT" = "n" -a \
+ "$SYSLOG_CONNECT" = "n" -a "$SYSLOG_OPER" = "n" -a \
+ "$SYSLOG_USERS" = "n" ]; then
+ define_macro LOG_FACILITY $LOG_FACILITY
+ define_bool USE_SYSLOG n
+ else
+ choice ' Log facility' \
+ "daemon CONFIG_DAEMON \
+ user CONFIG_USER \
+ local0-7 CONFIG_LOCAL" daemon
+ if [ "$CONFIG_DAEMON" = "y" ]; then
+ define_macro LOG_FACILITY LOG_DAEMON
+ else
+ if [ "$CONFIG_USER" = "y" ]; then
+ define_macro LOG_FACILITY LOG_USER
+ else
+ int ' Which local facility (0-7)' INT_LOCAL
+ define_macro LOG_FACILITY LOG_LOCAL$INT_LOCAL
+ fi
+ fi
+ echo " Using log facility $LOG_FACILITY"
+ fi
+ else
+ define_bool SYSLOG_KILL $SYSLOG_KILL
+ define_bool SYSLOG_SQUIT $SYSLOG_SQUIT
+ define_bool SYSLOG_CONNECT $SYSLOG_CONNECT
+ define_bool SYSLOG_OPER $SYSLOG_OPER
+ define_bool SYSLOG_USERS $SYSLOG_USERS
+ define_macro LOG_FACILITY $LOG_FACILITY
+ fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Configuration'
+ bool 'Use crypted passwords for N: lines' CRYPT_LINK_PASSWORD y
+ bool 'Use crypted passwords for operators' CRYPT_OPER_PASSWORD y
+ DUMMY=`echo "$BUFFERPOOL" | sed -e 's/[0-9]//g'`
+ if [ "$DUMMY" != "" ]; then
+ BUFFERPOOL=
+ fi
+ if [ "$HUB" = "y" ]; then
+ int 'Max size of the total of of all sendqs (bytes)' BUFFERPOOL 27000000
+ else
+ int 'Max size of the total of of all sendqs (bytes)' BUFFERPOOL 9000000
+ fi
+ int 'Max receive queue for clients (bytes)' CLIENT_FLOOD 1024
+ int 'Maximum number of network connections (23 - (FD_SETSIZE-4))' MAXCONNECTIONS 252
+ int 'Default client listen port' PORTNUM 6667
+ int 'Nickname history length' NICKNAMEHISTORYLENGTH 800
+ bool 'Allow Opers to see (dis)connects of local clients' ALLOW_SNO_CONNEXIT
+ if [ "$ALLOW_SNO_CONNEXIT" = "y" ]; then
+ bool 'Show IP address in client connection notices' SNO_CONNEXIT_IP
+ fi
+ bool 'Do you want to use R: lines in your configuration file' R_LINES
+ if [ "$R_LINES" = "y" ]; then
+ bool 'Process R: lines every rehash' R_LINES_REHASH y
+ bool 'Process R: lines always' R_LINES_OFTEN
+ else
+ define_bool R_LINES_REHASH $R_LINES_REHASH
+ define_bool R_LINES_OFTEN $R_LINES_OFTEN
+ fi
+ bool 'Do you want support for the old I:*:ONE:*:: construct (read help text!)' USEONE n
+ bool 'Send a short message instead of the MOTD to connecting clients' NODEFAULTMOTD y
+endmenu
+
+mainmenu_option next_comment
+comment 'Oper commands'
+ bool 'Allow (local) Opers to see all local invisible users' SHOW_INVISIBLE_USERS y
+ if [ "$SHOW_INVISIBLE_USERS" = "y" ]; then
+ bool 'Allow Opers to see all invisible users' SHOW_ALL_INVISIBLE_USERS y
+ fi
+ bool 'Allow global Opers (O:) to see inside secret channels' OPERS_SEE_IN_SECRET_CHANNELS y
+ if [ "$OPERS_SEE_IN_SECRET_CHANNELS" = "y" ]; then
+ bool 'Allow local Opers (o:) to see inside secret channels' LOCOP_SEE_IN_SECRET_CHANNELS n
+ fi
+ bool 'Do not truncate obnoxiously long /who output for opers' UNLIMIT_OPER_QUERY
+ bool 'Allow Opers to use the KILL command' OPER_KILL y
+ bool 'Allow Opers to use the REHASH command' OPER_REHASH y
+ bool 'Allow Opers to use the RESTART command' OPER_RESTART y
+ bool 'Allow Opers to use the DIE command' OPER_DIE y
+ bool 'Allow Opers to add local G-lines' OPER_LGLINE y
+ bool 'Allow Opers to connect from a remote site' OPER_REMOTE y
+ bool 'Allow local opers to use the REHASH command' LOCOP_REHASH y
+ bool 'Allow local opers to use the RESTART command' LOCOP_RESTART
+ bool 'Allow local opers to use the DIE command' LOCOP_DIE
+ bool 'Allow local opers to add local G-lines' LOCOP_LGLINE y
+endmenu
+
+mainmenu_option next_comment
+comment 'Server characteristics'
+ bool 'Do you want to have a default LIST parameter' CONFIG_LIST y
+ if [ "$CONFIG_LIST" = "y" ]; then
+ string 'Give default LIST parameter' DEFAULT_LIST 'T<10'
+ define_string DEFAULT_LIST_PARAM "$DEFAULT_LIST"
+ else
+ define_string DEFAULT_LIST "$DEFAULT_LIST"
+ define_bool DEFAULT_LIST_PARAM n
+ fi
+ bool 'K: line comments treated as a file by default' COMMENT_IS_FILE y
+ bool 'Only nullify idle-time on PRIVMSG' IDLE_FROM_MSG y
+endmenu
+
+mainmenu_option next_comment
+comment 'Mandatory defines (you should leave these untouched)'
+ int 'Max auto connects per class (1!)' MAXIMUM_LINKS 1
+ echo '* Never define this on a production server:'
+ bool 'Enable message logging' MSGLOG_ENABLED
+ if [ "$MSGLOG_ENABLED" = "y" ]; then
+ int 'Message log size' MSGLOG_SIZE 128
+ fi
+ if [ "$OPER_KILL" = "y" ]; then
+ bool 'Only allow KILLs of local clients' LOCAL_KILL_ONLY
+ else
+ define_bool LOCAL_KILL_ONLY $LOCAL_KILL_ONLY
+ fi
+ int 'KILL nick chase time limit (30)' KILLCHASETIMELIMIT 30
+ int 'Max number of channels per user (recommended: 5)' MAXCHANNELSPERUSER 10
+ int 'Max number of silence masks (15!)' MAXSILES 15
+ int 'Expected average banmask length (40!)' AVBANLEN 40
+ eval define_macro MAXSILELENGTH \'\($AVBANLEN * MAXSILES\)\'
+ echo '* These are default values, used for class 0:'
+ int 'Max server idle time (60)' TIMESEC 60
+ int 'Class 0 ping frequency (120)' PINGFREQUENCY 120
+ int 'Class 0 connect frequency (600)' CONNECTFREQUENCY 600
+ int 'Min time before a link is good (300)' HANGONGOODLINK 300
+ int 'Wait before reconnecting to good link (10!)' HANGONRETRYDELAY 10
+ int 'connect(2) timeout (90!)' CONNECTTIMEOUT 90
+ int 'Max send queue (40000)' DEFAULTMAXSENDQLENGTH 40000
+endmenu
+
--- /dev/null
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=config-sh.in
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_aux_dir=
+for ac_dir in . $srcdir/.; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in . $srcdir/." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+
+
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:553: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:583: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:634: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:666: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 677 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:682: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:708: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:713: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:722: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:741: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:774: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 789 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:795: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 806 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:812: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 823 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:829: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for AIX""... $ac_c" 1>&6
+echo "configure:854: checking for AIX" >&5
+cat > conftest.$ac_ext <<EOF
+#line 856 "configure"
+#include "confdefs.h"
+#ifdef _AIX
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF
+#define _ALL_SOURCE 1
+EOF
+
+else
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+
+
+echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6
+echo "configure:878: checking for POSIXized ISC" >&5
+if test -d /etc/conf/kconfig.d &&
+ grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1
+then
+ echo "$ac_t""yes" 1>&6
+ ISC=yes # If later tests want to check for ISC.
+ cat >> confdefs.h <<\EOF
+#define _POSIX_SOURCE 1
+EOF
+
+ if test "$GCC" = yes; then
+ CC="$CC -posix"
+ else
+ CC="$CC -Xp"
+ fi
+else
+ echo "$ac_t""no" 1>&6
+ ISC=
+fi
+
+ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6
+echo "configure:900: checking for minix/config.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 905 "configure"
+#include "confdefs.h"
+#include <minix/config.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:910: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ MINIX=yes
+else
+ echo "$ac_t""no" 1>&6
+MINIX=
+fi
+
+if test "$MINIX" = yes; then
+ cat >> confdefs.h <<\EOF
+#define _POSIX_SOURCE 1
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define _POSIX_1_SOURCE 2
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define _MINIX 1
+EOF
+
+fi
+
+
+
+
+echo $ac_n "checking for ${CC-cc} option to accept ANSI C""... $ac_c" 1>&6
+echo "configure:951: checking for ${CC-cc} option to accept ANSI C" >&5
+if eval "test \"`echo '$''{'am_cv_prog_cc_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ am_cv_prog_cc_stdc=no
+ac_save_CC="$CC"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ cat > conftest.$ac_ext <<EOF
+#line 967 "configure"
+#include "confdefs.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+
+int main() {
+
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+
+; return 0; }
+EOF
+if { (eval echo configure:1004: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ am_cv_prog_cc_stdc="$ac_arg"; break
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+CC="$ac_save_CC"
+
+fi
+
+if test -z "$am_cv_prog_cc_stdc"; then
+ echo "$ac_t""none needed" 1>&6
+else
+ echo "$ac_t""$am_cv_prog_cc_stdc" 1>&6
+fi
+case "x$am_cv_prog_cc_stdc" in
+ x|xno) ;;
+ *) CC="$CC $am_cv_prog_cc_stdc" ;;
+esac
+
+if test "$CFLAGS" != "" ; then
+ CFLAGS=`echo "$CFLAGS" | sed -e 's/-O2/-O3/'`
+fi
+if test "$CFLAGS" != "" ; then
+ CFLAGS=`echo "$CFLAGS" | sed -e 's/-pipe//g'`
+fi
+
+echo $ac_n "checking for crypt in -lc""... $ac_c" 1>&6
+echo "configure:1035: checking for crypt in -lc" >&5
+ac_lib_var=`echo c'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lc $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1043 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1054: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ true
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for crypt in -ldescrypt""... $ac_c" 1>&6
+echo "configure:1073: checking for crypt in -ldescrypt" >&5
+ac_lib_var=`echo descrypt'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ldescrypt $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1081 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1092: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="-ldescrypt $LIBS"
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6
+echo "configure:1111: checking for crypt in -lcrypt" >&5
+ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lcrypt $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1119 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1130: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo crypt | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lcrypt $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+fi
+
+echo $ac_n "checking for gethostbyname in -lc""... $ac_c" 1>&6
+echo "configure:1162: checking for gethostbyname in -lc" >&5
+ac_lib_var=`echo c'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lc $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1170 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:1181: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ true
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for gethostbyaddr in -lnsl""... $ac_c" 1>&6
+echo "configure:1200: checking for gethostbyaddr in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyaddr | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lnsl $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1208 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char gethostbyaddr();
+
+int main() {
+gethostbyaddr()
+; return 0; }
+EOF
+if { (eval echo configure:1219: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="-lnsl $LIBS"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for socket in -lc""... $ac_c" 1>&6
+echo "configure:1242: checking for socket in -lc" >&5
+ac_lib_var=`echo c'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lc $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1250 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:1261: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ true
+else
+ echo "$ac_t""no" 1>&6
+echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:1280: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lsocket $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1288 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:1299: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ LIBS="-lsocket $LIBS"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for res_mkquery in -lresolv""... $ac_c" 1>&6
+echo "configure:1322: checking for res_mkquery in -lresolv" >&5
+if eval "test \"`echo '$''{'unet_cv_lib_resolv'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1327 "configure"
+#include "confdefs.h"
+struct rrec;
+extern int res_mkquery(int, const char *, int, int, const char *,
+ int, struct rrec *, unsigned char *, int);
+int main() {
+int op;
+const char *dname;
+int class, type;
+const char *data;
+int datalen;
+struct rrec *newrr;
+unsigned char *buf;
+int buflen;
+res_mkquery(op,dname,class,type,data,datalen,newrr,buf,buflen)
+; return 0; }
+EOF
+if { (eval echo configure:1344: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ unet_cv_lib_resolv=no
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ OLD_LIBS="$LIBS"
+LIBS="$LIBS -lresolv"
+cat > conftest.$ac_ext <<EOF
+#line 1354 "configure"
+#include "confdefs.h"
+extern char *_res;
+int main() {
+*_res=0
+; return 0; }
+EOF
+if { (eval echo configure:1361: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ unet_cv_lib_resolv=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ unet_cv_lib_resolv=no
+fi
+rm -f conftest*
+LIBS="$OLD_LIBS"
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$unet_cv_lib_resolv" 1>&6
+if test $unet_cv_lib_resolv = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_LIB_RESOLV 1
+EOF
+
+ LIBS="$LIBS -lresolv"
+fi
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1386: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1391 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1399: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ ac_cv_header_stdc=yes
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1416 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "memchr" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1434 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "free" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+ :
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1455 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1466: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ :
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+ cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6
+echo "configure:1490: checking for sys/wait.h that is POSIX.1 compatible" >&5
+if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1495 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+int main() {
+int s;
+wait (&s);
+s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+; return 0; }
+EOF
+if { (eval echo configure:1511: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6
+if test $ac_cv_header_sys_wait_h = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_SYS_WAIT_H 1
+EOF
+
+fi
+
+for ac_hdr in malloc.h sys/malloc.h fcntl.h string.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h memory.h errno.h net/errno.h sys/cdefs.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1535: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1540 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1545: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:1573: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1578 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this. */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1627: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_c_const=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+ cat >> confdefs.h <<\EOF
+#define const
+EOF
+
+fi
+
+echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
+echo "configure:1648: checking whether byte ordering is bigendian" >&5
+if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+cat > conftest.$ac_ext <<EOF
+#line 1655 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1666: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+cat > conftest.$ac_ext <<EOF
+#line 1670 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1681: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_c_bigendian=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_c_bigendian=no
+fi
+rm -f conftest*
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+if test $ac_cv_c_bigendian = unknown; then
+if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1701 "configure"
+#include "confdefs.h"
+main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+if { (eval echo configure:1714: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_c_bigendian=no
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_c_bigendian=yes
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_c_bigendian" 1>&6
+if test $ac_cv_c_bigendian = yes; then
+ cat >> confdefs.h <<\EOF
+#define WORDS_BIGENDIAN 1
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:1738: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1743 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_size_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+ cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:1771: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1776 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:1785: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_header_time=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+ cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6
+echo "configure:1806: checking whether struct tm is in sys/time.h or time.h" >&5
+if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1811 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <time.h>
+int main() {
+struct tm *tp; tp->tm_sec;
+; return 0; }
+EOF
+if { (eval echo configure:1819: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_struct_tm=time.h
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_struct_tm" 1>&6
+if test $ac_cv_struct_tm = sys/time.h; then
+ cat >> confdefs.h <<\EOF
+#define TM_IN_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6
+echo "configure:1840: checking for uid_t in sys/types.h" >&5
+if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1845 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "uid_t" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_uid_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_type_uid_t" 1>&6
+if test $ac_cv_type_uid_t = no; then
+ cat >> confdefs.h <<\EOF
+#define uid_t int
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define gid_t int
+EOF
+
+fi
+
+echo $ac_n "checking size of short""... $ac_c" 1>&6
+echo "configure:1874: checking size of short" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_short'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1882 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(short));
+ exit(0);
+}
+EOF
+if { (eval echo configure:1893: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_short=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_short=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_short" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_SHORT $ac_cv_sizeof_short
+EOF
+
+
+echo $ac_n "checking size of int""... $ac_c" 1>&6
+echo "configure:1913: checking size of int" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1921 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(int));
+ exit(0);
+}
+EOF
+if { (eval echo configure:1932: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_int=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_int=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+EOF
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+echo "configure:1952: checking size of long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1960 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(long));
+ exit(0);
+}
+EOF
+if { (eval echo configure:1971: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_long=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+if test "$ac_cv_sizeof_int" = 2 ; then
+ echo $ac_n "checking for int16_t""... $ac_c" 1>&6
+echo "configure:1992: checking for int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int16_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1997 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_int16_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int16_t" 1>&6
+if test $ac_cv_type_int16_t = no; then
+ cat >> confdefs.h <<\EOF
+#define int16_t int
+EOF
+
+fi
+
+ echo $ac_n "checking for u_int16_t""... $ac_c" 1>&6
+echo "configure:2025: checking for u_int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int16_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2030 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])u_int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_u_int16_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_u_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int16_t" 1>&6
+if test $ac_cv_type_u_int16_t = no; then
+ cat >> confdefs.h <<\EOF
+#define u_int16_t unsigned int
+EOF
+
+fi
+
+elif test "$ac_cv_sizeof_short" = 2 ; then
+ echo $ac_n "checking for int16_t""... $ac_c" 1>&6
+echo "configure:2059: checking for int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int16_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2064 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_int16_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int16_t" 1>&6
+if test $ac_cv_type_int16_t = no; then
+ cat >> confdefs.h <<\EOF
+#define int16_t short
+EOF
+
+fi
+
+ echo $ac_n "checking for u_int16_t""... $ac_c" 1>&6
+echo "configure:2092: checking for u_int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int16_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2097 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])u_int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_u_int16_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_u_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int16_t" 1>&6
+if test $ac_cv_type_u_int16_t = no; then
+ cat >> confdefs.h <<\EOF
+#define u_int16_t unsigned short
+EOF
+
+fi
+
+else
+ { echo "configure: error: Cannot find a type with size of 16 bits" 1>&2; exit 1; }
+fi
+if test "$ac_cv_sizeof_int" = 4 ; then
+ echo $ac_n "checking for int32_t""... $ac_c" 1>&6
+echo "configure:2129: checking for int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2134 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_int32_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int32_t" 1>&6
+if test $ac_cv_type_int32_t = no; then
+ cat >> confdefs.h <<\EOF
+#define int32_t int
+EOF
+
+fi
+
+ echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+echo "configure:2162: checking for u_int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2167 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])u_int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_u_int32_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_u_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int32_t" 1>&6
+if test $ac_cv_type_u_int32_t = no; then
+ cat >> confdefs.h <<\EOF
+#define u_int32_t unsigned int
+EOF
+
+fi
+
+elif test "$ac_cv_sizeof_short" = 4 ; then
+ echo $ac_n "checking for int32_t""... $ac_c" 1>&6
+echo "configure:2196: checking for int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2201 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_int32_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int32_t" 1>&6
+if test $ac_cv_type_int32_t = no; then
+ cat >> confdefs.h <<\EOF
+#define int32_t short
+EOF
+
+fi
+
+ echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+echo "configure:2229: checking for u_int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2234 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])u_int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_u_int32_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_u_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int32_t" 1>&6
+if test $ac_cv_type_u_int32_t = no; then
+ cat >> confdefs.h <<\EOF
+#define u_int32_t unsigned short
+EOF
+
+fi
+
+elif test "$ac_cv_sizeof_long" = 4 ; then
+ echo $ac_n "checking for int32_t""... $ac_c" 1>&6
+echo "configure:2263: checking for int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2268 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_int32_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int32_t" 1>&6
+if test $ac_cv_type_int32_t = no; then
+ cat >> confdefs.h <<\EOF
+#define int32_t long
+EOF
+
+fi
+
+ echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+echo "configure:2296: checking for u_int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2301 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "(^|[^a-zA-Z_0-9])u_int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_type_u_int32_t=yes
+else
+ rm -rf conftest*
+ ac_cv_type_u_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int32_t" 1>&6
+if test $ac_cv_type_u_int32_t = no; then
+ cat >> confdefs.h <<\EOF
+#define u_int32_t unsigned long
+EOF
+
+fi
+
+else
+ { echo "configure: error: Cannot find a type with size of 32 bits" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking size of size_t""... $ac_c" 1>&6
+echo "configure:2333: checking size of size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_size_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2341 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(size_t));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2352: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sizeof_size_t=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sizeof_size_t=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_size_t" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t
+EOF
+
+echo $ac_n "checking printf format of size_t""... $ac_c" 1>&6
+echo "configure:2371: checking printf format of size_t" >&5
+if test "$ac_cv_sizeof_size_t" = 4 ; then
+ echo "$ac_t"""%u"" 1>&6
+ cat >> confdefs.h <<\EOF
+#define SIZE_T_FMT "%u"
+EOF
+
+else
+ echo "$ac_t"""%lu"" 1>&6
+ cat >> confdefs.h <<\EOF
+#define SIZE_T_FMT "%lu"
+EOF
+
+fi
+echo $ac_n "checking size of time_t""... $ac_c" 1>&6
+echo "configure:2386: checking size of time_t" >&5
+if eval "test \"`echo '$''{'unet_cv_sizeof_time_t'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ unet_cv_sizeof_time_t=0
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2394 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+#include <sys/types.h>
+main()
+{
+ FILE *f=fopen("conftestval", "w");
+ if (!f) exit(1);
+ fprintf(f, "%d\n", sizeof(time_t));
+ exit(0);
+}
+EOF
+if { (eval echo configure:2406: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ unet_cv_sizeof_time_t=`cat conftestval`
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ unet_cv_sizeof_time_t=0
+fi
+rm -fr conftest*
+fi
+
+fi
+
+if test "$unet_cv_sizeof_time_t" = 0 ; then
+ echo "$ac_t""unknown" 1>&6
+ cat >> confdefs.h <<\EOF
+#define TIME_T_FMT "%lu"
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define STIME_T_FMT "%ld"
+EOF
+
+else
+ echo "$ac_t""$unet_cv_sizeof_time_t" 1>&6
+ echo $ac_n "checking printf format of time_t""... $ac_c" 1>&6
+echo "configure:2433: checking printf format of time_t" >&5
+ if test "$unet_cv_sizeof_time_t" = "$ac_cv_sizeof_long" ; then
+ echo "$ac_t"""%lu"" 1>&6
+ cat >> confdefs.h <<\EOF
+#define TIME_T_FMT "%lu"
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define STIME_T_FMT "%ld"
+EOF
+
+ else
+ echo "$ac_t"""%u"" 1>&6
+ cat >> confdefs.h <<\EOF
+#define TIME_T_FMT "%u"
+EOF
+
+ cat >> confdefs.h <<\EOF
+#define STIME_T_FMT "%d"
+EOF
+
+ fi
+fi
+
+if test $ac_cv_prog_gcc = yes; then
+ echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6
+echo "configure:2459: checking whether ${CC-cc} needs -traditional" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_pattern="Autoconf.*'x'"
+ cat > conftest.$ac_ext <<EOF
+#line 2465 "configure"
+#include "confdefs.h"
+#include <sgtty.h>
+Autoconf TIOCGETP
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+else
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat > conftest.$ac_ext <<EOF
+#line 2483 "configure"
+#include "confdefs.h"
+#include <termio.h>
+Autoconf TCGETA
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "$ac_pattern" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+echo $ac_n "checking for 8-bit clean memcmp""... $ac_c" 1>&6
+echo "configure:2505: checking for 8-bit clean memcmp" >&5
+if eval "test \"`echo '$''{'ac_cv_func_memcmp_clean'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_func_memcmp_clean=no
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2513 "configure"
+#include "confdefs.h"
+
+main()
+{
+ char c0 = 0x40, c1 = 0x80, c2 = 0x81;
+ exit(memcmp(&c0, &c2, 1) < 0 && memcmp(&c1, &c2, 1) < 0 ? 0 : 1);
+}
+
+EOF
+if { (eval echo configure:2523: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_func_memcmp_clean=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_func_memcmp_clean=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_func_memcmp_clean" 1>&6
+test $ac_cv_func_memcmp_clean = no && LIBOBJS="$LIBOBJS memcmp.${ac_objext}"
+
+echo $ac_n "checking whether setvbuf arguments are reversed""... $ac_c" 1>&6
+echo "configure:2541: checking whether setvbuf arguments are reversed" >&5
+if eval "test \"`echo '$''{'ac_cv_func_setvbuf_reversed'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2549 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+/* If setvbuf has the reversed format, exit 0. */
+main () {
+ /* This call has the arguments reversed.
+ A reversed system may check and see that the address of main
+ is not _IOLBF, _IONBF, or _IOFBF, and return nonzero. */
+ if (setvbuf(stdout, _IOLBF, (char *) main, BUFSIZ) != 0)
+ exit(1);
+ putc('\r', stdout);
+ exit(0); /* Non-reversed systems segv here. */
+}
+EOF
+if { (eval echo configure:2563: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_func_setvbuf_reversed=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_func_setvbuf_reversed=no
+fi
+rm -fr conftest*
+fi
+
+rm -f core core.* *.core
+fi
+
+echo "$ac_t""$ac_cv_func_setvbuf_reversed" 1>&6
+if test $ac_cv_func_setvbuf_reversed = yes; then
+ cat >> confdefs.h <<\EOF
+#define SETVBUF_REVERSED 1
+EOF
+
+fi
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:2587: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2592 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:2609: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ ac_cv_type_signal=void
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:2628: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2633 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char vprintf(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2656: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:2680: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2685 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char _doprnt(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2708: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+for ac_func in strchr memcpy memmove
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2735: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2740 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2763: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in gethostname gettimeofday mkdir strerror strtoken
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2790: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2795 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2818: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in select socket uname
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2845: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2850 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2873: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in setrlimit inet_netof getrusage times res_init
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2900: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2905 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2928: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+for ac_hdr in poll.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2957: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 2962 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2967: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+if test -z "$unet_cv_func_poll_syscall" ; then
+ echo $ac_n "checking if poll is a system call (please wait)""... $ac_c" 1>&6
+echo "configure:2994: checking if poll is a system call (please wait)" >&5
+else
+ echo $ac_n "checking if poll is a system call""... $ac_c" 1>&6
+echo "configure:2997: checking if poll is a system call" >&5
+fi
+if eval "test \"`echo '$''{'unet_cv_func_poll_syscall'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ unet_cv_func_poll_syscall=no
+if test "$ac_cv_header_poll_h" = yes; then
+ unet_dirs=`find /usr/include/sys -type f -name '*.h' -exec egrep '^#include <[^/]*/.*>' {} \; | sed -e 's/^.*<//' -e 's%/.*$%%' | sort | uniq`
+ for i in $unet_dirs ; do
+ if test "$unet_cv_func_poll_syscall" = no ; then
+ unet_files=`ls /usr/include/$i/*.h 2> /dev/null`
+ if test -n "$unet_files" ; then
+ for j in $unet_files ; do
+ if test "$unet_cv_func_poll_syscall" = no ; then
+ unet_line=`egrep '^#define[[:space:]]+[[:alnum:]_]*[Pp][Oo][Ll][Ll]' $j`
+ if test -n "$unet_line" ; then
+ unet_sig=`echo "$unet_line" | sed -e 's/poll/fork/g' -e 's/POLL/FORK/g' -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g'`
+ unet_set=`for k in "$unet_sig" ; do echo $k; done | sed -e 's% %|%g'`
+ unet_match=`sed -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g' $j | egrep "$unet_set"`
+ if test -n "$unet_match" ; then
+ unet_cv_func_poll_syscall=yes
+ fi
+ fi
+ fi
+ done
+ fi
+ fi
+ done
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_func_poll_syscall" 1>&6
+
+
+echo $ac_n "checking for restartable system calls""... $ac_c" 1>&6
+echo "configure:3033: checking for restartable system calls" >&5
+if eval "test \"`echo '$''{'ac_cv_sys_restartable_syscalls'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3041 "configure"
+#include "confdefs.h"
+/* Exit 0 (true) if wait returns something other than -1,
+ i.e. the pid of the child, which means that wait was restarted
+ after getting the signal. */
+#include <sys/types.h>
+#include <signal.h>
+ucatch (isig) { }
+main () {
+ int i = fork (), status;
+ if (i == 0) { sleep (3); kill (getppid (), SIGINT); sleep (3); exit (0); }
+ signal (SIGINT, ucatch);
+ status = wait(&i);
+ if (status == -1) wait(&i);
+ exit (status == -1);
+}
+
+EOF
+if { (eval echo configure:3059: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_sys_restartable_syscalls=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_sys_restartable_syscalls=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_sys_restartable_syscalls" 1>&6
+if test $ac_cv_sys_restartable_syscalls = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_RESTARTABLE_SYSCALLS 1
+EOF
+
+fi
+
+
+for ac_prog in mawk gawk nawk awk
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:3087: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AWK="$ac_prog"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+AWK="$ac_cv_prog_AWK"
+if test -n "$AWK"; then
+ echo "$ac_t""$AWK" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$AWK" && break
+done
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:3117: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&6
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:3155: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
+echo "configure:3208: checking whether ln -s works" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ rm -f conftestdata
+if ln -s X conftestdata 2>/dev/null
+then
+ rm -f conftestdata
+ ac_cv_prog_LN_S="ln -s"
+else
+ ac_cv_prog_LN_S=ln
+fi
+fi
+LN_S="$ac_cv_prog_LN_S"
+if test "$ac_cv_prog_LN_S" = "ln -s"; then
+ echo "$ac_t""yes" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+for ac_prog in rm
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:3233: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_RMPROG'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$RMPROG" in
+ /*)
+ ac_cv_path_RMPROG="$RMPROG" # Let the user override the test with a path.
+ ;;
+ ?:/*)
+ ac_cv_path_RMPROG="$RMPROG" # Let the user override the test with a dos path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_RMPROG="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac
+fi
+RMPROG="$ac_cv_path_RMPROG"
+if test -n "$RMPROG"; then
+ echo "$ac_t""$RMPROG" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$RMPROG" && break
+done
+test -n "$RMPROG" || RMPROG="/bin/rm"
+
+for ac_prog in sh
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:3274: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_SHPROG'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$SHPROG" in
+ /*)
+ ac_cv_path_SHPROG="$SHPROG" # Let the user override the test with a path.
+ ;;
+ ?:/*)
+ ac_cv_path_SHPROG="$SHPROG" # Let the user override the test with a dos path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_SHPROG="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac
+fi
+SHPROG="$ac_cv_path_SHPROG"
+if test -n "$SHPROG"; then
+ echo "$ac_t""$SHPROG" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+test -n "$SHPROG" && break
+done
+test -n "$SHPROG" || SHPROG="/bin/sh"
+
+
+echo $ac_n "checking for set -h""... $ac_c" 1>&6
+echo "configure:3312: checking for set -h" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_set_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo "set -h; exit $?" > conftest ;
+$SHPROG ./conftest 2> conftest.out
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ unet_cv_sys_set_h=yes
+else
+ unet_cv_sys_set_h=no
+fi
+$RMPROG -fr conftest*
+fi
+
+echo "$ac_t""$unet_cv_sys_set_h" 1>&6
+
+
+echo $ac_n "checking for posix non-blocking""... $ac_c" 1>&6
+echo "configure:3331: checking for posix non-blocking" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_nonblocking_posix'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3339 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+ char b[12];
+ struct sockaddr x;
+ size_t l = sizeof(x);
+ int f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f >= 0 && !(fcntl(f, F_SETFL, O_NONBLOCK)))
+ {
+ signal(SIGALRM, alarmed);
+ alarm(2);
+ recvfrom(f, b, 12, 0, &x, &l);
+ alarm(0);
+ exit(0);
+ }
+ exit(1);
+}
+EOF
+if { (eval echo configure:3365: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ unet_cv_sys_nonblocking_posix=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ unet_cv_sys_nonblocking_posix=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_sys_nonblocking_posix" 1>&6
+if test $unet_cv_sys_nonblocking_posix = yes; then
+ cat >> confdefs.h <<\EOF
+#define NBLOCK_POSIX 1
+EOF
+
+else
+echo $ac_n "checking for bsd non-blocking""... $ac_c" 1>&6
+echo "configure:3387: checking for bsd non-blocking" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_nonblocking_bsd'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3395 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+ char b[12];
+ struct sockaddr x;
+ size_t l = sizeof(x);
+ int f = socket(AF_INET, SOCK_DGRAM, 0);
+ if (f >= 0 && !(fcntl(f, F_SETFL, O_NDELAY)))
+ {
+ signal(SIGALRM, alarmed);
+ alarm(2);
+ recvfrom(f, b, 12, 0, &x, &l);
+ alarm(0);
+ exit(0);
+ }
+ exit(1);
+}
+EOF
+if { (eval echo configure:3421: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ unet_cv_sys_nonblocking_bsd=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ unet_cv_sys_nonblocking_bsd=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_sys_nonblocking_bsd" 1>&6
+if test $unet_cv_sys_nonblocking_bsd = yes; then
+ cat >> confdefs.h <<\EOF
+#define NBLOCK_BSD 1
+EOF
+
+else
+ cat >> confdefs.h <<\EOF
+#define NBLOCK_SYSV 1
+EOF
+
+fi
+fi
+echo $ac_n "checking for posix signals""... $ac_c" 1>&6
+echo "configure:3449: checking for posix signals" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_signal_posix'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3454 "configure"
+#include "confdefs.h"
+#include <signal.h>
+int main() {
+sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L)
+; return 0; }
+EOF
+if { (eval echo configure:3461: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ unet_cv_sys_signal_posix=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ unet_cv_sys_signal_posix=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$unet_cv_sys_signal_posix" 1>&6
+if test $unet_cv_sys_signal_posix = yes; then
+ cat >> confdefs.h <<\EOF
+#define POSIX_SIGNALS 1
+EOF
+
+else
+echo $ac_n "checking for bsd reliable signals""... $ac_c" 1>&6
+echo "configure:3481: checking for bsd reliable signals" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_signal_bsd'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+ cat > conftest.$ac_ext <<EOF
+#line 3489 "configure"
+#include "confdefs.h"
+#include <signal.h>
+int calls = 0;
+$ac_cv_type_signal handler()
+{
+ if (calls) return;
+ calls++;
+ kill(getpid(), SIGTERM);
+ sleep(1);
+}
+int main(void)
+{
+ signal(SIGTERM, handler);
+ kill(getpid(), SIGTERM);
+ exit (0);
+}
+EOF
+if { (eval echo configure:3507: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ unet_cv_sys_signal_bsd=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ unet_cv_sys_signal_bsd=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_sys_signal_bsd" 1>&6
+if test $unet_cv_sys_signal_bsd = yes; then
+ cat >> confdefs.h <<\EOF
+#define BSD_RELIABLE_SIGNALS 1
+EOF
+
+else
+ cat >> confdefs.h <<\EOF
+#define SYSV_UNRELIABLE_SIGNALS 1
+EOF
+
+fi
+fi
+
+echo $ac_n "checking if the compiler understands -pipe""... $ac_c" 1>&6
+echo "configure:3536: checking if the compiler understands -pipe" >&5
+unet_cv_pipe_flags="$ac_cv_prog_gcc"
+if test "$ac_cv_prog_gcc" = no; then
+ OLDCFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -pipe"
+ cat > conftest.$ac_ext <<EOF
+#line 3542 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:3549: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ unet_cv_pipe_flags=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+ CFLAGS="$OLDCFLAGS"
+fi
+echo "$ac_t""$unet_cv_pipe_flags" 1>&6
+if test "$unet_cv_pipe_flags" = yes ; then
+ x=`echo $CFLAGS | grep 'pipe' 2>/dev/null`
+ if test "$x" = "" ; then
+ CFLAGS="$CFLAGS -pipe"
+ fi
+fi
+
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "config-sh Configure ../Makefile ../ircd/Makefile ../doc/Makefile Makefile setup.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@LIBOBJS@%$LIBOBJS%g
+s%@AWK@%$AWK%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@LN_S@%$LN_S%g
+s%@RMPROG@%$RMPROG%g
+s%@SHPROG@%$SHPROG%g
+s%@unet_cv_sys_set_h@%$unet_cv_sys_set_h%g
+s%@ac_cv_header_poll_h@%$ac_cv_header_poll_h%g
+s%@ac_cv_header_syslog_h@%$ac_cv_header_syslog_h%g
+s%@unet_cv_func_poll_syscall@%$unet_cv_func_poll_syscall%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"config-sh Configure ../Makefile ../ircd/Makefile ../doc/Makefile Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="setup.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+echo timestamp > stamp-h;
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
--- /dev/null
+dnl Process this file with autoconf to produce a configure script.
+dnl
+dnl Copyright (c) 1997, by Carlo Wood <carlo@runaway.xs4all.nl>
+
+dnl Make sure we are in the correct directory (someone could have run
+dnl 'configure' with a wrong '--srcdir'); Note that a bug in autoconf
+dnl forces us to have srcdir == dir where configure resides (config):
+AC_INIT(config-sh.in)
+
+dnl 'configure' must be run from within 'config/'.
+AC_CONFIG_AUX_DIR(.)
+
+dnl Define the input and output configuration header file,
+dnl (Generate config/setup.h from config/setup.h.in):
+AC_CONFIG_HEADER(setup.h)
+
+dnl Demand at least version 2.13 of autoconf
+AC_PREREQ(2.13)
+
+dnl This should be done early.
+AC_PROG_CC
+
+dnl UNIX Variants
+dnl Allow the use of BSD functions on AIX.
+AC_AIX
+dnl Allow the use of POSIX functions on several OS.
+AC_ISC_POSIX
+AC_MINIX
+dnl ANSIfy the C compiler whenever possible.
+AM_PROG_CC_STDC
+dnl Use -O3 instead of -O2.
+if test "$CFLAGS" != "" ; then
+ CFLAGS=`echo "$CFLAGS" | sed -e 's/-O2/-O3/'`
+fi
+dnl Remove -pipe during configure
+if test "$CFLAGS" != "" ; then
+ CFLAGS=`echo "$CFLAGS" | sed -e 's/-pipe//g'`
+fi
+
+dnl Checks for libraries.
+AC_CHECK_LIB(c, crypt, [true],
+ AC_CHECK_LIB(descrypt, crypt, LIBS="-ldescrypt $LIBS",
+ AC_CHECK_LIB(crypt, crypt)))
+AC_CHECK_LIB(c, gethostbyname, [true],
+ AC_CHECK_LIB(nsl, gethostbyaddr, LIBS="-lnsl $LIBS"))
+dnl IRIX has -lsocket, but doesn't need it.
+AC_CHECK_LIB(c, socket, [true],
+ AC_CHECK_LIB(socket, socket, LIBS="-lsocket $LIBS"))
+unet_CHECK_LIB_RESOLV
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(malloc.h sys/malloc.h fcntl.h string.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h memory.h errno.h net/errno.h sys/cdefs.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_BIGENDIAN
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_STRUCT_TM
+AC_TYPE_UID_T
+unet_CHECK_TYPE_SIZES
+
+dnl Define SIZE_T_FMT and TIME_T_FMT to be the printf format for
+dnl respectively size_t and time_t.
+unet_DEFINE_SIZE_T_FMT
+unet_DEFINE_TIME_T_FMT
+
+dnl Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MEMCMP
+AC_FUNC_SETVBUF_REVERSED
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(strchr memcpy memmove)
+AC_CHECK_FUNCS(gethostname gettimeofday mkdir strerror strtoken)
+AC_CHECK_FUNCS(select socket uname)
+AC_CHECK_FUNCS(setrlimit inet_netof getrusage times res_init)
+
+dnl Do we have a system call poll?
+unet_FUNC_POLL_SYSCALL
+
+dnl Do we have restarting syscalls ?
+AC_SYS_RESTARTABLE_SYSCALLS
+
+dnl Test for programs
+AC_PROG_AWK
+AC_PROG_MAKE_SET
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PATH_PROGS(RMPROG, rm, /bin/rm)
+AC_PATH_PROGS(SHPROG, sh, /bin/sh)
+
+dnl Test if /bin/sh supports 'set -h'
+AC_CACHE_CHECK([for set -h], unet_cv_sys_set_h,
+[echo "set -h; exit $?" > conftest ;
+$SHPROG ./conftest 2> conftest.out
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+ unet_cv_sys_set_h=yes
+else
+ unet_cv_sys_set_h=no
+fi
+$RMPROG -fr conftest*])
+dnl Used in Configure.
+AC_SUBST(unet_cv_sys_set_h)
+
+unet_NONBLOCKING
+unet_SIGNALS
+
+dnl Add -pipe when possible
+unet_PIPE_CFLAGS
+
+dnl Used in config-sh.
+AC_SUBST(ac_cv_header_poll_h)
+AC_SUBST(ac_cv_header_syslog_h)
+AC_SUBST(unet_cv_func_poll_syscall)
+
+dnl Finally really generate all output files:
+AC_OUTPUT(config-sh Configure ../Makefile ../ircd/Makefile ../doc/Makefile Makefile, [echo timestamp > stamp-h;],)
--- /dev/null
+. ./.config
+. ./parse.none
+mv ../doc/Makefile ../doc/Makefile.tmp
+sed -e "s:^MANDIR=.*:MANDIR=$MANDIR:" \
+ -e "s:^INSTALL *= *\.\..*:INSTALL=../config/install-sh -c:" \
+ ../doc/Makefile.tmp > ../doc/Makefile
+$RM -f ../doc/Makefile.tmp
--- /dev/null
+. ./.config
+. ./parse.none
+mv ../ircd/Makefile ../ircd/Makefile.tmp
+sed -e "s:^CC=.*:CC=$CC:" \
+ -e "s:^CFLAGS=.*:CFLAGS=$CFLAGS:" \
+ -e "s:^CPPFLAGS=.*:CPPFLAGS=$CPPFLAGS:" \
+ -e "s:^LDFLAGS=.*:LDFLAGS=$LDFLAGS:" \
+ -e "s:^IRCDLIBS=.*:IRCDLIBS=$IRCDLIBS:" \
+ -e "s:^IRCDMODE=.*:IRCDMODE=$IRCDMODE:" \
+ -e "s:^IRCDOWN=.*:IRCDOWN=$IRCDOWN:" \
+ -e "s:^IRCDGRP=.*:IRCDGRP=$IRCDGRP:" \
+ -e "s:^BINDIR=.*:BINDIR=$BINDIR:" \
+ -e "s:^SYMLINK=.*:SYMLINK=$SYMLINK:" \
+ -e "s:^INCLUDEFLAGS=.*:INCLUDEFLAGS=$INCLUDEFLAGS:" \
+ -e "s:^DPATH=.*:DPATH=$DPATH:" \
+ -e "s:^MPATH=.*:MPATH=$MPATH:" \
+ -e "s:^RPATH=.*:RPATH=$RPATH:" \
+ -e "s:^INSTALL *= *\.\..*:INSTALL=../config/install-sh -c:" \
+ ../ircd/Makefile.tmp > ../ircd/Makefile
+$RM -f ../ircd/Makefile.tmp
--- /dev/null
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
--- /dev/null
+if [ "$CFLAGS" = "none" ]; then
+ CFLAGS=""
+fi
+if [ "$LDFLAGS" = "none" ]; then
+ LDFLAGS=""
+fi
+if [ "$IRCDLIBS" = "none" ]; then
+ IRCDLIBS=""
+fi
--- /dev/null
+/* setup.h.in. Generated automatically from configure.in by autoheader. */
+/* Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+ *
+ * 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, or (at your option)
+ * any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* Define if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+#undef _ALL_SOURCE
+#endif
+
+/* Define to empty if the keyword does not work. */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define if you don't have vprintf but do have _doprnt. */
+#undef HAVE_DOPRNT
+
+/* Define if system calls automatically restart after interruption
+ by a signal. */
+#undef HAVE_RESTARTABLE_SYSCALLS
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the vprintf function. */
+#undef HAVE_VPRINTF
+
+/* Define if on MINIX. */
+#undef _MINIX
+
+/* Define if the system does not provide POSIX.1 features except
+ with this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define if you need to in order for stat and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+
+/* Define if the setvbuf function takes the buffering type as its second
+ argument and the buffer pointer as the third, as on System V
+ before release 3. */
+#undef SETVBUF_REVERSED
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+#undef size_t
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your <sys/time.h> declares struct tm. */
+#undef TM_IN_SYS_TIME
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
+/* Define if you have the resolv library (-lresolv) */
+#undef HAVE_LIB_RESOLV
+
+/* Define one of these, depending on wether you have
+ POSIX, BSD or SYSV non-blocking stuff */
+#undef NBLOCK_POSIX
+#undef NBLOCK_BSD
+#undef NBLOCK_SYSV
+
+/* Define on of these, depending on wether you have
+ POSIX, BSD or SYSV signal handling */
+#undef POSIX_SIGNALS
+#undef BSD_RELIABLE_SIGNALS
+#undef SYSV_UNRELIABLE_SIGNALS
+
+/* Define these to be unsigned integral internal types,
+ * of respecitvely 2 and 4 bytes in size, when not already
+ * defined in <sys/types.h>, <stdlib.h> or <stddef.h> */
+#undef u_int16_t
+#undef u_int32_t
+
+/* Define this to the printf format for size_t */
+#undef SIZE_T_FMT
+
+/* Define this to the printf format for time_t */
+#undef TIME_T_FMT
+
+/* Define this to the printf signed format for time_t */
+#undef STIME_T_FMT
+
+/* The number of bytes in a int. */
+#undef SIZEOF_INT
+
+/* The number of bytes in a long. */
+#undef SIZEOF_LONG
+
+/* The number of bytes in a short. */
+#undef SIZEOF_SHORT
+
+/* The number of bytes in a size_t. */
+#undef SIZEOF_SIZE_T
+
+/* Define if you have the gethostname function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define if you have the getrusage function. */
+#undef HAVE_GETRUSAGE
+
+/* Define if you have the gettimeofday function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define if you have the inet_netof function. */
+#undef HAVE_INET_NETOF
+
+/* Define if you have the memcpy function. */
+#undef HAVE_MEMCPY
+
+/* Define if you have the memmove function. */
+#undef HAVE_MEMMOVE
+
+/* Define if you have the mkdir function. */
+#undef HAVE_MKDIR
+
+/* Define if you have the res_init function. */
+#undef HAVE_RES_INIT
+
+/* Define if you have the select function. */
+#undef HAVE_SELECT
+
+/* Define if you have the setrlimit function. */
+#undef HAVE_SETRLIMIT
+
+/* Define if you have the socket function. */
+#undef HAVE_SOCKET
+
+/* Define if you have the strchr function. */
+#undef HAVE_STRCHR
+
+/* Define if you have the strerror function. */
+#undef HAVE_STRERROR
+
+/* Define if you have the strtoken function. */
+#undef HAVE_STRTOKEN
+
+/* Define if you have the times function. */
+#undef HAVE_TIMES
+
+/* Define if you have the uname function. */
+#undef HAVE_UNAME
+
+/* Define if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <net/errno.h> header file. */
+#undef HAVE_NET_ERRNO_H
+
+/* Define if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/cdefs.h> header file. */
+#undef HAVE_SYS_CDEFS_H
+
+/* Define if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/malloc.h> header file. */
+#undef HAVE_SYS_MALLOC_H
+
+/* Define if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the crypt library (-lcrypt). */
+#undef HAVE_LIBCRYPT
--- /dev/null
+/* Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+ *
+ * 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, or (at your option)
+ * any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
--- /dev/null
+#! /bin/sh
+chmod u+x config/install-sh config/configure ircd/crypt/sums ircd/crypt/crypter
+cd config
+if test ! -f config.cache && test -r ../../.config.cache; then
+ cp ../../.config.cache config.cache;
+fi
+./configure
+echo timestamp > ../doc/stamp-m
+echo timestamp > ../ircd/stamp-m
--- /dev/null
+Makefile
+stamp-m
--- /dev/null
+/************************************************************************
+ * IRC - Internet Relay Chat, doc/AUTHORS
+ * Copyright (C) 1990
+ *
+ * AUTHORS FILE:
+ * This file attempts to remember all contributors to the IRC
+ * developement. Names can be only added this file, no name
+ * should never be removed. This file must be included into all
+ * distributions of IRC and derived works.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+IRC was conceived of and written by Jarkko Oikarinen <jto@tolsun.oulu.fi>.
+IRC was originally written in University of Oulu, Computing Center.
+Jan 1991 - IRC 2.6 jto@tolsun.oulu.fi
+ - Multiple Channels and protocol changes
+
+Contributions were made by a cast of dozens, including the following:
+
+Markku Jarvinen <mta@tut.fi>: Emacs-like editing facility for the client
+
+Kimmo Suominen <kim@kannel.lut.fi>: HP-UX port
+
+Jeff Trim <jtrim@orion.cair.du.edu>: enhancements and advice
+
+Vijay Subramaniam <vijay@lll-winken.llnl.gov>: advice and ruthless publicity
+
+Karl Kleinpaste <karl@cis.ohio-state.edu>: user's manual
+
+Greg Lindahl <gl8f@virginia.edu>: AUTOMATON code, the Wumpus GM automaton,
+myriad bug fixes
+
+Bill Wisner <wisner@hayes.fai.alaska.edu>: numerous bug fixes and code
+enhancements
+
+Tom Davis <conslt16@zeus.unl.edu> and Tim Russell <russell@zeus.unl.edu>:
+VMS modifications
+
+Markku Savela <msa@tel4.tel.vtt.fi>: advice, support, and being the
+incentive to do some of our *own* coding. :)
+
+Tom Hopkins <hoppie@buengf.bu.edu>: bug fixes, quarantine lines,
+consolidation of various patches.
+
+Christopher Davis <ckd@cs.bu.edu>: EFnet/Anet gateway coding,
+many automata ;), documentation fixing.
+
+Helen Rose <hrose@cs.bu.edu>: documentation updating, and fixing.
+
+Tom Hinds <rocker@bucsf.bu.edu>: emacs client updating.
+
+Tim Miller <cerebus@bu-pub.bu.edu>: various server and client-breaking
+features.
+
+Darren Reed <avalon@coombs.anu.edu.au>: various bug fixes and enhancements.
+Introduced nickname and channelname hash tables into the server.
+
+The version 2.2 release was coordinated by Mike Bolotski
+<mikeb@salmon.ee.ubc.ca>.
+
+The version 2.4 release was coordinated by Markku Savela and
+Chelsea Ashley Dyerman
+
+The version 2.5.2 release was coordinated by Christopher Davis, Helen Rose,
+and Tom Hopkins.
+
+The versions 2.6.2, 2.7 and 2.8 releases were coordinated by Darren Reed.
+
+Contributions for the 2.8 release from the following people:
+Matthew Green <phone@coombs.anu.edu.au>
+Chuck Kane <ckane@ece.uiuc.edu>
+Matt Lyle <matt@oc.com>
+Vesa Ruokonen <ruokonen@lut.fi>
+
+Markku Savela <Markku.Savela@vtt.fi> / April 1990
+Fixed various bugs in 2.2PL1 release server (2.2msa.4) and changed
+sockets to use non-blocking mode (2.2msa.9). [I have absolutely
+nothing to do with clients :-]
+
+Chelsea Ashley Dyerman <chelsea@earth.cchem.berkeley.edu> / April 1990
+Rewrote the Makefiles, restructuring of source tree. Added libIrcd.a to
+the Makefile macros, numerous reformatting of server text messages, and
+added mkversion.sh to keep track of compilation statistics. Numerous
+bug fixes and enhancements, and co-coordinator of the 2.4 release.
+
+Jarle Lyngaas (nmijl@alf.uib.no) added Note functions to ircd.
+
+Armin Gruner <gruner@informatik.tu-muenchen.de> / May, June 1990:
+* Patched KILL-line feature for ircd.conf, works now.
+ Enhancement: Time intervals can be specified in passwd-field.
+ Result: KILL-Line is only active during these intervals
+* Patched PRIVMSG handling, now OPER can specify masks for sending
+ private messages, advantage: msg to all at a specified server or host.
+* Little tests on irc 2.5 alpha, fixed some little typos in client code.
+ Change: common/debug.c has been moved to ircd/s_debug.c, and a
+ irc/c_debug.c has been created, for the benefit that wrong server msg
+ are displayed if client does not recognize them. (strange, if a server
+ sends an 'unknown command', isn't it?)
+
+Tom Hopkins <hoppie@buengf.bu.edu> / September, October 1990:
+* Patched msa's K lines for servers (Q lines).
+* Consolidated several patches, including Stealth's logging patch.
+* Fixed several minor bugs.
+* Has done lots of other stuff that I can't seem to remember, but he
+ always works on code, so he has to have done alot more than three
+ lines worth. :)
+
+Thanks go to those persons not mentioned here who have added their advice,
+opinions, and code to IRC.
+
+Various modifications, bugreports, cleanups and testing by:
+
+Hugo Calendar <hugo@ucscb.ucsc.edu>
+Bo Adler <adler@csvax.cs.caltech.edu>
+Michael Sandrof <ms5n+@andrew.cmu.edu>
+Jon Solomon <jsol@cs.bu.edu>
+Jan Peterson <jlp@hamblin.math.byu.edu>
+Nathan Glasser <nathan@brokaw.lcs.mit.edu>
+Helen Rose <hrose@eff.org>
+Mike Pelletier <stealth@caen.engin.umich.edu>
+Basalat Ali Raja <gwydion@tavi.rice.edu>
+Eric P. Scott <eps@toaster.sfsu.edu>
+Dan Goodwin <fornax@wpi.wpi.edu>
+Noah Friedman <friedman@ai.mit.edu>
+
+
+UNDERNET (1991 - 1999)
+--------
+
+The Undernet versions (TSpre8, u2.9 and u2.10) are based on ircd-2.8.10 and
+contain thousands of hours of work by Carlo Wood <carlo@runaway.xs4all.nl>
+(Run on IRC). The number of protocol enhancements, changes and additions that
+have been added are too many to summarize. All patches are kept in the patch
+repository at http://www.xs4all.nl/~carlo17/ircd-dev/
+
+Various additions and bugfixes have been contributed by:
+
+Aaron <agifford@sci.dixie.edu>
+CapVideo <majdi@puck.nether.net>
+Chaos <simon@troll.elec.uow.edu.au>
+Cym <cym@acrux.net>
+Derrick <dirk@servtech.com>
+Ensor <dholmes@rahul.net>
+flux <cmlambertus@ucdavis.edu>
+Ghostwolf <foxxe@wolfspirit.org>
+Jamey <woodjr@durrance.Colorado.EDU>
+Jarle <jarlel@II.UIB.NO>
+Kev <klmitch@mit.edu>
+Nemesi <cocito@tin.it>
+Niels <niels@holding.pi.net>
+record <jegelhof@cloud9.net>
+smg <smg@lm.com>
+SeKs <intru@step.polymtl.ca>
+Simon- <sim@peace.netnation.com>
+Starfox <starfox@quicklink.com>
+Trio <trio@b62897.STUDENT.CWRU.Edu>
+WildThang <dvmitche@antietam.nssl.uoknor.edu>
+Xorath <vorac@wheel.dcn.davis.ca.us>
--- /dev/null
+# Format of this file: description<nl>variable<nl>helptext<nl><nl>.
+# If the question being documented is of type "choice", we list
+# only the first occurring config variable. The help texts
+# must not contain empty lines. No variable should occur twice; if it
+# does, only the first occurrence will be used by Configure. The lines
+# in a help text should be indented two positions. Lines starting with
+# `#' are ignored. Limit your lines to 78 characters.
+#
+# If you add a help text to this file, please try to be as gentle as
+# possible. Don't use unexplained acronyms and generally write for the
+# hypothetical admin who has just downloaded ircu for the first time.
+# Tell them what to do if they're unsure. Technical information
+# should go in a README in the Documentation directory. Mention all
+# the relevant READMEs and HOWTOs in the help text.
+#
+# All this was shamelessly stolen from several different sources. Many
+# thanks to all the contributors. The texts are copyrighted # (c) 1997
+# by Carlo Wood and governed by the GNU Public License.
+#
+
+Do you want to change your previous configuration
+CHANGE_CONFIG
+ You will be presented a series of questions that you have to answer
+ in order to configure the IRC daemon, prior to compilation.
+ If you went through this before, then your choices have been stored
+ in a file '.config'. If you want to use the same stored configuration
+ now, specify 'n'; this will quickly skip through all questions that
+ you already answered previously, only prompting you for NEW questions.
+ Note that NEW questions only can occur when you just upgraded to a
+ new version. Note also that if you abort by pressing ^C (control-C)
+ anywhere, then all answers are lost; you must finish it before the
+ answers are stored.
+ Pressing a 'c' or 'C' (followed by a return) on any question will
+ Continue the script in "use_defaults mode", that means that it will
+ take all default values unless it finds a NEW question (like when you
+ specify a 'n' here). 'C' will finish everything, but a 'c' will
+ only finish the current paragraph.
+ If you are unsure, or if you want to change a previously entered
+ configuration, specify 'y'.
+
+Which compiler do you want to use
+CC
+ Here you need to specify the C compiler you want to use.
+ Using 'gcc' is highly recommended, you might even want to install it
+ on your machine first. Note that you can specify the full path if you
+ are not sure if the compiler is in your PATH (or whether or not the right
+ compiler will be used). An example is: "/usr/ucb/cc".
+ The package needs an ANSI compiler. Some compilers need an extra option
+ to compile ANSI C. In those cases you can add these options also here.
+ For example, on a HPUX-8.x you would use (if you don't have gcc):
+ "cc -Aa -D_HPUX_SOURCE".
+ Note that you should not use quotes.
+
+What flags should I pass to $CC
+CFLAGS
+ These are the compiler flags, used for CC when compiling.
+ If you are not using gcc, it might be possible that your compiler is not
+ supporting -g and -O at the same time. The -g option is necessary to be
+ able to debug the daemon in the case it contains a bug that makes the
+ ircd core dump. Unless you use a version that is proven to be VERY stable,
+ it is highly recommended to use this option. All Undernet production servers
+ are expected to use it in order to help coder-com to track down bugs.
+ The -O3 will optimize the code - it also makes debugging harder.
+ If you have plenty of cpu cycles then you can use -O2 instead of -O3:
+ it will disable inlining which makes it easier to debug or core dump,
+ the daemon will use a few percent more cpu however.
+ If you are not running a production server you should remove the -Ox.
+ Ircd developers can optionally use more options to turn on extra warnings.
+ Developers (which are using gcc of course ;), should use:
+ "-g -Wall -pedantic -DGODMODE"
+ Note that you should not use quotes.
+ Note that the server uses several non-ANSI (though POSIX.1) function calls.
+
+Do you need extra include directories
+EXTRA_INCLUDEDIRS
+ If your compiler needs extra include directories, you can specify them
+ here as a space separated list of directories. Do not use quotes and do
+ not specify the '-I' prefix. Usually you don't have to specify any
+ extra include directory, in that case you should specify "none" here.
+ If unsure, try "none" (without quotes) and see if all the '#include'
+ header files are found during compilation.
+
+Which linker flags do you need
+LDFLAGS
+ Here you can specify extra flags that will be passed to the linker.
+ Usually you will not need to pass any flags and you can therefore
+ specify "none" here (without the quotes).
+ SunOS users may want to add "-Bstatic" (but only if you need it).
+ You can also specify any "-L..." flags here if you need those for
+ extra libraries.
+
+Which extra libraries do you need
+IRCDLIBS
+ Some Operating Systems need linking with extra libraries for some of the
+ functions used by the daemon. In some cases, it is not known which
+ libraries are needed, even when the Operating System is known. This is
+ for instance the case with SunOS, some need -lresolv, while others don't.
+ If you forget to add a library then this will result in 'undefined variables'
+ during linking. If you do not know which library to add, it might be
+ helpful to use the unix command `nm', which lists the variables of a
+ library. For instance, if you get "unknown variable '_res_mkquery'", and you
+ wonder if this is in /usr/lib/libresolv.so, you can do:
+ nm /usr/lib/libresolv.so | grep res_mkquery
+ Do not use the leading '_' in the grep, this underscore is added by the
+ assembler but is not part of the original variable name and does not show
+ up in the output of nm.
+ Most libraries are in /lib or /usr/lib, which are scanned by default. In
+ some cases you will need to tell the linker where to search for a library.
+ You can do this by adding an -L... option to IRCDLIBS. For instance:
+ "-L/usr/ucblib -lucb" will look for 'libucb.so' in /usr/ucblib too.
+ Here is a list of what you MAYBE need to specify depending on your
+ Operating System:
+ OS Specify here
+ NeXT != 2.0 -lsys_s
+ Dynix/ptx -lsocket -linet -lnsl -lseq
+ Dell SVR4 -lsocket -lnsl -lucb
+ All others Default provided by autoconf
+ If unsure use the default provided by autoconf.
+
+Where should I install the ircd binary
+BINDIR
+ After compilation (by typing 'make'), you can install the server with
+ the command 'make install'. This will install the ircd in the directory
+ you specify here. The package tries to use a meaningful name by naming
+ the binary "ircd.<tag>", where <tag> is the name of the last patch that
+ was applied by the maintainer. A symbolic link (to be specified next)
+ will be used to point to this binary. This allows a /RESTART to
+ immediately start the new version, while keeping the old binary.
+ Note that you need to have write permissions in this directory during
+ the install. Please re-check the permissions/owner and group after
+ installation.
+
+What should the name of the installed symbolic link to the executable be
+SYMLINK
+ 'make install' installs the binary with an unique name, however it makes
+ a symbolic link to this newly installed executable which always has the
+ same name, so you can use /RESTART and/or use this name in scripts that
+ automatically restart the ircd after a reboot or crash.
+ Here you can specify the name of that symbolic link. Note that it may
+ not contain a '/'; it is just the name if the symbolic link and will be
+ installed in BINDIR.
+
+Which permissions do you want the binary to have
+IRCDMODE
+ Here you need to specify the octal file mode of the ircd binary.
+ Recommended is 711 - but you might need setuid or something.
+ Note that using a setuid and starting the daemon as another user
+ does prohibit the daemon from core dumping in case of a crash on some
+ Operating Systems.
+
+Which owner do you want the binary to have
+IRCDOWN
+This will be the owner of the ircd binary after installation.
+
+Which group do you want the binary to have
+IRCDGRP
+This will be the group of the ircd binary after installation.
+
+Where should I install the man page
+MANDIR
+ This is the base directory where the manual page of the ircd is installed.
+ If you are not root on your system, you can change it to your personal
+ manpath directory (which of course should be in your MANPATH environment
+ variable then).
+
+Use inlining for a few crucial functions
+FORCEINLINE
+ This will increases the size of the executable with 7 kb, but it also
+ speeds up execution a bit :). Your compiler needs to understand the
+ keyword __inline__ (GNU gcc and egcs do).
+ If unsure, try if `y' compiles. If it doesn't, you can try using a
+ C++ compiler (ie, configure CC to be 'g++' instead 'gcc').
+
+You have poll(), but do you want to use it
+USE_POLL
+ Some Operating Systems implement select() by calling poll(), others
+ implement poll() by calling select(). The best performance will be
+ achieved by calling the lowest (sys)call ourselves of course.
+ The Undernet Daemon allows you to use select() or poll().
+ If you specify 'y' here, the daemon will use poll() directly, otherwise
+ it will use select(). If you don't know what your Operating System
+ uses as syscall, you can compile the server with USE_POLL and detach
+ the running process with 'strace -p <pid>', 'truss -p <pid>' or
+ 'trace -p <pid>' depending on your Operating System, these UNIX commands
+ will show you the syscalls and therefore show if you use poll() or select().
+ The advantage of using poll() is that you are not bothered by the limits
+ of select() and fd_set size (ie, the number of clients that connect).
+ The following Operating Systems seem to use poll():
+ Solaris 2.x, SunOS 4.x, AIX, Digital UNIX, and NetBSD.
+ linux-2.2.x use poll(), but only of your glibc was compiled with that
+ kernel (and it won't unless you compile it yourself).
+ The following Operating Systems use select():
+ linux-2.0.x.
+ If unsure, test it (a ./configure check will be added in ircu2.10.06).
+
+What is the domain name of your network
+DOMAINNAME
+ This define allows you to specify what you consider to be 'local'.
+ It is only used for statistics. When you issue the IRC command /stats w,
+ the server will respond with statistics of how many clients have been
+ connecting to your server in the last minute, hour and day. It will
+ give these statistics for all connections (including the servers), all
+ clients (from anywhere) and also for clients whose hostname ends on
+ the domain you specify here. So if you are an ISP and you want to know
+ specifically the client load from your own domain, specify that domain
+ here. If you are unsure what to do, then it isn't really important what
+ you give here, just don't give an empty string. A good guess is the last
+ two parts of your own hostname (ie, if your hostname is foo.bar.nowhere.org,
+ specify 'nowhere.org'). Note that the string you give should NOT start
+ with a '.' and you should not use quotes.
+
+Please give a random seed of eight characters
+RANDOM_SEED
+ You should specify exactly eight characters (0-9A-Za-z) here. Do not use
+ quotes or any other special characters. This value is used to initialize
+ the random generator of the server which is used to generate PING/PONG
+ cookies in order to stop spoofing IP-numbers (a PING with a random number is
+ sent to this IP-number and if the client doesn't respond with the
+ exact same number, access is denied). In order to make the random
+ number impossible to guess, it is important that you use your own random
+ seed here.
+
+Does your host have a reliable clock
+RELIABLE_CLOCK
+ You should really ONLY specify 'y' here when your system clock is
+ stable and accurate at all times (within a few seconds).
+ If you are running ntpdate on a regular basis, or an equivalent
+ like xntpd, to keep your system clock synchronized over the network,
+ then you might have an accurate clock. However, this is not guaranteed,
+ for example, it is known that xntpd gives unstable results on linux
+ in some cases. Note that an unstable clock is worse then an clock that
+ has a constant offset, because the servers attempt to correct for a
+ constant offset, but do not correct jumps of your system clock !
+ In general you SHOULD be running ntpdate or equivalent AND make sure it
+ works when you run a production server on Undernet. Otherwise leave
+ your clock alone and specify 'n' here.
+ If unsure specify 'n' !
+
+Change root (/) after start of daemon
+CHROOTDIR
+ If you are a security freak and you want to the daemon to run in
+ its own environment, then you can specify 'y' here. The daemon will
+ change '/' to 'DPATH' (which you will have to specify later).
+ If this confuses you or if you are uncertain, specify 'n'.
+
+Do you want the daemon set its own uid/gid
+CONFIG_SETUGID
+ If you specify 'y' here, then the daemon will attempt to set its
+ User ID (uid) and Group ID (gid) to the numeric values that you will
+ have to specify next. This only makes sense if you (have to) start
+ the server as root. The most secure operation of the server is to
+ not use setuid stuff (here or by means of setting the file mode)
+ and to run the server as a special user only (ie 'irc'). Of course
+ this user must have access to all log and configuration files.
+ Note that using a setuid and starting the daemon as another user
+ does prohibit the daemon from core dumping in case of a crash on some
+ Operating Systems.
+ This option is actually only necessary when you use the Change Root
+ option, because otherwise you can use the file mode to set the uid
+ and gid. Note that the server refuses to run as root.
+ If unsure, specify 'n'.
+
+UID of irc daemon
+IRC_UID
+ Ok, if you insist on using this option: Here you must specify the
+ numeric value of the uid that you want the server to run as.
+ Note that you need to look in the right /etc/passwd file, which isn't
+ the same file when you used the Change Root option.
+
+GID of irc daemon
+IRC_GID
+ Ok, if you insist on using this option: Here you must specify the
+ numeric value of the gid that you want the server to run as.
+ Note that you need to look in the right /etc/group file, which isn't
+ the same file when you used the Change Root option.
+
+Allow to specify configuration file on command line
+CMDLINE_CONFIG
+ If you specify 'y' here, you will be allowed to specify the ircd.conf
+ path (the ircd daemon configuration file) on the command line when
+ starting the daemon (with the -f <ircd.conf file> option).
+ Note that defining this and installing ircd SUID or SGID is a MAJOR
+ security problem - they can use the '-f' option to read any files
+ that the 'new' access lets them. Note also that defining this is
+ a major security hole if other users have accounts on the same machine;
+ when your ircd goes down and some other user starts up the server with
+ a new conf file that has some extra O-lines. So don't use this unless
+ you're debugging.
+
+Set up a Unix domain socket to connect clients/servers
+UNIXPORT
+ If there are lots of users having an account on the same machine
+ (which is very unlikely because the server needs all cpu ;), then
+ using a UNIX domain socket to connect these clients to is more
+ efficient then letting them connect via TCP/IP. A UNIX domain
+ socket is a special device that will be created in your File System.
+ Your client must also support connecting to a UNIX domain socket.
+ The name of the special device must be specified in the "ircd.conf"
+ file by means of an extra 'P: line', see doc/example.conf for the
+ syntax.
+ If you don't have many IRC-ing users on the same host as the server,
+ or when your local IRC client doesn't support UNIX domain sockets,
+ specify 'n' here. Otherwise specify 'y'.
+
+Do you need virtual hosting
+VIRTUAL_HOST
+ This is only needed when you want to run two or more servers on the
+ same machine and on the same port (but different devices).
+ In general you will only need this if you have at least two ethernet
+ cards in your machine with a different IP-number.
+ If you specify 'y' here, then you can "bind" a server to one of your
+ interfaces. You should use the command line option '-w' to tell the
+ server to which interface to bind to. No error is reported if this
+ fails, the server will simply not run.
+ If no '-w' option is given then the server name specified in the
+ 'M: line' of the "ircd.conf" file of the server is used, provided it
+ resolves to an IP-number of one of your interfaces. Note that
+ normally the name does not have to resolve, but when you define this,
+ it MUST resolve or you must use the -w command line option, or the
+ "bind" will fail.
+ If you are unsure, specify 'n'.
+
+Will you connect to more then one server at a time
+HUB
+ All servers of one IRC "network" are connected in a "tree" (no loops).
+ Servers that are only connected to one other server (called the
+ 'uplink') are called "leafs", servers that are connected to more then
+ one other server are called HUBs.
+ If you specify 'n' here then your server will prevent itself from acciden-
+ tally connecting to two servers at once, which is good because this is
+ generally bad for servers in "leaf" positions (they are net.wise located
+ too bad to route traffic). Note that on Undernet all newly linked servers
+ are linked as leafs during their test phase, and should specify 'n' here.
+
+Do you want support for the old I:*:ONE:*:: construct
+USEONE
+ Server versions prior to ircu2.10.05 used to use the string "ONE"
+ as password in an I: line to indicate that only one connection was
+ allowed for any given IP number that matched that I: line.
+ This method only counted the *local* connections though.
+ As of ircu2.10.05 you can specifiy a single(!) digit as password
+ which then will allow that many connections from the same IP number.
+ However, now the IP numbers of ALL clients are counted, also those
+ that are connected to other servers.
+ If you do not use the depricated "ONE" password in your ircd.conf,
+ specify 'n' here. Note that if you you DO use the "ONE" password
+ and you specify 'n' here, then you should change all occurances of
+ "ONE" to "1" (this is the recommended procedure).
+ If you are lazy and you don't want to change the "ONE" passwords
+ into a "1", then specify 'y' here.
+
+Send a short message instead of the MOTD to connecting clients
+NODEFAULTMOTD
+ Every time a client connects to your server, the full Message Of
+ The Day (as specified in its file MPATH) is sent to the client.
+ Even while many clients allow the user to ignore the message of
+ the day: the server still sends it. Many users never read the
+ message of the day anyway, making it a huge waste of bandwidth.
+ If you specify 'y' here than the server won't send the MOTD by
+ default to the client, but rather tell the client when the MOTD
+ was last changed and how to receive the MOTD by typing /MOTD.
+ If unsure specify 'n'.
+
+Do you want to enable debugging output
+DEBUGMODE
+ Sometimes things just don't work. This doesn't have to be a crash,
+ but it is also possible that your server just doesn't want to start
+ at all, or disallows clients to connect at all, etc.
+ With all such drastic and REPRODUCIBLE problems, it makes sense to
+ recompile the server with this option set and then running the
+ ircd (irc daemon) with the (extra) command line options: -t -x9
+ This will make the server run in the foreground and write debug output
+ to the terminal; in a lot of cases this can give a clue on what is
+ wrong (although more often it doesn't).
+ Because defining DEBUGMODE uses a LOT of cpu and is never useful
+ unless you are debugging a reproducible test case, you should never
+ specify 'y' here except for the reason just mentioned.
+ You should certainly NEVER specify 'y' for a server that runs on a
+ production net.
+
+Do you want memory- allocation and/or leak checking
+DEBUGMALLOC
+ If you specify 'y' here, then the server will start to do book keeping
+ on the allocated memory blocks. This uses extra cpu and memory,
+ so normally you do not want this - unless you are debugging.
+ This option uses 8 bytes extra per allocated memory block.
+ The main purpose of this option is to check if a call to free(2) is done
+ with a valid pointer - if the pointer was not previously returned by
+ malloc(2), calloc(2) or realloc(2), the server will core dump in a place
+ that allows the maintainer to get an idea of what went wrong - but only
+ when the server was compiled with the -g flag of course.
+ You also need to specify 'y' here if you want to search for memory leaks.
+ On a production server, specify 'n' - unless you have lots of cpu to
+ spare and you volunteer to search for memory leaks - contact the
+ maintainer in this case.
+ If unsure, specify 'n'.
+
+Do you want to have boundary checking
+MEMMAGICNUMS
+ One of the most nasty bugs are those where buffer overruns are involved.
+ In an attempt to catch those in an early stage, this option will add
+ so called "magic numbers" to the beginning and end of each allocated
+ memory block. When a block is freed or reallocated, the magic numbers
+ are checked and the server core dumps when they were corrupted.
+ This option uses 12 bytes extra per allocated memory block.
+ It doesn't really use much extra cpu compared to defining DEBUGMALLOC, so
+ you might as well specify 'y' here, just in case. It only makes sense
+ though if you compiled the server with compiler option '-g'.
+ If unsure, specify 'n'.
+
+Do you want memory leak testing (stats M)
+MEMLEAKSTATS
+ If you specify 'y' here then the server will start to do extra book keeping
+ on the allocated memory blocks, counting the number of currently allocated
+ blocks per source code location (file and line number). You will be able
+ to retrieve these statistics with the command /stats M.
+ When there is a memory leak, then allocated memory blocks that were allocated
+ under certain conditions are never freed (however the contents of those
+ memory blocks are never used anymore); this would result in a (slow?)
+ increase of the count of allocated memory blocks. This option allows to
+ find where these blocks were allocated which might give a clue on the memory
+ leak problem.
+ This option uses 4 bytes extra per allocated memory block.
+ If you want to look for memory leaks, specify 'y' - otherwise specify 'n'.
+
+Do you want extra info on allocated sizes
+MEMSIZESTATS
+ If you specify 'y' here then the server will start to do extra book keeping
+ on the sizes of the allocated memory blocks. /stats M will not only return
+ the number of allocated blocks, but also the total number of allocated
+ bytes involved. If you defined MEMLEAKSTATS to look for memory leaks, it
+ will give the total number of allocated memory per source code location
+ (file and line number).
+ This option uses 4 bytes extra per allocated memory block, unless you already
+ specified 'y' for MEMMAGICNUMS (boundary checking), because in that case
+ it was already included (and it doesn't matter what you specify here).
+ I think you should specify 'y' here, its more fun to see the sizes :).
+
+Do you want support for a time interval with /stats M
+MEMTIMESTATS
+ If you specify 'y' here then the server will start to do extra book keeping
+ on the allocated memory blocks, storing the time at which the memory block
+ was allocated. This especially slows down /stats M (but unless you use
+ that command frequently, it shouldn't really matter) and uses again 4 bytes
+ of extra memory per allocated memory block.
+ This option is especially useful if you are looking for memory leaks
+ because it allows you to specify a time window with /stats M for which
+ counted blocks must be returned. This allows to ignore recently allocated
+ blocks and permanently allocated blocks (since the start of the server).
+
+Are you testing on a host without DNS
+NODNS
+ If you are playing with the server off-line, and no DNS is available, then
+ long delays occur before the server starts up because it tries to resolv
+ the name given on the M:line (which usually isn't given in /etc/hosts) and
+ for each connecting client.
+ If you specify 'y' here, then a call to gethostbyname() will be done only
+ for the real hostname, and the server will not try to resolv clients that
+ connect to `localhost'.
+ Note that other calls to gethostbyname() are still done anyway if you
+ use VIRTUAL_HOST and that the server still tries to resolv clients
+ that connect to the real IP-number of the server.
+
+Directory where all ircd stuff sits
+DPATH
+ DPATH is provided so that the other path names may be provided in just
+ filename form. It is the Default PATH. When the server starts, it
+ chdir's to DPATH before chroot or any other file operation, making
+ it the "current directory" for the server. This is where core files
+ will go if the server core dumps.
+ Note that you should not include quotes here.
+ Note also that the command line option "-d <dir>" overrides the DPATH
+ you give here, except for the chroot (if you use that).
+
+Server configuration file
+CPATH
+ This is the IRC daemon Configuration filename, mostly called "ircd.conf".
+ If you just specify the filename, the server will read its configuration
+ file from the Default Path "DPATH", which you specified above. However,
+ you are also allowed to specify a full path.
+ Note that you should not include quotes here.
+
+Server MOTD file
+MPATH
+ MPATH is the filename, relative to DPATH, or the full path, of the
+ "Message Of The Day" file; mostly called "ircd.motd". The contents
+ of this file will be sent to every client that connects to the server,
+ after registration.
+ Note that you should not include quotes here.
+
+Server remote MOTD file (3 lines max)
+RPATH
+ RPATH is the filename, relative to DPATH, or the full path, of the
+ "Remote Message Of The Day" file; mostly called "remote.motd". The
+ contents of this file will be sent to every remote client that issues
+ a /MOTD <your server name>. Only the first three lines are sent, so
+ you might want to keep it that short ;).
+ Note that you should not include quotes here.
+
+File for server pid
+PPATH
+ PPATH is the filename, relative to DPATH, or the full path, of the
+ "PID file", mostly called "ircd.pid". It is used for storing the
+ server's PID so a ps(1) isn't necessary.
+ Note that you should not include quotes here.
+
+Do you want to log the use of /WHO x% (recommended)
+CONFIG_LOG_WHOX
+ Specify 'y' here if you want to log the use of /WHO ... x%... by your
+ Opers (see doc/readme.who). This is highly recommended since it will
+ reduce the abuse of this `spy' function. Note: You can disable this
+ spy function completely below, in which case you can give 'n' here.
+ If unsure specify 'y'.
+
+Give the path and(or) filename of this log file
+WPATH
+ WPATH is the filename, relative to DPATH, or the full path, of the
+ log file where the use of /WHO ... x% ... by your Opers will be logged
+ (see doc/readme.who), mostly called "whox.log".
+ Note that you should not include quotes here.
+
+Do you want to log G-lines to a separate file
+CONFIG_LOG_GLINES
+ Specify 'y' here if you want to log G-lines (Global access bans)
+ to a local file.
+
+Give the path and(or) filename of this log file
+GPATH
+ GPATH is the filename, relative to DPATH, or the full path, of the
+ log file where the G-lines will be stored, mostly called "gline.log".
+ Note that you should not include quotes here.
+
+Do you want to log connecting users to a separate file
+CONFIG_LOG_USERS
+ Specify 'y' here if you want to log who is connecting to your server.
+ This file can grow VERY fast on a large net work, so you probably
+ want to specify 'n' here.
+
+Give the path and(or) filename of this log file
+FNAME_USERLOG
+ Here you need to specify the name of the log file where the server
+ should write the data about connecting users to. You can also specify
+ a full path. Note that you should not include quotes here.
+
+Do you want to log Opers to a separate file
+CONFIG_LOG_OPERS
+ Specify 'y' here if you want to log who is successfully becoming an
+ IRC Operator on your server.
+
+Give the path and(or) filename of this log file
+FNAME_OPERLOG
+ Here you need to specify the name of the log file where the server
+ should write the data about Opering users. You can also specify a
+ full path. Note that you should not include quotes here.
+
+Do you want to use syslog
+USE_SYSLOG
+ If you are the sys admin of this machine, or if you have permission
+ of the sys admin to do so, you can let the server write data about
+ certain events to the syslog. You will be prompted for the events
+ that you want to log being one or more of: KILL's, SQUIT's, CONNECT's,
+ OPERing, Connecting Users and finally the log facility.
+ If you are unsure, specify 'n'. It is probably not a good idea to use
+ this on a large IRC net work.
+
+Log all operator kills to syslog
+SYSLOG_KILL
+ Specify 'y' here if you want all KILLs to be written to syslog.
+ Note that on a large IRC net work this is a LOT of data.
+
+Log all remote squits for all servers to syslog
+SYSLOG_SQUIT
+ Specify 'y' here if you want all SQUITs to be written to syslog.
+ Note that on a large IRC net work this is a LOT of data.
+
+Log remote connect messages for other all servers
+SYSLOG_CONNECT
+ Specify 'y' here if you want all CONNECTs to be written to syslog.
+ Note that on a large IRC net work this is a LOT of data.
+
+Log all users who successfully become an Oper
+SYSLOG_OPER
+ Specify 'y' here if you want all OPERs to be written to syslog.
+ Note that on a large IRC net work this is a LOT of data.
+
+Send userlog stuff to syslog
+SYSLOG_USERS
+ Specify 'y' here if you want all connecting users to be written to syslog.
+ Note that on a large IRC net work this is EXTREMELY MUCH data.
+ You really want to specify 'n' here.
+
+Log facility (daemon, user, local0-7)
+CONFIG_DAEMON
+ Well if you got this far and still need help, then I think you should
+ go back and specify 'n' at the question "Do you want to use syslog".
+
+Which local facility (0-7)
+INT_LOCAL
+ Well if you got this far and still need help, then I think you should
+ go back and specify 'n' at the question "Do you want to use syslog".
+
+Use m4 as a preprocessor on CPATH
+M4_PREPROC
+ If you use m4 macro's in your "ircd.conf" file, then you need to specify 'y',
+ which will enable m4 preprocessing of the "ircd.conf" file.
+ If you are unsure specify 'n'. Note using m4 macros has often lead to
+ problems (passwords or server names that match a macro name for instance),
+ while the benefits of using m4 are highly doubtful. Unless you are
+ already a m4 wizard and insist on using it I recommend to specify 'n' here.
+
+Use crypted passwords for N: lines
+CRYPT_LINK_PASSWORD
+ In order to allow other servers to connect to you, you need to specify
+ two configuration lines in the "ircd.conf" configuration file (CPATH).
+ Each of these lines contains a password; the C: line is used for connecting
+ to a remote server and contains the password that is sent to the remote
+ server for authentication, thus this password must be in plain text.
+ The other is the N: line and contains the password that the remote server
+ is sending to you. For security reasons it is advised to store this
+ password in DES encrypted form. If you specify 'y' here, you will be
+ allowed to use the DES encrypted password in the password field of the
+ N: line, see doc/example.conf for more details. Note that you should
+ use *different* passwords in the C: and N: lines respectively for obvious
+ reasons.
+
+Use crypted passwords for operators
+CRYPT_OPER_PASSWORD
+ In order to allow certain users to become IRC OPERators, they must
+ authenticate themselves with a password. This password is matched
+ against an 'O: line' in the "ircd.conf" configuration file, see
+ doc/example.conf for more details. If you specify 'y' here, you are
+ allowed to use the DES encrypted form of these passwords in your
+ "ircd.conf" file (even more, your Opers don't have to tell you their
+ real password, they can provide the DES encrypted form themselves).
+ Since it has happened often in the past that the "ircd.conf" file
+ was compromised somehow, you are highly encouraged to specify 'y' here
+ and use the DES encrypted form. You can find a program 'mkpasswd' in
+ the ircd/crypt directory that will allow you to generate the encrypted
+ form.
+
+Max size of the total of of all sendqs (bytes)
+BUFFERPOOL
+ This specifies the maximum amount of RAM that your server will allocate
+ for buffering sendQ's. Small leafs can use a value as little as 1000000,
+ while large HUBs need to specify a value as high as 20000000.
+ If you run out of memory, clients and/or servers are dropped with the
+ error "Buffer allocation error". Then you will have to up this number
+ (and install more RAM if appropriate).
+ If you want a more educated guess for this value then realize that any
+ value is good if you _really_ rather want to drop servers and clients
+ then allocate more memory; this will be the case when there is the
+ danger to run out memory for other allocations.
+ Even if you run the daemon on a dedicated machine, then specifying the
+ maximum of the RAM you have is a Bad Thing because really running out
+ of memory is a lot worse then dropping clients in a controlled way:
+ if possible you should have memory left for all the internal structures
+ (channels, clients, banlists, receive buffers) at all times.
+ On average, clients seem to use 150 bytes of sendQ, but at peak moments
+ this can easily increase to 2032 bytes per client (sendQs are allocated
+ in chunks of 2032 bytes).
+ The maximum possible ammount that can be allocated for sendQs is the
+ number of connected clients times whatever you specified as maximum
+ sendQ in your Y: lines in the ircd.conf file. Likely, that value will
+ be larger then the ammount of RAM you have.
+ The educated guess I talked about earlier would be 'number of clients'
+ times * 2048 bytes + 'size of net.burst' * n, where `n' is 1 for leafs
+ and up till 5 for HUB's. The 'size of net.burst' is about 125 bytes
+ per online client (on the total network).
+ For large HUBs with 4000 clients on undernet (30,000 users), this results
+ in 27 Mb. Leafs could use 12 Mb. Of course you can use less when you
+ have less than 4000 local clients.
+ Don't forget to specify this value in bytes.
+
+Max receive queue for clients (bytes)
+CLIENT_FLOOD
+ Currently, everything that a client sends to a server is read by the server
+ and stored in a buffer (the clients receive queue). The server will
+ process messages from this queue one by one (running over all clients
+ each time). When a client sends new messages faster they get processed,
+ and the size of its receive buffer reaches this value, the client is
+ dropped with the error "Excess flood". A reasonable value is 1024 bytes.
+ The maximum size is 8000 bytes.
+
+Maximum number of network connections (23 - (FD_SETSIZE-4))
+MAXCONNECTIONS
+ This specifies the maximum number of network connections the server
+ will use. You also need some non-network connects (log files etc), so
+ the maximum value is "FD_SETSIZE-4". The minimum value is 23.
+ The only benefit of using a small value is that your server uses less
+ memory - but *only* when you really have a small (client) load.
+ Routing server that hardly take clients can use 128 here for instance.
+ Servers that are always full should just specify the maximum amount
+ that still works (which might be less then FD_SETSIZE-4, some OS need
+ kernel hacking to allow more then 1024 fds per process). The only max.
+ value that is guaranteed to work is 252 ;). Note that if the value of
+ FD_SETSIZE is for instance 1024, then that doesn't mean you can't
+ connect MORE clients - but in this case you certainly need kernel
+ hacking. Find an experienced admin with the same Operating System and
+ ask him what the maximum is and how to achieve it.
+
+Default client listen port
+PORTNUM
+ This is the default listen port. You can specify more listen ports
+ in the "ircd.conf" file with 'P: lines'; see doc/example.conf for more
+ details on P: lines. Note that /stats p currently only shows P: lines,
+ which might be a reason for you to use a less often used value here.
+ Note that there is actually no difference between client and server
+ listen ports, but it is good practice to separate them for statistical
+ purpose (bandwidth usage statistics).
+
+Nickname history length
+NICKNAMEHISTORYLENGTH
+ This value specifies the length of the nick name history list, which
+ is only used for /WHOWAS. It uses about 300 to 400 bytes per entry.
+ Note that at a net.break so many client disappear that the whole
+ "whowas" list refreshed a few times (unless you make it as big as
+ 20,000 of course - but you shouldn't because thats a waste of ram
+ and cpu). A reasonable value is 'total number of clients' / 25.
+
+Allow Opers to see (dis)connects of local clients
+ALLOW_SNO_CONNEXIT
+ If you specify 'y' here, you will be allowed to see all client connects and
+ disconnects as a server notice. The historical reason for adding this
+ option was to detect clone bots that connected to your server. However,
+ on a large IRC network like Undernet, the number of clients that connect
+ are so huge that it is not possible to keep an eye on this and everyone
+ has been filtering these notices out anyway. Next to that it turned out
+ to use no less then 10% of the total cpu usage last time I measured it
+ (this has been improved after that, but still).
+ Unless you insist on seeing those notices you should specify 'n' here.
+ Note that in the meantime Undernet has a LOT of other (semi- and fully-
+ automated) ways to detect clone bots, which work a LOT better for this
+ purpose.
+
+Show IP address in client connection notices
+SNO_CONNEXIT_IP
+ Usually when showing a client connection, a nick, userid and hostname are
+ displayed. Selecting 'y' here will also display the numeric IP and connection
+ class of the connecting client. This can be useful for detecting spoofed DNS and
+ virtual hosted clones. This does use extra CPU though and is generally not needed,
+ however if a connection monitor bot is the only client that looks at these
+ notices, it is more efficient than sending USERIP for every connection. This
+ option makes the server compatible with Hybrid tcm bots.
+
+Do you want to use R: lines in your configuration file
+R_LINES
+ If you specify 'y' here you will be allowed to use R:lines in the "ircd.conf".
+ This allows more freedom in restricting connections to your server by
+ calling an external program to determine whether to allow the connection.
+ It also uses a lot of overhead however, and can bog things down, so you should
+ consider whether you really need them, and if you can handle the extra load.
+ If unsure, specify 'n'.
+
+Process R: lines every rehash
+R_LINES_REHASH
+ You may not want to have the R: lines checks everywhere since this can
+ cost a lot of time and delays. If you specify 'y' here, then R: lines are
+ checked whenever the "ircd.conf" file is reloaded (when the REHASH command
+ is used, or a signal SIGHUP is received by the daemon). This shouldn't be
+ too much of a drain on the system if the R:lines programs are short.
+
+Process R: lines always
+R_LINES_OFTEN
+ If you specify 'y' here then R: lines will be checked as often as K: lines.
+ Note that this is -very- likely to cause a severe drain on your resources.
+ Use at your own risk, specify 'n' unless your really sure.
+
+Allow (local) Opers to see all local invisible users
+SHOW_INVISIBLE_USERS
+ If you specify 'y' here, then your (local) IRC Operators will be able to
+ see all local invisible users (clients connected to your own server).
+ The reason for this is to hunt for clone bots, make sure your Operators do
+ not use this "feature" for spying on individuals and respect the user that
+ wishes to be invisible (mostly meaning that they don't want to be found when
+ on certain channels).
+ Note: If you answer 'n' here, then you will also not be able to see remote
+ invisible users (if you specify 'y' you will also get a configuration
+ question that asks you to specify whether or not you want your Opers to see
+ remote invisible users or not).
+
+Allow Opers to see all invisible users
+SHOW_ALL_INVISIBLE_USERS
+ If you specify 'y' here, then your global IRC Operators (O:) will be able
+ to see ALL invisible users. The reason for this is to hunt for clone bots,
+ make sure your Operators do not use this "feature" for spying on individuals
+ and respect the user that wishes to be invisible (mostly meaning that they
+ don't want to be found when on certain channels).
+
+Allow global Opers (O:) to see inside secret channels
+OPERS_SEE_IN_SECRET_CHANNELS
+ If you specify 'y' here, then your global IRC Operators (O:) will be able
+ to see who is on a specified, secret channel, without joining themselfs.
+ This can be needed to make a reasonable judgement in the case of a "channel
+ takeover" being reported, while the channel is set invite only.
+ See doc/readme.who for more details.
+
+Allow local Opers (o:) to see inside secret channels
+LOCOP_SEE_IN_SECRET_CHANNELS
+ If you specify 'y' here, then your local IRC Operators (o:) will be able
+ to see who is on a specified, secret channel, without joining themselfs.
+ This can be needed to make a reasonable judgement in the case of a "channel
+ takeover" being reported, while the channel is set invite only.
+ See doc/readme.who for more details.
+ If unsure, specify 'n'.
+
+Don't truncate obnoxiously long /who output for opers
+UNLIMIT_OPER_QUERY
+ A /who command can sometimes return several hundred lines of info. To
+ reduce flooding and sending too much, the output is truncated. By
+ answering 'y' to this, when an IRC Operator uses /who, the output will
+ not be truncated, no matter how much data is returned.
+
+Allow Opers to use the KILL command
+OPER_KILL
+ You can select 'n' if you don't think operators should be able
+ to use the KILL command, and wish to prevent your operators from
+ using it. This will not, however, prevent operators on other
+ servers from issuing KILLs to your clients. You probably want to
+ select 'y' for this unless you really really don't think KILL
+ should -ever- be used by an operator.
+
+Allow Opers to use the REHASH command
+OPER_REHASH
+ Allows operators to use the REHASH command to reload the servers
+ configuration file (ircd.conf) if you select 'n', you can still
+ reload the configuration file with a unix command,
+ kill -HUP `cat ircd.pid`. If unsure, select 'y'.
+
+Allow Opers to use the RESTART command
+OPER_RESTART
+ Allows an operator to use the RESTART command to cause the server
+ to restart, using the ircd executable in SPATH. If unsure, select 'y'.
+
+Allow Opers to use the DIE command
+OPER_DIE
+ Allows an operator to use the DIE command to shutdown the server
+ online. If you select 'n' you will need to send the server a kill
+ signal to shutdown the server. If unsure, select 'y'.
+
+Allow Opers to add local G-lines
+OPER_LGLINE
+ Allows operators to add local G-lines with the GLINE command. This is
+ like a *local* KILL, except that the user being killed can't immediately
+ reconnect: He will have to wait for the G-line to expire.
+ The reason for adding this is that a KILL is rather useless for removing
+ (or 'warning') abusers (it is still THE command to remove ghosts and
+ a-like, the reason KILL was added in the first place). However, adding
+ G-lines for a dynamic IP with expire times larger then 10 minutes is highly
+ discouraged: The user will already have dialed in via another IP or account
+ and the G-line would only harm other, innocent, users.
+
+Allow Opers to connect from a remote site
+OPER_REMOTE
+ If you select 'n' for this, clients must be on the 'same network' as
+ the server in order to gain oper privledges. If you're not sure, just
+ select 'y'.
+
+Allow local opers to use the REHASH command
+LOCOP_REHASH
+ Allows a local operator (defined by a lowercase o:line in ircd.conf)
+ to cause the server to reload its configuration file (ircd.conf) with
+ the REHASH command. If unsure, select 'n'.
+
+Allow local opers to use the RESTART command
+LOCOP_RESTART
+ Allows a local operator (defined by a lowercase o:line in ircd.conf)
+ to use the RESTART command. If unsure, select 'n'.
+
+Allow local opers to use the DIE command
+LOCOP_DIE
+ Allows a local operator (defined by a lowercase o:line in ircd.conf)
+ to use the DIE command. If unsure, select 'n'.
+
+Allow local opers to add local G-lines
+LOCOP_LGLINE
+ Allows a local operator (defined by a lowercase o:line in ircd.conf)
+ to add local G-lines with the GLINE command. This is like a *local* KILL,
+ except that the user being killed can't immediately reconnect: He will
+ have to wait for the G-line to expire.
+
+Do you want to have a default LIST parameter
+CONFIG_LIST
+ Pre-Undernet, the LIST command could either be given with one channel
+ name, or without any parameter. In the last case it would simply list
+ all channels. In time the IRC networks grew big, until the output of
+ the LIST command always filled up the sendQ of the client (and dis-
+ connected it). This was fixed by Carlo Wood (Run@IRC) on request of a
+ Dutch ISP whose users complained about this: The LIST output is now
+ generated in small chunks, generating more each time when there is room
+ in the clients sendQ. However, then it turned out that LIST (now it
+ worked) used 50% of all cpu (not even mentioning the bandwidth)...
+ This was unacceptable and the mentioned patch was disabled. On the
+ other hand we wanted LIST to work at least partly, so a few new
+ parameters have been added to LIST: <,>,C<,C>,T<,T> each followed by
+ a number they filter respectively the number of users on the channel,
+ the creation time of the channel (or age, depended on the value of
+ the number) and the topic set time.
+ If you specify 'y' here, then each time a "/LIST" (without parameter)
+ is issued by a client, a default parameter is used. Note that when
+ a parameter is used, the client can still max. sendq out - the send
+ flood control only works without any parameter.
+ If you specify 'n' here then a "/LIST" without parameters will list
+ all channels (and work), but as just said: it uses a LOT of cpu and
+ bandwidth on a large net.work.
+ If you specify 'y' you will be prompted for the default parameter.
+
+Give default LIST parameter
+DEFAULT_LIST
+ Here you need to specify the default LIST parameter which is used
+ when the server receives a LIST without any parameter.
+ You should use something that limits the output to a maximum of a
+ few hundred channels; for instance "T<10" (topic is set less then
+ 10 seconds ago) or ">10" (more then 10 users on the channel) or even
+ a combination of this. Note that you should not include quotes here.
+
+K: line comments treated as a file
+COMMENT_IS_FILE
+ If you specify 'y' here, then K: line comments (see doc/example.conf
+ for more details on the K: line syntax) will be treated as a filename
+ by default. The file needs to exist and will be written to clients
+ that match that K: line.
+ If you specify 'n' here, then K: line comments will be treated as
+ a comments by default.
+ In both cases you can override the default by prepending a filename
+ with a '!' or enclose a comment between double quotes.
+ If unsure, use the default.
+
+Only nullify idle-time on PRIVMSG
+IDLE_FROM_MSG
+ The IRC command WHOIS gives an idle time for clients. If you want that
+ this idle time is set to zero only when the clients send a PRIVMSG,
+ then you should specify a 'y' here.
+ If you specify a 'n' then the idle time will be nullified on all messages
+ except the server PING/PONG.
+
+Check clone limit (2!)
+CHECK_CLONE_LIMIT
+ Set this to 2.
+
+Check clone period (20!)
+CHECK_CLONE_PERIOD
+ Set this to 20.
+
+Check clone delay (600!)
+CHECK_CLONE_DELAY
+ Set this to 600.
+
+Max auto connects per class (1!)
+MAXIMUM_LINKS
+ Set this to 1.
+
+Enable message logging
+MSGLOG_ENABLED
+ Define this if you want the server to log received messages in static memory
+ at parsing time. -This is for debugging purposes only-. You might want to
+ redefine LOG_MASK_TYPE in s_debug.h and LOG_MASK_LEVEL in s_debug.c too.
+ The default is to log all messages that change some status in server's data
+ structures. Select 'n' unless you are debugging the server code.
+ DO NOT SELECT THIS ON PRODUCTION SERVERS!
+
+Message log size
+MSGLOG_SIZE
+ Number of messages to log. Keep this low as raising it to 1024 will use 800k
+ of _static_ memory! Recommended value is 128. You must include this even if
+ you selected 'n' for MSGLOG_ENABLED.
+
+Only allow KILLs of local clients
+LOCAL_KILL_ONLY
+ This only allows operators of this server to KILL clients directly connected
+ to this server. Operators will not be able to issue KILLs for clients on
+ other servers. Some networks (not Undernet) require that this be defined
+ for newly linking servers, but if you haven't been told to do otherwise,
+ specify 'n' here.
+
+Max server idle time (60)
+TIMESEC
+ This is the maximum idle time for the server. If no messages are received
+ in TIMSEC seconds, PINGFREQUENCY and CONNECTFREQUENCY are checked.
+ Recommended value is 60 seconds.
+
+KILL nick chase time limit (30)
+KILLCHASETIMELIMIT
+ This is the maximum amount of time a KILL command will automatically change
+ to the current nick of a user that has just changed nicks from the one given
+ with the original KILL. Don't change this unless you really need to.
+
+Max number of channels per user (recommended: 5)
+MAXCHANNELSPERUSER
+ This is the maximum number of channels a user can be in at a time.
+ The "mandatory" value on Undernet is currently 10. Since it only
+ influences the local server when you decrease it, its up to you to decide
+ if you want to use a smaller value. Do not use a larger value however,
+ because it DOES cost more memory and bandwidth on all other servers
+ when you allow users to join more channels simultaneously.
+ One of the most important reasons to choose a smaller value is the fact
+ that the now-a-days 'GUI' clients tend to stay on every channel they
+ join (they aren't bothered by flooding in other channels). It DOES take
+ your bandwidth however to send all those messages for 10 different
+ channels to all your users.
+
+Max number of silence masks (15!)
+MAXSILES
+ This is the maximum number of masks a user can silence at a time.
+ The silence command allows users to filter messages directed at them
+ from certain users or domains, at the source server. Increasing this
+ number allows users to use up more memory with inefficient use of the
+ command. If your not sure, don't change this.
+
+Expected average banmask length (40!)
+AVBANLEN
+ This is the expected average banmask length. Leave it at 40.
+
+Use .config of THIS source tree as your upgrade default
+CONFIG_NEW
+ Each source tree keeps its *own* config/.config file with the default
+ values for all questions (those that you gave the last time you did a
+ 'make config'). Whenever you do 'make config' again in this source tree,
+ you will get these defaults.
+ However, when you *upgrade* to a new version (and get a NEW source tree),
+ it doesn't have a .config yet and will (try to) use a .config of one of
+ your previous source trees.
+ If you specify 'y' here, then the last defaults of THIS source tree will
+ be used in your next upgrade. Note that any changes you make later to
+ config/.config (by running 'make config' again) will also take effect
+ on this later upgrade.
+ You can always change this by making a new hard link from .config ->
+ ircu2.10.xx/config/.config in the directory where you keep the source
+ trees.
+ If unsure, and you are not currently installing a test source tree,
+ specify 'y'. If this is a second source tree that you will only be
+ experimenting with, specify 'n'.
+
+Class 0 ping frequency (120)
+PINGFREQUENCY
+ If the daemon doesn't receive anything from any of its links within
+ PINGFREQUENCY seconds, then the it will attempt to check for an active link
+ with a PING message. If no reply is received within (PINGFREQUENCY * 2)
+ seconds, then the connection will be closed. This value is overridden by
+ a Y:line in "ircd.conf" if the connections I/C/N: line in "ircd.conf" assigns
+ a specific class to the connection (recommended).
+
+Class 0 connect frequency (600)
+CONNECTFREQUENCY
+ This is the default frequency that the server attempts to reconnect with
+ its uplink server if it is set to auto connect to it. Note that this value
+ is overridden by a Y:line in ircd.conf if the C/N:lines in ircd.conf
+ assigns a specific class to the connection (recommended).
+
+Min time before a link is good (300)
+HANGONGOODLINK
+ Often the net breaks for a short time and its useful to reestablish the
+ same connection faster than CONNECTFREQUENCY would allow, but to keep
+ from trying again on a bad connection, we require that the connection be
+ open for a certain minimum time. The recommended value is 300 seconds.
+
+Wait before reconnecting to good link (10!)
+HANGONRETRYDELAY
+ When attempting to quickly reestablish a connection to a good link, we
+ give the net a few seconds to steady. This time must be long enough for
+ the other end to notice it broke too. The recommended value is 10 seconds.
+
+connect(2) timeout (90!)
+CONNECTTIMEOUT
+ Number of seconds to wait for a connect(2) call to complete. NOTE: this
+ must be at *LEAST* 10. When a client connects, it has CONNECTTIMEOUT - 10
+ seconds for its host to respond to an ident lookup query and for a DNS
+ lookup to complete. It is recommended you don't change this value, but if
+ you do, consider the fact that users whose clients do not support NOSPOOF
+ will have to type /QUOTE PING <big number> before registration.
+
+Max send queue (40000)
+DEFAULTMAXSENDQLENGTH
+ This is the default value of the "max. sendq length" of Y: line classes
+ (see doc/example.conf for details on Y: lines). You will probably
+ always override this value in your "ircd.conf" with the Y: lines.
+ The given value used to be an often used value for client sendqs.
--- /dev/null
+# doc/Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+
+# 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, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+prefix=@prefix@
+INSTALL=@INSTALL@
+SHELL=@SHPROG@
+RM=@RMPROG@
+@SET_MAKE@
+# The following variables are replaced by what you give during configuration :
+
+MANDIR=
+
+#### End of system configuration section. ####
+
+all:
+
+distclean:
+ ${RM} -f Makefile stamp-m
+
+maintainer-clean: distclean
+
+install:
+ (test -d ${MANDIR}/man8 || mkdir ${MANDIR}/man8 || mkdir -p ${MANDIR}/man8) 2> /dev/null && ${INSTALL} -m 644 ircd.8 ${MANDIR}/man8
+
+uninstall:
+ ${RM} -f ${MANDIR}/man8/ircd.8
+
+# You need GNU make for this to work.
+Makefile: ../config/config.status Makefile.in ../config/gen.doc.Makefile \
+ ../config/.config stamp-m
+ @echo "recreating doc/Makefile"
+ @cd ../config; \
+ CONFIG_FILES=../doc/Makefile CONFIG_HEADERS= ./config.status > /dev/null; \
+ RM=${RM} ${SHELL} ./gen.doc.Makefile
+
+stamp-m:
+ echo timestamp > stamp-m
+
+../config/config.status:
+ @cd ../config; ${MAKE} config.status
--- /dev/null
+# ircd.conf configuration file for ircd version ircu2.9.mu and ircu2.10
+#
+# Written by Niels <niels@undernet.org>, based on the original example.conf,
+# server code and some real-life (ahem) experience.
+#
+# Thanks and credits to: Run, Trillian, Cym, Morrissey, Chaos, Flynn,
+# Xorath, WildThang, Mmmm, SeKs, Ghostwolf and
+# all other Undernet IRC Admins and Operators,
+# and programmers working on the Undernet ircd.
+
+# This is an example of the configuration file used by the Undernet ircd.
+#
+# This document is based on a (fictious) server in Europe with a
+# connection to the Undernet IRC network. It is primarily a leaf server,
+# but if all the other hubs in Europe aren't in service, it can connect
+# to one in the US by itself.
+#
+# All configuration options start with a letter identifying the option,
+# and a colon separated list of options. An asterisk indicates an
+# unused field.
+#
+# Please note that when ircd puts the configuration lines into practice,
+# it parses them exactly the other way round than they are listed here.
+# This means that you should start your I: lines with the "fall through",
+# most vanilla one and end with the most detailed.
+#
+# There is a difference between the ``hostname'' and the ``server name''
+# of the machine that the server is run on. For example, the host can
+# have ``veer.cs.vu.nl'' as FQDN, and ``Amsterdam.NL.EU.undernet.org'' as
+# server name.
+# A ``server mask'' is something like '*.EU.UnderNet.org'', which is
+# matched by 'Amsterdam.NL.EU.undernet.org' but not by
+# 'Manhattan.KS.US.undernet.org'.
+
+#
+# First some information about the server.
+# M:<server name>:*:<description>:<server port>:<server numeric>
+#
+# The <server port> is the port that other servers can connect to.
+# Client ports need to be specified with a P: line, see below.
+#
+# Note that <server numeric> has to be unique on the network your server
+# is running on, must be between 1 and 64, and is not updated on a rehash.
+
+M:London.UK.Eu.UnderNet.org:*:[127.0.0.1] University of London, England:4400:1
+
+#
+# This sets information that can be retrieved with the /ADMIN command.
+# It should contain at least an admin Email contact address.
+# A:<line 1>:<line 2>:<line 3>
+
+A:The University of London:Undernet IRC server:IRC Admins <irc@london.ac.uk>
+
+#
+# All connections to the server are associated with a certain ``connection
+# class'', be they incoming or outgoing (initiated by the server), be they
+# clients, servers or Martians. (Note that ircd doesn't have direct support
+# for Martians (yet?); they will have to register as normal users. ;-)
+# Take the following Y: lines only as a guide.
+# Y:<class>:<ping freq>:<connect freq>:<maximum links>:<sendq size>
+
+# Server classes: 90 = all your uplinks for who you do not wish to hub;
+# else in classes 80 and/or 70.
+# 50 = leaf servers (only used if your server is a hub)
+
+Y:90:90:300:1:1700000
+Y:80:90:300:1:1700000
+Y:70:90:300:1:1700000
+Y:50:90:300:10:1700000
+
+# Client classes. 10 = locals; 2 = for all .net and .com that are not
+# in Europe; 1 = for everybody.
+
+Y:10:90:0:100:160000
+Y:2:90:0:5:80000
+Y:1:90:0:400:160000
+
+#
+# To allow clients to connect, they need authorization. This can be
+# done based on hostmask, address mask, and/or with a password.
+# With intelligent use of classes and the maxconnections field in the
+# Y: lines, you can let in a specific domain, but get rid of all other
+# domains in the same toplevel, thus setting up some sort of 'reverse
+# K: line'.
+# I:<IP mask or crap to force resolving>:<opt passwd>:<hostmask>::<class>
+
+# Technical description (for examples, see below):
+# For every connecting client, the IP-number is know. A reverse lookup
+# on this IP-number is done to get the (/all) hostname(s).
+# Each hostname that belongs to this IP-number is matched to <hostmask>,
+# and the I: line is used when any matches; the client will then show
+# with this particular hostname. If none of the hostnames matches, then
+# the IP-number is matched against the <IP mask ...> field, if this matches
+# then the I: line is used nevertheless and the client will show with the
+# first (main) hostname if any; if the IP-number did not resolve then the
+# client will show with the dot notation of the IP-number.
+# There is a special case for the UNIX domain sockets and localhost connections
+# though; in this case the <IP mask ...> field is compared with the
+# name of the server (thus not with any IP-number representation). The name
+# of the server is the one returned in the numeric 002 reply, for example:
+# 002 Your host is 2.undernet.org[jolan.ppro], running version ...
+# Then the "jolan.ppro" is the name used for matching.
+# Therefore, unix domain sockets, and connections to localhost would
+# match this I: line:
+# I:jolan.ppro::foobar::1
+# Finally, I: lines with empty <IP mask ..> or <hostmask> fields are skipped.
+
+# This is the 'fallback' entry. All .uk, .nl, and all unresolved are
+# in these two lines.
+# By using two different lines, multiple connections from a single IP
+# are only allowed from hostnames which have both valid forward and
+# reverse DNS mappings.
+
+I:*@*:1:Unresolved::1
+I:Resolved::*@*::1
+
+# If you don't want unresolved dudes to be able to connect to your
+# server, use just:
+# I:NotMatchingCrap::*@*::1
+
+# Here, take care of all American ISPs.
+I:Resolved::*@*.com::2
+I:Resolved::*@*.net::2
+
+# Now list all the .com / .net domains that you wish to have access...
+# actually it's less work to do it this way than to do it the other
+# way around - K: lining every single ISP in the US.
+# I wish people in Holland just got a .nl domain, and not try to be
+# cool and use .com...
+I:Resolved::*@*.wirehub.net::1
+I:Resolved::*@*.planete.net::1
+I:Resolved::*@*.ivg.com::1
+I:Resolved::*@*.ib.com::1
+I:Resolved::*@*.ibm.net::1
+I:Resolved::*@*.hydro.com::1
+I:Resolved::*@*.NL.net::1
+
+# You can request a more complete listing, including the "list of standard
+# K-lines" from the Routing Committee; it will also be sent to you if
+# you apply for a server and get accepted.
+
+# Ourselves - this makes sure that we can get in, no matter how full
+# the server is (hopefully).
+I:*@193.37.*::*@*.london.ac.uk::10
+
+# You can put a digit (0..9) in the password field, which will make ircd
+# only accept a client when the total number of connections to the network
+# from the same IP number doesn't exceed this number.
+# The following example would accept at most one connection per IP number
+# from "*.swipnet.se" and at most two connections from dial up accounts
+# that have "dial??.*" as host mask:
+# I:Resolved:1:*@*.swipnet.se::1
+# I:Resolved:2:*@dial??.*::1
+
+#
+# It is possible to show a different Message of the Day to a connecting
+# client depending on its origin.
+# T:<hostmask>:<path to motd file>
+
+# DPATH/net_com.motd contains a special MOTD where users are encouraged
+# to register their domains and get their own I: lines if they're in
+# Europe, or move to US.UnderNet.org if they're in the USA.
+T:*.net:net_com.motd
+T:*.com:net_com.motd
+
+# A different MOTD for ourselves, where we point out that the helpdesk
+# better not be bothered with questions regarding irc...
+T:*.london.ac.uk:london.motd
+
+#
+# One of the many nice features of Undernet is ``Uworld'', a program
+# connected to the net as a server. This allows it to broadcast any mode
+# change, thus allowing opers to, for example, 'unlock' a channel that
+# has been taken over.
+# There is only one slight problem: the TimeStamp protocol prevents this.
+# So there is a configuration option to allow them anyway from a certain
+# server.
+# Note: (1) These lines are agreed on by every server admin on Undernet;
+# (2) These lines must be the same on every single server, or results
+# will be disasterous; (3) This is a useful feature, not something that
+# is a liability and abused regularly (well... :-)
+# If you're on Undernet, you MUST have these lines. I cannnot stress
+# this enough.
+# As of ircu2.10.05 is it possible to Jupe nicks. Juped nicks need to be
+# added to U: lines. As per CFV-0095, the following nicks must be juped,
+# it is not allowed to jupe others as well.
+
+U:Uworld.EU.undernet.org:EuWorld,E,protocol,StatServ,NoteServ,Undernet:*
+U:Uworld2.undernet.org:UWorld2,W,ChanSvr,ChanSaver,ChanServ,COM1,COM2,COM3,COM4:
+*
+U:Uworld.undernet.org:Uworld,X,NickSvr,NickSaver,NickServ,LPT1,LPT2,AUX:*
+
+#
+# While running your server, you will most probably encounter individuals
+# or groups of persons that you do not wish to have access to your server.
+#
+# For this purpose, the ircd understands "kill lines".
+# K:<host/IP mask>:"<opt reason>":<username mask>
+#
+# It is possible to use a file as comment for the ban.
+# K:<host/IP mask>:!<path to file>:<usermask>
+#
+# The default reason is: "You are banned from this server"
+# Note that K: lines are local to the server; if you ban a person or a
+# whole domain from your server, they can get on IRC via any other server
+# that doesn't have them K: lined (yet).
+
+# With a simple comment, using quotes:
+K:*.au:"Please use a nearer server":*
+K:*.edu:"Please use a nearer server":*
+
+# With a file, prepending a '!' before the filename.
+# The file can contain for example, a reason, a link to the
+# server rules and a contact address.
+K:unixbox.flooder.co.uk:!kline/youflooded.txt:*luser
+
+#
+# IP-based kill lines are designated with a lowercase 'k'. These lines
+# use the same format as normal K: lines, except they apply to all hosts,
+# even if an IP address has a properly resolving host name.
+k:192.168.*:!klines/martians:*
+
+#
+# A more flexible way of restricting access to your server is the use
+# of "restriction lines". These tell the server to start up an (external)
+# program, upon whose output is decided whether the client is allowed
+# access. The program should print "Y" or "N <reason>" on its stdout.
+# Note that the use of R: lines is discouraged and deprecated, needs a
+# compile-time define, eats CPU cycles and may well be taken out in
+# future releases of ircd.
+# R:<host/IP mask>:<program name>:<username mask>
+
+#
+# You probably want your server connected to other servers, so your users
+# have other users to chat with.
+# IRC servers connect to other servers forming a network with a star or
+# tree topology. Loops are not allowed.
+# In this network, two servers can be distinguished: "hub" and "leaf"
+# servers. Leaf servers connect to hubs; hubs connect to each other.
+# Of course, many servers can't be directly classified in one of these
+# categories. Both a fixed and a rule-based decision making system for
+# server links is provided for ircd to decide what links to allow, what
+# to let humans do themselves, and what links to (forcefully) disallow.
+#
+# The Connection and Allowing connection lines (also known as C/N lines)
+# define what servers the server connect to, and which servers are
+# allowed to connect. Note that they come in pairs; they do not work if
+# one if present and the other is absent.
+# C:<remote hostname or IP>:<password>:<remote server name>:<port>:<class>
+# N:<remote hostname or IP>:<password>:<remote server name>:<hostmask>:<class>
+#
+# If you wish to use ident, prepend "username@" to the hostname or IP
+# address (the first field).
+# If the "port" field is omitted, the server will not attempt to
+# establish a link with that server ("not autoconnecting").
+# The (optional) "host mask" field tells the server to represent itself
+# with "hostmask" dot-seperateed fields stripped from its servername
+# and replace it with "*.".
+# For example, if hostmask == 2 and the local server name is
+# "irc.sub.domain.com" it would be sent as "*.domain.com". This allows
+# for easier routing and linking of new servers.
+# This feature is not used on Undernet.
+
+# Our primary uplink.
+C:1.2.3.4:passwd:Amsterdam.NL.Eu.UnderNet.org:4400:90
+N:1.2.3.4:passwd:Amsterdam.NL.Eu.UnderNet.org::90
+
+#
+# If your server starts on a bit larger network, you'll probably get
+# assigned one or two uplinks to which your server can connect.
+# If your uplink(s) also connect to other servers than yours (which is
+# probable), you need to define your uplink as being allowed to "hub".
+# H:<allowed hostmask>::<server name>
+H:*.*::Amsterdam.NL.Eu.UnderNet.org
+
+#
+# Of course, the opposite is also possible: forcing a server to be
+# a leaf. L: lines follow Murphy's Law: if you use them, there's a big
+# chance that routing will be screwed up afterwards.
+# L:<opt disallowed hostmask>::<server mask>:<opt max depth>
+
+#
+# For an advanced, real-time rule-based routing decision making system
+# you can use Disallow lines. For more information, see doc/readme.crules.
+# D:<server mask that ircd will refuse to connect to>::<rule>
+# d:<server mask that ircd will not autoconnect to>::<rule>
+D:*.US.UnderNet.org::connected(*.US.UnderNet.org)
+d:*.EU.UnderNet.org::connected(Amsterdam.NL.EU.*)
+
+# The following line is recommended for leaf servers:
+d:*::directcon(*)
+
+#
+# Inevitably, you have reached the part about "IRC Operators". Oper status
+# grants some special privileges to a user, like the power to make the
+# server break or (try to) establish a connection with another server,
+# and to "kill" users off IRC.
+# I can write many pages about this; I will restrict myself to saying that
+# if you want to appoint somebody as IRC Operator on your server, that
+# person should be aware of his/her responsibilities, and that you, being
+# the admin, will be held accountable for their actions.
+#
+# There are two sorts of IRC Operators: "local" and "global". Local opers
+# can squit, connect and kill - but only locally: their +o user mode
+# is not not passed along to other servers. On Undernet, this prevents
+# them from using Uworld as well.
+# Depending on some defines in include/config.h, local operators are also
+# not allowed to /DIE and /RESTART the server.
+# Local operators are designated with a lowercase 'o'
+# O:<host/IP mask>:<encrypted password>:<Nick>::<connection class>
+# o:<host/IP mask>:<encrypted password>:<Nick>::<connection class>
+
+O:*@*.cs.vu.nl:VRKLKuGKn0jLs:Niels::10
+
+# Note that the <connection class> is optional, but leaving it away
+# puts the O: lines in class 0, which usually only accepts one connection
+# at a time. If you want users to Oper up more then once per O: line,
+# then use a connection class that allows more then one connection,
+# for example (using class 10 as in the example above):
+# Y:10:90:0:100:160000
+#
+# When your server gets fuller, you will notice delays when trying to
+# connect to your server's primary listening port. Via the Port lines
+# it is possible to specify additional ports (both AF_UNIX and AF_INET)
+# for ircd to listen to.
+# De facto ports are: 6667 - standard; 6660-6669 - additional client
+# ports;
+#
+# These are just hints, they are in no way official IANA or IETF policies.
+#
+# On a side note, the /UPING command uses port 7007/udp. If your server
+# is located behind a firewall, you may want to make another hole in it
+# for this port.
+#
+# P:<hostmask, or path>:::<client port number>
+
+P::::6667
+P::::6668
+P:*.nl:::6666
+P:/tmp/.ircd:::6667
+
+#
+# Well, you have now reached the end of this sample configuration file
+# If you have any questions, feel free to mail <doco-com@undernet.org>
+# or <wastelanders@undernet.org>.
+# If you are interested in linking your server to the Undernet IRC network
+# visit http://www.routing-com.undernet.org/, and if there are any problems
+# then contact <routing-com@undernet.org> asking for information.
+# Upgrades of the Undernet ircd can be found on http://coder-com.undernet.org/.
+#
+# For the rest: Good Luck!
+#
+# -- Niels.
--- /dev/null
+/************************************************************************
+ * IRC - Internet Relay Chat, 2.4.notes
+ * Copyright (C) 1990 Markku Savela
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+IRC 2.4 release notes 6 May 1990/msa (Markku.Savela@vtt.fi)
+============================================================
+
+ This document explains the changes I have done up to this
+point. Some additional changes and packaging has been made by
+Chelsea (chelsea@earth.cchem.berkeley.edu). This is personal
+view of the changes.
+
+CHANGES TO LAST THE OFFICIAL RELEASE (2.2PL1)
+
+ This release of irc2.4 is based to 2.2PL1 release (see the
+HISTORY chapter later in this document). Aside from fixing the
+bugs, this version is in many ways different from the 2.2PL1.
+The purpose of the most changes is to make it easier to run an
+IRC server. Normal users benefit from these changes indirectly
+by getting a better maintained server.
+
+1. Changes visible to normal users
+
+ Even while mainly fixing bugs, some user visible changes have
+crept in too.
+
+1.1 General note on wildcards
+
+ Many commands accept now wildcard matching where applicable. All
+compares are case insensitive (e.g. "a" == "A"). The wild cards are
+
+ ? matches any single character
+
+ * matches any number of characters, also empty
+ string. [PL1 had a bug, which caused "*du*"
+ not match "....edu"].
+
+1.2 Server supported wildcards for "/who mask" command.
+
+ Protocol message is "WHO mask", where mask can be
+
+ empty
+ 0 List all users [No change from PL1]
+
+ * List all users on the same channel where the user is
+ (or all, if user is on 0) [No change from PL1].
+
+ number List all users on the specified channel [No change
+ from PL1]. Note, if the "mask" begings with a digit,
+ this form is assumed, and the remainder of mask is
+ ignored, e.g. "/who 12*.fi" gives all people from
+ channel 12 and ignores the "*.fi" part.
+
+ mask If the mask is any string, it will be compared
+ *separately* to each information field of the user
+ and if a match is found in any field, that user
+ is included into the list. The fields searched
+ are
+
+ nickname
+ loginname (account name)
+ real name (text shown in parenthesis)
+ hostname (users machine)
+ servername (server he/she is using)
+
+ Note: servername is not usually shown on WHO output,
+ but is included in anyway. Example: finding all users
+ somehow connected with Finnish sites, can be achieved
+ with mask "*.fi".
+
+1.3 Changes to /whois command
+
+ As WHO, also /whois accepts wild cards as a parameters. WHOIS
+returns information for all users whose nickname matches the specified
+mask.
+
+ WHOIS automaticly calls WHOWAS [see below], if the attempted nickname
+is not found.
+
+1.4 Short term "WHOWAS" history
+
+ The server has a short in memory cache of the recent nickname changes
+(the current default is set to 200 last changes). The design goal of
+this is that it remembers changes in last few minutes, there is no
+intention of this to be a long term history. That must be a separate
+project, although it could use the hooks provided by this service.
+
+ "WHOWAS nickname" queries this cache and returns about the same
+information that WHOIS would do, if the nickname is found. Wildcards
+are not accepted here, this is a specifically designed feature. If
+the name is not found, WHOWAS doesn't reply anything. This is because
+the most useful use of WHOWAS is implicitly through "WHOIS".
+
+ This history is also implicitly utilized by KILL command.
+
+1.5 New SERVER-SERVER/SERVER-CLIENT protocol message WALLOPS
+
+ The message ":source WALLOPS :Message" sends the message text
+to all operators that are currently online. Any user can use this
+command, it's not restricted. How this function is activated, depends
+on the client, but if nothing else works, "/quote wallops text" should.
+
+ NOTE:This function will not be fully operational until *ALL*
+ servers have upgraded to version 2.4. Also, operators
+ must be using a client that recognizes this command.
+
+ This is really a hasty addition. But, done this way it follows
+the general IRC message philosophy, where messages are sent only
+to links where they are needed (e.g. WALLOPS goes only to servers
+that have opers online--it's not broadcast to every server).
+
+1.6 General use of wildcarding in server queries
+
+ All commands that previously took a servername as a parameter,
+now accept also a wildcarded mask. The mask is replaced with
+the first matching servername. The following user level commands
+are affected
+
+ /admin server -- administrative info
+ /time server -- local time
+ /version server -- the server version
+ /motd server -- "the message of the day"
+ /info server -- info (usually same on same server version)
+ /stat f server -- statistics information
+ /users server -- users logged on server machine
+
+ Note: Remote capability is a new feature for "info" and "stat"
+commands. Until all servers have upgraded, these commands may not
+reach the intended target and may return the information from some
+intermediate server.
+
+1.7 Marking user AWAY
+
+ v2.2PL1 version and earlier showed the AWAY-state (G) only for
+the local users of the same server. AWAY status could be queried
+only by sending a message to a user. This release (or since msa.4)
+broadcasts the away status to every server and the commands /WHO and
+/WHOIS give this information reliably.
+
+ A side effect of this change is: when a user marks himself/herself
+as AWAY, all pre-msa.4 servers that are reached will send back an
+acknowlegde message. Until all servers are upgraged, use of AWAY
+is somewhat inconvenient. If you get extra messages from AWAY,
+they also contain the server information. Use /admin command and
+send a *friendly* request for the admin to upgrage his/her server
+to a working version, namely 2.4 :)
+
+1.8 Servers don't restrict characters within messages
+
+ The parameter fields of the messages can now contain any characters
+in range 1-255, except '\r', '\n' and '\0'. The client programs should
+by default filter away the "dangerous" control characters, but intelligent
+clients can utilize this change and allow exchanges with foreign
+8-bit (or wider) charactersets. (The actual command parts must still be
+represented with the ordinary 7-bit characters.)
+
+2. Changes visible to the server administrator
+
+2.1 Identifying servers
+
+ Servers/clients have now always two names (it was this way in
+PL1, but I think this version makes the idea more clear):
+
+ Announced Name:
+
+ The official name of the server (the name you use in
+ /time, /quote connect, etc) or users nickname. Servers
+ name is usually the hostname, but can actually be almost
+ any string of characters resembling hostname. This one
+ is given in M-line of ircd.conf.
+
+ Socket hostname:
+
+ Socket hostname of the server or client. This is the hostname
+ of the connecting server/client and this is resolved from the
+ connection. If resolve cannot be done, ircd defaults to using
+ numeric IP-address. *ALL* access checks are based on this
+ name, especially noteworthy fact, if your resolver cannot find
+ hostnames by IP-address, you must allow the access by IP-numbers
+ in your ircd.conf.
+
+ In many places, where servers name is shown, actually both are
+shown. The general format of the displayed name is
+
+ AnnouncedName[SocketHostName]
+
+When a connection is yet unkown, there is no AnnouncedName, and if the
+AnnouncedName is the same as SocketHostName, the "[..]"-part is omitted.
+
+2.2 Many notices to local operators
+
+ If an oper is signed on the server, he/she will receive many
+notices about exceptional conditions and servers actions. When
+something goes wrong, it should be much easier to fix the problems.
+
+ Few often occurring, inportant error messages are
+
+ "Write error to SERVERNAME, closing link"
+
+ write() to socket returned with an error. Server is
+ closing the link. This means usually network problems
+ which you can do nothing about.
+
+ "Max buffering limit exceeded for SERVERNAME"
+
+ This is the situation where old server would have been
+ "frozen". The socket buffers in your OS have been filled and
+ even servers own predefined internal buffering MAX for a link
+ has been exceeded. Exceeding this limit most likely means
+ that the link is really dead, so the server closes the link
+ and scratches all queued output for it. If the limit is
+ set high ( > 20000 bytes), you won't usually see this, but
+ just "No responce from SERVERNAME, closing link" as the
+ server does not reply to PING as it should.
+
+ "Link SERVERNAME cancelled, server SERVERNAME already exits"
+
+ Two different servers from your net fragment attempted
+ to connect same other net fragment about the same time
+ and this collision is detected at your server. IRC routing
+ does not allow loops, the link causing the loop is closed.
+ (Which of the two links gets closed is mostly determined
+ by pure chance and timing--you may lose a better link this
+ way. Collisions should be rare in normal operation, if
+ the timers in "config.h" are not messed up too much...)
+
+ Of course, you get this too, if you try to connect to a
+ server that is already connected by some other route. In
+ that case your attempted connection is just safely cancelled.
+
+ The notices attempt to be self explaining.
+
+2.3 Links statistics collecting
+
+ IRCD now counts the bytes and messages transmitted to each open
+link. This information can be output with a command "/stats l"
+("/stats" or "/stat m" will give the old message count statistics).
+
+ Sample output
+
+Link SendQ SendM SendBytes RcveM RcveBytes Open since
+oddjob.uchicago 0 203 8067 772 34321 Sun May 6 02:15:45 1990
+cs.hut.fi[sauna 0 1916 79798 94 3082 Sun May 6 01:51:25 1990
+otax.tky.hut.fi 0 3722 151511 426 22690 Sun May 6 00:25:54 1990
+nada.kth.se 0 8775 355811 5333 223853 Sat May 5 14:11:49 1990
+vehka.cs.uta.fi 0 23816 882000 901 41156 Fri May 4 22:50:23 1990
+lut.fi 0 25145 943765 1068 35020 Fri May 4 22:34:16 1990
+kreeta.helsinki 0 24286 899191 957 47085 Fri May 4 22:33:28 1990
+naakka.tut.fi 0 27754 1067302 8288 362960 Fri May 4 22:33:14 1990
+joyx.joensuu.fi 0 30003 1172949 2300 80053 Fri May 4 22:33:05 1990
+tel4.tel.vtt.fi 04083771 167473890 863475 35022755 Mon Apr 23 00:15:17 1990
+ | | | | | |
+ | | | | | Link established
+ | | | | The number of bytes received
+ | | | The number protocol messages received
+ | | The number of bytes transmitted
+ | The number of protocol messages transmitted
+ The amount of queued data in bytes (if socket is hung)
+
+ The last row (with the local servername) contains the total
+cumulative counts for all connections since the server was started.
+
+ One can query the statistics of a remote server by adding the servers
+name to the command "/stat l servername". Of course, this only works,
+if all intermediate servers have upgraged. The first "old" server
+will stop the propagation and return the message counts by default.
+
+2.4 Connecting servers
+
+ An oper can manually activate a connection phase to any server
+defined in ircd.conf C-lines (to successfully complete the connection,
+the N-line must be present too). The message achieving this is
+
+ CONNECT servername portnumber
+
+ where servername may be a mask string containing wildcards. This
+name is matched against entries in ircd.conf (notice: the testing
+is made in reverse order, e.g. the last C-line in ircd.conf is tested
+first). If portnumber is omitted, the ircd uses the one given in the
+found C-line. If the C-line does not have the portnumber, the compiled
+default will be used (PORTNUM from config.h).
+
+ This release allows also for remote connecting. An oper can send
+a connect request to remote server with
+
+ CONNECT servername portnumber remoteserver
+
+This command is passed to the 'remoteserver' and it then tries to
+execute it like it was given locally. (If there are opers online on
+that server, they will get a notice about this happening.) Note, that
+one can remotely connect only what is defined in ircd.conf. Usually
+one needs and should use this only for immediate your neighbours. Nobody
+should randomly go and give connect requests to distant servers, unless
+one knows it's absolutely necessary and is very familiar about the
+linking setup there.
+
+2.4 Terminating connections
+
+ The SQUIT command in PL1 was not intended to be used manually and
+was very dangerous to use (it also created so called "ghost servers").
+Since msa.4, the SQUIT has been safer to use manually.
+
+
+ "SQUIT z" s a
+ \ /
+ \ /
+ ------- x ------- y --| |-- z ------- b
+ / ^ \
+ / | \
+ p c
+
+ "SQUIT z" will break the link between "y" and "z" if injected
+ into system from "s". After that the net will be in two fragmets,
+ broken between "y" and "z". Server "z" never sees the actual
+ SQUIT, all it observers is that the link to "y" suddenly closes
+ (opers on z would see it as "Server y closed the connection"
+ notice. Opers on y would see it as "Received remote SQUIT from
+ x", note that the actual source "s" is not identified in the
+ current version--for reasons too complicated to be explained
+ here).
+
+ *WARNING* *WARNING* If the server "y" is still running pre-msa.4
+ (like PL1), don't *EVER* issue a SQUIT for its links (unless the
+ link is to a leaf node or verifiably a "ghost server").
+
+ Note, that when the link between "y" and "z" breaks, y will spit
+ out SQUIT's for "z", "a", "b" and "c" to "x". At same time "z"
+ is sending SQUIT's for "x", "s", "p", etc to "a", "b" and "c".
+ SQUIT is normally generated by servers automaticly, it's just
+ a later modification (msa.4) that allows an OPER to use this
+ same message to "simulate" a link break at certain point.
+
+ *IMPORTANT* If server "z" has configuration "C:y::y:6667", it
+ automaticly attempts to reconnect after a short delay (currently
+ 10 seconds), but only *if* the connection has been up long enough
+ reliably (currently set to 10 minutes). If the thus formed link is
+ squit another time, it will not attempt to come back immeatedly.
+ This gives an oper time to reconfigure the links if that first
+ short delay is not enough.
+
+ As in all commands, also SQUIT accepts wildcards, but be careful to
+give sufficient identification. SQUIT of wrong server is not nice...
+
+2.5 KILL message
+
+ KILL will implicitly use the history database. If a KILL is
+issued for a nick that has been changed to another, the server
+will automaticly re-issue the kill with the new nickname, if
+the change has happened recently (current value should be 90
+seconds). If a "terrorist" is clearly distrupting channel by
+bombarding it with garbage from negative channels and changing
+nick all time, there is no need to consult the "WHOWAS" data
+base, just use the nickname that was used to send the garbage
+and ircd hunts the culprit down. When this change of target
+happens, the oper issuing the kill is notified.
+
+NOTE: With automatic, kill-proof-reconnecting clients, the
+ value of KILL is becoming insignificant...
+
+2.6 Changing the server defaults from the command line
+
+ The servers activation command is now
+
+ircd[ -f configfile][ -h servername][ -p portnumber][ -x debuglevel]
+
+where parameters can be given in any order. If the "configfile"
+is defined, it will override the default specified in the file
+"config.h". If "servername" is defined, it will override the
+one defined in the M-line on the configuration line. "portnumber"
+will override the compiled default (from "config.h") or the
+one from the M-line of the configuration file. The "debuglevel"
+will determine the amout of logging the server does into a
+log file that has been define in "config.h". The "debuglevel"
+should never be defined for a server running normally, it can
+quickly generate megabytes of trace. Usually needed only when
+the server is incapable of starting properly at all, then one
+run with "-x9" usually is enough to reveal the problem.
+
+3. General cleaning up and commenting the code
+
+ This issue is controversial. My way of fixing bugs is not just
+fix them, I also want to program defensively, make it difficult to
+make new errors. Thus I have heavily reformatted and reorganized
+those files that I have had to touch. Some functions have been
+renamed intentionally to catch all uses of those functions [because
+the functions semantics or calling sequences have been changed].
+
+ This release (2.4) will be the last IRC version I'm contributing
+to. If you have any wishes or complains about the code or functioining
+of IRC, use the source or ask whomever it happens to be the current
+developer.
+
+HISTORY
+
+ There have been many different versions of IRC and many of those
+versions are still in use. The following attempt to bring some
+clarification to the versions. This starts from 2.01.6, hopefully
+no servers are running older versions...
+
+ ...
+ ...
+ 2.01.6 A version from WiZ in summer 1989
+ ...
+ 2.01t6 A series of releases, which contained minor
+ 2.01T6 adjustements and bug fixes to the base version.
+ 2.01u6 Some of those fixes caused extra errors, of
+ 2.01U6 this series versions 2.01U6 and 2.01v6 are at
+ 2.01v6 least known to be rather stable.
+
+ 2.1.0 Mike Bolotski created these versions from the sources
+ 2.1.1 of 2.01U6, but unfortunale some devious bug crept in
+ and caused a lots of linking problems (the nasty "ghost
+ server problem" splintered the net constantly). These
+ versions must be deleted on sight :) [Autumn 1989]
+
+ 2.2 This version is the 2.01v6 sources repackaged into
+ multiple directories by Mike again. Probably nobody
+ is running this base version, because is was promptly
+ followed by two patch releases [Autumn 1989]
+
+ 2.2PL0 These two are the last major "official" releases
+ 2.2PL1 and most of the servers upgraged to either of
+ these.
+
+ 2.2msa Unfortunately 2.2PL1 version had a tendency to die
+ mysteriously very often. So, I started to look into the
+ code from March 1990 and that resulted a series of
+ patches to the 2.2PL1 server code, but finally
+ decided to release full server code releases of which
+ few have got wider distribution
+
+ 2.2msa.4
+ Has most of the known PL1 bugs fixed and seems
+ to be very reliable. But once servers started
+ staying up, a new problem appeared: socket
+ buffers started getting full and servers tended
+ to freeze very often for long intervals.
+
+ 2.3alpha
+ 2.3 Is an attempt to make an official release from 2.2msa.4
+ code, but hassles with changed copyrights make this
+ version unacceptable. Besides, 2.3alpha or 2.2msa.4 are
+ now obsolete, old versions :)
+
+ 2.2msa.x
+ To solve the freezing problems, the server code is changed
+ to use non-blocking sockets.
+
+ 2.2msa.7
+ 2.2msa.9
+ Are intermediate test versions, of which .9 seems
+ to have most of the problems solved.
+
+ 2.2msa.10
+ Never released. This is slightly improved version
+ of msa.9, some new features.
+
+ 2.4 Is a release which combines 2.2msa.10 and Chelsea's
+ modifications to the server. Also, this release has
+ once again reorganized the directories and makefiles.
+
+
+-- msa (Markku.Savela@vtt.fi)
--- /dev/null
+ * WHOREPLY and NAMREPLY become numberics instead of strings.
+
+ * msa's patches to kick/mode to attempt to follow nick name changes
+
+ * spike's patches to get SUMMON to find last idle tty
+
+ * melazy's various DNS improvements.
+
+ * pjg's saber C check
+
+ * prefix changed for all server->client messages in which the origin
+ of the sender is a client to appear as follows:
+ nick!user@host
+
+ * TRACE output changed.
+
+ * # channel topics broadcast
+
+ * +-numeric channels removed from server
+
+ * numerics for TRACE output 201-209
+
+ * new switches for STATS: i,k,q,y
+
+ * numerics for stats output 211-219
+
+ * MODE changed to also operate on users
+
+ * deoper added as both mode and direct command. deoper sends out
+ ":user OPER -" to other servers. (from Kaizzu)
+
+ * XTRA/VOICE/GRAPH removed.
+
+ * MODE +a scrapped.
+
+ * added custom ANSI-compatible ctype macros to ensure speed
+
+ * user modes i,w,s,o implemented as follows:
+ i - invisible. over rides any channel mode for those external
+ to your channel. you are invisible.
+
+ w - receive wallops
+
+ s - receive local service notices (errors, etc)
+
+ o - operator flag. (can not be set currently except using the
+ OPER command).
+
+ * MODE +b added to ban a user from a channel using "nick!user@host" as
+ the mask to match to the user. If the user matches the ban mask they
+ are not allowed in. MODE +b with no parameters returns the list of
+ ban masks currently in place. MODE +b <mask> and MODE -b <mask>
+ add/delete a ban mask respectively.
+ Thanks to HulkHogan (andy@lingua.cltr.uq.oz.au) for defining what
+ we needed here and the 'BlackBall' approach.
+
+ * Operator passwords may now be stored in the ircd.conf file as the
+ encrypted plaintext. Crypt(3) is used to generate the matching
+ plaintext from the OPER command. Thanks to the following people
+ for help with this:
+ Sean Batt (sean@coombs.anu.edu.au),
+ Andy. M. Jones (andy@lingua.cltr.uq.oz.au),
+ Nelson Minar (minar@reed.edu).
+
+ * Server now creates "ircd.pid" file when booted. Holds the current
+ pid of the server.
+
+ * Each O and I line in the ircd.conf file may be linked only a number
+ of times equal to or the max. links value for the class they belong
+ to.
+
+ * Server stores results of any successful DNS lookups for servers so
+ that future lookups are not needed. A rehash will wipe all previous
+ lookup results and cause the server to start over. The server will
+ attempt to lookup each hostname in a C/N line on booting. This may
+ cause a delay during starting the server.
+
+ * All functions should be of the form "function_name", macros of the
+ form "MacroName" and constants as CONSTANT.
+
+ * Services are now treated with some respect. A service is associated
+ with an S-line in the ircd.conf. A service must send a NICK/SERVICE
+ pair on connecting to achieve service status.
+
+ * JOIN/PART now accept a list of channels in the first parameter with
+ each separated by a ",". eg "JOIN #foo,#bar,#foobar"
+
+ * A hopcount for the distance to nicknames and servers has been
+ introduced (for better or worse). It is passed as the second
+ parameter to both NICK and SERVER.
+ WHO and LINKS both report the hopcount in the info field.
+
+ * Default for WALL and WALLOPS set "off"
+
+ * rearranged config.h
+
+ * ISON now returns nicknames with the actual case, i.e.
+ ISON wiz will answer WiZ
+
+New into 2.7.1
+
+ * STATS u, r, z
+ u - uptime
+ r - CPU useage stats
+ z - counts and shows current memory useage
+ r & z are only available if DEBUGMODE is defined in config.h
+
+ * GETHOST which forces all reverse lookups of ip#'s to also match
+ when doing a forward lookup of the hostname returned.
+
+ * SENDQ_ALWAYS buffering policy for sending data over links.
+ (Server tries to buffer as much data as possible before attempting
+ a write).
+
+New into 2.7.2
+
+ * NOTE (once again appears and in much better state)
+
+ * WHOWAS gives a list of known users of the nick in question
+ rather than just the most recent.
+
+ * Server can accept both server and client connections via a unix
+ domain socket. This provides greater security and reliability for
+ connections between the host and itself. (#define UNIXPORT)
+
+ * STATS C reports L-lines: New Numeric 241
+
+ * #define for showing all users the invisible count from LUSERS
--- /dev/null
+/************************************************************************
+ * IRC - Internet Relay Chat, doc/MANUAL
+ * Copyright (C) 1990, Karl Kleinpaste
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+ Date: 04 Apr 1989
+ Author: Karl Kleinpaste
+ karl@cis.ohio-state.edu
+
+ Last modification: 15 May 1992
+ by Mauri Haikola
+ mjh@stekt.oulu.fi
+
+ Modified for undernet: 7 Feb 1995
+ by Carlo Wood
+ carlo@runaway.xs4all.nl
+
+
+ INTERNET RELAY CHAT (IRC)
+ a real-time conversational system
+
+
+* 1: IRC - replacement for talk(1)
+
+IRC is a functional replacement for and improvement to talk(1). Talk
+is an old, primitive, atrocious, minimalist sort of keyboard/screen
+conversation tool, using a grotesque, machine-dependent protocol.
+IRC does everything talk does, but with a better protocol, allowing
+more than 2 users to talk at once, with access across the aggregate
+Internet, and providing a whole raft of other useful features.
+
+Note (added Apr 7, 1998): The above statement has been left there for
+historical reasons. It should be noted however that IRC is not any
+longer a replacement for talk(1). At the time IRC was first developed,
+people connected to internet all were using accounts on UNIX Operating
+Systems, which almost all did run a non-restricted fingerd and a talkd.
+This allowed to see if someone was logged in (with finger) and then
+summon him to talk by connecting to his talk daemon. For IRC however it
+is necessary to already be connected to an IRC server and one needs
+to pay attention to the window of the IRC client in order to see if someone
+wants to talk to you. Therefore IRC has become more of a 'chat box':
+a Real Time Chat environment for chatting, making friends and exchanging
+information. It has little resemblance anyore with talk(1).
+
+* 2: Entering Internet Relay Chat
+
+To enter Internet Relay Chat you need to run a client, which will start
+connecting to its default server. More info on clients can be achieved
+from ftp://ftp.undernet.org/pub/irc/docs/faq/underfaq.1. A lot of clients
+for all kinds of Operating Systems and (programming) languages can be
+found in ftp://ftp.undernet.org/pub/irc/clients/index.html.
+
+* 3: How much can be seen from here
+
+The universe - seriously.
+
+This is most formally called Internet Relay Chat. Server hosts are
+connected via a tree structure. The various servers relay control and
+message data among themselves to advertise the existence of other
+servers, users, and the channels and other resources being occupied by
+those users.
+
+* 4: Structure
+
+There is quite a lot of structure to the operation of IRC, as compared
+to crufty old talk(1). Since so little could be done with talk(1), it
+needed little structure. But to keep track of people spread literally
+around the world, the structure is useful so that one can speak to exactly
+those people with whom one wishes to speak. The structure is outlined in
+more detail in the paragraphs below.
+
+** 4.1: Nicknames
+
+All users of IRC are known to the system by a `nickname.' A nickname
+can be chosen at the moment the client connects, but can be changed at
+any time. Nickname clashes are not allowed; this is enforced by the servers.
+If one's intended nickname clashes with someone else as one enters chat, one
+will not be able to complete entry to IRC until one changes one's nickname
+to something else.
+
+** 4.2: Presence on a channel
+
+Fundamental to the operation of IRC is the concept of a channel. All
+users are `on a channel' while inside IRC. One enters the `null
+channel' first. One cannot send any messages while not in any
+chatting channel unless one has set up a private conversation in some
+way. The number of channels is virtually unlimited - whatever will
+fit in a string of 200 characters and starts with a #, & or + sign.
+A channel which is prefixed with a '#' (pound sign) is a global channel;
+available to everyone on the network. A channel prefixed with a
+'&' (ampersand) is a local channel; only available to users on the server
+you are connected to. While a channel prefixed with a + (addition sign)
+are global and modeless; those channels do accept mode changes.
+
+** 4.3: Main modes of #channels
+
+Public
+
+This is the default mode for a channel. When one is on a public
+channel, one can be seen by all other users (if one's own user mode
+permits this). Anyone can notice users on a public channel and join
+such a channel's conversation.
+
+Private
+
+This means that, although anyone can see that one is using chat, no
+one can tell what channel one is using unless one is already on that
+channel with oneself. Since the number of potential channels is in
+the billions, this is quite some security - all one gives away is the
+acknowledgement that one is using chat.
+
+Secret
+
+While one is on a secret channel, no one who is not on one's channel
+with oneself can even see that one is there. One's name does not show
+up in a wildcard search of active users. Of course, making a channel
+like '#test' secret gives a huge change to be discovered anyway.
+
+Changing the mode
+
+The mode of a channel (private, secret, invite-only, moderated,
+topic-limited, person-number-limited, no-messages-to-channel, ban
+someone from channel, etc.) is set by a channel operator, who is the
+first person to join a channel, or someone who has had channel
+operatorship bestowed on them by another channel operator.
+
+Local channels
+
+Channels which are prefixed with the ampersand (&) sign are local
+channels which mean they can only be accessed to users who are on
+the same server. For example, &help may exist on every server on
+the network, however each of them are different channels whereas
+global (#) channels are just one channel for the entire network.
+
+Modeless channels
+
+Channels that have a name that start with a plus sign (+) instead,
+are modeless. This means that nobody is channel operator and hence
+no mode changes can be done. The default mode of a +channel is "+nt".
+The intention of modeless channels is to avoid channel wars by making
+all users on that channel a-priori equal. The only possible abuse,
+channel flooding, should be solved with /ignore.
+
+*** 4.4: Conversations not using channels
+
+It is possible to conduct conversations with others without using the
+formalized channel structure. Doing so requires that two people set
+themselves up for private conversation using special commands; see
+User Commands below.
+
+** 5: Getting help
+
+Type "/help." Follow the instructions. Since this is a client feature
+it might not work for you, in which case you'd have to consult your
+local IRC guru or someone on the net.
+
+** 5.1: User commands
+
+In most clients, commands must start with a '/' (for example: /join #test).
+The most important commands supported by IRC are:
+
+ help quit who whois
+ list topic join part
+ links msg invite silence
+ names stats nick away
+ info clear query ignore
+ mode
+
+Also read the file ADD-TO-IRCRC for a description of Undernet specific
+commands and an example script for the ircII client.
+
+*** 5.1.1: /quit [comment]
+
+/quit exits chat. Optional comment may be included; see above.
+
+*** 5.1.2: /who [channelname_mask | user@host.mask]
+
+/who returns information on who is using chat. Users of public channels
+show up with one of their channels identified, if any. Users of private
+channels appear, but they are specified as being on a private, unspecified
+channel. Users of secret channels and users whose user mode is +i (invisible)
+do not appear at all.
+
+Giving a channel name as an argument to /who returns only those users of the
+specified channel. This still doesn't show users of secret channel or
+invisible users one is actually on the same channel with them. Users
+of private channels are shown, if an exact channel name is given.
+
+For a detailed explanation of the many options of /who, see doc/readme.who !
+
+*** 5.1.3: /whois <nickname>
+
+This returns information about individual users. Type "/whois nickname"
+to get information on the login name and host from which the nicknamed
+user comes. You can specify multiple nicknames to query by seperating
+each with a comma.
+
+*** 5.1.4: /topic <some topic string>
+
+Channels can be given off-the-cuff "topics." Saying "/topic some
+string of text" will associate that topic with the current channel.
+
+*** 5.1.5: /list [options] [channel.mask]
+
+/list will give lists of active channels, the number of users of each,
+and the topics therewith associated. Again, secret channels do not
+appear and private channels only appear as Prv.
+
+[options] is a comma seperated list of one or more of the following options:
+
+ >nnn
+ <nnn
+ C<ccc
+ C>ccc
+ T<ttt
+ and T>ttt
+
+This comma seperated list may not contain spaces.
+Here `nnn' is the minimum or maximum number of users on a channel,
+`ccc' is the minimum or maximum age or creation time of a channel, in
+respectively seconds or UTC. And `ttt' is the minimum or maximum age
+or creation time of the topic of the channel, in respectively seconds
+or UTC.
+
+On most servers, if no options are given, the server will use a
+default option (like "T<10") in order to strongly reduce the of number
+of listed channels.
+
+*** 5.1.6: /join <channel> [key]
+
+/join <channel_name> is the means to enter a channel. Give the channel
+name as an argument. If this is a secret or hidden channel, /who
+commands will show oneself and any other users of one's channel.
+
+One's arrival on a channel is announced to the rest of the users
+already on that channel. Silent, anonymous "lurking" is not
+supported.
+
+If the channel is locked with a key, you need to add the [key]
+parameter which acts as a password (cannot contain spaces).
+
+*** 5.1.7: /msg <nick> <some text string>
+
+A single message can be sent privately to a certain user with /msg.
+Type /msg nickname and the text to be sent. It will be sent privately
+to the indicated nickname.
+
+*** 5.1.8: /invite <nick> <#channel>
+
+If there is a user online to whom one wishes to speak, one may invite
+that user to join oneself on a certain channel. One types "/invite
+nickname" with an optional channel name. The receiving user gets a
+one-line message indicating the sender and the invitation. The
+receiving user is free to ignore the invitation, of course. You
+cannot invite users to a modeless channel.
+
+*** 5.1.9: /ignore <nick!user@host.mask>
+
+If one wants to ignore messages sent by some other user or users, it
+may be done with /ignore command. One can ignore someone by their
+nickname, or by their user@host data. Wildcards may be used. /ignore
+is only intended to ignore annoying public messages (messages sent to
+a channel), to stop flooding (a huge number of messages per second)
+you have to use banning for channel messages, and /silence for private
+messages. /mode <your nick> +d stops all messages from ALL channels.
+
+*** 5.1.12: /silence [nick!user@host.mask]
+
+This command effectively stops private message flooding at the server
+of the flooder. You can use "/silence nick" to get a list of the
+silence masks of 'nick'. This command is undernet specific and therefor
+not supported by all clients unless you add specifically a line to your
+clients configuration file.
+
+*** 5.1.11: /nick <new_nick>
+
+One can change nicknames by issuing "/nick new-nickname." All users
+on one's channel will be informed about the change. NOTE: If one enters
+chat with a nickname clash (e.g., one's login name is the same as
+someone else's, and the other user got there first), the system will
+not let one enter until one issues a /nick command with a unique
+nickname. Nicknames are limited to nine characters in length on the
+Undernet.
+
+*** 5.1.12: /mode #channel [lots of parameters]
+
+This command can be used for altering the various modes of a channel
+(see the explanation of channel modes above). /mode command can only
+be issued by channel operators. Please use /help, or the manual of
+your client to find out about this command.
+
+If you would like a list of the current modes in the channel, type
+/mode <channel> (you do not need to be a channel operator to do this).
+For a list of channel bans, type /mode <channel> +b.
+
+* 6: Questions, problems, troubles?
+
+If you have problems, please get and read the FAQs from
+ftp.undernet.org:/pub/irc/docs/underfaq.1 and underfaq.2.
+You can also ask for help on some of the operator channels on IRC,
+for example #help. They will be able to assist you in whatever
+problems you are having with IRC.
--- /dev/null
+/************************************************************************
+ * IRC - Internet Relay Chat, README
+ * Copyright (C) 1990
+ *
+ * For the list of authors and their e-mail addresses, see
+ * file doc/AUTHORS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+To install the new server, there is just a few changes to be made.
+
+* General Comments
+
+ Tue Nov 13 12:43:46 1990 Armin Gruner <gruner@informatik.tu-muenchen.de>
+
+ (APOLLO: Be sure to have NEED_INET_NETOF, NEED_INET_ADDR and
+ NEED_INET_NTOA undefined.)
+
+ Mon Nov 26 20:10:37 1990 Armin Gruner <gruner@informatik.tu-muenchen.de>
+
+ Comment: I just found that compiling on our SUN SPARC with GCC produces
+ code that dumps core.. (that might not happen on your machine, try it,
+ we may have out-of-date libraries). See netnews gnu.gcc.bug for a dis-
+ cussion about the matter. It seems that gcc passes struct's in a
+ different manner.
+
+* Server configuration
+
+ Edit the include/config.h to your hearts content, avoiding going
+ beyond the warning line, unless you are *absolute* sure you know
+ what you are doing.
+ If you happen to not take to this warning, you may end up with
+ a server that will not function properly and annoy not only you,
+ but users all around the world.
+
+ Old irc client can be found in this package and if you want to use it,
+ check Makefile in directory irc before compiling. There are other,
+ better irc clients in distribution and the client distributed with
+ this version is simply something to begin with if you don't happen
+ to have other clients available.
+
+ This version was brought to you by jto@tolsun.oulu.fi and send any
+ bug fixes and suggestions to me.
+
+NOTE: This server does *NOT* have MAIL system installed by default.
+ The reason is that it doesn't work with many systems.
+
+This version introduces you string channels, starting with
+a plus (+) sign. The first person joining a string channel
+claims it's ownership and after that is entitiled to use /mode
+command. Ownerships can be given and taken away with
+/mode <channel> +o <nickname> and /mode <channel> -o <nickname>
+Other flags are: s - secret, p - private, l - limited, i - inviteonly.
+ m - moderated, n - no private messages to channel,
+ t - topic settable by operator only
+New command /KICK <channel> <user> kicks a user off channel.
+
+--Jarkko (jto@oulu.fi)
--- /dev/null
+
+The available patches for 2.8*mu servers are:
+
+Tp8 = TimeStampPre8 - A protocol which disallows netsplit ops and channel
+ desynchs.
+
+Bquiet - does not allow a person who is banned to speak over the channel
+
+Silence - Cuts off flooding at local server
+
+Anc = Anti-Nick collide - *Intentional* nick collides are impossible with
+ this wonderful patch.
+
+W = Wallops - lets you send messages to other IRC ops
+
+ban = BanInformation - Lets you see who did a ban and when, as well
+
+sw = /stats w - lets you gather statistics on average client connects
+
+To = TopicInformation - Lets you see who set the topic for a channel and when
+
+S = Signon Time - Tells you when a local user signed onto IRC
+
+Cl = Client connect - Notifies opers on your server of client connects/
+ disconnects (with disconnect reason)
+
+TT = Trace Times - displays the time (in secs) since your server last heard
+ anything from a client/server, when you do /trace.
+
+KL = K-line comments - Allows you to modify the lame "no ghosts allowed" default
+ comment to whatever you wish, or alternately, display a
+ file to a rejected client.
+
+OF = Oper fail patch - displays a warning to current ops when someone fails
+ in entering the right oper password.
+
+MC = Mixed case patch - useful for those pesky clone bots and hacked logins;
+ disallows userids which have mixed case or control chars
+
+N = Note - allows you keep a "note" for other users, amongst other things
+
+-----------------------------------------------------------------------------
+Explanations for these patches follow.....
+
+Help on patches written by Mandar Mirashi (mmmirash@mailhost.ecn.uoknor.edu)
+ Mmmm on IRC.
+
+
+=============================================================================
+ TIMESTAMP
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+Info on TS protocol:
+
+ TSpre7
+
+------
+Effects of the TS protocol:
+
+> No mode wars possible.
+ When you take someones op there are three possibilities:
+ - You were too late (You was already de-opped on your server).
+ - You take it (On the *whole* net).
+ - You start taking it (on your server, but it is 'bounced' (ie your mode
+ change is cancelled); This occurs when you try to do mode change
+ direct after a net re-connection in a situation were you hacked op by
+ net-break riding.
+> No desynchronisation possible.
+> No unnecessary MODE msg send. You can't send 'double' mode's that don't
+ make sense. Servers don't send unnecessary MODE's either.
+> Hacking op is from now on *only* possible by admins that hacked their
+ servercode, and therefor easier to prosecute. Also you can 'hack' op still
+ exactly like now (by riding net break) on an *opless* channel.
+> The protocol is fully compatible with older servers: It is invisible
+ and it works with old servers like this: Every 'block' of direct connected
+ 2.8.x.TS servers will stay synchronized, Hacking op is impossible by means
+ of riding a net break between two TS-servers on channels that were created
+ within that block. In all other cases the same happens as without TS.
+
+Here follow technical details implemented in TSpre7:
+
+------
+
+Reference: 2.8.14/15.TSpre7
+Full list of TS-MODE-Change protocol:
+
+-Mode changes (originating by clients) are refused only:
+ 1) from a client that is directly connected and has no chan ops on
+ the channel on its server.
+ 2) when not being a change of the internal state of a server (Well, refused
+ is to strong, propagation of the mode is just unnecessary and thus not
+ done).
+ 3) from someone flagged as de-opped-by-server. (flag is reset when this
+ person is opped again by anyone) (The server detecting this mode change
+ cancels the mode change, by bouncing it upstream, thus keeping the net
+ synchronized).
+
+-An extra parameter is added behind MODE changes *done* by servers, sended
+ *to* other servers. It is a Universal Time in ascii seconds. This extra
+ parameter is NOT sended when it is 0 (can happen with old servers on the
+ net), 0 means <NONE> rather then Jan 1st 1970 :).
+ This parameter is the creationtime of the channel being: the universal time at
+ which the channel was created by a *local* client; or the non-zero received
+ creation time from an other server (as parameter with a mode change) that
+ was earlier then its own; or equal 0 when the channel was created by
+ a non-local client and no MODE with TS was received (yet).
+
+-Of the channel_flags is 1 bit more used: CHFL_DEOPPED, set when de-opped
+ by a server (compare CHFL_CHANOP, set when channel operator). It's reset
+ when opped. (And starts reset on joining (creation?) of a channel, this
+ will be changed to 'set' on join, when all servers have TS; making detection
+ of op hacking by admins a bit easier).
+
+-Timestamps (sended by TS-servers) are handled as follows:
+ Received TS Own TS Bounced/Propagated
+ equal equal propagated
+ later >0,earlier if ops: bounced with own TS
+ if no ops: propagated with own TS
+ (own TS is sended upstream too, to make sure
+ TS stays synchronized).
+ earlier later TS copied, propagated
+ none any propagated, own TS sended.
+ >0 none if ops: propagated, no TS sended, own TS stays 0.
+ if no ops: TS copied, propagated.
+
+-A mode change +/-o nick (+/- v) from a person that is deopped by a server
+ results in a -/+o nick back up stream (and is not propagated) if it was
+ an attempt to change the internal state of the receiving server.
+
+-kick is handled as mode -o, internal state 'not on channel' being 'already
+ de-opped'. Bounce includes JOIN and restoring o and v flags.
+ (Effect: You don't even *see* the kick on one side, on the other side
+ the person joins again and gets his flags back from the bouncing server).
+
+-A received TimeStamp that claims a creation time *earlier* then that
+ this server dissapeared from the net results in a HACK: notice (with
+ time difference in seconds). Bye OPER.. (This meaning, to hack op
+ on an existing channel that was created 15 minutes before you disconnected
+ your server, you will have generated a HACK notice: Clock set back at least
+ 900 seconds by <nick>.) (Not yet implemented, prob. in pre8).
+
+
+ TSpre8
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: *** IMPORTANT; RFC
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Thu, 14 Apr 94 18:03:38 METDST
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Hi, please read this carefully and respond if you think it will result
+in INCORRECT behaviour under any circumstances:
+
+Here follow technical details implemented in TSpre8:
+
+------
+
+Reference: 2.8.17.TSpre8
+Full list of TS-MODE-Change protocol:
+
+-Mode changes (originating by clients) are refused only:
+ 1) from a client that is directly connected and has no chan ops on
+ the channel on its server.
+ 2) when not being a change of the internal state of a server (Well, refused
+ is to strong, propagation of the mode is just unnecessary and thus not
+ done).
+ 3) from someone flagged as de-opped-by-server. (flag is reset when this
+ person is opped again by anyone) (The server detecting this mode change
+ cancels the mode change, by bouncing it upstream, thus keeping the net
+ synchronized).
+ 4) When a '0' as timestamp is received, originating from a server (see below).
+ Then the whole mode is ignored, a HACK notice is sent to all ops and the
+ string is propagated as received.
+
+-An extra parameter is added behind MODE changes *done* by servers, sent
+ *to* other servers *containing* a +o, -o, +v or -v.
+ It is a Universal Time in ascii seconds.
+ Whenever a HACK is detected, a HACK: notice is sent to all local opers,
+ and the full MODE is propagated with a '0' as timestamp, generating
+ a HACK notice on all other servers.
+ Otherwise this parameter is the creationtime of the channel being: the
+ universal time at which the channel was created by a *local* client;
+ or the non-zero received creation time from an other server (as parameter
+ with a mode change) that was earlier then its own; or equal 0 when the
+ channel was created by a non-local client and no MODE with TS was received
+ (yet).
+
+-Of the channel_flags is 1 bit more used: CHFL_DEOPPED, set when de-opped
+ by a server (compare CHFL_CHANOP, set when channel operator). It's reset
+ when opped. It starts *set* on joining (creation?) of a channel, making
+ detection of op hacking by admins a bit easier.
+
+-Timestamps (sent by TS-servers) are handled as follows:
+ Received TS Own TS Bounced/Propagated
+ equal equal propagated
+ later >0,earlier if ops: bounced with own TS
+ if no ops: TS copied, propagated
+ earlier later TS copied, propagated
+ 0 or none any HACK generated, 0 propagated, own TS is kept
+ >0 none TS copied, propagated.
+
+-A mode change +/-o nick (+/- v) from a person that is deopped by a server
+ results in a -/+o nick back up stream (and is not propagated) if it was
+ an attempt to change the internal state of the receiving server.
+
+-kick is handled as mode -o, internal state 'not on channel' being 'already
+ de-opped'. Bounce includes JOIN and restoring o and v flags.
+ (Effect: You don't even *see* the kick on one side, on the other side
+ the person joins again and gets his flags back from the bouncing server).
+
+-A received TimeStamp that claims a creation time *earlier* then that
+ this server dissapeared from the net results in a HACK: notice (with
+ time difference in seconds). Bye OPER.. (This meaning, to hack op
+ on an existing channel that was created 15 minutes before you disconnected
+ your server, you will have generated a HACK notice: Clock set back at least
+ 900 seconds by <nick>.)
+
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: TSpre8 can work! :)
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Wed, 20 Apr 94 11:44:39 METDST
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Well... it took me a few days (a night and some dreams actually), but
+I think I found a solution for the problem I mentioned during the meeting :)
+
+Let me first repeat the problem:
+
+- I stated that TSpre8 would prevent op hacking by admins, but... later
+ I realized that that was impossible the way wanted it :(
+ My idea was at first: Simply generate a HACK notice when a server
+ comes on the net with a creation time earlier then when it did split off
+ (and earlier then my own creation time). This sounds nice, but in
+ even this simple case it doesn't work:
+
+Server A and B, users a and b:
+
+ A -- B
+ |
+ @a TS=100
+
+Split at t=200
+
+ A B
+ |
+ @a
+
+b joins at t=300
+
+ A(TS=100) B(TS=300)
+ | |
+ @a @b
+
+Net joins:
+
+ A -- B
+ | |
+ a b
+
+Both are de-opped: b because he sends a TS of 300 with is greater (later)
+then 100 (correctly: he used the netbreak). And a is deopped with a
+HACK notice by B, because he introduces 1) a TS earlier then the existing
+TS (100<300) and 2) the 100 is earlier then the time the split occured.
+
+The reason why this goes wrong is simply because B *forgets* the channel
+AND the TS of 100, after the split... If B would *keep* in memory that
+the channel existed on A and with what TS, it would be possible, but only
+at cost of a lot of extra memory usage...
+
+Now my new idea :) It allows hacking, but only in not so very interesting
+cases... And at least it makes it extremely difficult for a newbee, so we
+might at least catch 99% before they understand how it works :)
+
+(This explanation should not be on a public ftp site anymore after a while :)
+
+New rules:
+
+- Servers that are OFF the net for more then one day are forgotten.
+- New servers (or forgotten servers), are always bounced except on channels
+ that have no ops (when they create a channel of their own thus :) unless
+ the receiving server is younger then one day and the introduced TS is
+ earlier then the start up time (minus 10 minutes :/) of the receiving server.
+ 'Birthdays' of those servers are also kept.
+- A server that splitted off while a channel already existed, and thus
+ has a creation time earlier then the "received squit time" of that
+ server, is not allowed to introduce an earlier timestamp then the
+ creationtime of the channel (HACK), and also not an equal TS when
+ younger then one day.
+- A server introducing a server with an earlier "time of received squit"
+ inherrits that time as its own "time of received squit".
+
+This allows to hack op on a channel that didn't exist when you splitted
+(not interesting). You also can't keep a server off the net till you need
+it (a telnet connection), because those can't do anything for one day long,
+unless they send the TS *equal* to the existing TS (The only exception :(),
+having to connect between two and one days before the hack, break between
+zero and one day before the hack but before the channel existed, connect
+and hack with equal TS.
+
+What do you think? Just for fun? :)
+
+Apart from that it would be suspicious when someone connects/breaks every
+24 hours a "test" server, channels that exist longer then one day are
+unhackable.
+
+The "disadvantages" are: servers that break off the net for *longer* then
+one day, but keep a channel up with an op, on *both sides of the net, will
+be completely de-opped after reconnection.
+
+*** IMPORTANT:
+
+I am absolutely not sure ;) if there aren't any other disadvantages or
+unwanted effects :) Please, think this over and mail me if you find some
+objection...
+
+Run
+
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: 2.8.19.U3 RELEASED
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Sun, 22 May 94 14:15:41 METDST
+Cc: carlo@sg.tn.tudelft.nl
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Hi all :)
+
+Proud to present: 2.8.19.U3 :)
+
+I have spend *enormous* amounts of time in TESTING this version,
+and I really hope it is completely bug free, but the changes are
+very big, so maybe persons who only want to upgrade/compile ONCE
+should wait a little longer then the compile cracks we have here ;)
+
+For real testing we need the HUBs though! So please, don't hesitate,
+Delft (a HUB) is running it already for a long time, also linked to
+other 2.8.19.U3 test servers.
+
+Before I'll tell about whats new in U3, I want to especially thank
+President for the tremendous help in testing TSpre8 -- I would never
+have been able bring up the stength to go through the difficult
+periods without him being there to listen to my technical complaints ;)
+
+=======================================================================
+
+NEW in .U3
+----------
+
+First all, TSpre8.
+
+It did not become what I hoped/expected to be possible :(
+Hacking will still be possible, but at least it will be a LOT
+more difficult when you don't know what you are doing, and I think
+we WILL catch (new) admins that think they can abuse their powers
+to be GOD on "their" channel.
+
+Especially, nobody will be able to hack *anything* with a normal nick.
+And because server modes are more obvious a hack, this alone is a
+step forward against admin hacking prevention imho.
+
+The .patch file is
+-rw------- 1 carlo users 65142 May 22 01:29 irc2.8.19-TSpre8.patch
+big.
+
+I'll now brows through it and mentions changes in the order they appear
+in the .patch file, arbitrary order thus ;)
+
+Zombies
+-------
+
+As mentioned before on 'wastelanders', I changed the internal way a KICK
+is handled, to be able to stop illegal -hacked- kicks from *outside* the
+channel. This has no effect on server-server protocol nor on server-client
+protocol. But because this way it is possible to keep (a little) memory
+for channels you're not on (being kicked from) I thought it would be no
+more then logical to stop people from joining the usual 10 ten channels
+at the same time, *including* the ones you are kicked from (because they
+occupy memory). This memory is released when you 1) Try to rejoin (so with
+all people having a auto-rejoin-on kick NOTHING changed at all), or 2)
+when you do a part - this is new and only intended to use when you do
+NOT have auto-rejoin, when you do not even WANT to rejoin, or try, assuming
+you might not be banned, when you have been kicked like this of a lot of
+channels and all together are "on" 10 channels so you NEED to leave one
+before you can join another... For this rare case, you must know on
+*which* channels you "are", therefor this is visible when you do a
+/names, or /who or /whois to yourself. It is never visible for others.
+Example:
+
+12:07 * Run (Daryl@sg.tn.tudelft.nl) has joined channel #wasteland
+*** Mode change "+o Run" on channel #wasteland by Wasted
+*** #wasteland : created Fri May 13 17:08:34 1994
+<Macro> Hi Run !
+*** You have been kicked off channel #wasteland by Run (Test)
+*** Run is Daryl@sg.tn.tudelft.nl (/msg Run profile)
+*** on channels: !#wasteland
+*** on irc via server Delft.NL.EU.undernet.org (Runaway Server
++[130.161.188.188])
+*** Run is away: Writting E-mail
+*** Run is an IRC Operator
+*** Run has been idle for 642 seconds.
+
+As you can see, the channel is marked with a '!' to show you are NOT
+not that channel... Both, a part #wasteland as well as a join (being
+not able to actually join because of ban, invite-only, key or limit), will
+remove you from this channel. The part will be sent back to (only) you, and
+everything has turned out to be 100% compatible with ircii protocol.
+Finally, of course the channel is removed when everyone is kicked and/or
+left the channel (a channel with only zombies is removed immedeately).
+
+#define RPL_CREATIONTIME 329
+--------------------------------
+
+A new numeric is sent when you ask for a MODE of a channel, by doing
+/MODE #channel
+without parameters.
+The reply is the same as before, but followed by a new numeric 329:
+
+/MODE #wasteland
+Delft.NL.EU.undernet.org 324 Run #wasteland +t
+Delft.NL.EU.undernet.org 329 Run #wasteland 768845314
+
+To supress this, you'll have to add something like:
+ON ^329 *
+to your .ircrc file. If you want to see this new numeric, you would
+add
+On ^329 "*" echo *** $1 : created $stime($2)
+
+It turns out that ircii clients ask for this mode when you join a
+channel, therefor you will see the creationtime when you join a channel,
+I'll leave it as an exercise to suppress this, but still being able to
+see it when you specifically ask for it :)
+
+New ircd.conf line
+------------------
+
+This is IMPORTANT :
+In order for Uworld to work you MUST add those lines to your ircd.conf file:
+
+U:Uworld.undernet.org::*
+U:Underworld.nl::*
+
+The later to allow the backup Underworld.nl to still function.
+If you forget this, or do it wrong, your server might refuse to accept
+certain mode changes from Uworld.undernet.org and start *bouncing*
+modes done by lusers that got op from it. The name of servers allowed
+to hack have to be agreed upon totally, by all admins. If one admin
+removes his U: line, the service will not work always correctly.
+
+When a server does a mode change that is detected to be a hack, you
+will see -as an oper, or +s luser- this notice:
+
+-> *uworld* opcom MODE #wasteland +o Mmmm
+!Uworld.undernet.org! Run is using Uworld to : MODE #wasteland +o Mmmm
+*** Notice -- HACK: Uworld.undernet.org MODE #wasteland +o Mmmm
+*** Mode change "+o Mmmm" on channel #wasteland by Uworld.undernet.org
+
+Normally, this HACK notice would NOT take effect! You still *see* the
+HACK notice for the U: line server(s) but then they DO take effect.
+
+Every other message (some including the word HACK) do also take effect,
+and are only a warning that someone is MAYBE hacking...
+I didn't see it occur yet.
+
+Removed bugs
+------------
+
+I did find some bugs in TSpre7, never thought that was possible :)
+I forgot what it exactly was, but under (very rare) circumstances it
+could be pretty serious :/
+
+One rather important thing is that now the TimeStamp is sent during a
+net re.join when there are no ops. Before it was possible to create
+a partly TimeStamp less net on an opless channel. TSpre8 garantees
+that the TS is synchronized on the whole net at any time.
+
+Other messages
+--------------
+
+Apart from the (true) HACK notice, you can get a:
+
+BOUNCE or HACK: notice, which does take effect and is most probably
+just a bounce of a mode done by an attacker: someone immedeately after
+a net re.join with his net.ride ops trying to de-op the others.
+I don't think this will happen often because it will be clear to all lusers
+that it is useless to try.
+
+NET.RIDE on opless #channel notice, you'll see this if someone does
+really abuses a net break to get ops on some opless channel. The mode
+does take effect however.
+
+FULL bounce of modes
+--------------------
+
+When before someone would ride a net break, and try something, ONLY
+his +/- o/v modes. Other modes like +mlk 1 \\|/\|/ would still take
+effect. With TSpre8 this changed... All modes (except bans) are bounced
+when someone rides a net break. Also the bouncing is more compact, while
+with TSpre7 every o and v mode took one line, now all modes are kept into
+one line.
+
+More allowed
+------------
+
+Before you was (how lame) not allowed to mix things like k, o and v...
+Now you are allowed, why not? Also you can use up to six parameters,
+really gives you a power kick ;)
+
+*** Mode change "+vvvvvv flux epa Skip Run Mmmm gyn" on channel #wasteland by
++Run
+
+User friendly mask fixing
+-------------------------
+
+The lame way Avalon fixes a mask (for a ban) is like this:
+
+/mode * +bb *.tudelft.nl Daryl@sg*.tn.tudelft.nl
+
+becomes:
+
+*** Mode change "+bb *.tudelft!*@* Daryl!*@sg*.tn.tudelft.nl" on channel
++#wasteland by Run
+
+The same on a TSpre8 server gives:
+
+*** Mode change "+b *!*@*.tudelft.nl" on channel #wasteland by Run
+
+While just Daryl@sg*.tn.tudelft.nl results in:
+
+*** Mode change "+b *!Daryl@sg*.tn.tudelft.nl" on channel #wasteland by Run
+
+which what one would expect!
+
+
+----------------------------------------------------------------
+
+Goodluck with compiling,
+
+Run
+
+PS If you encounter any problems, realize it is VERY unlikely that
+ it is .U3, but if you really think so, then first try to compile
+ plain 2.8.19. If you succeed, save the Makefile the include/config.h
+ and the include/setup.h. Unpack .U3, replace those files and recompile.
+ With this I assume you don't put your ircd.conf inside the source
+ directories of course, that would still have the paths set wrong then.
+
+ A smart move is to make patch file once for your Makefile/config.h :
+ First ONLY edit the Makefile and config.h (or copy the them from a
+ working source tree to a empty directory), and then make a diff with:
+ diff -rc irc2.8.19.clean irc2.8.19.my.makefile > Makefile.config.h.patch
+
+ That really speeds up upgrading with later versions.
+ (irc2.8.19.my.makefile only needs to contain
+ irc2.8.19.my.makefile/Makefile
+ irc2.8.19.my.makefile/include/config.h )
+ Of course, keep the include/setup.h seperately.
+
+### KILL for Mmm. Mmmm (stop it lamer (unnecessary flooding of alexbot))
+
+
+=============================================================================
+ BQUIET
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+Helpful ideas by: Aaron, agifford@sci.dixie.edu, Karll on IRC
+
+
+In order to fight flooding, and as discussed on wastelanders, banning
+someone on a channel will now also stop him from doing anything visible
+on the channel. This allows to let someone see what you think of him
+without even kicking him, he will leave by himself.
+He will still be able to appologise by private msgs of course and then
+he can be de-banned. A ban is this way a short cut for +m+vvvv everyone
+excpet the flooder. Note that even NICK floods are stopped: When you are
+on a channel where you are banned, you are not allowed to change your nick.
+
+=============================================================================
+ SILENCE
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+Helpful ideas by: Aaron, agifford@sci.dixie.edu, Karll on IRC
+
+My solution to flooders with clone bots etc :)
+Let the user that GETS flooded decide he doesn't want that and stop
+the flooder right at his own server (the server of the flooder).
+This is a new command, and the clients will need unfortunately a few
+lines in their .ircrc before it can work.
+Moreover, before this works, ALL servers must have .U3.
+
+The lines I use at the moment are:
+
+ALIAS SILENCE QUOTE SILENCE
+ALIAS SILE QUOTE SILENCE
+ON ^RAW_IRC "% SILENCE %" echo *** $*
+
+It turns out that some auto-rejoin on kick lines, like Davemans toolbox,
+disables the use of ON RAW_IRC, or at least makes it work incorrectly.
+You should disable this auto-rejoin line and you could add the one I use
+instead:
+
+ON ^RAW_IRC "% KICK % % *" {
+ IF ([$3]==[$N]) {
+ //QUOTE JOIN $2
+ ECHO $MID(11 5 $STIME($TIME())) * You have been kicked off channel $2 by $LEFT($INDEX(! $0) $0) \($MID(1 256 $4-)\) } {
+ ECHO $MID(11 5 $STIME($TIME())) * $3 has been kicked off channel $2 by $LEFT($INDEX(! $0) $0) \($MID(1 256 $4-)\) }
+}
+
+which are 6 lines, not 8.
+
+The way the silence patch works is as follows:
+
+When you add a silence mask (using the same user friendly mask fixing)
+like:
+
+/SILENCE Tsunami*@
+
+It will echo back to you how it is added:
+
+*** Run!Daryl@sg.tn.tudelft.nl SILENCE +*!Tsunami*@*
+
+Note that there is a '+' infront of the mask now.
+You can also type:
+
+/SILENCE +bot*
+
+*** Run!Daryl@sg.tn.tudelft.nl SILENCE +bot*!*@*
+
+If you want to silence one particular nick, you must add the '+', because
+when you do:
+
+/SILENCE nick
+
+and 'nick' exists, you will get the silence list of nick. Just using
+/SILENCE gives your own silence list:
+
+*** Run bot*!*@*
+*** Run *!Tsunami*@*
+*** End of Silence List
+
+However, check the silence list of someone ELSE make only really sense
+when you already did sent a message to this person. Because a silence
+mask only propagates to the server of the flooder when it is actually
+necessary. For instance: You can add up to 5 silence masks and delete them
+again and it will only be local to your own server. Only when someone
+would message you, matching a mask, the mask propagates to the server of
+the flooder. And stays there till you signoff, or till you delete it.
+If you delete a mask, it follows the same path as the +masks...
+
+As a result of this, +s lusers and opers will see the message:
+
+*** SILENCE : Unknown command (from Lausanne.CH.EU.UnderNet.org)
+
+When someone from *behind* a non .U3 server sends you a message
+(Lausanne is this case). So, you will STILL be flooded ;) UNTIL ALL
+servers are upgraded... (Or must do -s, but at least the net is flooded).
+
+
+To: wastelanders@rush.cc.edu
+From: agifford@sci.dixie.edu (Aaron Gifford)
+Subject: HELP with HELP for SILENCE
+Status: RO
+
+Hey, here's a VERY VERY VERY rough draft of a HELP entry for SILENCE,
+assuming it becomes a new command for ircII and not a replacement for
+IGNORE either via new code, or aliases like:
+ ALIAS SILENCE { QUOTE SILENCE $* }
+
+Anyway, PLEASE PLEASE PLEASE alter, modify, correct, add, hack-up, etc this
+rough draft and send me your alterations. I just typed this up VERY
+quickly because StGeorge is now running irc2.8.19.U3.1. The bug I mention
+in the file will hopefully disappear very soon, so that text will have to
+do likewise and vanish. :)
+
+Here it is, draft #1 HELP for SILENCE:
+
+Usage: SILENCE [<nick>]
+ SILENCE [+|-<pattern>]
+
+ SILENCE allows you to TOTALLY ignore all private messages (PRIVMSG's)
+ and notices (NOTICE's) from any user whose nick!user@host matches
+ the <pattern> parameter. The characters * and ? can be used
+ as wildcards in the pattern.
+
+ If you wanted to ignore all users from "somewhere.com" you would use:
+ SILENCE +*!*@somewhere.com
+
+ To ignore some with the nickname "Jerk" you would use:
+ SILENCE +Jerk
+ NOTE: The server will complete the pattern, storing it as "Jerk!*@*"
+
+ The only drawback of just SILENCE'ing someone by nickname only is
+ that the user could quickly change nicknames to avoid your pattern.
+
+ To remove a pattern, use a - before the pattern you want to remove.
+ When the command is used without any parameters, the server will list
+ all stored patterns you are currently ignoring with the SILENCE
+ command.
+
+ When used in the first form listed, you will see the SILENCE list for
+ the specified user. This list is not necessarily complete nor accurate
+ because of how servers share SILENCE information internally. You can
+ check to see if someone is ignoring you with SILENCE by first sending
+ that user a private message, then using the command to see the user's
+ SILENCE list.
+
+ Currently there is a bug in the servers (hopefully to be fixed soon)
+ that will add the nickname you specify to your SILENCE list when you
+ attempt to see that user's SILENCE list if that user is not currently
+ online.
+
+ Because SILENCE is a part of the IRC server protocol (on the Undernet)
+ it works much more efficiently than IGNORE, but is limited to a very
+ brief list of patterns. Also, you don't have as may options as you
+ do with IGNORE. If a user is flooding you, SILENCE is many times
+ more efficient than IGNORE because the offending user's PRIMSG's or
+ NOTICE's (including CTCP) are stopped at the server and never even
+ sent to your client.
+
+See Also:
+ IGNORE
+
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: Re: HELP with HELP for SILENCE
+To: agifford@sci.dixie.edu (Aaron Gifford) (Aaron Gifford)
+Date: Wed, 25 May 94 12:29:37 METDST
+Cc: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+In-Reply-To: <9405250313.AA18446@sci.dixie.edu>; from "Aaron Gifford" at May 24, 94 9:20 pm
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+> Here it is, draft #1 HELP for SILENCE:
+>
+> Usage: SILENCE [<nick>]
+> SILENCE [+|-<pattern>]
+>
+
+As it is now (I do not consider what you mentioned as a bug, I was aware
+of this effect and didn't choose to alter it), it really is:
+
+Usage: SILENCE [+|-]<pattern>
+Or : SILENCE <nick>
+
+When you leave the '+|-' away A '+' is assumed.
+
+The second possibility allows you to check your own silence lists, or
+allows to check if you are silenced by someone after you did message
+him but didn't get a respond (did ping him for instance).
+Indeed, this could be interpreted as a pattern, when <nick> doesn't
+exists.
+
+> If you wanted to ignore all users from "somewhere.com" you would use:
+> SILENCE +*!*@somewhere.com
+
+SILENCE somewhere.com
+
+would do.
+
+> When used in the first form listed, you will see the SILENCE list for
+> the specified user. This list is not necessarily complete nor accurate
+> because of how servers share SILENCE information internally. You can
+> check to see if someone is ignoring you with SILENCE by first sending
+> that user a private message, then using the command to see the user's
+> SILENCE list.
+
+Mention that a EVAL CTCP <nick> PING $TIME() is better...
+It would not be necessary to do the silence if the ping returns...
+To determine between huge lag and silence, you have to do the silence
+check after that.
+If you add something like this in the manual, people will automatically
+wait a while after the 'message' (ping), so that the servers have time
+to send the silence mask back. Otherwise they might think they can do
+a quick msg+silence at the same time. Nah... Not too important, unless
+with huge lag + silence combination.
+
+>
+> Currently there is a bug in the servers (hopefully to be fixed soon)
+> that will add the nickname you specify to your SILENCE list when you
+> attempt to see that user's SILENCE list if that user is not currently
+> online.
+
+I didn't consider this as too bad...
+But if people want it, I can change it so that you MUST add a '+' to
+add a mask that doesn't contain a '.', '!' or '@'.
+That would lead to 2.8.19.U3.2 and a very tiny patch for the people who
+already compiled .U3
+
+Run
+
+
+=============================================================================
+ U3 - required additions to .ircrc
+=============================================================================
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: Re: .ircrc codes for the new patches :)
+To: lamberdc@dad.cs.tuns.ca
+Date: Mon, 23 May 94 11:41:31 METDST
+Cc: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+In-Reply-To: <9405222118.AA02415@dad.cs.tuns.ca>; from "Donald "WHIZZARD" Lambert" at May 22, 94 6:18 pm
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+> hiya All
+> I was wondering if some one could send me a copy of the script/
+> for the new patches including the ban , topic and client connecting patches.
+>
+> And whatever is now out with the new .19 code :)
+>
+> thanks
+>
+> -- Donnie
+>
+> aka WHIZZARD
+
+The ftp.undernet.org:/pub/undernet/servers/Patches/README file:
+
+These are lines you need to add to your .ircrc file in order
+to use all posibilities .U3 profides you:
+
+To see when a channel was created:
+
+On ^329 * echo *** $1 : created $stime($2)
+
+When your server has the .ban patch use this to see who did a ban and when:
+
+On ^367 * if ([$4] != []) {echo *** $1 \($3 - $stime($4)) $2} {echo *** $1-}
+
+---------------------------
+When ALL servers upgraded to .U3, you can use this command:
+
+ALIAS SILENCE QUOTE SILENCE
+On ^RAW_IRC "% SILENCE %" echo *** $*
+
+Use this as:
+/SILENCE +mask
+
+To add 'mask' to your silence list (will completely stop all private
+messages from people matching 'mask' with their nick!user@host).
+You can VIEW your silence list by typing:
+/SILENCE
+
+If you message someone and he doesn't react (like with ping), then you
+can check if you match a silence mask he added by viewing his silence list
+with:
+/SILENCE nick
+
+A mask can be deleted by using the command:
+/SILENCE -mask
+
+When the some messages you from behind a NON-.U3 server you will not
+receive his message but the line:
+*** Unknown command (SILENCE) (From server.name.undernet.org)
+instead, so you will still be flooded.
+We hope all servers will be upgraded within a few months.
+
+------
+And my ircd.motd from Delft* :
+
+*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%
+ N E W : - This server now runs the official released
+ beta version 2.8.19.U3.1.ban
+ For you as users this means that:
+ -More security : .U3 contains the .TSpre8 patch with
+ disallows even ADMINs of servers to hack op on your
+ channel with a nick, most server modes are detected.
+ -The possibility to see the *creationtime* of a channel
+ (used with the TimeStamp (TS) protocol - unique on
+ undernet (disables the possibility of 'net.riding'))
+ -The possibility to stop EVERY kind of channel flooding
+ by *banning* someone : Now a ban stops not only
+ part/join floods, but also message floods and even
+ nick floods!
+ -The possibility to see who did when a certain ban.
+ -The possibility to stop anyone flooding you with
+ any kind of private messages at his *own* server!
+ (This will only work when ALL servers have upgraded)
+To be able to use all of this, ftp to sg.tn.tudelft.nl
+login: ftp ; password : anything ; file: /pub/README
+Put those lines in your .ircrc initialisation file !
+Have fun, Run.
+
+----
+
+Run
+
+=============================================================================
+ U3.1 -> U3.2
+=============================================================================
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: *BUG* .U3.1 found !!
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Wed, 25 May 94 16:45:39 METDST
+In-Reply-To: <457.9405250732@ccws-09.brunel.ac.uk>; from "James T Lowe" at May 25, 94 8:32 am
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+> :->
+> :-> Hiya..
+> :->
+> :-> Here's what I observed tonight:
+> :->
+> :-> *** Mmmm (mandar@roosevelt.ecn.uoknor.edu) has joined channel #friendly
+> :-> *** Users on #friendly: @Mmmm
+> :-> *** Mode change "-o Mmmm" on channel #friendly by Uxbridge.*
+>
+> Not surprising :
+>
+> #friendly RedRum H* cs93jtl@ccws-09.brunel.ac.uk
+> #friendly Emmy H lamphear@cheshire.oxy.edu
+> #friendly ChemBot H@ cmrobert@hellcat.ecn.uoknor.edu
+>
+>
+>
+> >From Norman :
+>
+> *** ChemBot is cmrobert@hellcat.ecn.uoknor.edu (Charles Michael Roberts)
+> *** on channels: @#ChatZone
+> *** on irc via server Norman.OK.US.undernet.org
+> *** ChemBot has been idle 10 minutes
+>
+>
+> and from Uxbridge :
+>
+> ** ChemBot is cmrobert@hellcat.ecn.uoknor.edu (Charles Michael Roberts)
+> *** on channels: @#chatZone @#friendly
+> *** on irc via server Norman.OK.US.undernet.org
+>
+> :-> But,
+> :->
+> :-> *** Mmmm has left channel #friendly
+> :-> *** Mmmm (mandar@roosevelt.ecn.uoknor.edu) has joined channel #test
+> :-> *** Users on #test: @Mmmm
+> :->
+> :-> works fine..
+> :->
+> :-> Is this due to the U lines? Uworld was in no way involved though :-(
+> :->
+> :-> I personally feel that uxbridge's retaining timestamps of old channels -
+> :-> Run, can ya take a look asap. It can prove most distressing for our users :(
+> :->
+> :-> Thanks!!
+> :->
+> :-> Mmmm
+>
+>
+
+Weeehhhw, yeah a real bug :/
+
+RedRum and I looked for it for almost 4 hours before it was found...
+
+I will release .U3.2 and a patch for .U3.1-U3.2 asap...
+
+Description of bug:
+
+When someone gets kicked (and doesn't (try to) rejoin), it is flagged
+as a zombie. After a net-break, users are mutual re-joined on both
+sides of the net. It turned out that a zombie is also rejoined after
+a net rejoin.
+
+What happened with ChemBot:
+
+ChemBot was on #friendly via Norman (non TSpre8). It was banned and then
+kicked. It tried to rejoin, but Norman didn't allow that (ban).
+Delft never saw this try, and ChemBot stayed as a zombie on #friendly.
+Then Delft-UxBridge broke and reconnected... Delft did send a JOIN for
+ChemBot to UxBridge, ending up in a nick-desynced state.
+When Mmmm joined #friendly, he was the first on #friendly on all of the
+net except UxBridge... He was opped by Norman, but that is considered
+as a HACK by UxBridge and was bounced (ChemBot was still there *with*
+ops (those flags aren't reset when someone is marked zombie)).
+The second time Mmmm joined, he again got ops from Norman which now
+was accepted by UxBridge because this +o could be a BOUNCE of the de-op
+by UxBridge (Generating a BOUNCE or HACK: notice on UxBridge).
+
+Run
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: Release 2.8.19.U3.2
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Wed, 25 May 94 23:30:57 METDST
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Hi all,
+
+I released 2.8.19.U3.2
+
+Fixed:
+
+ - Rejoining of zombies after net break :/ (ChemBot case)
+ - Silence command now give: No such nick, when doing /silence nick
+ - I fixed the way a kick is handle, because in a last minute
+ thought I realized MURC would get trouble otherwise, see below.
+
+As usual you can get it from ftp.undernet.org:/pub/undernet/servers
+
+Patches/irc2.8.19.U3.1-2.patch : If you already had .U3.1
+
+irc2.8.19.U3.2.tar.gz : If start from scratch (DO SO!!!)
+
+For those who use the irc2.8.19.U3.1-2.patch ...
+
+------------------------------------------
+*** EDIT include/patchlevel.h !!!!!!!! ***
+------------------------------------------
+
+This patch will change your version to irc2.8.19.U3.2 so if you run
+ .ban EDIT it !!!
+
+=========================================================================
+
+The change in KICK handling is as follows:
+
+- A kick received from a local client, or for a local client or
+ from a direction in which the kicked client is located, is
+ simply handled as before: completely removed from channel, no zombie.
+ This means also that there is no client-server protocol change anymore:
+ /who, /whois and /names won't change.
+
+- A kick received for a local client originating from a remote client
+ lets the server sent a PART upstream. Since this results for non TSpre8
+ servers in a remote "You're not on that channel" message, this
+ message is suppressed (would never occur anyway now we are completely
+ synced).
+
+The reason why this was needed is mainly because MURC constantly kicks
+people who have auto-rejoin disabled from different channels. With U3.1
+they would get into problems after ten channels (needed to send extra
+PART's).
+
+Run
+
+--
+-------------------------------------------------------------------------------
+| carlo@sg.tn.tudelft.nl | Run @ IRC |
+| | Admin of Delft.NL.EU.undernet.org |
+| * Don't expect anything of live, | and Ircserver.et.tudelft.nl |
+| or you'll miss all the rest of it.| |
+-------------------------------------------------------------------------------
+
+
+
+=============================================================================
+ U3->U4, ANTI NICK COLLIDE
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+
+Hi all...
+
+After we dealt with channel msg-, join/part- and nick-floods (.bquiet),
+and with private message flooding (.silence), now in a sort of follow up
+to the anti net.break.ride (.TSpre7) and anti admin.hacks (TSpre8) we are
+about to deal with one of the last vulnerabilities of our net:
+nick-collision bots.
+Called .anc (anti nick collision).
+ - - -
+
+Socially spoken it does the following (I hope):
+
+- Only kills the RIGHT person, when a nick collision occurs.
+
+This would mean:
+
+A) If someone tries to harash by connecting to servers that just broke off
+and then take the nick of a person on the other side, both would be
+killed on reconnection. But with the .anc patch on both connecting servers,
+only the net.break rider will be killed.
+
+B) Secondly, when your server (or side) breaks off and you connect to some
+other server on the other side, it happens sometimes that due to lag you get
+killed by a nick collision after reconnection of the net.
+
+A and B differ strongly in the sense that in case A the *new* -the youngest-
+nick must be killed, while in case B the *old* (lagged) nick must be
+killed.
+Technically this means that we have to look at the user@host.name too.
+This gives rise to some incompatible changes, and therefor, this patch
+must be done in two steps: First we deal with the nick-collision bots and
+make the server compatible with both - the old and new protocol. And then
+once all server upgraded, we deal with the last step: the nick collision
+with yourself.
+
+In the future there will be a third possible condition in which we can have
+a nick collision: (long example follows, can be skipped)
+
+C) The net breaks, and reconnects else where, and somehow a race condition
+occurs between the 'SERVER' messages of the servers of one side.
+For example:
+
+Servers: Part A Part B1 PartB2
+Nicks a(A),b(B) a(A),b(B) a(A),b(B)
+Part A breaks off Part B:
+ <-- :b QUIT --> :a QUIT
+ <-- SQUIT serversB --> SQUIT serversA
+Result: a(A) b(B) b(B)
+A reconnects to Part B1, but immedeately breaks off again:
+ -->SERVERs A
+ -->NICK a
+ -->:a USER ...
+Break:
+ -->SERVERs A
+ -->NICK a
+ -->:a USER ...
+ --> :a QUIT
+ --> SQUIT serversA
+A connects to part B2, note that 'part B1 --> part B2' is lagged and the
+"SERVERs A ... etc" didn't arrive yet on partB2.
+Servers: Part B1 Part B2 Part A
+Nicks: b(B) b(B) a(A)
+ -->SERVERs A
+ -->NICK a
+ -->:a USER ...
+ --> :a QUIT
+ --> SQUIT serversA
+ --> SERVERs B
+ --> NICK b
+ --> :b USER ...
+ <-- SERVERs A
+ <-- NICK a
+ <-- :a USER ...
+Result *before* the lagged messages arive on Part B2:
+Nicks: b(B) b(B),a(A) b(B),a(A)
+ -->SERVERs A
+ -->NICK a
+ -->:a USER ...
+ -->:a QUIT
+ -->SQUIT serversA
+And when the lagged messages arrive on Part B2, the
+'SERVERs A' get all ignored: "server exists", even more, Part B2 disconnects
+Part B1... :/. Now we are going to deal with the "server exists" problem
+*once* (attaching a timestamp to SERVER I think), in which case 'SERVERs A'
+would only be ignored by Part B2. Then the 'NICK a' would cause a nick
+collision with 1) Same user@host.name, 2) same server A, and 3) same
+nick-TS ! This means: We need to ignore 'NICK nick' when is has the same
+TimeStamp and the same user@host. But when the user@host differ, two people
+signed on at exactly the same time with the same nick and we must kill
+*both* to avoid a desync.
+----
+
+How to handle a nick collision, general
+---------------------------------------
+
+Up till now when a nick collision occurs, both nicks (when a nick change
+from 'old' to 'new' is involved) are KILLed in ALL directions.. wiped off the
+net thus.
+But even with wiping off the net in mind, it doesn't make sense to kill in
+old direction, it is sufficient to deal with "our side" of the net, because
+every nick collision occurs on two servers, both can deal with their side.
+As an example:
+
+Servers: A B
+Nicks: a(A) a(B)
+Reconnection:
+ <-- NICK a
+ NICK a -->
+
+As you see, if A receives the 'NICK a' from B, it only has to deal with
+its own side, because it is certain that B will receive the 'NICK a' from
+A and handle it too as a nick collision (and therefore this 'NICK a' *is*
+already a 'KILL' message).
+
+Thus, when we receive a 'NICK' that gives rise to the need of purging
+a nick on *our* side, we deal with it by doing:
+sendto_serv_butone(cptr,":%s KILL ...
+which sends the KILL to all server connections except the link 'cptr' that
+generated the nick collision.
+Also then we have to destroy the memory usage of the killed client on our
+own server, and disconnect him if it is ours, so we call exit_client().
+
+Summary so far
+--------------
+
+Ok, we discussed when to kill who. Resulting rules are:
+
+We receive a "NICK new" or ":old NICK new" from a server direction, and
+we already have a registered 'new'. Then we have a nick collison and deal
+with it as follows:
+1) If the user@host differ,
+ and our 'new' is younger or equal, KILL our 'new'.
+ and our 'new' is older, ignore the 'NICK new', but kill 'old' on
+ our side if it was a nick change.
+2) If user@host is the same:
+ and our 'new' is older, KILL our 'new'.
+ and our 'new' is younger, ignore the 'NICK new', but kill
+ 'old' on our side if it was a nick change.
+ and our 'new' is equal, KILL our 'new',
+ and kill 'old' on our side too if it was a nick change.
+
+Remarks:
+ The last case, where have a ':old NICK new' collission with
+the same user@host and TimeStamp, can't be case C), but it
+is possible that *we* did send a 'NICK new', and the server at
+the other side kills 'old' off... So we have to do it too.
+ With this protocol we *ignore* 'NICK new' message, but of course
+in most cases they will be followed by at least a ':new USER ...' and
+probably
+more like ':new JOIN #...'. This would cause 'Fake direction' errors and
+the current protocol KILLs such a nick in *ALL* direction (???). Now, we
+DON'T want to KILL the nick in the right direction do we? And killing the
+fake direction nick makes no sense: it will be killed automatically due to
+the fact we did send a 'NICK new' in that direction. Thus: we can/have to
+remove the Fake Direction kills.
+ Of course, when we receive a 'NICK new hopcount :TimeStamp', we
+*can't* compare with the user@host, because it takes some time before we
+will receive the 'USER'... We also can't store the nick, because it must
+be unique. To solve this, we need to change the protocol so that 'NICK new'
+contains all information and the USER message will be redundant and removed
+in a later patch. This also reduces net.bursts.
+
+Attaching a TimeStamp (TS) to nicks
+-----------------------------------
+
+Whenever someone takes a new nick, which is available of course, either by
+changing nick or by signing on, the local server attaches a TimeStamp (TS)
+to the nick. Now there will be sent:
+
+NICK new hopcount TS user host.name server.name :Real name
+or
+:old NICK new :TS
+
+This is 100% compatible with the existing protocol.
+
+When a server receives such a nick from a neighbouring server it copies the
+TS, keeping track of the local change time. (When not all servers have
+upgraded, and no TS is received, the .anc server will fill in the time
+itself - being a slight advantage over keeping it 0. It also will assume
+that the host.names are equal or not equal resulting a as many kills as
+needed in the worst case).
+
+
+Examples
+--------
+
+Servers: A B
+Nicks: a(A),b(B) b(B),a(A)
+Both change simultaneously to nick 'c', but 'a' slightly faster (at time=1,
+and b at time=2):
+ c(A),b(B) c(B),a(A)
+ -> :a NICK c :1
+ :b NICK c :2 <-
+Then A receives a ':b NICK c :2' where 2 > 1, and KILLs b on its own side.
+B however receives ':a NICK c :1' where 1 < 2, and KILLs c on its own side.
+Result: c(A) c(A)
+
+Due to 'lag', more :c PRIVMSG .. from B to A can follow, resulting in a
+fake direction. 'A' will simply ignore them and not kill c (kills for
+fake direction are nonsense anyway).
+
+In the case that someone signs on, taking the same nick as a nick change
+we have almost the same:
+
+Servers: A B
+Nicks: a(A) a(A)
+'a' changes simultaneously to nick 'c', but slightly faster (at time=1),
+as a new 'c' signs on at B (time=2).
+ c(A) a(A),c(B)
+ -> :a NICK c :1
+ NICK c 1 :2 <-
+Then A receives a 'NICK c 1 :2' where 2 > 1, and ignores it simply.
+B however receives ':a NICK c :1' where 1 < 2, and KILLs c on its own side.
+Result: c(A) c(A)
+
+If the new 'c' was a little bit earlier, we get:
+
+Servers: A B
+Nicks: a(A) a(A)
+'a' changes simultaneously to nick 'c', and slightly slower (at time=2),
+as a new 'c' signs on at B (time=1).
+ c(A) a(A),c(B)
+ -> :a NICK c :2
+ NICK c 1 :1 <-
+Then A receives a 'NICK c 1 :1' where 1 < 2, and KILLs c on its own side.
+B however receives ':a NICK c :2' where 2 > 1, and KILLs a on its own side.
+
+Result: c(B) c(B)
+
+Last case, two people sign on (or during a net reconnection):
+
+Server: A B
+Sign on: c(A) c(B)
+ -> NICK c 1 :1
+ NICK c 1 :2 <-
+Then A receives 'NICK c 1 :2' where 2 > 1, and ignores it.
+and B receives a 'NICK c 1 :1' where 1 < 2, and KILLs c on its own side.
+Result: c(A) c(A)
+
+Note: the above didn't take equal TS's into account, and I assumed
+user@hosts to be different: the nick collision bot cases.
+
+A last possibility when someone starts hacking... a 'NICK new' twice
+from the same direction, should not be accepted: we kill the earlier one
+always.
+
+Compatibility problems
+----------------------
+
+In the future, we want to also take 'user@host' into account... Therefor,
+we need to start to ignore 'NICK new' and only act upon ':new USER ...'.
+We can only do that if also 'USER contains the hopcount and TimeStamp'...
+If we change the protocol immedeately to say:
+:nick USER user host.name server.name hopcount TimeStamp :Real name
+the 'hopcount' would be treated as realname, and we the rest would be
+lost :(
+
+We can also transfer the info to 'NICK', with:
+
+:server.name NICK nick hopcount user host.name TimeStamp :Real name
+
+and later forget the USER message. Although someone lamer uses
+the C source line " if (sptr == cptr) ..." in m_nick() to determine if
+it was a 'NICK new' or a ':old NICK new' :/ Geesh (unlogical). He should
+have used " if (IsServer(sptr)) ...". You would need three upgrade steps
+or we will have to do with:
+NICK nick hopcount user host.name server.name TimeStamp :Real name
+
+The nice thing about this is, that we can check how many parameters we
+receive and then immedeately use the user@host if it is there... That way
+the .acn will immedeately work once everyone upgraded once, and the second
+step would only get rid of the 'USER' line between servers.
+
+Run
+
+
+=============================================================================
+ WALLOPS
+=============================================================================
+Usage: /WALLOPS <message>
+
+Sends a message to IRC ops on-line. Remember that users who are /umode +w
+can see wallops as well.
+
+
+=============================================================================
+ STATS W
+=============================================================================
+Author: Michael Vanloon (michaelv@iastate.edu) - mlv on IRC
+Help on /stats w :
+
+I've been working on something I think would be quite a useful
+addition to the ircd. It keeps track of the average number of local
+clients, total clients, and total connections (including servers) over
+various periods of time, currently over the last minute, hour, day and
+week. It is invoked by "/stats w server.name". You may try it
+yourself by "/stats w *.iastate.edu". A sample from
+ircserver.iastate.edu looks like this:
+
+*** Minute Hour Day Week Userload for:
+*** 44.91 39.4 33 33 iastate.edu clients
+*** 114.40 103.2 69 65 total clients
+*** 120.40 109.0 73 70 total connections
+*** * End of /STATS report
+
+I'm debating changing it to show average connections over the last
+minute, hour, day, prev. day, and prev. day, as I think the data
+doesn't change enough after that to really be useful to justify
+keeping it over an entire week.
+
+On smaller, less used servers, it should add a negligible amount to
+the resident memory consumed by the ircd. On a large hub such as the
+*.bu.edu servers, penfold, or ircserver.iastate.edu, it might add as
+much as 300k to the amount of memory the ircd attempts to keep
+resident. The amount is determined solely by the number of
+connects/disconnects the server processes over the span of time
+measured.
+
+The code is bulletproof and has undergone *extensive* debugging and
+testing before I announced it here.
+
+The reason I'm posting this is because I would like to know how many
+people think this would be a useful addition to the server. In
+addition, I'd like feedback on whether you think this should be a
+standard part of the distributed server code. I think it should, but
+Avalon wants me to post here first and see how others feel about it.
+I'd appreciate your input.
+
+I will be making a patched 2.7.2 server available with this included,
+for those who would like to have this and stability too. I'll also be
+hooking it into 2.8.x soon, and giving it back to Av to include in the
+standard 2.8 distribution as it matures, if he sees fit to do so.
+
+Thanks for your time...
+
+ --Michael (mlv)
+
+IRC log started Wed Aug 18 21:52
+*** Value of LOG set to ON
+*mlv* it's the usage of your server
+*mlv* average number of users on your server over the last minute, hour, day, yesterday, and the day before
+*mlv* local clients, total clients, and total connections (clients + servers)
+-ircserver.iastate.edu- Minute Hour Day Yest. YYest. Userload for:
+-ircserver.iastate.edu- 23.00 23.0 16 17 11 iastate.edu clients
+-ircserver.iastate.edu- 52.00 52.8 37 35 23 total clients
+-ircserver.iastate.edu- 61.00 61.7 45 42 21 total connections
+-> *mlv* hmm...so iastate had 23 local clients in the last minute?
+*mlv* right... in the last minute the average number of local users on our server was 23
+*mlv* 23.45 actually
+-> *mlv* okie...gotcha... thanks :) one other thing
+*mlv* there were an average of 23.1 local users on here over the last hour
+*mlv* shoot
+-> *mlv* is it possible to specify multiple domains?
+-> *mlv* for e.g. uoknor.edu and okstate.edu cos those will be local to midway
+*mlv* it could be coded in, but 1) my code doesn't support it out of the box, and 2) that would add more state info which would increase the memory usage a bit
+-> *mlv* hmm...
+*mlv* it's purely informational... i wouldn't worry about it, really that much
+-> *mlv* okay...also, the Makefile on the ftp site seems hosed.....there's junk at the end...I tried both the .Z and the .gz
+*mlv* i'm thinking about making it log by connection class... but i'll have to use a more efficient statistical algorithm (less precise) if i do that
+*mlv* that way you could put all the local domains into certain classes
+*mlv* oh yeah... it's harmless, just weird
+-> *mlv* that'll work :)
+-> *mlv* well...thanks for your help....will have a look at the stats w patch when you're finished with it :)
+IRC Log ended *** Wed Aug 18 22:22
+
+
+=============================================================================
+ BAN/TOPIC/SIGNON INFO
+=============================================================================
+Author: Paul Foley (pfoley@kauri.vuw.ac.nz) SIO on IRC
+
+Help on Ban/Topic/Signon :
+
+Since these patches allow the server to maintain additional information, the
+server replies have been changes for the /mode #channel +b (#367), /whois
+(#317) and an additional reply #333 has been added for topic info. The time
+is returned as a long integer in UTC format in all cases. Since the existing
+clients will ignore this additional information, you will need to either
+patch the client, or in case you're using ircII, use the foll. /on statements
+to take benefit of these patches :
+
+on ^367 * if ([$4] != []) {echo *** $1 \($3 - $stime($4)) $2} {echo *** $1-}
+on ^333 * echo *** Topic for $1 set by $2 on $stime($3)
+on ^317 * if (index(012345679 $3) != -1) {echo *** $1 has been idle for $2 seconds. Signon at $stime($3)} {echo *** $1 has been idle for $2 seconds.}
+
+
+Once you have done this, you can see info as follows:
+/mode #wasteland +b
+*** #wasteland (Mmmm - Thu Aug 19 04:44:24 1993) test!*@*
+
+/topic #wasteland
+*** Topic for #wasteland: We all love Axl Rose!
+*** Topic for #wasteland set by rbarnes on Thu Aug 19 04:26:56 1993
+
+/whois Mmmm
+*** Mmmm is mandar@essex.ecn.uoknor.edu (Mmmm,I get high with a little help
++from my friends)
+*** on channels: @#wasteland
+*** on irc via server essex.ecn.uoknor.edu (MIDWEST HUB..HELPS YOU GET THERE
++SOONER)
+*** Mmmm is an IRC Operator
+*** Mmmm has been idle for 454 seconds. Signon at Wed Aug 18 23:47:19 1993
+
+
+=============================================================================
+ CLIENT NOTIFY
+=============================================================================
+Authors: Patrick Ashmore (pda@engr.engr.uark.edu) - Twilight1 on IRC
+ Mandar Mirashi (mmmirash@mailhost.ecn.uoknor.edu) - Mmmm on IRC
+ Tony Vencill (vencill@iastate.edu) - Tony/Tonto on IRC
+
+Help on Client Notify:
+
+This patch allows all opers on your server to see clients connecting to your
+server and disconnecting from it (with the reason why they disconnected).
+This can help you identify troublesome clients, or redirect remote clients
+to closer servers, or troubleshoot the reason why a client disconnected.
+
+A sample output:
+
+*** Notice -- Client connecting : Karll (agifford@sci.dixie.edu).
+
+*** Notice -- Client exiting : Karll (agifford@sci.dixie.edu) [Bad link?].
+
+PS: if you wish to selectively decide when you wish to see these connection
+notices, use the foll. script
+
+on ^server_notice "% % NOTICE -- CLIENT*" if (hideit != 1) {echo *** $2-}
+alias show @ hideit = 0;echo *** You can now see clients connecting/exiting
+alias hide @ hideit = 1;echo *** You will no longer see clients connecting/exiting
+
+If you then wish to not see client connects/exits when you are opered, simply
+type /hide. If you wish to see them again, type /show.
+
+=============================================================================
+ TRACE TIMES
+=============================================================================
+Author: Tony Vencill (vencill@iastate.edu) - Tony/Tonto on IRC
+
+Help on Trace Time:
+
+ This useful patch lets you identify the times since your server last
+heard from any connected servers or clients. This time is displayed in
+seconds, appended to each line of your /trace output. It can be very
+helpful in identifying which servers are going to drop off or which
+clients may ping timeout from your server.
+
+A sample output:
+
+/trace essex*
+*** Serv [30] ==> 10S 8C cancun.caltech.edu *!*@essex.ecn.uoknor.edu 73
+*** Serv [30] ==> 9S 6C imageek.york.cuny.edu *!*@essex.ecn.uoknor.edu 27
+*** Serv [0] ==> 1S 0C inga1.acc.stolaf.edu[130.71.192.16]
++*!*@essex.ecn.uoknor.edu 58
+*** Serv [0] ==> 1S 0C shadow.acc.iit.edu *!*@essex.ecn.uoknor.edu 97
+*** Serv [0] ==> 1S 2C curie.ualr.edu Mmmm!mmmirash@essex.ecn.uoknor.edu 98
+*** Serv [0] ==> 1S 1C piaget.phys.ksu.edu *!*@essex.ecn.uoknor.edu 117
+*** Oper [0] ==> Mmmm[essex.ecn.uoknor.edu] 0
+*** Serv [50] ==> 1S 0C pv1629.vincent.iastate.edu *!*@essex.ecn.uoknor.edu 7
+*** Class 0 Entries linked: 6
+*** Class 50 Entries linked: 1
+*** Class 30 Entries linked: 2
+
+
+=============================================================================
+ K- line comments
+=============================================================================
+Author: Mandar Mirashi (mmmirash@mailhost.ecn.uoknor.edu) - Mmmm on IRC
+
+This extremely useful patch allows you to specify either a comment or an
+interval in the 2nd field of the K line (instead of the previous format
+of just a restricted interval). The "comment" can even be configured to
+be a *file* name which can then be displayed to the client rejected via
+the K line. All existing K lines will work as they are. This patch is
+an *enhancement* to the K-lines.
+
+e.g. (of a comment field)
+
+K:*.sdsu.edu:Flooding.is.not.cool.lamer:masc0482
+
+As far as possible, do not use a white space in the comment field, because
+this breaks ircII's /stats k handling. You can use the period (.) or the
+underscore instead (_).
+
+e.g (of a file to be output to a rejected client
+ - #define COMMENT_IS_FILE in config.h)
+
+K:*.netcom.com:/ecn/staff0/irc/servers/vinson/sorry.txt:*
+
+
+=============================================================================
+ OPER FAIL
+=============================================================================
+Authors: Michael Vanloon (michaelv@iastate.edu) - mlv on IRC
+ Jon C Green (jcgreen@iastate.edu) - Jon2 on IRC
+ Bryan Collins (b@ctpm.org) - bwy on IRC
+
+This patch notifies you if someone tries to gain oper on your server and
+fails. The notice goes out only to the operators on the server.
+
+*** Notice -- Failed OPER attempt by M (mmmirash@lincoln.ecn.uoknor.edu)
+[crackit]
+
+
+=============================================================================
+ MIXED CASE
+=============================================================================
+Authors: Michael Vanloon (michaelv@iastate.edu) - mlv on IRC
+ Jon C Green (jcgreen@iastate.edu) - Jon2 on IRC
+
+"Here's a patch mlv and I wrote that prevents clients with mixed-case usernames
+from connecting. I don't know of many sites that allow mixed-case, so it
+is effective for stopping many clonebot attacks and also stops many
+would-be username hackers." - Jon2
+
+This has been slightly modified by Mmmm to give an option of ignoring the
+case of the first character if desired (useful for those PC users who
+intuitevely enter their first name with the first letter capitalised).
+
+e.g.
+*** Notice -- Invalid username: buankBOT[saucer.cc.umr.edu]
+
+
+=============================================================================
+ NOTE
+=============================================================================
+
+Usage:
+ \ 2NOTE\ 2 USER [&passwd] [+-flags] [+-maxtime] <nick!username@host> <msg>
+- or SEND|SPY|FIND|WAITFOR|NEWS <same as USER args.>
+* or SEND|SPY|FIND|WAITFOR|WALL|WALLOPS|DENY|NEWS|KEY <see USER args.>
+ \ 2NOTE\ 2 LS|COUNT|RM|LOG [&pwd][+-flags][#ID] <nick!user@host> [date]
+ \ 2NOTE\ 2 FLAG [&passwd] [+-flags] [#ID] <nick!username@host> <+-flags>
+* \ 2NOTE\ 2 SENT [NAME|COUNT|USERS] <f.nick!f.name@host> <date> [RM]
+- \ 2NOTE\ 2 STATS [MSM|MSW|MUM|MUW|MST|MSF|USED]
+- \ 2NOTE\ 2 SENT [NAME|COUNT]
+* \ 2NOTE\ 2 STATS [MSM|MSW|MUM|MUW|MST|MSF|USED|RESET] [value]
+* \ 2NOTE\ 2 SAVE
+
+ The Note system have two main functions:
+ 1. Let you send one line messages to irc users which
+ they will get when they next sign on to irc.
+ Example: NOTE SEND <nick> Hi, this is a note to you.
+ 2. Let you spy on people, to see when they sign on or off,
+ change nick name or join channels.
+ Example: NOTE SPY +100 <nick> (spy on nick for 100 days)
+
+ You may fill in none or any of the arguments listed above, including
+ * or ? at any place, as nick@*.edu, !username, ni?k!username etc...
+ Other usefull features may be NOTE WAIT <nick>, making nick and
+ you get a message when you both are on at the same time.
+
+ Note was developed 1990 by jarle@stud.cs.uit.no (Wizible on IRC).
+
+
+*Usage: NOTE [<server>] ANTIWALL
+* Switch off b flag for wall's which you have received on specified
+* server. The person who queued the wall would be notified about
+* the antiwall, and who requested this.
+
+
+Usage: NOTE COUNT [&<passwd>] [+|-flags] [#<ID>] <nick!username@host>
+ Displays the number of messages sent from your nick and username. See
+ HELP LS for more info. See HELP FLAG for more info about flag setting.
+
+
+*Usage: NOTE DENY [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+* <nick!user@host> <msg>
+* DENY is an alias for USER +RZ (default max 1 day)
+* This command makes it impossible for any matching recipient to
+* queue any Note requests until timeout.
+
+
+Usage: NOTE FIND [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+ <nick!username@host> <msg>
+ FIND is an alias for USER +FLR (default max 1 day)
+ This command makes the server search for any matching recipient, and
+ send a note message back if this is found. If <msg> field is used, this
+ should specify the realname of the person to be searched for. Examples:
+ FIND -4 foo*!username@host
+ FIND @host Internet*
+ FIND nicky Annie*
+ FIND +7 * Annie*
+
+
+Usage: NOTE FLAG [&<passwd>] [+|-<flags>] [#<ID>]
+ <nick!username@host> <+|-flags>
+ You can add or delete as many flags as you wish with +/-<flag>.
+ + switch the flag on, and - switch it off. Example: -S+RL
+ Following flags with its default set specified first are available:
+ -S > News flag for subscribing.
+ -M > Request is removed after you sign off.
+ -Q > Ignore request if recipient's first nick is equal to username.
+ -I > Ignore request if recipient is not on same server as request.
+ -W > Ignore request if recipient is not an operator.
+ -Y > Ignore request if sender is not on IRC.
+ -N > Let server send a note to you if message is delivered.
+ -D > Same as N, except you only get a message (no queued note to you).
+ -R > Repeat processing the request until timeout.
+ -F > Let server send note info for matching recipient(s). Any message
+ part specify what to match with the realname of the recipient.
+ -L > Same as F, except you only get a message (no queued note to you).
+ -C > Make sender's nicks be valid in all cases username@host is valid.
+ -V > Make sender's "nick*" be valid in all cases username@host is valid.
+ -X > Let server display if recipient signs on/off IRC or change
+ nickname. Any message specified is returned to sender.
+ -A > Show what server matching user is on using X flag.
+ -J > Show what channel matching user is on using X flag.
+ -U > Do not display nick-change using X flag.
+ -E > Ignore request if nick, name and host matches the message text
+ starting with any number of this format: 'nick!name@host nick!... '
+* -B > Send a message to every account who match the nick!user@host
+* This creates a received list with flag H set. (see LS +h)
+* -T > Send a message not all nicks on same accounts too using B flag.
+* -K > Give keys to unlock privileged flags by setting that flags on.
+* The recipient does also get privileges to queue unlimited
+* numer of requests, list privileged flags and see all stats.
+* -Z > Make it impossible for recipient to queue anything at all.
+ Other flags which are only displayed but can't be set by user:
+ -O > Request is queued by an operator.
+ -G > Notice message is generated by server.
+- -B > Broadcasting message.
+* -H > Flag list for who's received Broadcast message (B flag).
+- Notice: Message is not sent to recipient using F, L, R or X flag.
+- Using this flags, no message needs to be specified.
+* Notice: Message is not sent to recip. using F, L, R, X, K, Z or H
+* flag (except if B flag is set for R). For this flags, no msg. needed.
+
+ Examples:
+ FLAG * +cj : Switch on c and j flag for all requests.
+ FLAG +x * +c : Switch on c flag for all req. which have x flag.
+ FLAG nick -c+j : Switch off c flag and which on j flag for nick
+
+
+*Usage: NOTE KEY [&<passwd>] [+|-<flags>] [+|-<maxtime>] <nick!user@host>
+* KEY is an alias for USER +KR (default max 1 day)
+* This command is for allowing no-opers to use oper-options by specifying
+* the flag to unlock. Be careful with this option!
+* Example: KEY +365 +s * would make it possible for everybody to use s flag.
+
+
+Usage: NOTE LOG [&<passwd>] [+|-<flags>] [#<ID>] <nick!username@host>
+ Displays how long time since matching person was on IRC. This works
+ only after use of NOTE SPY. The log is protected to be seen for other
+ users than the person who queued the SPY request. To get short
+ output, do not specify any arguments. Example:
+ LOG : Print short log of all
+ LOG * : Print long log including real names of all
+ LOG nick : Print long log for user nick.
+
+
+Usage: NOTE LS [&<passwd>] [+|-<flags>] [#<ID>]
+ <nick!username@host> [<date>]
+ Displays requests you have queued. No arguments would show you
+ all requests without the message field.
+ Use flags for matching all messages which have the specified flags set
+ on or off. See HELP NOTE FLAG for more info about flag settings. Time
+ can be specified on the form day.month.year or only day, or day/month,
+ and separated with one of the three '.,/' characters. You can also
+ specify -n for n days ago. Examples: 1.jan-90, 1/1.90, 3, 3/5, 3.may.
+ If only '-' and no number is specified max time is set to all days.
+ The time specified is always the local time on your system. Example:
+ LS !user would show you all requests to username@*
+ LS +x would show all your SPY requests.
+ LS #300 would show you only request #300.
+
+
+Usage: NOTE NEWS [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+ <group!username@host>
+ NEWS with no message is an alias for USER +RS (default max 60 days)
+ This command is for subscribing on a specified newsgroup from any
+ user(s) or host(s). Wildcards may be used anywhere. Example:
+ NEWS irc.note : Subscribe on irc.note
+* NEWS irc.note@*.no : Send to group irc.note, but only for
+* users at host *.no
+* NEWS irc.note : Send to all for group irc.note
+* NEWS Users@*.edu Hi : Send Hi to all users using note in your
+* server located at host *.edu.
+ (Advanced users may use User +rs <...> <filter> where filter is a
+ string which must matches with field in received news message)
+- Only opers can send news as default.
+* To send news add message and use same format as subscribing, except
+* that username field must matches with subscribed group as alt.irc!*.irc -
+* everybody subscribing on a*.irc or *.irc or alt.irc... would get the news.
+* A speciall case is the group Users which everybody using note in the server
+* are member of.
+
+
+Usage: NOTE RM [&<passwd>] [+|-<flags>] [#<ID>] <nick!username@host>
+ Deletes any messages sent from your nick and username which matches
+ with nick and username@host. Use flags for matching all messages which
+ have the specified flags set on or off. See HELP FLAG for more info
+ about flag settings, and HELP LS for info about time.
+
+
+*Usage: NOTE SAVE
+* SAVE saves all messages with the save flag set. Notice that the
+* messages are automatically saved (see HELP STATS). Each time server is
+* restarted, the save file is read and messages are restored. If no users
+* are connected to this server when saving, the ID number for each
+* message is renumbered.
+
+
+Usage: NOTE SEND [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+ <nick!username@host> <msg>
+ SEND is an alias for USER +D (default max 60 days)
+ This command is for sending a message to recipient, and let the server
+ send a note + a notice to sender if this is on IRC - if message is sent.
+ SEND foobar Hello, this is a test.
+ SEND +7 !username@*.edu Hello again!
+
+
+-Usage: NOTE SENT [NAME|COUNT]
+*Usage: NOTE SENT [NAME|COUNT|USERS] <f.nick!f.name@host> <date> [RM]
+ Displays host and time for messages which are queued without specifying
+ any password. If no option is specified SENT displays host/time for
+ messages sent from your nick and username.
+ NAME displays host/time for messages sent from your username
+ COUNT displays number of messages sent from your username
+* USERS Displays the number of messages in [], and names for all users
+* who have queued any message which matches with spec. nick/name/host.
+* RM means that the server removes the messages from the specified user.
+
+
+*Usage: NOTE SERVICE <nick> <note command>
+* Useful in robots. Note will take the requests as if from <nick>
+
+
+Usage: NOTE SPY [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+ <nick!username@host> [msg]
+ SPY is an alias for USER +RX (default 1 max day)
+ SPY makes the server tell you if any matching recipient sign(s)
+ on/off IRC or change nick name. No message needs to be specified.
+ However, if a message is specified this is returned to sender including
+ with the message about recipient. Message could for example be one or
+ more Ctrl-G characters to activate the bell on senders machine.
+ As an alternative for using C flag, <msg> field could start with
+ any number of nicks on this format: %nick1 %nick2... %nickn, with
+ no space between % and the nick name you use. Spy messages would be
+ valid for any of the nicks specified.
+ SPY with no argument would tell you what users you have spy on who are
+ currently on IRC. The system logs last time the last matching person was
+ on IRC for each SPY request is queued in the server. See NOTE LOG for this.
+ You may use flag +A to see what server matching user is on,
+ and/or +J flag to see what channel. (Read HELP NOTE USER for more). Example:
+ SPY foobar!username@host <ctrl-G>
+ SPY +10 foobar
+ SPY +aj &secret * <ctrl-G>
+ SPY +365 +e !user nick!*@* <ctrl-G>
+ SPY % +7 foo!user
+ SPY +5 nicky %mynick %meenick
+
+
+-Usage: NOTE STATS [MSM|MSW|MUM|MUW|MST|MSF|USED]
+*Usage: NOTE STATS [MSM|MSW|MUM|MUW|MST|MSF|USED|RESET] [value]
+ STATS with no option displays the values of the following variables:
+ MSM: Max number of server messages.
+ MSW: Max number of server messages with username-wildcards.
+ MUM: Max number of user messages.
+ MUW: Max number of user messages with username-wildcards.
+ MST: Max server time.
+ MSF: Note save frequency (checks for save only when an user register)
+ Notice that 'dynamic' mark after MSM means that if there are more
+ messages in the server than MSM, the current number of messages are
+ displayed instead of MSM.
+ Only one of this variables are displayed if specified.
+* You can change any of the stats by specifying new value after it.
+* RESET sets the stats to the same values which is set when starting the
+* server daemon if no note file exist. Notice that this stats are saved
+* in same file as the other messages.
+
+
+Usage: NOTE USER [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+ <nick!username@host> <msg>
+ With USER you can queue a message in the server, and when the recipient
+ signs on/off IRC, change nick or join any channel, note checks for
+ valid messages. This works even if the sender is not on IRC. See HELP
+ FLAGS for more info.
+ Password can be up to ten characters long. You may specify password
+ using the &, % or $ character. & is equal to to $, except working much
+ better cause client use $ for other things...
+ The % character works like &, except it makes the queuing silent. It
+ makess also sense to use this without any following password.
+ If any request queued is equal to any previous except time and maxtime,
+ only maxtime is changed as specified. You then get "Joined" instead of
+ "Queued".
+ HELP FLAGS for info about flag settings. Username can be specified
+ without @host. Do not use wildcards in username if you know what it
+ is, even if it's possible. Max time before the server automatically
+ remove the message from the queue, is specified with hours with a
+ '-' character first, or days if a '+' character is specified, as
+ -5 hours, or +10 days. Default maxtime is 7 days.
+ Note: The received message is *directly* displayed on the screen,
+ without the need for a read or remove request.
+ NOTE USER &secret +WN +10 Wizible!jarlek@ifi.uio.no Howdy!
+ is an example of a message sent only to the specified recipient if
+ this person is an operator, and after receiving the message, the server
+ sends a note message back to sender to inform about the delivery.
+ NOTE USER +XR -5 Anybody <ctrl-G>
+ is an example which makes the server to tell when Anybody signs
+ on/off irc, change nick etc. This process repeats for 5 hours.
+ NOTE USER +FL @*.edu *account*
+ is an example which makes the server send a message back if any real-
+ name of any user matches *account*. Message is sent back as note from
+ server, or directly as a notice if sender is on IRC at this time.
+
+
+Usage: NOTE WAITFOR [&<pwd>] [+|-<flags>] [+|-<maxtime>]
+ <nick!username@host> [<msg>]
+ WAITFOR is an alias for USER +YD (default max 1 day)
+ Default message is [Waiting]
+ This command is for telling the recipient if this appears on IRC that
+ you are waiting for him/her and notice that this got that message. Example:
+ WAITFOR foobar
+ WAITFOR -2 foobar!username@*
+ WAITFOR foobar Waiting for you until pm3:00..
+
+
+*Usage: NOTE WALL [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+* <nick!user@host> <msg>
+* WALL is an alias for USER +BR (default max 1 day)
+* This command is for sending a message once to every matching user
+* on IRC. Be careful using this command. WALL creates a list of
+* persons received the message (and should not have it once more time)
+* with H flag set. This list can be displayed using ls +h from the
+* nick and username@host which the WALL request is queued from.
+* Removing the headers (H) before WALL request is removed would cause
+* the message to be sent once more to what users specified in list.
+* WALL +7 @*.edu Do not do this! - Makes it clear for all users using
+* IRC on host @*.edu the next 7 days how stupid it is to send such WALL's ;)
+
+
+*Usage: NOTE WALLOPS [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+* <nick!user@host> <msg>
+* WALLOPS is an alias for USER +BRW (default max 1 day)
+* This command is same as WALL, except only opers could receive it.
+=============================================================================
--- /dev/null
+/************************************************************************
+ * IRC - Internet Relay Chat, doc/HISTORY
+ * Copyright (C) 1990
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+HISTORY of Recent IRC Versions.
+
+Previous version numbering schemes have caused some confusion, which this
+document attempts to resolve.
+
+The original test versions released by WiZ were numbered 2.01?6 where
+the ? refers to a letter. The last known stable version was U6.
+Version 2.1.0/1 was rewritten by Mike Bolotski from the U6 sources but
+several bugs were introduced during the rewrite. After several weeks,
+almost all servers backed up to U6. Version v6 contained comparatively
+minor modifications from U6.
+
+Version 2.2 consists of the v6 source repackaged into multiple directories,
+and with modified documentation. From now on, the version number will
+stay relatively constant. As minor changes and bug fixes are added, they
+will be distributed in the form of context diffs, to be applied with the
+'patch' command. Each bugfix will bump the patchlevel (PL) of the release
+by 1. The PL is documented in the version.c file in the lib/misc directory.
+
+Version 2.3 was unfortunate mistake containing copyright violations
+so it was soon taken off distribution.
+
+Version 2.4 contains *very* many bug fixes, enhancements, and "hooks" for
+use in future releases. The source tree has been restructured, and the
+Makefiles rewritten to be recursive and follow the new source tree layout.
+
+Version 2.5 contains string channels and channel modes (as well as
+channel operators). Also Wizible's MAIL system was included as an option.
+
+Hopefully, whoever provides a fix will also update the respective
+ChangeLogs to summarize the changes, as well as adding a description of
+the bug to the BugList file.
--- /dev/null
+Hi fellow undernetters,
+
+I forgot if it was requested on routing-com or here, but you won't see me
+cross posting, so I did choose 'wastelanders'.
+
+The request was to mail an overview of the changes 2.8 ==> u2.9,
+especially for the new Opers, but also as a reminder for others.
+
+The patch file from irc2.8.21.mu3.1 to ircu2.9.17.mu is 446652 bytes.
+So you will understand I can't cover every little change.
+
+New commands
+------------
+
+/UPING <server.to> [<port>] [<server.from>] [<number of packets>]
+
+Sends <number of packets> (default 5, max 20) size 1024 bytes, from (remote)
+server <server.from> (default local server) to <server.to>. The default
+port is 7007 and the same on all servers. If a server is down, you can
+still use port 7 (echo). Also 2.8 echo's, on port PORTNUM (config.h).
+UPING uses udp, you need CN lines (masks as server names are allowed) but
+the connection doesn't have to exist already.
+
+/RPING <server.to> [<server.from>] [<Optional remark>]
+
+Sends one 'RPING' message using the irc protocol over an existing link.
+It allows to measure the lag of remote links, respons is in ms accuracy.
+The <Optional remark> can be used to measure to total pingtime to your
+client (like the CTCP PING) or to add a serial number for automation.
+
+/MAP [server.mask]
+
+Shows a map in the layout as Router.
+
+/SETTIME
+
+Only for debugging, isn't needed. (Oper only).
+
+Changed commands
+----------------
+
+/CONNECT
+
+No doubt the biggest impact of 2.9 is on connecting:
+When the link is physically possible, your /connect ALWAYS succeeds
+except when an H: or L: line somewhere on the net forbids it, or when
+*after* your connect another connect is done that cause a loop. The only
+restriction is that you are not allowed to make deliberately a loop:
+you must first squit. Loops only happen when to connects are done
+simultaneously and the SERVER messages had not yet time to propagate
+over the whole net.
+When a connect (manual or automatically) is done for a link that used
+to get "server exists", with 2.9 the Ghost is squitted off the net,
+making it possible to recover faster from breaks caused by bad links.
+If on the other hand a loop occurs because two parts connect at two
+points, the servers that detect the server nick collision will squit
+the most logical link to break the loop, and only one link. This results
+thus in a connected net one way or the other (for this all 2.8 servers need
+to be off the net! Till that time the net will connect and then break
+at two places, giving more messages then right now with only 2.8).
+2.9 servers also notify the Opers (or users with +s) about net.junctions
+and net.breaks. It does this even better then Router: A lot faster, always
+correct (REAL junctions), and independent of Router: You will also see
+them when Router is 'on the other side'.
+
+/TIME
+
+Has changed. Now also shows the system clock / TimeStamp clock offset.
+
+/MODE +b
+
+You only have to be joined, not be chan op anymore.
+
+/MODE <nick> +d
+
+Makes the user 'Deaf'. Needed for the channel registration service.
+Channel messages are not routed to a Deaf person decreasing bandwidth use.
+
+/LINKS
+
+Output also shows used protocol for that link.
+
+New numerics
+------------
+
+RPL_MAP, RPL_MAPMORE, RPL_MAPEND and RPL_TRACEPING.
+
+Bug fixes
+---------
+
+- A handshaking link doesn't pingtime out; That can interfere with
+ slow nameserver lookups.
+
+- U: lines (and K: lines) now active directly after a /rehash
+
+- Don't bind() a socket before connect(), thats useless on machines
+ with just one ip number (like we all have), and can confuse
+ some OS's I found out.
+
+Significant Patches
+-------------------
+
+The following patches have been the objective. To realize them I needed to
+rewrite and change huge other parts of the code also, because lot of the code
+in 2.8 is under great tension of re-re-re-patches.
+
+- Rewrote m_server. Objective:
+ = Allow ghosted servers to reconnect (solution "server exists").
+ To allow for this:
+ - Added a timestamp to SQUIT, this timestamp functions as a label
+ which matches the corresponding SERVER (connect).
+ - Added a prefix for every message, absolutely necessary to keep track
+ of the correct order (direction actually).
+
+- The oci has been added (oper sees invisibles on own server).
+
+- A new NOTE is added, many bugs removed and extremely speeded up due to
+ a better interface with the rest of the code.
+
+- The TimeStamp clocks are now automatically synchronized, so a wrong
+ system clock isn't a problem anymore.
+
+- Added a Protocol-version and detection. This allows protocol changes
+ with a *MUCH* higher backwards compatibility.
+
+- Server now keeps track of the server map. This allowed for /MAP and
+ a lot of speed ups (don't have to scan through all clients to find a
+ server) but much more important: The disconnect burst could be brought
+ back to ONE message (instead of a QUIT for ever single client).
+ Apart from decreasing bandwidth use, this was necessary for other
+ important protocol changes, and even more to allow important future
+ changes that will reduce the connect.burst as well. The most important
+ current impact is that it allows SQUIT to travel down stream AND up stream.
+ Because directionless messages can loose the order, the timestamp on
+ SQUIT was needed to check the validity.
+
+- In the client structure a pointer to the server structure is used
+ rather then the full servername, using less memory AND speeding up
+ several places because you don't need to lookup the servername
+ anymore.
+
+- USER removed from the connect burst (now all in NICK).
+
+Other patches
+-------------
+
+- exit_client() is rewritten.
+ Added are exit_client_msg() and exit_new_server().
+ This has especially impact on the possibilities within the protocol.
+ The old exit_client() was clumsy and therefor already used in an incorrect
+ way at several places. The kludges around this part of the code made it
+ impossible to make any changes without breaking something else. Only after
+ the rewrite it was possible to make changes described else where.
+ This also allowed to improve the error message handling to the point that
+ Opers see *always* the error messages involved with routing (also those
+ from remote /connects, delayed errors and squit reasons 'from the other
+ side').
+
+- send.c is more or less rewritten. varargs are fixed now and send.c is
+ highly optimized for speed (possible because of new internal server map).
+
+- All useable dog3 code speed ups have been added.
+ These include:
+ - Added a head pointer in the dyn buffer.
+ - several code optimisations
+ - continious kill line checking removed (I added the check at
+ the place where it belongs: after a /rehash).
+
+- Useable patches from dl:
+ - Stop as much as possible flooding from unregistered connections.
+ - VERSION and ADMIN available for unregistered users.
+ - syslog (if defined) KILLs of local clients.
+
+- Many compile warnings have been removed. Also a special fix for DYNIX to
+ make UPING/RPING also work there (needed gettimeofday()).
+
+Package changes
+---------------
+
+- The irc client is removed from the package as are several old files with
+ incorrect old useless info (Like 'WHATSNEW', ChangeLog that stopped at 1992).
+- A Makefile.dist is added.
+- Slighty changed doc/Manual
+- New doc/NOTE manual
+- NO_DEFAULT_INVISIBLE removed; users are always visible by default.
+- Last but not least: patchlevel.h is rewritten so any additional patch
+ can do the version update itself, without interfering: No need to edit
+ this by hand anymore.
+
+Summary
+=======
+
+- Less memory usage
+- Speeded up code
+- Less bandwidth use, especially disconnect burst
+- "Server exists" solved
+- Error messages concerning (remote) /connects now always visible.
+- New tools to do (remote) link testing
+- Intelligent and improved SQUIT handling, should stop unwanted breaks.
+- squits comments visible everywhere.
+- net.junction and net.break notices
+- Overall protocol streamlining allowing for future improvements
+ of the protocol.
+
+Run
+
--- /dev/null
+.\" @(#)irc.1 2.6 7 Oct 90
+.TH IRC 1 "7 October 1990"
+.SH NAME
+irc \- User Interface to Internet Relay Chat Protocol
+.SH SYNOPSIS
+\fBirc\fP [\fB-p\fP \fIportnum\fP] [\fB-c\fP \fIchannel\fP] [ \fInickname\fP [ \fIserver\fP ]]
+.SH DESCRIPTION
+.LP
+\fBIrc\fP is a user interface to the Internet Relay Chat, a CB-like
+interactive discussion environment. It is structured into \fIchannels\fP,
+which are public discussion forums, and also allows for private intercommunication.
+Each participant has a \fInickname\fP, which is the one specified in the command
+line or else his login name.
+.LP
+Once invoked, \fBirc\fP connects as a client to the specified server,
+\fIserver\fP or to the default one (see below). The screen splits into a dialogue
+window (the major part
+of the screen) and a command line, from which messages can be sent and
+commands given to control irc.
+.SH COMMAND SYNTAX
+The syntax of irc commands is of the form \fB/COMMAND\fP. The most notable
+ones are listed below. For an uptodate list, use the \fBHELP\fP command
+of \fBirc\fP. Case is ignored.
+.IP "\fB/ADMIN\fR [\fIserver\fP]"
+Prints administrative information about an IRC \fIserver\fP.
+.IP "\fB/AWAY\fP [\fImessage\fP]"
+Mark yourself as being away (with an automatic reply \fImessage\fP
+if specified)
+.IP "\fB/BYE\fR, \fB/EXIT\fR, \fB/QUIT\fR"
+Terminate the session
+.IP "\fB/CHANNEL\fR [\fIchannel\fP]"
+Join another \fIchannel\fP
+.IP "\fB/CLEAR\fR"
+Clear the screen
+.IP "\fB/HELP\fR [\fIcommand\fP]"
+Display a brief description of the \fIcommand\fP (or list all commands, if none
+specified).
+.IP "\fB/SUMMON\fR \fIuser\fP"
+Allows to summon a \fIuser\fP specified as a full Internet address, i.e.,
+\fIlogin@host.domain\fP, to an IRC dialogue session (in much the same
+way as the talk(1) command). It is usable ONLY if the irc daemon runs on
+the target machine (host.domain).
+.IP "\fB/TOPIC\fR \fItopic\fP"
+Sets the \fItopic\fP for the current channel
+.IP "\fB/WHO\fR [\fIchannel\fP|*]"
+Lists all users of IRC if no argument, of the specified \fIchannel\fP or of the
+current channel (*).
+.SH ARGUMENTS
+.IP "\fB-p\fP \fIportnum\fP"
+TCP/IP "port number. Default is 6667 and this option should seldom if ever"
+be used.
+.IP "\fB-c\fP \fIchannel\fP"
+\fIChannel\fP number to join upon beginning of the session. Default is no channel.
+.IP "\fInickname\fP"
+\fINickname\fP used in the session (can be changed with the \fB/NICK\fP command).
+Default is user login name.
+.IP "\fIserver\fP"
+\fIServer\fP to connect to. Default is specified in the irc system configuration
+file, and can be superseded with the environment variable IRCSERVER.
+.SH EXAMPLE
+.RS
+.nf
+tolmoon% \fBirc -p6667 Wizard tolsun\fP
+.fi
+.RE
+.LP
+connects you to irc server in host tolsun (port 6667) with nickname Wizard
+.SH COPYRIGHT
+Copyright (c) 1988 University of Oulu, Computing Center, Finland.
+.nf
+Copyright (c) 1988,1989,1990 Jarkko Oikarinen
+.nf
+All rights reserved.
+For full COPYRIGHT see LICENSE file with IRC package.
+.SH "SEE ALSO"
+ircd(8)
+.SH BUGS
+What bugs ?
+.SH AUTHOR
+Jarkko Oikarinen <jto@tolsun.oulu.fi>
+.nf
+Manual page updated by Michel Fingerhut <Michel.Fingerhut@ircam.fr>
--- /dev/null
+.\" @(#)ircd.8 2.0 (beta version) 29 Mar 1989
+.TH IRCD 8 "29 March 1989"
+.SH NAME
+ircd \- The Internet Relay Chat Program Server
+.SH SYNOPSIS
+.hy 0
+.IP \fBircd\fP
+[-a] [-c] [-i] [-o] [-q] [-t] [-d directory]
+[-f configfile] [-w interface] [-x debuglevel] [-h hostname] [-p portnum]
+.SH DESCRIPTION
+.LP
+\fIircd\fP is the server (daemon) program for the Internet Relay Chat
+Program. The \fIircd\fP is a server in that its function is to "serve"
+the client program \fIirc(1)\fP with messages and commands. All commands
+and user messages are passed directly to the \fIircd\fP for processing
+and relaying to other ircd sites. The \fIirc(1)\fP program depends upon
+there being an \fIircd\fP server running somewhere (either on your local
+UNIX site or a remote ircd site) so that it will have somewhere to connect
+to and thus allow the user to begin talking to other users.
+.SH OPTIONS
+.TP
+.B \-d directory
+This option tells the server to change to that directory and use
+that as a reference point when opening \fIircd.conf\fP and other startup
+files.
+.TP
+.B \-o
+Starts up a local ircdaemon. Standard input can be used to send IRC
+commands to the daemon. The user logging in from standard input will
+be given operator privileges on this local ircd. If ircd is a setuid program,
+it will call setuid(getuid()) before going to local mode. This option
+can be used in inetd.conf to allow users to open their own irc clients
+by simply connecting their clients to the correct ports. For example:
+.TP
+.B
+irc stream tcp nowait irc /etc/ircd ircd \\-f/etc/ircd.conf \\-o
+
+allows users connecting to irc port (specified in /etc/services) to start
+up their own ircdaemon. The configuration file should be used to check from
+which hosts these connections are allowed from. This option also turns
+on the autodie option -a.
+.TP
+.B \-a
+Instructs the server to automatically die off if it loses all it's clients.
+.TP
+.B \-t
+Instructs the server run in the foreground and to direct debugging output to standard output.
+.TP
+.B \-x#
+Defines the debuglevel for ircd. The higher the debuglevel, the more stuff
+gets directed to debugging file (or standard output if -t option was used
+as well).
+.TP
+.B \-i
+The server was started by inetd and it should start accepting connections
+from standard input. The following inetd.conf-line could be used to start
+up ircd automatically when needed:
+.TP
+.B
+ircd stream tcp wait irc /etc/ircd ircd \-i
+
+allows inetd to start up ircd on request.
+.TP
+.B \-w interface
+If the server was compiled with VIRTUAL_HOST (run 'make config' to toggle
+this compile option), then \fIinterface\fP is passed to gethostbyname(3) in
+order to retrieve the IP-number of the interface to bind to. An example
+would be to use '-w localhost', after which the server only listens on the
+loopback interface. Run `ifconfig -a' to see which interfaces you have.
+.TP
+.B \-f filename
+Specifies the ircd.conf file to be used for this ircdaemon. The option
+is used to override the default ircd.conf given at compile time.
+.TP
+.B \-c
+This flag must be given if you are running ircd from \fI/dev/console\fP or
+any other situation where fd 0 isnt a tty and you want the server to fork
+off and run in the background. This needs to be given if you are starting
+\fIircd\fP from an \fIrc\fP (such as \fI/etc/rc.local\fP) file.
+.TP
+.B \-q
+Using the -q option stops the server from doing DNS lookups on all the
+servers in your \fIircd.conf\fP file when it boots. This can take a lengthy
+amount of time if you have a large number of servers and they are not all
+close by.
+.TP
+.B \-h hostname
+Allows the user to manually set the server name at startup. The default
+name is hostname.domainname.
+.B \-p portname
+Specifies the server port where the daemon should start waiting for connections
+from other servers. Clients should connect to ports as specified in the ircd.conf file by means of a P: line.
+.TP
+.SH
+If you plan to connect your \fIircd\fP server to an existing Irc-Network,
+you will need to alter your local IRC CONFIGURATION FILE (typically named
+"ircd.conf") so that it will accept and make connections to other \fIircd\fP
+servers. This file contains the hostnames, Network Addresses, and sometimes
+passwords for connections to other ircds around the world. Because
+description of the actual file format of the "ircs.conf" file is beyond the
+scope of this document, please refer to the file INSTALL in the IRC source
+files documentation directory.
+.LP
+BOOTING THE SERVER: The \fIircd\fP server can be started as part of the
+UNIX boot procedure or just by placing the server into Unix Background.
+Keep in mind that if it is *not* part of your UNIXES Boot-up procedure
+then you will have to manually start the \fIircd\fP server each time your
+UNIX is rebooted. This means if your UNIX is prone to crashing
+or going for for repairs a lot it would make sense to start the \fIircd\fP
+server as part of your UNIX bootup procedure. In some cases the \fIirc(1)\fP
+will automatically attempt to boot the \fIircd\fP server if the user is
+on the SAME UNIX that the \fIircd\fP is supposed to be running on. If the
+\fIirc(1)\fP cannot connect to the \fIircd\fP server it will try to start
+the server on it's own and will then try to reconnect to the newly booted
+\fIircd\fP server.
+.SH EXAMPLE
+.RS
+.nf
+tolsun% \fBircd\fP
+.fi
+.RE
+.LP
+Places \fIircd\fP into UNIX Background and starts up the server for use.
+Note: You do not have to add the "&" to this command, the program will
+automatically detach itself from tty.
+.SH COPYRIGHT
+(c) 1988,1989 University of Oulu, Computing Center, Finland,
+.LP
+(c) 1988,1989 Department of Information Processing Science,
+University of Oulu, Finland
+.LP
+(c) 1988,1989,1990,1991 Jarkko Oikarinen
+.LP
+For full COPYRIGHT see LICENSE file with IRC package.
+.LP
+.RE
+.SH FILES
+ /etc/utmp
+ "irc.conf"
+.SH "SEE ALSO"
+irc(1)
+.SH BUGS
+None... ;-) if somebody finds one, please inform author
+.SH AUTHOR
+Jarkko Oikarinen, currently jto@tolsun.oulu.fi,
+manual page written by Jeff Trim, jtrim@orion.cair.du.edu,
+later modified by jto@tolsun.oulu.fi.
--- /dev/null
+SmartRoute
+Rule based connects
+Draft 4 - Aug 19, 1994
+by Tony Vencill
+
+Rule based connects allow an admin to specify under what conditions
+a connect should not be allowed. If no rules are specified for a
+given C and/or N line it will be allowed under any condition.
+
+A rule may consist of any legal combination of the following functions
+and operators.
+
+Functions
+---------
+connected(targetmask) - true if a server other than that processing
+ the rule is connected that matches the
+ target mask
+directcon(targetmask) - true if a server other than that processing
+ the rule is directly connected that matches
+ the target mask
+via(viamask, targetmask) - true if a server other than that processing
+ the rule matches the target mask and is
+ connected via a directly connected server
+ that matches the via mask
+directop() - true if an oper is directly connected
+
+Unary operators
+---------------
+! eg: !argument - true if the argument is false
+
+Binary operartors
+-----------------
+&& eg: arg1&&arg2 - true if arg1 and arg2 are both true
+|| eg: arg1||arg2 - true if arg1, arg2, or both are true
+
+Parenthesis () are allowed for grouping arguments, but if no parenthesis
+are included, && will take precedence over ||, ! will take precedence
+over both && and ||, and the function will be evaluated from left to
+right. White space in a rule is ignored. Invalid characters in a rule
+will lead to the rule being ignored.
+
+Examples
+--------
+
+A simple example of a connect rule might be:
+
+connected(*eu.under*)
+
+This might be used in a US undernet server for a Europe CN pair to
+insure that a second Europe link is not allowed if one US-EU link
+already exists. Note that on the undernet, US server names are
+city.state.us.undernet.org and Europe server names are
+city.country.eu.undernet.org.
+
+A more interesting example might be:
+
+connected(*eu.under*) &&
+ ( !direct(*eu.under*) || via(manhat*, *eu.under*) )
+
+Imagine the Boston undernet server uses this rule on its Europe CN
+pairs. This says that if a Europe server is already connected, a
+Boston-Europe connect will not be allowed. It also says that if a
+Europe server does already exist and Boston is not directly connected
+to one or more Europe servers or Manhattan is, the Boston-Europe
+connect will not be allowed. This has the effect of allowing multiple
+US-EU links but attempting to limit these links to one server (ie:
+Boston will not initiate its first Europe link if another server is
+already linking Europe). This rule will also prefer to let Manhattan
+handle the US-EU link by disallowing Boston-Europe links if a Europe
+server is already linked to Manhattan.
+
+A example of the remaining function, directop(), is:
+
+connected(*eu.under*) || directop()
+
+If this line is used on Boston for the Paderborn CN pair, it will allow
+connects to Paderborn only if another Europe server is not already
+connected and there is not an oper on Boston. If this rule is
+overrideable (ie: is applied only to autoconnects as described below),
+then it will disallow Boston autoconnects to Paderborn while a Boston
+oper is online, but allow oper-initiated connects to Paderborn under any
+circumstance. This directop() function could be used to invoke less
+prefered routes only when an oper is not present to handle routing, or
+conversly to allow use of less preferable routes only when an oper is
+present to monitor their performance.
+
+ircd.conf entries
+-----------------
+
+A rule is listed in the ircd.conf file using a D or d line (which can
+be thought of as a "disallow" line). D lines will apply to all oper
+and server originated connects, while d lines will apply only to
+autoconnects (ie: they are overrideable by opers). The formats are:
+
+D:targetmask::rule
+d:targetmask::rule
+
+Remember that newlines are not allowed in conf lines. Two examples
+(from above) are:
+
+D:*eu.under*::connected(*eu.under*)
+d:*eu.under*::connected(*eu.under*) || directop()
+
+Connects originating from other servers will be checked against and
+matching D lines, while matching d lines will be ignored as it will not
+be clear whether or not the connection attempt is oper initiated.
+
+Checking and viewing rules
+--------------------------
+
+The chkconf program that comes with the servers has been modified to
+also check your connect rules. If running in debug mode, parsing errors
+will show up at debug level 8. To view rules online, "/stats d" can be
+used to see all rules and "/stats D" can be used to view those rules
+which affect oper initiated connects and accepts.
+
+Processing and storage
+----------------------
+
+The rules are parsed when the conf file is read and transformed into a
+more efficiently computed form, then all applicable rules are
+evaluated each time a connect command is given or an autoconnect is
+due. If more than one applicable rule is given, only one need
+evaluate to true for the connect to be allowed (ie: the rules are ored
+together). Note that conditions that exist when the connect is
+initiated might differ from conditions when the link is established.
--- /dev/null
+If you want to indent this source file, in order to convert
+the source tree to the used programming style, you should use
+`make indent' in the base directory.
+
+For this to work you need to have `indent' version 2.1.0 or higher
+in your PATH. GNU indent 2.1.0 is available from all GNU sites,
+its main ftp site is ftp://ftp.gnu.org/indent/. Or you can download
+it directly from the webpage of its maintainer at
+http://www.xs4all.nl/~carlo17/indent/
--- /dev/null
+WHO documentation, updated on 02 Jan 1999.
+
+Since ircu2.10.02 the WHO command had been changed from what
+described in RFC1459, while still keeping backward compatibility,
+actually it has been changed again in u2.10.05 so that since this
+release the format of the who query is now:
+
+[:source] WHO <mask1> [<options> [<mask2>]]
+
+<mask2> is optional, if mask2 is present it's used for matching and
+mask1 is ignored, otherwise mask1 is used for matching, since mask2
+is the last parameter it *can* contain a space and this can help
+when trying to match a "realname".
+
+When matching IP numbers the <mask> can be in 3 forms:
+
+- The old and well known IRC masks using * and ? as wanted
+- The IPmask form a.b.c.d/e.f.g.h as used in most firewalls and
+ system configurations, where what is before the / are the bits
+ we expect in the IP number and what is after the / is the
+ "filter mask" telling wich bits whould be considered and wich
+ should be ignored.
+- The IPmask form a.b.c.d/bitcount where bitcount is an integer
+ between 0 and 31 (inclusive), the matching will be for the IPs
+ whose first "bitcount" bits are equal to those in a.b.c.d
+
+Note that:
+. The bitcount must be between 0 and 31, 32 is NOT good (and
+ makes no sense to use it... just match against the static IP
+ a.b.c.d)
+. The missing pieces of both the bitmask and the ipnumber in
+ the forms ipnumber/bitmask and ipnumber/bitcount default to
+ zero from right to left, this is NOT what inet_aton and most
+ tools do but makes more sense here IMO, in example /who 194.243/16
+ is taken as /who 194.243.0.0/255.255.0.0 (inet_aton whould
+ take 194.243 as 194.0.0.243).
+. For the above reason and specified validity limits 1.2.3.4/31
+ becomes 1.2.3.4/255.255.255.254 while 1.2.3.4/32 becomes
+ 1.2.3.4/32.0.0.0 :)
+
+For all the other fields th match happens as has always been,
+i.e. it's only considered the IRC mask with * and ? (that is:
+don't expect to catch an user with "realname" = "1.2.3.4" when
+doing "/who 1.2/16 h" :)
+
+For both the masks and the options (and thus for all flags) case is
+NOT significative (so "/who <any> o" is exactly the same as
+"/who <ANY> O".
+
+The "options2 part can be as follows:
+
+ [<flags>][%[<fields>[,<querytype>]]]
+
+in wich:
+
+ <flags>: can be a sequence of field matching flags, use mode matching
+ flags and special purpose flags
+
+ Field matching flags, when one of these is specified the field in
+ question is matched against the mask, otherwise it's not matched.
+
+ n Nick (in nick!user@host)
+ u Username (in nick!user@host)
+ h Hostname (in nick!user@host)
+ i Numeric IP (the unresolved host)
+ s Servername (the canonic name of the server the guy is on)
+ r Info text (formerly "Realname")
+
+ If no field-matching flags are specified they default to what
+ old servers used to do: nuhsr (= everything except the numeric IP)
+
+ User mode matching flags (specifying one of these means that only
+ clients with that umode are considered, what is not specified
+ is always matched):
+
+ o Irc operator
+ [In the future more flags will be supported, basically all
+ usermodes plus the +/- specificators to revert the filtering]
+
+ Special purpose flags:
+
+ x If this is specified the extended visibility of information
+ for opers is applied, what this means depends on the fact that
+ you are local or global operator and on how the admin configured
+ the server (global and eventually local irc opers might be
+ allowed with this flag to see +i local users, to see all +i users,
+ to see users into +p and/or +s channels, and so on). Using the 'x'
+ flag while not beeing irc operator is meaningless (it will be
+ ignored), using it while oper'd means that the query is almost
+ certainly logged and the admin might (rightfully) ask you an
+ explanation on why you did.
+
+ The rest, what follows the %, that is [%[fields[,<querytype>]]],
+ is as it has always been since the first who.patch, the <fields> part
+ specifies wich fields to include in the output as:
+
+ c : Include (first) channel name
+ d : Include "distance" in hops (hopcount)
+ f : Include flags (all of them)
+ h : Include hostname
+ i : Include IP
+ n : Include nick
+ r : Include real name
+ s : Include server name
+ t : Include the querytype in the reply
+ u : Include userID with eventual ~
+
+And the ,<querytype> final option can be used to specify what you want
+the server to say in the querytype field of the output, useful to
+filter the output in scripts that do a kind of "on 354 ..."
+
+If no %fields are specified the reply is _exactly_ the same as
+has always been, numeric 352, same fields, same order.
+
+If one or more %fields are specified the reply uses a new numeric,
+since an out-of-standard 352 crashes EPIC and confuses several other
+clients. I used 354.
+
+:"source" 354 "target" ["querytype"] ["channel"] ["user"]
+ ["IP"] ["host"] ["server"] ["nick"]
+ ["flags"] ["hops"] [:"realname"]
+
+Where only the fields specified in the %fields options are present.
+
+"querytype" is the same value passed in the /who command, it
+is provided to simplify scripting, in example one could pass
+a certain value in the query and have that value "signal" back
+what is to be done with those replies.
+
+The number of lines in the reply is still limited to avoid self-flooding
+and sooner or later another limitation will be added: you will be forced
+to do no more than one /who query every 'n' seconds where 'n' depends
+on the number of fields you actually match (the field-match flags specified
+before % in the option, defaulting to 6 if you don't specify an option
+at all), infact matching against many fields as the default query does
+severely affects the CPU usage of the server and is *much* better to
+specify with the field-atching flags what you are looking for, in example
+when you are looking for all french users a "/who *.fr h" is A LOT
+better than just "/who *.fr" (and actually you want users that have the
+_hostname_ matching *.fr, you wouldn't want to match a japanese user
+that has the realname "ku fung-kay aj.fr" in example...)
+
+Note that:
+
+- An user doing a "/who whatever" or a "/who whatever o"
+ will not see any change (except for the anti-flood limit
+ and sooner or later the CPU usage limit)
+
+- An user doing a "/who #wasteland %n" will get just a list
+ of nicks (lame, very lame way of doing it :-)
+
+- An user doing a "/who 0 o%nuhs" will get a list of the opers
+ with Nick, userID, server and hostname like:
+
+:Amst* 354 Nemesi #wasteland nbakker pc73.a.sn.no Oslo*.org Niels
+
+- An user doing a "/who 0 o%tnuhs,166" will get a list of the opers
+ with Nick, userID, server and hostname like the above but with a
+ request type field of 166 like:
+
+ :Amst* 354 Nemesi 166 #wasteland nbakker pc73.a.sn.no
+ Oslo-R.NO.EU.Undernet.org Niels
+
+ So that he can have in example a script that does
+ "on 354 * 166" display "There is an oper ..."
+
+- The client will have to sort/format the fields by itself,
+ the _order_ in wich flags are passed is not significant,
+ the fields in the reply will always have the same order.
+
+- The maximum number of _lines_ reported as reply for a query
+ is 2048/(n+4) where 'n' is the number of flags "enabled"
+ that is the number of fields included in each reply.
+
+ Actually: 1 field returned = maximum 409 replies
+ 2 fields returned = maximum 341 replies
+ 3 fields returned = maximum 292 replies
+ 4 fields returned = maximum 256 replies
+ 5 fields returned = maximum 227 replies
+ 6 fields returned = maximum 204 replies
+ 7 fields returned = maximum 186 replies (default query)
+ 8 fields returned = maximum 170 replies
+ 9 fields returned = maximum 157 replies
+ 10 fields returned = maximum 146 replies
+
+ If the limit is reached before completing the query the
+ reply is truncated and a new numeric error is issued after
+ the "End of WHO", anyway the "end of" numeric is _always_
+ sent (otherwise some scripts and clients get crazy).
+
+The actual "mask" to match can have one of the two following forms:
+
+- A comma-separated list of elements: in this case each element
+ is treated as a flat channel or nick name and is not matched
+ to the other elements. Nicks do count in the limit of output
+ lines (they should not be that many anyway), channels count
+ if who asks the query is not on the channel. (That is: a /who
+ #channel gives unlimited output if you are in there).
+
+- A _single_ mask: in this case (no commas, only one element) the
+ mask is first checked to be a full channel or nickname, then
+ it is matched against all relevant fiels as already known.
+ These happens in different steps with replicates-removal so
+ that if one has (?) something like "#wasteland" as "real name"
+ or is on a channel named "#***MyChan***" it all works nicely.
+
+Miscellaneous bug fixes / "undocumented feature" changes:
+
+- /who NickName did not show the user with nick = NickName when it
+ was invisible, even if the nick was given completely (without
+ wildchars) now it does, since one could always see him as /whois
+ NickName.
+ It does not report him twice if he also has in example the
+ userID == NickName and is -i.
+
+- ":source WHO :The Black Hacker" did not report an user having
+ "The Black Hacker" as real name, now it does. Since this can only
+ be done without the flags/format specificator because that would
+ become the "last parameter" an escape has been provided: if you
+ pass to m_who _3_ parameters the first one will be ignored and the
+ last one used for matching, like in example
+ ":source WHO foo %nuh :*Black Hacker*" where "foo" will not
+ be used and the match will happen on "*Black Hacker*".
+ (It was passed through clean_channelname() that prevented the mask
+ from containing spaces and such...)
+
+- When one user was umode -i he was shown or not depending on the
+ fact he was on a +p or +s channel... since we are doing a lookup
+ on the _user_ this makes no sense to me, example:
+ Neme1 : umode -i, on no channels, was SEEN with a /who 0
+ Neme2 : umode -i, on channel #p with chmode +p, was NOT SEEN by /who 0
+ Neme3 : umode -i, on channel #s with chmode +s, was NOT SEEN by /who 0
+
+ Now all users "-i" are matched with a "/who mask", the +i users
+ instead must bee on a _common_ channel to be seen.
+
+ Basically beeing on "one" +s|p channel "forced" a +i status while
+ one might want to be on #secret (mode +s) and have nobody know that
+ he is in there but on the other side stay -i so others can find him.
+ Of course a +s|p channel is never shown in the reply unless who asks
+ the query is in there, if no "visible" channels are available for
+ a -i user he is shown on "channel *".
+
+- When one user is +i is shown _only_ if there is a common channel,
+ the first common channel found is shown in the reply.
+
+- As requested by many persons an escape has been provided for opers,
+ when #defined SHOW_ALL_CHANNELS opers can /who #channel from outside
+ and see users in there even if the channel is +s|+p
+ Each admin decides locally if this feature is enabled to his opers.
+
+- As requested by many admins an escape from the query-size limit
+ has been provided for opers, by #defining UNLIMIT_OPER_QUERY opers
+ can do unlimited sized /who-s (until they get disconnected by max
+ SendQ exceeded ;)
+ Again admins will decide if enable or not this feature.
+
+- A /who a,c,b,d,e,f used to return as many ** END OF WHO as there
+ were elements in the list, since now the command is supposed to be
+ _efficient_ for /who nick1,nick2,nick3 .. I return a _single_ end
+ of query message.
+
+- /who did not work for a channel named in example #**StarWars**
+ now it does handle it properly (the mask was passed through
+ collapse() and then.. did not find that channel, fixed).
+
+- "/who #John" did not report an user having '#John' as "Real name",
+ now it does (and does NOT report him twice if he is ALSO on a
+ channel named #John, strange but true: this can happen).
+
+- "/who a,b,c,d" where a b c and d are channelnames/nicks now uses
+ an hash lookup and therefore is extremely efficient, if _only_ one
+ field is specified it is looked in all the fields; who really wants
+ _only_ users on a specific channel or a single nick (without looking
+ for a match in the other fields) can force the server to consider
+ the parameter as a list adding a comma somewhere, like:
+
+ "/who #Italia," or "/who ,Nemesi"
+
+ Or even better to avoid misbehaviour with other servers:
+ "/who #Italia %... #Italia," or "/who Nemesi %... Nemesi,"
+
+ This will make old servers act properly and new ones and should
+ be the reccomended way for GUI based clients to get
+ a channel's userlist and all the infos they want about users
+ on the channel.
+
+Regards, Andrea aka Nemesi
+
--- /dev/null
+More, and up to date, information can be retrieved from the following
+world wide web pages:
+
+* Undernet Documents Project:
+ http://www.user-com.undernet.org/documents/
+
+* Release Notes & Patch Repository:
+ http://coder-com.undernet.org/
+
+* ircII scripts to support the undernet extentions:
+ http://coder-com.undernet.org/ircii/
+
+* Undernet Use Policy:
+ http://www.user-com.undernet.org/documents/aup.html
+
+* Operator Etiquette:
+ http://http://www.user-com.undernet.org/documents/operman.html
+
+* New server links & Routing info:
+ http://routing-com.undernet.org/
+
+* Information about large number of file descriptors per process:
+ linux: ftp://ftp.linux.org.za/linux/local/kernel/
--- /dev/null
+#ifndef IPCHECK_H
+#define IPCHECK_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int IPcheck_local_connect(aClient *cptr);
+extern void IPcheck_connect_fail(aClient *cptr);
+extern void IPcheck_connect_succeeded(aClient *cptr);
+extern int IPcheck_remote_connect(aClient *cptr, const char *hostname,
+ int is_burst);
+extern void IPcheck_disconnect(aClient *cptr);
+extern unsigned short IPcheck_nr(aClient *cptr);
+
+#endif /* IPCHECK_H */
--- /dev/null
+#ifndef BSD_H
+#define BSD_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern RETSIGTYPE dummy(HANDLER_ARG(int sig));
+extern int deliver_it(aClient *cptr, const char *str, int len);
+
+extern int writecalls;
+extern int writeb[10];
+
+#endif
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/channel.h
+ * Copyright (C) 1990 Jarkko Oikarinen
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "list.h"
+
+#ifndef CHANNEL_H
+#define CHANNEL_H
+
+/*=============================================================================
+ * General defines
+ */
+
+#define MAXMODEPARAMS 6
+#define MODEBUFLEN 200
+
+#define KEYLEN 23
+#define TOPICLEN 160
+#define CHANNELLEN 200
+#define MAXBANS 30
+#define MAXBANLENGTH 1024
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define ChannelExists(n) (FindChannel(n) != NullChn)
+#define NullChn ((aChannel *)0)
+#define CREATE 1 /* whether a channel should be
+ * created or just tested for existance */
+
+#define CHFL_CHANOP 0x0001 /* Channel operator */
+#define CHFL_VOICE 0x0002 /* the power to speak */
+#define CHFL_DEOPPED 0x0004 /* Is de-opped by a server */
+#define CHFL_SERVOPOK 0x0008 /* Server op allowed */
+#define CHFL_ZOMBIE 0x0010 /* Kicked from channel */
+#define CHFL_BAN 0x0020 /* ban channel flag */
+#define CHFL_BAN_IPMASK 0x0040 /* ban mask is an IP-number mask */
+#define CHFL_BAN_OVERLAPPED 0x0080 /* ban overlapped, need bounce */
+#define CHFL_OVERLAP (CHFL_CHANOP|CHFL_VOICE)
+#define CHFL_BURST_JOINED 0x0100 /* Just joined by net.junction */
+#define CHFL_BURST_BAN 0x0200 /* Ban part of last BURST */
+#define CHFL_BURST_BAN_WIPEOUT 0x0400 /* Ban will be wiped at end of BURST */
+#define CHFL_BANVALID 0x0800 /* CHFL_BANNED bit is valid */
+#define CHFL_BANNED 0x1000 /* Channel member is banned */
+#define CHFL_SILENCE_IPMASK 0x2000 /* silence mask is an IP-number mask */
+
+/* Channel Visibility macros */
+
+#define MODE_CHANOP CHFL_CHANOP
+#define MODE_VOICE CHFL_VOICE
+#define MODE_PRIVATE 0x0004
+#define MODE_SECRET 0x0008
+#define MODE_MODERATED 0x0010
+#define MODE_TOPICLIMIT 0x0020
+#define MODE_INVITEONLY 0x0040
+#define MODE_NOPRIVMSGS 0x0080
+#define MODE_KEY 0x0100
+#define MODE_BAN 0x0200
+#define MODE_LIMIT 0x0400
+#define MODE_SENDTS 0x0800 /* TS was 0 during a local user /join; send
+ * temporary TS; can be removed when all 2.10 */
+#define MODE_LISTED 0x10000
+
+/*
+ * mode flags which take another parameter (With PARAmeterS)
+ */
+#define MODE_WPARAS (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT)
+
+#define HoldChannel(x) (!(x))
+/* name invisible */
+#define SecretChannel(x) ((x) && ((x)->mode.mode & MODE_SECRET))
+/* channel not shown but names are */
+#define HiddenChannel(x) ((x) && ((x)->mode.mode & MODE_PRIVATE))
+/* channel visible */
+#define ShowChannel(v,c) (PubChannel(c) || IsMember((v),(c)))
+#define PubChannel(x) ((!x) || ((x)->mode.mode & \
+ (MODE_PRIVATE | MODE_SECRET)) == 0)
+#define is_listed(x) ((x)->mode.mode & MODE_LISTED)
+
+#define IsLocalChannel(name) (*(name) == '&')
+#define IsModelessChannel(name) (*(name) == '+')
+#define IsChannelName(name) (*(name) == '#' || \
+ IsModelessChannel(name) || IsLocalChannel(name))
+
+/* used in SetMode() in channel.c and m_umode() in s_msg.c */
+
+#define MODE_NULL 0
+#define MODE_ADD 0x40000000
+#define MODE_DEL 0x20000000
+
+/*=============================================================================
+ * Structures
+ */
+
+struct SMode {
+ unsigned int mode;
+ unsigned int limit;
+ char key[KEYLEN + 1];
+};
+
+struct Channel {
+ struct Channel *nextch, *prevch, *hnextch;
+ Mode mode;
+ time_t creationtime;
+ char topic[TOPICLEN + 1];
+ char topic_nick[NICKLEN + 1];
+ time_t topic_time;
+ unsigned int users;
+ struct SLink *members;
+ struct SLink *invites;
+ struct SLink *banlist;
+ char chname[1];
+};
+
+struct ListingArgs {
+ time_t max_time;
+ time_t min_time;
+ unsigned int max_users;
+ unsigned int min_users;
+ unsigned int topic_limits;
+ time_t max_topic_time;
+ time_t min_topic_time;
+ struct Channel *chptr;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern Link *IsMember(aClient *cptr, aChannel *chptr);
+extern void remove_user_from_channel(aClient *sptr, aChannel *chptr);
+extern int is_chan_op(aClient *cptr, aChannel *chptr);
+extern int is_zombie(aClient *cptr, aChannel *chptr);
+extern int has_voice(aClient *cptr, aChannel *chptr);
+extern int can_send(aClient *cptr, aChannel *chptr);
+extern void send_channel_modes(aClient *cptr, aChannel *chptr);
+extern int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern char *pretty_mask(char *mask);
+extern void del_invite(aClient *cptr, aChannel *chptr);
+extern void list_next_channels(aClient *cptr, int nr);
+extern int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_create(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_destruct(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_burst(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_invite(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_list(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void send_user_joins(aClient *cptr, aClient *user);
+
+extern aChannel *channel;
+
+#endif /* CHANNEL_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/class.h
+ * Copyright (C) 1990 Darren Reed
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CLASS_H
+#define CLASS_H
+
+/*=========================================================================
+ * Structures
+ */
+
+struct ConfClass {
+ unsigned int conClass;
+ unsigned int conFreq;
+ unsigned int pingFreq;
+ unsigned int maxLinks;
+ unsigned int maxSendq;
+ unsigned int links;
+ struct ConfClass *next;
+};
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define ConClass(x) ((x)->conClass)
+#define ConFreq(x) ((x)->conFreq)
+#define PingFreq(x) ((x)->pingFreq)
+#define MaxLinks(x) ((x)->maxLinks)
+#define MaxSendq(x) ((x)->maxSendq)
+#define Links(x) ((x)->links)
+
+#define ConfLinks(x) ((x)->confClass->links)
+#define ConfMaxLinks(x) ((x)->confClass->maxLinks)
+#define ConfClass(x) ((x)->confClass->conClass)
+#define ConfConFreq(x) ((x)->confClass->conFreq)
+#define ConfPingFreq(x) ((x)->confClass->pingFreq)
+#define ConfSendq(x) ((x)->confClass->maxSendq)
+
+#define FirstClass() classes
+#define NextClass(x) ((x)->next)
+
+#define MarkDelete(x) do { MaxLinks(x) = (unsigned int)-1; } while(0)
+#define IsMarkedDelete(x) (MaxLinks(x) == (unsigned int)-1)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern aConfClass *find_class(unsigned int cclass);
+extern aConfClass *make_class(void);
+extern void free_class(aConfClass * tmp);
+extern unsigned int get_con_freq(aConfClass * clptr);
+extern unsigned int get_client_ping(aClient *acptr);
+extern unsigned int get_conf_class(aConfItem *aconf);
+extern unsigned int get_client_class(aClient *acptr);
+extern void add_class(unsigned int conclass, unsigned int ping,
+ unsigned int confreq, unsigned int maxli, size_t sendq);
+extern void check_class(void);
+extern void initclass(void);
+extern void report_classes(aClient *sptr);
+extern size_t get_sendq(aClient *cptr);
+
+extern aConfClass *classes;
+
+#endif /* CLASS_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/common.h
+ * Copyright (C) 1998 Andrea Cocito
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+ /*
+ All the code in common.h/common.c is taken from the NTL
+ (Nemesi's Tools Library), adapted for ircu's character set
+ and thereafter released under GNU GPL, from there comes the
+ NTL_ prefix of all macro and object names.
+ Removed isXdigit() to leave space to other char sets in the
+ bitmap, should give the same results as isxdigit() on any
+ implementation and isn't used in IRC anyway.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+/*=============================================================================
+ * System's headers needed in this header file
+ */
+
+#include "sys.h"
+#include <limits.h>
+
+/*=============================================================================
+ * Macros and constants for internal use,
+ * WARNING: match.c depends on these macros, don't change them
+ * without looking at that part of the code too !
+ */
+
+#define NTL_ALNUM 0x0001 /* (NTL_ALPHA|NTL_DIGIT) */
+#define NTL_ALPHA 0x0002 /* (NTL_LOWER|NTL_UPPER) */
+#define NTL_CNTRL 0x0004 /* \000 - \037 == 0x00 - 0x1F */
+#define NTL_DIGIT 0x0008 /* 0123456789 */
+#define NTL_GRAPH 0x0010 /* (NTL_ALNUM|NTL_PUNCT) */
+#define NTL_LOWER 0x0020 /* abcdefghijklmnopqrstuvwxyz{|}~ */
+#define NTL_PRINT 0x0040 /* (NTL_GRAPH|' ') */
+#define NTL_PUNCT 0x0080 /* !"#$%&'()*+,-./:;<=>?@_` */
+#define NTL_SPACE 0x0100 /* \011\012\013\014\015\040 */
+#define NTL_UPPER 0x0200 /* ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^ */
+#define NTL_IRCCH 0x0400 /* Channel's names charset */
+#define NTL_IRCCL 0x0800 /* Force toLower() in ch-name */
+#define NTL_IRCNK 0x1000 /* Nick names charset, aka isvalid() */
+#define NTL_IRCUI 0x2000 /* UserIDs charset, IRCHN plus tilde */
+#define NTL_IRCHN 0x4000 /* Hostnames charset (weak, RFC 1033) */
+#define NTL_IRCIP 0x8000 /* Numeric IPs charset (DIGIT and .) */
+#define NTL_EOL 0x10000 /* \r\n */
+
+/*=============================================================================
+ * Structures
+ */
+
+/*=============================================================================
+ * Externally visible function-like macros
+ */
+
+#define DupString(x, y) (strcpy((x = (char *)RunMalloc(strlen(y) + 1)), y))
+
+#define toLower(c) (NTL_tolower_tab[(c)-CHAR_MIN])
+#define toUpper(c) (NTL_toupper_tab[(c)-CHAR_MIN])
+
+/* Char classification pseudo-functions, when others are needed add them */
+#define isAlnum(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_ALNUM)
+#define isAlpha(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_ALPHA)
+#define isDigit(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_DIGIT)
+#define isLower(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_LOWER)
+#define isSpace(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_SPACE)
+#define isUpper(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_UPPER)
+#define isCntrl(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_CNTRL)
+
+#define isIrcCh(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCCH)
+#define isIrcCl(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCCL)
+#define isIrcNk(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCNK)
+#define isIrcUi(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCUI)
+#define isIrcHn(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCHN)
+#define isIrcIp(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCIP)
+#define isEol(c) (NTL_char_attrib[(c)-CHAR_MIN] & NTL_EOL)
+
+/* String classification pseudo-functions, when others are needed add them,
+ strIsXxxxx(s) is true when IsXxxxx(c) is true for every char in s */
+
+#define strIsAlnum(s) (strChattr(s) & NTL_ALNUM)
+#define strIsAlpha(s) (strChattr(s) & NTL_ALPHA)
+#define strIsDigit(s) (strChattr(s) & NTL_DIGIT)
+#define strIsLower(s) (strChattr(s) & NTL_LOWER)
+#define strIsSpace(s) (strChattr(s) & NTL_SPACE)
+#define strIsUpper(s) (strChattr(s) & NTL_UPPER)
+
+#define strIsIrcCh(s) (strChattr(s) & NTL_IRCCH)
+#define strIsIrcCl(s) (strChattr(s) & NTL_IRCCL)
+#define strIsIrcNk(s) (strChattr(s) & NTL_IRCNK)
+#define strIsIrcUi(s) (strChattr(s) & NTL_IRCUI)
+#define strIsIrcHn(s) (strChattr(s) & NTL_IRCHN)
+#define strIsIrcIp(s) (strChattr(s) & NTL_IRCIP)
+
+/*=============================================================================
+ * Externally visible static memory stuff
+ */
+#ifdef MAKETABLES
+extern char NTL_tolower_tab[]; /* 256 bytes */
+extern char NTL_toupper_tab[]; /* 256 bytes */
+extern unsigned int NTL_char_attrib[]; /* 256 ints = 0.5 to 2 kilobytes */
+#else
+extern const char NTL_tolower_tab[]; /* 256 bytes */
+extern const char NTL_toupper_tab[]; /* 256 bytes */
+extern const unsigned int NTL_char_attrib[]; /* 256 ints = 0.5 to 2 kilobytes */
+#endif
+
+/*=============================================================================
+ * Critical small functions to inline even in separate compilation
+ * when FORCEINLINE is defined (provided you have a compiler that supports
+ * `inline').
+ */
+
+#define NTL_HDR_strChattr int strChattr(const char *s)
+
+#define NTL_SRC_strChattr register const char *rs = s; \
+ register int x = ~0; \
+ while(*rs) \
+ x &= NTL_char_attrib[*rs++ - CHAR_MIN]; \
+ return x;
+
+#define NTL_HDR_strCasediff int strCasediff(const char *a, const char *b)
+
+#define NTL_SRC_strCasediff register const char *ra = a; \
+ register const char *rb = b; \
+ while(toLower(*ra) == toLower(*rb++)) \
+ if(!*ra++) \
+ return 0; \
+ return 1;
+
+#ifndef FORCEINLINE
+extern NTL_HDR_strChattr;
+extern NTL_HDR_strCasediff;
+#else /* FORCEINLINE */
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+inline NTL_HDR_strChattr { NTL_SRC_strChattr }
+inline NTL_HDR_strCasediff { NTL_SRC_strCasediff }
+#else
+static __inline__ NTL_HDR_strChattr { NTL_SRC_strChattr }
+static __inline__ NTL_HDR_strCasediff { NTL_SRC_strCasediff }
+#endif
+/* *INDENT-ON* */
+#endif /* FORCEINLINE */
+
+/*=============================================================================
+ * Proto types of other externally visible functions
+ */
+
+extern int strnChattr(const char *s, const size_t n);
+extern int strCasecmp(const char *a, const char *b);
+extern int strnCasecmp(const char *a, const char *b, const size_t n);
+
+#endif /* COMMON_H */
--- /dev/null
+#ifndef CRULE_H
+#define CRULE_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void crule_free(char **elem);
+extern int crule_eval(char *rule);
+extern char *crule_parse(char *rule);
+
+#endif /* CRULE_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/dbuf.h
+ * Copyright (C) 1990 Markku Savela
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef INCLUDED_dbuf_h
+#define INCLUDED_dbuf_h
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h> /* size_t */
+#define INCLUDED_sys_types_h
+#endif
+
+/*
+ * These two globals should be considered read only
+ */
+extern int DBufAllocCount; /* GLOBAL - count of dbufs allocated */
+extern int DBufUsedCount; /* GLOBAL - count of dbufs in use */
+
+struct DBufBuffer;
+
+struct DBuf {
+ size_t length; /* Current number of bytes stored */
+ struct DBufBuffer *head; /* First data buffer, if length > 0 */
+ struct DBufBuffer *tail; /* last data buffer, if length > 0 */
+};
+
+/*
+ * DBufLength - Returns the current number of bytes stored into the buffer.
+ */
+#define DBufLength(dyn) ((dyn)->length)
+
+/*
+ * DBufClear - Scratch the current content of the buffer.
+ * Release all allocated buffers and make it empty.
+ */
+#define DBufClear(dyn) dbuf_delete((dyn), DBufLength(dyn))
+
+/*
+ * Prototypes
+ */
+extern void dbuf_delete(struct DBuf *dyn, size_t length);
+extern int dbuf_put(struct DBuf *dyn, const char *buf, size_t length);
+extern const char *dbuf_map(const struct DBuf *dyn, size_t *length);
+extern size_t dbuf_get(struct DBuf *dyn, char *buf, size_t length);
+extern size_t dbuf_getmsg(struct DBuf *dyn, char *buf, size_t length);
+extern void dbuf_count_memory(size_t *allocated, size_t *used);
+
+
+#endif /* INCLUDED_dbuf_h */
--- /dev/null
+#ifndef INCLUDED_fileio_h
+#define INCLUDED_fileio_h
+
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h> /* size_t */
+#define INCLUDED_sys_types_h
+#endif
+#ifndef INCLUDED_sys_stat_h
+#include <sys/stat.h> /* struct stat */
+#define INCLUDED_sys_stat_h
+#endif
+
+/*
+ * FileBuf is a mirror of the ANSI FILE struct, but it works for any
+ * file descriptor. FileBufs are allocated when a file is opened with
+ * fbopen, and they are freed when the file is closed using fbclose.
+ */
+typedef struct FileBuf FBFILE;
+
+/*
+ * open a file and return a FBFILE*, see fopen(3)
+ */
+extern FBFILE *fbopen(const char *filename, const char *mode);
+/*
+ * associate a file descriptor with a FBFILE*
+ * if a FBFILE* is associated here it MUST be closed using fbclose
+ * see fdopen(3)
+ */
+extern FBFILE *fdbopen(int fd, const char *mode);
+/*
+ * close a file opened with fbopen, see fclose(3)
+ */
+extern void fbclose(FBFILE * fb);
+/*
+ * return the next character from the file, EOF on end of file
+ * see fgetc(3)
+ */
+extern int fbgetc(FBFILE * fb);
+/*
+ * return next string in a file up to and including the newline character
+ * see fgets(3)
+ */
+extern char *fbgets(char *buf, size_t len, FBFILE * fb);
+/*
+ * write a null terminated string to a file, see fputs(3)
+ */
+extern int fbputs(const char *str, FBFILE * fb);
+/*
+ * return the status of the file associated with fb, see fstat(3)
+ */
+extern int fbstat(struct stat *sb, FBFILE * fb);
+
+#endif /* INCLUDED_fileio_h */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/h.h
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef H_H
+#define H_H
+
+/* Typedefs */
+
+typedef struct Client aClient;
+typedef struct Server aServer;
+typedef struct User anUser;
+typedef struct Channel aChannel;
+typedef struct SMode Mode;
+typedef struct ConfItem aConfItem;
+typedef struct Message aMessage;
+typedef struct MessageTree aMessageTree;
+typedef struct Gline aGline;
+typedef struct ListingArgs aListingArgs;
+typedef struct MotdItem aMotdItem;
+typedef struct trecord atrecord;
+typedef unsigned int snomask_t;
+typedef struct ConfClass aConfClass;
+typedef struct hashentry aHashEntry;
+typedef struct SLink Link;
+typedef struct DSlink Dlink;
+typedef struct Whowas aWhowas;
+
+#include "s_debug.h"
+
+#endif /* H_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/hash.h
+ * Copyright (C) 1998 by Andrea "Nemesi" Cocito
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#include "s_serv.h" /* For STAT_* values and StatusMask() macro */
+
+/*=============================================================================
+ * general defines
+ */
+
+/* Now client and channel hash table must be of the same size */
+#define HASHSIZE 32000
+
+/*=============================================================================
+ * Structures
+ */
+
+/*=============================================================================
+ * Macros for internal use
+ */
+
+/*=============================================================================
+ * Externally visible pseudofunctions (macro interface to internal functions)
+ */
+
+/* Raw calls, expect a core if you pass a NULL or zero-length name */
+#define SeekChannel(name) hSeekChannel((name))
+#define SeekClient(name) hSeekClient((name), ~StatusMask(STAT_PING))
+#define SeekUser(name) hSeekClient((name), StatusMask(STAT_USER))
+#define SeekServer(name) hSeekClient((name), StatusMask(STAT_ME) | \
+ StatusMask(STAT_SERVER) )
+
+/* Safer macros with sanity check on name, WARNING: these are _macros_,
+ no side effects allowed on <name> ! */
+#define FindChannel(name) (BadPtr((name))?NULL:SeekChannel(name))
+#define FindClient(name) (BadPtr((name))?NULL:SeekClient(name))
+#define FindUser(name) (BadPtr((name))?NULL:SeekUser(name))
+#define FindServer(name) (BadPtr((name))?NULL:SeekServer(name))
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void hash_init(void); /* Call me on startup */
+extern int hAddClient(aClient *cptr);
+extern int hAddChannel(aChannel *chptr);
+extern int hRemClient(aClient *cptr);
+extern int hChangeClient(aClient *cptr, char *newname);
+extern int hRemChannel(aChannel *chptr);
+extern aClient *hSeekClient(char *name, int TMask);
+extern aChannel *hSeekChannel(char *name);
+
+extern int m_hash(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+extern int isNickJuped(char *nick);
+extern int addNickJupes(char *nicks);
+extern void clearNickJupes(void);
+
+#endif /* HASH_H */
--- /dev/null
+#ifndef IRCD_H
+#define IRCD_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define TStime() (now + TSoffset)
+#define BadPtr(x) (!(x) || (*(x) == '\0'))
+
+/* Miscellaneous defines */
+
+#define UDP_PORT "7007"
+#define MINOR_PROTOCOL "09"
+#define MAJOR_PROTOCOL "10"
+#define BASE_VERSION "u2.10"
+
+/* Flags for bootup options (command line flags) */
+
+#define BOOT_CONSOLE 1
+#define BOOT_QUICK 2
+#define BOOT_DEBUG 4
+#define BOOT_INETD 8
+#define BOOT_TTY 16
+#define BOOT_OPER 32
+#define BOOT_AUTODIE 64
+
+/*=============================================================================
+ * Proto types
+ */
+
+#ifdef PROFIL
+extern RETSIGTYPE s_monitor(HANDLER_ARG(int sig));
+#endif
+extern RETSIGTYPE s_die(HANDLER_ARG(int sig));
+extern RETSIGTYPE s_restart(HANDLER_ARG(int sig));
+
+extern void restart(char *mesg);
+extern void server_reboot(void);
+
+extern aClient me;
+extern time_t now;
+extern aClient *client;
+extern time_t TSoffset;
+extern unsigned int bootopt;
+extern time_t nextdnscheck;
+extern time_t nextconnect;
+extern int dorehash;
+extern time_t nextping;
+extern unsigned short int portnum;
+extern char *configfile;
+extern int debuglevel;
+extern char *debugmode;
+
+#endif /* IRCD_H */
--- /dev/null
+#ifndef LIST_H
+#define LIST_H
+
+/*=============================================================================
+ * General defines
+ */
+
+/*=============================================================================
+ * Macro's
+ */
+
+/* ============================================================================
+ * Structures
+ */
+
+struct SLink {
+ struct SLink *next;
+ union {
+ aClient *cptr;
+ struct Channel *chptr;
+ struct ConfItem *aconf;
+ char *cp;
+ struct {
+ char *banstr;
+ char *who;
+ time_t when;
+ } ban;
+ } value;
+ unsigned int flags;
+};
+
+struct DSlink {
+ struct DSlink *next;
+ struct DSlink *prev;
+ union {
+ aClient *cptr;
+ struct Channel *chptr;
+ struct ConfItem *aconf;
+ char *cp;
+ } value;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void free_link(Link *lp);
+extern Link *make_link(void);
+extern Link *find_user_link(Link *lp, aClient *ptr);
+extern void initlists(void);
+extern void outofmemory(void);
+extern aClient *make_client(aClient *from, int status);
+extern void free_client(aClient *cptr);
+extern struct User *make_user(aClient *cptr);
+extern struct Server *make_server(aClient *cptr);
+extern void free_user(struct User *user, aClient *cptr);
+extern void remove_client_from_list(aClient *cptr);
+extern void add_client_to_list(aClient *cptr);
+extern Dlink *add_dlink(Dlink **lpp, aClient *cp);
+extern void remove_dlink(Dlink **lpp, Dlink *lp);
+extern struct ConfItem *make_conf(void);
+extern void delist_conf(struct ConfItem *aconf);
+extern void free_conf(struct ConfItem *aconf);
+extern aGline *make_gline(int is_ipmask, char *host, char *reason, char *name,
+ time_t expire);
+extern aGline *find_gline(aClient *cptr, aGline **pgline);
+extern void free_gline(aGline *agline, aGline *pgline);
+extern void send_listinfo(aClient *cptr, char *name);
+
+#endif /* LIST_H */
--- /dev/null
+#ifndef MAP_H
+#define MAP_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_map(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+#endif /* MAP_H */
--- /dev/null
+#ifndef MATCH_H
+#define MATCH_H
+
+/*=============================================================================
+ * System headers used by this header file
+ */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*=============================================================================
+ * Structures
+ */
+
+struct in_mask {
+ struct in_addr bits;
+ struct in_addr mask;
+ int fall;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int mmatch(const char *old_mask, const char *new_mask);
+extern int match(const char *ma, const char *na);
+extern char *collapse(char *pattern);
+
+extern int matchcomp(char *cmask, int *minlen, int *charset, const char *mask);
+extern int matchexec(const char *string, const char *cmask, int minlen);
+extern int matchdecomp(char *mask, const char *cmask);
+extern int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen);
+extern int matchcompIP(struct in_mask *imask, const char *mask);
+
+#endif /* MATCH_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/msg.h
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef MSG_H
+#define MSG_H
+
+/*=============================================================================
+ * General defines
+ */
+
+#define MAXPARA 15
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+/*
+ * Tokenization:
+ * Each command must have a TOK_COMMAND and MSG_COMMAND definition.
+ * If you don't want one or the other, make them the same.
+ * Also each command has a "msgclass" used for debugging purposes.
+ */
+
+/* *INDENT-OFF* */
+
+#define MSG_PRIVATE "PRIVMSG" /* PRIV */
+#define TOK_PRIVATE "P"
+#define CLASS_PRIVATE LEVEL_PROPAGATE
+
+#define MSG_WHO "WHO" /* WHO -> WHOC */
+#define TOK_WHO "H"
+#define CLASS_WHO LEVEL_QUERY
+
+#define MSG_WHOIS "WHOIS" /* WHOI */
+#define TOK_WHOIS "W"
+#define CLASS_WHOIS LEVEL_QUERY
+
+#define MSG_WHOWAS "WHOWAS" /* WHOW */
+#define TOK_WHOWAS "X"
+#define CLASS_WHOWAS LEVEL_QUERY
+
+#define MSG_USER "USER" /* USER */
+#define TOK_USER "USER"
+#define CLASS_USER LEVEL_CLIENT
+
+#define MSG_NICK "NICK" /* NICK */
+#define TOK_NICK "N"
+#define CLASS_NICK LEVEL_CLIENT
+
+#define MSG_SERVER "SERVER" /* SERV */
+#define TOK_SERVER "S"
+#define CLASS_SERVER LEVEL_MAP
+
+#define MSG_LIST "LIST" /* LIST */
+#define TOK_LIST "LIST"
+#define CLASS_LIST LEVEL_QUERY
+
+#define MSG_TOPIC "TOPIC" /* TOPI */
+#define TOK_TOPIC "T"
+#define CLASS_TOPIC LEVEL_PROPAGATE
+
+#define MSG_INVITE "INVITE" /* INVI */
+#define TOK_INVITE "I"
+#define CLASS_INVITE LEVEL_MODE
+
+#define MSG_VERSION "VERSION" /* VERS */
+#define TOK_VERSION "V"
+#define CLASS_VERSION LEVEL_QUERY
+
+#define MSG_QUIT "QUIT" /* QUIT */
+#define TOK_QUIT "Q"
+#define CLASS_QUIT LEVEL_CLIENT
+
+#define MSG_SQUIT "SQUIT" /* SQUI */
+#define TOK_SQUIT "SQ"
+#define CLASS_SQUIT LEVEL_MAP
+
+#define MSG_KILL "KILL" /* KILL */
+#define TOK_KILL "D"
+#define CLASS_KILL LEVEL_CLIENT
+
+#define MSG_INFO "INFO" /* INFO */
+#define TOK_INFO "F"
+#define CLASS_INFO LEVEL_QUERY
+
+#define MSG_LINKS "LINKS" /* LINK */
+#define TOK_LINKS "LI"
+#define CLASS_LINKS LEVEL_QUERY
+
+#define MSG_STATS "STATS" /* STAT */
+#define TOK_STATS "R"
+#define CLASS_STATS LEVEL_QUERY
+
+#define MSG_HELP "HELP" /* HELP */
+#define TOK_HELP "HELP"
+#define CLASS_HELP LEVEL_QUERY
+
+#define MSG_ERROR "ERROR" /* ERRO */
+#define TOK_ERROR "Y"
+#define CLASS_ERROR LEVEL_PROPAGATE
+
+#define MSG_AWAY "AWAY" /* AWAY */
+#define TOK_AWAY "A"
+#define CLASS_AWAY LEVEL_PROPAGATE
+
+#define MSG_CONNECT "CONNECT" /* CONN */
+#define TOK_CONNECT "CO"
+#define CLASS_CONNECT LEVEL_PROPAGATE
+
+#define MSG_UPING "UPING" /* UPIN */
+#define TOK_UPING "UP"
+#define CLASS_UPING LEVEL_PROPAGATE
+
+#define MSG_MAP "MAP" /* MAP */
+#define TOK_MAP "MAP"
+#define CLASS_MAP LEVEL_QUERY
+
+#define MSG_PING "PING" /* PING */
+#define TOK_PING "G"
+#define CLASS_PING LEVEL_PROPAGATE
+
+#define MSG_PONG "PONG" /* PONG */
+#define TOK_PONG "Z"
+#define CLASS_PONG LEVEL_CLIENT
+
+#define MSG_OPER "OPER" /* OPER */
+#define TOK_OPER "OPER"
+#define CLASS_OPER LEVEL_PROPAGATE
+
+#define MSG_PASS "PASS" /* PASS */
+#define TOK_PASS "PA"
+#define CLASS_PASS LEVEL_CLIENT
+
+#define MSG_WALLOPS "WALLOPS" /* WALL */
+#define TOK_WALLOPS "WA"
+#define CLASS_WALLOPS LEVEL_PROPAGATE
+
+#define MSG_DESYNCH "DESYNCH" /* DESY */
+#define TOK_DESYNCH "DS"
+#define CLASS_DESYNCH LEVEL_PROPAGATE
+
+#define MSG_TIME "TIME" /* TIME */
+#define TOK_TIME "TI"
+#define CLASS_TIME LEVEL_QUERY
+
+#define MSG_SETTIME "SETTIME" /* SETT */
+#define TOK_SETTIME "SE"
+#define CLASS_SETTIME LEVEL_PROPAGATE
+
+#define MSG_RPING "RPING" /* RPIN */
+#define TOK_RPING "RI"
+#define CLASS_RPING LEVEL_PROPAGATE
+
+#define MSG_RPONG "RPONG" /* RPON */
+#define TOK_RPONG "RO"
+#define CLASS_RPONG LEVEL_PROPAGATE
+
+#define MSG_NAMES "NAMES" /* NAME */
+#define TOK_NAMES "E"
+#define CLASS_NAMES LEVEL_QUERY
+
+#define MSG_ADMIN "ADMIN" /* ADMI */
+#define TOK_ADMIN "AD"
+#define CLASS_ADMIN LEVEL_QUERY
+
+#define MSG_TRACE "TRACE" /* TRAC */
+#define TOK_TRACE "TR"
+#define CLASS_TRACE LEVEL_PROPAGATE
+
+#define MSG_NOTICE "NOTICE" /* NOTI */
+#define TOK_NOTICE "O"
+#define CLASS_NOTICE LEVEL_PROPAGATE
+
+#define MSG_WALLCHOPS "WALLCHOPS" /* WC */
+#define TOK_WALLCHOPS "WC"
+#define CLASS_WALLCHOPS LEVEL_PROPAGATE
+
+#define MSG_CPRIVMSG "CPRIVMSG" /* CPRI */
+#define TOK_CPRIVMSG "CP"
+#define CLASS_CPRIVMSG LEVEL_CLIENT
+
+#define MSG_CNOTICE "CNOTICE" /* CNOT */
+#define TOK_CNOTICE "CN"
+#define CLASS_CNOTICE LEVEL_CLIENT
+
+#define MSG_JOIN "JOIN" /* JOIN */
+#define TOK_JOIN "J"
+#define CLASS_JOIN LEVEL_CHANNEL
+
+#define MSG_PART "PART" /* PART */
+#define TOK_PART "L"
+#define CLASS_PART LEVEL_CHANNEL
+
+#define MSG_LUSERS "LUSERS" /* LUSE */
+#define TOK_LUSERS "LU"
+#define CLASS_LUSERS LEVEL_QUERY
+
+#define MSG_MOTD "MOTD" /* MOTD */
+#define TOK_MOTD "MO"
+#define CLASS_MOTD LEVEL_QUERY
+
+#define MSG_MODE "MODE" /* MODE */
+#define TOK_MODE "M"
+#define CLASS_MODE LEVEL_MODE
+
+#define MSG_KICK "KICK" /* KICK */
+#define TOK_KICK "K"
+#define CLASS_KICK LEVEL_CHANNEL
+
+#define MSG_USERHOST "USERHOST" /* USER -> USRH */
+#define TOK_USERHOST "USERHOST"
+#define CLASS_USERHOST LEVEL_QUERY
+
+#define MSG_USERIP "USERIP" /* USER -> USIP */
+#define TOK_USERIP "USERIP"
+#define CLASS_USERIP LEVEL_QUERY
+
+#define MSG_ISON "ISON" /* ISON */
+#define TOK_ISON "ISON"
+#define CLASS_ISON LEVEL_QUERY
+
+#define MSG_SQUERY "SQUERY" /* SQUE */
+#define TOK_SQUERY "SQUERY"
+#define CLASS_SQUERY LEVEL_QUERY
+
+#define MSG_SERVLIST "SERVLIST" /* SERV -> SLIS */
+#define TOK_SERVLIST "SERVSET"
+#define CLASS_SERVLIST LEVEL_QUERY
+
+#define MSG_SERVSET "SERVSET" /* SERV -> SSET */
+#define TOK_SERVSET "SERVSET"
+#define CLASS_SERVSET LEVEL_CLIENT
+
+#define MSG_REHASH "REHASH" /* REHA */
+#define TOK_REHASH "REHASH"
+#define CLASS_REHASH LEVEL_MAP
+
+#define MSG_RESTART "RESTART" /* REST */
+#define TOK_RESTART "RESTART"
+#define CLASS_RESTART LEVEL_MAP
+
+#define MSG_CLOSE "CLOSE" /* CLOS */
+#define TOK_CLOSE "CLOSE"
+#define CLASS_CLOSE LEVEL_CLIENT
+
+#define MSG_DIE "DIE" /* DIE */
+#define TOK_DIE "DIE"
+#define CLASS_DIE LEVEL_MAP
+
+#define MSG_HASH "HASH" /* HASH */
+#define TOK_HASH "HASH"
+#define CLASS_HASH LEVEL_QUERY
+
+#define MSG_DNS "DNS" /* DNS -> DNSS */
+#define TOK_DNS "DNS"
+#define CLASS_DNS LEVEL_QUERY
+
+#define MSG_SILENCE "SILENCE" /* SILE */
+#define TOK_SILENCE "U"
+#define CLASS_SILENCE LEVEL_PROPAGATE
+
+#define MSG_GLINE "GLINE" /* GLIN */
+#define TOK_GLINE "GL"
+#define CLASS_GLINE LEVEL_CLIENT
+
+#define MSG_BURST "BURST" /* BURS */
+#define TOK_BURST "B"
+#define CLASS_BURST LEVEL_CHANNEL
+
+#define MSG_CREATE "CREATE" /* CREA */
+#define TOK_CREATE "C"
+#define CLASS_CREATE LEVEL_CHANNEL
+
+#define MSG_DESTRUCT "DESTRUCT" /* DEST */
+#define TOK_DESTRUCT "DE"
+#define CLASS_DESTRUCT LEVEL_CHANNEL
+
+#define MSG_END_OF_BURST "END_OF_BURST" /* END_ */
+#define TOK_END_OF_BURST "EB"
+#define CLASS_END_OF_BURST LEVEL_MAP
+
+#define MSG_END_OF_BURST_ACK "EOB_ACK" /* EOB_ */
+#define TOK_END_OF_BURST_ACK "EA"
+#define CLASS_END_OF_BURST_ACK LEVEL_MAP
+
+/* *INDENT-ON* */
+
+/*=============================================================================
+ * Constants
+ */
+#define MFLG_SLOW 0x01 /* Command can be executed roughly *
+ * once per 2 seconds. */
+#define MFLG_UNREG 0x02 /* Command available to unregistered *
+ * clients. */
+
+/*=============================================================================
+ * Structures
+ */
+
+struct Message {
+ unsigned int msgclass;
+ char *cmd;
+ char *tok;
+ int (*func) (aClient *cptr, aClient *sptr, int parc, char *parv[]);
+ /* cptr = Connected client ptr
+ sptr = Source client ptr
+ parc = parameter count
+ parv = parameter variable array */
+ unsigned int count;
+ unsigned int parameters;
+ unsigned char flags; /* bit 0 set means that this command is allowed
+ to be used only on the average of once per 2
+ seconds -SRB */
+ unsigned int bytes;
+};
+
+struct MessageTree {
+ char *final;
+ struct Message *msg;
+ struct MessageTree *pointers[26];
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern struct Message msgtab[];
+
+#endif /* MSG_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/numeric.h
+ * Copyright (C) 1990 Jarkko Oikarinen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef NUMERIC_H
+#define NUMERIC_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+/*
+ * Reserve numerics 000-099 for server-client connections where the client
+ * is local to the server. If any server is passed a numeric in this range
+ * from another server then it is remapped to 100-199. -avalon
+ */
+#define RPL_WELCOME 1
+#define RPL_YOURHOST 2
+#define RPL_CREATED 3
+#define RPL_MYINFO 4
+#define RPL_MAP 5 /* Undernet extension */
+#define RPL_MAPMORE 6 /* Undernet extension */
+#define RPL_MAPEND 7 /* Undernet extension */
+#define RPL_SNOMASK 8 /* Undernet extension */
+#define RPL_STATMEMTOT 9 /* Undernet extension */
+#define RPL_STATMEM 10 /* Undernet extension */
+/* RPL_YOURCOOKIE 14 IRCnet extension */
+
+/*
+ * Errors are in the range from 400-599 currently and are grouped by what
+ * commands they come from.
+ */
+#define ERR_NOSUCHNICK 401
+#define ERR_NOSUCHSERVER 402
+#define ERR_NOSUCHCHANNEL 403
+#define ERR_CANNOTSENDTOCHAN 404
+#define ERR_TOOMANYCHANNELS 405
+#define ERR_WASNOSUCHNICK 406
+#define ERR_TOOMANYTARGETS 407
+#define ERR_NOORIGIN 409
+
+#define ERR_NORECIPIENT 411
+#define ERR_NOTEXTTOSEND 412
+#define ERR_NOTOPLEVEL 413
+#define ERR_WILDTOPLEVEL 414
+
+#define ERR_QUERYTOOLONG 416 /* Undernet extension */
+
+#define ERR_UNKNOWNCOMMAND 421
+#define ERR_NOMOTD 422
+#define ERR_NOADMININFO 423
+/* ERR_FILEERROR 424 removed from RFC1459 */
+
+#define ERR_NONICKNAMEGIVEN 431
+#define ERR_ERRONEUSNICKNAME 432
+#define ERR_NICKNAMEINUSE 433
+#define ERR_NICKCOLLISION 436
+#define ERR_BANNICKCHANGE 437 /* Undernet extension */
+#define ERR_NICKTOOFAST 438 /* Undernet extension */
+#define ERR_TARGETTOOFAST 439 /* Undernet extension */
+
+#define ERR_USERNOTINCHANNEL 441
+#define ERR_NOTONCHANNEL 442
+#define ERR_USERONCHANNEL 443
+/* ERR_NOLOGIN 444 removed from RFC1459 */
+/* ERR_SUMMONDISABLED 445 removed from RFC1459 */
+/* ERR_USERSDISABLED 446 removed from RFC1459 */
+
+#define ERR_NOTREGISTERED 451
+/* ERR_IDCOLLISION 452 IRCnet extension */
+/* ERR_NICKLOST 453 IRCnet extension */
+
+#define ERR_NEEDMOREPARAMS 461
+#define ERR_ALREADYREGISTRED 462
+#define ERR_NOPERMFORHOST 463
+#define ERR_PASSWDMISMATCH 464
+#define ERR_YOUREBANNEDCREEP 465
+#define ERR_YOUWILLBEBANNED 466
+#define ERR_KEYSET 467 /* Undernet extension */
+#define ERR_INVALIDUSERNAME 468 /* Undernet extension */
+
+#define ERR_CHANNELISFULL 471
+#define ERR_UNKNOWNMODE 472
+#define ERR_INVITEONLYCHAN 473
+#define ERR_BANNEDFROMCHAN 474
+#define ERR_BADCHANNELKEY 475
+#define ERR_BADCHANMASK 476 /* Undernet extension */
+/* ERR_NEEDREGGEDNICK 477 DalNet Extention */
+#define ERR_BANLISTFULL 478 /* Undernet extension */
+/* #define ERR_BADCHANNAME 479 EFNet extension */
+
+#define ERR_NOPRIVILEGES 481
+#define ERR_CHANOPRIVSNEEDED 482
+#define ERR_CANTKILLSERVER 483
+#define ERR_ISCHANSERVICE 484 /* Undernet extension */
+/* ERR_CHANTOORECENT 487 IRCnet extension */
+/* ERR_TSLESSCHAN 488 IRCnet extension */
+#define ERR_VOICENEEDED 489 /* Undernet extension */
+
+#define ERR_NOOPERHOST 491
+
+#define ERR_UMODEUNKNOWNFLAG 501
+#define ERR_USERSDONTMATCH 502
+
+#define ERR_SILELISTFULL 511 /* Undernet extension */
+
+#define ERR_NOSUCHGLINE 512 /* Undernet extension */
+#define ERR_BADPING 513 /* Undernet extension */
+
+/*
+ * Numberic replies from server commands.
+ * These are currently in the range 200-399.
+ */
+#define RPL_NONE 300
+#define RPL_AWAY 301
+#define RPL_USERHOST 302
+#define RPL_ISON 303
+#define RPL_TEXT 304
+#define RPL_UNAWAY 305
+#define RPL_NOWAWAY 306
+#define RPL_USERIP 307 /* Undernet extension */
+
+#define RPL_WHOISUSER 311 /* See also RPL_ENDOFWHOIS */
+#define RPL_WHOISSERVER 312
+#define RPL_WHOISOPERATOR 313
+
+#define RPL_WHOWASUSER 314 /* See also RPL_ENDOFWHOWAS */
+#define RPL_ENDOFWHO 315 /* See RPL_WHOREPLY/RPL_WHOSPCRPL */
+
+/* RPL_WHOISCHANOP 316 removed from RFC1459 */
+
+#define RPL_WHOISIDLE 317
+
+#define RPL_ENDOFWHOIS 318 /* See RPL_WHOISUSER/RPL_WHOISSERVER/
+ RPL_WHOISOPERATOR/RPL_WHOISIDLE */
+#define RPL_WHOISCHANNELS 319
+
+#define RPL_LISTSTART 321
+#define RPL_LIST 322
+#define RPL_LISTEND 323
+#define RPL_CHANNELMODEIS 324
+/* RPL_CHANNELPASSIS 325 IRCnet extension */
+/* RPL_NOCHANPASS 326 IRCnet extension */
+/* RPL_CHPASSUNKNOWN 327 IRCnet extension */
+#define RPL_CREATIONTIME 329
+
+#define RPL_NOTOPIC 331
+#define RPL_TOPIC 332
+#define RPL_TOPICWHOTIME 333 /* Undernet extension */
+#define RPL_LISTUSAGE 334 /* Undernet extension */
+/* RPL_CHANPASSOK 338 IRCnet extension */
+/* RPL_BADCHANPASS 339 IRCnet extension */
+
+#define RPL_INVITING 341
+/* RPL_SUMMONING 342 removed from RFC1459 */
+/* RPL_EXCEPTLIST 348 IRCnet extension */
+/* RPL_ENDOFEXCEPTLIST 349 IRCnet extension */
+
+#define RPL_VERSION 351
+
+#define RPL_WHOREPLY 352 /* See also RPL_ENDOFWHO */
+#define RPL_NAMREPLY 353 /* See also RPL_ENDOFNAMES */
+#define RPL_WHOSPCRPL 354 /* Undernet extension,
+ See also RPL_ENDOFWHO */
+
+#define RPL_KILLDONE 361
+#define RPL_CLOSING 362
+#define RPL_CLOSEEND 363
+#define RPL_LINKS 364
+#define RPL_ENDOFLINKS 365
+#define RPL_ENDOFNAMES 366 /* See RPL_NAMREPLY */
+#define RPL_BANLIST 367
+#define RPL_ENDOFBANLIST 368
+#define RPL_ENDOFWHOWAS 369
+
+#define RPL_INFO 371
+#define RPL_MOTD 372
+#define RPL_INFOSTART 373
+#define RPL_ENDOFINFO 374
+#define RPL_MOTDSTART 375
+#define RPL_ENDOFMOTD 376
+
+#define RPL_YOUREOPER 381
+#define RPL_REHASHING 382
+#define RPL_MYPORTIS 384
+#define RPL_NOTOPERANYMORE 385 /* Extension to RFC1459 */
+
+#define RPL_TIME 391
+
+#define RPL_TRACELINK 200
+#define RPL_TRACECONNECTING 201
+#define RPL_TRACEHANDSHAKE 202
+#define RPL_TRACEUNKNOWN 203
+#define RPL_TRACEOPERATOR 204
+#define RPL_TRACEUSER 205
+#define RPL_TRACESERVER 206
+#define RPL_TRACENEWTYPE 208
+#define RPL_TRACECLASS 209
+
+#define RPL_STATSLINKINFO 211
+#define RPL_STATSCOMMANDS 212
+#define RPL_STATSCLINE 213
+#define RPL_STATSNLINE 214
+#define RPL_STATSILINE 215
+#define RPL_STATSKLINE 216
+#define RPL_STATSPLINE 217 /* Undernet extenstion */
+#define RPL_STATSYLINE 218
+#define RPL_ENDOFSTATS 219 /* See also RPL_STATSDLINE */
+
+#define RPL_UMODEIS 221
+
+#define RPL_SERVICEINFO 231
+#define RPL_ENDOFSERVICES 232
+#define RPL_SERVICE 233
+#define RPL_SERVLIST 234
+#define RPL_SERVLISTEND 235
+
+#define RPL_STATSLLINE 241
+#define RPL_STATSUPTIME 242
+#define RPL_STATSOLINE 243
+#define RPL_STATSHLINE 244
+/* RPL_STATSSLINE 245 Reserved */
+#define RPL_STATSTLINE 246 /* Undernet extension */
+#define RPL_STATSGLINE 247 /* Undernet extension */
+#define RPL_STATSULINE 248 /* Undernet extension */
+#define RPL_STATSDEBUG 249 /* Extension to RFC1459 */
+#define RPL_STATSCONN 250 /* Undernet extension */
+
+#define RPL_LUSERCLIENT 251
+#define RPL_LUSEROP 252
+#define RPL_LUSERUNKNOWN 253
+#define RPL_LUSERCHANNELS 254
+#define RPL_LUSERME 255
+#define RPL_ADMINME 256
+#define RPL_ADMINLOC1 257
+#define RPL_ADMINLOC2 258
+#define RPL_ADMINEMAIL 259
+
+#define RPL_TRACELOG 261
+#define RPL_TRACEPING 262 /* Extension to RFC1459 */
+
+#define RPL_SILELIST 271 /* Undernet extension */
+#define RPL_ENDOFSILELIST 272 /* Undernet extension */
+
+/* RPL_STATSDELTA 274 IRCnet extension */
+#define RPL_STATSDLINE 275 /* Undernet extension */
+
+#define RPL_GLIST 280 /* Undernet extension */
+#define RPL_ENDOFGLIST 281 /* Undernet extension */
+
+#endif /* NUMERIC_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/h.h
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef INCLUDED_numnicks_h
+#define INCLUDED_numnicks_h
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h>
+#define INCLUDED_sys_types_h
+#endif
+
+/*=============================================================================
+ * General defines
+ */
+
+/*
+ * used for buffer size calculations in channel.c
+ */
+#define NUMNICKLEN 5 /* strlen("YYXXX") */
+
+/*=============================================================================
+ * Macros
+ */
+
+/*
+ * Use this macro as follows: sprintf(buf, "%s%s ...", NumNick(cptr), ...);
+ */
+#define NumNick(c) (c)->user->server->yxx, (c)->yxx
+
+/*
+ * Use this macro as follows: sprintf(buf, "%s ...", NumServ(cptr), ...);
+ */
+#define NumServ(c) (c)->yxx
+
+/*
+ * Use this macro as follows: sprintf(buf, "%s%s ...", NumServCap(cptr), ...);
+ */
+#define NumServCap(c) (c)->yxx, (c)->serv->nn_capacity
+
+/*=============================================================================
+ * Structures
+ */
+struct Client;
+
+/*=============================================================================
+ * Proto types
+ */
+extern int SetRemoteNumNick(struct Client *cptr, const char *yxx);
+extern void SetLocalNumNick(struct Client *cptr);
+extern void RemoveYXXClient(struct Client *server, const char *yxx);
+extern void SetServerYXX(struct Client *cptr,
+ struct Client *server, const char *yxx);
+extern void ClearServerYXX(const struct Client *server);
+
+extern void SetYXXCapacity(struct Client *myself, size_t max_clients);
+extern void SetYXXServerName(struct Client *myself, unsigned int numeric);
+
+extern int markMatchexServer(const char *cmask, int minlen);
+extern struct Client *find_match_server(char *mask);
+extern struct Client *findNUser(const char *yxx);
+extern struct Client *FindNServer(const char *numeric);
+
+extern unsigned int base64toint(const char *str);
+extern const char *inttobase64(char *buf, unsigned int v, size_t count);
+
+#ifndef NO_PROTOCOL9
+extern const char *CreateNNforProtocol9server(const struct Client *server);
+#endif
+
+#endif /* INCLUDED_numnicks_h */
--- /dev/null
+#ifndef OPERCMDS_H
+#define OPERCMDS_H
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define GLINE_ACTIVE 1
+#define GLINE_IPMASK 2
+#define GLINE_LOCAL 4
+
+/*
+ * G-line macros.
+ */
+
+#define GlineIsActive(g) ((g)->gflags & GLINE_ACTIVE)
+#define GlineIsIpMask(g) ((g)->gflags & GLINE_IPMASK)
+#define GlineIsLocal(g) ((g)->gflags & GLINE_LOCAL)
+
+#define SetActive(g) ((g)->gflags |= GLINE_ACTIVE)
+#define ClearActive(g) ((g)->gflags &= ~GLINE_ACTIVE)
+#define SetGlineIsIpMask(g) ((g)->gflags |= GLINE_IPMASK)
+#define SetGlineIsLocal(g) ((g)->gflags |= GLINE_LOCAL)
+
+/*=============================================================================
+ * Structures
+ */
+
+struct Gline {
+ struct Gline *next;
+ char *host;
+ char *reason;
+ char *name;
+ time_t expire;
+ unsigned int gflags;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+extern int m_rehash(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+#endif
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+extern int m_restart(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+#endif
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+extern int m_die(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+#endif
+extern int m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_wallops(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_time(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_settime(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_rping(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_rpong(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_trace(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_close(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_gline(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+#endif /* OPERCMDS_H */
--- /dev/null
+#ifndef PACKET_H
+#define PACKET_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int dopacket(aClient *cptr, char *buffer, int length);
+extern int client_dopacket(aClient *cptr, size_t length);
+
+#endif /* PACKET_H */
--- /dev/null
+#ifndef PARSE_H
+#define PARSE_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int parse_client(aClient *cptr, char *buffer, char *bufend);
+extern int parse_server(aClient *cptr, char *buffer, char *bufend);
+extern void initmsgtree(void);
+
+#endif /* PARSE_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/patchlevel.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * PATCHes
+ *
+ * Only put here ADDED special stuff, for instance: ".mu3" or ".ban"
+ * Please start the patchlevel with a '.'
+ *
+ * IMPORTANT: Since u2.9 there is a new format of this file. The reason
+ * is that this way it shouldn't be needed anymore for the user to edit
+ * this manually !!!
+ * If you do, be sure you know what you are doing!
+ *
+ * For patch devellopers:
+ * To make a diff of your patch, edit any of the below lines containing
+ * a "" (an EMPTY string). Your patch will then succeed, with only an
+ * offset, on the first empty place in the users patchlevel.h.
+ * Do not change anyother line, the '\' are to make sure that the 'fuzz'
+ * will stay 0. --Run
+ */
+
+#define PATCH1 \
+ \
+ \
+ \
+ ".07"
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH2 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH3 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH4 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH5 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH6 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH7 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH8 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH9 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH10 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH11 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH12 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH13 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH14 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH15 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH16 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH17 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH18 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH19 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH20 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH21 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH22 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH23 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH24 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH25 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH26 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH27 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH28 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH29 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH30 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH31 \
+ \
+ \
+ \
+ ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#ifdef TESTNET
+#define PATCH32 \
+ \
+ \
+ \
+ ".testnet"
+#else
+#define PATCH32 ""
+#endif
+
+/*
+ * Deliberate empty lines
+ */
+
+/* Do NOT edit those: */
+
+#ifndef BASE_VERSION
+#define BASE_VERSION "u2.10"
+#endif
+
+#ifndef MAJOR_PROTOCOL
+#define MAJOR_PROTOCOL "10"
+#endif
--- /dev/null
+#ifndef QUERYCMDS_H
+#define QUERYCMDS_H
+
+/*=============================================================================
+ * Structs
+ */
+
+struct lusers_st {
+ /* Local connections: */
+ unsigned int unknowns; /* IsUnknown() || IsConnecting() || IsHandshake() */
+ unsigned int local_servers; /* IsServer() && MyConnect() */
+ unsigned int local_clients; /* IsUser() && MyConnect() */
+ /* Global counts: */
+ unsigned int servers; /* IsServer() || IsMe() */
+ unsigned int clients; /* IsUser() */
+ /* Global user mode changes: */
+ unsigned int inv_clients; /* IsUser() && IsInvisible() */
+ unsigned int opers; /* IsUser() && IsOper() */
+ /* Misc: */
+ unsigned int channels;
+};
+
+/*=============================================================================
+ * Macros
+ */
+
+/* Macros for remote connections: */
+#define Count_newremoteclient(nrof) (++nrof.clients)
+#define Count_newremoteserver(nrof) (++nrof.servers)
+#define Count_remoteclientquits(nrof) (--nrof.clients)
+#define Count_remoteserverquits(nrof) (--nrof.servers)
+
+/* Macros for local connections: */
+#define Count_newunknown(nrof) (++nrof.unknowns)
+#define Count_unknownbecomesclient(cptr, nrof) \
+ do { \
+ --nrof.unknowns; ++nrof.local_clients; ++nrof.clients; \
+ if (match("*" DOMAINNAME, cptr->sockhost) == 0) \
+ ++current_load.local_count; \
+ if (nrof.local_clients > max_client_count) \
+ max_client_count = nrof.local_clients; \
+ if (nrof.local_clients + nrof.local_servers > max_connection_count) \
+ { \
+ max_connection_count = nrof.local_clients + nrof.local_servers; \
+ if (max_connection_count % 10 == 0) \
+ sendto_ops("Maximum connections: %d (%d clients)", \
+ max_connection_count, max_client_count); \
+ } \
+ } while(0)
+#define Count_unknownbecomesserver(nrof) do { --nrof.unknowns; ++nrof.local_servers; ++nrof.servers; } while(0)
+#define Count_clientdisconnects(cptr, nrof) \
+ do \
+ { \
+ --nrof.local_clients; --nrof.clients; \
+ if (match("*" DOMAINNAME, cptr->sockhost) == 0) \
+ --current_load.local_count; \
+ } while(0)
+#define Count_serverdisconnects(nrof) do { --nrof.local_servers; --nrof.servers; } while(0)
+#define Count_unknowndisconnects(nrof) (--nrof.unknowns)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_version(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_info(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_links(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_help(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_admin(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_motd(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+extern struct lusers_st nrof;
+
+#endif /* QUERYCMDS_H */
--- /dev/null
+#ifndef RANDOM_H
+#define RANDOM_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern unsigned int ircrandom(void);
+
+#endif /* RANDOM_H */
--- /dev/null
+#ifndef RES_H
+#define RES_H
+
+#include <netinet/in.h>
+#include <netdb.h>
+#ifdef HPUX
+#ifndef h_errno
+extern int h_errno;
+#endif
+#endif
+#include "list.h"
+
+/*=============================================================================
+ * General defines
+ */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int init_resolver(void);
+extern time_t timeout_query_list(void);
+extern void del_queries(char *cp);
+extern void add_local_domain(char *hname, int size);
+extern struct hostent *gethost_byname(char *name, Link *lp);
+extern struct hostent *gethost_byaddr(struct in_addr *addr, Link *lp);
+extern struct hostent *get_res(char *lp);
+extern time_t expire_cache(void);
+extern void flush_cache(void);
+extern int m_dns(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern size_t cres_mem(aClient *sptr);
+
+#endif /* RES_H */
--- /dev/null
+/*
+ * runmalloc.h
+ *
+ * (C) Copyright 1996 - 1997, Carlo Wood (carlo@runaway.xs4all.nl)
+ *
+ * Headerfile of runmalloc.c
+ *
+ */
+
+#ifndef RUNMALLOC_H
+#define RUNMALLOC_H
+
+#ifdef DEBUGMALLOC
+
+#if defined(MEMMAGICNUMS) && !defined(MEMSIZESTATS)
+#define MEMSIZESTATS
+#endif
+#ifndef MEMLEAKSTATS
+#undef MEMTIMESTATS
+#endif
+
+/*=============================================================================
+ * Proto types
+ */
+
+#ifdef MEMLEAKSTATS
+extern void *RunMalloc_memleak(size_t size, int line, const char *filename);
+extern void *RunCalloc_memleak(size_t nmemb, size_t size,
+ int line, const char *filename);
+extern void *RunRealloc_memleak(void *ptr, size_t size,
+ int line, const char *filename);
+struct Client;
+extern void report_memleak_stats(struct Client *sptr, int parc, char *parv[]);
+#define RunMalloc(x) RunMalloc_memleak(x, __LINE__, __FILE__)
+#define RunCalloc(x,y) RunCalloc_memleak(x,y, __LINE__, __FILE__)
+#define RunRealloc(x,y) RunRealloc_memleak(x,y, __LINE__, __FILE__)
+#else
+extern void *RunMalloc(size_t size);
+extern void *RunCalloc(size_t nmemb, size_t size);
+extern void *RunRealloc(void *ptr, size_t size);
+#endif
+extern int RunFree_test(void *ptr);
+extern void RunFree(void *ptr);
+#ifdef MEMSIZESTATS
+extern unsigned int get_alloc_cnt(void);
+extern size_t get_mem_size(void);
+#endif
+
+#else /* !DEBUGMALLOC */
+
+#include <stdlib.h>
+
+#undef MEMSIZESTATS
+#undef MEMMAGICNUMS
+#undef MEMLEAKSTATS
+#undef MEMTIMESTATS
+
+#define Debug_malloc(x)
+#define RunMalloc(x) malloc(x)
+#define RunCalloc(x,y) calloc(x,y)
+#define RunRealloc(x,y) realloc(x,y)
+#define RunFree(x) free(x)
+
+#endif /* DEBUGMALLOC */
+
+#endif /* RUNMALLOC_H */
--- /dev/null
+#ifndef S_AUTH_H
+#define S_AUTH_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void start_auth(aClient *cptr);
+extern void send_authports(aClient *cptr);
+extern void read_authports(aClient *cptr);
+
+#endif /* S_AUTH_H */
--- /dev/null
+#ifndef S_BSD_H
+#define S_BSD_H
+
+#include <netdb.h>
+#include "s_conf.h"
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define FLAGS_PINGSENT 0x0001 /* Unreplied ping sent */
+#define FLAGS_DEADSOCKET 0x0002 /* Local socket is dead--Exiting soon */
+#define FLAGS_KILLED 0x0004 /* Prevents "QUIT" from being sent for this */
+#define FLAGS_OPER 0x0008 /* Operator */
+#define FLAGS_LOCOP 0x0010 /* Local operator -- SRB */
+#define FLAGS_INVISIBLE 0x0020 /* makes user invisible */
+#define FLAGS_WALLOP 0x0040 /* send wallops to them */
+#define FLAGS_SERVNOTICE 0x0080 /* server notices such as kill */
+#define FLAGS_BLOCKED 0x0100 /* socket is in a blocked condition */
+#define FLAGS_UNIX 0x0200 /* socket is in the unix domain, not inet */
+#define FLAGS_CLOSING 0x0400 /* set when closing to suppress errors */
+#define FLAGS_LISTEN 0x0800 /* used to mark clients which we listen() on */
+#define FLAGS_CHKACCESS 0x1000 /* ok to check clients access if set */
+#define FLAGS_DOINGDNS 0x2000 /* client is waiting for a DNS response */
+#define FLAGS_AUTH 0x4000 /* client is waiting on rfc931 response */
+#define FLAGS_WRAUTH 0x8000 /* set if we havent writen to ident server */
+#define FLAGS_LOCAL 0x00010000 /* set for local clients */
+#define FLAGS_GOTID 0x00020000 /* successful ident lookup achieved */
+#define FLAGS_DOID 0x00040000 /* I-lines say must use ident return */
+#define FLAGS_NONL 0x00080000 /* No \n in buffer */
+#define FLAGS_TS8 0x00100000 /* Why do you want to know? */
+#define FLAGS_PING 0x00200000 /* Socket needs to send udp pings */
+#define FLAGS_ASKEDPING 0x00400000 /* Client asked for udp ping */
+#define FLAGS_MAP 0x00800000 /* Show server on the map */
+#define FLAGS_JUNCTION 0x01000000 /* Junction causing the net.burst */
+#define FLAGS_DEAF 0x02000000 /* Makes user deaf */
+#define FLAGS_CHSERV 0x04000000 /* Disallow KICK or MODE -o on the user;
+ don't display channels in /whois */
+#define FLAGS_BURST 0x08000000 /* Server is receiving a net.burst */
+#define FLAGS_BURST_ACK 0x10000000 /* Server is waiting for eob ack */
+#define FLAGS_DEBUG 0x20000000 /* send global debug/anti-hack info */
+#define FLAGS_IPCHECK 0x40000000 /* Added or updated IPregistry data */
+
+#define SEND_UMODES \
+ (FLAGS_INVISIBLE|FLAGS_OPER|FLAGS_WALLOP|FLAGS_DEAF|FLAGS_CHSERV|FLAGS_DEBUG)
+#define ALL_UMODES (SEND_UMODES|FLAGS_SERVNOTICE|FLAGS_LOCOP)
+#define FLAGS_ID (FLAGS_DOID|FLAGS_GOTID)
+
+/*
+ * flags macros.
+ */
+#define IsOper(x) ((x)->flags & FLAGS_OPER)
+#define IsLocOp(x) ((x)->flags & FLAGS_LOCOP)
+#define IsInvisible(x) ((x)->flags & FLAGS_INVISIBLE)
+#define IsDeaf(x) ((x)->flags & FLAGS_DEAF)
+#define IsChannelService(x) ((x)->flags & FLAGS_CHSERV)
+#define IsAnOper(x) ((x)->flags & (FLAGS_OPER|FLAGS_LOCOP))
+#define IsPrivileged(x) (IsAnOper(x) || IsServer(x))
+#define SendWallops(x) ((x)->flags & FLAGS_WALLOP)
+#define SendDebug(x) ((x)->flags & FLAGS_DEBUG)
+#define SendServNotice(x) ((x)->flags & FLAGS_SERVNOTICE)
+#define IsUnixSocket(x) ((x)->flags & FLAGS_UNIX)
+#define IsListening(x) ((x)->flags & FLAGS_LISTEN)
+#define DoAccess(x) ((x)->flags & FLAGS_CHKACCESS)
+#define IsLocal(x) ((x)->flags & FLAGS_LOCAL)
+#define IsDead(x) ((x)->flags & FLAGS_DEADSOCKET)
+#define IsJunction(x) ((x)->flags & FLAGS_JUNCTION)
+#define IsBurst(x) ((x)->flags & FLAGS_BURST)
+#define IsBurstAck(x) ((x)->flags & FLAGS_BURST_ACK)
+#define IsBurstOrBurstAck(x) ((x)->flags & (FLAGS_BURST|FLAGS_BURST_ACK))
+#define IsIPChecked(x) ((x)->flags & FLAGS_IPCHECK)
+
+#define SetOper(x) ((x)->flags |= FLAGS_OPER)
+#define SetLocOp(x) ((x)->flags |= FLAGS_LOCOP)
+#define SetInvisible(x) ((x)->flags |= FLAGS_INVISIBLE)
+#define SetWallops(x) ((x)->flags |= FLAGS_WALLOP)
+#define SetDebug(x) ((x)->flags |= FLAGS_DEBUG)
+#define SetUnixSock(x) ((x)->flags |= FLAGS_UNIX)
+#define SetDNS(x) ((x)->flags |= FLAGS_DOINGDNS)
+#define DoingDNS(x) ((x)->flags & FLAGS_DOINGDNS)
+#define SetAccess(x) ((x)->flags |= FLAGS_CHKACCESS)
+#define DoingAuth(x) ((x)->flags & FLAGS_AUTH)
+#define NoNewLine(x) ((x)->flags & FLAGS_NONL)
+#define DoPing(x) ((x)->flags & FLAGS_PING)
+#define SetAskedPing(x) ((x)->flags |= FLAGS_ASKEDPING)
+#define AskedPing(x) ((x)->flags & FLAGS_ASKEDPING)
+#define SetJunction(x) ((x)->flags |= FLAGS_JUNCTION)
+#define SetBurst(x) ((x)->flags |= FLAGS_BURST)
+#define SetBurstAck(x) ((x)->flags |= FLAGS_BURST_ACK)
+#define SetIPChecked(x) ((x)->flags |= FLAGS_IPCHECK)
+
+#define ClearOper(x) ((x)->flags &= ~FLAGS_OPER)
+#define ClearLocOp(x) ((x)->flags &= ~FLAGS_LOCOP)
+#define ClearInvisible(x) ((x)->flags &= ~FLAGS_INVISIBLE)
+#define ClearWallops(x) ((x)->flags &= ~FLAGS_WALLOP)
+#define ClearDebug(x) ((x)->flags &= ~FLAGS_DEBUG)
+#define ClearDNS(x) ((x)->flags &= ~FLAGS_DOINGDNS)
+#define ClearAuth(x) ((x)->flags &= ~FLAGS_AUTH)
+#define ClearAccess(x) ((x)->flags &= ~FLAGS_CHKACCESS)
+#define ClearPing(x) ((x)->flags &= ~FLAGS_PING)
+#define ClearAskedPing(x) ((x)->flags &= ~FLAGS_ASKEDPING)
+#define ClearBurst(x) ((x)->flags &= ~FLAGS_BURST)
+#define ClearBurstAck(x) ((x)->flags &= ~FLAGS_BURST_ACK)
+
+/* used for async dns values */
+
+#define ASYNC_NONE 0
+#define ASYNC_CLIENT 1
+#define ASYNC_CONNECT 2
+#define ASYNC_CONF 3
+#define ASYNC_PING 4
+
+/* server notice stuff */
+
+#define SNO_ADD 1
+#define SNO_DEL 2
+#define SNO_SET 3
+ /* DON'T CHANGE THESE VALUES ! */
+ /* THE CLIENTS DEPEND ON IT ! */
+#define SNO_OLDSNO 0x1 /* unsorted old messages */
+#define SNO_SERVKILL 0x2 /* server kills (nick collisions) */
+#define SNO_OPERKILL 0x4 /* oper kills */
+#define SNO_HACK2 0x8 /* desyncs */
+#define SNO_HACK3 0x10 /* temporary desyncs */
+#define SNO_UNAUTH 0x20 /* unauthorized connections */
+#define SNO_TCPCOMMON 0x40 /* common TCP or socket errors */
+#define SNO_TOOMANY 0x80 /* too many connections */
+#define SNO_HACK4 0x100 /* Uworld actions on channels */
+#define SNO_GLINE 0x200 /* glines */
+#define SNO_NETWORK 0x400 /* net join/break, etc */
+#define SNO_IPMISMATCH 0x800 /* IP mismatches */
+#define SNO_THROTTLE 0x1000 /* host throttle add/remove notices */
+#define SNO_OLDREALOP 0x2000 /* old oper-only messages */
+#define SNO_CONNEXIT 0x4000 /* client connect/exit (ugh) */
+
+#define SNO_ALL 0x7fff /* Don't make it larger then significant,
+ * that looks nicer */
+
+#define SNO_USER (SNO_ALL & ~SNO_OPER)
+
+#define SNO_DEFAULT (SNO_NETWORK|SNO_OPERKILL|SNO_GLINE)
+#define SNO_OPERDEFAULT (SNO_DEFAULT|SNO_HACK2|SNO_HACK4|SNO_THROTTLE|SNO_OLDSNO)
+#define SNO_OPER (SNO_CONNEXIT|SNO_OLDREALOP)
+#define SNO_NOISY (SNO_SERVKILL|SNO_UNAUTH)
+
+/*
+ * simple defines to differentiate between a tty and socket for
+ * add_connection() -Simon
+ */
+
+#define ADCON_TTY 0
+#define ADCON_SOCKET 1
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int setsnomask(aClient *cptr, snomask_t newmask, int what);
+extern snomask_t umode_make_snomask(snomask_t oldmask, char *arg, int what);
+extern int connect_server(aConfItem *aconf, aClient *by, struct hostent *hp);
+extern void report_error(char *text, aClient *cptr);
+extern int inetport(aClient *cptr, char *name, unsigned short int port);
+extern int add_listener(aConfItem *aconf);
+extern void close_listeners(void);
+extern void init_sys(void);
+extern void write_pidfile(void);
+extern enum AuthorizationCheckResult check_client(aClient *cptr);
+extern int check_server(aClient *cptr);
+extern void close_connection(aClient *cptr);
+extern int get_sockerr(aClient *cptr);
+extern void set_non_blocking(int fd, aClient *cptr);
+extern aClient *add_connection(aClient *cptr, int fd, int type);
+extern int read_message(time_t delay);
+extern void get_my_name(aClient *cptr, char *name, size_t len);
+extern int setup_ping(void);
+
+extern int highest_fd, resfd;
+extern unsigned int readcalls;
+extern aClient *loc_clients[MAXCONNECTIONS];
+#ifdef VIRTUAL_HOST
+extern struct sockaddr_in vserv;
+#endif
+
+#endif /* S_BSD_H */
--- /dev/null
+#ifndef S_CONF_H
+#define S_CONF_H
+
+#include "list.h"
+#include <netinet/in.h>
+#include <netdb.h>
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define CONF_ILLEGAL 0x80000000
+#define CONF_MATCH 0x40000000
+#define CONF_CLIENT 0x0002
+#define CONF_CONNECT_SERVER 0x0004
+#define CONF_NOCONNECT_SERVER 0x0008
+#define CONF_LOCOP 0x0010
+#define CONF_OPERATOR 0x0020
+#define CONF_ME 0x0040
+#define CONF_KILL 0x0080
+#define CONF_ADMIN 0x0100
+#ifdef R_LINES
+#define CONF_RESTRICT 0x0200
+#endif
+#define CONF_CLASS 0x0400
+#define CONF_LEAF 0x1000
+#define CONF_LISTEN_PORT 0x2000
+#define CONF_HUB 0x4000
+#define CONF_UWORLD 0x8000
+#define CONF_CRULEALL 0x00200000
+#define CONF_CRULEAUTO 0x00400000
+#define CONF_TLINES 0x00800000
+#define CONF_IPKILL 0x00010000
+
+#define CONF_OPS (CONF_OPERATOR | CONF_LOCOP)
+#define CONF_SERVER_MASK (CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER)
+#define CONF_CLIENT_MASK (CONF_CLIENT | CONF_OPS | CONF_SERVER_MASK)
+#define CONF_CRULE (CONF_CRULEALL | CONF_CRULEAUTO)
+#define CONF_KLINE (CONF_KILL | CONF_IPKILL)
+
+#define IsIllegal(x) ((x)->status & CONF_ILLEGAL)
+
+/*=============================================================================
+ * Structures
+ */
+
+struct ConfItem {
+ unsigned int status; /* If CONF_ILLEGAL, delete when no clients */
+ unsigned int clients; /* Number of *LOCAL* clients using this */
+ struct in_addr ipnum; /* ip number of host field */
+ char *host;
+ char *passwd;
+ char *name;
+ unsigned short int port;
+ time_t hold; /* Hold action until this time
+ (calendar time) */
+#ifndef VMSP
+ struct ConfClass *confClass; /* Class of connection */
+#endif
+ struct ConfItem *next;
+};
+
+struct MotdItem {
+ char line[82];
+ struct MotdItem *next;
+};
+
+struct trecord {
+ char *hostmask;
+ struct MotdItem *tmotd;
+ struct tm tmotd_tm;
+ struct trecord *next;
+};
+
+enum AuthorizationCheckResult {
+ ACR_OK,
+ ACR_NO_AUTHORIZATION,
+ ACR_TOO_MANY_IN_CLASS,
+ ACR_TOO_MANY_FROM_IP,
+ ACR_ALREADY_AUTHORIZED,
+ ACR_BAD_SOCKET
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern aConfItem *find_conf_host(Link *lp, char *host, int statmask);
+extern void det_confs_butmask(aClient *cptr, int mask);
+extern enum AuthorizationCheckResult attach_Iline(aClient *cptr,
+ struct hostent *hp, char *sockhost);
+extern aConfItem *count_cnlines(Link *lp);
+extern int detach_conf(aClient *cptr, aConfItem *aconf);
+extern enum AuthorizationCheckResult attach_conf(aClient *cptr,
+ aConfItem *aconf);
+extern aConfItem *find_admin(void);
+extern aConfItem *find_me(void);
+extern aConfItem *attach_confs(aClient *cptr, const char *name, int statmask);
+extern aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask);
+extern aConfItem *find_conf_exact(char *name, char *user, char *host,
+ int statmask);
+extern aConfItem *find_conf_name(char *name, int statmask);
+extern aConfItem *find_conf(Link *lp, const char *name, int statmask);
+extern aConfItem *find_conf_ip(Link *lp, char *ip, char *user, int statmask);
+extern int rehash(aClient *cptr, int sig);
+extern int initconf(int opt);
+extern void read_tlines(void);
+extern int find_kill(aClient *cptr);
+extern int find_restrict(aClient *cptr);
+extern int m_killcomment(aClient *sptr, char *parv, char *filename);
+extern aMotdItem *read_motd(char *motdfile);
+
+extern aConfItem *conf;
+extern aGline *gline;
+extern struct tm motd_tm;
+extern aMotdItem *motd;
+extern aMotdItem *rmotd;
+extern atrecord *tdata;
+
+#endif /* S_CONF_H */
--- /dev/null
+#ifndef S_DEBUG_H
+#define S_DEBUG_H
+
+#include <stdarg.h>
+#ifdef MSGLOG_ENABLED
+#include "struct.h" /* Needed for HOSTLEN */
+#endif
+
+#ifdef DEBUGMODE
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define Debug(x) debug x
+#define LOGFILE LPATH
+
+/*
+ * defined debugging levels
+ */
+#define DEBUG_FATAL 0
+#define DEBUG_ERROR 1 /* report_error() and other
+ errors that are found */
+#define DEBUG_NOTICE 3
+#define DEBUG_DNS 4 /* used by all DNS related routines - a *lot* */
+#define DEBUG_INFO 5 /* general usful info */
+#define DEBUG_NUM 6 /* numerics */
+#define DEBUG_SEND 7 /* everything that is sent out */
+#define DEBUG_DEBUG 8 /* anything to do with debugging,
+ ie unimportant :) */
+#define DEBUG_MALLOC 9 /* malloc/free calls */
+#define DEBUG_LIST 10 /* debug list use */
+
+/*=============================================================================
+ * proto types
+ */
+
+extern void vdebug(int level, const char *form, va_list vl);
+extern void debug(int level, const char *form, ...)
+ __attribute__ ((format(printf, 2, 3)));
+extern void send_usage(aClient *cptr, char *nick);
+
+#else /* !DEBUGMODE */
+
+#define Debug(x)
+#define LOGFILE "/dev/null"
+
+#endif /* !DEBUGMODE */
+
+extern void count_memory(aClient *cptr, char *nick);
+extern char serveropts[];
+
+/*=============================================================================
+ * Message logging service
+ */
+
+/*
+ * Message levels: these are inclusive, i.e. a message that is LEVEL_MAP
+ * affects also clients and channels and is propagated and needs a query of
+ * some status, and therefore belongs to all the classes, in the same way
+ * _every_ message is parsed so belongs to LEVEL_PARSED
+ */
+
+/* Messages that affect servers' map */
+#define LEVEL_MAP 6
+
+/* Messages that affect clients existance */
+#define LEVEL_CLIENT 5
+
+/* Messages that affect channel existance */
+#define LEVEL_CHANNEL 4
+
+/* Messages that affect channel modes */
+#define LEVEL_MODE 3
+
+/* Messages that are only to be propagated somewhere */
+#define LEVEL_PROPAGATE 2
+
+/*
+ * Messages that only perform queries
+ * note how every message may need some status query over data structs
+ * and at the same time every query might need to be propagated
+ * somewhere... so the distinction between levels PROPAGATE and
+ * QUERY is quite fuzzy
+ */
+#define LEVEL_QUERY 1
+
+/* Messages that only perform queries */
+#define LEVEL_PARSED 0
+
+#ifdef MSGLOG_ENABLED
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define LogMessage(x) Log_Message x
+#define StoreBuffer(x) Store_Buffer x
+
+/* Logging mask, selection on type of connection */
+#define LOG_PING (0x8000 >> (8 + STAT_PING))
+#define LOG_LOG (0x8000 >> (8 + STAT_LOG))
+#define LOG_MASTER (0x8000 >> (8 + STAT_MASTER))
+#define LOG_CONNECTING (0x8000 >> (8 + STAT_CONNECTING))
+#define LOG_HANDSHAKE (0x8000 >> (8 + STAT_HANDSHAKE))
+#define LOG_ME (0x8000 >> (8 + STAT_ME))
+#define LOG_UNKNOWN (0x8000 >> (8 + STAT_UNKNOWN))
+#define LOG_SERVER (0x8000 >> (8 + STAT_SERVER))
+#define LOG_CLIENT (0x8000 >> (8 + STAT_USER))
+
+/*
+ * Define here the type of connection(s) that will be monitored.
+ * Default is to log messages coming from any connection.
+ */
+#define LOG_MASK_TYPE \
+ ( LOG_PING | LOG_LOG | LOG_MASTER | LOG_CONNECTING | \
+ LOG_HANDSHAKE | LOG_ME | LOG_UNKNOWN | LOG_SERVER | LOG_CLIENT )
+
+/*=============================================================================
+ * data structures
+ */
+
+struct log_entry {
+ int cptr_status;
+ char cptr_name[HOSTLEN + 1];
+ char cptr_yxx[3];
+ int cptr_fd;
+
+ int sptr_status;
+ char sptr_name[HOSTLEN + 1];
+ char sptr_yxx[4];
+ char sptr_from_name[HOSTLEN + 1];
+
+ char buffer[512];
+
+ /* The following may be lost before log gets used,
+ anyhow they are only here for usage through gdb */
+
+ aClient *cptr;
+ aClient *sptr;
+};
+
+/*=============================================================================
+ * proto types
+ */
+
+extern void Log_Message(aClient *sptr, int msgclass);
+extern void Store_Buffer(char *buf, aClient *cptr);
+
+#else /* !MSGLOG_ENABLED */
+
+#define LogMessage(x)
+#define StoreBuffer(x)
+
+#endif /* !MSGLOG_ENABLED */
+
+#endif /* S_DEBUG_H */
--- /dev/null
+#ifndef S_ERR_H
+#define S_ERR_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern char *err_str(int numeric);
+extern char *rpl_str(int numeric);
+
+#endif /* S_ERR_H */
--- /dev/null
+#ifndef S_MISC_H
+#define S_MISC_H
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define CPTR_KILLED -2
+
+/*=============================================================================
+ * Structures
+ */
+
+struct stats {
+ unsigned int is_cl; /* number of client connections */
+ unsigned int is_sv; /* number of server connections */
+ unsigned int is_ni; /* connection but no idea who it was */
+ unsigned short int is_cbs; /* bytes sent to clients */
+ unsigned short int is_cbr; /* bytes received to clients */
+ unsigned short int is_sbs; /* bytes sent to servers */
+ unsigned short int is_sbr; /* bytes received to servers */
+ unsigned int is_cks; /* k-bytes sent to clients */
+ unsigned int is_ckr; /* k-bytes received to clients */
+ unsigned int is_sks; /* k-bytes sent to servers */
+ unsigned int is_skr; /* k-bytes received to servers */
+ time_t is_cti; /* time spent connected by clients */
+ time_t is_sti; /* time spent connected by servers */
+ unsigned int is_ac; /* connections accepted */
+ unsigned int is_ref; /* accepts refused */
+ unsigned int is_unco; /* unknown commands */
+ unsigned int is_wrdi; /* command going in wrong direction */
+ unsigned int is_unpf; /* unknown prefix */
+ unsigned int is_empt; /* empty message */
+ unsigned int is_num; /* numeric message */
+ unsigned int is_kill; /* number of kills generated on collisions */
+ unsigned int is_fake; /* MODE 'fakes' */
+ unsigned int is_asuc; /* successful auth requests */
+ unsigned int is_abad; /* bad auth requests */
+ unsigned int is_udp; /* packets recv'd on udp port */
+ unsigned int is_loc; /* local connections made */
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int check_registered(aClient *sptr);
+extern int check_registered_user(aClient *sptr);
+extern int exit_client(aClient *cptr, aClient *bcptr,
+ aClient *sptr, char *comment);
+extern char *myctime(time_t value);
+extern char *get_client_name(aClient *sptr, int showip);
+extern int exit_client_msg(aClient *cptr, aClient *bcptr,
+ aClient *sptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void initstats(void);
+extern char *date(time_t clock);
+extern char *get_client_host(aClient *cptr);
+extern void get_sockhost(aClient *cptr, char *host);
+extern char *my_name_for_link(char *name, aConfItem *aconf);
+extern int vexit_client_msg(aClient *cptr, aClient *bcptr,
+ aClient *sptr, char *pattern, va_list vl);
+extern void checklist(void);
+extern void tstats(aClient *cptr, char *name);
+
+extern struct stats *ircstp;
+
+#endif /* S_MISC_H */
--- /dev/null
+#ifndef S_NUMERIC_H
+#define S_NUMERIC_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int do_numeric(int numeric, int nnn, aClient *cptr, aClient *sptr,
+ int parc, char *parv[]);
+
+#endif /* S_NUMERIC_H */
--- /dev/null
+#ifndef S_PING_H
+#define S_PING_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int start_ping(aClient *cptr);
+extern void send_ping(aClient *cptr);
+extern void read_ping(aClient *cptr);
+extern int ping_server(aClient *cptr);
+extern int m_uping(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void end_ping(aClient *cptr);
+extern void cancel_ping(aClient *sptr, aClient *acptr);
+
+#endif /* S_PING_H */
--- /dev/null
+#ifndef S_SERV_H
+#define S_SERV_H
+
+#include "struct.h"
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define STAT_PING 0
+#define STAT_LOG 1 /* logfile for -x */
+#define STAT_MASTER 2 /* Local ircd master before identification */
+#define STAT_CONNECTING 3
+#define STAT_HANDSHAKE 4
+#define STAT_ME 5
+#define STAT_UNKNOWN 6
+#define STAT_UNKNOWN_USER 7 /* Connect to client port */
+#define STAT_UNKNOWN_SERVER 8 /* Connect to server port */
+#define STAT_SERVER 9
+#define STAT_USER 10
+
+/*
+ * for when you wanna create a bitmask of status values
+ */
+#define StatusMask(T) (1<<(T))
+#define IsStatMask(x, s) (StatusMask((x)->status) & (s))
+
+/*
+ * status macros.
+ */
+#define IsRegistered(x) (IsStatMask(x, \
+ StatusMask(STAT_SERVER)|\
+ StatusMask(STAT_USER)))
+#define IsConnecting(x) ((x)->status == STAT_CONNECTING)
+#define IsHandshake(x) ((x)->status == STAT_HANDSHAKE)
+#define IsMe(x) ((x)->status == STAT_ME)
+#define IsUnknown(x) (IsStatMask(x, \
+ StatusMask(STAT_UNKNOWN)|\
+ StatusMask(STAT_UNKNOWN_USER)|\
+ StatusMask(STAT_UNKNOWN_SERVER)|\
+ StatusMask(STAT_MASTER)))
+#define IsServerPort(x) ((x)->status == STAT_UNKNOWN_SERVER )
+#define IsUserPort(x) ((x)->status == STAT_UNKNOWN_USER )
+#define IsMaster(x) ((x)->status == STAT_MASTER)
+#define IsClient(x) (IsStatMask(x, \
+ StatusMask(STAT_MASTER)|\
+ StatusMask(STAT_HANDSHAKE)|\
+ StatusMask(STAT_ME)|\
+ StatusMask(STAT_UNKNOWN)|\
+ StatusMask(STAT_UNKNOWN_USER)|\
+ StatusMask(STAT_UNKNOWN_SERVER)|\
+ StatusMask(STAT_SERVER)|\
+ StatusMask(STAT_USER)))
+#define IsTrusted(x) (IsStatMask(x, \
+ StatusMask(STAT_PING)|\
+ StatusMask(STAT_LOG)|\
+ StatusMask(STAT_CONNECTING)|\
+ StatusMask(STAT_HANDSHAKE)|\
+ StatusMask(STAT_ME)|\
+ StatusMask(STAT_SERVER)))
+#ifdef DEBUGMODE /* Coredump if we miss something... */
+#define IsServer(x) ( ((x)->status == STAT_SERVER) && \
+ (((x)->serv) ? 1 : (*((char *) NULL) = 0)) )
+#define IsUser(x) ( ((x)->status == STAT_USER) && \
+ (((x)->user) ? 1 : (*((char *) NULL) = 0)) )
+#else
+#define IsServer(x) ((x)->status == STAT_SERVER)
+#define IsUser(x) ((x)->status == STAT_USER)
+#endif
+#define IsLog(x) ((x)->status == STAT_LOG)
+#define IsPing(x) ((x)->status == STAT_PING)
+
+#define SetMaster(x) ((x)->status = STAT_MASTER)
+#define SetConnecting(x) ((x)->status = STAT_CONNECTING)
+#define SetHandshake(x) ((x)->status = STAT_HANDSHAKE)
+#define SetServer(x) ((x)->status = STAT_SERVER)
+#define SetMe(x) ((x)->status = STAT_ME)
+#define SetUser(x) ((x)->status = STAT_USER)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_server_estab(aClient *cptr, aConfItem *aconf, aConfItem *bconf);
+extern int m_error(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_end_of_burst(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_end_of_burst_ack(aClient *cptr, aClient *sptr,
+ int parc, char *parv[]);
+extern int m_desynch(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+extern unsigned int max_connection_count, max_client_count;
+
+#endif /* S_SERV_H */
--- /dev/null
+#ifndef S_USER_H
+#define S_USER_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+/*
+ * Nick flood limit
+ * Minimum time between nick changes.
+ * (The first two changes are allowed quickly after another however).
+ */
+#define NICK_DELAY 30
+
+/*
+ * Target flood time.
+ * Minimum time between target changes.
+ * (MAXTARGETS are allowed simultaneously however).
+ * Its set to a power of 2 because we devide through it quite a lot.
+ */
+#define TARGET_DELAY 128
+
+/* return values for hunt_server() */
+
+#define HUNTED_NOSUCH (-1) /* if the hunted server is not found */
+#define HUNTED_ISME 0 /* if this server should execute the command */
+#define HUNTED_PASS 1 /* if message passed onwards successfully */
+
+/* used when sending to #mask or $mask */
+
+#define MATCH_SERVER 1
+#define MATCH_HOST 2
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_umode(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int is_silenced(aClient *sptr, aClient *acptr);
+extern int hunt_server(int, aClient *cptr, aClient *sptr,
+ char *command, int server, int parc, char *parv[]);
+extern aClient *next_client(aClient *next, char *ch);
+extern int m_nick(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_private(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_notice(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_wallchops(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_cprivmsg(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_cnotice(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_user(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_quit(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_away(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_ping(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_pong(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_oper(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_pass(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_userhost(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_userip(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_ison(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern char *umode_str(aClient *cptr);
+extern void send_umode(aClient *cptr, aClient *sptr, int old, int sendmask);
+extern int del_silence(aClient *sptr, char *mask);
+extern int m_silence(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void set_snomask(aClient *, snomask_t, int);
+extern int is_snomask(char *);
+extern int check_target_limit(aClient *sptr, void *target, const char *name,
+ int created);
+extern void add_target(aClient *sptr, void *target);
+
+extern struct SLink *opsarray[];
+
+#endif /* S_USER_H */
--- /dev/null
+#ifndef SEND_H
+#define SEND_H
+
+/*=============================================================================
+ * Macros
+ */
+
+#define LastDeadComment(cptr) ((cptr)->info)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void sendto_one(aClient *to, char *pattern, ...)
+ __attribute__ ((format(printf, 2, 3)));
+extern void sendbufto_one(aClient *to);
+extern void sendto_ops(const char *pattern, ...)
+ __attribute__ ((format(printf, 1, 2)));
+extern void sendto_channel_butserv(aChannel *chptr, aClient *from,
+ char *pattern, ...) __attribute__ ((format(printf, 3, 4)));
+extern void sendto_serv_butone(aClient *one, char *pattern, ...)
+ __attribute__ ((format(printf, 2, 3)));
+extern void sendto_match_servs(aChannel *chptr, aClient *from,
+ char *format, ...) __attribute__ ((format(printf, 3, 4)));
+extern void sendto_lowprot_butone(aClient *cptr, int p, char *pattern, ...)
+ __attribute__ ((format(printf, 3, 4)));
+extern void sendto_highprot_butone(aClient *cptr, int p, char *pattern, ...)
+ __attribute__ ((format(printf, 3, 4)));
+extern void sendto_prefix_one(Reg1 aClient *to, Reg2 aClient *from,
+ char *pattern, ...) __attribute__ ((format(printf, 3, 4)));
+extern void flush_connections(int fd);
+extern void send_queued(aClient *to);
+extern void vsendto_one(aClient *to, char *pattern, va_list vl);
+extern void sendto_channel_butone(aClient *one, aClient *from,
+ aChannel *chptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void sendto_lchanops_butone(aClient *one, aClient *from,
+ aChannel *chptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void sendto_chanopsserv_butone(aClient *one, aClient *from,
+ aChannel *chptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void sendto_common_channels(aClient *user, char *pattern, ...)
+ __attribute__ ((format(printf, 2, 3)));
+extern void sendto_match_butone(aClient *one, aClient *from, char *mask,
+ int what, char *pattern, ...) __attribute__ ((format(printf, 5, 6)));
+extern void sendto_lops_butone(aClient *one, char *pattern, ...)
+ __attribute__ ((format(printf, 2, 3)));
+extern void vsendto_ops(const char *pattern, va_list vl);
+extern void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...)
+ __attribute__ ((format(printf, 3, 4)));
+extern void sendto_g_serv_butone(aClient *one, char *pattern, ...)
+ __attribute__ ((format(printf, 2, 3)));
+extern void sendto_realops(const char *pattern, ...)
+ __attribute__ ((format(printf, 1, 2)));
+extern void vsendto_op_mask(register snomask_t mask,
+ const char *pattern, va_list vl);
+extern void sendto_op_mask(snomask_t mask, const char *pattern, ...)
+ __attribute__ ((format(printf, 2, 3)));
+extern void sendbufto_op_mask(snomask_t mask);
+extern void sendbufto_serv_butone(aClient *one);
+
+extern char sendbuf[2048];
+
+#endif /* SEND_H */
--- /dev/null
+#ifndef SPRINTF_IRC
+#define SPRINTF_IRC
+
+#include <stdarg.h>
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern char *vsprintf_irc(register char *str, register const char *format,
+ register va_list);
+extern char *sprintf_irc(register char *str, register const char *format, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+extern const char atoi_tab[4000];
+
+#endif /* SPRINTF_IRC */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/struct.h
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ * Copyright (C) 1996 -1997 Carlo Wood
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef STRUCT_H
+#define STRUCT_H
+
+#include <netinet/in.h> /* Needed for struct in_addr */
+#include "whowas.h" /* Needed for whowas struct */
+
+#ifndef INCLUDED_dbuf_h
+#include "dbuf.h"
+#endif
+
+
+/*=============================================================================
+ * General defines
+ */
+
+#define NICKLEN 9
+#define USERLEN 10
+#define HOSTLEN 63
+#define REALLEN 50
+#define PASSWDLEN 20
+#define BUFSIZE 512 /* WARNING: *DONT* CHANGE THIS!!!! */
+#define MAXTARGETS 20
+#define STARTTARGETS 10
+#define RESERVEDTARGETS 12
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define CLIENT_LOCAL_SIZE sizeof(aClient)
+#define CLIENT_REMOTE_SIZE offsetof(aClient, count)
+
+#define MyConnect(x) ((x)->from == (x))
+#define MyUser(x) (MyConnect(x) && IsUser(x))
+#define MyOper(x) (MyConnect(x) && IsOper(x))
+#define Protocol(x) ((x)->serv->prot)
+
+/*=============================================================================
+ * Structures
+ *
+ * Only put structures here that are being used in a very large number of
+ * source files. Other structures go in the header file of there corresponding
+ * source file, or in the source file itself (when only used in that file).
+ */
+
+struct Client {
+ struct Client *next, *prev, *hnext;
+ struct User *user; /* ...defined, if this is a User */
+ struct Server *serv; /* ...defined, if this is a server */
+ struct Whowas *whowas; /* Pointer to ww struct to be freed on quit */
+ char yxx[4]; /* Numeric Nick: YMM if this is a server,
+ XX0 if this is a user */
+ time_t lasttime; /* ...should be only LOCAL clients? --msa */
+ time_t firsttime; /* time client was created */
+ time_t since; /* last time we parsed something */
+ time_t lastnick; /* TimeStamp on nick */
+ int marker; /* /who processing marker */
+ unsigned int flags; /* client flags */
+ struct Client *from; /* == self, if Local Client, *NEVER* NULL! */
+ int fd; /* >= 0, for local clients */
+ unsigned int hopcount; /* number of servers to this 0 = local */
+ short status; /* Client type */
+ struct in_addr ip; /* Real ip# - NOT defined for remote servers! */
+ char name[HOSTLEN + 1]; /* Unique name of the client, nick or host */
+ char username[USERLEN + 1]; /* username here now for auth stuff */
+ char info[REALLEN + 1]; /* Free form additional client information */
+ /*
+ * The following fields are allocated only for local clients
+ * (directly connected to *this* server with a socket.
+ * The first of them *MUST* be the "count"--it is the field
+ * to which the allocation is tied to! *Never* refer to
+ * these fields, if (from != self).
+ */
+ unsigned int count; /* Amount of data in buffer, DON'T PUT
+ variables ABOVE this one! */
+ snomask_t snomask; /* mask for server messages */
+ char buffer[BUFSIZE]; /* Incoming message buffer; or the error that
+ caused this clients socket to be `dead' */
+ unsigned short int lastsq; /* # of 2k blocks when sendqueued called last */
+ time_t nextnick; /* Next time that a nick change is allowed */
+ time_t nexttarget; /* Next time that a target change is allowed */
+ unsigned char targets[MAXTARGETS]; /* Hash values of current targets */
+ unsigned int cookie; /* Random number the user must PONG */
+ struct DBuf sendQ; /* Outgoing message queue--if socket full */
+ struct DBuf recvQ; /* Hold for data incoming yet to be parsed */
+ unsigned int sendM; /* Statistics: protocol messages send */
+ unsigned int sendK; /* Statistics: total k-bytes send */
+ unsigned int receiveM; /* Statistics: protocol messages received */
+ unsigned int receiveK; /* Statistics: total k-bytes received */
+ unsigned short int sendB; /* counters to count upto 1-k lots of bytes */
+ unsigned short int receiveB; /* sent and received. */
+ struct Client *acpt; /* listening client which we accepted from */
+ struct SLink *confs; /* Configuration record associated */
+ int authfd; /* fd for rfc931 authentication */
+ unsigned short int port; /* and the remote port# too :-) */
+ struct hostent *hostp;
+ struct ListingArgs *listing;
+#ifdef pyr
+ struct timeval lw;
+#endif
+ char sockhost[HOSTLEN + 1]; /* This is the host name from the socket and
+ after which the connection was accepted. */
+ char passwd[PASSWDLEN + 1];
+};
+
+struct Server {
+ struct Server *nexts;
+ struct Client *up; /* Server one closer to me */
+ struct DSlink *down; /* List with downlink servers */
+ struct DSlink *updown; /* own Dlink in up->serv->down struct */
+ aClient **client_list; /* List with client pointers on this server */
+ struct User *user; /* who activated this connection */
+ struct ConfItem *nline; /* N-line pointer for this server */
+ time_t timestamp; /* Remotely determined connect try time */
+ time_t ghost; /* Local time at which a new server
+ caused a Ghost */
+ unsigned short prot; /* Major protocol */
+ unsigned short nn_last; /* Last numeric nick for p9 servers only */
+ unsigned int nn_mask; /* [Remote] FD_SETSIZE - 1 */
+ char nn_capacity[4]; /* numeric representation of server capacity */
+#ifdef LIST_DEBUG
+ struct Client *bcptr;
+#endif
+ char *last_error_msg; /* Allocated memory with last message receive with an ERROR */
+ char by[NICKLEN + 1];
+};
+
+struct User {
+ struct User *nextu;
+ struct Client *server; /* client structure of server */
+ struct SLink *channel; /* chain of channel pointer blocks */
+ struct SLink *invited; /* chain of invite pointer blocks */
+ struct SLink *silence; /* chain of silence pointer blocks */
+ char *away; /* pointer to away message */
+ time_t last;
+ unsigned int refcnt; /* Number of times this block is referenced */
+ unsigned int joined; /* number of channels joined */
+ char username[USERLEN + 1];
+ char host[HOSTLEN + 1];
+#ifdef LIST_DEBUG
+ struct Client *bcptr;
+#endif
+};
+
+#endif /* STRUCT_H */
--- /dev/null
+#ifndef SUPPORT_H
+#define SUPPORT_H
+
+#include <netinet/in.h>
+
+/*=============================================================================
+ * Proto types
+ */
+
+#ifndef HAVE_STRTOKEN
+extern char *strtoken(char **save, char *str, char *fs);
+#endif
+#ifndef HAVE_STRERROR
+extern char *strerror(int err_no);
+#endif
+extern void dumpcore(const char *pattern, ...)
+ __attribute__ ((format(printf, 1, 2)));
+extern char *inetntoa(struct in_addr in);
+extern int check_if_ipmask(const char *mask);
+extern void write_log(const char *filename, const char *pattern, ...);
+
+#endif /* SUPPORT_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/sys.h
+ * Copyright (C) 1990 University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __sys_include__
+#define __sys_include__
+
+#include "../config/config.h"
+#include "../config/setup.h"
+
+#ifdef __osf__
+#define _OSF_SOURCE
+#endif
+
+#ifdef __sun__
+#ifdef __svr4__
+#define SOL2
+#else
+#define SUNOS4
+#endif
+#endif
+
+#if WORDS_BIGENDIAN
+# define BIT_ZERO_ON_LEFT
+#else
+# define BIT_ZERO_ON_RIGHT
+#endif
+
+#ifdef _SEQUENT_ /* Dynix 1.4 or 2.0 Generic Define.. */
+#undef BSD
+#define SYSV /* Also #define SYSV */
+#endif
+
+#ifdef __hpux
+#define HPUX
+#endif
+
+#ifdef sgi
+#define SGI
+#endif
+
+#if defined(mips)
+#undef SYSV
+#undef BSD
+#define BSD 1 /* mips only works in bsd43 environment */
+#endif
+
+#ifdef BSD_RELIABLE_SIGNALS
+#if defined(SYSV_UNRELIABLE_SIGNALS) || defined(POSIX_SIGNALS)
+#error You stuffed up config.h signals #defines use only one.
+#endif
+#define HAVE_RELIABLE_SIGNALS
+#endif
+
+#ifdef SYSV_UNRELIABLE_SIGNALS
+#ifdef POSIX_SIGNALS
+#error You stuffed up config.h signals #defines use only one.
+#endif
+#undef HAVE_RELIABLE_SIGNALS
+#endif
+
+#ifdef POSIX_SIGNALS
+#define HAVE_RELIABLE_SIGNALS
+#endif
+
+/*
+ * safety margin so we can always have one spare fd, for motd/authd or
+ * whatever else. -24 allows "safety" margin of 10 listen ports, 8 servers
+ * and space reserved for logfiles, DNS sockets and identd sockets etc.
+ */
+#define MAXCLIENTS (MAXCONNECTIONS-24)
+
+#ifdef HAVECURSES
+#define DOCURSES
+#else
+#undef DOCURSES
+#endif
+
+#ifdef HAVETERMCAP
+#define DOTERMCAP
+#else
+#undef DOTERMCAP
+#endif
+
+#ifndef UNIXPORT
+#undef UNIXPORTPATH
+#endif
+
+#if defined(CLIENT_FLOOD)
+#if (CLIENT_FLOOD > 8000) || (CLIENT_FLOOD < 512)
+#error CLIENT_FLOOD needs redefining.
+#endif
+#else
+#error CLIENT_FLOOD undefined
+#endif
+
+#ifndef CONFIG_SETUGID
+#undef IRC_UID
+#undef IRC_GID
+#endif
+
+#define Reg1 register
+#define Reg2 register
+#define Reg3 register
+#define Reg4 register
+#define Reg5 register
+#define Reg6 register
+#define Reg7 register
+#define Reg8 register
+#define Reg9 register
+#define Reg10 register
+
+/* Define FD_SETSIZE to what we want before including sys/types.h on BSD */
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__bsdi__)
+#if ((!defined(USE_POLL)) && (!defined(FD_SETSIZE)))
+#define FD_SETSIZE ((MAXCONNECTIONS)+4)
+#endif
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef __osf__
+#undef _OSF_SOURCE
+/* Buggy header */
+#include <netdb.h>
+#define _OSF_SOURCE
+#endif
+
+#if HAVE_ERRNO_H
+# include <errno.h>
+#else
+# if HAVE_NET_ERRNO_H
+# include <net/errno.h>
+# endif
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && \
+ !defined(__bsdi__) && !defined(__alpha) && !defined(__GLIBC__)
+extern char *sys_errlist[];
+#endif
+
+/* See AC_HEADER_STDC in 'info autoconf' */
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if HAVE_STRING_H
+# include <string.h>
+# endif
+# ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr(), *strrchr(), *strtok();
+# if HAVE_MEMORY_H /* See AC_MEMORY_H in 'info autoconf' */
+# include <memory.h>
+# endif
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memset(a, b, c) bzero(a, c) /* We ONLY use memset(x, 0, y) */
+# else
+# if NEED_BZERO /* This is not used yet - needs to be added to `configure' */
+# define bzero(a, c) memset((a), 0, (c)) /* Some use it in FD_ZERO */
+# endif
+# endif
+# ifndef HAVE_MEMMOVE
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#if defined(_AIX) || (defined(__STRICT_ANSI__) && __GLIBC__ >= 2)
+#include <sys/select.h>
+#endif
+
+/* See AC_HEADER_TIME in 'info autoconf' */
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef SOL2
+#define OPT_TYPE char /* opt type for get/setsockopt */
+#else
+#define OPT_TYPE void
+#endif
+
+#ifdef SUNOS4
+#define LIMIT_FMT "%d"
+#else
+#if (defined(__bsdi__) || defined(__NetBSD__))
+#define LIMIT_FMT "%qd"
+#else
+#define LIMIT_FMT "%ld"
+#endif
+#endif
+
+/* Different name on NetBSD and FreeBSD --Skip */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__bsdi__)
+#define dn_skipname __dn_skipname
+#endif
+
+#if defined(DEBUGMODE) && !defined(DEBUGMALLOC)
+#define DEBUGMALLOC
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#else /* !STDC_HEADERS */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_MALLOC_H
+#include <sys/malloc.h>
+#endif /* HAVE_SYS_MALLOC_H */
+#endif /* HAVE_MALLOC_H */
+#endif /* !STDC_HEADERS */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#include "runmalloc.h"
+
+#define MyCoreDump *((int *)NULL)=0
+
+/* This isn't really POSIX :(, but we really need it -- can this be replaced ? */
+#if defined(__STRICT_ANSI__) && !defined(_AIX)
+extern int gettimeofday(struct timeval *tv, struct timezone *tz);
+#endif
+
+/*
+ * The following part is donated by Carlo Wood from his package 'libr':
+ * (C) Copyright 1996 by Carlo Wood. All rights reserved.
+ */
+
+/* GNU CC improvements: We can only use this if we have a gcc/g++ compiler */
+#ifdef __GNUC__
+
+#if (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+#define NO_ATTRIBUTE
+#endif
+
+#else /* !__GNUC__ */
+
+/* No attributes if we don't have gcc-2.7 or higher */
+#define NO_ATTRIBUTE
+
+#endif /* !__GNUC__ */
+
+#ifdef __cplusplus
+#define HANDLER_ARG(x) x
+#define UNUSED(x)
+#else
+#define HANDLER_ARG(x)
+#ifdef NO_ATTRIBUTE
+#define __attribute__(x)
+#define UNUSED(x) unused_##x
+#else
+#define UNUSED(x) x __attribute__ ((unused))
+#endif
+#endif
+
+#ifdef NO_ATTRIBUTE
+#define RCSTAG_CC(string) static char unused_rcs_ident[] = string
+#else
+#define RCSTAG_CC(string) static char rcs_ident[] __attribute__ ((unused)) = string
+#endif
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#else /* !HAVE_SYS_MALLOC_H */
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef __cplusplus
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS }
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+#endif /* !HAVE_SYS_CDEFS_H */
+
+#endif /* __sys_include__ */
--- /dev/null
+/*
+ * Userload module by Michael L. VanLoon (mlv) <michaelv@iastate.edu>
+ * Written 2/93. Originally grafted into irc2.7.2g 4/93.
+ * Rewritten 9/97 by Carlo Wood for ircu2.10.01.
+ *
+ * IRC - Internet Relay Chat, ircd/userload.h
+ * Copyright (C) 1990 University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef USERLOAD_H
+#define USERLOAD_H
+
+/*=============================================================================
+ * Structures
+ */
+
+struct current_load_st {
+ unsigned int client_count;
+ unsigned int local_count;
+ unsigned int conn_count;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void update_load(void);
+extern void calc_load(aClient *sptr);
+extern void initload(void);
+
+extern struct current_load_st current_load;
+
+#endif /* USERLOAD_H */
--- /dev/null
+#ifndef VERSION_H
+#define VERSION_H
+
+extern const char *version;
+extern const char *creation;
+extern const char *infotext[];
+extern const char *generation;
+
+#endif /* VERSION_H */
--- /dev/null
+#ifndef WHOCMDS_H
+#define WHOCMDS_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_who(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_whois(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+#endif /* WHOCMDS_H */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/whowas.h
+ * Copyright (C) 1990 Markku Savela
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef WHOWAS_H
+#define WHOWAS_H
+
+/*=============================================================================
+ * General defines
+ */
+
+#define BITS_PER_COL 3
+#define BITS_PER_COL_MASK 0x7
+#define WW_MAX_INITIAL 16
+
+#define MAX_SUB (1 << BITS_PER_COL)
+#define WW_MAX_INITIAL_MASK (WW_MAX_INITIAL - 1)
+#define WW_MAX (WW_MAX_INITIAL * MAX_SUB)
+
+/*=============================================================================
+ * Structures
+ */
+
+struct Whowas {
+ unsigned int hashv;
+ char *name;
+ char *username;
+ char *hostname;
+ char *servername;
+ char *realname;
+ char *away;
+ time_t logoff;
+ struct Client *online; /* Needed for get_history() (nick chasing) */
+ struct Whowas *hnext; /* Next entry with the same hash value */
+ struct Whowas **hprevnextp; /* Pointer to previous next pointer */
+ struct Whowas *cnext; /* Next entry with the same 'online' pointer */
+ struct Whowas **cprevnextp; /* Pointer to previous next pointer */
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern aClient *get_history(const char *nick, time_t timelimit);
+extern void add_history(aClient *cptr, int still_on);
+extern void off_history(const aClient *cptr);
+extern void initwhowas(void);
+extern int m_whowas(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void count_whowas_memory(int *wwu, size_t *wwm, int *wwa, size_t *wwam);
+
+#endif /* WHOWAS_H */
--- /dev/null
+Makefile
+stamp-m
+version.c
+ircd
+chkconf
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/IPcheck.c
+ * Copyright (C) 1998 Carlo Wood ( Run @ undernet.org )
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file should be edited in a window with a width of 141 characters */
+
+#include "sys.h"
+#include <netinet/in.h>
+#include "h.h"
+#include "IPcheck.h"
+#include "querycmds.h"
+#include "struct.h"
+#include "s_user.h"
+#include "s_bsd.h"
+#include "struct.h"
+#ifdef GODMODE
+#include "numnicks.h"
+#endif
+#include "send.h"
+
+RCSTAG_CC("$Id$");
+
+extern aClient me;
+extern time_t now;
+
+/*
+ * IP number and last targets of a user that just disconnected.
+ * Used to allow a user that shortly disconnected to rejoin
+ * the channels he/she was on.
+ */
+struct ip_targets_st {
+ struct in_addr ip;
+ unsigned char free_targets;
+ unsigned char targets[MAXTARGETS];
+};
+
+/* We keep one IPregistry for each IP number (for both, remote and local clients) */
+struct IPregistry {
+ union {
+ struct in_addr ip; /* The IP number of the registry entry. */
+ struct ip_targets_st *ptr; /* The IP number of the registry entry, and a list of targets */
+ } ip_targets;
+ unsigned int last_connect:16; /* Time of last connect (attempt), see BITMASK below,
+ or time of last disconnect when `connected' is zero. */
+ unsigned int connected:8; /* Used for IP# throttling: Number of currently on-line clients with this IP number */
+ unsigned int connect_attempts:4; /* Used for connect speed throttling: Number of clients that connected with this IP number
+ or `15' when then real value is >= 15. This value is only valid when the last connect
+ was less then IPCHECK_CLONE_PERIOD seconds ago, it should considered to be 0 otherwise. */
+ unsigned int free_targets:4; /* Number of free targets that the next local client will inherit on connect,
+ or HAS_TARGETS_MAGIC when ip_targets.ptr is a pointer to a ip_targets_st. */
+};
+
+struct IPregistry_vector {
+ unsigned short length;
+ unsigned short allocated_length;
+ struct IPregistry *vector;
+};
+
+#define HASHTABSIZE 0x2000 /* Must be power of 2 */
+static struct IPregistry_vector IPregistry_hashtable[HASHTABSIZE];
+
+/*
+ * Calculate a `hash' value between 0 and HASHTABSIZE, from the internet address `in_addr'.
+ * Apply it immedeately to the table, effectively hiding the table itself.
+ */
+#define CALCULATE_HASH(in_addr) \
+ struct IPregistry_vector *hash; \
+ do { register unsigned int ip = (in_addr).s_addr; \
+ hash = &IPregistry_hashtable[((ip >> 14) + (ip >> 7) + ip) & (HASHTABSIZE - 1)]; } while(0)
+
+/*
+ * Fit `now' in an unsigned short, the advantage is that we use less memory `struct IPregistry::last_connect' can be smaller
+ * while the only disadvantage is that if someone reconnects after exactly 18 hours and 12 minutes, and NOBODY with the
+ * same _hash_ value for this IP-number did disconnect in the meantime, then the server will think he reconnected immedeately.
+ * In other words: No disadvantage at all.
+ */
+#define BITMASK 0xffff /* Same number of bits as `struct IPregistry::last_connect' */
+#define NOW ((unsigned short)(now & BITMASK))
+#define CONNECTED_SINCE(x) ((unsigned short)((now & BITMASK) - (x)->last_connect))
+
+#define IPCHECK_CLONE_LIMIT 2
+#define IPCHECK_CLONE_PERIOD 20
+#define IPCHECK_CLONE_DELAY 600
+
+#define HAS_TARGETS_MAGIC 15
+#define HAS_TARGETS(entry) ((entry)->free_targets == HAS_TARGETS_MAGIC)
+
+#if STARTTARGETS >= HAS_TARGETS_MAGIC
+#error "That doesn't fit in 4 bits, does it?"
+#endif
+
+/* IP(entry) returns the `struct in_addr' of the IPregistry. */
+#define IP(entry) (HAS_TARGETS(entry) ? (entry)->ip_targets.ptr->ip : (entry)->ip_targets.ip)
+#define FREE_TARGETS(entry) (HAS_TARGETS(entry) ? (entry)->ip_targets.ptr->free_targets : (entry)->free_targets)
+
+static unsigned short count = 10000, average_length = 4;
+
+static struct IPregistry *IPregistry_add(struct IPregistry_vector *iprv)
+{
+ if (iprv->length == iprv->allocated_length)
+ {
+ iprv->allocated_length += 4;
+ iprv->vector =
+ (struct IPregistry *)RunRealloc(iprv->vector,
+ iprv->allocated_length * sizeof(struct IPregistry));
+ }
+ return &iprv->vector[iprv->length++];
+}
+
+static struct IPregistry *IPregistry_find(struct IPregistry_vector *iprv,
+ struct in_addr ip)
+{
+ if (iprv->length > 0)
+ {
+ struct IPregistry *i, *end = &iprv->vector[iprv->length];
+ for (i = &iprv->vector[0]; i < end; ++i)
+ if (IP(i).s_addr == ip.s_addr)
+ return i;
+ }
+ return NULL;
+}
+
+static struct IPregistry *IPregistry_find_with_expire(struct IPregistry_vector
+ *iprv, struct in_addr ip)
+{
+ struct IPregistry *last = &iprv->vector[iprv->length - 1]; /* length always > 0 because searched element always exists */
+ struct IPregistry *curr;
+ struct IPregistry *retval = NULL; /* Core dump if we find nothing :/ - can be removed when code is stable */
+
+ for (curr = &iprv->vector[0]; curr < last;)
+ {
+ if (IP(curr).s_addr == ip.s_addr)
+ /* `curr' is element we looked for */
+ retval = curr;
+ else if (curr->connected == 0)
+ {
+ if (CONNECTED_SINCE(curr) > 600U) /* Don't touch this number, it has statistical significance */
+ {
+ /* `curr' expired */
+ if (HAS_TARGETS(curr))
+ RunFree(curr->ip_targets.ptr);
+ *curr = *last--;
+ iprv->length--;
+ if (--count == 0)
+ {
+ /* Make ever 10000 disconnects an estimation of the average vector length */
+ count = 10000;
+ average_length =
+ (nrof.clients + nrof.unknowns + nrof.local_servers) / HASHTABSIZE;
+ }
+ /* Now check the new element (last) that was moved to this position */
+ continue;
+ }
+ else if (CONNECTED_SINCE(curr) > 120U && HAS_TARGETS(curr))
+ {
+ /* Expire storage of targets */
+ struct in_addr ip1 = curr->ip_targets.ptr->ip;
+ curr->free_targets = curr->ip_targets.ptr->free_targets;
+ RunFree(curr->ip_targets.ptr);
+ curr->ip_targets.ip = ip1;
+ }
+ }
+ /* Did not expire, check next element */
+ ++curr;
+ }
+ /* Now check the last element in the list (curr == last) */
+ if (IP(curr).s_addr == ip.s_addr)
+ /* `curr' is element we looked for */
+ retval = curr;
+ else if (curr->connected == 0)
+ {
+ if (CONNECTED_SINCE(curr) > 600U) /* Don't touch this number, it has statistical significance */
+ {
+ /* `curr' expired */
+ if (HAS_TARGETS(curr))
+ RunFree(curr->ip_targets.ptr);
+ iprv->length--;
+ if (--count == 0)
+ {
+ /* Make ever 10000 disconnects an estimation of the average vector length */
+ count = 10000;
+ average_length =
+ (nrof.clients + nrof.unknowns + nrof.local_servers) / HASHTABSIZE;
+ }
+ }
+ else if (CONNECTED_SINCE(curr) > 120U && HAS_TARGETS(curr))
+ {
+ /* Expire storage of targets */
+ struct in_addr ip1 = curr->ip_targets.ptr->ip;
+ curr->free_targets = curr->ip_targets.ptr->free_targets;
+ RunFree(curr->ip_targets.ptr);
+ curr->ip_targets.ip = ip1;
+ }
+ }
+ /* Do we need to shrink the vector? */
+ if (iprv->allocated_length > average_length
+ && iprv->allocated_length - iprv->length >= 4)
+ {
+ struct IPregistry *newpos;
+ iprv->allocated_length = iprv->length;
+ newpos =
+ (struct IPregistry *)RunRealloc(iprv->vector,
+ iprv->allocated_length * sizeof(struct IPregistry));
+ if (newpos != iprv->vector) /* Is this ever true? */
+ {
+ retval =
+ (struct IPregistry *)((char *)retval + ((char *)newpos -
+ (char *)iprv->vector));
+ iprv->vector = newpos;
+ }
+ }
+ return retval;
+}
+
+static void reset_connect_time(struct IPregistry *entry)
+{
+ unsigned int previous_free_targets;
+
+ /* Apply aging */
+ previous_free_targets =
+ FREE_TARGETS(entry) + CONNECTED_SINCE(entry) / TARGET_DELAY;
+ if (previous_free_targets > STARTTARGETS)
+ previous_free_targets = STARTTARGETS;
+ if (HAS_TARGETS(entry))
+ entry->ip_targets.ptr->free_targets = previous_free_targets;
+ else
+ entry->free_targets = previous_free_targets;
+
+ entry->last_connect = NOW;
+}
+
+/*
+ * IPcheck_local_connect
+ *
+ * Event:
+ * A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
+ *
+ * Action:
+ * Update the IPcheck registry.
+ * Return < 0 if the connection should be rejected, otherwise 0.
+ * -1 : Throttled
+ * -2 : Too many connections from your host
+ *
+ * Throttling:
+ *
+ * A connection should be rejected when a connection from the same IP number was
+ * received IPCHECK_CLONE_LIMIT times before this connect attempt, with
+ * reconnect intervals of IPCHECK_CLONE_PERIOD seconds or less.
+ *
+ * Free target inheritance:
+ *
+ * When the client is accepted, then the number of Free Targets
+ * of the cptr is set to the value stored in the found IPregistry
+ * structure, or left at STARTTARGETS. This can be done by changing
+ * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))',
+ * where FREE_TARGETS may range from 0 till STARTTARGETS.
+ */
+int IPcheck_local_connect(aClient *cptr)
+{
+ struct IPregistry *entry;
+ CALCULATE_HASH(cptr->ip);
+ SetIPChecked(cptr); /* Mark that we did add/update an IPregistry entry */
+ if (!(entry = IPregistry_find(hash, cptr->ip)))
+ {
+ entry = IPregistry_add(hash);
+ entry->ip_targets.ip = cptr->ip; /* The IP number of registry entry */
+ entry->last_connect = NOW; /* Seconds since last connect (attempt) */
+ entry->connected = 1; /* Number of currently connected clients with this IP number */
+ entry->connect_attempts = 1; /* Number of clients that connected with this IP number */
+ entry->free_targets = STARTTARGETS; /* Number of free targets that a client gets on connect */
+ return 0;
+ }
+#ifdef GODMODE
+ sendto_one(cptr,
+ "ERROR :I saw your face before my friend (connected: %u; connect_attempts %u; free_targets %u)",
+ entry->connected, entry->connect_attempts, FREE_TARGETS(entry));
+#endif
+ /* Note that this also connects server connects. It is hard and not interesting, to change that. */
+ if (++(entry->connected) == 0) /* Don't allow more then 255 connects from one IP number, ever */
+ return -2;
+ if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
+ entry->connect_attempts = 0;
+ reset_connect_time(entry);
+ if (++(entry->connect_attempts) == 0) /* Check for overflow */
+ --(entry->connect_attempts);
+ if (entry->connect_attempts <= IPCHECK_CLONE_LIMIT)
+ cptr->nexttarget = now - (TARGET_DELAY * (FREE_TARGETS(entry) - 1));
+#ifdef DEBUGMODE
+ else
+#else
+ else if (now - me.since > IPCHECK_CLONE_DELAY) /* Don't refuse connection when we just rebooted the server */
+#endif
+ return -1;
+ return 0;
+}
+
+/*
+ * IPcheck_remote_connect
+ *
+ * Event:
+ * A remote client connected to Undernet, with IP number `cptr->ip.s_addr'
+ * and hostname `hostname'.
+ *
+ * Action:
+ * Update the IPcheck registry.
+ * Return -1 on failure, 0 on success.
+ */
+int IPcheck_remote_connect(aClient *cptr, const char *UNUSED(hostname),
+ int is_burst)
+{
+ struct IPregistry *entry;
+ CALCULATE_HASH(cptr->ip);
+ SetIPChecked(cptr); /* Mark that we did add/update an IPregistry entry */
+ if (!(entry = IPregistry_find(hash, cptr->ip)))
+ {
+ entry = IPregistry_add(hash);
+ entry->ip_targets.ip = cptr->ip; /* The IP number of registry entry */
+ entry->last_connect = NOW; /* Seconds since last connect (attempt) */
+ entry->connected = 1; /* Number of currently connected clients with this IP number */
+ entry->connect_attempts = is_burst ? 1 : 0; /* Number of clients that connected with this IP number */
+ entry->free_targets = STARTTARGETS; /* Number of free targets that a client gets on connect */
+ }
+ else
+ {
+#ifdef GODMODE
+ sendto_one(cptr,
+ "%s NOTICE %s%s :I saw your face before my friend (connected: %u; connect_attempts %u; free_targets %u)",
+ NumServ(&me), NumNick(cptr), entry->connected, entry->connect_attempts,
+ FREE_TARGETS(entry));
+#endif
+ if (++(entry->connected) == 0) /* Don't allow more then 255 connects from one IP number, ever */
+ return -1;
+ if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
+ entry->connect_attempts = 0;
+ if (!is_burst)
+ {
+ if (++(entry->connect_attempts) == 0) /* Check for overflow */
+ --(entry->connect_attempts);
+ reset_connect_time(entry);
+ }
+ }
+ return 0;
+}
+
+/*
+ * IPcheck_connect_fail
+ *
+ * Event:
+ * This local client failed to connect due to legal reasons.
+ *
+ * Action:
+ * Neutralize the effect of calling IPcheck_local_connect, in such
+ * a way that the client won't be penalized when trying to reconnect
+ * again.
+ */
+void IPcheck_connect_fail(aClient *cptr)
+{
+ struct IPregistry *entry;
+ CALCULATE_HASH(cptr->ip);
+ entry = IPregistry_find(hash, cptr->ip);
+ entry->connect_attempts--;
+}
+
+/*
+ * IPcheck_connect_succeeded
+ *
+ * Event:
+ * A client succeeded to finish the registration.
+ *
+ * Finish IPcheck registration of a successfully, locally connected client.
+ */
+void IPcheck_connect_succeeded(aClient *cptr)
+{
+ struct IPregistry *entry;
+ const char *tr = "";
+ CALCULATE_HASH(cptr->ip);
+ entry = IPregistry_find(hash, cptr->ip);
+ if (HAS_TARGETS(entry))
+ {
+ memcpy(cptr->targets, entry->ip_targets.ptr->targets, MAXTARGETS);
+ tr = " tr";
+ }
+ sendto_one(cptr, ":%s NOTICE %s :on %u ca %u(%u) ft %u(%u)%s",
+ me.name, cptr->name, entry->connected, entry->connect_attempts,
+ IPCHECK_CLONE_LIMIT, FREE_TARGETS(entry), STARTTARGETS, tr);
+}
+
+/*
+ * IPcheck_disconnect
+ *
+ * Event:
+ * A local client disconnected or a remote client left Undernet.
+ *
+ * Action:
+ * Update the IPcheck registry.
+ * Remove all expired IPregistry structures from the hash bucket
+ * that belongs to this clients IP number.
+ */
+void IPcheck_disconnect(aClient *cptr)
+{
+ struct IPregistry *entry;
+ CALCULATE_HASH(cptr->ip);
+ entry = IPregistry_find_with_expire(hash, cptr->ip);
+ if (--(entry->connected) == 0) /* If this was the last one, set `last_connect' to disconnect time (used for expiration) */
+ {
+ if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD)
+ entry->connect_attempts = 0; /* Otherwise we'd penetalize for this old value if the client reconnects within 20 seconds */
+ reset_connect_time(entry);
+ }
+ if (MyConnect(cptr))
+ {
+ unsigned int inheritance;
+ /* Copy the clients targets */
+ if (HAS_TARGETS(entry))
+ {
+ entry->free_targets = entry->ip_targets.ptr->free_targets;
+ RunFree(entry->ip_targets.ptr);
+ }
+ entry->ip_targets.ptr =
+ (struct ip_targets_st *)RunMalloc(sizeof(struct ip_targets_st));
+ entry->ip_targets.ptr->ip = cptr->ip;
+ entry->ip_targets.ptr->free_targets = entry->free_targets;
+ entry->free_targets = HAS_TARGETS_MAGIC;
+ memcpy(entry->ip_targets.ptr->targets, cptr->targets, MAXTARGETS);
+ /*
+ * This calculation can be pretty unfair towards large multi-user hosts, but
+ * there is "nothing" we can do without also allowing spam bots to send more
+ * messages or by drastically increasing the ammount of memory used in the IPregistry.
+ *
+ * The problem is that when a client disconnects, leaving no free targets, then
+ * the next client from that IP number has to pay for it (getting no free targets).
+ * But ALSO the next client, and the next client, and the next client etc - until
+ * another client disconnects that DOES leave free targets. The reason for this
+ * is that if there are 10 SPAM bots, and they all disconnect at once, then they
+ * ALL should get no free targets when reconnecting. We'd need to store an entry
+ * per client (instead of per IP number) to avoid this.
+ */
+ if (cptr->nexttarget <= now)
+ inheritance = (now - cptr->nexttarget) / TARGET_DELAY + 1; /* Number of free targets */
+ else
+ inheritance = 0;
+ /* Add bonus, this is pretty fuzzy, but it will help in some cases. */
+ if (now - cptr->firsttime > 600) /* Was longer then 10 minutes online? */
+ inheritance += (now - cptr->firsttime - 600) / TARGET_DELAY;
+ /* Finally, store smallest value for Judgement Day */
+ if (inheritance < entry->ip_targets.ptr->free_targets)
+ entry->ip_targets.ptr->free_targets = inheritance;
+ }
+}
+
+/*
+ * IPcheck_nr
+ *
+ * Returns number of clients with the same IP number
+ */
+unsigned short IPcheck_nr(aClient *cptr)
+{
+ struct IPregistry *entry;
+ CALCULATE_HASH(cptr->ip);
+ entry = IPregistry_find(hash, cptr->ip);
+ return (entry ? entry->connected : 0);
+}
--- /dev/null
+# ircd/Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1990 Jarkko Oikarinen
+# Copyright (C) 1997 Carlo Wood
+
+# 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, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+# Output variables of the 'configure' script:
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+srcdir=@srcdir@
+DEFS=@DEFS@
+INSTALL=@INSTALL@
+SHELL=@SHPROG@
+RM=@RMPROG@
+AWK=@AWK@
+LN_S=@LN_S@
+MV=mv
+CHMOD=chmod
+CHOWN=chown
+CHGRP=chgrp
+MKDIR=mkdir
+TOUCH=touch
+GREP=grep
+@SET_MAKE@
+# The following variables are replaced by what you give during configuration :
+
+BINDIR=
+SYMLINK=
+IRCDMODE=
+IRCDOWN=
+IRCDGRP=
+DPATH=
+MPATH=
+RPATH=
+
+CC=
+CFLAGS=
+CPPFLAGS=
+LDFLAGS=
+IRCDLIBS=
+
+#### End of system configuration section. ####
+
+OBJS=IPcheck.o bsd.o channel.o class.o common.o crule.o dbuf.o fileio.o ircd.o \
+ list.o map.o match.o numnicks.o opercmds.o packet.o parse.o querycmds.o \
+ random.o res.o runmalloc.o s_auth.o s_bsd.o s_conf.o s_debug.o s_err.o \
+ s_misc.o s_numeric.o s_ping.o s_serv.o s_user.o send.o sprintf_irc.o \
+ support.o userload.o whocmds.o whowas.o hash.o
+
+SRC=${OBJS:%.o=%.c}
+
+all:
+ ( cd ..; make -f Makefile )
+
+.SUFFIXES: .c .o
+
+.c.o:
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+build: ircd chkconf
+
+ircd: ${OBJS} ../include/patchlevel.h
+ ${SHELL} version.c.SH
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c version.c
+ ${CC} ${CFLAGS} ${OBJS} version.o ${LDFLAGS} ${IRCDLIBS} -o ircd
+ ${CHMOD} ${IRCDMODE} ircd
+
+chkcrule.o: crule.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/ircd.h \
+ ../include/match.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/common.h ../include/crule.h
+ ${CC} ${CFLAGS} ${CPPFLAGS} -DCR_CHKCONF -o chkcrule.o -c crule.c
+
+chkconf: chkconf.o match.o common.o chkcrule.o runmalloc.o fileio.o
+ ${CC} ${CFLAGS} ${CPPFLAGS} \
+ chkconf.o match.o common.o chkcrule.o runmalloc.o fileio.o \
+ ${LDFLAGS} ${IRCDLIBS} -o chkconf
+
+install: build
+ @if [ ! -d ${DPATH} -a ! -f ${DPATH} ]; then \
+ echo "Creating directory ${DPATH}"; \
+ ${MKDIR} ${DPATH}; \
+ ${CHMOD} 700 ${DPATH}; \
+ ${CHOWN} ${IRCDOWN} ${DPATH}; \
+ ${CHGRP} ${IRCDGRP} ${DPATH}; \
+ fi
+ @echo `date +%y%m%d%H%M`.`cat ../.patches | \
+ ${AWK} -F . '{ if ($$(NF)~/\+$$/) { \
+ for(i=1;i<NF;i++) \
+ printf("%s_",$$i); \
+ gsub("\\\\+","",$$(NF)); \
+ }; \
+ print $$(NF) }'` > /tmp/ircd.tag;
+ @echo "Installing new ircd as ${BINDIR}/ircd.`cat /tmp/ircd.tag` :"
+ ${INSTALL} -m ${IRCDMODE} -o ${IRCDOWN} -g ${IRCDGRP} ircd ${BINDIR}/ircd.`cat /tmp/ircd.tag`
+ @( cd ${BINDIR}; \
+ ${RM} -f ${SYMLINK}; \
+ ${LN_S} ircd.`cat /tmp/ircd.tag` ${SYMLINK}; )
+ @${RM} /tmp/ircd.tag
+ ${INSTALL} -s -m 700 -o ${IRCDOWN} -g ${IRCDGRP} chkconf ${BINDIR}
+ ${INSTALL} -m 600 -o ${IRCDOWN} -g ${IRCDGRP} ../doc/example.conf ${DPATH}
+ ( cd ${DPATH}; \
+ ${TOUCH} ${MPATH}; \
+ ${TOUCH} ${RPATH}; \
+ ${CHOWN} ${IRCDOWN} ${MPATH} ${RPATH}; \
+ ${CHGRP} ${IRCDGRP} ${MPATH} ${RPATH}; )
+
+uninstall:
+ @if [ "${BINDIR}" != "${DPATH}" ]; then \
+ echo "${RM} -f ${BINDIR}/${SYMLINK} ${BINDIR}/ircd.9* ${BINDIR}/chkconf"; \
+ ${RM} -f ${BINDIR}/${SYMLINK} ${BINDIR}/ircd.9* ${BINDIR}/chkconf; \
+ fi
+ @echo "Please remove the contents of ${DPATH} manually"
+
+clean:
+ ${RM} -f *.o ircd version.c chkconf
+
+distclean: clean
+ ${RM} -f Makefile stamp-m
+
+maintainer-clean: distclean
+
+ctables: common.c
+ ${CC} -I../include -DMAKETABLES common.c || exit 1
+ { ${GREP} -A1 -B1000 ^...NTL_TOK_START common.c ; ./a.out ; \
+ ${GREP} -A1000 -B1 ^...NTL_TOK_END common.c ; } > common.temp || exit 1;
+ ${MV} common.temp common.c
+ ${RM} a.out
+
+depend:
+ @if [ -f Makefile.in.bak ]; then \
+ echo "make depend: First remove ircd/Makefile.in.bak"; \
+ else \
+ ( ${MV} Makefile.in Makefile.in.bak; \
+ ${GREP} -A1 -B1000 '^# DO NOT DELETE THIS LINE' Makefile.in.bak > Makefile.in;\
+ ${CC} ${CFLAGS} -MM ${CPPFLAGS} ${SRC:hash.c=} >> Makefile.in; ) \
+ fi
+
+hash.o: hash.c ../include/sys.h ../config/config.h \
+ ../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/common.h ../include/match.h ../include/hash.h \
+ ../include/channel.h ../include/list.h ../include/send.h \
+ ../include/s_serv.h ../include/ircd.h \
+ ircd.c version.c.SH
+ @CC="${CC}" CFLAGS="${CFLAGS}" CPPFLAGS="${CPPFLAGS}" \
+ crypt/sums
+ ${CC} ${CFLAGS} ${CPPFLAGS} -c hash.c -o hash.o
+ @${RM} -f hash.c
+ @${MV} -f hash.c.old hash.c
+ @${TOUCH} hash.o
+
+# Coders: You need GNU make for this to work
+Makefile: ../config/config.status Makefile.in ../config/gen.ircd.Makefile \
+ ../config/config.h ../config/.config stamp-m
+ @echo "recreating ircd/Makefile"
+ @cd ../config; \
+ CONFIG_FILES=../ircd/Makefile CONFIG_HEADERS= \
+ ./config.status >/dev/null; \
+ RM=${RM} ${SHELL} ./gen.ircd.Makefile
+
+stamp-m:
+ echo timestamp > stamp-m
+
+../config/config.status:
+ @cd ../config; ${MAKE} config.status
+
+../config/config.h:
+ @cd ../config; ${MAKE} config.h
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+IPcheck.o: IPcheck.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/IPcheck.h ../include/querycmds.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_user.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/numnicks.h ../include/send.h
+bsd.o: bsd.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/ircd.h ../include/bsd.h
+channel.o: channel.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/channel.h ../include/list.h \
+ ../include/parse.h ../include/send.h ../include/s_err.h \
+ ../include/numeric.h ../include/ircd.h ../include/common.h \
+ ../include/match.h ../include/hash.h ../include/s_serv.h \
+ ../include/s_misc.h ../include/s_user.h ../include/s_conf.h \
+ ../include/s_bsd.h ../include/msg.h ../include/support.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/querycmds.h
+class.o: class.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/class.h ../include/s_conf.h \
+ ../include/list.h ../include/s_serv.h ../include/send.h \
+ ../include/s_err.h ../include/numeric.h ../include/ircd.h
+common.o: common.c ../include/common.h ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h
+crule.o: crule.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/ircd.h \
+ ../include/match.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/common.h ../include/crule.h
+dbuf.o: dbuf.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/dbuf.h
+ircd.o: ircd.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_serv.h ../include/send.h ../include/ircd.h \
+ ../include/s_conf.h ../include/class.h ../include/s_misc.h \
+ ../include/parse.h ../include/match.h ../include/s_bsd.h \
+ ../include/crule.h ../include/userload.h ../include/numeric.h \
+ ../include/hash.h ../include/bsd.h ../include/version.h \
+ ../include/numnicks.h
+list.o: list.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/send.h \
+ ../include/s_conf.h ../include/list.h ../include/class.h \
+ ../include/match.h ../include/ircd.h ../include/s_serv.h \
+ ../include/support.h ../include/s_misc.h ../include/s_bsd.h \
+ ../include/res.h ../include/common.h ../include/s_user.h \
+ ../include/opercmds.h
+map.o: map.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/send.h \
+ ../include/match.h ../include/list.h ../include/s_err.h \
+ ../include/ircd.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/s_misc.h ../include/map.h
+match.o: match.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/common.h ../include/match.h \
+ ../include/ircd.h
+numnicks.o: numnicks.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/s_serv.h ../include/struct.h \
+ ../include/dbuf.h ../include/whowas.h ../include/common.h \
+ ../include/numnicks.h ../include/ircd.h ../include/parse.h \
+ ../include/s_misc.h ../include/match.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h
+opercmds.o: opercmds.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/opercmds.h ../include/struct.h \
+ ../include/dbuf.h ../include/whowas.h ../include/ircd.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/list.h \
+ ../include/send.h ../include/s_err.h ../include/numeric.h \
+ ../include/match.h ../include/s_misc.h ../include/class.h \
+ ../include/s_user.h ../include/common.h ../include/msg.h \
+ ../include/sprintf_irc.h ../include/userload.h ../include/parse.h \
+ ../include/numnicks.h ../include/crule.h ../include/version.h \
+ ../include/support.h ../include/s_serv.h ../include/hash.h
+packet.o: packet.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_misc.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h ../include/ircd.h \
+ ../include/msg.h ../include/parse.h ../include/send.h \
+ ../include/packet.h ../include/s_serv.h
+parse.o: parse.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/send.h \
+ ../include/parse.h ../include/common.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h ../include/msg.h \
+ ../include/s_user.h ../include/channel.h ../include/s_ping.h \
+ ../include/res.h ../include/map.h ../include/hash.h \
+ ../include/numeric.h ../include/ircd.h ../include/s_misc.h \
+ ../include/s_numeric.h ../include/numnicks.h ../include/opercmds.h \
+ ../include/querycmds.h ../include/whocmds.h
+querycmds.o: querycmds.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/parse.h ../include/send.h ../include/s_err.h \
+ ../include/numeric.h ../include/ircd.h ../include/s_user.h \
+ ../include/version.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/s_misc.h ../include/match.h \
+ ../include/s_serv.h ../include/msg.h ../include/channel.h \
+ ../include/numnicks.h ../include/userload.h ../include/support.h \
+ ../include/querycmds.h
+random.o: random.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h \
+ ../include/random.h
+res.o: res.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/numeric.h ../include/send.h ../include/s_misc.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/ircd.h \
+ ../include/s_ping.h ../include/support.h ../include/common.h \
+ ../include/sprintf_irc.h
+runmalloc.o: runmalloc.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/send.h ../include/numeric.h ../include/s_err.h \
+ ../include/ircd.h ../include/s_serv.h ../include/numnicks.h
+s_auth.o: s_auth.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/common.h ../include/send.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/s_misc.h ../include/support.h \
+ ../include/ircd.h ../include/s_auth.h ../include/sprintf_irc.h
+s_bsd.o: s_bsd.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/s_serv.h \
+ ../include/numeric.h ../include/send.h ../include/s_misc.h \
+ ../include/hash.h ../include/s_err.h ../include/ircd.h \
+ ../include/support.h ../include/s_auth.h ../include/class.h \
+ ../include/packet.h ../include/s_ping.h ../include/channel.h \
+ ../include/version.h ../include/parse.h ../include/common.h \
+ ../include/bsd.h ../include/numnicks.h ../include/s_user.h \
+ ../include/sprintf_irc.h ../include/querycmds.h ../include/IPcheck.h
+s_conf.o: s_conf.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/opercmds.h \
+ ../include/numeric.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/class.h ../include/s_misc.h \
+ ../include/match.h ../include/common.h ../include/s_err.h \
+ ../include/s_bsd.h ../include/ircd.h ../include/crule.h \
+ ../include/res.h ../include/support.h ../include/parse.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/IPcheck.h \
+ ../include/hash.h
+s_debug.o: s_debug.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/hash.h \
+ ../include/s_serv.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/class.h ../include/ircd.h \
+ ../include/s_bsd.h ../include/bsd.h ../include/res.h \
+ ../include/channel.h ../include/numnicks.h
+s_err.o: s_err.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/numeric.h ../include/s_err.h \
+ ../include/sprintf_irc.h
+s_misc.o: s_misc.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/numeric.h \
+ ../include/send.h ../include/s_conf.h ../include/list.h \
+ ../include/s_misc.h ../include/common.h ../include/match.h \
+ ../include/hash.h ../include/s_bsd.h ../include/res.h \
+ ../include/ircd.h ../include/s_ping.h ../include/channel.h \
+ ../include/s_err.h ../include/support.h ../include/userload.h \
+ ../include/parse.h ../include/s_user.h ../include/numnicks.h \
+ ../include/sprintf_irc.h ../include/querycmds.h ../include/IPcheck.h
+s_numeric.o: s_numeric.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_serv.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/send.h ../include/support.h \
+ ../include/parse.h ../include/numeric.h ../include/channel.h \
+ ../include/ircd.h ../include/hash.h ../include/numnicks.h \
+ ../include/s_numeric.h
+s_ping.o: s_ping.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/match.h ../include/res.h \
+ ../include/s_bsd.h ../include/s_serv.h ../include/ircd.h \
+ ../include/s_ping.h ../include/support.h ../include/numeric.h \
+ ../include/s_user.h ../include/s_err.h ../include/common.h \
+ ../include/numnicks.h
+s_serv.o: s_serv.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/ircd.h ../include/s_serv.h \
+ ../include/s_misc.h ../include/sprintf_irc.h ../include/send.h \
+ ../include/s_err.h ../include/numeric.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h ../include/hash.h \
+ ../include/common.h ../include/match.h ../include/crule.h \
+ ../include/parse.h ../include/numnicks.h ../include/userload.h \
+ ../include/s_user.h ../include/channel.h ../include/querycmds.h \
+ ../include/IPcheck.h
+s_user.o: s_user.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/common.h ../include/s_serv.h \
+ ../include/numeric.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/s_misc.h ../include/match.h \
+ ../include/hash.h ../include/s_bsd.h ../include/s_err.h \
+ ../include/parse.h ../include/ircd.h ../include/s_user.h \
+ ../include/support.h ../include/channel.h ../include/random.h \
+ ../include/version.h ../include/msg.h ../include/userload.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/querycmds.h \
+ ../include/IPcheck.h
+send.o: send.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/s_serv.h ../include/send.h \
+ ../include/s_misc.h ../include/common.h ../include/match.h \
+ ../include/ircd.h ../include/channel.h ../include/bsd.h \
+ ../include/class.h ../include/s_user.h ../include/sprintf_irc.h
+sprintf_irc.o: sprintf_irc.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/sprintf_irc.h
+support.o: support.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/send.h ../include/ircd.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/list.h \
+ ../include/support.h ../include/sprintf_irc.h ../include/common.h
+userload.o: userload.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/send.h ../include/s_misc.h \
+ ../include/userload.h ../include/ircd.h ../include/numnicks.h \
+ ../include/s_serv.h ../include/querycmds.h
+whocmds.o: whocmds.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/common.h ../include/s_serv.h \
+ ../include/numeric.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/s_misc.h ../include/match.h \
+ ../include/hash.h ../include/s_bsd.h ../include/s_err.h \
+ ../include/parse.h ../include/ircd.h ../include/s_user.h \
+ ../include/support.h ../include/channel.h ../include/random.h \
+ ../include/version.h ../include/msg.h ../include/userload.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/querycmds.h \
+ ../include/IPcheck.h
+whowas.o: whowas.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h \
+ ../include/common.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/numeric.h ../include/send.h ../include/s_misc.h \
+ ../include/s_err.h ../include/ircd.h ../include/list.h \
+ ../include/s_user.h ../include/support.h
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, common/bsd.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <signal.h>
+#include <sys/socket.h> /* Needed for send() */
+#include "h.h"
+#include "struct.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "bsd.h"
+
+RCSTAG_CC("$Id$");
+
+#ifdef DEBUGMODE
+int writecalls = 0;
+int writeb[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+RETSIGTYPE dummy(HANDLER_ARG(int UNUSED(sig)))
+{
+#ifndef HAVE_RELIABLE_SIGNALS
+ signal(SIGALRM, dummy);
+ signal(SIGPIPE, dummy);
+#ifndef HPUX /* Only 9k/800 series require this,
+ but don't know how to.. */
+#ifdef SIGWINCH
+ signal(SIGWINCH, dummy);
+#endif
+#endif
+#else
+#ifdef POSIX_SIGNALS
+ struct sigaction act;
+
+ act.sa_handler = dummy;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGALRM);
+ sigaddset(&act.sa_mask, SIGPIPE);
+#ifdef SIGWINCH
+ sigaddset(&act.sa_mask, SIGWINCH);
+#endif
+ sigaction(SIGALRM, &act, (struct sigaction *)NULL);
+ sigaction(SIGPIPE, &act, (struct sigaction *)NULL);
+#ifdef SIGWINCH
+ sigaction(SIGWINCH, &act, (struct sigaction *)NULL);
+#endif
+#endif
+#endif
+}
+
+/*
+ * deliver_it
+ * Attempt to send a sequence of bytes to the connection.
+ * Returns
+ *
+ * < 0 Some fatal error occurred, (but not EWOULDBLOCK).
+ * This return is a request to close the socket and
+ * clean up the link.
+ *
+ * >= 0 No real error occurred, returns the number of
+ * bytes actually transferred. EWOULDBLOCK and other
+ * possibly similar conditions should be mapped to
+ * zero return. Upper level routine will have to
+ * decide what to do with those unwritten bytes...
+ *
+ * *NOTE* alarm calls have been preserved, so this should
+ * work equally well whether blocking or non-blocking
+ * mode is used...
+ *
+ * We don't use blocking anymore, that is impossible with the
+ * net.loads today anyway. Commented out the alarms to save cpu.
+ * --Run
+ */
+int deliver_it(aClient *cptr, const char *str, int len)
+{
+ int retval;
+ aClient *acpt = cptr->acpt;
+
+#ifdef DEBUGMODE
+ writecalls++;
+#endif
+#ifdef VMS
+ retval = netwrite(cptr->fd, str, len);
+#else
+ retval = send(cptr->fd, str, len, 0);
+ /*
+ * Convert WOULDBLOCK to a return of "0 bytes moved". This
+ * should occur only if socket was non-blocking. Note, that
+ * all is Ok, if the 'write' just returns '0' instead of an
+ * error and errno=EWOULDBLOCK.
+ *
+ * ...now, would this work on VMS too? --msa
+ */
+ if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN ||
+#ifdef SOL2
+ errno == ENOMEM || errno == ENOSR ||
+#endif
+ errno == ENOBUFS))
+ {
+ retval = 0;
+ cptr->flags |= FLAGS_BLOCKED;
+ }
+ else if (retval > 0)
+ {
+#ifdef pyr
+ gettimeofday(&cptr->lw, NULL);
+#endif
+ cptr->flags &= ~FLAGS_BLOCKED;
+ }
+
+#endif
+#ifdef DEBUGMODE
+ if (retval < 0)
+ {
+ writeb[0]++;
+ Debug((DEBUG_ERROR, "write error (%s) to %s",
+ sys_errlist[errno], cptr->name));
+ }
+ else if (retval == 0)
+ writeb[1]++;
+ else if (retval < 16)
+ writeb[2]++;
+ else if (retval < 32)
+ writeb[3]++;
+ else if (retval < 64)
+ writeb[4]++;
+ else if (retval < 128)
+ writeb[5]++;
+ else if (retval < 256)
+ writeb[6]++;
+ else if (retval < 512)
+ writeb[7]++;
+ else if (retval < 1024)
+ writeb[8]++;
+ else
+ writeb[9]++;
+#endif
+ if (retval > 0)
+ {
+ cptr->sendB += retval;
+ me.sendB += retval;
+ if (cptr->sendB > 1023)
+ {
+ cptr->sendK += (cptr->sendB >> 10);
+ cptr->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
+ }
+ if (acpt != &me)
+ {
+ acpt->sendB += retval;
+ if (acpt->sendB > 1023)
+ {
+ acpt->sendK += (acpt->sendB >> 10);
+ acpt->sendB &= 0x03ff;
+ }
+ }
+ else if (me.sendB > 1023)
+ {
+ me.sendK += (me.sendB >> 10);
+ me.sendB &= 0x03ff;
+ }
+ }
+ return (retval);
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/channel.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Co Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdlib.h>
+#include "h.h"
+#include "struct.h"
+#include "channel.h"
+#include "parse.h"
+#include "whowas.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "ircd.h"
+#include "common.h"
+#include "match.h"
+#include "list.h"
+#include "hash.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "s_conf.h"
+#include "s_bsd.h"
+#include "msg.h"
+#include "common.h"
+#include "s_serv.h"
+#include "channel.h"
+#include "support.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+
+RCSTAG_CC("$Id$");
+
+aChannel *channel = NullChn;
+
+static void sendmodeto_one(aClient *cptr, char *from, char *name,
+ char *mode, char *param, time_t creationtime);
+static void add_invite(aClient *, aChannel *);
+static int add_banid(aClient *, aChannel *, char *, int, int);
+static Link *next_overlapped_ban(void);
+static Link *next_removed_overlapped_ban(void);
+static int can_join(aClient *, aChannel *, char *);
+static void channel_modes(aClient *, char *, char *, aChannel *);
+static int del_banid(aChannel *, char *, int);
+static int is_banned(aClient *, aChannel *, Link *);
+static int number_of_zombies(aChannel *);
+static int is_deopped(aClient *, aChannel *);
+static int set_mode(aClient *, aClient *, aChannel *, int,
+ char **, char *, char *, char *, int *);
+static void sub1_from_channel(aChannel *);
+static void send_hack_notice(aClient *, aClient *, int, char *[], int, int);
+static void clean_channelname(char *);
+
+void del_invite(aClient *, aChannel *);
+
+static char *PartFmt1 = ":%s PART %s";
+static char *PartFmt2 = ":%s PART %s :%s";
+/*
+ * some buffers for rebuilding channel/nick lists with ,'s
+ */
+static char nickbuf[BUFSIZE], buf[BUFSIZE];
+static char modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];
+static char nparabuf[MODEBUFLEN];
+
+/*
+ * Maximum acceptable lag time in seconds: A channel younger than
+ * this is not protected against hacking admins.
+ * Mainly here to check if the TS clocks really sync (otherwise this
+ * will start causing HACK notices.
+ * This value must be the same on all servers.
+ *
+ * This value has been increased to 1 day in order to distinguish this
+ * "normal" type of HACK wallops / desyncs, from possiblity still
+ * existing bugs.
+ */
+#define TS_LAG_TIME ((time_t)86400)
+
+/*
+ * A Magic TS that is used for channels that are created by JOIN,
+ * a channel with this TS accepts all TS without complaining that
+ * it might receive later via MODE or CREATE.
+ */
+#define MAGIC_REMOTE_JOIN_TS 1270080000
+
+/*
+ * return the length (>=0) of a chain of links.
+ */
+static int list_length(Link *lp)
+{
+ Reg2 int count = 0;
+
+ for (; lp; lp = lp->next)
+ count++;
+ return count;
+}
+
+/*
+ * find_chasing
+ *
+ * Find the client structure for a nick name (user) using history
+ * mechanism if necessary. If the client is not found, an error
+ * message (NO SUCH NICK) is generated. If the client was found
+ * through the history, chasing will be 1 and otherwise 0.
+ */
+static aClient *find_chasing(aClient *sptr, char *user, int *chasing)
+{
+ Reg2 aClient *who = FindClient(user);
+
+ if (chasing)
+ *chasing = 0;
+ if (who)
+ return who;
+ if (!(who = get_history(user, (long)KILLCHASETIMELIMIT)))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, sptr->name, user);
+ return NULL;
+ }
+ if (chasing)
+ *chasing = 1;
+ return who;
+}
+
+/*
+ * Create a string of form "foo!bar@fubar" given foo, bar and fubar
+ * as the parameters. If NULL, they become "*".
+ */
+static char *make_nick_user_host(char *nick, char *name, char *host)
+{
+ static char namebuf[NICKLEN + USERLEN + HOSTLEN + 3];
+ sprintf_irc(namebuf, "%s!%s@%s", nick, name, host);
+ return namebuf;
+}
+
+/*
+ * Create a string of form "foo!bar@123.456.789.123" given foo, bar and the
+ * IP-number as the parameters. If NULL, they become "*".
+ */
+static char *make_nick_user_ip(char *nick, char *name, struct in_addr ip)
+{
+ static char ipbuf[NICKLEN + USERLEN + 16 + 3];
+ sprintf_irc(ipbuf, "%s!%s@%s", nick, name, inetntoa(ip));
+ return ipbuf;
+}
+
+/*
+ * add_banid
+ *
+ * `cptr' must be the client adding the ban.
+ *
+ * If `change' is true then add `banid' to channel `chptr'.
+ * Returns 0 if the ban was added.
+ * Returns -2 if the ban already existed and was marked CHFL_BURST_BAN_WIPEOUT.
+ * Return -1 otherwise.
+ *
+ * Those bans that overlapped with `banid' are flagged with CHFL_BAN_OVERLAPPED
+ * when `change' is false, otherwise they will be removed from the banlist.
+ * Subsequently calls to next_overlapped_ban() or next_removed_overlapped_ban()
+ * respectively will return these bans until NULL is returned.
+ *
+ * If `firsttime' is true, the ban list as returned by next_overlapped_ban()
+ * is reset (unless a non-zero value is returned, in which case the
+ * CHFL_BAN_OVERLAPPED flag might not have been reset!).
+ *
+ * --Run
+ */
+static Link *next_ban, *prev_ban, *removed_bans_list;
+
+static int add_banid(aClient *cptr, aChannel *chptr, char *banid,
+ int change, int firsttime)
+{
+ Reg1 Link *ban, **banp;
+ Reg2 int cnt = 0, removed_bans = 0, len = strlen(banid);
+
+ if (firsttime)
+ {
+ next_ban = NULL;
+ if (prev_ban || removed_bans_list)
+ MyCoreDump; /* Memory leak */
+ }
+ if (MyUser(cptr))
+ collapse(banid);
+ for (banp = &chptr->banlist; *banp;)
+ {
+ len += strlen((*banp)->value.ban.banstr);
+ ++cnt;
+ if (((*banp)->flags & CHFL_BURST_BAN_WIPEOUT))
+ {
+ if (!strcmp((*banp)->value.ban.banstr, banid))
+ {
+ (*banp)->flags &= ~CHFL_BURST_BAN_WIPEOUT;
+ return -2;
+ }
+ }
+ else if (!mmatch((*banp)->value.ban.banstr, banid))
+ return -1;
+ if (!mmatch(banid, (*banp)->value.ban.banstr))
+ {
+ Link *tmp = *banp;
+ if (change)
+ {
+ if (MyUser(cptr))
+ {
+ cnt--;
+ len -= strlen(tmp->value.ban.banstr);
+ }
+ *banp = tmp->next;
+#if 0
+ /* Silently remove overlapping bans */
+ RunFree(tmp->value.ban.banstr);
+ RunFree(tmp->value.ban.who);
+ free_link(tmp);
+#else
+ /* These will be sent to the user later as -b */
+ tmp->next = removed_bans_list;
+ removed_bans_list = tmp;
+ removed_bans = 1;
+#endif
+ }
+ else if (!(tmp->flags & CHFL_BURST_BAN_WIPEOUT))
+ {
+ tmp->flags |= CHFL_BAN_OVERLAPPED;
+ if (!next_ban)
+ next_ban = tmp;
+ banp = &tmp->next;
+ }
+ else
+ banp = &tmp->next;
+ }
+ else
+ {
+ if (firsttime)
+ (*banp)->flags &= ~CHFL_BAN_OVERLAPPED;
+ banp = &(*banp)->next;
+ }
+ }
+ if (MyUser(cptr) && !removed_bans && (len > MAXBANLENGTH || (cnt >= MAXBANS)))
+ {
+ sendto_one(cptr, err_str(ERR_BANLISTFULL), me.name, cptr->name,
+ chptr->chname, banid);
+ return -1;
+ }
+ if (change)
+ {
+ char *ip_start;
+ ban = make_link();
+ ban->next = chptr->banlist;
+ ban->value.ban.banstr = (char *)RunMalloc(strlen(banid) + 1);
+ strcpy(ban->value.ban.banstr, banid);
+ ban->value.ban.who = (char *)RunMalloc(strlen(cptr->name) + 1);
+ strcpy(ban->value.ban.who, cptr->name);
+ ban->value.ban.when = now;
+ ban->flags = CHFL_BAN; /* This bit is never used I think... */
+ if ((ip_start = strrchr(banid, '@')) && check_if_ipmask(ip_start + 1))
+ ban->flags |= CHFL_BAN_IPMASK;
+ chptr->banlist = ban;
+ /* Erase ban-valid-bit */
+ for (ban = chptr->members; ban; ban = ban->next)
+ ban->flags &= ~CHFL_BANVALID; /* `ban' == channel member ! */
+ }
+ return 0;
+}
+
+static Link *next_overlapped_ban(void)
+{
+ Reg1 Link *tmp = next_ban;
+ if (tmp)
+ {
+ Reg2 Link *ban;
+ for (ban = tmp->next; ban; ban = ban->next)
+ if ((ban->flags & CHFL_BAN_OVERLAPPED))
+ break;
+ next_ban = ban;
+ }
+ return tmp;
+}
+
+static Link *next_removed_overlapped_ban(void)
+{
+ Reg1 Link *tmp = removed_bans_list;
+ if (prev_ban)
+ {
+ if (prev_ban->value.ban.banstr) /* Can be set to NULL in set_mode() */
+ RunFree(prev_ban->value.ban.banstr);
+ RunFree(prev_ban->value.ban.who);
+ free_link(prev_ban);
+ }
+ if (tmp)
+ removed_bans_list = removed_bans_list->next;
+ prev_ban = tmp;
+ return tmp;
+}
+
+/*
+ * del_banid
+ *
+ * If `change' is true, delete `banid' from channel `chptr'.
+ * Returns `false' if removal was (or would have been) successful.
+ */
+static int del_banid(aChannel *chptr, char *banid, int change)
+{
+ Reg1 Link **ban;
+ Reg2 Link *tmp;
+
+ if (!banid)
+ return -1;
+ for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
+ if (strCasediff(banid, (*ban)->value.ban.banstr) == 0)
+ {
+ tmp = *ban;
+ if (change)
+ {
+ *ban = tmp->next;
+ RunFree(tmp->value.ban.banstr);
+ RunFree(tmp->value.ban.who);
+ free_link(tmp);
+ /* Erase ban-valid-bit, for channel members that are banned */
+ for (tmp = chptr->members; tmp; tmp = tmp->next)
+ if ((tmp->flags & (CHFL_BANNED | CHFL_BANVALID)) ==
+ (CHFL_BANNED | CHFL_BANVALID))
+ tmp->flags &= ~CHFL_BANVALID; /* `tmp' == channel member */
+ }
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * IsMember - returns Link * if a person is joined and not a zombie
+ */
+Link *IsMember(aClient *cptr, aChannel *chptr)
+{
+ Link *lp;
+ return (((lp = find_user_link(chptr->members, cptr)) &&
+ !(lp->flags & CHFL_ZOMBIE)) ? lp : NULL);
+}
+
+/*
+ * is_banned - a non-zero value if banned else 0.
+ */
+static int is_banned(aClient *cptr, aChannel *chptr, Link *member)
+{
+ Reg1 Link *tmp;
+ char *s, *ip_s = NULL;
+
+ if (!IsUser(cptr))
+ return 0;
+
+ if (member)
+ {
+ if ((member->flags & CHFL_BANVALID))
+ return (member->flags & CHFL_BANNED);
+ }
+
+ s = make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host);
+
+ for (tmp = chptr->banlist; tmp; tmp = tmp->next)
+ {
+ if ((tmp->flags & CHFL_BAN_IPMASK))
+ {
+ if (!ip_s)
+ ip_s = make_nick_user_ip(cptr->name, cptr->user->username, cptr->ip);
+ if (match(tmp->value.ban.banstr, ip_s) == 0)
+ break;
+ }
+ else if (match(tmp->value.ban.banstr, s) == 0)
+ break;
+ }
+
+ if (member)
+ {
+ member->flags |= CHFL_BANVALID;
+ if (tmp)
+ {
+ member->flags |= CHFL_BANNED;
+ return 1;
+ }
+ else
+ {
+ member->flags &= ~CHFL_BANNED;
+ return 0;
+ }
+ }
+
+ return (tmp != NULL);
+}
+
+/*
+ * adds a user to a channel by adding another link to the channels member
+ * chain.
+ */
+static void add_user_to_channel(aChannel *chptr, aClient *who, int flags)
+{
+ Reg1 Link *ptr;
+
+ if (who->user)
+ {
+ ptr = make_link();
+ ptr->value.cptr = who;
+ ptr->flags = flags;
+ ptr->next = chptr->members;
+ chptr->members = ptr;
+ chptr->users++;
+
+ ptr = make_link();
+ ptr->value.chptr = chptr;
+ ptr->next = who->user->channel;
+ who->user->channel = ptr;
+ who->user->joined++;
+ }
+}
+
+void remove_user_from_channel(aClient *sptr, aChannel *chptr)
+{
+ Reg1 Link **curr;
+ Reg2 Link *tmp;
+ Reg3 Link *lp = chptr->members;
+
+ for (; lp && (lp->flags & CHFL_ZOMBIE || lp->value.cptr == sptr);
+ lp = lp->next);
+ for (;;)
+ {
+ for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
+ if (tmp->value.cptr == sptr)
+ {
+ *curr = tmp->next;
+ free_link(tmp);
+ break;
+ }
+ for (curr = &sptr->user->channel; (tmp = *curr); curr = &tmp->next)
+ if (tmp->value.chptr == chptr)
+ {
+ *curr = tmp->next;
+ free_link(tmp);
+ break;
+ }
+ sptr->user->joined--;
+ if (lp)
+ break;
+ if (chptr->members)
+ sptr = chptr->members->value.cptr;
+ else
+ break;
+ sub1_from_channel(chptr);
+ }
+ sub1_from_channel(chptr);
+}
+
+int is_chan_op(aClient *cptr, aChannel *chptr)
+{
+ Reg1 Link *lp;
+
+ if (chptr)
+ if ((lp = find_user_link(chptr->members, cptr)) &&
+ !(lp->flags & CHFL_ZOMBIE))
+ return (lp->flags & CHFL_CHANOP);
+
+ return 0;
+}
+
+static int is_deopped(aClient *cptr, aChannel *chptr)
+{
+ Reg1 Link *lp;
+
+ if (chptr)
+ if ((lp = find_user_link(chptr->members, cptr)))
+ return (lp->flags & CHFL_DEOPPED);
+
+ return (IsUser(cptr) ? 1 : 0);
+}
+
+int is_zombie(aClient *cptr, aChannel *chptr)
+{
+ Reg1 Link *lp;
+
+ if (chptr)
+ if ((lp = find_user_link(chptr->members, cptr)))
+ return (lp->flags & CHFL_ZOMBIE);
+
+ return 0;
+}
+
+int has_voice(aClient *cptr, aChannel *chptr)
+{
+ Reg1 Link *lp;
+
+ if (chptr)
+ if ((lp = find_user_link(chptr->members, cptr)) &&
+ !(lp->flags & CHFL_ZOMBIE))
+ return (lp->flags & CHFL_VOICE);
+
+ return 0;
+}
+
+int can_send(aClient *cptr, aChannel *chptr)
+{
+ Reg1 Link *lp;
+
+ lp = IsMember(cptr, chptr);
+
+ if ((!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE)) ||
+ (lp->flags & CHFL_ZOMBIE)) && MyUser(cptr) && is_banned(cptr, chptr, lp))
+ return (MODE_BAN);
+
+ if (chptr->mode.mode & MODE_MODERATED &&
+ (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE)) ||
+ (lp->flags & CHFL_ZOMBIE)))
+ return (MODE_MODERATED);
+
+ if (!lp && ((chptr->mode.mode & MODE_NOPRIVMSGS) ||
+ IsModelessChannel(chptr->chname)))
+ return (MODE_NOPRIVMSGS);
+
+ return 0;
+}
+
+/*
+ * write the "simple" list of channel modes for channel chptr onto buffer mbuf
+ * with the parameters in pbuf.
+ */
+static void channel_modes(aClient *cptr, char *mbuf, char *pbuf,
+ aChannel *chptr)
+{
+ *mbuf++ = '+';
+ if (chptr->mode.mode & MODE_SECRET)
+ *mbuf++ = 's';
+ else if (chptr->mode.mode & MODE_PRIVATE)
+ *mbuf++ = 'p';
+ if (chptr->mode.mode & MODE_MODERATED)
+ *mbuf++ = 'm';
+ if (chptr->mode.mode & MODE_TOPICLIMIT)
+ *mbuf++ = 't';
+ if (chptr->mode.mode & MODE_INVITEONLY)
+ *mbuf++ = 'i';
+ if (chptr->mode.mode & MODE_NOPRIVMSGS)
+ *mbuf++ = 'n';
+ if (chptr->mode.limit)
+ {
+ *mbuf++ = 'l';
+ sprintf_irc(pbuf, "%d", chptr->mode.limit);
+ }
+ if (*chptr->mode.key)
+ {
+ *mbuf++ = 'k';
+ if (is_chan_op(cptr, chptr) || IsServer(cptr))
+ {
+ if (chptr->mode.limit)
+ strcat(pbuf, " ");
+ strcat(pbuf, chptr->mode.key);
+ }
+ }
+ *mbuf = '\0';
+ return;
+}
+
+static int send_mode_list(aClient *cptr, char *chname, time_t creationtime,
+ Link *top, int mask, char flag)
+{
+ Reg1 Link *lp;
+ Reg2 char *cp, *name;
+ int count = 0, send = 0, sent = 0;
+
+ cp = modebuf + strlen(modebuf);
+ if (*parabuf) /* mode +l or +k xx */
+ count = 1;
+ for (lp = top; lp; lp = lp->next)
+ {
+ if (!(lp->flags & mask))
+ continue;
+ if (mask == CHFL_BAN)
+ name = lp->value.ban.banstr;
+ else
+ name = lp->value.cptr->name;
+ if (strlen(parabuf) + strlen(name) + 11 < (size_t)MODEBUFLEN)
+ {
+ strcat(parabuf, " ");
+ strcat(parabuf, name);
+ count++;
+ *cp++ = flag;
+ *cp = '\0';
+ }
+ else if (*parabuf)
+ send = 1;
+ if (count == 6)
+ send = 1;
+ if (send)
+ {
+ /* cptr is always a server! So we send creationtimes */
+ sendmodeto_one(cptr, me.name, chname, modebuf, parabuf, creationtime);
+ sent = 1;
+ send = 0;
+ *parabuf = '\0';
+ cp = modebuf;
+ *cp++ = '+';
+ if (count != 6)
+ {
+ strcpy(parabuf, name);
+ *cp++ = flag;
+ }
+ count = 0;
+ *cp = '\0';
+ }
+ }
+ return sent;
+}
+
+/*
+ * send "cptr" a full list of the modes for channel chptr.
+ */
+void send_channel_modes(aClient *cptr, aChannel *chptr)
+{
+ int sent;
+ if (IsLocalChannel(chptr->chname))
+ return;
+
+ *modebuf = *parabuf = '\0';
+ channel_modes(cptr, modebuf, parabuf, chptr);
+
+ if (Protocol(cptr) < 10)
+ {
+ sent = send_mode_list(cptr, chptr->chname, chptr->creationtime,
+ chptr->members, CHFL_CHANOP, 'o');
+ if (!sent && chptr->creationtime)
+ sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT, me.name,
+ chptr->chname, modebuf, parabuf, chptr->creationtime);
+ else if (modebuf[1] || *parabuf)
+ sendmodeto_one(cptr, me.name,
+ chptr->chname, modebuf, parabuf, chptr->creationtime);
+
+ *parabuf = '\0';
+ *modebuf = '+';
+ modebuf[1] = '\0';
+ send_mode_list(cptr, chptr->chname, chptr->creationtime,
+ chptr->banlist, CHFL_BAN, 'b');
+ if (modebuf[1] || *parabuf)
+ sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
+ parabuf, chptr->creationtime);
+
+ *parabuf = '\0';
+ *modebuf = '+';
+ modebuf[1] = '\0';
+ send_mode_list(cptr, chptr->chname, chptr->creationtime,
+ chptr->members, CHFL_VOICE, 'v');
+ if (modebuf[1] || *parabuf)
+ sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
+ parabuf, chptr->creationtime);
+ }
+ else
+ {
+ static unsigned int current_flags[4] =
+ { 0, CHFL_CHANOP | CHFL_VOICE, CHFL_VOICE, CHFL_CHANOP };
+ int first = 1, full = 1, flag_cnt = 0, new_mode = 0;
+ size_t len, sblen;
+ Link *lp1 = chptr->members;
+ Link *lp2 = chptr->banlist;
+ for (first = 1; full; first = 0) /* Loop for multiple messages */
+ {
+ full = 0; /* Assume by default we get it
+ all in one message */
+
+ /* (Continued) prefix: "<Y> BURST <channel> <TS>" */
+ sprintf_irc(sendbuf, "%s BURST %s " TIME_T_FMT, NumServ(&me),
+ chptr->chname, chptr->creationtime);
+ sblen = strlen(sendbuf);
+
+ if (first && modebuf[1]) /* Add simple modes (iklmnpst)
+ if first message */
+ {
+ /* prefix: "<Y> BURST <channel> <TS>[ <modes>[ <params>]]" */
+ sendbuf[sblen++] = ' ';
+ strcpy(sendbuf + sblen, modebuf);
+ sblen += strlen(modebuf);
+ if (*parabuf)
+ {
+ sendbuf[sblen++] = ' ';
+ strcpy(sendbuf + sblen, parabuf);
+ sblen += strlen(parabuf);
+ }
+ }
+
+ /* Attach nicks, comma seperated " nick[:modes],nick[:modes],..." */
+ /* Run 4 times over all members, to group the members with the
+ * same mode together */
+ for (first = 1; flag_cnt < 4;
+ lp1 = chptr->members, new_mode = 1, flag_cnt++)
+ {
+ for (; lp1; lp1 = lp1->next)
+ {
+ if ((lp1->flags & (CHFL_CHANOP | CHFL_VOICE)) !=
+ current_flags[flag_cnt])
+ continue; /* Skip members with different flags */
+ if (sblen + NUMNICKLEN + 4 > BUFSIZE - 3)
+ /* The 4 is a possible ",:ov"
+ The -3 is for the "\r\n\0" that is added in send.c */
+ {
+ full = 1; /* Make sure we continue after
+ sending it so far */
+ break; /* Do not add this member to this message */
+ }
+ sendbuf[sblen++] = first ? ' ' : ',';
+ first = 0; /* From now on, us comma's to add new nicks */
+
+ sprintf_irc(sendbuf + sblen, "%s%s", NumNick(lp1->value.cptr));
+ sblen += strlen(sendbuf + sblen);
+
+ if (new_mode) /* Do we have a nick with a new mode ? */
+ {
+ new_mode = 0;
+ sendbuf[sblen++] = ':';
+ if (lp1->flags & CHFL_CHANOP)
+ sendbuf[sblen++] = 'o';
+ if (lp1->flags & CHFL_VOICE)
+ sendbuf[sblen++] = 'v';
+ }
+ }
+ if (full)
+ break;
+ }
+
+ if (!full)
+ {
+ /* Attach all bans, space seperated " :%ban ban ..." */
+ for (first = 2; lp2; lp2 = lp2->next)
+ {
+ len = strlen(lp2->value.ban.banstr);
+ if (sblen + len + 1 + first > BUFSIZE - 3)
+ /* The +1 stands for the added ' '.
+ * The +first stands for the added ":%".
+ * The -3 is for the "\r\n\0" that is added in send.c
+ */
+ {
+ full = 1;
+ break;
+ }
+ if (first)
+ {
+ first = 0;
+ sendbuf[sblen++] = ' ';
+ sendbuf[sblen++] = ':'; /* Will be last parameter */
+ sendbuf[sblen++] = '%'; /* To tell bans apart */
+ }
+ else
+ sendbuf[sblen++] = ' ';
+ strcpy(sendbuf + sblen, lp2->value.ban.banstr);
+ sblen += len;
+ }
+ }
+
+ sendbuf[sblen] = '\0';
+ sendbufto_one(cptr); /* Send this message */
+ } /* Continue when there was something
+ that didn't fit (full==1) */
+ }
+}
+
+/*
+ * m_mode
+ * parv[0] - sender
+ * parv[1] - channel
+ */
+
+int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ int badop, sendts;
+ aChannel *chptr;
+
+ /* Now, try to find the channel in question */
+ if (parc > 1)
+ {
+ chptr = FindChannel(parv[1]);
+ if (chptr == NullChn)
+ return m_umode(cptr, sptr, parc, parv);
+ }
+ else
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "MODE");
+ return 0;
+ }
+
+ sptr->flags &= ~FLAGS_TS8;
+
+ if (MyUser(sptr))
+ clean_channelname(parv[1]);
+ else if (IsLocalChannel(parv[1]))
+ return 0;
+
+ /* sending an error wasnt good, lets just send an empty mode reply.. poptix */
+ if (IsModelessChannel(chptr->chname))
+ {
+ if (IsUser(sptr))
+ sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
+ chptr->chname, "+nt", "");
+ return 0;
+ }
+
+ if (parc < 3)
+ {
+ *modebuf = *parabuf = '\0';
+ modebuf[1] = '\0';
+ channel_modes(sptr, modebuf, parabuf, chptr);
+ sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
+ chptr->chname, modebuf, parabuf);
+ sendto_one(sptr, rpl_str(RPL_CREATIONTIME), me.name, parv[0],
+ chptr->chname, chptr->creationtime);
+ return 0;
+ }
+
+ if (!(sendts = set_mode(cptr, sptr, chptr, parc - 2, parv + 2,
+ modebuf, parabuf, nparabuf, &badop)))
+ {
+ sendto_one(sptr, err_str(IsMember(sptr, chptr) ? ERR_CHANOPRIVSNEEDED :
+ ERR_NOTONCHANNEL), me.name, parv[0], chptr->chname);
+ return 0;
+ }
+
+ if (badop >= 2)
+ send_hack_notice(cptr, sptr, parc, parv, badop, 1);
+
+ if (strlen(modebuf) > (size_t)1 || sendts > 0)
+ {
+ if (badop != 2 && strlen(modebuf) > (size_t)1)
+ sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s %s",
+ parv[0], chptr->chname, modebuf, parabuf);
+ if (IsLocalChannel(chptr->chname))
+ return 0;
+ /* We send a creationtime of 0, to mark it as a hack --Run */
+ if (IsServer(sptr) && (badop == 2 || sendts > 0))
+ {
+ if (*modebuf == '\0')
+ strcpy(modebuf, "+");
+ if (badop != 2)
+ {
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s %s " TIME_T_FMT,
+ parv[0], chptr->chname, modebuf, parabuf,
+ (badop == 4) ? (time_t) 0 : chptr->creationtime);
+ sendto_highprot_butone(cptr, 10, ":%s MODE %s %s %s " TIME_T_FMT,
+ parv[0], chptr->chname, modebuf, nparabuf,
+ (badop == 4) ? (time_t) 0 : chptr->creationtime);
+ }
+ }
+ else
+ {
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s %s",
+ parv[0], chptr->chname, modebuf, parabuf);
+ sendto_highprot_butone(cptr, 10, ":%s MODE %s %s %s",
+ parv[0], chptr->chname, modebuf, nparabuf);
+ }
+ }
+ return 0;
+}
+
+static int DoesOp(char *modebuf)
+{
+ modebuf--; /* Is it possible that a mode
+ starts with o and not +o ? */
+ while (*++modebuf)
+ if (*modebuf == 'o' || *modebuf == 'v')
+ return (1);
+ return 0;
+}
+
+/* This function should be removed when all servers are 2.10 */
+static void sendmodeto_one(aClient *cptr, char *from, char *name,
+ char *mode, char *param, time_t creationtime)
+{
+ if (IsServer(cptr) && DoesOp(mode) && creationtime)
+ sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT,
+ from, name, mode, param, creationtime);
+ else
+ sendto_one(cptr, ":%s MODE %s %s %s", from, name, mode, param);
+}
+
+/*
+ * pretty_mask
+ *
+ * by Carlo Wood (Run), 05 Oct 1998.
+ *
+ * Canonify a mask.
+ *
+ * When the nick is longer then NICKLEN, it is cut off (its an error of course).
+ * When the user name or host name are too long (USERLEN and HOSTLEN
+ * respectively) then they are cut off at the start with a '*'.
+ *
+ * The following transformations are made:
+ *
+ * 1) xxx -> nick!*@*
+ * 2) xxx.xxx -> *!*@host
+ * 3) xxx!yyy -> nick!user@*
+ * 4) xxx@yyy -> *!user@host
+ * 5) xxx!yyy@zzz -> nick!user@host
+ */
+char *pretty_mask(char *mask)
+{
+ static char star[2] = { '*', 0 };
+ char *last_dot = NULL;
+ char *ptr;
+
+ /* Case 1: default */
+ char *nick = mask;
+ char *user = star;
+ char *host = star;
+
+ /* Do a _single_ pass through the characters of the mask: */
+ for (ptr = mask; *ptr; ++ptr)
+ {
+ if (*ptr == '!')
+ {
+ /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
+ user = ++ptr;
+ host = star;
+ }
+ else if (*ptr == '@')
+ {
+ /* Case 4: Found last '@' (without finding a '!' yet) */
+ nick = star;
+ user = mask;
+ host = ++ptr;
+ }
+ else if (*ptr == '.')
+ {
+ /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
+ last_dot = ptr;
+ continue;
+ }
+ else
+ continue;
+ for (; *ptr; ++ptr)
+ {
+ if (*ptr == '@')
+ {
+ /* Case 4 or 5: Found last '@' */
+ host = ptr + 1;
+ }
+ }
+ break;
+ }
+ if (user == star && last_dot)
+ {
+ /* Case 2: */
+ nick = star;
+ user = star;
+ host = mask;
+ }
+ /* Check lengths */
+ if (nick != star)
+ {
+ char *nick_end = (user != star) ? user - 1 : ptr;
+ if (nick_end - nick > NICKLEN)
+ nick[NICKLEN] = 0;
+ *nick_end = 0;
+ }
+ if (user != star)
+ {
+ char *user_end = (host != star) ? host - 1 : ptr;
+ if (user_end - user > USERLEN)
+ {
+ user = user_end - USERLEN;
+ *user = '*';
+ }
+ *user_end = 0;
+ }
+ if (host != star && ptr - host > HOSTLEN)
+ {
+ host = ptr - HOSTLEN;
+ *host = '*';
+ }
+ return make_nick_user_host(nick, user, host);
+}
+
+static char bmodebuf[MODEBUFLEN], bparambuf[MODEBUFLEN];
+static char nbparambuf[MODEBUFLEN]; /* "Numeric" Bounce Parameter Buffer */
+
+/*
+ * Check and try to apply the channel modes passed in the parv array for
+ * the client ccptr to channel chptr. The resultant changes are printed
+ * into mbuf and pbuf (if any) and applied to the channel.
+ */
+static int set_mode(aClient *cptr, aClient *sptr, aChannel *chptr, int parc,
+ char *parv[], char *mbuf, char *pbuf, char *npbuf, int *badop)
+{
+ static Link chops[MAXPARA - 2]; /* This size is only needed when a broken
+ server sends more then MAXMODEPARAMS
+ parameters */
+ static int flags[] = {
+ MODE_PRIVATE, 'p', MODE_SECRET, 's',
+ MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n',
+ MODE_TOPICLIMIT, 't', MODE_INVITEONLY, 'i',
+ MODE_VOICE, 'v', MODE_KEY, 'k',
+ 0x0, 0x0
+ };
+
+ Reg1 Link *lp;
+ Reg2 char *curr = parv[0], *cp = NULL;
+ Reg3 int *ip;
+ Link *member, *tmp = NULL;
+ unsigned int whatt = MODE_ADD, bwhatt = 0;
+ int limitset = 0, bounce, add_banid_called = 0;
+ size_t len, nlen, blen, nblen;
+ int keychange = 0;
+ unsigned int nusers = 0, newmode;
+ int opcnt = 0, banlsent = 0;
+ int doesdeop = 0, doesop = 0, hacknotice = 0, change, gotts = 0;
+ aClient *who;
+ Mode *mode, oldm;
+ static char numeric[16];
+ char *bmbuf = bmodebuf, *bpbuf = bparambuf, *nbpbuf = nbparambuf;
+ time_t newtime = (time_t) 0;
+ aConfItem *aconf;
+
+ *mbuf = *pbuf = *npbuf = *bmbuf = *bpbuf = *nbpbuf = '\0';
+ *badop = 0;
+ if (parc < 1)
+ return 0;
+
+ mode = &(chptr->mode);
+ memcpy(&oldm, mode, sizeof(Mode));
+ /*
+ * Mode is accepted when sptr is a channel operator
+ * but also when the mode is received from a server.
+ * At this point, let any member pass, so they are allowed
+ * to see the bans.
+ */
+ if (!(IsServer(cptr) || (tmp = IsMember(sptr, chptr))))
+ return 0;
+
+ newmode = mode->mode;
+
+ while (curr && *curr)
+ {
+ switch (*curr)
+ {
+ case '+':
+ whatt = MODE_ADD;
+ break;
+ case '-':
+ whatt = MODE_DEL;
+ break;
+ case 'o':
+ case 'v':
+ if (--parc <= 0)
+ break;
+ parv++;
+ if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
+ break;
+ /*
+ * Check for nickname changes and try to follow these
+ * to make sure the right client is affected by the
+ * mode change.
+ * Even if we find a nick with find_chasing() there
+ * is still a reason to ignore in a special case.
+ * We need to ignore the mode when:
+ * - It is part of a net.burst (from a server and
+ * a MODE_ADD). Ofcourse we don't ignore mode
+ * changes from Uworld.
+ * - The found nick is not on the right side off
+ * the net.junction.
+ * This fixes the bug that when someone (tries to)
+ * ride a net.break and does so with the nick of
+ * someone on the otherside, that he is nick collided
+ * (killed) but his +o still ops the other person.
+ */
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ {
+ if (!(who = find_chasing(sptr, parv[0], NULL)))
+ break;
+ }
+ else
+ {
+ if (!(who = findNUser(parv[0])))
+ break;
+ }
+ if (whatt == MODE_ADD && IsServer(sptr) && who->from != sptr->from &&
+ !find_conf_host(cptr->confs, sptr->name, CONF_UWORLD))
+ break;
+ if (!(member = find_user_link(chptr->members, who)) ||
+ (MyUser(sptr) && (member->flags & CHFL_ZOMBIE)))
+ {
+ sendto_one(cptr, err_str(ERR_USERNOTINCHANNEL),
+ me.name, cptr->name, who->name, chptr->chname);
+ break;
+ }
+ /* if the user is +k, prevent a deop from local user */
+ if (whatt == MODE_DEL && IsChannelService(who) &&
+ MyUser(cptr) && *curr == 'o')
+ {
+ sendto_one(cptr, err_str(ERR_ISCHANSERVICE), me.name,
+ cptr->name, parv[0], chptr->chname);
+ break;
+ }
+ if (whatt == MODE_ADD)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cptr = who;
+ if (IsServer(sptr) && (!(who->flags & FLAGS_TS8) || ((*curr == 'o') &&
+ !(member->flags & (CHFL_SERVOPOK | CHFL_CHANOP)))))
+ *badop = ((member->flags & CHFL_DEOPPED) && (*curr == 'o')) ? 2 : 3;
+ lp->flags = (*curr == 'o') ? MODE_CHANOP : MODE_VOICE;
+ lp->flags |= MODE_ADD;
+ }
+ else if (whatt == MODE_DEL)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cptr = who;
+ doesdeop = 1; /* Also when -v */
+ lp->flags = (*curr == 'o') ? MODE_CHANOP : MODE_VOICE;
+ lp->flags |= MODE_DEL;
+ }
+ if (*curr == 'o')
+ doesop = 1;
+ break;
+ case 'k':
+ if (--parc <= 0)
+ break;
+ parv++;
+ /* check now so we eat the parameter if present */
+ if (keychange)
+ break;
+ else
+ {
+ char *s = &(*parv)[-1];
+ unsigned short count = KEYLEN + 1;
+
+ while (*++s > ' ' && *s != ':' && --count);
+ *s = '\0';
+ if (!**parv) /* nothing left in key */
+ break;
+ }
+ if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
+ break;
+ if (whatt == MODE_ADD)
+ {
+ if (*mode->key && !IsServer(cptr))
+ sendto_one(cptr, err_str(ERR_KEYSET),
+ me.name, cptr->name, chptr->chname);
+ else if (!*mode->key || IsServer(cptr))
+ {
+ lp = &chops[opcnt++];
+ lp->value.cp = *parv;
+ if (strlen(lp->value.cp) > (size_t)KEYLEN)
+ lp->value.cp[KEYLEN] = '\0';
+ lp->flags = MODE_KEY | MODE_ADD;
+ keychange = 1;
+ }
+ }
+ else if (whatt == MODE_DEL)
+ {
+ if (strCasediff(mode->key, *parv) == 0 || IsServer(cptr))
+ {
+ lp = &chops[opcnt++];
+ lp->value.cp = mode->key;
+ lp->flags = MODE_KEY | MODE_DEL;
+ keychange = 1;
+ }
+ }
+ break;
+ case 'b':
+ if (--parc <= 0)
+ {
+ if (banlsent) /* Only send it once */
+ break;
+ for (lp = chptr->banlist; lp; lp = lp->next)
+ sendto_one(cptr, rpl_str(RPL_BANLIST), me.name, cptr->name,
+ chptr->chname, lp->value.ban.banstr, lp->value.ban.who,
+ lp->value.ban.when);
+ sendto_one(cptr, rpl_str(RPL_ENDOFBANLIST), me.name, cptr->name,
+ chptr->chname);
+ banlsent = 1;
+ break;
+ }
+ parv++;
+ if (BadPtr(*parv))
+ break;
+ if (MyUser(sptr))
+ {
+ if ((cp = strchr(*parv, ' ')))
+ *cp = 0;
+ if (opcnt >= MAXMODEPARAMS || **parv == ':' || **parv == '\0')
+ break;
+ }
+ if (whatt == MODE_ADD)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cp = *parv;
+ lp->flags = MODE_ADD | MODE_BAN;
+ }
+ else if (whatt == MODE_DEL)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cp = *parv;
+ lp->flags = MODE_DEL | MODE_BAN;
+ }
+ break;
+ case 'l':
+ /*
+ * limit 'l' to only *1* change per mode command but
+ * eat up others.
+ */
+ if (limitset)
+ {
+ if (whatt == MODE_ADD && --parc > 0)
+ parv++;
+ break;
+ }
+ if (whatt == MODE_DEL)
+ {
+ limitset = 1;
+ nusers = 0;
+ break;
+ }
+ if (--parc > 0)
+ {
+ if (BadPtr(*parv))
+ break;
+ if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
+ break;
+ if (!(nusers = atoi(*++parv)))
+ continue;
+ lp = &chops[opcnt++];
+ lp->flags = MODE_ADD | MODE_LIMIT;
+ limitset = 1;
+ break;
+ }
+ sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS),
+ me.name, cptr->name, "MODE +l");
+ break;
+ case 'i': /* falls through for default case */
+ if (whatt == MODE_DEL)
+ while ((lp = chptr->invites))
+ del_invite(lp->value.cptr, chptr);
+ default:
+ for (ip = flags; *ip; ip += 2)
+ if (*(ip + 1) == *curr)
+ break;
+
+ if (*ip)
+ {
+ if (whatt == MODE_ADD)
+ {
+ if (*ip == MODE_PRIVATE)
+ newmode &= ~MODE_SECRET;
+ else if (*ip == MODE_SECRET)
+ newmode &= ~MODE_PRIVATE;
+ newmode |= *ip;
+ }
+ else
+ newmode &= ~*ip;
+ }
+ else if (!IsServer(cptr))
+ sendto_one(cptr, err_str(ERR_UNKNOWNMODE),
+ me.name, cptr->name, *curr);
+ break;
+ }
+ curr++;
+ /*
+ * Make sure mode strings such as "+m +t +p +i" are parsed
+ * fully.
+ */
+ if (!*curr && parc > 0)
+ {
+ curr = *++parv;
+ parc--;
+ /* If this was from a server, and it is the last
+ * parameter and it starts with a digit, it must
+ * be the creationtime. --Run
+ */
+ if (IsServer(sptr))
+ {
+ if (parc == 1 && isDigit(*curr))
+ {
+ newtime = atoi(curr);
+ if (newtime && chptr->creationtime == MAGIC_REMOTE_JOIN_TS)
+ {
+ chptr->creationtime = newtime;
+ *badop = 0;
+ }
+ gotts = 1;
+ if (newtime == 0)
+ {
+ *badop = 2;
+ hacknotice = 1;
+ }
+ else if (newtime > chptr->creationtime)
+ { /* It is a net-break ride if we have ops.
+ bounce modes if we have ops. --Run */
+ if (doesdeop)
+ *badop = 2;
+ else if (chptr->creationtime == 0)
+ {
+ if (chptr->creationtime == 0 || doesop)
+ chptr->creationtime = newtime;
+ *badop = 0;
+ }
+ /* Bounce: */
+ else
+ *badop = 1;
+ }
+ /*
+ * A legal *badop can occur when two
+ * people join simultaneously a channel,
+ * Allow for 10 min of lag (and thus hacking
+ * on channels younger then 10 min) --Run
+ */
+ else if (*badop == 0 ||
+ chptr->creationtime > (TStime() - TS_LAG_TIME))
+ {
+ if (newtime < chptr->creationtime)
+ chptr->creationtime = newtime;
+ *badop = 0;
+ }
+ break;
+ }
+ }
+ else
+ *badop = 0;
+ }
+ } /* end of while loop for MODE processing */
+
+ /* Now reject non chan ops */
+ if (!IsServer(cptr) && (!tmp || !(tmp->flags & CHFL_CHANOP)))
+ {
+ *badop = 0;
+ return (opcnt || newmode != mode->mode || limitset || keychange) ? 0 : -1;
+ }
+
+ if (doesop && newtime == 0 && IsServer(sptr))
+ *badop = 2;
+
+ if (*badop >= 2 &&
+ (aconf = find_conf_host(cptr->confs, sptr->name, CONF_UWORLD)))
+ *badop = 4;
+
+ bounce = (*badop == 1 || *badop == 2 || is_deopped(sptr, chptr)) ? 1 : 0;
+
+ whatt = 0;
+ for (ip = flags; *ip; ip += 2)
+ if ((*ip & newmode) && !(*ip & oldm.mode))
+ {
+ if (bounce)
+ {
+ if (bwhatt != MODE_DEL)
+ {
+ *bmbuf++ = '-';
+ bwhatt = MODE_DEL;
+ }
+ *bmbuf++ = *(ip + 1);
+ }
+ else
+ {
+ if (whatt != MODE_ADD)
+ {
+ *mbuf++ = '+';
+ whatt = MODE_ADD;
+ }
+ mode->mode |= *ip;
+ *mbuf++ = *(ip + 1);
+ }
+ }
+
+ for (ip = flags; *ip; ip += 2)
+ if ((*ip & oldm.mode) && !(*ip & newmode))
+ {
+ if (bounce)
+ {
+ if (bwhatt != MODE_ADD)
+ {
+ *bmbuf++ = '+';
+ bwhatt = MODE_ADD;
+ }
+ *bmbuf++ = *(ip + 1);
+ }
+ else
+ {
+ if (whatt != MODE_DEL)
+ {
+ *mbuf++ = '-';
+ whatt = MODE_DEL;
+ }
+ mode->mode &= ~*ip;
+ *mbuf++ = *(ip + 1);
+ }
+ }
+
+ blen = nblen = 0;
+ if (limitset && !nusers && mode->limit)
+ {
+ if (bounce)
+ {
+ if (bwhatt != MODE_ADD)
+ {
+ *bmbuf++ = '+';
+ bwhatt = MODE_ADD;
+ }
+ *bmbuf++ = 'l';
+ sprintf(numeric, "%-15d", mode->limit);
+ if ((cp = strchr(numeric, ' ')))
+ *cp = '\0';
+ strcat(bpbuf, numeric);
+ blen += strlen(numeric);
+ strcat(bpbuf, " ");
+ strcat(nbpbuf, numeric);
+ nblen += strlen(numeric);
+ strcat(nbpbuf, " ");
+ }
+ else
+ {
+ if (whatt != MODE_DEL)
+ {
+ *mbuf++ = '-';
+ whatt = MODE_DEL;
+ }
+ mode->mode &= ~MODE_LIMIT;
+ mode->limit = 0;
+ *mbuf++ = 'l';
+ }
+ }
+ /*
+ * Reconstruct "+bkov" chain.
+ */
+ if (opcnt)
+ {
+ Reg1 int i = 0;
+ Reg2 char c = 0;
+ unsigned int prev_whatt = 0;
+
+ for (; i < opcnt; i++)
+ {
+ lp = &chops[i];
+ /*
+ * make sure we have correct mode change sign
+ */
+ if (whatt != (lp->flags & (MODE_ADD | MODE_DEL)))
+ {
+ if (lp->flags & MODE_ADD)
+ {
+ *mbuf++ = '+';
+ prev_whatt = whatt;
+ whatt = MODE_ADD;
+ }
+ else
+ {
+ *mbuf++ = '-';
+ prev_whatt = whatt;
+ whatt = MODE_DEL;
+ }
+ }
+ len = strlen(pbuf);
+ nlen = strlen(npbuf);
+ /*
+ * get c as the mode char and tmp as a pointer to
+ * the parameter for this mode change.
+ */
+ switch (lp->flags & MODE_WPARAS)
+ {
+ case MODE_CHANOP:
+ c = 'o';
+ cp = lp->value.cptr->name;
+ break;
+ case MODE_VOICE:
+ c = 'v';
+ cp = lp->value.cptr->name;
+ break;
+ case MODE_BAN:
+ /*
+ * I made this a bit more user-friendly (tm):
+ * nick = nick!*@*
+ * nick!user = nick!user@*
+ * user@host = *!user@host
+ * host.name = *!*@host.name --Run
+ */
+ c = 'b';
+ cp = pretty_mask(lp->value.cp);
+ break;
+ case MODE_KEY:
+ c = 'k';
+ cp = lp->value.cp;
+ break;
+ case MODE_LIMIT:
+ c = 'l';
+ sprintf(numeric, "%-15d", nusers);
+ if ((cp = strchr(numeric, ' ')))
+ *cp = '\0';
+ cp = numeric;
+ break;
+ }
+
+ /* What could be added: cp+' '+' '+<TS>+'\0' */
+ if (len + strlen(cp) + 13 > (size_t)MODEBUFLEN ||
+ nlen + strlen(cp) + NUMNICKLEN + 12 > (size_t)MODEBUFLEN)
+ break;
+
+ switch (lp->flags & MODE_WPARAS)
+ {
+ case MODE_KEY:
+ if (strlen(cp) > (size_t)KEYLEN)
+ *(cp + KEYLEN) = '\0';
+ if ((whatt == MODE_ADD && (*mode->key == '\0' ||
+ strCasediff(mode->key, cp) != 0)) ||
+ (whatt == MODE_DEL && (*mode->key != '\0')))
+ {
+ if (bounce)
+ {
+ if (*mode->key == '\0')
+ {
+ if (bwhatt != MODE_DEL)
+ {
+ *bmbuf++ = '-';
+ bwhatt = MODE_DEL;
+ }
+ strcat(bpbuf, cp);
+ blen += strlen(cp);
+ strcat(bpbuf, " ");
+ blen++;
+ strcat(nbpbuf, cp);
+ nblen += strlen(cp);
+ strcat(nbpbuf, " ");
+ nblen++;
+ }
+ else
+ {
+ if (bwhatt != MODE_ADD)
+ {
+ *bmbuf++ = '+';
+ bwhatt = MODE_ADD;
+ }
+ strcat(bpbuf, mode->key);
+ blen += strlen(mode->key);
+ strcat(bpbuf, " ");
+ blen++;
+ strcat(nbpbuf, mode->key);
+ nblen += strlen(mode->key);
+ strcat(nbpbuf, " ");
+ nblen++;
+ }
+ *bmbuf++ = c;
+ mbuf--;
+ if (*mbuf != '+' && *mbuf != '-')
+ mbuf++;
+ else
+ whatt = prev_whatt;
+ }
+ else
+ {
+ *mbuf++ = c;
+ strcat(pbuf, cp);
+ len += strlen(cp);
+ strcat(pbuf, " ");
+ len++;
+ strcat(npbuf, cp);
+ nlen += strlen(cp);
+ strcat(npbuf, " ");
+ nlen++;
+ if (whatt == MODE_ADD)
+ strncpy(mode->key, cp, KEYLEN);
+ else
+ *mode->key = '\0';
+ }
+ }
+ break;
+ case MODE_LIMIT:
+ if (nusers && nusers != mode->limit)
+ {
+ if (bounce)
+ {
+ if (mode->limit == 0)
+ {
+ if (bwhatt != MODE_DEL)
+ {
+ *bmbuf++ = '-';
+ bwhatt = MODE_DEL;
+ }
+ }
+ else
+ {
+ if (bwhatt != MODE_ADD)
+ {
+ *bmbuf++ = '+';
+ bwhatt = MODE_ADD;
+ }
+ sprintf(numeric, "%-15d", mode->limit);
+ if ((cp = strchr(numeric, ' ')))
+ *cp = '\0';
+ strcat(bpbuf, numeric);
+ blen += strlen(numeric);
+ strcat(bpbuf, " ");
+ blen++;
+ strcat(nbpbuf, numeric);
+ nblen += strlen(numeric);
+ strcat(nbpbuf, " ");
+ nblen++;
+ }
+ *bmbuf++ = c;
+ mbuf--;
+ if (*mbuf != '+' && *mbuf != '-')
+ mbuf++;
+ else
+ whatt = prev_whatt;
+ }
+ else
+ {
+ *mbuf++ = c;
+ strcat(pbuf, cp);
+ len += strlen(cp);
+ strcat(pbuf, " ");
+ len++;
+ strcat(npbuf, cp);
+ nlen += strlen(cp);
+ strcat(npbuf, " ");
+ nlen++;
+ mode->limit = nusers;
+ }
+ }
+ break;
+ case MODE_CHANOP:
+ case MODE_VOICE:
+ tmp = find_user_link(chptr->members, lp->value.cptr);
+ if (lp->flags & MODE_ADD)
+ {
+ change = (~tmp->flags) & CHFL_OVERLAP & lp->flags;
+ if (change && bounce)
+ {
+ if (lp->flags & MODE_CHANOP)
+ tmp->flags |= CHFL_DEOPPED;
+ if (bwhatt != MODE_DEL)
+ {
+ *bmbuf++ = '-';
+ bwhatt = MODE_DEL;
+ }
+ *bmbuf++ = c;
+ strcat(bpbuf, lp->value.cptr->name);
+ blen += strlen(lp->value.cptr->name);
+ strcat(bpbuf, " ");
+ blen++;
+ sprintf_irc(nbpbuf + nblen, "%s%s ", NumNick(lp->value.cptr));
+ nblen += strlen(nbpbuf + nblen);
+ change = 0;
+ }
+ else if (change)
+ {
+ tmp->flags |= lp->flags & CHFL_OVERLAP;
+ if (lp->flags & MODE_CHANOP)
+ {
+ tmp->flags &= ~CHFL_DEOPPED;
+ if (IsServer(sptr))
+ tmp->flags &= ~CHFL_SERVOPOK;
+ }
+ }
+ }
+ else
+ {
+ change = tmp->flags & CHFL_OVERLAP & lp->flags;
+ if (change && bounce)
+ {
+ if (lp->flags & MODE_CHANOP)
+ tmp->flags &= ~CHFL_DEOPPED;
+ if (bwhatt != MODE_ADD)
+ {
+ *bmbuf++ = '+';
+ bwhatt = MODE_ADD;
+ }
+ *bmbuf++ = c;
+ strcat(bpbuf, lp->value.cptr->name);
+ blen += strlen(lp->value.cptr->name);
+ strcat(bpbuf, " ");
+ blen++;
+ sprintf_irc(nbpbuf + nblen, "%s%s ", NumNick(lp->value.cptr));
+ blen += strlen(bpbuf + blen);
+ change = 0;
+ }
+ else
+ {
+ tmp->flags &= ~change;
+ if ((change & MODE_CHANOP) && IsServer(sptr))
+ tmp->flags |= CHFL_DEOPPED;
+ }
+ }
+ if (change || *badop == 2 || *badop == 4)
+ {
+ *mbuf++ = c;
+ strcat(pbuf, cp);
+ len += strlen(cp);
+ strcat(pbuf, " ");
+ len++;
+ sprintf_irc(npbuf + nlen, "%s%s ", NumNick(lp->value.cptr));
+ nlen += strlen(npbuf + nlen);
+ npbuf[nlen++] = ' ';
+ npbuf[nlen] = 0;
+ }
+ else
+ {
+ mbuf--;
+ if (*mbuf != '+' && *mbuf != '-')
+ mbuf++;
+ else
+ whatt = prev_whatt;
+ }
+ break;
+ case MODE_BAN:
+/*
+ * Only bans aren't bounced, it makes no sense to bounce last second
+ * bans while propagating bans done before the net.rejoin. The reason
+ * why I don't bounce net.rejoin bans is because it is too much
+ * work to take care of too long strings adding the necessary TS to
+ * net.burst bans -- RunLazy
+ * We do have to check for *badop==2 now, we don't want HACKs to take
+ * effect.
+ *
+ * Since BURST - I *did* implement net.rejoin ban bouncing. So now it
+ * certainly makes sense to also bounce 'last second' bans (bans done
+ * after the net.junction). -- RunHardWorker
+ */
+ if ((change = (whatt & MODE_ADD) &&
+ !add_banid(sptr, chptr, cp, !bounce, !add_banid_called)))
+ add_banid_called = 1;
+ else
+ change = (whatt & MODE_DEL) && !del_banid(chptr, cp, !bounce);
+
+ if (bounce && change)
+ {
+ change = 0;
+ if ((whatt & MODE_ADD))
+ {
+ if (bwhatt != MODE_DEL)
+ {
+ *bmbuf++ = '-';
+ bwhatt = MODE_DEL;
+ }
+ }
+ else if ((whatt & MODE_DEL))
+ {
+ if (bwhatt != MODE_ADD)
+ {
+ *bmbuf++ = '+';
+ bwhatt = MODE_ADD;
+ }
+ }
+ *bmbuf++ = c;
+ strcat(bpbuf, cp);
+ blen += strlen(cp);
+ strcat(bpbuf, " ");
+ blen++;
+ strcat(nbpbuf, cp);
+ nblen += strlen(cp);
+ strcat(nbpbuf, " ");
+ nblen++;
+ }
+ if (change)
+ {
+ *mbuf++ = c;
+ strcat(pbuf, cp);
+ len += strlen(cp);
+ strcat(pbuf, " ");
+ len++;
+ strcat(npbuf, cp);
+ nlen += strlen(cp);
+ strcat(npbuf, " ");
+ nlen++;
+ }
+ else
+ {
+ mbuf--;
+ if (*mbuf != '+' && *mbuf != '-')
+ mbuf++;
+ else
+ whatt = prev_whatt;
+ }
+ break;
+ }
+ } /* for (; i < opcnt; i++) */
+ } /* if (opcnt) */
+
+ *mbuf++ = '\0';
+ *bmbuf++ = '\0';
+
+ /* Bounce here */
+ if (!hacknotice && *bmodebuf && chptr->creationtime)
+ {
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT,
+ me.name, chptr->chname, bmodebuf, bparambuf,
+ *badop == 2 ? (time_t) 0 : chptr->creationtime);
+ else
+ sendto_one(cptr, "%s MODE %s %s %s " TIME_T_FMT,
+ NumServ(&me), chptr->chname, bmodebuf, nbparambuf,
+ *badop == 2 ? (time_t) 0 : chptr->creationtime);
+ }
+ /* If there are possibly bans to re-add, bounce them now */
+ if (add_banid_called && bounce)
+ {
+ Link *ban[6]; /* Max 6 bans at a time */
+ size_t len[6], sblen, total_len;
+ int cnt, delayed = 0;
+ while (delayed || (ban[0] = next_overlapped_ban()))
+ {
+ len[0] = strlen(ban[0]->value.ban.banstr);
+ cnt = 1; /* We already got one ban :) */
+ sblen = sprintf_irc(sendbuf, ":%s MODE %s +b",
+ me.name, chptr->chname) - sendbuf;
+ total_len = sblen + 1 + len[0]; /* 1 = ' ' */
+ /* Find more bans: */
+ delayed = 0;
+ while (cnt < 6 && (ban[cnt] = next_overlapped_ban()))
+ {
+ len[cnt] = strlen(ban[cnt]->value.ban.banstr);
+ if (total_len + 5 + len[cnt] > BUFSIZE) /* 5 = "b \r\n\0" */
+ {
+ delayed = cnt + 1; /* != 0 */
+ break; /* Flush */
+ }
+ sendbuf[sblen++] = 'b';
+ total_len += 2 + len[cnt++]; /* 2 = "b " */
+ }
+ while (cnt--)
+ {
+ sendbuf[sblen++] = ' ';
+ strcpy(sendbuf + sblen, ban[cnt]->value.ban.banstr);
+ sblen += len[cnt];
+ }
+ sendbufto_one(cptr); /* Send bounce to uplink */
+ if (delayed)
+ ban[0] = ban[delayed - 1];
+ }
+ }
+ /* Send -b's of overlapped bans to clients to keep them synchronized */
+ if (add_banid_called && !bounce)
+ {
+ Link *ban;
+ char *banstr[6]; /* Max 6 bans at a time */
+ size_t len[6], sblen, psblen, total_len;
+ int cnt, delayed = 0;
+ Link *lp;
+ aClient *acptr;
+ if (IsServer(sptr))
+ psblen = sprintf_irc(sendbuf, ":%s MODE %s -b",
+ sptr->name, chptr->chname) - sendbuf;
+ else /* We rely on IsRegistered(sptr) being true for MODE */
+ psblen = sprintf_irc(sendbuf, ":%s!%s@%s MODE %s -b", sptr->name,
+ sptr->user->username, sptr->user->host, chptr->chname) - sendbuf;
+ while (delayed || (ban = next_removed_overlapped_ban()))
+ {
+ if (!delayed)
+ {
+ len[0] = strlen((banstr[0] = ban->value.ban.banstr));
+ ban->value.ban.banstr = NULL;
+ }
+ cnt = 1; /* We already got one ban :) */
+ sblen = psblen;
+ total_len = sblen + 1 + len[0]; /* 1 = ' ' */
+ /* Find more bans: */
+ delayed = 0;
+ while (cnt < 6 && (ban = next_removed_overlapped_ban()))
+ {
+ len[cnt] = strlen((banstr[cnt] = ban->value.ban.banstr));
+ ban->value.ban.banstr = NULL;
+ if (total_len + 5 + len[cnt] > BUFSIZE) /* 5 = "b \r\n\0" */
+ {
+ delayed = cnt + 1; /* != 0 */
+ break; /* Flush */
+ }
+ sendbuf[sblen++] = 'b';
+ total_len += 2 + len[cnt++]; /* 2 = "b " */
+ }
+ while (cnt--)
+ {
+ sendbuf[sblen++] = ' ';
+ strcpy(sendbuf + sblen, banstr[cnt]);
+ RunFree(banstr[cnt]);
+ sblen += len[cnt];
+ }
+ for (lp = chptr->members; lp; lp = lp->next)
+ if (MyConnect(acptr = lp->value.cptr) && !(lp->flags & CHFL_ZOMBIE))
+ sendbufto_one(acptr);
+ if (delayed)
+ {
+ banstr[0] = banstr[delayed - 1];
+ len[0] = len[delayed - 1];
+ }
+ }
+ }
+
+ return gotts ? 1 : -1;
+}
+
+/* We are now treating the <key> part of /join <channel list> <key> as a key
+ * ring; that is, we try one key against the actual channel key, and if that
+ * doesn't work, we try the next one, and so on. -Kev -Texaco
+ * Returns: 0 on match, 1 otherwise
+ * This version contributed by SeKs <intru@info.polymtl.ca>
+ */
+static int compall(char *key, char *keyring)
+{
+ register char *p1;
+
+top:
+ p1 = key; /* point to the key... */
+ while (*p1 && *p1 == *keyring)
+ { /* step through the key and ring until they
+ don't match... */
+ p1++;
+ keyring++;
+ }
+
+ if (!*p1 && (!*keyring || *keyring == ','))
+ /* ok, if we're at the end of the and also at the end of one of the keys
+ in the keyring, we have a match */
+ return 0;
+
+ if (!*keyring) /* if we're at the end of the key ring, there
+ weren't any matches, so we return 1 */
+ return 1;
+
+ /* Not at the end of the key ring, so step
+ through to the next key in the ring: */
+ while (*keyring && *(keyring++) != ',');
+
+ goto top; /* and check it against the key */
+}
+
+static int can_join(aClient *sptr, aChannel *chptr, char *key)
+{
+ Reg1 Link *lp;
+
+ /* Now a banned user CAN join if invited -- Nemesi */
+ /* Now a user CAN escape channel limit if invited -- bfriendly */
+ if ((chptr->mode.mode & MODE_INVITEONLY) || (is_banned(sptr, chptr, NULL)
+ || (chptr->mode.limit && chptr->users >= chptr->mode.limit)))
+ {
+ for (lp = sptr->user->invited; lp; lp = lp->next)
+ if (lp->value.chptr == chptr)
+ break;
+ if (!lp)
+ {
+ if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
+ return (ERR_CHANNELISFULL);
+ /* This can return an "Invite only" msg instead of the "You are banned"
+ if _both_ conditions are true, but who can say what is more
+ appropriate ? checking again IsBanned would be _SO_ cpu-xpensive ! */
+ return ((chptr->mode.mode & MODE_INVITEONLY) ?
+ ERR_INVITEONLYCHAN : ERR_BANNEDFROMCHAN);
+ }
+ }
+
+ /* now using compall (above) to test against a whole key ring -Kev */
+ if (*chptr->mode.key && (BadPtr(key) || compall(chptr->mode.key, key)))
+ return (ERR_BADCHANNELKEY);
+
+ return 0;
+}
+
+/*
+ * Remove bells and commas from channel name
+ */
+
+static void clean_channelname(char *cn)
+{
+ for (; *cn; cn++)
+ {
+ if (!isIrcCh(*cn))
+ {
+ *cn = '\0';
+ return;
+ }
+ if (isIrcCl(*cn))
+#ifndef FIXME
+ {
+#endif
+ *cn = toLower(*cn);
+#ifndef FIXME
+ /* Missed the Icelandic letter ETH last time: */
+ if ((unsigned char)(*cn) == 0xd0)
+ *cn = (char)0xf0;
+ }
+#endif
+ }
+}
+
+/*
+ * Get Channel block for i (and allocate a new channel
+ * block, if it didn't exists before).
+ */
+static aChannel *get_channel(aClient *cptr, char *chname, int flag)
+{
+ Reg1 aChannel *chptr;
+ int len;
+
+ if (BadPtr(chname))
+ return NULL;
+
+ len = strlen(chname);
+ if (MyUser(cptr) && len > CHANNELLEN)
+ {
+ len = CHANNELLEN;
+ *(chname + CHANNELLEN) = '\0';
+ }
+ if ((chptr = FindChannel(chname)))
+ return (chptr);
+ if (flag == CREATE)
+ {
+ chptr = (aChannel *)RunMalloc(sizeof(aChannel) + len);
+ ++nrof.channels;
+ memset(chptr, 0, sizeof(aChannel));
+ strcpy(chptr->chname, chname);
+ if (channel)
+ channel->prevch = chptr;
+ chptr->prevch = NULL;
+ chptr->nextch = channel;
+ chptr->creationtime = MyUser(cptr) ? TStime() : (time_t) 0;
+ channel = chptr;
+ hAddChannel(chptr);
+ }
+ return chptr;
+}
+
+static void add_invite(aClient *cptr, aChannel *chptr)
+{
+ Reg1 Link *inv, **tmp;
+
+ del_invite(cptr, chptr);
+ /*
+ * Delete last link in chain if the list is max length
+ */
+ if (list_length(cptr->user->invited) >= MAXCHANNELSPERUSER)
+ del_invite(cptr, cptr->user->invited->value.chptr);
+ /*
+ * Add client to channel invite list
+ */
+ inv = make_link();
+ inv->value.cptr = cptr;
+ inv->next = chptr->invites;
+ chptr->invites = inv;
+ /*
+ * Add channel to the end of the client invite list
+ */
+ for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next));
+ inv = make_link();
+ inv->value.chptr = chptr;
+ inv->next = NULL;
+ (*tmp) = inv;
+}
+
+/*
+ * Delete Invite block from channel invite list and client invite list
+ */
+void del_invite(aClient *cptr, aChannel *chptr)
+{
+ Reg1 Link **inv, *tmp;
+
+ for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
+ if (tmp->value.cptr == cptr)
+ {
+ *inv = tmp->next;
+ free_link(tmp);
+ break;
+ }
+
+ for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
+ if (tmp->value.chptr == chptr)
+ {
+ *inv = tmp->next;
+ free_link(tmp);
+ break;
+ }
+}
+
+/* List and skip all channels that are listen */
+void list_next_channels(aClient *cptr, int nr)
+{
+ aListingArgs *args = cptr->listing;
+ aChannel *chptr = args->chptr;
+ chptr->mode.mode &= ~MODE_LISTED;
+ while (is_listed(chptr) || --nr >= 0)
+ {
+ for (; chptr; chptr = chptr->nextch)
+ {
+ if (!cptr->user || (SecretChannel(chptr) && !IsMember(cptr, chptr)))
+ continue;
+ if (chptr->users > args->min_users && chptr->users < args->max_users &&
+ chptr->creationtime > args->min_time &&
+ chptr->creationtime < args->max_time &&
+ (!args->topic_limits || (*chptr->topic &&
+ chptr->topic_time > args->min_topic_time &&
+ chptr->topic_time < args->max_topic_time)))
+ {
+ sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name,
+ ShowChannel(cptr, chptr) ? chptr->chname : "*",
+ chptr->users, ShowChannel(cptr, chptr) ? chptr->topic : "");
+ chptr = chptr->nextch;
+ break;
+ }
+ }
+ if (!chptr)
+ {
+ RunFree(cptr->listing);
+ cptr->listing = NULL;
+ sendto_one(cptr, rpl_str(RPL_LISTEND), me.name, cptr->name);
+ break;
+ }
+ }
+ if (chptr)
+ {
+ cptr->listing->chptr = chptr;
+ chptr->mode.mode |= MODE_LISTED;
+ }
+}
+
+/*
+ * Subtract one user from channel i (and free channel
+ * block, if channel became empty).
+ */
+static void sub1_from_channel(aChannel *chptr)
+{
+ Reg2 Link *tmp;
+ Link *obtmp;
+
+ if (chptr->users > 1) /* Can be 0, called for an empty channel too */
+ {
+ --chptr->users;
+ return;
+ }
+
+ /* Channel became (or was) empty: Remove channel */
+ if (is_listed(chptr))
+ {
+ int i;
+ for (i = 0; i <= highest_fd; i++)
+ {
+ aClient *acptr;
+ if ((acptr = loc_clients[i]) && acptr->listing &&
+ acptr->listing->chptr == chptr)
+ {
+ list_next_channels(acptr, 1);
+ break; /* Only one client can list a channel */
+ }
+ }
+ }
+ /*
+ * Now, find all invite links from channel structure
+ */
+ while ((tmp = chptr->invites))
+ del_invite(tmp->value.cptr, chptr);
+
+ tmp = chptr->banlist;
+ while (tmp)
+ {
+ obtmp = tmp;
+ tmp = tmp->next;
+ RunFree(obtmp->value.ban.banstr);
+ RunFree(obtmp->value.ban.who);
+ free_link(obtmp);
+ }
+ if (chptr->prevch)
+ chptr->prevch->nextch = chptr->nextch;
+ else
+ channel = chptr->nextch;
+ if (chptr->nextch)
+ chptr->nextch->prevch = chptr->prevch;
+ hRemChannel(chptr);
+ --nrof.channels;
+ RunFree((char *)chptr);
+}
+
+/*
+ * m_join
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[2] = channel keys (client), or channel TS (server)
+ */
+int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ static char jbuf[BUFSIZE], mbuf[BUFSIZE];
+ Reg1 Link *lp;
+ Reg3 aChannel *chptr;
+ Reg4 char *name, *keysOrTS = NULL;
+ int i = 0, zombie = 0, sendcreate = 0;
+ unsigned int flags = 0;
+ size_t jlen = 0, mlen = 0;
+ size_t *buflen;
+ char *p = NULL, *bufptr;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "JOIN");
+ return 0;
+ }
+
+ for (p = parv[1]; *p; p++) /* find the last "JOIN 0" in the line -Kev */
+ if (*p == '0'
+ && (*(p + 1) == ',' || *(p + 1) == '\0' || !isIrcCh(*(p + 1))))
+ {
+ /* If it's a single "0", remember the place; we will start parsing
+ the channels after the last 0 in the line -Kev */
+ parv[1] = p;
+ if (!*(p + 1))
+ break;
+ p++;
+ }
+ else
+ { /* Step through to the next comma or until the
+ end of the line, in an attempt to save CPU
+ -Kev */
+ while (*p != ',' && *p != '\0')
+ p++;
+ if (!*p)
+ break;
+ }
+
+ keysOrTS = parv[2]; /* Remember where our keys are or the TS is;
+ parv[2] needs to be NULL for the call to
+ m_names below -Kev */
+ parv[2] = p = NULL;
+
+ *jbuf = *mbuf = '\0'; /* clear both join and mode buffers -Kev */
+ /*
+ * Rebuild list of channels joined to be the actual result of the
+ * JOIN. Note that "JOIN 0" is the destructive problem.
+ */
+ for (name = strtoken(&p, parv[1], ","); name; name = strtoken(&p, NULL, ","))
+ {
+ size_t len;
+ if (MyConnect(sptr))
+ clean_channelname(name);
+ else if (IsLocalChannel(name))
+ continue;
+ if (*name == '0' && *(name + 1) == '\0')
+ {
+ /* Remove the user from all his channels -Kev */
+ while ((lp = sptr->user->channel))
+ {
+ chptr = lp->value.chptr;
+ if (!is_zombie(sptr, chptr))
+ sendto_channel_butserv(chptr, sptr, PartFmt2,
+ parv[0], chptr->chname, "Left all channels");
+ remove_user_from_channel(sptr, chptr);
+ }
+ /* Just in case */
+ *mbuf = *jbuf = '\0';
+ mlen = jlen = 0;
+ }
+ else
+ { /* not a /join 0, so treat it as
+ a /join #channel -Kev */
+ if (!IsChannelName(name))
+ {
+ if (MyUser(sptr))
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name);
+ continue;
+ }
+
+ if (MyConnect(sptr))
+ {
+ /*
+ * Local client is first to enter previously nonexistant
+ * channel so make them (rightfully) the Channel Operator.
+ * This looks kind of ugly because we try to avoid calling the strlen()
+ */
+ if (ChannelExists(name))
+ {
+ flags = CHFL_DEOPPED;
+ sendcreate = 0;
+ }
+ else if (strlen(name) > CHANNELLEN)
+ {
+ *(name + CHANNELLEN) = '\0';
+ if (ChannelExists(name))
+ {
+ flags = CHFL_DEOPPED;
+ sendcreate = 0;
+ }
+ else
+ {
+ flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP;
+ sendcreate = 1;
+ }
+ }
+ else
+ {
+ flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP;
+ sendcreate = 1;
+ }
+
+ if (sptr->user->joined >= MAXCHANNELSPERUSER)
+ {
+ chptr = get_channel(sptr, name, !CREATE);
+ sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS),
+ me.name, parv[0], chptr ? chptr->chname : name);
+ break; /* Can't return, else he won't get on ANY
+ channels! Break out of the for loop instead.
+ -Kev */
+ }
+ }
+ chptr = get_channel(sptr, name, CREATE);
+ if (chptr && (lp = find_user_link(chptr->members, sptr)))
+ {
+ if (lp->flags & CHFL_ZOMBIE)
+ {
+ zombie = 1;
+ flags = lp->flags & (CHFL_DEOPPED | CHFL_SERVOPOK);
+ remove_user_from_channel(sptr, chptr);
+ chptr = get_channel(sptr, name, CREATE);
+ }
+ else
+ continue;
+ }
+ name = chptr->chname;
+ if (!chptr->creationtime) /* A remote JOIN created this channel ? */
+ chptr->creationtime = MAGIC_REMOTE_JOIN_TS;
+ if (parc > 2)
+ {
+ if (chptr->creationtime == MAGIC_REMOTE_JOIN_TS)
+ chptr->creationtime = atoi(keysOrTS);
+ else
+ parc = 2; /* Don't pass it on */
+ }
+ if (!zombie)
+ {
+ if (!MyConnect(sptr))
+ flags = CHFL_DEOPPED;
+ if (sptr->flags & FLAGS_TS8)
+ flags |= CHFL_SERVOPOK;
+ }
+ if (MyConnect(sptr))
+ {
+ int created = chptr->users == 0;
+ if (check_target_limit(sptr, chptr, chptr->chname, created))
+ {
+ if (created) /* Did we create the channel? */
+ sub1_from_channel(chptr); /* Remove it again! */
+ continue;
+ }
+ if ((i = can_join(sptr, chptr, keysOrTS)))
+ {
+ sendto_one(sptr, err_str(i), me.name, parv[0], chptr->chname);
+ continue;
+ }
+ }
+ /*
+ * Complete user entry to the new channel (if any)
+ */
+ add_user_to_channel(chptr, sptr, flags);
+
+ /*
+ * Notify all other users on the new channel
+ */
+ sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name);
+
+ if (MyUser(sptr))
+ {
+ del_invite(sptr, chptr);
+ if (chptr->topic[0] != '\0')
+ {
+ sendto_one(sptr, rpl_str(RPL_TOPIC), me.name,
+ parv[0], name, chptr->topic);
+ sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME), me.name, parv[0], name,
+ chptr->topic_nick, chptr->topic_time);
+ }
+ parv[1] = name;
+ m_names(cptr, sptr, 2, parv);
+ }
+ }
+
+ /* Select proper buffer; mbuf for creation, jbuf otherwise */
+
+ if (*name == '&')
+ continue; /* Head off local channels at the pass */
+
+ bufptr = (sendcreate == 0) ? jbuf : mbuf;
+ buflen = (sendcreate == 0) ? &jlen : &mlen;
+ len = strlen(name);
+ if (*buflen < BUFSIZE - len - 2)
+ {
+ if (*bufptr)
+ {
+ strcat(bufptr, ","); /* Add to join buf */
+ *buflen += 1;
+ }
+ strncat(bufptr, name, BUFSIZE - *buflen - 1);
+ *buflen += len;
+ }
+ sendcreate = 0; /* Reset sendcreate */
+ }
+
+#ifndef NO_PROTOCOL9
+ if (*jbuf || *mbuf) /* Propagate joins to P09 servers */
+ sendto_lowprot_butone(cptr, 9, (*jbuf && *mbuf) ? ":%s JOIN %s,%s" :
+ ":%s JOIN %s%s", parv[0], jbuf, mbuf);
+#endif
+
+ if (*jbuf) /* Propgate joins to P10 servers */
+#ifdef NO_PROTOCOL9
+ sendto_serv_butone(cptr,
+ parc > 2 ? ":%s JOIN %s %s" : ":%s JOIN %s", parv[0], jbuf, keysOrTS);
+#else
+ sendto_highprot_butone(cptr, 10,
+ parc > 2 ? ":%s JOIN %s %s" : ":%s JOIN %s", parv[0], jbuf, keysOrTS);
+#endif
+ if (*mbuf) /* and now creation events */
+#ifdef NO_PROTOCOL9
+ sendto_serv_butone(cptr, "%s%s CREATE %s " TIME_T_FMT,
+ NumNick(sptr), mbuf, TStime());
+#else
+ sendto_highprot_butone(cptr, 10, "%s%s CREATE %s " TIME_T_FMT,
+ NumNick(sptr), mbuf, TStime());
+#endif
+
+ if (MyUser(sptr))
+ { /* shouldn't ever set TS for remote JOIN's */
+ if (*jbuf)
+ { /* check for channels that need TS's */
+ p = NULL;
+ for (name = strtoken(&p, jbuf, ","); name; name = strtoken(&p, NULL, ","))
+ {
+ chptr = get_channel(sptr, name, !CREATE);
+ if (chptr && chptr->mode.mode & MODE_SENDTS)
+ { /* send a TS? */
+ sendto_serv_butone(cptr, ":%s MODE %s + " TIME_T_FMT, me.name,
+ chptr->chname, chptr->creationtime); /* ok, send TS */
+ chptr->mode.mode &= ~MODE_SENDTS; /* reset flag */
+ }
+ }
+ }
+
+ if (*mbuf)
+ { /* ok, send along modes for creation events to P9 */
+ p = NULL;
+ for (name = strtoken(&p, mbuf, ","); name; name = strtoken(&p, NULL, ","))
+ {
+ chptr = get_channel(sptr, name, !CREATE);
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s +o %s " TIME_T_FMT,
+ me.name, chptr->chname, parv[0], chptr->creationtime);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * m_destruct
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel channelname
+ * parv[2] = channel time stamp
+ *
+ * This function does nothing, it does passes DESTRUCT to the other servers.
+ * In the future we will start to use this message.
+ *
+ */
+int m_destruct(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ time_t chanTS; /* Creation time of the channel */
+
+ if (parc < 3 || *parv[2] == '\0')
+ return 0;
+
+#ifdef GODMODE
+ /* Allow DESTRUCT from user */
+ if (MyUser(sptr))
+ sptr = &me;
+ else
+#endif
+
+ /* sanity checks: Only accept DESTRUCT messages from servers */
+ if (!IsServer(sptr))
+ return 0;
+
+ /* Don't pass on DESTRUCT messages for channels that exist */
+ if (FindChannel(parv[1]))
+ return 0;
+
+ chanTS = atoi(parv[2]);
+
+ /* Pass on DESTRUCT message */
+ sendto_highprot_butone(cptr, 10, "%s DESTRUCT %s " TIME_T_FMT,
+ NumServ(sptr), parv[1], chanTS);
+
+ return 0;
+}
+
+/*
+ * m_create
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel names
+ * parv[2] = channel time stamp
+ */
+int m_create(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ char cbuf[BUFSIZE]; /* Buffer for list with channels
+ that `sptr' really creates */
+ time_t chanTS; /* Creation time for all channels
+ in the comma seperated list */
+ char *p, *name;
+ Reg5 aChannel *chptr;
+ int badop;
+
+ /* sanity checks: Only accept CREATE messages from servers */
+ if (!IsServer(cptr) || parc < 3 || *parv[2] == '\0')
+ return 0;
+
+ chanTS = atoi(parv[2]);
+
+ *cbuf = '\0'; /* Start with empty buffer */
+
+ /* For each channel in the comma seperated list: */
+ for (name = strtoken(&p, parv[1], ","); name; name = strtoken(&p, NULL, ","))
+ {
+ badop = 0; /* Default is to accept the op */
+ if ((chptr = FindChannel(name)))
+ {
+ name = chptr->chname;
+ if (TStime() - chanTS > TS_LAG_TIME)
+ {
+ /* A bounce would not be accepted anyway - if we get here something
+ is wrong with the TS clock syncing (or we have more then
+ TS_LAG_TIME lag, or an admin is hacking */
+ badop = 2;
+ /* This causes a HACK notice on all upstream servers: */
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s MODE %s -o %s 0", me.name, name, sptr->name);
+ else
+ sendto_one(cptr, ":%s MODE %s -o %s%s 0",
+ me.name, name, NumNick(sptr));
+ /* This causes a WALLOPS on all downstream servers and a notice to our
+ own opers: */
+ parv[1] = name; /* Corrupt parv[1], it is not used anymore anyway */
+ send_hack_notice(cptr, sptr, parc, parv, badop, 2);
+ }
+ else if (chptr->creationtime && chanTS > chptr->creationtime &&
+ chptr->creationtime != MAGIC_REMOTE_JOIN_TS)
+ {
+ /* We (try) to bounce the mode, because the CREATE is used on an older
+ channel, probably a net.ride */
+ badop = 1;
+ /* Send a deop upstream: */
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s MODE %s -o %s " TIME_T_FMT, me.name,
+ name, sptr->name, chptr->creationtime);
+ else
+ sendto_one(cptr, ":%s MODE %s -o %s%s " TIME_T_FMT, me.name,
+ name, NumNick(sptr), chptr->creationtime);
+ }
+ }
+ else /* Channel doesn't exist: create it */
+ chptr = get_channel(sptr, name, CREATE);
+
+ /* Add and mark ops */
+ add_user_to_channel(chptr, sptr,
+ (badop || IsModelessChannel(name)) ? CHFL_DEOPPED : CHFL_CHANOP);
+
+ /* Send user join to the local clients (if any) */
+ sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name);
+
+ if (badop) /* handle badop: convert CREATE into JOIN */
+ sendto_serv_butone(cptr, ":%s JOIN %s " TIME_T_FMT,
+ sptr->name, name, chptr->creationtime);
+ else
+ {
+ /* Send the op to local clients:
+ (if any; extremely unlikely, but it CAN happen) */
+ if (!IsModelessChannel(name))
+ sendto_channel_butserv(chptr, sptr, ":%s MODE %s +o %s",
+ sptr->user->server->name, name, parv[0]);
+
+ /* Set/correct TS and add the channel to the
+ buffer for accepted channels: */
+ chptr->creationtime = chanTS;
+ if (*cbuf)
+ strcat(cbuf, ",");
+ strcat(cbuf, name);
+ }
+ }
+
+ if (*cbuf) /* Any channel accepted with ops ? */
+ {
+#ifdef NO_PROTOCOL9
+ sendto_serv_butone(cptr, "%s%s CREATE %s " TIME_T_FMT,
+ NumNick(sptr), cbuf, chanTS);
+#else
+ /* send CREATEs to 2.10 servers */
+ sendto_highprot_butone(cptr, 10, "%s%s CREATE %s " TIME_T_FMT,
+ NumNick(sptr), cbuf, chanTS);
+
+ /* And JOIN + MODE to 2.9 servers; following
+ is not needed after all are 2.10 */
+ sendto_lowprot_butone(cptr, 9, ":%s JOIN %s", parv[0], cbuf);
+ p = NULL;
+ for (name = strtoken(&p, cbuf, ","); name; name = strtoken(&p, NULL, ","))
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s +o %s " TIME_T_FMT,
+ sptr->user->server->name, name, parv[0], chanTS);
+#endif
+ }
+
+ return 0;
+}
+
+static size_t prefix_len;
+
+static void add_token_to_sendbuf(char *token, size_t *sblenp, int *firstp,
+ int *send_itp, char is_a_ban, int mode)
+{
+ int first = *firstp;
+
+ /*
+ * Heh - we do not need to test if it still fits in the buffer, because
+ * this BURST message is reconstructed from another BURST message, and
+ * it only can become smaller. --Run
+ */
+
+ if (*firstp) /* First token in this parameter ? */
+ {
+ *firstp = 0;
+ if (*send_itp == 0)
+ *send_itp = 1; /* Buffer contains data to be sent */
+ sendbuf[(*sblenp)++] = ' ';
+ if (is_a_ban)
+ {
+ sendbuf[(*sblenp)++] = ':'; /* Bans are always the last "parv" */
+ sendbuf[(*sblenp)++] = is_a_ban;
+ }
+ }
+ else /* Of course, 'send_it' is already set here */
+ /* Seperate banmasks with a space because
+ they can contain commas themselfs: */
+ sendbuf[(*sblenp)++] = is_a_ban ? ' ' : ',';
+ strcpy(sendbuf + *sblenp, token);
+ *sblenp += strlen(token);
+ if (!is_a_ban) /* nick list ? Need to take care
+ of modes for nicks: */
+ {
+ static int last_mode = 0;
+ mode &= CHFL_CHANOP | CHFL_VOICE;
+ if (first)
+ last_mode = 0;
+ if (last_mode != mode) /* Append mode like ':ov' if changed */
+ {
+ last_mode = mode;
+ sendbuf[(*sblenp)++] = ':';
+ if (mode & CHFL_CHANOP)
+ sendbuf[(*sblenp)++] = 'o';
+ if (mode & CHFL_VOICE)
+ sendbuf[(*sblenp)++] = 'v';
+ }
+ sendbuf[*sblenp] = '\0';
+ }
+}
+
+static void cancel_mode(aClient *sptr, aChannel *chptr, char m,
+ const char *param, int *count)
+{
+ static char *pb, *sbp, *sbpi;
+ int paramdoesntfit = 0;
+ if (*count == -1) /* initialize ? */
+ {
+ sbp = sbpi =
+ sprintf_irc(sendbuf, ":%s MODE %s -", sptr->name, chptr->chname);
+ pb = parabuf;
+ *count = 0;
+ }
+ /* m == 0 means flush */
+ if (m)
+ {
+ if (param)
+ {
+ size_t nplen = strlen(param);
+ if (pb - parabuf + nplen + 23 > MODEBUFLEN)
+ paramdoesntfit = 1;
+ else
+ {
+ *sbp++ = m;
+ *pb++ = ' ';
+ strcpy(pb, param);
+ pb += nplen;
+ ++*count;
+ }
+ }
+ else
+ *sbp++ = m;
+ }
+ else if (*count == 0)
+ return;
+ if (*count == 6 || !m || paramdoesntfit)
+ {
+#ifndef NO_PROTOCOL9
+ Dlink *lp;
+ char *sbe;
+#endif
+ Link *member;
+ strcpy(sbp, parabuf);
+#ifndef NO_PROTOCOL9
+ sbe = sbp + strlen(parabuf);
+#endif
+ for (member = chptr->members; member; member = member->next)
+ if (MyUser(member->value.cptr))
+ sendbufto_one(member->value.cptr);
+#ifndef NO_PROTOCOL9
+ sprintf_irc(sbe, " " TIME_T_FMT, chptr->creationtime);
+ /* Send 'sendbuf' to all 2.9 downlinks: */
+ for (lp = me.serv->down; lp; lp = lp->next)
+ if (Protocol(lp->value.cptr) < 10)
+ sendbufto_one(lp->value.cptr);
+#endif
+ sbp = sbpi;
+ pb = parabuf;
+ *count = 0;
+ }
+ if (paramdoesntfit)
+ {
+ *sbp++ = m;
+ *pb++ = ' ';
+ strcpy(pb, param);
+ pb += strlen(param);
+ ++*count;
+ }
+}
+
+/*
+ * m_burst -- by Run carlo@runaway.xs4all.nl december 1995 till march 1997
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel name
+ * parv[2] = channel timestamp
+ * The meaning of the following parv[]'s depend on their first character:
+ * If parv[n] starts with a '+':
+ * Net burst, additive modes
+ * parv[n] = <mode>
+ * parv[n+1] = <param> (optional)
+ * parv[n+2] = <param> (optional)
+ * If parv[n] starts with a '%', then n will be parc-1:
+ * parv[n] = %<ban> <ban> <ban> ...
+ * If parv[n] starts with another character:
+ * parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
+ * where <mode> is the channel mode (ov) of nick and all following nicks.
+ *
+ * Example:
+ * "S BURST #channel 87654321 +ntkl key 123 AAA,AAB:o,BAA,BAB:ov :%ban1 ban2"
+ *
+ * Anti net.ride code.
+ *
+ * When the channel already exist, and its TS is larger then
+ * the TS in the BURST message, then we cancel all existing modes.
+ * If its is smaller then the received BURST message is ignored.
+ * If it's equal, then the received modes are just added.
+ */
+int m_burst(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 aChannel *chptr;
+ time_t timestamp;
+ int netride = 0, wipeout = 0, n;
+ int send_it = 0, add_banid_not_called = 1;
+ Mode *current_mode;
+ size_t sblen, mblen = 0;
+ int mblen2, pblen2, cnt;
+ int prev_mode;
+ char prev_key[KEYLEN + 1];
+ Link *lp;
+#ifndef NO_PROTOCOL9
+ int ts_sent = 0;
+#endif
+
+ /* BURST is only for servers and has at least 4 parameters */
+ if (!IsServer(cptr) || parc < 4)
+ return 0;
+
+ if (!IsBurst(sptr))
+ {
+ int i;
+ char *p;
+ if (find_conf_host(cptr->confs, sptr->name, CONF_UWORLD))
+ {
+ p =
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- HACK(4): %s BURST %s %s", me.name,
+ sptr->name, parv[1], parv[2]);
+ for (i = 3; i < parc - 1; ++i)
+ p = sprintf_irc(p, " %s", parv[i]);
+ sprintf_irc(p, " :%s", parv[parc - 1]);
+ sendbufto_op_mask(SNO_HACK4);
+ }
+ else
+ {
+#if 1 /* FIXME: This should be removed after all HUBs upgraded to ircu2.10.05 */
+ SetBurst(sptr);
+ if (MyConnect(sptr))
+#endif
+ return exit_client_msg(cptr, cptr, &me,
+ "HACK: BURST message outside net.burst from %s", sptr->name);
+ }
+ }
+
+ /* Find the channel, or create it - note that the creation time
+ * will be 0 if it has to be created */
+ chptr = get_channel(sptr, parv[1], CREATE);
+ current_mode = &chptr->mode;
+ prev_mode = chptr->mode.mode;
+ if (*chptr->mode.key)
+ {
+ prev_mode |= MODE_KEY;
+ strcpy(prev_key, chptr->mode.key);
+ }
+ if (chptr->mode.limit)
+ prev_mode |= MODE_LIMIT;
+
+ timestamp = atoi(parv[2]);
+
+ /* Copy the new TS when the received creationtime appears to be older */
+ if (!chptr->creationtime || chptr->creationtime > timestamp)
+ {
+ /* Set the new timestamp */
+ chptr->creationtime = timestamp;
+ send_it = 1; /* Make sure we pass on the different timestamp ! */
+ /* Mark all bans as needed to be wiped out */
+ for (lp = chptr->banlist; lp; lp = lp->next)
+ lp->flags |= CHFL_BURST_BAN_WIPEOUT;
+ /*
+ * Only the first BURST for this channel can have creationtime > timestamp,
+ * so at this moment ALL members are on OUR side, and thus all net.riders:
+ */
+ wipeout = 1;
+ }
+ for (lp = chptr->members; lp; lp = lp->next)
+ lp->flags &= ~CHFL_BURST_JOINED; /* Set later for nicks in the BURST msg */
+ /* If `wipeout' is set then these will be deopped later. */
+
+ /* If the entering creationtime is younger, ignore the modes */
+ if (chptr->creationtime < timestamp)
+ netride = 1; /* Only pass on the nicks (so they JOIN) */
+
+ /* Prepare buffers to pass the message */
+ *bparambuf = *bmodebuf = *parabuf = '\0';
+ pblen2 = 0;
+ *modebuf = '+';
+ mblen2 = 1;
+ cnt = 0;
+ prefix_len = sblen = sprintf_irc(sendbuf, "%s BURST %s " TIME_T_FMT,
+ NumServ(sptr), chptr->chname, chptr->creationtime) - sendbuf;
+
+ /* Run over all remaining parameters */
+ for (n = 3; n < parc; n++)
+ switch (*parv[n]) /* What type is it ? mode, nicks or bans ? */
+ {
+ case '+': /* modes */
+ {
+ char *p = parv[n];
+ while (*(++p)) /* Run over all mode characters */
+ {
+ switch (*p) /* which mode ? */
+ {
+ /*
+ * The following cases all do the following:
+ * - In case wipeout needed, reset 'prev_mode' to indicate this
+ * mode should not be cancelled.
+ * - If wipeout or (not netride and the new mode is a change),
+ * add it to bmodebuf and bparabuf for propagation.
+ * - Else ignore it.
+ * - Add it to modebuf and parabuf for propagation to the
+ * clients when not netride and the new mode is a change.
+ * Special cases:
+ * - If a +s is received, cancel a +p and sent a -p to the
+ * clients too (if +p was set).
+ * - If a +p is received and +s is set, ignore the +p.
+ */
+ case 'i':
+ {
+ register int tmp;
+ prev_mode &= ~MODE_INVITEONLY;
+ if (!(tmp = netride ||
+ (current_mode->mode & MODE_INVITEONLY)) || wipeout)
+ {
+ bmodebuf[mblen++] = 'i';
+ current_mode->mode |= MODE_INVITEONLY;
+ }
+ if (!tmp)
+ modebuf[mblen2++] = 'i';
+ break;
+ }
+ case 'k':
+ {
+ register int tmp;
+ char *param = parv[++n];
+ prev_mode &= ~MODE_KEY;
+ if (!(tmp = netride || (*current_mode->key &&
+ (!strcmp(current_mode->key, param) ||
+ (!wipeout && strcmp(current_mode->key, param) < 0)))) ||
+ wipeout)
+ {
+ bmodebuf[mblen++] = 'k';
+ strcat(bparambuf, " ");
+ strcat(bparambuf, param);
+ strncpy(current_mode->key, param, KEYLEN);
+ }
+ if (!tmp && !wipeout)
+ {
+ modebuf[mblen2++] = 'k';
+ parabuf[pblen2++] = ' ';
+ strcpy(parabuf + pblen2, param);
+ pblen2 += strlen(param);
+ cnt++;
+ }
+ break;
+ }
+ case 'l':
+ {
+ register int tmp;
+ unsigned int param = atoi(parv[++n]);
+ prev_mode &= ~MODE_LIMIT;
+ if (!(tmp = netride || (current_mode->limit &&
+ (current_mode->limit == param ||
+ (!wipeout && current_mode->limit < param)))) || wipeout)
+ {
+ bmodebuf[mblen++] = 'l';
+ sprintf_irc(bparambuf + strlen(bparambuf), " %d", param);
+ current_mode->limit = param;
+ }
+ if (!tmp)
+ {
+ modebuf[mblen2++] = 'l';
+ pblen2 = sprintf_irc(parabuf + pblen2, " %d", param) - parabuf;
+ cnt++;
+ }
+ break;
+ }
+ case 'm':
+ {
+ register int tmp;
+ prev_mode &= ~MODE_MODERATED;
+ if (!(tmp = netride ||
+ (current_mode->mode & MODE_MODERATED)) || wipeout)
+ {
+ bmodebuf[mblen++] = 'm';
+ current_mode->mode |= MODE_MODERATED;
+ }
+ if (!tmp)
+ modebuf[mblen2++] = 'm';
+ break;
+ }
+ case 'n':
+ {
+ register int tmp;
+ prev_mode &= ~MODE_NOPRIVMSGS;
+ if (!(tmp = netride ||
+ (current_mode->mode & MODE_NOPRIVMSGS)) || wipeout)
+ {
+ bmodebuf[mblen++] = 'n';
+ current_mode->mode |= MODE_NOPRIVMSGS;
+ }
+ if (!tmp)
+ modebuf[mblen2++] = 'n';
+ break;
+ }
+ case 'p':
+ {
+ register int tmp;
+
+ /* Special case: */
+ if (!netride && !wipeout && (current_mode->mode & MODE_SECRET))
+ break;
+
+ prev_mode &= ~MODE_PRIVATE;
+ if (!(tmp = netride ||
+ (current_mode->mode & MODE_PRIVATE)) || wipeout)
+ {
+ bmodebuf[mblen++] = 'p';
+ current_mode->mode |= MODE_PRIVATE;
+ }
+ if (!tmp)
+ modebuf[mblen2++] = 'p';
+ break;
+ }
+ case 's':
+ {
+ register int tmp;
+ prev_mode &= ~MODE_SECRET;
+ if (!(tmp = netride ||
+ (current_mode->mode & MODE_SECRET)) || wipeout)
+ {
+ bmodebuf[mblen++] = 's';
+ current_mode->mode |= MODE_SECRET;
+ }
+ if (!tmp)
+ modebuf[mblen2++] = 's';
+
+ /* Special case: */
+ if (!netride && !wipeout && (current_mode->mode & MODE_PRIVATE))
+ {
+ int i;
+ for (i = mblen2 - 1; i >= 0; --i)
+ modebuf[i + 2] = modebuf[i];
+ modebuf[0] = '-';
+ modebuf[1] = 'p';
+ mblen2 += 2;
+ current_mode->mode &= ~MODE_PRIVATE;
+ }
+
+ break;
+ }
+ case 't':
+ {
+ register int tmp;
+ prev_mode &= ~MODE_TOPICLIMIT;
+ if (!(tmp = netride ||
+ (current_mode->mode & MODE_TOPICLIMIT)) || wipeout)
+ {
+ bmodebuf[mblen++] = 't';
+ current_mode->mode |= MODE_TOPICLIMIT;
+ }
+ if (!tmp)
+ modebuf[mblen2++] = 't';
+ break;
+ }
+ }
+ } /* <-- while over all modes */
+
+ bmodebuf[mblen] = '\0';
+ sendbuf[sblen] = '\0';
+ if (mblen) /* Anything to send at all ? */
+ {
+ send_it = 1;
+ strcpy(sendbuf + sblen, " +");
+ sblen += 2;
+ strcpy(sendbuf + sblen, bmodebuf);
+ sblen += mblen;
+ strcpy(sendbuf + sblen, bparambuf);
+ sblen += strlen(bparambuf);
+ }
+ break; /* Done mode part */
+ }
+ case '%': /* bans */
+ {
+ char *pv, *p = NULL, *ban;
+ int first = 1;
+ if (netride)
+ break; /* Ignore bans */
+ /* Run over all bans */
+ for (pv = parv[n] + 1; (ban = strtoken(&p, pv, " ")); pv = NULL)
+ {
+ int ret;
+ /*
+ * The following part should do the following:
+ * - If the new (un)ban is not a _change_ it is ignored.
+ * - Else, add it to sendbuf for later use.
+ * - If sendbuf is full, send it, and prepare a new
+ * message in sendbuf.
+ */
+ ret = add_banid(sptr, chptr, ban, 1, add_banid_not_called);
+ if (ret == 0)
+ {
+ add_banid_not_called = 0;
+ /* Mark this new ban so we can send it to the clients later */
+ chptr->banlist->flags |= CHFL_BURST_BAN;
+ }
+ if (ret != -1)
+ /* A new ban was added or an existing one needs to be passed on.
+ * Also add it to sendbuf: */
+ add_token_to_sendbuf(ban, &sblen, &first, &send_it, '%', 0);
+ }
+ break; /* Done bans part */
+ }
+ default: /* nicks */
+ {
+ char *pv, *p = NULL, *nick, *ptr;
+ int first = 1;
+ /* Default mode: */
+ int default_mode = CHFL_DEOPPED;
+ /* Run over all nicks */
+ for (pv = parv[n]; (nick = strtoken(&p, pv, ",")); pv = NULL)
+ {
+ aClient *acptr;
+ if ((ptr = strchr(nick, ':'))) /* New default mode ? */
+ {
+ *ptr = '\0'; /* Fix 'nick' */
+ acptr = findNUser(nick);
+ if (!netride)
+ {
+ /* Calculate new mode change: */
+ default_mode = CHFL_DEOPPED;
+ while (*(++ptr))
+ if (*ptr == 'o')
+ {
+ default_mode |= CHFL_CHANOP;
+ default_mode &= ~CHFL_DEOPPED;
+ }
+ else if (*ptr == 'v')
+ default_mode |= CHFL_VOICE;
+ else
+ break;
+ }
+ }
+ else
+ acptr = findNUser(nick);
+ /*
+ * Note that at this point we already received a 'NICK' for any
+ * <nick> numeric that is joining (and possibly opped) here.
+ * Therefore we consider the following situations:
+ * - The <nick> numeric exists and is from the direction of cptr: ok
+ * - The <nick> numeric does not exist:
+ * Apparently this previous <nick> numeric was killed (upstream)
+ * or it collided with an existing <nick> name.
+ * - The <nick> numeric exists but is from another direction:
+ * Apparently this previous <nick> numeric was killed,
+ * and due to a reroute it signed on via another link (probably
+ * a nick [numeric] collision).
+ * Note that it can't be a QUIT or SQUIT, because a QUIT would
+ * come from the same direction as the BURST (cptr) while an
+ * upstream SQUIT removes the source (server) and we would thus
+ * have this BURST ignored already.
+ * This means that if we find the nick and it is from the correct
+ * direction, it joins. If it doesn't exist or is from another
+ * direction, we have to ignore it. If all nicks are ignored, we
+ * remove the channel again when it is empty and don't propagate
+ * the BURST message.
+ */
+ if (acptr && acptr->from == cptr)
+ {
+ /*
+ * The following should do the following:
+ * - Add it to sendbuf for later use.
+ * - If sendbuf is full, send it, and prepare a new
+ * message in sendbuf.
+ */
+ add_token_to_sendbuf(nick, &sblen, &first, &send_it, 0,
+ default_mode);
+ /* Let is take effect: (Note that in the case of a netride
+ * 'default_mode' is always CHFL_DEOPPED here). */
+ add_user_to_channel(chptr, acptr, default_mode);
+ chptr->members->flags |= CHFL_BURST_JOINED;
+ }
+ } /* <-- Next nick */
+ if (!chptr->members) /* All nicks collided and channel is empty ? */
+ {
+ sub1_from_channel(chptr);
+ return 0; /* Forget about the (rest of the) message... */
+ }
+ break; /* Done nicks part */
+ }
+ } /* <-- Next parameter if any */
+ if (!chptr->members) /* This message only contained bans (then the previous
+ message only contained collided nicks, see above) */
+ {
+ sub1_from_channel(chptr);
+ if (!add_banid_not_called)
+ while (next_removed_overlapped_ban());
+ return 0; /* Forget about the (rest of the) message... */
+ }
+
+ /* The last (possibly only) message is always send here */
+ if (send_it) /* Anything (left) to send ? */
+ {
+ Dlink *lp;
+ Link *member;
+
+ /* send 'sendbuf' to all downlinks */
+ for (lp = me.serv->down; lp; lp = lp->next)
+ {
+ if (lp->value.cptr == cptr)
+ continue;
+ if (Protocol(lp->value.cptr) > 9)
+ sendbufto_one(lp->value.cptr);
+ }
+
+ /*
+ * Now we finally can screw sendbuf again...
+ * Send all changes to the local clients:
+ *
+ * First send all joins and op them, because 2.9 servers
+ * would protest with a HACK if we first de-opped people.
+ * However, we don't send the +b bans yes, because we
+ * DO first want to -b the old bans (otherwise it's confusing).
+ */
+
+ /* Send all joins: */
+ for (member = chptr->members; member; member = member->next)
+ if (member->flags & CHFL_BURST_JOINED)
+ {
+ sendto_channel_butserv(chptr, member->value.cptr, ":%s JOIN :%s",
+ member->value.cptr->name, chptr->chname);
+#ifndef NO_PROTOCOL9
+ /* And to 2.9 servers: */
+ sendto_lowprot_butone(cptr, 9, ":%s JOIN %s",
+ member->value.cptr->name, chptr->chname);
+#endif
+ }
+
+ if (!netride)
+ {
+ /* Send all +o and +v modes: */
+ for (member = chptr->members; member; member = member->next)
+ {
+ if ((member->flags & CHFL_BURST_JOINED))
+ {
+ int mode = CHFL_CHANOP;
+ for (;;)
+ {
+ if ((member->flags & mode))
+ {
+ modebuf[mblen2++] = (mode == CHFL_CHANOP) ? 'o' : 'v';
+ parabuf[pblen2++] = ' ';
+ strcpy(parabuf + pblen2, member->value.cptr->name);
+ pblen2 += strlen(member->value.cptr->name);
+ if (6 == ++cnt)
+ {
+ modebuf[mblen2] = 0;
+ sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+ parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s " TIME_T_FMT,
+ parv[0], chptr->chname, modebuf, parabuf,
+ chptr->creationtime);
+ ts_sent = 1;
+#endif
+ *parabuf = 0;
+ pblen2 = 0;
+ mblen2 = 1;
+ cnt = 0;
+ }
+ }
+ if (mode == CHFL_CHANOP)
+ mode = CHFL_VOICE;
+ else
+ break;
+ }
+ }
+ }
+ /* Flush MODEs: */
+ if (cnt > 0 || mblen2 > 1)
+ {
+ modebuf[mblen2] = 0;
+ sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+ parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s " TIME_T_FMT,
+ parv[0], chptr->chname, modebuf, parabuf, chptr->creationtime);
+ ts_sent = 1;
+#endif
+ }
+#ifndef NO_PROTOCOL9
+ else if (send_it && !ts_sent)
+ {
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s + " TIME_T_FMT,
+ parv[0], chptr->chname, chptr->creationtime);
+ ts_sent = 1;
+ }
+#endif
+ }
+ }
+
+ if (wipeout)
+ {
+ Link *lp;
+ Link **ban;
+ int mode;
+ char m;
+ int count = -1;
+
+ /* Now cancel all previous simple modes */
+ if ((prev_mode & MODE_SECRET))
+ cancel_mode(sptr, chptr, 's', NULL, &count);
+ if ((prev_mode & MODE_PRIVATE))
+ cancel_mode(sptr, chptr, 'p', NULL, &count);
+ if ((prev_mode & MODE_MODERATED))
+ cancel_mode(sptr, chptr, 'm', NULL, &count);
+ if ((prev_mode & MODE_TOPICLIMIT))
+ cancel_mode(sptr, chptr, 't', NULL, &count);
+ if ((prev_mode & MODE_INVITEONLY))
+ cancel_mode(sptr, chptr, 'i', NULL, &count);
+ if ((prev_mode & MODE_NOPRIVMSGS))
+ cancel_mode(sptr, chptr, 'n', NULL, &count);
+ if ((prev_mode & MODE_LIMIT))
+ {
+ current_mode->limit = 0;
+ cancel_mode(sptr, chptr, 'l', NULL, &count);
+ }
+ if ((prev_mode & MODE_KEY))
+ {
+ *current_mode->key = 0;
+ cancel_mode(sptr, chptr, 'k', prev_key, &count);
+ }
+ current_mode->mode &= ~prev_mode;
+
+ /* And deop and devoice all net.riders on my side */
+ mode = CHFL_CHANOP;
+ m = 'o';
+ for (;;)
+ {
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ if ((lp->flags & CHFL_BURST_JOINED))
+ continue; /* This is not a net.rider from
+ this side of the net.junction */
+ if ((lp->flags & mode))
+ {
+ lp->flags &= ~mode;
+ if (mode == CHFL_CHANOP)
+ lp->flags |= CHFL_DEOPPED;
+ cancel_mode(sptr, chptr, m, lp->value.cptr->name, &count);
+ }
+ }
+ if (mode == CHFL_VOICE)
+ break;
+ mode = CHFL_VOICE;
+ m = 'v';
+ }
+
+ /* And finally wipeout all bans that are left */
+ for (ban = &chptr->banlist; *ban;)
+ {
+ Link *tmp = *ban;
+ if ((tmp->flags & CHFL_BURST_BAN_WIPEOUT))
+ {
+ cancel_mode(sptr, chptr, 'b', tmp->value.ban.banstr, &count);
+ /* Copied from del_banid(): */
+ *ban = tmp->next;
+ RunFree(tmp->value.ban.banstr);
+ RunFree(tmp->value.ban.who);
+ free_link(tmp);
+ /* Erase ban-valid-bit, for channel members that are banned */
+ for (tmp = chptr->members; tmp; tmp = tmp->next)
+ if ((tmp->flags & (CHFL_BANNED | CHFL_BANVALID)) ==
+ (CHFL_BANNED | CHFL_BANVALID))
+ tmp->flags &= ~CHFL_BANVALID; /* `tmp' == channel member */
+ }
+ else
+ ban = &tmp->next;
+ }
+ /* Also wipeout overlapped bans */
+ if (!add_banid_not_called)
+ {
+ Link *ban;
+ while ((ban = next_removed_overlapped_ban()))
+ cancel_mode(sptr, chptr, 'b', ban->value.ban.banstr, &count);
+ }
+ cancel_mode(sptr, chptr, 0, NULL, &count); /* flush */
+ }
+
+ if (send_it && !netride)
+ {
+ Link *bl;
+ int deban;
+
+ if (add_banid_not_called || !(bl = next_removed_overlapped_ban()))
+ {
+ deban = 0;
+ bl = chptr->banlist;
+ *modebuf = '+';
+ }
+ else
+ {
+ deban = 1;
+ *modebuf = '-';
+ }
+
+ mblen2 = 1;
+ pblen2 = 0;
+ cnt = 0;
+ for (;;)
+ {
+ size_t nblen = 0;
+ if (bl)
+ nblen = strlen(bl->value.ban.banstr);
+ if (cnt == 6 || (!bl && cnt) || pblen2 + nblen + 12 > MODEBUFLEN) /* The last check is to make sure
+ that the receiving 2.9 will
+ still process this */
+ {
+ /* Time to send buffer */
+ modebuf[mblen2] = 0;
+ sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+ parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s",
+ parv[0], chptr->chname, modebuf, parabuf);
+#endif
+ *modebuf = deban ? '-' : '+';
+ mblen2 = 1;
+ pblen2 = 0;
+ cnt = 0;
+ }
+ if (!bl) /* Done ? */
+ break;
+ if (deban || (bl->flags & CHFL_BURST_BAN))
+ {
+ /* Add ban to buffers and remove it */
+ modebuf[mblen2++] = 'b';
+ parabuf[pblen2++] = ' ';
+ strcpy(parabuf + pblen2, bl->value.ban.banstr);
+ pblen2 += nblen;
+ cnt++;
+ bl->flags &= ~CHFL_BURST_BAN;
+ }
+ if (deban)
+ {
+ if (!(bl = next_removed_overlapped_ban()))
+ {
+ deban = 0;
+ modebuf[mblen2++] = '+';
+ bl = chptr->banlist;
+ }
+ }
+ else
+ bl = bl->next;
+ }
+ /* Flush MODE [-b]+b ...: */
+ if (cnt > 0 || mblen2 > 1)
+ {
+ modebuf[mblen2] = 0;
+ sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+ parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s " TIME_T_FMT,
+ parv[0], chptr->chname, modebuf, parabuf, chptr->creationtime);
+#endif
+ }
+#ifndef NO_PROTOCOL9
+ else if (send_it && !ts_sent)
+ sendto_lowprot_butone(cptr, 9, ":%s MODE %s + " TIME_T_FMT,
+ parv[0], chptr->chname, chptr->creationtime);
+#endif
+ }
+
+ return 0;
+}
+
+/*
+ * m_part
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[parc - 1] = comment
+ */
+int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 aChannel *chptr;
+ Reg2 Link *lp;
+ char *p = NULL, *name, pbuf[BUFSIZE];
+ char *comment = (parc > 2 && !BadPtr(parv[parc - 1])) ? parv[parc - 1] : NULL;
+
+ *pbuf = '\0'; /* Initialize the part buffer... -Kev */
+
+ sptr->flags &= ~FLAGS_TS8;
+
+ if (parc < 2 || parv[1][0] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PART");
+ return 0;
+ }
+
+ for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ chptr = get_channel(sptr, name, !CREATE);
+ if (!chptr)
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name);
+ continue;
+ }
+ if (*name == '&' && !MyUser(sptr))
+ continue;
+ /* Do not use IsMember here: zombies must be able to part too */
+ if (!(lp = find_user_link(chptr->members, sptr)))
+ {
+ /* Normal to get when our client did a kick
+ for a remote client (who sends back a PART),
+ so check for remote client or not --Run */
+ if (MyUser(sptr))
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
+ chptr->chname);
+ continue;
+ }
+ /* Recreate the /part list for sending to servers */
+ if (*name != '&')
+ {
+ if (*pbuf)
+ strcat(pbuf, ",");
+ strcat(pbuf, name);
+ }
+ if (can_send(sptr, chptr) != 0) /* Returns 0 if we CAN send */
+ comment = NULL;
+ /* Send part to all clients */
+ if (!(lp->flags & CHFL_ZOMBIE))
+ {
+ if (comment)
+ sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], chptr->chname,
+ comment);
+ else
+ sendto_channel_butserv(chptr, sptr, PartFmt1, parv[0], chptr->chname);
+ }
+ else if (MyUser(sptr))
+ {
+ if (comment)
+ sendto_one(sptr, PartFmt2, parv[0], chptr->chname, comment);
+ else
+ sendto_one(sptr, PartFmt1, parv[0], chptr->chname);
+ }
+ remove_user_from_channel(sptr, chptr);
+ }
+ /* Send out the parts to all servers... -Kev */
+ if (*pbuf)
+ {
+ if (comment)
+ sendto_serv_butone(cptr, PartFmt2, parv[0], pbuf, comment);
+ else
+ sendto_serv_butone(cptr, PartFmt1, parv[0], pbuf);
+ }
+ return 0;
+}
+
+/*
+ * m_kick
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[2] = client to kick
+ * parv[parc-1] = kick comment
+ */
+int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aClient *who;
+ aChannel *chptr;
+ char *comment;
+ Link *lp, *lp2;
+
+ sptr->flags &= ~FLAGS_TS8;
+
+ if (parc < 3 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KICK");
+ return 0;
+ }
+
+ if (IsServer(sptr))
+ send_hack_notice(cptr, sptr, parc, parv, 1, 3);
+
+ comment = (BadPtr(parv[parc - 1])) ? parv[0] : parv[parc - 1];
+ if (strlen(comment) > (size_t)TOPICLEN)
+ comment[TOPICLEN] = '\0';
+
+ *nickbuf = *buf = '\0';
+
+ chptr = get_channel(sptr, parv[1], !CREATE);
+ if (!chptr)
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]);
+ return 0;
+ }
+ if (IsLocalChannel(parv[1]) && !MyUser(sptr))
+ return 0;
+ if (IsModelessChannel(parv[1]))
+ {
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
+ chptr->chname);
+ return 0;
+ }
+ if (!IsServer(cptr) && !is_chan_op(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+ me.name, parv[0], chptr->chname);
+ return 0;
+ }
+
+ lp2 = find_user_link(chptr->members, sptr);
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ {
+ if (!(who = find_chasing(sptr, parv[2], NULL)))
+ return 0; /* No such user left! */
+ }
+ else if (!(who = findNUser(parv[2])))
+ return 0; /* No such user left! */
+ /* if the user is +k, prevent a kick from local user */
+ if (IsChannelService(who) && MyUser(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name,
+ parv[0], who->name, chptr->chname);
+ return 0;
+ }
+ if (((lp = find_user_link(chptr->members, who)) &&
+ !(lp->flags & CHFL_ZOMBIE)) || IsServer(sptr))
+ {
+ if (who->from != cptr &&
+ ((lp2 && (lp2->flags & CHFL_DEOPPED)) || (!lp2 && IsUser(sptr))))
+ {
+ /*
+ * Bounce here:
+ * cptr must be a server (or cptr == sptr and
+ * sptr->flags can't have DEOPPED set
+ * when CHANOP is set).
+ */
+ sendto_one(cptr, ":%s JOIN %s", who->name, parv[1]);
+ if (lp->flags & CHFL_CHANOP)
+ {
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s MODE %s +o %s " TIME_T_FMT,
+ me.name, parv[1], who->name, chptr->creationtime);
+ else
+ sendto_one(cptr, "%s MODE %s +o %s%s " TIME_T_FMT,
+ NumServ(&me), parv[1], NumNick(who), chptr->creationtime);
+ }
+ if (lp->flags & CHFL_VOICE)
+ {
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s MODE %s +v %s " TIME_T_FMT,
+ me.name, chptr->chname, who->name, chptr->creationtime);
+ else
+ sendto_one(cptr, "%s MODE %s +v %s%s " TIME_T_FMT,
+ NumServ(&me), parv[1], NumNick(who), chptr->creationtime);
+ }
+ }
+ else
+ {
+ if (lp)
+ sendto_channel_butserv(chptr, sptr,
+ ":%s KICK %s %s :%s", parv[0], chptr->chname, who->name, comment);
+ if (!IsLocalChannel(parv[1]))
+ {
+ sendto_lowprot_butone(cptr, 9, ":%s KICK %s %s :%s",
+ parv[0], chptr->chname, who->name, comment);
+ sendto_highprot_butone(cptr, 10, ":%s KICK %s %s%s :%s",
+ parv[0], parv[1], NumNick(who), comment);
+ }
+ if (lp)
+ {
+/*
+ * Consider:
+ *
+ * client
+ * |
+ * c
+ * |
+ * X --a--> A --b--> B --d--> D
+ * |
+ * who
+ *
+ * Where `who' is being KICK-ed by a "KICK" message received by server 'A'
+ * via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
+ *
+ * a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
+ * Remove the user immedeately when no users are left on the channel.
+ * b) On server B : remove the user (who/lp) from the channel, send a
+ * PART upstream (to A) and pass on the KICK.
+ * c) KICKed by `client'; On server B : remove the user (who/lp) from the
+ * channel, and pass on the KICK.
+ * d) On server D : remove the user (who/lp) from the channel, and pass on
+ * the KICK.
+ *
+ * Note:
+ * - Setting the ZOMBIE flag never hurts, we either remove the
+ * client after that or we don't.
+ * - The KICK message was already passed on, as should be in all cases.
+ * - `who' is removed in all cases except case a) when users are left.
+ * - A PART is only sent upstream in case b).
+ *
+ * 2 aug 97:
+ *
+ * 6
+ * |
+ * 1 --- 2 --- 3 --- 4 --- 5
+ * | |
+ * kicker who
+ *
+ * We also need to turn 'who' into a zombie on servers 1 and 6,
+ * because a KICK from 'who' (kicking someone else in that direction)
+ * can arrive there afterwards - which should not be bounced itself.
+ * Therefore case a) also applies for servers 1 and 6.
+ *
+ * --Run
+ */
+ /* Default for case a): */
+ lp->flags |= CHFL_ZOMBIE;
+ /* Case b) or c) ?: */
+ if (MyUser(who)) /* server 4 */
+ {
+ if (IsServer(cptr)) /* Case b) ? */
+ sendto_one(cptr, PartFmt1, who->name, parv[1]);
+ remove_user_from_channel(who, chptr);
+ return 0;
+ }
+ if (who->from == cptr) /* True on servers 1, 5 and 6 */
+ {
+ aClient *acptr = IsServer(sptr) ? sptr : sptr->user->server;
+ for (; acptr != &me; acptr = acptr->serv->up)
+ if (acptr == who->user->server) /* Case d) (server 5) */
+ {
+ remove_user_from_channel(who, chptr);
+ return 0;
+ }
+ }
+ /* Case a) (servers 1, 2, 3 and 6) */
+ for (lp = chptr->members; lp; lp = lp->next)
+ if (!(lp->flags & CHFL_ZOMBIE))
+ break;
+ if (!lp)
+ remove_user_from_channel(who, chptr);
+#ifdef GODMODE
+ else
+ sendto_op_mask(SNO_HACK2, "%s is now a zombie on %s",
+ who->name, chptr->chname);
+#endif
+ }
+ }
+ }
+ else if (MyUser(sptr))
+ sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
+ me.name, parv[0], who->name, chptr->chname);
+
+ return 0;
+}
+
+/*
+ * m_topic
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[parc - 1] = topic (if parc > 2)
+ */
+int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aChannel *chptr;
+ char *topic = NULL, *name, *p = NULL;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "TOPIC");
+ return 0;
+ }
+
+ if (parc > 2)
+ topic = parv[parc - 1];
+
+ for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ chptr = NULL;
+ if (!IsChannelName(name) || !(chptr = FindChannel(name)) ||
+ ((topic || SecretChannel(chptr)) && !IsMember(sptr, chptr)))
+ {
+ sendto_one(sptr, err_str(chptr ? ERR_NOTONCHANNEL : ERR_NOSUCHCHANNEL),
+ me.name, parv[0], chptr ? chptr->chname : name);
+ continue;
+ }
+ if (IsModelessChannel(name))
+ {
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
+ chptr->chname);
+ continue;
+ }
+ if (IsLocalChannel(name) && !MyUser(sptr))
+ continue;
+
+ if (!topic) /* only asking for topic */
+ {
+ if (chptr->topic[0] == '\0')
+ sendto_one(sptr, rpl_str(RPL_NOTOPIC), me.name, parv[0], chptr->chname);
+ else
+ {
+ sendto_one(sptr, rpl_str(RPL_TOPIC),
+ me.name, parv[0], chptr->chname, chptr->topic);
+ sendto_one(sptr, rpl_str(RPL_TOPICWHOTIME),
+ me.name, parv[0], chptr->chname,
+ chptr->topic_nick, chptr->topic_time);
+ }
+ }
+ else if (((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
+ is_chan_op(sptr, chptr)) && topic)
+ {
+ /* setting a topic */
+ strncpy(chptr->topic, topic, TOPICLEN);
+ strncpy(chptr->topic_nick, sptr->name, NICKLEN);
+ chptr->topic_time = now;
+ sendto_serv_butone(cptr, ":%s TOPIC %s :%s",
+ parv[0], chptr->chname, chptr->topic);
+ sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s",
+ parv[0], chptr->chname, chptr->topic);
+ }
+ else
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+ me.name, parv[0], chptr->chname);
+ }
+ return 0;
+}
+
+/*
+ * m_invite
+ * parv[0] - sender prefix
+ * parv[1] - user to invite
+ * parv[2] - channel name
+ *
+ * - INVITE now is accepted only if who does it is chanop (this of course
+ * implies that channel must exist and he must be on it).
+ *
+ * - On the other side it IS processed even if channel is NOT invite only
+ * leaving room for other enhancements like inviting banned ppl. -- Nemesi
+ *
+ */
+int m_invite(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr;
+ aChannel *chptr;
+
+ if (parc < 3 || *parv[2] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "INVITE");
+ return 0;
+ }
+
+ if (!(acptr = FindUser(parv[1])))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
+ return 0;
+ }
+
+ if (is_silenced(sptr, acptr))
+ return 0;
+
+ if (MyUser(sptr))
+ clean_channelname(parv[2]);
+ else if (IsLocalChannel(parv[2]))
+ return 0;
+
+ if (*parv[2] == '0' || !IsChannelName(parv[2]))
+ return 0;
+
+ if (!(chptr = FindChannel(parv[2])))
+ {
+ if (IsModelessChannel(parv[2]) || IsLocalChannel(parv[2]))
+ {
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], parv[2]);
+ return 0;
+ }
+
+ /* Do not disallow to invite to non-existant #channels, otherwise they
+ would simply first be created, causing only MORE bandwidth usage. */
+ if (MyConnect(sptr))
+ {
+ if (check_target_limit(sptr, acptr, acptr->name, 0))
+ return 0;
+
+ sendto_one(sptr, rpl_str(RPL_INVITING), me.name, parv[0],
+ acptr->name, parv[2]);
+
+ if (acptr->user->away)
+ sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0],
+ acptr->name, acptr->user->away);
+ }
+
+ sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s", parv[0],
+ acptr->name, parv[2]);
+
+ return 0;
+ }
+
+ if (!IsMember(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
+ chptr->chname);
+ return 0;
+ }
+
+ if (IsMember(acptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_USERONCHANNEL),
+ me.name, parv[0], acptr->name, chptr->chname);
+ return 0;
+ }
+
+ if (MyConnect(sptr))
+ {
+ if (!is_chan_op(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+ me.name, parv[0], chptr->chname);
+ return 0;
+ }
+
+ /* If we get here, it was a VALID and meaningful INVITE */
+
+ if (check_target_limit(sptr, acptr, acptr->name, 0))
+ return 0;
+
+ sendto_one(sptr, rpl_str(RPL_INVITING), me.name, parv[0],
+ acptr->name, chptr->chname);
+
+ if (acptr->user->away)
+ sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0],
+ acptr->name, acptr->user->away);
+ }
+
+ if (MyConnect(acptr))
+ add_invite(acptr, chptr);
+
+ sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s", parv[0],
+ acptr->name, chptr->chname);
+
+ return 0;
+}
+
+static int number_of_zombies(aChannel *chptr)
+{
+ Reg1 Link *lp;
+ Reg2 int count = 0;
+ for (lp = chptr->members; lp; lp = lp->next)
+ if (lp->flags & CHFL_ZOMBIE)
+ count++;
+ return count;
+}
+
+/*
+ * m_list
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel list or user/time limit
+ * parv[2...] = more user/time limits
+ */
+int m_list(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ aChannel *chptr;
+ char *name, *p = NULL;
+ int show_usage = 0, show_channels = 0, param;
+ aListingArgs args = {
+ 2147483647, /* max_time */
+ 0, /* min_time */
+ 4294967295U, /* max_users */
+ 0, /* min_users */
+ 0, /* topic_limits */
+ 2147483647, /* max_topic_time */
+ 0, /* min_topic_time */
+ NULL /* chptr */
+ };
+
+ if (sptr->listing) /* Already listing ? */
+ {
+ sptr->listing->chptr->mode.mode &= ~MODE_LISTED;
+ RunFree(sptr->listing);
+ sptr->listing = NULL;
+ sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, sptr->name);
+ if (parc < 2)
+ return 0; /* Let LIST abort a listing. */
+ }
+
+ if (parc < 2) /* No arguments given to /LIST ? */
+ {
+#ifdef DEFAULT_LIST_PARAM
+ static char *defparv[MAXPARA + 1];
+ static int defparc = 0;
+ static char lp[] = DEFAULT_LIST_PARAM;
+ int i;
+
+ if (!defparc)
+ {
+ char *s = lp, *t;
+
+ defparc = 1;
+ defparv[defparc++] = t = strtok(s, " ");
+ while (t && defparc < MAXPARA)
+ {
+ if ((t = strtok(NULL, " ")))
+ defparv[defparc++] = t;
+ }
+ }
+ for (i = 1; i < defparc; i++)
+ parv[i] = defparv[i];
+ parv[i] = NULL;
+ parc = defparc;
+#endif /* DEFAULT_LIST_PARAM */
+ }
+
+ /* Decode command */
+ for (param = 1; !show_usage && parv[param]; param++)
+ {
+ char *p = parv[param];
+ do
+ {
+ int is_time = 0;
+ switch (*p)
+ {
+ case 'T':
+ case 't':
+ is_time++;
+ args.topic_limits = 1;
+ /* Fall through */
+ case 'C':
+ case 'c':
+ is_time++;
+ p++;
+ if (*p != '<' && *p != '>')
+ {
+ show_usage = 1;
+ break;
+ }
+ /* Fall through */
+ case '<':
+ case '>':
+ {
+ p++;
+ if (!isDigit(*p))
+ show_usage = 1;
+ else
+ {
+ if (is_time)
+ {
+ time_t val = atoi(p);
+ if (p[-1] == '<')
+ {
+ if (val < 80000000) /* Toggle UTC/offset */
+ {
+ /*
+ * Demands that
+ * 'TStime() - chptr->creationtime < val * 60'
+ * Which equals
+ * 'chptr->creationtime > TStime() - val * 60'
+ */
+ if (is_time == 1)
+ args.min_time = TStime() - val * 60;
+ else
+ args.min_topic_time = TStime() - val * 60;
+ }
+ else if (is_time == 1) /* Creation time in UTC was entered */
+ args.max_time = val;
+ else /* Topic time in UTC was entered */
+ args.max_topic_time = val;
+ }
+ else if (val < 80000000)
+ {
+ if (is_time == 1)
+ args.max_time = TStime() - val * 60;
+ else
+ args.max_topic_time = TStime() - val * 60;
+ }
+ else if (is_time == 1)
+ args.min_time = val;
+ else
+ args.min_topic_time = val;
+ }
+ else if (p[-1] == '<')
+ args.max_users = atoi(p);
+ else
+ args.min_users = atoi(p);
+ if ((p = strchr(p, ',')))
+ p++;
+ }
+ break;
+ }
+ default:
+ if (!IsChannelName(p))
+ {
+ show_usage = 1;
+ break;
+ }
+ if (parc != 2) /* Don't allow a mixture of channels with <,> */
+ show_usage = 1;
+ show_channels = 1;
+ p = NULL;
+ break;
+ }
+ }
+ while (!show_usage && p); /* p points after comma, or is NULL */
+ }
+
+ if (show_usage)
+ {
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ "Usage: \002/QUOTE LIST\002 \037parameters\037");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ "Where \037parameters\037 is a space or comma seperated "
+ "list of one or more of:");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ " \002<\002\037max_users\037 ; Show all channels with less "
+ "than \037max_users\037.");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ " \002>\002\037min_users\037 ; Show all channels with more "
+ "than \037min_users\037.");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ " \002C<\002\037max_minutes\037 ; Channels that exist less "
+ "than \037max_minutes\037.");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ " \002C>\002\037min_minutes\037 ; Channels that exist more "
+ "than \037min_minutes\037.");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ " \002T<\002\037max_minutes\037 ; Channels with a topic last "
+ "set less than \037max_minutes\037 ago.");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ " \002T>\002\037min_minutes\037 ; Channels with a topic last "
+ "set more than \037min_minutes\037 ago.");
+ sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+ "Example: LIST <3,>1,C<10,T>0 ; 2 users, younger than 10 min., "
+ "topic set.");
+ return 0;
+ }
+
+ sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
+
+ if (!show_channels)
+ {
+ if (args.max_users > args.min_users + 1 && args.max_time > args.min_time &&
+ args.max_topic_time > args.min_topic_time) /* Sanity check */
+ {
+ if ((sptr->listing = (aListingArgs *)RunMalloc(sizeof(aListingArgs))))
+ {
+ memcpy(sptr->listing, &args, sizeof(aListingArgs));
+ if ((sptr->listing->chptr = channel))
+ {
+ int m = channel->mode.mode & MODE_LISTED;
+ list_next_channels(sptr, 64);
+ channel->mode.mode |= m;
+ return 0;
+ }
+ RunFree(sptr->listing);
+ sptr->listing = NULL;
+ }
+ }
+ sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
+ return 0;
+ }
+
+ for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ chptr = FindChannel(name);
+ if (chptr && ShowChannel(sptr, chptr) && sptr->user)
+ sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0],
+ ShowChannel(sptr, chptr) ? chptr->chname : "*",
+ chptr->users - number_of_zombies(chptr), chptr->topic);
+ }
+
+ sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
+ return 0;
+}
+
+/*
+ * m_names - Added by Jto 27 Apr 1989
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ */
+int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 aChannel *chptr;
+ Reg2 aClient *c2ptr;
+ Reg3 Link *lp;
+ aChannel *ch2ptr = NULL;
+ int idx, flag, len, mlen;
+ char *s, *para = parc > 1 ? parv[1] : NULL;
+
+ if (parc > 2 && hunt_server(1, cptr, sptr, ":%s NAMES %s %s", 2, parc, parv))
+ return 0;
+
+ mlen = strlen(me.name) + 10 + strlen(sptr->name);
+
+ if (!BadPtr(para))
+ {
+ s = strchr(para, ',');
+ if (s)
+ {
+ parv[1] = ++s;
+ m_names(cptr, sptr, parc, parv);
+ }
+ clean_channelname(para);
+ ch2ptr = FindChannel(para);
+ }
+
+ /*
+ * First, do all visible channels (public and the one user self is)
+ */
+
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ {
+ if ((chptr != ch2ptr) && !BadPtr(para))
+ continue; /* -- wanted a specific channel */
+ if (!MyConnect(sptr) && BadPtr(para))
+ continue;
+#ifndef GODMODE
+ if (!ShowChannel(sptr, chptr))
+ continue; /* -- users on this are not listed */
+#endif
+
+ /* Find users on same channel (defined by chptr) */
+
+ strcpy(buf, "* ");
+ len = strlen(chptr->chname);
+ strcpy(buf + 2, chptr->chname);
+ strcpy(buf + 2 + len, " :");
+
+ if (PubChannel(chptr))
+ *buf = '=';
+ else if (SecretChannel(chptr))
+ *buf = '@';
+ idx = len + 4;
+ flag = 1;
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ c2ptr = lp->value.cptr;
+#ifndef GODMODE
+ if (sptr != c2ptr && IsInvisible(c2ptr) && !IsMember(sptr, chptr))
+ continue;
+#endif
+ if (lp->flags & CHFL_ZOMBIE)
+ {
+ if (lp->value.cptr != sptr)
+ continue;
+ else
+ {
+ strcat(buf, "!");
+ idx++;
+ }
+ }
+ else if (lp->flags & CHFL_CHANOP)
+ {
+ strcat(buf, "@");
+ idx++;
+ }
+ else if (lp->flags & CHFL_VOICE)
+ {
+ strcat(buf, "+");
+ idx++;
+ }
+ strcat(buf, c2ptr->name);
+ strcat(buf, " ");
+ idx += strlen(c2ptr->name) + 1;
+ flag = 1;
+#ifdef GODMODE
+ {
+ char yxx[6];
+ sprintf_irc(yxx, "%s%s", NumNick(c2ptr));
+ if (c2ptr != findNUser(yxx))
+ MyCoreDump;
+ sprintf_irc(buf + strlen(buf), "(%s) ", yxx);
+ idx += 6;
+ }
+ if (mlen + idx + NICKLEN + 11 > BUFSIZE)
+#else
+ if (mlen + idx + NICKLEN + 5 > BUFSIZE)
+#endif
+ /* space, modifier, nick, \r \n \0 */
+ {
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+ strcpy(buf, "* ");
+ strncpy(buf + 2, chptr->chname, len + 1);
+ buf[len + 2] = 0;
+ strcat(buf, " :");
+ if (PubChannel(chptr))
+ *buf = '=';
+ else if (SecretChannel(chptr))
+ *buf = '@';
+ idx = len + 4;
+ flag = 0;
+ }
+ }
+ if (flag)
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+ }
+ if (!BadPtr(para))
+ {
+ sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0],
+ ch2ptr ? ch2ptr->chname : para);
+ return (1);
+ }
+
+ /* Second, do all non-public, non-secret channels in one big sweep */
+
+ strcpy(buf, "* * :");
+ idx = 5;
+ flag = 0;
+ for (c2ptr = client; c2ptr; c2ptr = c2ptr->next)
+ {
+ aChannel *ch3ptr;
+ int showflag = 0, secret = 0;
+
+#ifndef GODMODE
+ if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr)))
+#else
+ if (!IsUser(c2ptr))
+#endif
+ continue;
+ lp = c2ptr->user->channel;
+ /*
+ * Don't show a client if they are on a secret channel or when
+ * they are on a channel sptr is on since they have already
+ * been show earlier. -avalon
+ */
+ while (lp)
+ {
+ ch3ptr = lp->value.chptr;
+#ifndef GODMODE
+ if (PubChannel(ch3ptr) || IsMember(sptr, ch3ptr))
+#endif
+ showflag = 1;
+ if (SecretChannel(ch3ptr))
+ secret = 1;
+ lp = lp->next;
+ }
+ if (showflag) /* Have we already shown them ? */
+ continue;
+#ifndef GODMODE
+ if (secret) /* On any secret channels ? */
+ continue;
+#endif
+ strcat(buf, c2ptr->name);
+ strcat(buf, " ");
+ idx += strlen(c2ptr->name) + 1;
+ flag = 1;
+#ifdef GODMODE
+ {
+ char yxx[6];
+ sprintf_irc(yxx, "%s%s", NumNick(c2ptr));
+ if (c2ptr != findNUser(yxx))
+ MyCoreDump;
+ sprintf_irc(buf + strlen(buf), "(%s) ", yxx);
+ idx += 6;
+ }
+#endif
+#ifdef GODMODE
+ if (mlen + idx + NICKLEN + 9 > BUFSIZE)
+#else
+ if (mlen + idx + NICKLEN + 3 > BUFSIZE) /* space, \r\n\0 */
+#endif
+ {
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+ strcpy(buf, "* * :");
+ idx = 5;
+ flag = 0;
+ }
+ }
+ if (flag)
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+ sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], "*");
+ return (1);
+}
+
+void send_user_joins(aClient *cptr, aClient *user)
+{
+ Reg1 Link *lp;
+ Reg2 aChannel *chptr;
+ Reg3 int cnt = 0, len = 0, clen;
+ char *mask;
+
+ *buf = ':';
+ strcpy(buf + 1, user->name);
+ strcat(buf, " JOIN ");
+ len = strlen(user->name) + 7;
+
+ for (lp = user->user->channel; lp; lp = lp->next)
+ {
+ chptr = lp->value.chptr;
+ if ((mask = strchr(chptr->chname, ':')))
+ if (match(++mask, cptr->name))
+ continue;
+ if (*chptr->chname == '&')
+ continue;
+ if (is_zombie(user, chptr))
+ continue;
+ clen = strlen(chptr->chname);
+ if (clen + 1 + len > BUFSIZE - 3)
+ {
+ if (cnt)
+ {
+ buf[len - 1] = '\0';
+ sendto_one(cptr, "%s", buf);
+ }
+ *buf = ':';
+ strcpy(buf + 1, user->name);
+ strcat(buf, " JOIN ");
+ len = strlen(user->name) + 7;
+ cnt = 0;
+ }
+ strcpy(buf + len, chptr->chname);
+ cnt++;
+ len += clen;
+ if (lp->next)
+ {
+ len++;
+ strcat(buf, ",");
+ }
+ }
+ if (*buf && cnt)
+ sendto_one(cptr, "%s", buf);
+
+ return;
+}
+
+/*
+ * send_hack_notice()
+ *
+ * parc & parv[] are the same as that of the calling function:
+ * mtype == 1 is from m_mode, 2 is from m_create, 3 is from m_kick.
+ *
+ * This function prepares sendbuf with the server notices and wallops
+ * to be sent for all hacks. -Ghostwolf 18-May-97
+ */
+
+static void send_hack_notice(aClient *cptr, aClient *sptr, int parc,
+ char *parv[], int badop, int mtype)
+{
+ aChannel *chptr;
+ static char params[MODEBUFLEN];
+ int i = 3;
+ chptr = FindChannel(parv[1]);
+ *params = '\0';
+
+ if (Protocol(cptr) < 10) /* We don't get numeric nicks from P09 */
+ { /* servers, so this can be sent "As Is" */
+ if (mtype == 1)
+ {
+ while (i < parc)
+ {
+ strcat(params, " ");
+ strcat(params, parv[i++]);
+ }
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- %sHACK(%d): %s MODE %s %s%s [" TIME_T_FMT
+ "]", me.name, (badop == 3) ? "BOUNCE or " : "", badop, parv[0],
+ parv[1], parv[2], params, chptr->creationtime);
+ sendbufto_op_mask((badop == 3) ? SNO_HACK3 : (badop ==
+ 4) ? SNO_HACK4 : SNO_HACK2);
+
+ if ((IsServer(sptr)) && (badop == 2))
+ {
+ sprintf_irc(sendbuf, ":%s DESYNCH :HACK: %s MODE %s %s%s",
+ me.name, parv[0], parv[1], parv[2], params);
+ sendbufto_serv_butone(cptr);
+ }
+ }
+ else if (mtype == 3)
+ {
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- HACK: %s KICK %s %s :%s",
+ me.name, sptr->name, parv[1], parv[2], parv[3]);
+ sendbufto_op_mask(SNO_HACK4);
+ }
+ }
+ else
+ {
+ /* P10 servers require numeric nick conversion before sending. */
+ switch (mtype)
+ {
+ case 1: /* Convert nicks for MODE HACKs here */
+ {
+ char *mode = parv[2];
+ while (i < parc)
+ {
+ while (*mode && *mode != 'o' && *mode != 'v')
+ ++mode;
+ strcat(params, " ");
+ if (*mode == 'o' || *mode == 'v')
+ {
+ register aClient *acptr;
+ if ((acptr = findNUser(parv[i])) != NULL) /* Convert nicks here */
+ strcat(params, acptr->name);
+ else
+ {
+ strcat(params, "<");
+ strcat(params, parv[i]);
+ strcat(params, ">");
+ }
+ }
+ else /* If it isn't a numnick, send it 'as is' */
+ strcat(params, parv[i]);
+ i++;
+ }
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- %sHACK(%d): %s MODE %s %s%s ["
+ TIME_T_FMT "]", me.name, (badop == 3) ? "BOUNCE or " : "", badop,
+ parv[0], parv[1], parv[2], params, chptr->creationtime);
+ sendbufto_op_mask((badop == 3) ? SNO_HACK3 : (badop ==
+ 4) ? SNO_HACK4 : SNO_HACK2);
+
+ if ((IsServer(sptr)) && (badop == 2))
+ {
+ sprintf_irc(sendbuf, ":%s DESYNCH :HACK: %s MODE %s %s%s",
+ me.name, parv[0], parv[1], parv[2], params);
+ sendbufto_serv_butone(cptr);
+ }
+ break;
+ }
+ case 2: /* No conversion is needed for CREATE; the only numnick is sptr */
+ {
+ sendto_serv_butone(cptr, ":%s DESYNCH :HACK: %s CREATE %s %s",
+ me.name, sptr->name, chptr->chname, parv[2]);
+ sendto_op_mask(SNO_HACK2, "HACK(2): %s CREATE %s %s",
+ sptr->name, chptr->chname, parv[2]);
+ break;
+ }
+ case 3: /* Convert nick in KICK message */
+ {
+ aClient *acptr;
+ if ((acptr = findNUser(parv[2])) != NULL) /* attempt to convert nick */
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- HACK: %s KICK %s %s :%s",
+ me.name, sptr->name, parv[1], acptr->name, parv[3]);
+ else /* if conversion fails, send it 'as is' in <>'s */
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- HACK: %s KICK %s <%s> :%s",
+ me.name, sptr->name, parv[1], parv[2], parv[3]);
+ sendbufto_op_mask(SNO_HACK4);
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/chkconf.c
+ * Copyright (C) 1993 Darren Reed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HPUX
+#include <arpa/inet.h>
+#endif /* HPUX */
+#ifdef R_LINES
+#include <signal.h>
+#endif
+#include "h.h"
+#include "s_conf.h"
+#include "class.h"
+#include "common.h"
+#include "ircd.h"
+#include "fileio.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * For the connect rule patch.. these really should be in a header,
+ * but i see h.h isn't included for some reason.. so they're here.
+ */
+char *crule_parse(char *rule);
+void crule_free(char **elem);
+
+static void new_class(int cn);
+static char confchar(unsigned int status);
+static char *getfield(char *newline);
+static int validate(aConfItem *top);
+static aConfItem *chk_initconf(void);
+static aConfClass *get_class(int cn, int ism);
+
+static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0;
+static char *chk_configfile = CPATH;
+static char nullfield[] = "";
+static char maxsendq[12];
+
+/* A few dummy variables and functions needed to link with runmalloc.o */
+time_t now;
+struct Client {
+ time_t since;
+ char name[1];
+} me;
+void debug(int UNUSED(level), const char *form, ...)
+{
+ va_list vl;
+ va_start(vl, form);
+ if (debugflag > 8)
+ {
+ vfprintf(stderr, form, vl);
+ fprintf(stderr, "\n");
+ }
+ va_end(vl);
+}
+void sendto_one(aClient *UNUSED(to), char *UNUSED(pattern), ...)
+{
+}
+char *rpl_str(int UNUSED(numeric))
+{
+ return "";
+}
+
+int main(int argc, char *argv[])
+{
+ const char *dpath = DPATH;
+ chk_configfile = "ircd.conf";
+
+ while (argc > 1)
+ {
+ if (*argv[1] == '-')
+ {
+ switch (argv[1][1])
+ {
+ case 'd':
+ if (argc > 2)
+ {
+ dpath = argv[2];
+ --argc;
+ ++argv;
+ }
+ else
+ {
+ fprintf(stderr, "-d: Missing path\n");
+ exit(-1);
+ }
+ break;
+ case 'x':
+ debugflag = 1;
+ if (isdigit(argv[1][2]))
+ debugflag = atoi(&argv[1][2]);
+ break;
+ default:
+ fprintf(stderr, "Ignoring unknown option -%c\n", argv[1][1]);
+ }
+ }
+ else
+ chk_configfile = argv[1];
+ --argc;
+ ++argv;
+ }
+
+ if (chdir(dpath))
+ {
+ fprintf(stderr, "chdir(\"%s\") : %s\n", dpath, strerror(errno));
+ exit(-1);
+ }
+ else if (debugflag > 1)
+ fprintf(stderr, "chdir(\"%s\") : Success", dpath);
+
+ new_class(0);
+
+ return validate(chk_initconf());
+}
+
+/*
+ * chk_initconf()
+ *
+ * Read configuration file.
+ *
+ * Returns -1, if file cannot be opened
+ * 0, if file opened.
+ */
+static aConfItem *chk_initconf(void)
+{
+ FBFILE *file;
+ char line[512], *tmp, *s, *crule;
+ int ccount = 0, ncount = 0, flags = 0;
+ aConfItem *aconf = NULL, *ctop = NULL;
+
+ fprintf(stderr, "chk_initconf(): ircd.conf = %s\n", chk_configfile);
+ if (NULL == (file = fbopen(chk_configfile, "r")))
+ {
+ perror("open");
+ return NULL;
+ }
+
+ while (fbgets(line, sizeof(line) - 1, file))
+ {
+ if (aconf)
+ {
+ if (aconf->host)
+ RunFree(aconf->host);
+ if (aconf->passwd)
+ RunFree(aconf->passwd);
+ if (aconf->name)
+ RunFree(aconf->name);
+ }
+ else
+ aconf = (aConfItem *)RunMalloc(sizeof(*aconf));
+ aconf->host = (char *)NULL;
+ aconf->passwd = (char *)NULL;
+ aconf->name = (char *)NULL;
+ aconf->confClass = (aConfClass *) NULL;
+ if ((tmp = strchr(line, '\n')))
+ *tmp = 0;
+ /*
+ * Do quoting of characters and # detection.
+ */
+ for (tmp = line; *tmp; tmp++)
+ {
+ if (*tmp == '\\')
+ {
+ switch (*(tmp + 1))
+ {
+ case 'n':
+ *tmp = '\n';
+ break;
+ case 'r':
+ *tmp = '\r';
+ break;
+ case 't':
+ *tmp = '\t';
+ break;
+ case '0':
+ *tmp = '\0';
+ break;
+ default:
+ *tmp = *(tmp + 1);
+ break;
+ }
+ if (!*(tmp + 1))
+ break;
+ else
+ for (s = tmp; (*s = *++s);)
+ ;
+ tmp++;
+ }
+ else if (*tmp == '#')
+ *tmp = '\0';
+ }
+ if (!*line || *line == '#' || *line == '\n' ||
+ *line == ' ' || *line == '\t')
+ continue;
+
+ if (line[1] != ':')
+ {
+ fprintf(stderr, "ERROR: Bad config line (%s)\n", line);
+ continue;
+ }
+
+ if (debugflag)
+ printf("\n%s\n", line);
+ fflush(stdout);
+
+ tmp = getfield(line);
+ if (!tmp)
+ {
+ fprintf(stderr, "\tERROR: no fields found\n");
+ continue;
+ }
+
+ aconf->status = CONF_ILLEGAL;
+
+ switch (*tmp)
+ {
+ case 'A': /* Name, e-mail address of administrator */
+ case 'a': /* of this server. */
+ aconf->status = CONF_ADMIN;
+ break;
+ case 'C': /* Server where I should try to connect */
+ case 'c': /* in case of lp failures */
+ ccount++;
+ aconf->status = CONF_CONNECT_SERVER;
+ break;
+ /* Connect rule */
+ case 'D':
+ aconf->status = CONF_CRULEALL;
+ break;
+ /* Connect rule - autos only */
+ case 'd':
+ aconf->status = CONF_CRULEAUTO;
+ break;
+ case 'H': /* Hub server line */
+ case 'h':
+ aconf->status = CONF_HUB;
+ break;
+ case 'I': /* Just plain normal irc client trying */
+ case 'i': /* to connect me */
+ aconf->status = CONF_CLIENT;
+ break;
+ case 'K': /* Kill user line on irc.conf */
+ aconf->status = CONF_KILL;
+ break;
+ case 'k': /* Kill user line based on IP in ircd.conf */
+ aconf->status = CONF_IPKILL;
+ break;
+ /* Operator. Line should contain at least */
+ /* password and host where connection is */
+ case 'L': /* guaranteed leaf server */
+ case 'l':
+ aconf->status = CONF_LEAF;
+ break;
+ /* Me. Host field is name used for this host */
+ /* and port number is the number of the port */
+ case 'M':
+ case 'm':
+ aconf->status = CONF_ME;
+ break;
+ case 'N': /* Server where I should NOT try to */
+ case 'n': /* connect in case of lp failures */
+ /* but which tries to connect ME */
+ ++ncount;
+ aconf->status = CONF_NOCONNECT_SERVER;
+ break;
+ case 'O':
+ aconf->status = CONF_OPERATOR;
+ break;
+ /* Local Operator, (limited privs --SRB) */
+ case 'o':
+ aconf->status = CONF_LOCOP;
+ break;
+ case 'P': /* listen port line */
+ case 'p':
+ aconf->status = CONF_LISTEN_PORT;
+ break;
+#ifdef R_LINES
+ case 'R': /* extended K line */
+ case 'r': /* Offers more options of how to restrict */
+ aconf->status = CONF_RESTRICT;
+ break;
+#endif
+ case 'T':
+ case 't':
+ aconf->status = CONF_TLINES;
+ break;
+ case 'U':
+ case 'u':
+ aconf->status = CONF_UWORLD;
+ break;
+ case 'Y':
+ case 'y':
+ aconf->status = CONF_CLASS;
+ break;
+ default:
+ fprintf(stderr, "\tERROR: unknown conf line letter (%c)\n", *tmp);
+ break;
+ }
+
+ if (IsIllegal(aconf))
+ continue;
+
+ for (;;) /* Fake loop, that I can use break here --msa */
+ {
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ DupString(aconf->host, tmp);
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ DupString(aconf->passwd, tmp);
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ DupString(aconf->name, tmp);
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ aconf->port = atoi(tmp);
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ if (!(aconf->status & (CONF_CLASS | CONF_ME)))
+ {
+ aconf->confClass = get_class(atoi(tmp), 0);
+ break;
+ }
+ if (aconf->status & CONF_ME)
+ aconf->confClass = get_class(atoi(tmp), 1);
+ break;
+ }
+ if (!aconf->confClass && (aconf->status & (CONF_CONNECT_SERVER |
+ CONF_ME | CONF_NOCONNECT_SERVER | CONF_OPS | CONF_CLIENT)))
+ {
+ fprintf(stderr, "\tWARNING: No class. Default 0\n");
+ aconf->confClass = get_class(0, 0);
+ }
+ /*
+ * If conf line is a class definition, create a class entry
+ * for it and make the conf_line illegal and delete it.
+ */
+ if (aconf->status & CONF_CLASS)
+ {
+ if (!aconf->host)
+ {
+ fprintf(stderr, "\tERROR: no class #\n");
+ continue;
+ }
+ if (!tmp)
+ {
+ fprintf(stderr, "\tWARNING: missing sendq field\n");
+ fprintf(stderr, "\t\t default: %d\n", DEFAULTMAXSENDQLENGTH);
+ sprintf(maxsendq, "%d", DEFAULTMAXSENDQLENGTH);
+ }
+ else
+ sprintf(maxsendq, "%d", atoi(tmp));
+ new_class(atoi(aconf->host));
+ aconf->confClass = get_class(atoi(aconf->host), 0);
+ goto print_confline;
+ }
+
+ if (aconf->status & CONF_LISTEN_PORT)
+ {
+#ifdef UNIXPORT
+ struct stat sb;
+
+ if (!aconf->host)
+ fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
+ else if (strchr(aconf->host, '/'))
+ {
+ if (stat(aconf->host, &sb) == -1)
+ {
+ fprintf(stderr, "\tERROR: (%s) ", aconf->host);
+ perror("stat");
+ }
+ else if ((sb.st_mode & S_IFMT) != S_IFDIR)
+ fprintf(stderr, "\tERROR: %s not directory\n", aconf->host);
+ }
+#else
+ if (!aconf->host)
+ fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
+ else if (strchr(aconf->host, '/'))
+ fprintf(stderr, "\t%s\n", "WARNING: / present in P-line "
+ "for non-UNIXPORT configuration");
+#endif
+ aconf->confClass = get_class(0, 0);
+ goto print_confline;
+ }
+
+ if (aconf->status & CONF_SERVER_MASK &&
+ (!aconf->host || strchr(aconf->host, '*') || strchr(aconf->host, '?')))
+ {
+ fprintf(stderr, "\tERROR: bad host field\n");
+ continue;
+ }
+
+ if (aconf->status & CONF_SERVER_MASK && BadPtr(aconf->passwd))
+ {
+ fprintf(stderr, "\tERROR: empty/no password field\n");
+ continue;
+ }
+
+ if (aconf->status & CONF_SERVER_MASK && !aconf->name)
+ {
+ fprintf(stderr, "\tERROR: bad name field\n");
+ continue;
+ }
+
+ if (aconf->status & (CONF_SERVER_MASK | CONF_OPS))
+ if (!strchr(aconf->host, '@'))
+ {
+ char *newhost;
+ int len = 3; /* *@\0 = 3 */
+
+ len += strlen(aconf->host);
+ newhost = (char *)RunMalloc(len);
+ sprintf(newhost, "*@%s", aconf->host);
+ RunFree(aconf->host);
+ aconf->host = newhost;
+ }
+
+ /* parse the connect rules to detect errors, but free
+ * any allocated storage immediately -- we're just looking
+ * for errors.. */
+ if (aconf->status & CONF_CRULE)
+ if ((crule = (char *)crule_parse(aconf->name)) != NULL)
+ crule_free(&crule);
+
+ if (!aconf->confClass)
+ aconf->confClass = get_class(0, 0);
+ sprintf(maxsendq, "%d", ConfClass(aconf));
+
+ if ((aconf->status & CONF_ADMIN) && (!aconf->name ||
+ !aconf->passwd || !aconf->host))
+ fprintf(stderr, "ERROR: Your A: line must have 4 fields!\n");
+
+ if (!aconf->name)
+ DupString(aconf->name, nullfield);
+ if (!aconf->passwd)
+ DupString(aconf->passwd, nullfield);
+ if (!aconf->host)
+ DupString(aconf->host, nullfield);
+ if (aconf->status & (CONF_ME | CONF_ADMIN))
+ {
+ if (flags & aconf->status)
+ fprintf(stderr, "ERROR: multiple %c-lines\n",
+ toUpper(confchar(aconf->status)));
+ else
+ flags |= aconf->status;
+ }
+ print_confline:
+ if (debugflag > 8)
+ printf("(%d) (%s) (%s) (%s) (%u) (%s)\n",
+ aconf->status, aconf->host, aconf->passwd,
+ aconf->name, aconf->port, maxsendq);
+ fflush(stdout);
+ if (aconf->status & (CONF_SERVER_MASK | CONF_HUB | CONF_LEAF))
+ {
+ aconf->next = ctop;
+ ctop = aconf;
+ aconf = NULL;
+ }
+ }
+ fbclose(file);
+ return ctop;
+}
+
+static aConfClass *get_class(int cn, int ism)
+{
+ static aConfClass cls;
+ if (ism == 1)
+ {
+ cls.conClass = (unsigned int)-1;
+ if ((cn >= 1) && (cn <= 64))
+ cls.conClass = cn;
+ else
+ fprintf(stderr, "\tWARNING: server numeric %d is not 1-64\n", cn);
+ }
+ else
+ {
+ int i = numclasses - 1;
+ cls.conClass = (unsigned int)-1;
+ for (; i >= 0; i--)
+ if (classarr[i] == cn)
+ {
+ cls.conClass = cn;
+ break;
+ }
+ if (i == -1)
+ fprintf(stderr, "\tWARNING: class %d not found\n", cn);
+ }
+ return &cls;
+}
+
+static void new_class(int cn)
+{
+ numclasses++;
+ if (classarr)
+ classarr = (int *)RunRealloc(classarr, sizeof(int) * numclasses);
+ else
+ classarr = (int *)RunMalloc(sizeof(int));
+ classarr[numclasses - 1] = cn;
+}
+
+/*
+ * field breakup for ircd.conf file.
+ */
+static char *getfield(char *newline)
+{
+ static char *line = NULL;
+ char *end, *field;
+
+ if (newline)
+ line = newline;
+ if (line == NULL)
+ return (NULL);
+
+ field = line;
+ if ((end = strchr(line, ':')) == NULL)
+ {
+ line = NULL;
+ if ((end = strchr(field, '\n')) == NULL)
+ end = field + strlen(field);
+ }
+ else
+ line = end + 1;
+ *end = '\0';
+ return (field);
+}
+
+static int validate(aConfItem *top)
+{
+ Reg1 aConfItem *aconf, *bconf;
+ unsigned int otype, valid = 0;
+
+ if (!top)
+ return 0;
+
+ for (aconf = top; aconf; aconf = aconf->next)
+ {
+ if (aconf->status & CONF_MATCH)
+ continue;
+
+ if (aconf->status & CONF_SERVER_MASK)
+ {
+ if (aconf->status & CONF_CONNECT_SERVER)
+ otype = CONF_NOCONNECT_SERVER;
+ else if (aconf->status & CONF_NOCONNECT_SERVER)
+ otype = CONF_CONNECT_SERVER;
+ else /* Does this ever happen ? */
+ continue;
+
+ for (bconf = top; bconf; bconf = bconf->next)
+ {
+ if (bconf == aconf || !(bconf->status & otype))
+ continue;
+ if (bconf->confClass == aconf->confClass &&
+ !strCasediff(bconf->name, aconf->name) &&
+ !strCasediff(bconf->host, aconf->host))
+ {
+ aconf->status |= CONF_MATCH;
+ bconf->status |= CONF_MATCH;
+ break;
+ }
+ }
+ }
+ else
+ for (bconf = top; bconf; bconf = bconf->next)
+ {
+ if ((bconf == aconf) || !(bconf->status & CONF_SERVER_MASK))
+ continue;
+ if (!strCasediff(bconf->name, aconf->name))
+ {
+ aconf->status |= CONF_MATCH;
+ break;
+ }
+ }
+ }
+
+ fprintf(stderr, "\n");
+ for (aconf = top; aconf; aconf = aconf->next)
+ if (aconf->status & CONF_MATCH)
+ valid++;
+ else
+ fprintf(stderr, "Unmatched %c:%s:%s:%s\n",
+ confchar(aconf->status), aconf->host, aconf->passwd, aconf->name);
+ return valid ? 0 : -1;
+}
+
+static char confchar(unsigned int status)
+{
+ static char letrs[] = "ICNoOMKARYLPH";
+ char *s = letrs;
+
+ status &= ~(CONF_MATCH | CONF_ILLEGAL);
+
+ for (; *s; s++, status >>= 1)
+ if (status & 1)
+ return *s;
+ return '-';
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/class.c
+ * Copyright (C) 1990 Darren Reed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "class.h"
+#include "s_conf.h"
+#include "s_serv.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "ircd.h"
+
+RCSTAG_CC("$Id$");
+
+#define BAD_CONF_CLASS ((unsigned int)-1)
+#define BAD_PING ((unsigned int)-2)
+#define BAD_CLIENT_CLASS ((unsigned int)-3)
+
+aConfClass *classes;
+
+unsigned int get_conf_class(aConfItem *aconf)
+{
+ if ((aconf) && (aconf->confClass))
+ return (ConfClass(aconf));
+
+ Debug((DEBUG_DEBUG, "No Class For %s", (aconf) ? aconf->name : "*No Conf*"));
+
+ return (BAD_CONF_CLASS);
+
+}
+
+static unsigned int get_conf_ping(aConfItem *aconf)
+{
+ if ((aconf) && (aconf->confClass))
+ return (ConfPingFreq(aconf));
+
+ Debug((DEBUG_DEBUG, "No Ping For %s", (aconf) ? aconf->name : "*No Conf*"));
+
+ return (BAD_PING);
+}
+
+unsigned int get_client_class(aClient *acptr)
+{
+ Reg1 Link *tmp;
+ Reg2 aConfClass *cl;
+ unsigned int retc = BAD_CLIENT_CLASS;
+
+ if (acptr && !IsMe(acptr) && !IsPing(acptr) && (acptr->confs))
+ for (tmp = acptr->confs; tmp; tmp = tmp->next)
+ {
+ if (!tmp->value.aconf || !(cl = tmp->value.aconf->confClass))
+ continue;
+ if (ConClass(cl) > retc || retc == BAD_CLIENT_CLASS)
+ retc = ConClass(cl);
+ }
+
+ Debug((DEBUG_DEBUG, "Returning Class %d For %s", retc, acptr->name));
+
+ return (retc);
+}
+
+int unsigned get_client_ping(aClient *acptr)
+{
+ unsigned int ping = 0, ping2;
+ aConfItem *aconf;
+ Link *link;
+
+ link = acptr->confs;
+
+ if (link)
+ while (link)
+ {
+ aconf = link->value.aconf;
+ if (aconf->status &
+ (CONF_CLIENT | CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER))
+ {
+ ping2 = get_conf_ping(aconf);
+ if ((ping2 != BAD_PING) && ((ping > ping2) || !ping))
+ ping = ping2;
+ }
+ link = link->next;
+ }
+ else
+ {
+ ping = PINGFREQUENCY;
+ Debug((DEBUG_DEBUG, "No Attached Confs"));
+ }
+ if (ping <= 0)
+ ping = PINGFREQUENCY;
+ Debug((DEBUG_DEBUG, "Client %s Ping %d", acptr->name, ping));
+ return (ping);
+}
+
+unsigned int get_con_freq(aConfClass * clptr)
+{
+ if (clptr)
+ return (ConFreq(clptr));
+ else
+ return (CONNECTFREQUENCY);
+}
+
+/*
+ * When adding a class, check to see if it is already present first.
+ * if so, then update the information for that class, rather than create
+ * a new entry for it and later delete the old entry.
+ * if no present entry is found, then create a new one and add it in
+ * immeadiately after the first one (class 0).
+ */
+void add_class(unsigned int conClass, unsigned int ping, unsigned int confreq,
+ unsigned int maxli, size_t sendq)
+{
+ aConfClass *t, *p;
+
+ t = find_class(conClass);
+ if ((t == classes) && (conClass != 0))
+ {
+ p = (aConfClass *) make_class();
+ NextClass(p) = NextClass(t);
+ NextClass(t) = p;
+ }
+ else
+ p = t;
+ Debug((DEBUG_DEBUG, "Add Class %u: p %p t %p - cf: %u pf: %u ml: %u sq: %d",
+ conClass, p, t, confreq, ping, maxli, sendq));
+ ConClass(p) = conClass;
+ ConFreq(p) = confreq;
+ PingFreq(p) = ping;
+ MaxLinks(p) = maxli;
+ MaxSendq(p) = (sendq > 0) ? sendq : DEFAULTMAXSENDQLENGTH;
+ if (p != t)
+ Links(p) = 0;
+}
+
+aConfClass *find_class(unsigned int cclass)
+{
+ aConfClass *cltmp;
+
+ for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp))
+ if (ConClass(cltmp) == cclass)
+ return cltmp;
+ return classes;
+}
+
+void check_class(void)
+{
+ aConfClass *cltmp, *cltmp2;
+
+ Debug((DEBUG_DEBUG, "Class check:"));
+
+ for (cltmp2 = cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp2))
+ {
+ Debug((DEBUG_DEBUG,
+ "Class %d : CF: %d PF: %d ML: %d LI: %d SQ: %d",
+ ConClass(cltmp), ConFreq(cltmp), PingFreq(cltmp),
+ MaxLinks(cltmp), Links(cltmp), MaxSendq(cltmp)));
+ if (IsMarkedDelete(cltmp))
+ {
+ NextClass(cltmp2) = NextClass(cltmp);
+ if (Links(cltmp) == 0)
+ free_class(cltmp);
+ }
+ else
+ cltmp2 = cltmp;
+ }
+}
+
+void initclass(void)
+{
+ classes = (aConfClass *) make_class();
+
+ ConClass(FirstClass()) = 0;
+ ConFreq(FirstClass()) = CONNECTFREQUENCY;
+ PingFreq(FirstClass()) = PINGFREQUENCY;
+ MaxLinks(FirstClass()) = MAXIMUM_LINKS;
+ MaxSendq(FirstClass()) = DEFAULTMAXSENDQLENGTH;
+ Links(FirstClass()) = 0;
+ NextClass(FirstClass()) = NULL;
+}
+
+void report_classes(aClient *sptr)
+{
+ aConfClass *cltmp;
+
+ for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp))
+ sendto_one(sptr, rpl_str(RPL_STATSYLINE), me.name, sptr->name,
+ 'Y', ConClass(cltmp), PingFreq(cltmp), ConFreq(cltmp),
+ MaxLinks(cltmp), MaxSendq(cltmp));
+}
+
+size_t get_sendq(aClient *cptr)
+{
+ size_t sendq = DEFAULTMAXSENDQLENGTH;
+ Link *tmp;
+ aConfClass *cl;
+
+ if (cptr && !IsMe(cptr) && (cptr->confs))
+ for (tmp = cptr->confs; tmp; tmp = tmp->next)
+ {
+ if (!tmp->value.aconf || !(cl = tmp->value.aconf->confClass))
+ continue;
+ if (ConClass(cl) != BAD_CLIENT_CLASS)
+ sendq = MaxSendq(cl);
+ }
+ return sendq;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, include/common.c
+ * Copyright (C) 1998 Andrea Cocito
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * CODERS WARNING: DO _NOT_ EDIT THE TABLES IN THIS FILE
+ * Instead:
+ * a) Edit the table generator below, specifically the makeTables() function
+ * b) Recreate the common.c tables with 'make ctables'.
+ */
+
+
+/*=============================================================================
+ * Actual source of the library stuff, not generated when making the tables
+ */
+
+#ifndef MAKETABLES
+
+/*=============================================================================
+ * Headers needed in this source file
+ */
+
+#include "common.h"
+
+/*=============================================================================
+ * Functions eventually inlined or visible externally
+ */
+
+#ifndef FORCEINLINE
+/* *INDENT-OFF* */
+NTL_HDR_strChattr { NTL_SRC_strChattr }
+NTL_HDR_strCasediff { NTL_SRC_strCasediff }
+/* *INDENT-ON* */
+#endif /* !FORCEINLINE */
+
+/*=============================================================================
+ * Other functions visible externally
+ */
+
+int strnChattr(const char *s, const size_t n)
+{
+ register const char *rs = s;
+ register int x = ~0;
+ register int r = n;
+ while (*rs && r--)
+ x &= NTL_char_attrib[*rs++ - CHAR_MIN];
+ return x;
+}
+
+int strCasecmp(const char *a, const char *b)
+{
+ register const char *ra = a;
+ register const char *rb = b;
+ while (toLower(*ra) == toLower(*rb))
+ if (!*ra++)
+ return 0;
+ else
+ rb++;
+ return (*ra - *rb);
+}
+
+int strnCasecmp(const char *a, const char *b, const size_t n)
+{
+ register const char *ra = a;
+ register const char *rb = b;
+ register int left = n;
+ if (!left--)
+ return 0;
+ while (toLower(*ra) == toLower(*rb))
+ if ((!(*(ra++))) || (!(left--)))
+ return 0;
+ else
+ rb++;
+ return (*ra - *rb);
+}
+
+/*=============================================================================
+ * Automatically generated tables, don't touch these by hand !
+ */
+
+/* *INDENT-OFF* */
+/*
+ * DO not touch anything below this line !
+ * NTL_TOK_START
+ */
+const char NTL_tolower_tab[] = {
+#if (CHAR_MIN<0)
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xc8-xcf */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xd0-xd7 */ '\xd0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xd7',
+/* xd8-xdf */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xdf',
+/* xe0-xe7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xe8-xef */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xf0-xf7 */ '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
+/* xf8-xff */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff'
+ ,
+#endif /* (CHAR_MIN<0) */
+/* x00-x07 */ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
+/* x08-x0f */ '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
+/* x10-x17 */ '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
+/* x18-x1f */ '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
+/* ' '-x27 */ ' ', '!', '"', '#', '$', '%', '&', '\x27',
+/* '('-'/' */ '(', ')', '*', '+', ',', '-', '.', '/',
+/* '0'-'7' */ '0', '1', '2', '3', '4', '5', '6', '7',
+/* '8'-'?' */ '8', '9', ':', ';', '<', '=', '>', '?',
+/* '@'-'G' */ '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 'H'-'O' */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 'P'-'W' */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 'X'-'_' */ 'x', 'y', 'z', '{', '|', '}', '~', '_',
+/* '`'-'g' */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 'h'-'o' */ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 'p'-'w' */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 'x'-x7f */ 'x', 'y', 'z', '{', '|', '}', '~', '\x7f'
+#if (!(CHAR_MIN<0))
+ ,
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xc8-xcf */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xd0-xd7 */ '\xd0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xd7',
+/* xd8-xdf */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xdf',
+/* xe0-xe7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xe8-xef */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xf0-xf7 */ '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
+/* xf8-xff */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff'
+#endif /* (!(CHAR_MIN<0)) */
+ };
+
+const char NTL_toupper_tab[] = {
+#if (CHAR_MIN<0)
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xc8-xcf */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xd0-xd7 */ '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
+/* xd8-xdf */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
+/* xe0-xe7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xe8-xef */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xf0-xf7 */ '\xf0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xf7',
+/* xf8-xff */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xff'
+ ,
+#endif /* (CHAR_MIN<0) */
+/* x00-x07 */ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
+/* x08-x0f */ '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
+/* x10-x17 */ '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
+/* x18-x1f */ '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
+/* ' '-x27 */ ' ', '!', '"', '#', '$', '%', '&', '\x27',
+/* '('-'/' */ '(', ')', '*', '+', ',', '-', '.', '/',
+/* '0'-'7' */ '0', '1', '2', '3', '4', '5', '6', '7',
+/* '8'-'?' */ '8', '9', ':', ';', '<', '=', '>', '?',
+/* '@'-'G' */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+/* 'H'-'O' */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+/* 'P'-'W' */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+/* 'X'-'_' */ 'X', 'Y', 'Z', '[', '\x5c', ']', '^', '_',
+/* '`'-'g' */ '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+/* 'h'-'o' */ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+/* 'p'-'w' */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+/* 'x'-x7f */ 'X', 'Y', 'Z', '[', '\x5c', ']', '^', '\x7f'
+#if (!(CHAR_MIN<0))
+ ,
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xc8-xcf */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xd0-xd7 */ '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
+/* xd8-xdf */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
+/* xe0-xe7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xe8-xef */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xf0-xf7 */ '\xf0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xf7',
+/* xf8-xff */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xff'
+#endif /* (!(CHAR_MIN<0)) */
+ };
+
+const unsigned int NTL_char_attrib[] = {
+#if (CHAR_MIN<0)
+/* x80-x87 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x88-x8f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x90-x97 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x98-x9f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa0-xa7 */ 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa8-xaf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb0-xb7 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb8-xbf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xc0-xc7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xc8-xcf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xd0-xd7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xd8-xdf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xe0-xe7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xe8-xef */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xf0-xf7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400,
+/* xf8-xff */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400
+ ,
+#endif /* (CHAR_MIN<0) */
+/* x00-x07 */ 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0004,
+/* x08-x0f */ 0x0404, 0x0504, 0x10504, 0x0504, 0x0504, 0x10504, 0x0404, 0x0404,
+/* x10-x17 */ 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404,
+/* x18-x1f */ 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404,
+/* ' '-x27 */ 0x0140, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x24d0,
+/* '('-'/' */ 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x00d0, 0x74d0, 0xe4d0, 0x04d0,
+/* '0'-'7' */ 0xf459, 0xf459, 0xf459, 0xf459, 0xf459, 0xf459, 0xf459, 0xf459,
+/* '8'-'?' */ 0xf459, 0xf459, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0,
+/* '@'-'G' */ 0x04d0, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653,
+/* 'H'-'O' */ 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653,
+/* 'P'-'W' */ 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653,
+/* 'X'-'_' */ 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x74d0,
+/* '`'-'g' */ 0x34d0, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473,
+/* 'h'-'o' */ 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473,
+/* 'p'-'w' */ 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473,
+/* 'x'-x7f */ 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x0400
+#if (!(CHAR_MIN<0))
+ ,
+/* x80-x87 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x88-x8f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x90-x97 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x98-x9f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa0-xa7 */ 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa8-xaf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb0-xb7 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb8-xbf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xc0-xc7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xc8-xcf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xd0-xd7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xd8-xdf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xe0-xe7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xe8-xef */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xf0-xf7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400,
+/* xf8-xff */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400
+#endif /* (!(CHAR_MIN<0)) */
+ };
+
+/*
+ * NTL_TOK_END
+ * DO not touch anything above this line !
+ */
+/* *INDENT-ON* */
+
+#endif /* !MAKETABLES */
+
+/*=============================================================================
+ * TABLE GENERATOR
+ * The following part of code is NOT included in the actual server's
+ * or library source, it's just used to build the above tables
+ *
+ * This should rebuild the actual tables and automatically place them
+ * into this source file, note that this part of code is for developers
+ * only, it's supposed to work on both signed and unsigned chars but I
+ * actually tested it only on signed-char architectures, the code and
+ * macros actually used by the server instead DO work and have been tested
+ * on platforms where0 char is both signed or unsigned, this is true as long
+ * as the <limits.h> macros are set properly and without any need to rebuild
+ * the tables (wich as said an admin should NEVER do, tables need to be rebuilt
+ * only when one wants to really change the results or when one has to
+ * compile on architectures where a char is NOT eight bits [?!], yes
+ * it all is supposed to work in that case too... but I can't test it
+ * because I've not found a machine in the world where this happes).
+ *
+ * NEVER -f[un]signed-char on gcc since that does NOT fix the named macros
+ * and you end up in a non-ANSI environment where CHAR_MIN and CHAR_MAX
+ * are _not_ the real limits of a default 'char' type. This is true for
+ * both admins and coders.
+ *
+ */
+
+#ifdef MAKETABLES
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "common.h"
+
+static void zeroTables(void);
+static void markString(int macro, const char *s);
+static void unMarkString(int macro, const char *s);
+static void markRange(int macro, char from, char to);
+static void moveMacro(int from, int to);
+static void setLowHi(const char firstlow, const char lastlow,
+ const char firsthi);
+
+char NTL_tolower_tab[1 + CHAR_MAX - CHAR_MIN]; /* 256 bytes */
+char NTL_toupper_tab[1 + CHAR_MAX - CHAR_MIN]; /* 256 bytes */
+int NTL_char_attrib[1 + CHAR_MAX - CHAR_MIN]; /* 256 ints = 0.5 to 2 kilobytes */
+
+/*
+ * makeTables()
+ * Where we make the tables, edit ONLY this to change the tables.
+ */
+
+static void makeTables(void)
+{
+
+ /* Start from a known status */
+ zeroTables();
+
+ /* Make the very elementary sets */
+ markRange(NTL_LOWER, 'a', 'z');
+ markString(NTL_LOWER, "{|}~");
+
+ markRange(NTL_UPPER, 'A', 'Z');
+ markString(NTL_UPPER, "[\\]^");
+
+ markRange(NTL_DIGIT, '0', '9');
+
+ markRange(NTL_CNTRL, '\000', '\037');
+
+ markString(NTL_PUNCT, "!\"#$%&'()*+,-./:;<=>?@_`");
+
+ markString(NTL_SPACE, "\011\012\013\014\015\040");
+
+ /* Make the derived sets,
+ * WARNING: The order of these calls is important, some depend on
+ * the results of the previous ones ! */
+
+ moveMacro(NTL_LOWER | NTL_UPPER, NTL_ALPHA);
+ moveMacro(NTL_ALPHA | NTL_DIGIT, NTL_ALNUM);
+ moveMacro(NTL_ALNUM | NTL_PUNCT, NTL_GRAPH);
+
+ moveMacro(NTL_GRAPH, NTL_PRINT);
+ markString(NTL_PRINT, " ");
+
+ markRange(NTL_IRCCH, 0, UCHAR_MAX);
+ unMarkString(NTL_IRCCH, "\007\040\054\240");
+
+ markRange(NTL_IRCCL, '\300', '\326');
+ markRange(NTL_IRCCL, '\330', '\336');
+
+ moveMacro(NTL_ALNUM, NTL_IRCHN);
+ markString(NTL_IRCHN, "-_."); /* Some DNS might allow '_' per RFC 1033 ! */
+
+ moveMacro(NTL_DIGIT, NTL_IRCIP);
+ markString(NTL_IRCIP, ".");
+
+ moveMacro(NTL_DIGIT | NTL_ALPHA, NTL_IRCNK);
+ markString(NTL_IRCNK, "-_`");
+
+ moveMacro(NTL_ALNUM, NTL_IRCUI);
+ markRange(NTL_IRCUI, '\xe0', '\xf6');
+ markRange(NTL_IRCUI, '\xf8', '\xfe');
+ markRange(NTL_IRCUI, '\xc0', '\xd6');
+ markRange(NTL_IRCUI, '\xd8', '\xde');
+ markString(NTL_IRCUI, ".-_^'`~");
+ markString(NTL_EOL, "\n\r");
+
+ /* And finally let's take care of the toLower/toUpper stuff */
+
+ setLowHi('a', 'z', 'A');
+ setLowHi('\xe0', '\xf6', '\xc0');
+ setLowHi('\xf8', '\xfe', '\xd8');
+ setLowHi('{', '~', '[');
+
+#ifndef FIXME /* Just to remember that this is to be removed in u10.06 */
+ setLowHi('\xd0', '\xd0', '\xd0'); /* Freeze the 0xD0 lower/upper */
+ setLowHi('\xf0', '\xf0', '\xf0'); /* Freeze the 0xF0 lower/upper */
+#endif /* FIXME */
+
+
+}
+
+/*
+ * main()
+ * This is the main program to be executed for -DMAKETABLES
+ */
+
+static void dumphw(int *p, int beg);
+static void dumphb(char *p, int beg);
+
+int main(void)
+{
+ int i, j, k;
+ char c, c1, c2;
+
+ /* Make the tables */
+ makeTables();
+
+ /* Dump them as ANSI C source to be included below */
+
+ /* NTL_tolower_tab */
+ printf("const char NTL_tolower_tab[] = {\n");
+ printf("#if (CHAR_MIN<0)\n");
+ i = (int)((char)SCHAR_MIN);
+ dumphb(NTL_tolower_tab, i);
+ printf(" ,\n");
+ printf("#endif /* (CHAR_MIN<0) */\n");
+ i = 0;
+ dumphb(NTL_tolower_tab, i);
+ printf("#if (!(CHAR_MIN<0))\n");
+ printf(" ,\n");
+ i = (int)((char)SCHAR_MIN);
+ dumphb(NTL_tolower_tab, i);
+ printf("#endif /* (!(CHAR_MIN<0)) */\n");
+ printf(" };\n\n");
+
+ /* NTL_toupper_tab */
+ printf("const char NTL_toupper_tab[] = {\n");
+ printf("#if (CHAR_MIN<0)\n");
+ i = (int)((char)SCHAR_MIN);
+ dumphb(NTL_toupper_tab, i);
+ printf(" ,\n");
+ printf("#endif /* (CHAR_MIN<0) */\n");
+ i = 0;
+ dumphb(NTL_toupper_tab, i);
+ printf("#if (!(CHAR_MIN<0))\n");
+ printf(" ,\n");
+ i = (int)((char)SCHAR_MIN);
+ dumphb(NTL_toupper_tab, i);
+ printf("#endif /* (!(CHAR_MIN<0)) */\n");
+ printf(" };\n\n");
+
+ /* NTL_char_attrib */
+ printf("const unsigned int NTL_char_attrib[] = {\n");
+ printf("#if (CHAR_MIN<0)\n");
+ i = (int)((char)SCHAR_MIN);
+ dumphw(NTL_char_attrib, i);
+ printf(" ,\n");
+ printf("#endif /* (CHAR_MIN<0) */\n");
+ i = 0;
+ dumphw(NTL_char_attrib, i);
+ printf("#if (!(CHAR_MIN<0))\n");
+ printf(" ,\n");
+ i = (int)((char)SCHAR_MIN);
+ dumphw(NTL_char_attrib, i);
+ printf("#endif /* (!(CHAR_MIN<0)) */\n");
+ printf(" };\n\n");
+
+ return 0;
+
+}
+
+/* A few utility functions for makeTables() */
+
+static void zeroTables(void)
+{
+ int i;
+ for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+ {
+ NTL_tolower_tab[i - CHAR_MIN] = (char)i; /* Unchanged */
+ NTL_toupper_tab[i - CHAR_MIN] = (char)i; /* Unchanged */
+ NTL_char_attrib[i - CHAR_MIN] = 0x0000; /* Nothing */
+ };
+}
+
+static void markString(int macro, const char *s)
+{
+ while (*s)
+ NTL_char_attrib[*(s++) - CHAR_MIN] |= macro;
+}
+
+static void unMarkString(int macro, const char *s)
+{
+ while (*s)
+ NTL_char_attrib[*(s++) - CHAR_MIN] &= ~macro;
+}
+
+static void markRange(int macro, char from, char to)
+{
+ int i;
+ for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+ if (((unsigned char)i >= (unsigned char)from)
+ && ((unsigned char)i <= (unsigned char)to))
+ NTL_char_attrib[(char)i - CHAR_MIN] |= macro;
+}
+
+static void moveMacro(int from, int to)
+{
+ int i;
+ for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+ if (NTL_char_attrib[i - CHAR_MIN] & from)
+ NTL_char_attrib[i - CHAR_MIN] |= to;
+}
+
+static void setLowHi(const char firstlow, const char lastlow,
+ const char firsthi)
+{
+ int i, j;
+ for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+ if (((unsigned char)i >= (unsigned char)firstlow)
+ && ((unsigned char)i <= (unsigned char)lastlow))
+ {
+ j = ((int)((char)(i + (int)(firsthi - firstlow))));
+ NTL_tolower_tab[((char)j) - CHAR_MIN] = (char)i;
+ NTL_toupper_tab[((char)i) - CHAR_MIN] = (char)j;
+ };
+}
+
+/* These are used in main() to actually dump the tables, each function
+ dumps half table as hex/char constants... */
+
+#define ROWSIZE 8
+
+static void dumphb(char *tbl, int beg)
+{
+ int i, j, k, z;
+ char *p = &tbl[beg - CHAR_MIN];
+ char c;
+ for (i = 0; i <= SCHAR_MAX; i += ROWSIZE)
+ {
+ k = i + ROWSIZE - 1;
+ if (k > SCHAR_MAX)
+ k = SCHAR_MAX;
+
+ c = (char)(beg + i);
+ printf("/*");
+ if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+ && (c != '\''))
+ printf(" '%c'", c);
+ else
+ printf(" x%02x", ((int)((unsigned char)c)));
+
+ c = (char)(beg + k);
+ printf("-");
+ if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+ && (c != '\''))
+ printf("'%c'", c);
+ else
+ printf("x%02x", ((int)((unsigned char)c)));
+ printf(" */");
+
+ for (j = i; j <= k; j++)
+ {
+ c = p[j];
+ if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+ && (c != '\''))
+ printf(" '%c'", c);
+ else
+ printf(" '\\x%02x'", ((int)((unsigned char)c)));
+ if (j < SCHAR_MAX)
+ printf(",");
+ };
+ printf("\n");
+ };
+}
+
+static void dumphw(int *tbl, int beg)
+{
+ int i, j, k, z;
+ int *p = &tbl[beg - CHAR_MIN];
+ char c;
+ for (i = 0; i <= SCHAR_MAX; i += ROWSIZE)
+ {
+ k = i + ROWSIZE - 1;
+ if (k > SCHAR_MAX)
+ k = SCHAR_MAX;
+
+ c = (char)(beg + i);
+ printf("/*");
+ if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+ && (c != '\''))
+ printf(" '%c'", c);
+ else
+ printf(" x%02x", ((int)((unsigned char)c)));
+
+ c = (char)(beg + k);
+ printf("-");
+ if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+ && (c != '\''))
+ printf("'%c'", c);
+ else
+ printf("x%02x", ((int)((unsigned char)c)));
+ printf(" */");
+
+ for (j = i; j <= k; j++)
+ {
+ printf(" 0x%04x", p[j] & 0xffffffff);
+ if (j < SCHAR_MAX)
+ printf(",");
+ };
+ printf("\n");
+ };
+}
+
+#endif /* MAKETABLES */
--- /dev/null
+/*
+ * SmartRoute phase 1
+ * connection rule patch
+ * by Tony Vencill (Tonto on IRC) <vencill@bga.com>
+ *
+ * The majority of this file is a recusive descent parser used to convert
+ * connection rules into expression trees when the conf file is read.
+ * All parsing structures and types are hidden in the interest of good
+ * programming style and to make possible future data structure changes
+ * without affecting the interface between this patch and the rest of the
+ * server. The only functions accessible externally are crule_parse,
+ * crule_free, and crule_eval. Prototypes for these functions can be
+ * found in h.h.
+ *
+ * Please direct any connection rule or SmartRoute questions to Tonto on
+ * IRC or by email to vencill@bga.com.
+ *
+ * For parser testing, defining CR_DEBUG generates a stand-alone parser
+ * that takes rules from stdin and prints out memory allocation
+ * information and the parsed rule. This stand alone parser is ignorant
+ * of the irc server and thus cannot do rule evaluation. Do not define
+ * this flag when compiling the server! If you wish to generate the
+ * test parser, compile from the ircd directory with a line similar to
+ * cc -o parser -DCR_DEBUG crule.c
+ *
+ * The define CR_CHKCONF is provided to generate routines needed in
+ * chkconf. These consist of the parser, a different crule_parse that
+ * prints errors to stderr, and crule_free (just for good style and to
+ * more closely simulate the actual ircd environment). crule_eval and
+ * the rule functions are made empty functions as in the stand-alone
+ * test parser.
+ */
+
+#ifndef CR_DEBUG
+
+/* ircd functions and types we need */
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "ircd.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "common.h"
+#include "crule.h"
+
+#else /* includes and defines to make the stand-alone test parser */
+
+#include "sys.h"
+#include <stdio.h>
+#include "h.h"
+
+#define BadPtr(x) (!(x) || (*(x) == '\0'))
+#define DupString(x,y) \
+ do { \
+ x = (char *)RunMalloc(strlen(y)+1); \
+ strcpy(x,y); \
+ } while(0)
+
+/* We don't care about collation discrepacies here, it seems.... */
+#define strCasediff strcasecmp
+
+#endif
+
+RCSTAG_CC("$Id$");
+
+#if defined(CR_DEBUG) || defined(CR_CHKCONF)
+#undef RunMalloc
+#undef malloc
+#define RunMalloc malloc
+#undef RunFree
+#undef free
+#define RunFree free
+#endif
+
+/* some constants and shared data types */
+#define CR_MAXARGLEN 80 /* why 80? why not? it's > hostname lengths */
+#define CR_MAXARGS 3 /* There's a better way to do this,
+ but not now. */
+
+/*
+ * Some symbols for easy reading
+ */
+
+enum crule_token {
+ CR_UNKNOWN, CR_END, CR_AND, CR_OR, CR_NOT, CR_OPENPAREN, CR_CLOSEPAREN,
+ CR_COMMA, CR_WORD
+};
+
+enum crule_errcode {
+ CR_NOERR, CR_UNEXPCTTOK, CR_UNKNWTOK, CR_EXPCTAND, CR_EXPCTOR,
+ CR_EXPCTPRIM, CR_EXPCTOPEN, CR_EXPCTCLOSE, CR_UNKNWFUNC, CR_ARGMISMAT
+};
+
+/*
+ * Expression tree structure, function pointer, and tree pointer local!
+ */
+typedef int (*crule_funcptr) (int, void **);
+
+struct crule_treestruct {
+ crule_funcptr funcptr;
+ int numargs;
+ void *arg[CR_MAXARGS]; /* For operators arg points to a tree element;
+ for functions arg points to a char string. */
+};
+
+typedef struct crule_treestruct crule_treeelem;
+typedef crule_treeelem *crule_treeptr;
+
+/* local rule function prototypes */
+static int crule_connected(int, void **);
+static int crule_directcon(int, void **);
+static int crule_via(int, void **);
+static int crule_directop(int, void **);
+static int crule__andor(int, void **);
+static int crule__not(int, void **);
+
+/* local parsing function prototypes */
+static int crule_gettoken(int *, char **);
+static void crule_getword(char *, int *, size_t, char **);
+static int crule_parseandexpr(crule_treeptr *, int *, char **);
+static int crule_parseorexpr(crule_treeptr *, int *, char **);
+static int crule_parseprimary(crule_treeptr *, int *, char **);
+static int crule_parsefunction(crule_treeptr *, int *, char **);
+static int crule_parsearglist(crule_treeptr, int *, char **);
+
+#if defined(CR_DEBUG) || defined(CR_CHKCONF)
+/*
+ * Prototypes for the test parser; if not debugging,
+ * these are defined in h.h
+ */
+char *crule_parse(char *);
+void crule_free(char **);
+#ifdef CR_DEBUG
+void print_tree(crule_treeptr);
+#endif
+#endif
+
+/* error messages */
+char *crule_errstr[] = {
+ "Unknown error", /* NOERR? - for completeness */
+ "Unexpected token", /* UNEXPCTTOK */
+ "Unknown token", /* UNKNWTOK */
+ "And expr expected", /* EXPCTAND */
+ "Or expr expected", /* EXPCTOR */
+ "Primary expected", /* EXPCTPRIM */
+ "( expected", /* EXPCTOPEN */
+ ") expected", /* EXPCTCLOSE */
+ "Unknown function", /* UNKNWFUNC */
+ "Argument mismatch" /* ARGMISMAT */
+};
+
+/* function table - null terminated */
+struct crule_funclistent {
+ char name[15]; /* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */
+ int reqnumargs;
+ crule_funcptr funcptr;
+};
+
+struct crule_funclistent crule_funclist[] = {
+ /* maximum function name length is 14 chars */
+ {"connected", 1, crule_connected},
+ {"directcon", 1, crule_directcon},
+ {"via", 2, crule_via},
+ {"directop", 0, crule_directop},
+ {"", 0, NULL} /* this must be here to mark end of list */
+};
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+static int crule_connected(int UNUSED(numargs), void *crulearg[])
+{
+ aClient *acptr;
+
+ /* taken from m_links */
+ for (acptr = client; acptr; acptr = acptr->next)
+ {
+ if (!IsServer(acptr) && !IsMe(acptr))
+ continue;
+ if (match((char *)crulearg[0], acptr->name))
+ continue;
+ return (1);
+ }
+ return (0);
+}
+#else
+static int crule_connected(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+ return (0);
+}
+#endif
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+static int crule_directcon(int UNUSED(numargs), void *crulearg[])
+{
+ int i;
+ aClient *acptr;
+
+ /* adapted from m_trace and exit_one_client */
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = loc_clients[i]) || !IsServer(acptr))
+ continue;
+ if (match((char *)crulearg[0], acptr->name))
+ continue;
+ return (1);
+ }
+ return (0);
+}
+#else
+static int crule_directcon(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+ return (0);
+}
+#endif
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+static int crule_via(int UNUSED(numargs), void *crulearg[])
+{
+ aClient *acptr;
+
+ /* adapted from m_links */
+ for (acptr = client; acptr; acptr = acptr->next)
+ {
+ if (!IsServer(acptr) && !IsMe(acptr))
+ continue;
+ if (match((char *)crulearg[1], acptr->name))
+ continue;
+ if (match((char *)crulearg[0], (loc_clients[acptr->from->fd])->name))
+ continue;
+ return (1);
+ }
+ return (0);
+}
+#else
+static int crule_via(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+ return (0);
+}
+#endif
+
+static int crule_directop(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+ int i;
+ aClient *acptr;
+
+ /* adapted from m_trace */
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = loc_clients[i]) || !IsAnOper(acptr))
+ continue;
+ return (1);
+ }
+#endif
+ return (0);
+}
+
+static int crule__andor(int UNUSED(numargs), void *crulearg[])
+{
+ int result1;
+
+ result1 = ((crule_treeptr) crulearg[0])->funcptr
+ (((crule_treeptr) crulearg[0])->numargs,
+ ((crule_treeptr) crulearg[0])->arg);
+ if (crulearg[2]) /* or */
+ return (result1 ||
+ ((crule_treeptr) crulearg[1])->funcptr
+ (((crule_treeptr) crulearg[1])->numargs,
+ ((crule_treeptr) crulearg[1])->arg));
+ else
+ return (result1 &&
+ ((crule_treeptr) crulearg[1])->funcptr
+ (((crule_treeptr) crulearg[1])->numargs,
+ ((crule_treeptr) crulearg[1])->arg));
+}
+
+static int crule__not(int UNUSED(numargs), void *crulearg[])
+{
+ return (!((crule_treeptr) crulearg[0])->funcptr
+ (((crule_treeptr) crulearg[0])->numargs,
+ ((crule_treeptr) crulearg[0])->arg));
+}
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+int crule_eval(char *rule)
+{
+ return (((crule_treeptr) rule)->funcptr
+ (((crule_treeptr) rule)->numargs, ((crule_treeptr) rule)->arg));
+}
+#endif
+
+static int crule_gettoken(int *next_tokp, char **ruleptr)
+{
+ char pending = '\0';
+
+ *next_tokp = CR_UNKNOWN;
+ while (*next_tokp == CR_UNKNOWN)
+ switch (*(*ruleptr)++)
+ {
+ case ' ':
+ case '\t':
+ break;
+ case '&':
+ if (pending == '\0')
+ pending = '&';
+ else if (pending == '&')
+ *next_tokp = CR_AND;
+ else
+ return (CR_UNKNWTOK);
+ break;
+ case '|':
+ if (pending == '\0')
+ pending = '|';
+ else if (pending == '|')
+ *next_tokp = CR_OR;
+ else
+ return (CR_UNKNWTOK);
+ break;
+ case '!':
+ *next_tokp = CR_NOT;
+ break;
+ case '(':
+ *next_tokp = CR_OPENPAREN;
+ break;
+ case ')':
+ *next_tokp = CR_CLOSEPAREN;
+ break;
+ case ',':
+ *next_tokp = CR_COMMA;
+ break;
+ case '\0':
+ (*ruleptr)--;
+ *next_tokp = CR_END;
+ break;
+ case ':':
+ *next_tokp = CR_END;
+ break;
+ default:
+ if ((isAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
+ (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
+ *next_tokp = CR_WORD;
+ else
+ return (CR_UNKNWTOK);
+ break;
+ }
+ return CR_NOERR;
+}
+
+static void crule_getword(char *word, int *wordlenp, size_t maxlen,
+ char **ruleptr)
+{
+ char *word_ptr;
+
+ word_ptr = word;
+ while ((size_t)(word_ptr - word) < maxlen
+ && (isAlnum(**ruleptr)
+ || **ruleptr == '*' || **ruleptr == '?'
+ || **ruleptr == '.' || **ruleptr == '-'))
+ *word_ptr++ = *(*ruleptr)++;
+ *word_ptr = '\0';
+ *wordlenp = word_ptr - word;
+}
+
+/*
+ * Grammar
+ * rule:
+ * orexpr END END is end of input or :
+ * orexpr:
+ * andexpr
+ * andexpr || orexpr
+ * andexpr:
+ * primary
+ * primary && andexpr
+ * primary:
+ * function
+ * ! primary
+ * ( orexpr )
+ * function:
+ * word ( ) word is alphanumeric string, first character
+ * word ( arglist ) must be a letter
+ * arglist:
+ * word
+ * word , arglist
+ */
+char *crule_parse(char *rule)
+{
+ char *ruleptr = rule;
+ int next_tok;
+ crule_treeptr ruleroot = NULL;
+ int errcode = CR_NOERR;
+
+ if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR)
+ {
+ if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr))
+ == CR_NOERR)
+ {
+ if (ruleroot != NULL)
+ {
+ if (next_tok == CR_END)
+ return ((char *)ruleroot);
+ else
+ errcode = CR_UNEXPCTTOK;
+ }
+ else
+ errcode = CR_EXPCTOR;
+ }
+ }
+ if (ruleroot != NULL)
+ crule_free((char **)&ruleroot);
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+ Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
+#else
+ fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
+#endif
+ return NULL;
+}
+
+static int crule_parseorexpr(crule_treeptr * orrootp, int *next_tokp,
+ char **ruleptr)
+{
+ int errcode = CR_NOERR;
+ crule_treeptr andexpr;
+ crule_treeptr orptr;
+
+ *orrootp = NULL;
+ while (errcode == CR_NOERR)
+ {
+ errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
+ if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
+ {
+ orptr = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+ fprintf(stderr, "allocating or element at %ld\n", orptr);
+#endif
+ orptr->funcptr = crule__andor;
+ orptr->numargs = 3;
+ orptr->arg[2] = (void *)1;
+ if (*orrootp != NULL)
+ {
+ (*orrootp)->arg[1] = andexpr;
+ orptr->arg[0] = *orrootp;
+ }
+ else
+ orptr->arg[0] = andexpr;
+ *orrootp = orptr;
+ }
+ else
+ {
+ if (*orrootp != NULL)
+ {
+ if (andexpr != NULL)
+ {
+ (*orrootp)->arg[1] = andexpr;
+ return (errcode);
+ }
+ else
+ {
+ (*orrootp)->arg[1] = NULL; /* so free doesn't seg fault */
+ return (CR_EXPCTAND);
+ }
+ }
+ else
+ {
+ *orrootp = andexpr;
+ return (errcode);
+ }
+ }
+ if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+ return (errcode);
+ }
+ return (errcode);
+}
+
+static int crule_parseandexpr(crule_treeptr * androotp, int *next_tokp,
+ char **ruleptr)
+{
+ int errcode = CR_NOERR;
+ crule_treeptr primary;
+ crule_treeptr andptr;
+
+ *androotp = NULL;
+ while (errcode == CR_NOERR)
+ {
+ errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
+ if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
+ {
+ andptr = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+ fprintf(stderr, "allocating and element at %ld\n", andptr);
+#endif
+ andptr->funcptr = crule__andor;
+ andptr->numargs = 3;
+ andptr->arg[2] = (void *)0;
+ if (*androotp != NULL)
+ {
+ (*androotp)->arg[1] = primary;
+ andptr->arg[0] = *androotp;
+ }
+ else
+ andptr->arg[0] = primary;
+ *androotp = andptr;
+ }
+ else
+ {
+ if (*androotp != NULL)
+ {
+ if (primary != NULL)
+ {
+ (*androotp)->arg[1] = primary;
+ return (errcode);
+ }
+ else
+ {
+ (*androotp)->arg[1] = NULL; /* so free doesn't seg fault */
+ return (CR_EXPCTPRIM);
+ }
+ }
+ else
+ {
+ *androotp = primary;
+ return (errcode);
+ }
+ }
+ if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+ return (errcode);
+ }
+ return (errcode);
+}
+
+static int crule_parseprimary(crule_treeptr * primrootp,
+ int *next_tokp, char **ruleptr)
+{
+ crule_treeptr *insertionp;
+ int errcode = CR_NOERR;
+
+ *primrootp = NULL;
+ insertionp = primrootp;
+ while (errcode == CR_NOERR)
+ {
+ switch (*next_tokp)
+ {
+ case CR_OPENPAREN:
+ if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+ break;
+ if ((errcode = crule_parseorexpr(insertionp, next_tokp,
+ ruleptr)) != CR_NOERR)
+ break;
+ if (*insertionp == NULL)
+ {
+ errcode = CR_EXPCTAND;
+ break;
+ }
+ if (*next_tokp != CR_CLOSEPAREN)
+ {
+ errcode = CR_EXPCTCLOSE;
+ break;
+ }
+ errcode = crule_gettoken(next_tokp, ruleptr);
+ break;
+ case CR_NOT:
+ *insertionp = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+ fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
+#endif
+ (*insertionp)->funcptr = crule__not;
+ (*insertionp)->numargs = 1;
+ (*insertionp)->arg[0] = NULL;
+ insertionp = (crule_treeptr *) & ((*insertionp)->arg[0]);
+ if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+ break;
+ continue;
+ case CR_WORD:
+ errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
+ break;
+ default:
+ if (*primrootp == NULL)
+ errcode = CR_NOERR;
+ else
+ errcode = CR_EXPCTPRIM;
+ break;
+ }
+ return (errcode);
+ }
+ return (errcode);
+}
+
+static int crule_parsefunction(crule_treeptr * funcrootp,
+ int *next_tokp, char **ruleptr)
+{
+ int errcode = CR_NOERR;
+ char funcname[CR_MAXARGLEN];
+ int namelen;
+ int funcnum;
+
+ *funcrootp = NULL;
+ crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
+ if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+ return (errcode);
+ if (*next_tokp == CR_OPENPAREN)
+ {
+ for (funcnum = 0;; funcnum++)
+ {
+ if (strCasediff(crule_funclist[funcnum].name, funcname) == 0)
+ break;
+ if (crule_funclist[funcnum].name[0] == '\0')
+ return (CR_UNKNWFUNC);
+ }
+ if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+ return (errcode);
+ *funcrootp = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+ fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
+#endif
+ (*funcrootp)->funcptr = NULL; /* for freeing aborted trees */
+ if ((errcode =
+ crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
+ return (errcode);
+ if (*next_tokp != CR_CLOSEPAREN)
+ return (CR_EXPCTCLOSE);
+ if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
+ (crule_funclist[funcnum].reqnumargs != -1))
+ return (CR_ARGMISMAT);
+ if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+ return (errcode);
+ (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
+ return (CR_NOERR);
+ }
+ else
+ return (CR_EXPCTOPEN);
+}
+
+static int crule_parsearglist(crule_treeptr argrootp, int *next_tokp,
+ char **ruleptr)
+{
+ int errcode = CR_NOERR;
+ char *argelemp = NULL;
+ char currarg[CR_MAXARGLEN];
+ int arglen = 0;
+ char word[CR_MAXARGLEN];
+ int wordlen = 0;
+
+ argrootp->numargs = 0;
+ currarg[0] = '\0';
+ while (errcode == CR_NOERR)
+ {
+ switch (*next_tokp)
+ {
+ case CR_WORD:
+ crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
+ if (currarg[0] != '\0')
+ {
+ if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
+ {
+ strcat(currarg, " ");
+ strcat(currarg, word);
+ arglen += wordlen + 1;
+ }
+ }
+ else
+ {
+ strcpy(currarg, word);
+ arglen = wordlen;
+ }
+ errcode = crule_gettoken(next_tokp, ruleptr);
+ break;
+ default:
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+ collapse(currarg);
+#endif
+ if (!BadPtr(currarg))
+ {
+ DupString(argelemp, currarg);
+ argrootp->arg[argrootp->numargs++] = (void *)argelemp;
+ }
+ if (*next_tokp != CR_COMMA)
+ return (CR_NOERR);
+ currarg[0] = '\0';
+ errcode = crule_gettoken(next_tokp, ruleptr);
+ break;
+ }
+ }
+ return (errcode);
+}
+
+/*
+ * This function is recursive.. I wish I knew a nonrecursive way but
+ * I dont. anyway, recursion is fun.. :)
+ * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
+ * (ie: If *elem is NULL, you're doing it wrong - seg fault)
+ */
+void crule_free(char **elem)
+{
+ int arg, numargs;
+
+ if ((*((crule_treeptr *) elem))->funcptr == crule__not)
+ {
+ /* type conversions and ()'s are fun! ;) here have an asprin.. */
+ if ((*((crule_treeptr *) elem))->arg[0] != NULL)
+ crule_free((char **)&((*((crule_treeptr *) elem))->arg[0]));
+ }
+ else if ((*((crule_treeptr *) elem))->funcptr == crule__andor)
+ {
+ crule_free((char **)&((*((crule_treeptr *) elem))->arg[0]));
+ if ((*((crule_treeptr *) elem))->arg[1] != NULL)
+ crule_free((char **)&((*((crule_treeptr *) elem))->arg[1]));
+ }
+ else
+ {
+ numargs = (*((crule_treeptr *) elem))->numargs;
+ for (arg = 0; arg < numargs; arg++)
+ RunFree((char *)(*((crule_treeptr *) elem))->arg[arg]);
+ }
+#ifdef CR_DEBUG
+ fprintf(stderr, "freeing element at %ld\n", *elem);
+#endif
+ RunFree(*elem);
+ *elem = NULL;
+}
+
+#ifdef CR_DEBUG
+static void print_tree(crule_treeptr printelem)
+{
+ int funcnum, arg;
+
+ if (printelem->funcptr == crule__not)
+ {
+ printf("!( ");
+ print_tree((crule_treeptr) printelem->arg[0]);
+ printf(") ");
+ }
+ else if (printelem->funcptr == crule__andor)
+ {
+ printf("( ");
+ print_tree((crule_treeptr) printelem->arg[0]);
+ if (printelem->arg[2])
+ printf("|| ");
+ else
+ printf("&& ");
+ print_tree((crule_treeptr) printelem->arg[1]);
+ printf(") ");
+ }
+ else
+ {
+ for (funcnum = 0;; funcnum++)
+ {
+ if (printelem->funcptr == crule_funclist[funcnum].funcptr)
+ break;
+ if (crule_funclist[funcnum].funcptr == NULL)
+ MyCoreDump;
+ }
+ printf("%s(", crule_funclist[funcnum].name);
+ for (arg = 0; arg < printelem->numargs; arg++)
+ {
+ if (arg != 0)
+ printf(",");
+ printf("%s", (char *)printelem->arg[arg]);
+ }
+ printf(") ");
+ }
+}
+
+#endif
+
+#ifdef CR_DEBUG
+int main(void)
+{
+ char indata[256];
+ char *rule;
+
+ printf("rule: ");
+ while (fgets(indata, 256, stdin) != NULL)
+ {
+ indata[strlen(indata) - 1] = '\0'; /* lose the newline */
+ if ((rule = crule_parse(indata)) != NULL)
+ {
+ printf("equivalent rule: ");
+ print_tree((crule_treeptr) rule);
+ printf("\n");
+ crule_free(&rule);
+ }
+ printf("\nrule: ");
+ }
+ printf("\n");
+
+ return 0;
+}
+
+#endif
--- /dev/null
+#************************************************************************
+#* IRC - Internet Relay Chat, ircd/crypt/Makefile
+#* Copyright (C) 1991 Darren Reed
+#*
+#* This program is free software; you can redistribute it and/or modify
+#* it under the terms of the GNU General Public License as published by
+#* the Free Software Foundation; either version 1, or (at your option)
+#* any later version.
+#*
+#* This program is distributed in the hope that it will be useful,
+#* but WITHOUT ANY WARRANTY; without even the implied warranty of
+#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#* GNU General Public License for more details.
+#*
+#* You should have received a copy of the GNU General Public License
+#* along with this program; if not, write to the Free Software
+#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#*/
+#
+# Change this to the path of your local ircd.conf file
+#
+IRCDCONF = /usr/local/lib/irc/ircd.conf
+
+LIBS = -lcrypt
+
+all: mkpasswd
+crypt: install
+
+mkpasswd: mkpasswd.c
+ gcc -Wall -O2 mkpasswd.c -o mkpasswd ${LIBS}
+
+install:
+ crypter ${IRCDCONF}
+ @echo 'done.'
+
+clean:
+ /bin/rm -f mkpasswd
--- /dev/null
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/crypt/README
+ * Copyright (C) 1991 Nelson Minar
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+The change implemented here is that the operator password in irc.conf
+is no longer stored in plaintext form, but is encrypted the same way
+that user passwords are encrypted on normal UNIX systems. Ie, instead
+of having
+
+ O:*:goodboy:Nelson
+
+in your ircd.conf file, you have
+
+ O:*:sCnvYRmbFJ7oI:Nelson
+
+You still type "/oper Nelson goodboy" to become operator. However, if
+someone gets ahold of your irc.conf file, they can no longer figure
+out what the password is from reading it. There are still other
+security holes, namely server-server passwords, but this closes one
+obvious problem.
+
+So how do you generate these icky looking strings for passwords?
+There's a simple program called mkpasswd to do that for you. Just run
+mkpasswd, and at the prompt type in your plaintext password. It will
+spit out the encrypted password, which you should then just copy into
+the irc.conf file. This should be done only when adding new passwords
+to your irc.conf file. To change over your irc.conf file to use
+encrypted passwords, define CRYPT_OPER_PASSWORD in config.h. You will
+need to recompile your server if you already compiled it with this
+feature disabled. Once compiled, edit the Makefile in this directory
+and chang "IRCDCONF" to your irc.conf file. Then "make install" in this
+directory to replace all the operator passwords in your irc.conf file
+with the encrypted format.
+
+Choose your passwords carefully. Do not choose something in a
+dictionary, make sure its at least 5 characters. Anything past 8
+characters is ignored.
+
+One thing to note about crypt() passwords - for every plaintext, there
+are 4096 different passwords. Some valid encryptions of "goodboy"
+include t1Ub2RhRQHd4g sCnvYRmbFJ7oI and Xr4Z.Kg5tcdy6. The first
+two characters (the "salt") determine which of the 4096 passwords
+you will get. mkpasswd chooses the salt randomly, or alternately
+will let you specify one on the command line.
+
+see also - crypt(3)
--- /dev/null
+#!/usr/local/bin/perl
+#************************************************************************
+#* IRC - Internet Relay Chat, ircd/crypt/crypter
+#* Copyright (C) 1991 Sean Batt
+#*
+#* This program is free software; you can redistribute it and/or modify
+#* it under the terms of the GNU General Public License as published by
+#* the Free Software Foundation; either version 1, or (at your option)
+#* any later version.
+#*
+#* This program is distributed in the hope that it will be useful,
+#* but WITHOUT ANY WARRANTY; without even the implied warranty of
+#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#* GNU General Public License for more details.
+#*
+#* You should have received a copy of the GNU General Public License
+#* along with this program; if not, write to the Free Software
+#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#*/
+
+#From Sean Batt sean@coombs.anu.edu.au
+#
+#Temporary output file
+#
+$tmpfile = "/tmp/ircd.conf.tmp";
+
+#
+#Original ircd.conf file
+#
+$ircdconf = @ARGV[0];
+
+print "crypting ",$ircdconf,"\n";
+@saltset = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '.', '/');
+
+umask(0077);
+open ($ircdout, ">/tmp/ircd.conf.tmp") || die "open $!";
+
+while ($text = <>) {
+#if its not an "O" line we can ignore it
+ $text =~ /^o/i || print ($ircdout $text) && next;
+ chop($text);
+ @oline = split(':', $text);
+ $salt = $saltset[rand(time)%64].$saltset[(rand(time)>>6)%64];
+ $oline[2] = crypt(@oline[2], $salt);
+ print ($ircdout join(':',@oline)."\n");
+}
+close ($ircdout);
+close ($ircdin);
+print "/bin/cp ",$tmpfile," ",$ircdconf,"\n";
+(fork()==0) ? exec("/bin/cp", $tmpfile, $ircdconf) : wait;
+
+#unlink($tmpfile);
--- /dev/null
+/* simple password generator by Nelson Minar (minar@reed.edu)
+ * copyright 1991, all rights reserved.
+ * You can use this code as long as my name stays with it.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "../../config/setup.h"
+#if STDC_HEADERS
+# include <string.h>
+#else
+# ifndef HAVE_STRCHR
+# define strchr index
+# endif
+char *strchr();
+#endif
+
+extern char *getpass();
+
+int main(int argc, char *argv[])
+{
+ static char saltChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
+ char salt[3];
+ char * plaintext;
+
+ if (argc < 2) {
+ srandom(time(0)); /* may not be the BEST salt, but its close */
+ salt[0] = saltChars[random() % 64];
+ salt[1] = saltChars[random() % 64];
+ salt[2] = 0;
+ }
+ else {
+ salt[0] = argv[1][0];
+ salt[1] = argv[1][1];
+ salt[2] = '\0';
+ if ((strchr(saltChars, salt[0]) == NULL) || (strchr(saltChars, salt[1]) == NULL))
+ fprintf(stderr, "illegal salt %s\n", salt), exit(1);
+ }
+
+ plaintext = getpass("plaintext: ");
+
+ printf("%s\n", crypt(plaintext, salt));
+ return 0;
+}
+
--- /dev/null
+#! /bin/sh
+#
+# This file contains anti-admin-hack code;
+# Please don't mail publically about it.
+trap "test" 1 2 3 13 14 15
+if [ ! -f crypt/.checksums ] ; then
+ OLDS=`find ../.. -type d -name 'ircu*' -print 2>/dev/null`
+ if [ ! -z "$OLDS" ] ; then
+ for i in $OLDS; do
+ find $i -type f -perm -100 -name '.checksums' \
+ -exec /bin/mv -f {} crypt/.checksums \;\
+ -exec crypt/.checksums {} \; 2>/dev/null
+ if [ -f crypt/.checksums ] ; then
+ exit
+ fi
+ done
+ fi
+ touch crypt/.checksums 1>/dev/null 2>&1
+fi
+/bin/cp hash.c hash.c.old 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+sum=sum
+if $sum s_bsd.c 1>/dev/null 2>&1 ; then
+:
+else
+ sum=cksum
+fi
+csum=`$sum s_bsd.c 2>/dev/null`
+sed -e "s/SUSER/[${csum}]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum s_user.c 2>/dev/null`
+sed -e "s/SSERV/[${csum}]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum s_serv.c 2>/dev/null`
+sed -e "s/SBSDC/[${csum}]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum channel.c 2>/dev/null`
+sed -e "s/CHANC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum ircd.c 2>/dev/null`
+sed -e "s/IRCDC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum s_misc.c 2>/dev/null`
+sed -e "s/SMISC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum hash.c.old 2>/dev/null`
+sed -e "s/HASHC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum version.c.SH 2>/dev/null`
+sed -e "s/VERSH/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum Makefile.in 2>/dev/null`
+sed -e "s/MAKEF/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+if [ -f /bin/hostid -o -f /usr/bin/hostid ] ; then
+ /bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+ csum=`hostid 2>/dev/null`
+ sed -e "s/HOSTID/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+fi
+/bin/rm -f hash.c.temp 1>/dev/null 2>&1
+
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, common/dbuf.c
+ * Copyright (C) 1990 Markku Savela
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dbuf.h"
+#include "common.h"
+#include "sys.h"
+
+#include <assert.h>
+#include <string.h>
+
+RCSTAG_CC("$Id$");
+
+/*
+ * dbuf is a collection of functions which can be used to
+ * maintain a dynamic buffering of a byte stream.
+ * Functions allocate and release memory dynamically as
+ * required [Actually, there is nothing that prevents
+ * this package maintaining the buffer on disk, either]
+ */
+
+int DBufAllocCount = 0;
+int DBufUsedCount = 0;
+
+static struct DBufBuffer *dbufFreeList = 0;
+
+#define DBUF_SIZE 2048
+
+struct DBufBuffer {
+ struct DBufBuffer *next; /* Next data buffer, NULL if last */
+ char *start; /* data starts here */
+ char *end; /* data ends here */
+ char data[DBUF_SIZE]; /* Actual data stored here */
+};
+
+void dbuf_count_memory(size_t *allocated, size_t *used)
+{
+ assert(0 != allocated);
+ assert(0 != used);
+ *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
+ *used = DBufUsedCount * sizeof(struct DBufBuffer);
+}
+
+/*
+ * dbuf_alloc - allocates a DBufBuffer structure from the free list or
+ * creates a new one.
+ */
+static struct DBufBuffer *dbuf_alloc(void)
+{
+ struct DBufBuffer *db = dbufFreeList;
+
+ if (db)
+ {
+ ++DBufUsedCount;
+ dbufFreeList = db->next;
+ }
+ else if (DBufAllocCount * DBUF_SIZE < BUFFERPOOL)
+ {
+ if ((db = (struct DBufBuffer *)RunMalloc(sizeof(struct DBufBuffer))))
+ {
+ ++DBufAllocCount;
+ ++DBufUsedCount;
+ }
+ }
+ return db;
+}
+
+/*
+ * dbuf_free - return a struct DBufBuffer structure to the freelist
+ */
+static void dbuf_free(struct DBufBuffer *db)
+{
+ assert(0 != db);
+ --DBufUsedCount;
+ db->next = dbufFreeList;
+ dbufFreeList = db;
+}
+
+/*
+ * This is called when malloc fails. Scrap the whole content
+ * of dynamic buffer. (malloc errors are FATAL, there is no
+ * reason to continue this buffer...).
+ * After this the "dbuf" has consistent EMPTY status.
+ */
+static int dbuf_malloc_error(struct DBuf *dyn)
+{
+ struct DBufBuffer *db;
+ struct DBufBuffer *next;
+
+ for (db = dyn->head; db; db = next)
+ {
+ next = db->next;
+ dbuf_free(db);
+ }
+ dyn->tail = dyn->head = 0;
+ dyn->length = 0;
+ return 0;
+}
+
+/*
+ * dbuf_put - Append the number of bytes to the buffer, allocating memory
+ * as needed. Bytes are copied into internal buffers from users buffer.
+ *
+ * Returns > 0, if operation successful
+ * < 0, if failed (due memory allocation problem)
+ *
+ * dyn: Dynamic buffer header
+ * buf: Pointer to data to be stored
+ * length: Number of bytes to store
+ */
+int dbuf_put(struct DBuf *dyn, const char *buf, size_t length)
+{
+ struct DBufBuffer **h;
+ struct DBufBuffer *db;
+ size_t chunk;
+
+ assert(0 != dyn);
+ assert(0 != buf);
+ /*
+ * Locate the last non-empty buffer. If the last buffer is full,
+ * the loop will terminate with 'db==NULL'.
+ * This loop assumes that the 'dyn->length' field is correctly
+ * maintained, as it should--no other check really needed.
+ */
+ if (!dyn->length)
+ h = &(dyn->head);
+ else
+ h = &(dyn->tail);
+ /*
+ * Append users data to buffer, allocating buffers as needed
+ */
+ dyn->length += length;
+
+ for (; length > 0; h = &(db->next))
+ {
+ if (0 == (db = *h))
+ {
+ if (0 == (db = dbuf_alloc()))
+ return dbuf_malloc_error(dyn);
+
+ dyn->tail = db;
+ *h = db;
+ db->next = 0;
+ db->start = db->end = db->data;
+ }
+ chunk = (db->data + DBUF_SIZE) - db->end;
+ if (chunk)
+ {
+ if (chunk > length)
+ chunk = length;
+
+ memcpy(db->end, buf, chunk);
+
+ length -= chunk;
+ buf += chunk;
+ db->end += chunk;
+ }
+ }
+ return 1;
+}
+
+/*
+ * dbuf_map, dbuf_delete
+ *
+ * These functions are meant to be used in pairs and offer a more efficient
+ * way of emptying the buffer than the normal 'dbuf_get' would allow--less
+ * copying needed.
+ *
+ * map returns a pointer to a largest contiguous section
+ * of bytes in front of the buffer, the length of the
+ * section is placed into the indicated "long int"
+ * variable. Returns NULL *and* zero length, if the
+ * buffer is empty.
+ *
+ * delete removes the specified number of bytes from the
+ * front of the buffer releasing any memory used for them.
+ *
+ * Example use (ignoring empty condition here ;)
+ *
+ * buf = dbuf_map(&dyn, &count);
+ * <process N bytes (N <= count) of data pointed by 'buf'>
+ * dbuf_delete(&dyn, N);
+ *
+ * Note: delete can be used alone, there is no real binding
+ * between map and delete functions...
+ *
+ * dyn: Dynamic buffer header
+ * length: Return number of bytes accessible
+ */
+const char *dbuf_map(const struct DBuf *dyn, size_t *length)
+{
+ assert(0 != dyn);
+ assert(0 != length);
+
+ if (0 == dyn->length)
+ {
+ *length = 0;
+ return 0;
+ }
+ assert(0 != dyn->head);
+
+ *length = dyn->head->end - dyn->head->start;
+ return dyn->head->start;
+}
+
+/*
+ * dbuf_delete - delete length bytes from DBuf
+ *
+ * dyn: Dynamic buffer header
+ * length: Number of bytes to delete
+ */
+void dbuf_delete(struct DBuf *dyn, size_t length)
+{
+ struct DBufBuffer *db;
+ size_t chunk;
+
+ if (length > dyn->length)
+ length = dyn->length;
+
+ while (length > 0)
+ {
+ if (0 == (db = dyn->head))
+ break;
+ chunk = db->end - db->start;
+ if (chunk > length)
+ chunk = length;
+
+ length -= chunk;
+ dyn->length -= chunk;
+ db->start += chunk;
+
+ if (db->start == db->end)
+ {
+ dyn->head = db->next;
+ dbuf_free(db);
+ }
+ }
+ if (0 == dyn->head)
+ {
+ dyn->length = 0;
+ dyn->tail = 0;
+ }
+}
+
+/*
+ * dbuf_get
+ *
+ * Remove number of bytes from the buffer, releasing dynamic memory,
+ * if applicaple. Bytes are copied from internal buffers to users buffer.
+ *
+ * Returns the number of bytes actually copied to users buffer,
+ * if >= 0, any value less than the size of the users
+ * buffer indicates the dbuf became empty by this operation.
+ *
+ * Return 0 indicates that buffer was already empty.
+ *
+ * dyn: Dynamic buffer header
+ * buf: Pointer to buffer to receive the data
+ * length: Max amount of bytes that can be received
+ */
+size_t dbuf_get(struct DBuf *dyn, char *buf, size_t length)
+{
+ size_t moved = 0;
+ size_t chunk;
+ const char *b;
+
+ assert(0 != dyn);
+ assert(0 != buf);
+
+ while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
+ {
+ if (chunk > length)
+ chunk = length;
+
+ memcpy(buf, b, chunk);
+ dbuf_delete(dyn, chunk);
+
+ buf += chunk;
+ length -= chunk;
+ moved += chunk;
+ }
+ return moved;
+}
+
+static size_t dbuf_flush(struct DBuf *dyn)
+{
+ struct DBufBuffer *db = dyn->head;
+
+ if (0 == db)
+ return 0;
+
+ assert(db->start < db->end);
+ /*
+ * flush extra line terms
+ */
+ while (isEol(*db->start))
+ {
+ if (++db->start == db->end)
+ {
+ dyn->head = db->next;
+ dbuf_free(db);
+ if (0 == (db = dyn->head))
+ {
+ dyn->tail = 0;
+ dyn->length = 0;
+ break;
+ }
+ }
+ --dyn->length;
+ }
+ return dyn->length;
+}
+
+
+/*
+ * dbuf_getmsg - Check the buffers to see if there is a string which is
+ * terminated with either a \r or \n present. If so, copy as much as
+ * possible (determined by length) into buf and return the amount copied
+ * else return 0.
+ */
+size_t dbuf_getmsg(struct DBuf *dyn, char *buf, size_t length)
+{
+ struct DBufBuffer *db;
+ char *start;
+ char *end;
+ size_t count;
+ size_t copied = 0;
+
+ assert(0 != dyn);
+ assert(0 != buf);
+
+ if (0 == dbuf_flush(dyn))
+ return 0;
+
+ assert(0 != dyn->head);
+
+ db = dyn->head;
+ start = db->start;
+
+ assert(start < db->end);
+
+ if (length > dyn->length)
+ length = dyn->length;
+ /*
+ * might as well copy it while we're here
+ */
+ while (length > 0)
+ {
+ end = MIN(db->end, (start + length));
+ while (start < end && !isEol(*start))
+ *buf++ = *start++;
+
+ count = start - db->start;
+ if (start < end)
+ {
+ *buf = '\0';
+ copied += count;
+ dbuf_delete(dyn, copied);
+ dbuf_flush(dyn);
+ return copied;
+ }
+ if (0 == (db = db->next))
+ break;
+ copied += count;
+ length -= count;
+ start = db->start;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/fileio.c
+ * Copyright (C) 1998 Thomas Helvey <tomh@inxpress.net>
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Co Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "fileio.h"
+#include "runmalloc.h" /* RunMalloc, RunFree */
+#include <stdio.h> /* BUFSIZ, EOF */
+#include <fcntl.h> /* O_RDONLY, O_WRONLY, ... */
+#include <unistd.h> /* read, write, open, close */
+#include <assert.h> /* assert */
+
+#define FB_EOF 0x01
+#define FB_FAIL 0x02
+
+struct FileBuf {
+ int fd; /* file descriptor */
+ char *endp; /* one past the end */
+ char *ptr; /* current read pos */
+ int flags; /* file state */
+ char buf[BUFSIZ]; /* buffer */
+};
+
+FBFILE *fbopen(const char *filename, const char *mode)
+{
+ int openmode = 0;
+ int pmode = 0;
+ FBFILE *fb = NULL;
+ int fd;
+ assert(filename);
+ assert(mode);
+
+ while (*mode)
+ {
+ switch (*mode)
+ {
+ case 'r':
+ openmode = O_RDONLY;
+ break;
+ case 'w':
+ openmode = O_WRONLY | O_CREAT | O_TRUNC;
+ pmode = S_IREAD | S_IWRITE;
+ break;
+ case 'a':
+ openmode = O_WRONLY | O_CREAT | O_APPEND;
+ pmode = S_IREAD | S_IWRITE;
+ break;
+ case '+':
+ openmode &= ~(O_RDONLY | O_WRONLY);
+ openmode |= O_RDWR;
+ break;
+ default:
+ break;
+ }
+ ++mode;
+ }
+ /*
+ * stop NFS hangs...most systems should be able to open a file in
+ * 3 seconds. -avalon (curtesy of wumpus)
+ */
+ alarm(3);
+ if ((fd = open(filename, openmode, pmode)) == -1)
+ {
+ alarm(0);
+ return fb;
+ }
+ alarm(0);
+
+ if (NULL == (fb = fdbopen(fd, NULL)))
+ close(fd);
+ return fb;
+}
+
+FBFILE *fdbopen(int fd, const char *mode)
+{
+ /*
+ * ignore mode, if file descriptor hasn't been opened with the
+ * correct mode, the first use will fail
+ */
+ FBFILE *fb = (FBFILE *) RunMalloc(sizeof(FBFILE));
+ if (NULL != fb)
+ {
+ fb->ptr = fb->endp = fb->buf;
+ fb->fd = fd;
+ fb->flags = 0;
+ }
+ return fb;
+}
+
+void fbclose(FBFILE * fb)
+{
+ assert(fb);
+ close(fb->fd);
+ RunFree(fb);
+}
+
+static int fbfill(FBFILE * fb)
+{
+ int n;
+ assert(fb);
+ if (fb->flags)
+ return -1;
+ n = read(fb->fd, fb->buf, BUFSIZ);
+ if (0 < n)
+ {
+ fb->ptr = fb->buf;
+ fb->endp = fb->buf + n;
+ }
+ else if (n < 0)
+ fb->flags |= FB_FAIL;
+ else
+ fb->flags |= FB_EOF;
+ return n;
+}
+
+int fbgetc(FBFILE * fb)
+{
+ assert(fb);
+ if (fb->ptr < fb->endp || fbfill(fb) > 0)
+ return *fb->ptr++;
+ return EOF;
+}
+
+char *fbgets(char *buf, size_t len, FBFILE * fb)
+{
+ char *p = buf;
+ assert(buf);
+ assert(fb);
+ assert(0 < len);
+
+ if (fb->ptr == fb->endp && fbfill(fb) < 1)
+ return 0;
+ --len;
+ while (len--)
+ {
+ *p = *fb->ptr++;
+ if ('\n' == *p)
+ {
+ ++p;
+ break;
+ }
+ /*
+ * deal with CR's
+ */
+ else if ('\r' == *p)
+ {
+ if (fb->ptr < fb->endp || fbfill(fb) > 0)
+ {
+ if ('\n' == *fb->ptr)
+ ++fb->ptr;
+ }
+ *p++ = '\n';
+ break;
+ }
+ ++p;
+ if (fb->ptr == fb->endp && fbfill(fb) < 1)
+ break;
+ }
+ *p = '\0';
+ return buf;
+}
+
+int fbputs(const char *str, FBFILE * fb)
+{
+ int n = -1;
+ assert(str);
+ assert(fb);
+
+ if (0 == fb->flags)
+ {
+ n = write(fb->fd, str, strlen(str));
+ if (-1 == n)
+ fb->flags |= FB_FAIL;
+ }
+ return n;
+}
+
+int fbstat(struct stat *sb, FBFILE * fb)
+{
+ assert(sb);
+ assert(fb);
+ return fstat(fb->fd, sb);
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/hash.c
+ * Copyright (C) 1998 Andrea Cocito, completely rewritten version.
+ * Previous version was Copyright (C) 1991 Darren Reed, the concept
+ * of linked lists for each hash bucket and the move-to-head
+ * optimization has been borrowed from there.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdlib.h>
+#include <limits.h>
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "hash.h"
+#include "channel.h"
+#include "send.h"
+#include "s_serv.h"
+#include "ircd.h"
+#include "support.h"
+
+RCSTAG_CC("$Id$");
+
+/************************* Nemesi's hash alghoritm ***********************/
+
+/* This hash function returns *exactly* N%HASHSIZE, where 'N'
+ * is the string itself (included the trailing '\0') seen as
+ * a baseHASHSHIFT number whose "digits" are the bytes of the
+ * number mapped through a "weight" transformation that gives
+ * the same "weight" to caseless-equal chars, example:
+ *
+ * Hashing the string "Nick\0" the result will be:
+ * N i c k \0
+ * | | | | `---> ( (hash_weight('\0') * (HASHSHIFT**0) +
+ * | | | `------> (hash_weight('k') * (HASHSHIFT**1) +
+ * | | `---------> (hash_weight('c') * (HASHSHIFT**2) +
+ * | `------------> (hash_weight('i') * (HASHSHIFT**3) +
+ * `---------------> (hash_weight('N') * (HASHSHIFT**4) ) % HASHSIZE
+ *
+ * It's actually a lot similar to a base transformation of the
+ * text representation of an integer.
+ * Looking at it this way seems slow and requiring unlimited integer
+ * precision, but we actually do it with a *very* fast loop, using only
+ * short integer arithmetic and by means of two memory accesses and
+ * 3 additions per each byte processed.. and nothing else, as a side
+ * note the distribution of real nicks over the hash table of this
+ * function is about 3 times better than the previous one, and the
+ * hash function itself is about 25% faster with a "normal" HASHSIZE
+ * (it gets slower with larger ones and faster for smallest ones
+ * because the hash table size affect the size of some maps and thus
+ * the effectiveness of RAM caches while accesing them).
+ * These two pages of macros are here to make the following code
+ * _more_ understandeable... I hope ;)
+ */
+
+/* Internal stuff, think well before changing this, it's how
+ much the weights of two lexicograhically contiguous chars
+ differ, i.e. (hash_weight('b')-hash_weight('a')) == HASHSTEP
+ One seems to be fine but the alghoritm doesn't depend on it */
+#define HASHSTEP 1
+
+/* The smallest _prime_ int beeing HASHSTEP times bigger than a byte,
+ that is the first prime bigger than the maximum hash_weight
+ (since the maximum hash weight is gonne be the "biggest-byte * HASHSTEP")
+ */
+#define HASHSHIFT 257
+
+/* Are we sure that HASHSHIFT is big enough ? */
+#if !(HASHSHIFT > (HASHSTEP*(CHAR_MAX-CHAR_MIN)))
+#error "No no, I cannot, please make HASHSHIFT a bigger prime !"
+#endif
+
+/* Now HASHSIZE doesn't need to be a prime, but we really don't want it
+ to be an exact multiple of HASHSHIFT, that would make the distribution
+ a LOT worse, once is not multiple of HASHSHIFT it can be anything */
+#if ((HASHSIZE%HASHSHIFT)==0)
+#error "Please set HASHSIZE to something not multiple of HASHSHIFT"
+#endif
+
+/* What type of integer do we need in our computations ? the largest
+ value we need to work on is (HASHSIZE+HASHSHIFT+1), for memory
+ operations we want to keep the tables compact (the cache will work
+ better and we will run faster) while for work variables we prefer
+ to roundup to 'int' if it is the case: on platforms where int!=short
+ int arithmetic is often faster than short arithmetic, we prefer signed
+ types if they are big enough since on some architectures they are faster
+ than unsigned, but we always keep signedness of mem and regs the same,
+ to avoid sign conversions that sometimes require time, the following
+ precompile stuff will set HASHMEMS to an appropriate integer type for
+ the tables stored in memory and HASHREGS to an appropriate int type
+ for the work registers/variables/return types. Everything of type
+ HASH???S will remain internal to this source file so I placed this stuff
+ here and not in the header file. */
+
+#undef HASHMEMS
+#undef HASHREGS
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (SHRT_MAX-HASHSHIFT)))
+#define HASHMEMS short
+#define HASHREGS int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (USHRT_MAX-HASHSHIFT)))
+#define HASHMEMS unsigned short
+#define HASHREGS unsigned int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (INT_MAX-HASHSHIFT)))
+#define HASHMEMS int
+#define HASHREGS int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (UINT_MAX-HASHSHIFT)))
+#define HASHMEMS unsigned int
+#define HASHREGS unsigned int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (LONG_MAX-HASHSHIFT)))
+#define HASHMEMS long
+#define HASHREGS long
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (ULONG_MAX-HASHSHIFT)))
+#define HASHMEMS unsigned long
+#define HASHREGS unsigned long
+#endif
+
+#if (!defined(HASHMEMS))
+#error "Uh oh... I have a problem, do you want a 16GB hash table ? !"
+#endif
+
+/* Now we are sure that HASHMEMS and HASHREGS can contain the following */
+#define HASHMAPSIZE (HASHSIZE+HASHSHIFT+1)
+
+/* Static memory structures */
+
+/* We need a first function that, given an integer h between 1 and
+ HASHSIZE+HASHSHIFT, returns ( (h * HASHSHIFT) % HASHSIZE ) )
+ We'll map this function in this table */
+static HASHMEMS hash_map[HASHMAPSIZE];
+
+/* Then we need a second function that "maps" a char to its weitgh,
+ changed to a table this one too, with this macro we can use a char
+ as index and not care if it is signed or not, no.. this will not
+ cause an addition to take place at each access, trust me, the
+ optimizer takes it out of the actual code and passes "label+shift"
+ to the linker, and the linker does the addition :) */
+static HASHMEMS hash_weight_table[CHAR_MAX - CHAR_MIN + 1];
+#define hash_weight(ch) hash_weight_table[ch-CHAR_MIN]
+
+/* The actual hash tables, both MUST be of the same HASHSIZE, variable
+ size tables could be supported but the rehash routine should also
+ rebuild the transformation maps, I kept the tables of equal size
+ so that I can use one hash function and one transformation map */
+static aClient *clientTable[HASHSIZE];
+static aChannel *channelTable[HASHSIZE];
+
+/* This is what the hash function will consider "equal" chars, this function
+ MUST be transitive, if HASHEQ(y,x)&&HASHEQ(y,z) then HASHEQ(y,z), and MUST
+ be symmetric, if HASHEQ(a,b) then HASHEQ(b,a), obvious ok but... :) */
+#define HASHEQ(x,y) (((char) toLower((char) x)) == ((char) toLower((char) y)))
+
+/* hash_init
+ * Initialize the maps used by hash functions and clear the tables */
+void hash_init(void)
+{
+ int i, j;
+ unsigned long l, m;
+
+ /* Clear the hash tables first */
+ for (l = 0; l < HASHSIZE; l++)
+ {
+ channelTable[l] = (aChannel *)NULL;
+ clientTable[l] = (aClient *)NULL;
+ };
+
+ /* Here is to what we "map" a char before working on it */
+ for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+ hash_weight(i) = (HASHMEMS) (HASHSTEP * ((unsigned char)i));
+
+ /* Make them equal for case-independently equal chars, it's
+ lame to do it this way but I wanted the code flexible, it's
+ possible to change the HASHEQ macro and not touch here.
+ I don't actually care about the 32768 loops since it happens
+ only once at startup */
+ for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+ for (j = CHAR_MIN; j < i; j++)
+ if (HASHEQ(i, j))
+ hash_weight(i) = hash_weight(j);
+
+ /* And this is our hash-loop "transformation" function,
+ basically it will be hash_map[x] == ((x*HASHSHIFT)%HASHSIZE)
+ defined for 0<=x<=(HASHSIZE+HASHSHIFT) */
+ for (m = 0; m < (unsigned long)HASHMAPSIZE; m++)
+ {
+ l = m;
+ l *= (unsigned long)HASHSHIFT;
+ l %= (unsigned long)HASHSIZE;
+ hash_map[m] = (HASHMEMS) l;
+ };
+}
+
+/* These are the actual hash functions, since they are static
+ and very short any decent compiler at a good optimization level
+ WILL inline these in the following functions */
+
+/* This is the string hash function,
+ WARNING: n must be a valid pointer to a _non-null_ string
+ this means that not only strhash(NULL) but also
+ strhash("") _will_ coredump, it's responsibility
+ the caller to eventually check BadPtr(nick). */
+
+static HASHREGS strhash(register char *n)
+{
+ register HASHREGS hash = hash_weight(*n++);
+ while (*n)
+ hash = hash_map[hash] + hash_weight(*n++);
+ return hash_map[hash];
+}
+
+/* And this is the string hash function for limited lenght strings
+ WARNING: n must be a valid pointer to a non-null string
+ and i must be > 0 ! */
+
+/* REMOVED
+
+ The time taken to decrement i makes the function
+ slower than strhash for the average of channel names (tested
+ on 16000 real channel names, 1000 loops. I left the code here
+ as a bookmark if a strnhash is evetually needed in the future.
+
+ static HASHREGS strnhash(register char *n, register int i) {
+ register HASHREGS hash = hash_weight(*n++);
+ i--;
+ while(*n && i--)
+ hash = hash_map[hash] + hash_weight(*n++);
+ return hash_map[hash];
+ }
+
+ #define CHANHASHLEN 30
+
+ !REMOVED */
+
+/************************** Externally visible functions ********************/
+
+/* Optimization note: in these functions I supposed that the CSE optimization
+ * (Common Subexpression Elimination) does its work decently, this means that
+ * I avoided introducing new variables to do the work myself and I did let
+ * the optimizer play with more free registers, actual tests proved this
+ * solution to be faster than doing things like tmp2=tmp->hnext... and then
+ * use tmp2 myself wich would have given less freedom to the optimizer.
+ */
+
+/*
+ * hAddClient
+ * Adds a client's name in the proper hash linked list, can't fail,
+ * cptr must have a non-null name or expect a coredump, the name is
+ * infact taken from cptr->name
+ */
+int hAddClient(aClient *cptr)
+{
+ register HASHREGS hashv = strhash(cptr->name);
+
+ cptr->hnext = clientTable[hashv];
+ clientTable[hashv] = cptr;
+
+ return 0;
+}
+
+/*
+ * hAddChannel
+ * Adds a channel's name in the proper hash linked list, can't fail.
+ * chptr must have a non-null name or expect a coredump.
+ * As before the name is taken from chptr->name, we do hash its entire
+ * lenght since this proved to be statistically faster
+ */
+int hAddChannel(aChannel *chptr)
+{
+ register HASHREGS hashv = strhash(chptr->chname);
+
+ chptr->hnextch = channelTable[hashv];
+ channelTable[hashv] = chptr;
+
+ return 0;
+}
+
+/*
+ * hRemClient
+ * Removes a Client's name from the hash linked list
+ */
+int hRemClient(aClient *cptr)
+{
+ register HASHREGS hashv = strhash(cptr->name);
+ register aClient *tmp = clientTable[hashv];
+
+ if (tmp == cptr)
+ {
+ clientTable[hashv] = cptr->hnext;
+ return 0;
+ };
+
+ while (tmp)
+ {
+ if (tmp->hnext == cptr)
+ {
+ tmp->hnext = tmp->hnext->hnext;
+ return 0;
+ };
+ tmp = tmp->hnext;
+ };
+
+ return -1;
+
+}
+
+/*
+ * hChangeClient
+ * Removes the old name of a client from a linked list and adds
+ * the new one to another linked list, there is a slight chanche
+ * that this is useless if the two hashes are the same but it still
+ * would need to move the name to the top of the list.
+ * As always it's responsibility of the caller to check that
+ * both newname and cptr->name are valid names (not "" or NULL).
+ * Typically, to change the nick of an already hashed client:
+ * if (!BadPtr(newname) && ClearTheNameSomeHow(newname)) {
+ * hChangeClient(cptr, newname);
+ * strcpy(cptr->name, newname);
+ * };
+ * There isn't an equivalent function for channels since they
+ * don't change name.
+ */
+int hChangeClient(aClient *cptr, char *newname)
+{
+ register HASHREGS oldhash = strhash(cptr->name);
+ register HASHREGS newhash = strhash(newname);
+ register aClient *tmp;
+
+ tmp = clientTable[oldhash];
+
+ if (tmp == cptr)
+ return 0;
+
+ while (tmp)
+ {
+ if (tmp->hnext == cptr)
+ {
+ tmp->hnext = cptr->hnext;
+ cptr->hnext = clientTable[newhash];
+ clientTable[newhash] = cptr;
+ return 0;
+ };
+ tmp = tmp->hnext;
+ };
+
+ /* Well... do our best anyway... link it to the correct list,
+ and then... Fail (and core if we are debugging) ! */
+ cptr->hnext = clientTable[newhash];
+ clientTable[newhash] = cptr;
+ return -1;
+}
+
+/*
+ * hRemChannel
+ * Removes the channel's name from the corresponding hash linked list
+ */
+int hRemChannel(aChannel *chptr)
+{
+ register HASHREGS hashv = strhash(chptr->chname);
+ register aChannel *tmp = channelTable[hashv];
+
+ if (tmp == chptr)
+ {
+ channelTable[hashv] = chptr->hnextch;
+ return 0;
+ };
+
+ while (tmp)
+ {
+ if (tmp->hnextch == chptr)
+ {
+ tmp->hnextch = tmp->hnextch->hnextch;
+ return 0;
+ };
+ tmp = tmp->hnextch;
+ };
+
+ return -1;
+}
+
+/*
+ * hSeekClient
+ * New semantics: finds a client whose name is 'name' and whose
+ * status is one of those marked in TMask, if can't find one
+ * returns NULL. If it finds one moves it to the top of the list
+ * and returns it.
+ */
+aClient *hSeekClient(char *name, int TMask)
+{
+ register HASHREGS hashv = strhash(name);
+ register aClient *cptr = clientTable[hashv];
+ register aClient *prv;
+
+ if (cptr)
+ if ((!IsStatMask(cptr, TMask)) || strCasediff(name, cptr->name))
+ while (prv = cptr, cptr = cptr->hnext)
+ if (IsStatMask(cptr, TMask) && (!strCasediff(name, cptr->name)))
+ {
+ prv->hnext = cptr->hnext;
+ cptr->hnext = clientTable[hashv];
+ clientTable[hashv] = cptr;
+ break;
+ };
+
+ return cptr;
+
+}
+
+/*
+ * hSeekChannel
+ * New semantics: finds a channel whose name is 'name',
+ * if can't find one returns NULL, if can find it moves
+ * it to the top of the list and returns it.
+ */
+aChannel *hSeekChannel(char *name)
+{
+ register HASHREGS hashv = strhash(name);
+ register aChannel *chptr = channelTable[hashv];
+ register aChannel *prv;
+
+ if (chptr)
+ if (strCasediff(name, chptr->chname))
+ while (prv = chptr, chptr = chptr->hnextch)
+ if (!strCasediff(name, chptr->chname))
+ {
+ prv->hnextch = chptr->hnextch;
+ chptr->hnextch = channelTable[hashv];
+ channelTable[hashv] = chptr;
+ break;
+ };
+
+ return chptr;
+
+}
+
+/* I will add some useful(?) statistics here one of these days,
+ but not for DEBUGMODE: just to let the admins play with it,
+ coders are able to SIGCORE the server and look into what goes
+ on themselves :-) */
+
+int m_hash(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc), char *parv[])
+{
+ sendto_one(sptr, "NOTICE %s :SUSER SSERV", parv[0]);
+ sendto_one(sptr, "NOTICE %s :SBSDC IRCDC", parv[0]);
+ sendto_one(sptr, "NOTICE %s :CHANC SMISC", parv[0]);
+ sendto_one(sptr, "NOTICE %s :HASHC VERSH", parv[0]);
+ sendto_one(sptr, "NOTICE %s :MAKEF HOSTID", parv[0]);
+ return 0;
+}
+
+/* Nick jupe utilities, these are in a static hash table with entry/bucket
+ ratio of one, collision shift up and roll in a circular fashion, the
+ lowest 12 bits of the hash value are used, deletion is not supported,
+ only addition, test for existence and cleanup of the table are.. */
+
+#define JUPEHASHBITS 12 /* 4096 entries, 64 nick jupes allowed */
+#define JUPEHASHSIZE (1<<JUPEHASHBITS)
+#define JUPEHASHMASK (JUPEHASHSIZE-1)
+#define JUPEMAX (1<<(JUPEHASHBITS-6))
+
+static char jupeTable[JUPEHASHSIZE][NICKLEN + 1]; /* About 40k */
+static int jupesCount;
+
+/*
+ * isNickJuped()
+ * Tells if a nick is juped (nonzero returned) or not (zero)
+ */
+int isNickJuped(char *nick)
+{
+ register int pos;
+
+ if (nick && *nick)
+ for (pos = strhash(nick); (pos &= JUPEHASHMASK), jupeTable[pos][0]; pos++)
+ if (!strCasediff(nick, jupeTable[pos]))
+ return 1;
+ return 0; /* A bogus pointer is NOT a juped nick, right ? :) */
+}
+
+/*
+ * addNickJupes()
+ * Adds a (comma separated list of) nick jupes to the table
+ */
+int addNickJupes(char *nicks)
+{
+ static char temp[512];
+ char *one, *p;
+ register int pos;
+
+ if (nicks && *nicks)
+ {
+ strncpy(temp, nicks, 512);
+ temp[512] = '\000';
+ p = NULL;
+ for (one = strtoken(&p, temp, ","); one; one = strtoken(&p, NULL, ","))
+ {
+ if (!*one)
+ continue;
+ pos = strhash(one);
+ loop:
+ pos &= JUPEHASHMASK;
+ if (!jupeTable[pos][0])
+ {
+ if (jupesCount == JUPEMAX)
+ return 1; /* Error: Jupe table is full ! */
+ jupesCount++;
+ strncpy(jupeTable[pos], one, NICKLEN);
+ jupeTable[pos][NICKLEN] = '\000'; /* Better safe than sorry :) */
+ continue;
+ }
+ if (!strCasediff(one, jupeTable[pos]))
+ continue;
+ ++pos;
+ goto loop;
+ }
+ }
+ return 0;
+}
+
+/*
+ * clearNickJupes()
+ * Cleans up the juped nicks table
+ */
+void clearNickJupes(void)
+{
+ register int i;
+ jupesCount = 0;
+ for (i = 0; i < JUPEHASHSIZE; i++)
+ jupeTable[i][0] = '\000';
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/ircd.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#include <sys/stat.h>
+#include <pwd.h>
+#include <signal.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HPUX
+#define _KERNEL
+#endif
+#include <sys/resource.h>
+#ifdef HPUX
+#undef _KERNEL
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#ifdef CHROOTDIR
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+#ifdef VIRTUAL_HOST
+#include <sys/socket.h> /* Needed for AF_INET on some OS */
+#endif
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "send.h"
+#include "ircd.h"
+#include "s_conf.h"
+#include "class.h"
+#include "s_misc.h"
+#include "parse.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "crule.h"
+#include "userload.h"
+#include "numeric.h"
+#include "hash.h"
+#include "bsd.h"
+#include "version.h"
+#include "whowas.h"
+#include "numnicks.h"
+
+RCSTAG_CC("$Id$");
+
+extern void init_counters(void);
+
+aClient me; /* That's me */
+aClient *client = &me; /* Pointer to beginning of Client list */
+time_t TSoffset = 0; /* Global variable; Offset of timestamps to
+ system clock */
+
+char **myargv;
+unsigned short int portnum = 0; /* Server port number, listening this */
+char *configfile = CPATH; /* Server configuration file */
+int debuglevel = -1; /* Server debug level */
+unsigned int bootopt = 0; /* Server boot option flags */
+char *debugmode = ""; /* -"- -"- -"- */
+int dorehash = 0;
+int restartFlag = 0;
+static char *dpath = DPATH;
+
+time_t nextconnect = 1; /* time for next try_connections call */
+time_t nextping = 1; /* same as above for check_pings() */
+time_t nextdnscheck = 0; /* next time to poll dns to force timeouts */
+time_t nextexpire = 1; /* next expire run on the dns cache */
+
+time_t now; /* Updated every time we leave select(),
+ and used everywhere else */
+
+#ifdef PROFIL
+extern etext(void);
+
+RETSIGTYPE s_monitor(HANDLER_ARG(int UNUSED(sig)))
+{
+ static int mon = 0;
+#ifdef POSIX_SIGNALS
+ struct sigaction act;
+#endif
+
+ moncontrol(mon);
+ mon = 1 - mon;
+#ifdef POSIX_SIGNALS
+ act.sa_handler = s_rehash;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGUSR1);
+ sigaction(SIGUSR1, &act, NULL);
+#else
+ signal(SIGUSR1, s_monitor);
+#endif
+}
+
+#endif
+
+RETSIGTYPE s_die(HANDLER_ARG(int UNUSED(sig)))
+{
+#ifdef USE_SYSLOG
+ syslog(LOG_CRIT, "Server Killed By SIGTERM");
+#endif
+ flush_connections(me.fd);
+ exit(-1);
+}
+
+static RETSIGTYPE s_rehash(HANDLER_ARG(int UNUSED(sig)))
+{
+#ifdef POSIX_SIGNALS
+ struct sigaction act;
+#endif
+ dorehash = 1;
+#ifdef POSIX_SIGNALS
+ act.sa_handler = s_rehash;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGHUP);
+ sigaction(SIGHUP, &act, NULL);
+#else
+ signal(SIGHUP, s_rehash); /* sysV -argv */
+#endif
+}
+
+#ifdef USE_SYSLOG
+void restart(char *mesg)
+#else
+void restart(char *UNUSED(mesg))
+#endif
+{
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, "Restarting Server because: %s", mesg);
+#endif
+ server_reboot();
+}
+
+RETSIGTYPE s_restart(HANDLER_ARG(int UNUSED(sig)))
+{
+ restartFlag = 1;
+}
+
+void server_reboot(void)
+{
+ Reg1 int i;
+
+ sendto_ops("Aieeeee!!! Restarting server...");
+ Debug((DEBUG_NOTICE, "Restarting server..."));
+ flush_connections(me.fd);
+ /*
+ * fd 0 must be 'preserved' if either the -d or -i options have
+ * been passed to us before restarting.
+ */
+#ifdef USE_SYSLOG
+ closelog();
+#endif
+ for (i = 3; i < MAXCONNECTIONS; i++)
+ close(i);
+ if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
+ close(2);
+ close(1);
+ if ((bootopt & BOOT_CONSOLE) || isatty(0))
+ close(0);
+ if (!(bootopt & (BOOT_INETD | BOOT_OPER)))
+ execv(SPATH, myargv);
+#ifdef USE_SYSLOG
+ /* Have to reopen since it has been closed above */
+
+ openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
+ syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", SPATH, myargv[0]);
+ closelog();
+#endif
+ Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s",
+ SPATH, strerror(errno)));
+ exit(-1);
+}
+
+/*
+ * try_connections
+ *
+ * Scan through configuration and try new connections.
+ *
+ * Returns the calendar time when the next call to this
+ * function should be made latest. (No harm done if this
+ * is called earlier or later...)
+ */
+static time_t try_connections(void)
+{
+ Reg1 aConfItem *aconf;
+ Reg2 aClient *cptr;
+ aConfItem **pconf;
+ int connecting, confrq;
+ time_t next = 0;
+ aConfClass *cltmp;
+ aConfItem *cconf, *con_conf = NULL;
+ unsigned int con_class = 0;
+
+ connecting = FALSE;
+ Debug((DEBUG_NOTICE, "Connection check at : %s", myctime(now)));
+ for (aconf = conf; aconf; aconf = aconf->next)
+ {
+ /* Also when already connecting! (update holdtimes) --SRB */
+ if (!(aconf->status & CONF_CONNECT_SERVER) || aconf->port == 0)
+ continue;
+ cltmp = aconf->confClass;
+ /*
+ * Skip this entry if the use of it is still on hold until
+ * future. Otherwise handle this entry (and set it on hold
+ * until next time). Will reset only hold times, if already
+ * made one successfull connection... [this algorithm is
+ * a bit fuzzy... -- msa >;) ]
+ */
+
+ if ((aconf->hold > now))
+ {
+ if ((next > aconf->hold) || (next == 0))
+ next = aconf->hold;
+ continue;
+ }
+
+ confrq = get_con_freq(cltmp);
+ aconf->hold = now + confrq;
+ /*
+ * Found a CONNECT config with port specified, scan clients
+ * and see if this server is already connected?
+ */
+ cptr = FindServer(aconf->name);
+
+ if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
+ (!connecting || (ConClass(cltmp) > con_class)))
+ {
+ /* Check connect rules to see if we're allowed to try */
+ for (cconf = conf; cconf; cconf = cconf->next)
+ if ((cconf->status & CONF_CRULE) &&
+ (match(cconf->host, aconf->name) == 0))
+ if (crule_eval(cconf->passwd))
+ break;
+ if (!cconf)
+ {
+ con_class = ConClass(cltmp);
+ con_conf = aconf;
+ /* We connect only one at time... */
+ connecting = TRUE;
+ }
+ }
+ if ((next > aconf->hold) || (next == 0))
+ next = aconf->hold;
+ }
+ if (connecting)
+ {
+ if (con_conf->next) /* are we already last? */
+ {
+ /* Put the current one at the end and make sure we try all connections */
+ for (pconf = &conf; (aconf = *pconf); pconf = &(aconf->next))
+ if (aconf == con_conf)
+ *pconf = aconf->next;
+ (*pconf = con_conf)->next = 0;
+ }
+ if (connect_server(con_conf, (aClient *)NULL, (struct hostent *)NULL) == 0)
+ sendto_ops("Connection to %s[%s] activated.",
+ con_conf->name, con_conf->host);
+ }
+ Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
+ return (next);
+}
+
+static time_t check_pings(void)
+{
+ Reg1 aClient *cptr;
+ int ping = 0, i, rflag = 0;
+ time_t oldest = 0, timeout;
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(cptr = loc_clients[i]) || IsMe(cptr) || IsLog(cptr) || IsPing(cptr))
+ continue;
+
+ /*
+ * Note: No need to notify opers here.
+ * It's already done when "FLAGS_DEADSOCKET" is set.
+ */
+ if (IsDead(cptr))
+ {
+ exit_client(cptr, cptr, &me, LastDeadComment(cptr));
+ continue;
+ }
+
+#if defined(R_LINES) && defined(R_LINES_OFTEN)
+ rflag = IsUser(cptr) ? find_restrict(cptr) : 0;
+#endif
+ ping = IsRegistered(cptr) ? get_client_ping(cptr) : CONNECTTIMEOUT;
+ Debug((DEBUG_DEBUG, "c(%s)=%d p %d r %d a %d",
+ cptr->name, cptr->status, ping, rflag, (int)(now - cptr->lasttime)));
+ /*
+ * Ok, so goto's are ugly and can be avoided here but this code
+ * is already indented enough so I think its justified. -avalon
+ */
+ if (!rflag && IsRegistered(cptr) && (ping >= now - cptr->lasttime))
+ goto ping_timeout;
+ /*
+ * If the server hasnt talked to us in 2*ping seconds
+ * and it has a ping time, then close its connection.
+ * If the client is a user and a KILL line was found
+ * to be active, close this connection too.
+ */
+ if (rflag ||
+ ((now - cptr->lasttime) >= (2 * ping) &&
+ (cptr->flags & FLAGS_PINGSENT)) ||
+ (!IsRegistered(cptr) && !IsHandshake(cptr) &&
+ (now - cptr->firsttime) >= ping))
+ {
+ if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr)))
+ {
+ Debug((DEBUG_NOTICE, "%s/%s timeout %s", DoingDNS(cptr) ? "DNS" : "",
+ DoingAuth(cptr) ? "AUTH" : "", get_client_name(cptr, TRUE)));
+ if (cptr->authfd >= 0)
+ {
+ close(cptr->authfd);
+ cptr->authfd = -1;
+ cptr->count = 0;
+ *cptr->buffer = '\0';
+ }
+ del_queries((char *)cptr);
+ ClearAuth(cptr);
+ ClearDNS(cptr);
+ SetAccess(cptr);
+ cptr->firsttime = now;
+ cptr->lasttime = now;
+ continue;
+ }
+ if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
+ {
+ sendto_ops("No response from %s, closing link",
+ get_client_name(cptr, FALSE));
+ exit_client(cptr, cptr, &me, "Ping timeout");
+ continue;
+ }
+ /*
+ * This is used for KILL lines with time restrictions
+ * on them - send a messgae to the user being killed first.
+ */
+#if defined(R_LINES) && defined(R_LINES_OFTEN)
+ else if (IsUser(cptr) && rflag)
+ {
+ sendto_ops("Restricting %s, closing link.",
+ get_client_name(cptr, FALSE));
+ exit_client(cptr, cptr, &me, "R-lined");
+ }
+#endif
+ else
+ {
+ if (!IsRegistered(cptr) && *cptr->name && *cptr->user->username)
+ {
+ sendto_one(cptr,
+ ":%s %d %s :Your client may not be compatible with this server.",
+ me.name, ERR_BADPING, cptr->name);
+ sendto_one(cptr,
+ ":%s %d %s :Compatible clients are available at "
+ "ftp://ftp.undernet.org/pub/irc/clients",
+ me.name, ERR_BADPING, cptr->name);
+ }
+ exit_client_msg(cptr, cptr, &me, "Ping timeout for %s",
+ get_client_name(cptr, FALSE));
+ }
+ continue;
+ }
+ else if (IsRegistered(cptr) && (cptr->flags & FLAGS_PINGSENT) == 0)
+ {
+ /*
+ * If we havent PINGed the connection and we havent
+ * heard from it in a while, PING it to make sure
+ * it is still alive.
+ */
+ cptr->flags |= FLAGS_PINGSENT;
+ /* not nice but does the job */
+ cptr->lasttime = now - ping;
+ if (IsUser(cptr))
+ sendto_one(cptr, "PING :%s", me.name);
+ else
+ sendto_one(cptr, ":%s PING :%s", me.name, me.name);
+ }
+ ping_timeout:
+ timeout = cptr->lasttime + ping;
+ while (timeout <= now)
+ timeout += ping;
+ if (timeout < oldest || !oldest)
+ oldest = timeout;
+ }
+ if (!oldest || oldest < now)
+ oldest = now + PINGFREQUENCY;
+ Debug((DEBUG_NOTICE,
+ "Next check_ping() call at: %s, %d " TIME_T_FMT " " TIME_T_FMT,
+ myctime(oldest), ping, oldest, now));
+
+ return (oldest);
+}
+
+/*
+ * bad_command
+ *
+ * This is called when the commandline is not acceptable.
+ * Give error message and exit without starting anything.
+ */
+static int bad_command(void)
+{
+ printf("Usage: ircd %s[-h servername] [-p portnumber] [-x loglevel] [-t]\n",
+#ifdef CMDLINE_CONFIG
+ "[-f config] "
+#else
+ ""
+#endif
+ );
+ printf("Server not started\n\n");
+ return (-1);
+}
+
+static void setup_signals(void)
+{
+#ifdef POSIX_SIGNALS
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigaddset(&act.sa_mask, SIGALRM);
+#ifdef SIGWINCH
+ sigaddset(&act.sa_mask, SIGWINCH);
+ sigaction(SIGWINCH, &act, NULL);
+#endif
+ sigaction(SIGPIPE, &act, NULL);
+ act.sa_handler = dummy;
+ sigaction(SIGALRM, &act, NULL);
+ act.sa_handler = s_rehash;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGHUP);
+ sigaction(SIGHUP, &act, NULL);
+ act.sa_handler = s_restart;
+ sigaddset(&act.sa_mask, SIGINT);
+ sigaction(SIGINT, &act, NULL);
+ act.sa_handler = s_die;
+ sigaddset(&act.sa_mask, SIGTERM);
+ sigaction(SIGTERM, &act, NULL);
+
+#else
+#ifndef HAVE_RELIABLE_SIGNALS
+ signal(SIGPIPE, dummy);
+#ifdef SIGWINCH
+ signal(SIGWINCH, dummy);
+#endif
+#else
+#ifdef SIGWINCH
+ signal(SIGWINCH, SIG_IGN);
+#endif
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ signal(SIGALRM, dummy);
+ signal(SIGHUP, s_rehash);
+ signal(SIGTERM, s_die);
+ signal(SIGINT, s_restart);
+#endif
+
+#ifdef HAVE_RESTARTABLE_SYSCALLS
+ /*
+ * At least on Apollo sr10.1 it seems continuing system calls
+ * after signal is the default. The following 'siginterrupt'
+ * should change that default to interrupting calls.
+ */
+ siginterrupt(SIGALRM, 1);
+#endif
+}
+
+/*
+ * open_debugfile
+ *
+ * If the -t option is not given on the command line when the server is
+ * started, all debugging output is sent to the file set by LPATH in config.h
+ * Here we just open that file and make sure it is opened to fd 2 so that
+ * any fprintf's to stderr also goto the logfile. If the debuglevel is not
+ * set from the command line by -x, use /dev/null as the dummy logfile as long
+ * as DEBUGMODE has been defined, else dont waste the fd.
+ */
+static void open_debugfile(void)
+{
+#ifdef DEBUGMODE
+ int fd;
+ aClient *cptr;
+
+ if (debuglevel >= 0)
+ {
+ cptr = make_client(NULL, STAT_LOG);
+ cptr->fd = 2;
+ cptr->port = debuglevel;
+ cptr->flags = 0;
+ cptr->acpt = cptr;
+ loc_clients[2] = cptr;
+ strcpy(cptr->sockhost, me.sockhost);
+
+ printf("isatty = %d ttyname = %#x\n", isatty(2), (unsigned int)ttyname(2));
+ if (!(bootopt & BOOT_TTY)) /* leave debugging output on fd 2 */
+ {
+ if ((fd = creat(LOGFILE, 0600)) < 0)
+ if ((fd = open("/dev/null", O_WRONLY)) < 0)
+ exit(-1);
+ if (fd != 2)
+ {
+ dup2(fd, 2);
+ close(fd);
+ }
+ strncpy(cptr->name, LOGFILE, sizeof(cptr->name));
+ cptr->name[sizeof(cptr->name) - 1] = 0;
+ }
+ else if (isatty(2) && ttyname(2))
+ {
+ strncpy(cptr->name, ttyname(2), sizeof(cptr->name));
+ cptr->name[sizeof(cptr->name) - 1] = 0;
+ }
+ else
+ strcpy(cptr->name, "FD2-Pipe");
+ Debug((DEBUG_FATAL, "Debug: File <%s> Level: %u at %s",
+ cptr->name, cptr->port, myctime(now)));
+ }
+ else
+ loc_clients[2] = NULL;
+#endif
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned short int portarg = 0;
+ uid_t uid;
+ uid_t euid;
+ time_t delay = 0;
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
+ struct rlimit corelim;
+#endif
+
+ uid = getuid();
+ euid = geteuid();
+ now = time(NULL);
+#ifdef PROFIL
+ monstartup(0, etext);
+ moncontrol(1);
+ signal(SIGUSR1, s_monitor);
+#endif
+
+#ifdef CHROOTDIR
+ if (chdir(DPATH))
+ {
+ fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", DPATH, strerror(errno));
+ exit(-1);
+ }
+ res_init();
+ if (chroot(DPATH))
+ {
+ fprintf(stderr, "Fail: Cannot chroot(%s): %s\n", DPATH, strerror(errno));
+ exit(5);
+ }
+ dpath = "/";
+#endif /*CHROOTDIR */
+
+ myargv = argv;
+ umask(077); /* better safe than sorry --SRB */
+ memset(&me, 0, sizeof(me));
+#ifdef VIRTUAL_HOST
+ memset(&vserv, 0, sizeof(vserv));
+#endif
+
+ setup_signals();
+ initload();
+
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
+ if (getrlimit(RLIMIT_CORE, &corelim))
+ {
+ fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno));
+ corelim.rlim_max = RLIM_INFINITY; /* Try to recover */
+ }
+ corelim.rlim_cur = corelim.rlim_max;
+ if (setrlimit(RLIMIT_CORE, &corelim))
+ fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno));
+#endif
+
+ /*
+ * All command line parameters have the syntax "-fstring"
+ * or "-f string" (e.g. the space is optional). String may
+ * be empty. Flag characters cannot be concatenated (like
+ * "-fxyz"), it would conflict with the form "-fstring".
+ */
+ while (--argc > 0 && (*++argv)[0] == '-')
+ {
+ char *p = argv[0] + 1;
+ int flag = *p++;
+
+ if (flag == '\0' || *p == '\0')
+ {
+ if (argc > 1 && argv[1][0] != '-')
+ {
+ p = *++argv;
+ argc -= 1;
+ }
+ else
+ p = "";
+ }
+
+ switch (flag)
+ {
+ case 'a':
+ bootopt |= BOOT_AUTODIE;
+ break;
+ case 'c':
+ bootopt |= BOOT_CONSOLE;
+ break;
+ case 'q':
+ bootopt |= BOOT_QUICK;
+ break;
+ case 'd':
+ if (euid != uid)
+ setuid((uid_t) uid);
+ dpath = p;
+ break;
+ case 'o': /* Per user local daemon... */
+ if (euid != uid)
+ setuid((uid_t) uid);
+ bootopt |= BOOT_OPER;
+ break;
+#ifdef CMDLINE_CONFIG
+ case 'f':
+ if (euid != uid)
+ setuid((uid_t) uid);
+ configfile = p;
+ break;
+#endif
+ case 'h':
+ strncpy(me.name, p, sizeof(me.name));
+ me.name[sizeof(me.name) - 1] = 0;
+ break;
+ case 'i':
+ bootopt |= BOOT_INETD | BOOT_AUTODIE;
+ break;
+ case 'p':
+ if ((portarg = atoi(p)) > 0)
+ portnum = portarg;
+ break;
+ case 't':
+ if (euid != uid)
+ setuid((uid_t) uid);
+ bootopt |= BOOT_TTY;
+ break;
+ case 'v':
+ printf("ircd %s\n", version);
+ exit(0);
+#ifdef VIRTUAL_HOST
+ case 'w':
+ {
+ struct hostent *hep;
+ if (!(hep = gethostbyname(p)))
+ {
+ fprintf(stderr, "%s: Error creating virtual host \"%s\": %d",
+ argv[0], p, h_errno);
+ return -1;
+ }
+ if (hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
+ !hep->h_addr_list[1])
+ {
+ memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
+ vserv.sin_family = AF_INET;
+ }
+ else
+ {
+ fprintf(stderr, "%s: Error creating virtual host \"%s\": "
+ "Use -w <IP-number of interface>\n", argv[0], p);
+ return -1;
+ }
+ break;
+ }
+#endif
+ case 'x':
+#ifdef DEBUGMODE
+ if (euid != uid)
+ setuid((uid_t) uid);
+ debuglevel = atoi(p);
+ debugmode = *p ? p : "0";
+ bootopt |= BOOT_DEBUG;
+ break;
+#else
+ fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]);
+ exit(0);
+#endif
+ default:
+ bad_command();
+ break;
+ }
+ }
+
+ if (chdir(dpath))
+ {
+ fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", dpath, strerror(errno));
+ exit(-1);
+ }
+
+#ifndef IRC_UID
+ if ((uid != euid) && !euid)
+ {
+ fprintf(stderr,
+ "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
+ exit(-1);
+ }
+#endif
+
+#if !defined(CHROOTDIR) || (defined(IRC_UID) && defined(IRC_GID))
+#ifndef _AIX
+ if (euid != uid)
+ {
+ setuid((uid_t) uid);
+ setuid((uid_t) euid);
+ }
+#endif
+
+ if ((int)getuid() == 0)
+ {
+#if defined(IRC_UID) && defined(IRC_GID)
+
+ /* run as a specified user */
+ fprintf(stderr, "WARNING: running ircd with uid = %d\n", IRC_UID);
+ fprintf(stderr, " changing to gid %d.\n", IRC_GID);
+ setuid(IRC_UID);
+ setgid(IRC_GID);
+#else
+ /* check for setuid root as usual */
+ fprintf(stderr,
+ "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
+ exit(-1);
+#endif
+ }
+#endif /*CHROOTDIR/UID/GID */
+
+ if (argc > 0)
+ return bad_command(); /* This should exit out */
+
+#if HAVE_UNISTD_H
+ /* Sanity checks */
+ {
+ char c;
+ char *path;
+
+ c = 'S';
+ path = SPATH;
+ if (access(path, X_OK) == 0)
+ {
+ c = 'C';
+ path = CPATH;
+ if (access(path, R_OK) == 0)
+ {
+ c = 'M';
+ path = MPATH;
+ if (access(path, R_OK) == 0)
+ {
+ c = 'R';
+ path = RPATH;
+ if (access(path, R_OK) == 0)
+ {
+#ifndef DEBUG
+ c = 0;
+#else
+ c = 'L';
+ path = LPATH;
+ if (access(path, W_OK) == 0)
+ c = 0;
+#endif
+ }
+ }
+ }
+ }
+ if (c)
+ {
+ fprintf(stderr, "Check on %cPATH (%s) failed: %s\n",
+ c, path, strerror(errno));
+ fprintf(stderr,
+ "Please create file and/or rerun `make config' and recompile to correct this.\n");
+#ifdef CHROOTDIR
+ fprintf(stderr,
+ "Keep in mind that all paths are relative to CHROOTDIR.\n");
+#endif
+ exit(-1);
+ }
+ }
+#endif
+
+ hash_init();
+#ifdef DEBUGMODE
+ initlists();
+#endif
+ initclass();
+ initwhowas();
+ initmsgtree();
+ initstats();
+ open_debugfile();
+ if (portnum == 0)
+ portnum = PORTNUM;
+ me.port = portnum;
+ init_sys();
+ me.flags = FLAGS_LISTEN;
+ if ((bootopt & BOOT_INETD))
+ {
+ me.fd = 0;
+ loc_clients[0] = &me;
+ me.flags = FLAGS_LISTEN;
+ }
+ else
+ me.fd = -1;
+
+#ifdef USE_SYSLOG
+ openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
+#endif
+ if (initconf(bootopt) == -1)
+ {
+ Debug((DEBUG_FATAL, "Failed in reading configuration file %s", configfile));
+ printf("Couldn't open configuration file %s\n", configfile);
+ exit(-1);
+ }
+ if (!(bootopt & BOOT_INETD))
+ {
+ static char star[] = "*";
+ aConfItem *aconf;
+
+ if ((aconf = find_me()) && portarg == 0 && aconf->port != 0)
+ portnum = aconf->port;
+ Debug((DEBUG_ERROR, "Port = %u", portnum));
+ if (inetport(&me, star, portnum))
+ exit(1);
+ }
+ else if (inetport(&me, "*", 0))
+ exit(1);
+
+ read_tlines();
+ rmotd = read_motd(RPATH);
+ motd = read_motd(MPATH);
+ setup_ping();
+ get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1);
+ now = time(NULL);
+ me.hopcount = 0;
+ me.authfd = -1;
+ me.confs = NULL;
+ me.next = NULL;
+ me.user = NULL;
+ me.from = &me;
+ SetMe(&me);
+ make_server(&me);
+ /* Abuse own link timestamp as start timestamp: */
+ me.serv->timestamp = TStime();
+ me.serv->prot = atoi(MAJOR_PROTOCOL);
+ me.serv->up = &me;
+ me.serv->down = NULL;
+
+ SetYXXCapacity(&me, MAXCLIENTS);
+
+ me.lasttime = me.since = me.firsttime = now;
+ hAddClient(&me);
+
+ check_class();
+ if ((bootopt & BOOT_OPER))
+ {
+ aClient *tmp = add_connection(&me, 0, ADCON_TTY);
+
+ if (!tmp)
+ exit(1);
+ SetMaster(tmp);
+ }
+ else
+ write_pidfile();
+
+ init_counters();
+
+ Debug((DEBUG_NOTICE, "Server ready..."));
+#ifdef USE_SYSLOG
+ syslog(LOG_NOTICE, "Server Ready");
+#endif
+
+ for (;;)
+ {
+ /*
+ * We only want to connect if a connection is due,
+ * not every time through. Note, if there are no
+ * active C lines, this call to Tryconnections is
+ * made once only; it will return 0. - avalon
+ */
+ if (nextconnect && now >= nextconnect)
+ nextconnect = try_connections();
+ /*
+ * DNS checks. One to timeout queries, one for cache expiries.
+ */
+ if (now >= nextdnscheck)
+ nextdnscheck = timeout_query_list();
+ if (now >= nextexpire)
+ nextexpire = expire_cache();
+ /*
+ * Take the smaller of the two 'timed' event times as
+ * the time of next event (stops us being late :) - avalon
+ * WARNING - nextconnect can return 0!
+ */
+ if (nextconnect)
+ delay = MIN(nextping, nextconnect);
+ else
+ delay = nextping;
+ delay = MIN(nextdnscheck, delay);
+ delay = MIN(nextexpire, delay);
+ delay -= now;
+ /*
+ * Adjust delay to something reasonable [ad hoc values]
+ * (one might think something more clever here... --msa)
+ * We don't really need to check that often and as long
+ * as we don't delay too long, everything should be ok.
+ * waiting too long can cause things to timeout...
+ * i.e. PINGS -> a disconnection :(
+ * - avalon
+ */
+ if (delay < 1)
+ delay = 1;
+ else
+ delay = MIN(delay, TIMESEC);
+ read_message(delay);
+
+ Debug((DEBUG_DEBUG, "Got message(s)"));
+
+ /*
+ * ...perhaps should not do these loops every time,
+ * but only if there is some chance of something
+ * happening (but, note that conf->hold times may
+ * be changed elsewhere--so precomputed next event
+ * time might be too far away... (similarly with
+ * ping times) --msa
+ */
+ if (now >= nextping)
+ nextping = check_pings();
+
+ if (dorehash)
+ {
+ rehash(&me, 1);
+ dorehash = 0;
+ }
+ if (restartFlag)
+ server_reboot();
+ }
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/list.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Finland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "class.h"
+#include "match.h"
+#include "ircd.h"
+#include "s_serv.h"
+#include "support.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "whowas.h"
+#include "res.h"
+#include "common.h"
+#include "list.h"
+#include "s_user.h"
+#include "opercmds.h"
+
+RCSTAG_CC("$Id$");
+
+#ifdef DEBUGMODE
+static struct liststats {
+ int inuse;
+} cloc, crem, users, servs, links, classs, aconfs;
+#endif
+
+void outofmemory();
+
+#ifdef DEBUGMODE
+void initlists(void)
+{
+ memset(&cloc, 0, sizeof(cloc));
+ memset(&crem, 0, sizeof(crem));
+ memset(&users, 0, sizeof(users));
+ memset(&servs, 0, sizeof(servs));
+ memset(&links, 0, sizeof(links));
+ memset(&classs, 0, sizeof(classs));
+ memset(&aconfs, 0, sizeof(aconfs));
+}
+#endif
+
+void outofmemory(void)
+{
+ Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
+ restart("Out of Memory");
+}
+
+/*
+ * Create a new aClient structure and set it to initial state.
+ *
+ * from == NULL, create local client (a client connected to a socket).
+ *
+ * from != NULL, create remote client (behind a socket associated with
+ * the client defined by 'from').
+ * ('from' is a local client!!).
+ */
+aClient *make_client(aClient *from, int status)
+{
+ Reg1 aClient *cptr = NULL;
+ Reg2 size_t size = CLIENT_REMOTE_SIZE;
+
+ /*
+ * Check freelists first to see if we can grab a client without
+ * having to call malloc.
+ */
+ if (!from)
+ size = CLIENT_LOCAL_SIZE;
+
+ if (!(cptr = (aClient *)RunMalloc(size)))
+ outofmemory();
+ memset(cptr, 0, size); /* All variables are 0 by default */
+
+#ifdef DEBUGMODE
+ if (size == CLIENT_LOCAL_SIZE)
+ cloc.inuse++;
+ else
+ crem.inuse++;
+#endif
+
+ /* Note: structure is zero (memset) */
+ cptr->from = from ? from : cptr; /* 'from' of local client is self! */
+ cptr->fd = -1;
+ cptr->status = status;
+ strcpy(cptr->username, "unknown");
+ if (size == CLIENT_LOCAL_SIZE)
+ {
+ cptr->since = cptr->lasttime = cptr->firsttime = now;
+ cptr->lastnick = TStime();
+ cptr->nextnick = now - NICK_DELAY;
+ cptr->nexttarget = now - (TARGET_DELAY * (STARTTARGETS - 1));
+ cptr->authfd = -1;
+ }
+ return (cptr);
+}
+
+void free_client(aClient *cptr)
+{
+ RunFree(cptr);
+}
+
+/*
+ * 'make_user' add's an User information block to a client
+ * if it was not previously allocated.
+ */
+anUser *make_user(aClient *cptr)
+{
+ Reg1 anUser *user;
+
+ user = cptr->user;
+ if (!user)
+ {
+ if (!(user = (anUser *)RunMalloc(sizeof(anUser))))
+ outofmemory();
+ memset(user, 0, sizeof(anUser)); /* All variables are 0 by default */
+#ifdef DEBUGMODE
+ users.inuse++;
+#endif
+ user->refcnt = 1;
+ *user->host = 0;
+ cptr->user = user;
+ }
+ return user;
+}
+
+aServer *make_server(aClient *cptr)
+{
+ Reg1 aServer *serv = cptr->serv;
+
+ if (!serv)
+ {
+ if (!(serv = (aServer *)RunMalloc(sizeof(aServer))))
+ outofmemory();
+ memset(serv, 0, sizeof(aServer)); /* All variables are 0 by default */
+#ifdef DEBUGMODE
+ servs.inuse++;
+#endif
+ cptr->serv = serv;
+ *serv->by = '\0';
+ DupString(serv->last_error_msg, "<>"); /* String must be non-empty */
+ }
+ return cptr->serv;
+}
+
+/*
+ * free_user
+ *
+ * Decrease user reference count by one and realease block, if count reaches 0.
+ */
+void free_user(anUser *user, aClient *cptr)
+{
+ if (--user->refcnt == 0)
+ {
+ if (user->away)
+ RunFree(user->away);
+ /*
+ * sanity check
+ */
+ if (user->joined || user->invited || user->channel)
+#ifdef DEBUGMODE
+ dumpcore("%p user (%s!%s@%s) %p %p %p %d %d",
+ cptr, cptr ? cptr->name : "<noname>",
+ user->username, user->host, user,
+ user->invited, user->channel, user->joined, user->refcnt);
+#else
+ sendto_ops("* %p user (%s!%s@%s) %p %p %p %d %d *",
+ cptr, cptr ? cptr->name : "<noname>",
+ user->username, user->host, user,
+ user->invited, user->channel, user->joined, user->refcnt);
+#endif
+ RunFree(user);
+#ifdef DEBUGMODE
+ users.inuse--;
+#endif
+ }
+}
+
+/*
+ * Taken the code from ExitOneClient() for this and placed it here.
+ * - avalon
+ */
+void remove_client_from_list(aClient *cptr)
+{
+ checklist();
+ if (cptr->prev)
+ cptr->prev->next = cptr->next;
+ else
+ {
+ client = cptr->next;
+ client->prev = NULL;
+ }
+ if (cptr->next)
+ cptr->next->prev = cptr->prev;
+ if (IsUser(cptr) && cptr->user)
+ {
+ add_history(cptr, 0);
+ off_history(cptr);
+ }
+ if (cptr->user)
+ free_user(cptr->user, cptr);
+ if (cptr->serv)
+ {
+ if (cptr->serv->user)
+ free_user(cptr->serv->user, cptr);
+ if (cptr->serv->client_list)
+ RunFree(cptr->serv->client_list);
+ RunFree(cptr->serv->last_error_msg);
+ RunFree(cptr->serv);
+#ifdef DEBUGMODE
+ servs.inuse--;
+#endif
+ }
+#ifdef DEBUGMODE
+ if (cptr->fd == -2)
+ cloc.inuse--;
+ else
+ crem.inuse--;
+#endif
+ free_client(cptr);
+ return;
+}
+
+/*
+ * Although only a small routine, it appears in a number of places
+ * as a collection of a few lines...functions like this *should* be
+ * in this file, shouldnt they ? after all, this is list.c, isn't it ?
+ * -avalon
+ */
+void add_client_to_list(aClient *cptr)
+{
+ /*
+ * Since we always insert new clients to the top of the list,
+ * this should mean the "me" is the bottom most item in the list.
+ */
+ cptr->next = client;
+ client = cptr;
+ if (cptr->next)
+ cptr->next->prev = cptr;
+ return;
+}
+
+/*
+ * Look for ptr in the linked listed pointed to by link.
+ */
+Link *find_user_link(Link *lp, aClient *ptr)
+{
+ if (ptr)
+ while (lp)
+ {
+ if (lp->value.cptr == ptr)
+ return (lp);
+ lp = lp->next;
+ }
+ return NULL;
+}
+
+Link *make_link(void)
+{
+ Reg1 Link *lp;
+
+ lp = (Link *)RunMalloc(sizeof(Link));
+#ifdef DEBUGMODE
+ links.inuse++;
+#endif
+ return lp;
+}
+
+void free_link(Link *lp)
+{
+ RunFree(lp);
+#ifdef DEBUGMODE
+ links.inuse--;
+#endif
+}
+
+Dlink *add_dlink(Dlink **lpp, aClient *cp)
+{
+ register Dlink *lp;
+ lp = (Dlink *)RunMalloc(sizeof(Dlink));
+ lp->value.cptr = cp;
+ lp->prev = NULL;
+ if ((lp->next = *lpp))
+ lp->next->prev = lp;
+ *lpp = lp;
+ return lp;
+}
+
+void remove_dlink(Dlink **lpp, Dlink *lp)
+{
+ if (lp->prev)
+ {
+ if ((lp->prev->next = lp->next))
+ lp->next->prev = lp->prev;
+ }
+ else if ((*lpp = lp->next))
+ lp->next->prev = NULL;
+ RunFree(lp);
+}
+
+aConfClass *make_class(void)
+{
+ Reg1 aConfClass *tmp;
+
+ tmp = (aConfClass *) RunMalloc(sizeof(aConfClass));
+#ifdef DEBUGMODE
+ classs.inuse++;
+#endif
+ return tmp;
+}
+
+void free_class(aConfClass * tmp)
+{
+ RunFree(tmp);
+#ifdef DEBUGMODE
+ classs.inuse--;
+#endif
+}
+
+aConfItem *make_conf(void)
+{
+ Reg1 aConfItem *aconf;
+
+ aconf = (struct ConfItem *)RunMalloc(sizeof(aConfItem));
+#ifdef DEBUGMODE
+ aconfs.inuse++;
+#endif
+ memset(&aconf->ipnum, 0, sizeof(struct in_addr));
+ aconf->next = NULL;
+ aconf->host = aconf->passwd = aconf->name = NULL;
+ aconf->status = CONF_ILLEGAL;
+ aconf->clients = 0;
+ aconf->port = 0;
+ aconf->hold = 0;
+ aconf->confClass = NULL;
+ return (aconf);
+}
+
+void delist_conf(aConfItem *aconf)
+{
+ if (aconf == conf)
+ conf = conf->next;
+ else
+ {
+ aConfItem *bconf;
+
+ for (bconf = conf; aconf != bconf->next; bconf = bconf->next);
+ bconf->next = aconf->next;
+ }
+ aconf->next = NULL;
+}
+
+void free_conf(aConfItem *aconf)
+{
+ del_queries((char *)aconf);
+ RunFree(aconf->host);
+ if (aconf->passwd)
+ memset(aconf->passwd, 0, strlen(aconf->passwd));
+ RunFree(aconf->passwd);
+ RunFree(aconf->name);
+ RunFree(aconf);
+#ifdef DEBUGMODE
+ aconfs.inuse--;
+#endif
+ return;
+}
+
+aGline *make_gline(int is_ipmask, char *host, char *reason,
+ char *name, time_t expire)
+{
+ Reg4 aGline *agline;
+
+ agline = (struct Gline *)RunMalloc(sizeof(aGline)); /* alloc memory */
+ DupString(agline->host, host); /* copy vital information */
+ DupString(agline->reason, reason);
+ DupString(agline->name, name);
+ agline->expire = expire;
+ agline->gflags = GLINE_ACTIVE; /* gline is active */
+ if (is_ipmask)
+ SetGlineIsIpMask(agline);
+ agline->next = gline; /* link it into the list */
+ return (gline = agline);
+}
+
+aGline *find_gline(aClient *cptr, aGline **pgline)
+{
+ Reg3 aGline *agline = gline, *a2gline = NULL;
+
+ while (agline)
+ { /* look through all glines */
+ if (agline->expire <= TStime())
+ { /* handle expired glines */
+ free_gline(agline, a2gline);
+ agline = a2gline ? a2gline->next : gline;
+ if (!agline)
+ break; /* agline == NULL means gline == NULL */
+ continue;
+ }
+
+ /* Does gline match? */
+ /* Added a check against the user's IP address as well -Kev */
+ if ((GlineIsIpMask(agline) ?
+ match(agline->host, inetntoa(cptr->ip)) :
+ match(agline->host, cptr->sockhost)) == 0 &&
+ match(agline->name, cptr->user->username) == 0)
+ {
+ if (pgline)
+ *pgline = a2gline; /* If they need it, give them the previous gline
+ entry (probably for free_gline, below) */
+ return agline;
+ }
+
+ a2gline = agline;
+ agline = agline->next;
+ }
+
+ return NULL; /* found no glines */
+}
+
+void free_gline(aGline *agline, aGline *pgline)
+{
+ if (pgline)
+ pgline->next = agline->next; /* squeeze agline out */
+ else
+ gline = agline->next;
+
+ RunFree(agline->host); /* and free up the memory */
+ RunFree(agline->reason);
+ RunFree(agline->name);
+ RunFree(agline);
+}
+
+#ifdef DEBUGMODE
+void send_listinfo(aClient *cptr, char *name)
+{
+ int inuse = 0, mem = 0, tmp = 0;
+
+ sendto_one(cptr, ":%s %d %s :Local: inuse: %d(%d)",
+ me.name, RPL_STATSDEBUG, name, inuse += cloc.inuse,
+ tmp = cloc.inuse * CLIENT_LOCAL_SIZE);
+ mem += tmp;
+ sendto_one(cptr, ":%s %d %s :Remote: inuse: %d(%d)",
+ me.name, RPL_STATSDEBUG, name,
+ crem.inuse, tmp = crem.inuse * CLIENT_REMOTE_SIZE);
+ mem += tmp;
+ inuse += crem.inuse;
+ sendto_one(cptr, ":%s %d %s :Users: inuse: %d(%d)",
+ me.name, RPL_STATSDEBUG, name, users.inuse,
+ tmp = users.inuse * sizeof(anUser));
+ mem += tmp;
+ inuse += users.inuse,
+ sendto_one(cptr, ":%s %d %s :Servs: inuse: %d(%d)",
+ me.name, RPL_STATSDEBUG, name, servs.inuse,
+ tmp = servs.inuse * sizeof(aServer));
+ mem += tmp;
+ inuse += servs.inuse,
+ sendto_one(cptr, ":%s %d %s :Links: inuse: %d(%d)",
+ me.name, RPL_STATSDEBUG, name, links.inuse,
+ tmp = links.inuse * sizeof(Link));
+ mem += tmp;
+ inuse += links.inuse,
+ sendto_one(cptr, ":%s %d %s :Classes: inuse: %d(%d)",
+ me.name, RPL_STATSDEBUG, name, classs.inuse,
+ tmp = classs.inuse * sizeof(aConfClass));
+ mem += tmp;
+ inuse += classs.inuse,
+ sendto_one(cptr, ":%s %d %s :Confs: inuse: %d(%d)",
+ me.name, RPL_STATSDEBUG, name, aconfs.inuse,
+ tmp = aconfs.inuse * sizeof(aConfItem));
+ mem += tmp;
+ inuse += aconfs.inuse,
+ sendto_one(cptr, ":%s %d %s :Totals: inuse %d %d",
+ me.name, RPL_STATSDEBUG, name, inuse, mem);
+}
+
+#endif
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/map.c
+ * Copyright (C) 1994 Carlo Wood ( Run @ undernet.org )
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "match.h"
+#include "list.h"
+#include "s_err.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "map.h"
+
+RCSTAG_CC("$Id$");
+
+static void dump_map(aClient *cptr, aClient *server, char *mask,
+ int prompt_length)
+{
+ static char prompt[64];
+ register Dlink *lp;
+ register char *p = &prompt[prompt_length];
+ register int cnt = 0;
+
+ *p = '\0';
+ if (prompt_length > 60)
+ sendto_one(cptr, rpl_str(RPL_MAPMORE), me.name, cptr->name,
+ prompt, server->name);
+ else
+ sendto_one(cptr, rpl_str(RPL_MAP), me.name, cptr->name,
+ prompt, server->name);
+ if (prompt_length > 0)
+ {
+ p[-1] = ' ';
+ if (p[-2] == '`')
+ p[-2] = ' ';
+ }
+ if (prompt_length > 60)
+ return;
+ strcpy(p, "|-");
+ for (lp = server->serv->down; lp; lp = lp->next)
+ if (match(mask, lp->value.cptr->name))
+ lp->value.cptr->flags &= ~FLAGS_MAP;
+ else
+ {
+ lp->value.cptr->flags |= FLAGS_MAP;
+ cnt++;
+ }
+ for (lp = server->serv->down; lp; lp = lp->next)
+ {
+ if ((lp->value.cptr->flags & FLAGS_MAP) == 0)
+ continue;
+ if (--cnt == 0)
+ *p = '`';
+ dump_map(cptr, lp->value.cptr, mask, prompt_length + 2);
+ }
+ if (prompt_length > 0)
+ p[-1] = '-';
+}
+
+/*
+ * m_map -- by Run
+ *
+ * parv[0] = sender prefix
+ * parv[1] = server mask
+ */
+int m_map(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ if (parc < 2)
+ parv[1] = "*";
+
+ dump_map(sptr, &me, parv[1], 0);
+ sendto_one(sptr, rpl_str(RPL_MAPEND), me.name, parv[0]);
+
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, common/match.c
+ * Copyright (C) 1990 Jarkko Oikarinen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "match.h"
+#include "ircd.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * mmatch()
+ *
+ * Written by Run (carlo@runaway.xs4all.nl), 25-10-96
+ */
+
+/*
+ * From: Carlo Wood <carlo@runaway.xs4all.nl>
+ * Message-Id: <199609021026.MAA02393@runaway.xs4all.nl>
+ * Subject: [C-Com] Analysis for `mmatch' (was: gline4 problem)
+ * To: coder-com@mail.undernet.org (coder committee)
+ * Date: Mon, 2 Sep 1996 12:26:01 +0200 (MET DST)
+ *
+ * We need a new function `mmatch(const char *old_mask, const char *new_mask)'
+ * which returns `true' likewise the current `match' (start with copying it),
+ * but which treats '*' and '?' in `new_mask' differently (not "\*" and "\?" !)
+ * as follows: a '*' in `new_mask' does not match a '?' in `old_mask' and
+ * a '?' in `new_mask' does not match a '\?' in `old_mask'.
+ * And ofcourse... a '*' in `new_mask' does not match a '\*' in `old_mask'...
+ * And last but not least, '\?' and '\*' in `new_mask' now become one character.
+ */
+
+int mmatch(const char *old_mask, const char *new_mask)
+{
+ register const char *m = old_mask;
+ register const char *n = new_mask;
+ const char *ma = m;
+ const char *na = n;
+ int wild = 0;
+ int mq = 0, nq = 0;
+
+ while (1)
+ {
+ if (*m == '*')
+ {
+ while (*m == '*')
+ m++;
+ wild = 1;
+ ma = m;
+ na = n;
+ }
+
+ if (!*m)
+ {
+ if (!*n)
+ return 0;
+ for (m--; (m > old_mask) && (*m == '?'); m--)
+ ;
+ if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
+ return 0;
+ if (!wild)
+ return 1;
+ m = ma;
+
+ /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
+ if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
+ ++na;
+
+ n = ++na;
+ }
+ else if (!*n)
+ {
+ while (*m == '*')
+ m++;
+ return (*m != 0);
+ }
+ if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+ {
+ m++;
+ mq = 1;
+ }
+ else
+ mq = 0;
+
+ /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
+ if ((*n == '\\') && ((n[1] == '*') || (n[1] == '?')))
+ {
+ n++;
+ nq = 1;
+ }
+ else
+ nq = 0;
+
+/*
+ * This `if' has been changed compared to match() to do the following:
+ * Match when:
+ * old (m) new (n) boolean expression
+ * * any (*m == '*' && !mq) ||
+ * ? any except '*' (*m == '?' && !mq && (*n != '*' || nq)) ||
+ * any except * or ? same as m (!((*m == '*' || *m == '?') && !mq) &&
+ * toLower(*m) == toLower(*n) &&
+ * !((mq && !nq) || (!mq && nq)))
+ *
+ * Here `any' also includes \* and \? !
+ *
+ * After reworking the boolean expressions, we get:
+ * (Optimized to use boolean shortcircuits, with most frequently occuring
+ * cases upfront (which took 2 hours!)).
+ */
+ if ((*m == '*' && !mq) ||
+ ((!mq || nq) && toLower(*m) == toLower(*n)) ||
+ (*m == '?' && !mq && (*n != '*' || nq)))
+ {
+ if (*m)
+ m++;
+ if (*n)
+ n++;
+ }
+ else
+ {
+ if (!wild)
+ return 1;
+ m = ma;
+
+ /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
+ if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
+ ++na;
+
+ n = ++na;
+ }
+ }
+}
+
+/*
+ * Compare if a given string (name) matches the given
+ * mask (which can contain wild cards: '*' - match any
+ * number of chars, '?' - match any single character.
+ *
+ * return 0, if match
+ * 1, if no match
+ */
+
+/*
+ * match
+ *
+ * Rewritten by Andrea Cocito (Nemesi), November 1998.
+ *
+ */
+
+/****************** Nemesi's match() ***************/
+
+int match(const char *mask, const char *string)
+{
+ register const char *m = mask, *s = string;
+ register char ch;
+ const char *bm, *bs; /* Will be reg anyway on a decent CPU/compiler */
+
+ /* Process the "head" of the mask, if any */
+ while ((ch = *m++) && (ch != '*'))
+ switch (ch)
+ {
+ case '\\':
+ if (*m == '?' || *m == '*')
+ ch = *m++;
+ default:
+ if (toLower(*s) != toLower(ch))
+ return 1;
+ case '?':
+ if (!*s++)
+ return 1;
+ };
+ if (!ch)
+ return *s;
+
+ /* We got a star: quickly find if/where we match the next char */
+got_star:
+ bm = m; /* Next try rollback here */
+ while ((ch = *m++))
+ switch (ch)
+ {
+ case '?':
+ if (!*s++)
+ return 1;
+ case '*':
+ bm = m;
+ continue; /* while */
+ case '\\':
+ if (*m == '?' || *m == '*')
+ ch = *m++;
+ default:
+ goto break_while; /* C is structured ? */
+ };
+break_while:
+ if (!ch)
+ return 0; /* mask ends with '*', we got it */
+ ch = toLower(ch);
+ while (toLower(*s++) != ch)
+ if (!*s)
+ return 1;
+ bs = s; /* Next try start from here */
+
+ /* Check the rest of the "chunk" */
+ while ((ch = *m++))
+ {
+ switch (ch)
+ {
+ case '*':
+ goto got_star;
+ case '\\':
+ if (*m == '?' || *m == '*')
+ ch = *m++;
+ default:
+ if (toLower(*s) != toLower(ch))
+ {
+ m = bm;
+ s = bs;
+ goto got_star;
+ };
+ case '?':
+ if (!*s++)
+ return 1;
+ };
+ };
+ if (*s)
+ {
+ m = bm;
+ s = bs;
+ goto got_star;
+ };
+ return 0;
+}
+
+/*
+ * collapse()
+ * Collapse a pattern string into minimal components.
+ * This particular version is "in place", so that it changes the pattern
+ * which is to be reduced to a "minimal" size.
+ *
+ * (C) Carlo Wood - 6 Oct 1998
+ * Speedup rewrite by Andrea Cocito, December 1998.
+ * Note that this new optimized alghoritm can *only* work in place.
+ */
+
+char *collapse(char *mask)
+{
+ register int star = 0;
+ register char *m = mask;
+ register char *b;
+
+ if (m)
+ {
+ do
+ {
+ if ((*m == '*') && ((m[1] == '*') || (m[1] == '?')))
+ {
+ b = m;
+ do
+ {
+ if (*m == '*')
+ star = 1;
+ else
+ {
+ if (star && (*m != '?'))
+ {
+ *b++ = '*';
+ star = 0;
+ };
+ *b++ = *m;
+ if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+ *b++ = *++m;
+ };
+ }
+ while (*m++);
+ break;
+ }
+ else
+ {
+ if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+ m++;
+ };
+ }
+ while (*m++);
+ };
+ return mask;
+}
+
+/*
+ ***************** Nemesi's matchcomp() / matchexec() **************
+ */
+
+/* These functions allow the use of "compiled" masks, you compile a mask
+ * by means of matchcomp() that gets the plain text mask as input and writes
+ * its result in the memory locations addressed by the 3 parameters:
+ * - *cmask will contain the text of the compiled mask
+ * - *minlen will contain the lenght of the shortest string that can match
+ * the mask
+ * - *charset will contain the minimal set of chars needed to match the mask
+ * You can pass NULL as *charset and it will be simply not returned, but you
+ * MUST pass valid pointers for *minlen and *cmask (wich must be big enough
+ * to contain the compiled mask text that is in the worst case as long as the
+ * text of the mask itself in plaintext format) and the return value of
+ * matchcomp() will be the number of chars actually written there (excluded
+ * the trailing zero). cmask can be == mask, matchcomp() can work in place.
+ * The {cmask, minlen} couple of values make the real compiled mask and
+ * need to be passed to the functions that use the compiled mask, if you pass
+ * the wrong minlen or something wrong in cmask to one of these expect a
+ * coredump. This means that when you record a compiled mask you must store
+ * *both* these values.
+ * Once compiled the mask can be used to match a string by means of
+ * matchexec(), it can be printed back to human-readable format by means
+ * of sprintmatch() or it can be compared to another compiled mask by means
+ * of mmexec() that will tell if it completely overrides that mask (a lot like
+ * what mmatch() does for plain text masks).
+ * You can gain a lot of speed in many situations avoiding to matchexec() when:
+ * - The maximum lenght of the field you are about to match() the mask to is
+ * shorter than minlen, in example when matching abc*def*ghil with a nick:
+ * It just cannot match since a nick is at most 9 chars long and the mask
+ * needs at least 10 chars (10 will be the value returned in minlen).
+ * - The charset allowed for the field you are about to match to doesn't
+ * "contain" the charset returned by matchcomp(), in example when you
+ * have *.* as mask it makes no sense to try to match it against a nick
+ * because, again, a nick can't contain a '.', you can check this with
+ * a simple (charset & NTL_IRCNK) in this case.
+ * - As a special case, since compiled masks are forced to lowercase,
+ * it would make no sense to use the NTL_LOWER and NTL_UPPER on a compiled
+ * mask, thus they are reused as follows: if the NTL_LOWER bit of charset
+ * is set it means that the mask contains only non-wilds chars (i.e. you can
+ * use strCasecmp() to match it or a direct hash lookup), if the NTL_UPPER
+ * bit is set it means that it contains only wild chars (and you can
+ * match it with strlen(field)>=minlen).
+ * Do these optimizations ONLY when the data you are about to pass to
+ * matchexec() are *known* to be invalid in advance, using strChattr()
+ * or strlen() on the text would be slower than calling matchexec() directly
+ * and let it fail.
+ * Internally a compiled mask contain in the *cmask area the text of
+ * the plain text form of the mask itself with applied the following hacks:
+ * - All characters are forced to lowercase (so that uppercase letters and
+ * specifically the symbols 'A' and 'Z' are reserved for special use)
+ * - All non-escaped stars '*' are replaced by the letter 'Z'
+ * - All non-escaped question marks '?' are replaced by the letter 'A'
+ * - All escape characters are removed, the wilds escaped by them are
+ * then passed by without the escape since they don't collide anymore
+ * with the real wilds (encoded as A/Z)
+ * - Finally the part of the mask that follows the last asterisk is
+ * reversed (byte order mirroring) and moved right after the first
+ * asterisk.
+ * After all this a mask like: Head*CHUNK1*chu\*nK2*ch??k3*TaIl
+ * .... becomes: headZliatZchunk1Zchu*nk2ZchAAk3
+ * This can still be printed on a console, more or less understood by an
+ * human and handled with the usual str*() library functions.
+ * When you store somewhere the compiled mask you can avoid storing the
+ * textform of it since it can be "decompiled" by means of sprintmatch(),
+ * but at that time the following things are changed in the mask:
+ * - All chars have been forced to lowercase.
+ * - The mask is collapsed.
+ * The balance point of using compiled masks in terms of CPU is when you expect
+ * to use matchexec() instead of match() at least 20 times on the same mask
+ * or when you expect to use mmexec() instead of mmatch() 3 times.
+ */
+
+ /*
+ * matchcomp()
+ *
+ * Compiles a mask into a form suitable for using in matchexec().
+ */
+
+int matchcomp(char *cmask, int *minlen, int *charset, const char *mask)
+{
+ const char *m = mask;
+ char *b = cmask;
+ char *fs = NULL;
+ char *ls = NULL;
+ char *x1, *x2;
+ int l1, l2, lmin, loop, sign;
+ int star = 0;
+ int cnt = 0;
+ char ch;
+ int chset = ~0;
+ int chset2 = (NTL_LOWER | NTL_UPPER);
+
+ if (m)
+ while ((ch = *m++))
+ switch (ch)
+ {
+ case '*':
+ star = 1;
+ break;
+ case '?':
+ cnt++;
+ *b++ = 'A';
+ chset2 &= ~NTL_LOWER;
+ break;
+ case '\\':
+ if ((*m == '?') || (*m == '*'))
+ ch = *m++;
+ default:
+ if (star)
+ {
+ ls = b;
+ fs = fs ? fs : b;
+ *b++ = 'Z';
+ chset2 &= ~NTL_LOWER;
+ star = 0;
+ };
+ cnt++;
+ chset &= NTL_char_attrib[((*b++ = toLower(ch))) - CHAR_MIN];
+ chset2 &= ~NTL_UPPER;
+ };
+
+ if (charset)
+ *charset = (chset | chset2);
+
+ if (star)
+ {
+ ls = b;
+ fs = (fs ? fs : b);
+ *b++ = 'Z';
+ };
+
+ if (ls)
+ {
+ for (x1 = ls + 1, x2 = (b - 1); x1 < x2; x1++, x2--)
+ {
+ ch = *x1;
+ *x1 = *x2;
+ *x2 = ch;
+ };
+ l1 = (ls - fs);
+ l2 = (b - ls);
+ x1 = fs;
+ while ((lmin = (l1 < l2) ? l1 : l2))
+ {
+ x2 = x1 + l1;
+ for (loop = 0; loop < lmin; loop++)
+ {
+ ch = x1[loop];
+ x1[loop] = x2[loop];
+ x2[loop] = ch;
+ };
+ x1 += lmin;
+ sign = l1 - l2;
+ l1 -= (sign < 0) ? 0 : lmin;
+ l2 -= (sign > 0) ? 0 : lmin;
+ };
+ };
+
+ *b = '\0';
+ *minlen = cnt;
+ return (b - cmask);
+
+}
+
+/*
+ * matchexec()
+ *
+ * Executes a match with a mask previosuly compiled with matchcomp()
+ * Note 1: If the mask isn't correctly produced by matchcomp() I will core
+ * Note 2: 'min' MUST be the value returned by matchcomp on that mask,
+ * or.... I will core even faster :-)
+ * Note 3: This piece of code is not intended to be nice but efficient.
+ */
+
+int matchexec(const char *string, const char *cmask, int minlen)
+{
+ register const char *s = string - 1;
+ register const char *b = cmask - 1;
+ register int trash;
+ register const char *bb, *bs;
+ register char ch;
+
+tryhead:
+ while ((toLower(*++s) == *++b) && *s);
+ if (!*s)
+ return ((*b != '\000') && ((*b++ != 'Z') || (*b != '\000')));
+ if (*b != 'Z')
+ {
+ if (*b == 'A')
+ goto tryhead;
+ return 1;
+ };
+
+ bs = s;
+ while (*++s);
+
+ if ((trash = (s - string - minlen)) < 0)
+ return 2;
+
+trytail:
+ while ((toLower(*--s) == *++b) && *b && (toLower(*--s) == *++b) && *b
+ && (toLower(*--s) == *++b) && *b && (toLower(*--s) == *++b) && *b);
+ if (*b != 'Z')
+ {
+ if (*b == 'A')
+ goto trytail;
+ return (*b != '\000');
+ };
+
+ s = --bs;
+ bb = b;
+
+ while ((ch = *++b))
+ {
+ while ((toLower(*++s) != ch))
+ if (--trash < 0)
+ return 4;
+ bs = s;
+
+ trychunk:
+ while ((toLower(*++s) == *++b) && *b);
+ if (!*b)
+ return 0;
+ if (*b == 'Z')
+ {
+ bs = --s;
+ bb = b;
+ continue;
+ };
+ if (*b == 'A')
+ goto trychunk;
+
+ b = bb;
+ s = bs;
+ if (--trash < 0)
+ return 5;
+ };
+
+ return 0;
+}
+
+/*
+ * matchdecomp()
+ * Prints the human readable version of *cmask into *mask, (decompiles
+ * cmask).
+ * The area pointed by *mask MUST be big enough (the mask might be up to
+ * twice the size of its compiled form if it's made all of \? or \*, and
+ * this function can NOT work in place since it might enflate the mask)
+ * The printed mask is not identical to the one that was compiled to cmask,
+ * infact it is 1) forced to all lowercase, 2) collapsed, both things
+ * are supposed to NOT change it's meaning.
+ * It returns the number of chars actually written to *mask;
+ */
+
+int matchdecomp(char *mask, const char *cmask)
+{
+ register char *rtb = mask;
+ register const char *rcm = cmask;
+ register const char *begtail, *endtail;
+
+ if (rtb == NULL)
+ return (-1);
+
+ if (rcm == NULL)
+ return (-2);
+
+ for (; (*rcm != 'Z'); rcm++, rtb++)
+ {
+ if ((*rcm == '?') || (*rcm == '*'))
+ *rtb++ = '\\';
+ if (!((*rtb = ((*rcm == 'A') ? '?' : *rcm))))
+ return (rtb - mask);
+ };
+
+ begtail = rcm++;
+ *rtb++ = '*';
+
+ while (*rcm && (*rcm != 'Z'))
+ rcm++;
+
+ endtail = rcm;
+
+ if (*rcm)
+ {
+ while (*++rcm)
+ switch (*rcm)
+ {
+ case 'A':
+ *rtb++ = '?';
+ break;
+ case 'Z':
+ *rtb++ = '*';
+ break;
+ case '*':
+ case '?':
+ *rtb++ = '\\';
+ default:
+ *rtb++ = *rcm;
+ };
+ *rtb++ = '*';
+ };
+
+ for (rcm = endtail; (--rcm) > begtail; *rtb++ = ((*rcm == 'A') ? '?' : *rcm))
+ if ((*rcm == '?') || (*rcm == '*'))
+ *rtb++ = '\\';
+
+ *rtb = '\000';
+ return (rtb - mask);
+}
+
+/*
+ * mmexec()
+ * Checks if a wider compiled mask (wcm/wminlen) completely overrides
+ * a more restrict one (rcm/rminlen), basically what mmatch() does for
+ * non-compiled masks, returns 0 if the override is true (like mmatch()).
+ * "the wider overrides the restrict" means that any string that matches
+ * the restrict one _will_ also match the wider one, always.
+ * In this we behave differently from mmatch() because in example we return
+ * true for " a?*cd overrides a*bcd " for wich the override happens for how
+ * we literally defined it, here mmatch() would have returned false.
+ * The original concepts and the base alghoritm are copied from mmatch()
+ * written by Run (Carlo Wood), this function is written by
+ * Nemesi (Andrea Cocito)
+ */
+
+int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen)
+{
+ register const char *w, *r, *br, *bw, *rx, *rz;
+ register int eat, trash;
+
+ /* First of all rm must have enough non-stars to 'contain' wm */
+ if ((trash = rminlen - wminlen) < 0)
+ return 1;
+ w = wcm;
+ r = rcm;
+ eat = 0;
+
+ /* Let's start the game, remember that '*' is mapped to 'Z', '?'
+ is mapped to 'A' and that head?*??*?chunk*???*tail becomes
+ headAAAAZliatAAAZchunk for compiled masks */
+
+ /* Match the head of wm with the head of rm */
+ for (; (*r) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); r++, w++);
+ if (*r == 'Z')
+ while (*w == 'A') /* Eat extra '?' before '*' in wm if got '*' in rm */
+ w++, eat++;
+ if (*w != 'Z') /* head1<any>.. can't match head2<any>.. */
+ return ((*w) || (*r)) ? 1 : 0; /* and head<nul> matches only head<nul> */
+ if (!*++w)
+ return 0; /* headZ<nul> matches head<anything> */
+
+ /* Does rm have any stars in it ? let's check */
+ for (rx = r; *r && (*r != 'Z'); r++);
+ if (!*r)
+ {
+ /* rm has no stars and thus isn't a mask but it's just a flat
+ string: special handling occurs here, note that eat must be 0 here */
+
+ /* match the tail */
+ if (*w != 'Z')
+ {
+ for (; r--, (*w) && ((*w == *r) || (*w == 'A')); w++);
+ if (*w != 'Z') /* headZliat1<any> fails on head<any>2tail */
+ return (*w) ? 1 : 0; /* but headZliat<nul> matches head<any>tail */
+ };
+
+ /* match the chunks */
+ while (1)
+ { /* This loop can't break but only return */
+
+ for (bw = w++; (*w != *rx); rx++) /* Seek the 1st char of the chunk */
+ if (--trash < 0) /* See if we can trash one more char of rm */
+ return 1; /* If not we can only fail of course */
+ for (r = ++rx, w++; (*w) && ((*w == *r) || (*w == 'A')); r++, w++);
+ if (!*w) /* Did last loop match the rest of chunk ? */
+ return 0; /* ... Yes, end of wm, matched ! */
+ if (*w != 'Z')
+ { /* ... No, hitted non-star */
+ w = bw; /* Rollback at beginning of chunk */
+ if (--trash < 0) /* Trashed the char where this try started */
+ return 1; /* if we can't trash more chars fail */
+ }
+ else
+ {
+ rx = r; /* Successfully matched a chunk, move rx */
+ }; /* and go on with the next one */
+ };
+ };
+
+ /* rm has at least one '*' and thus is a 'real' mask */
+ rz = r++; /* rx = unused of head, rz = beg-tail */
+
+ /* Match the tail of wm (if any) against the tail of rm */
+ if (*w != 'Z')
+ {
+ for (; (*w) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); w++, r++);
+ if (*r == 'Z') /* extra '?' before tail are fluff, just flush 'em */
+ while (*w == 'A')
+ w++;
+ if (*w != 'Z') /* We aren't matching a chunk, can't rollback */
+ return (*w) ? 1 : 0;
+ };
+
+ /* Match the chunks of wm against what remains of the head of rm */
+ while (1)
+ {
+ bw = w;
+ for (bw++; (rx < rz) && (*bw != *rx); rx++) /* Seek the first */
+ if (--trash < 0) /* waste some trash reserve */
+ return 1;
+ if (!(rx < rz)) /* head finished */
+ break;
+ for (bw++, (br = ++rx);
+ (br < rz) && (*bw) && ((*bw == *br) || (*bw == 'A')); br++, bw++);
+ if (!(br < rz)) /* Note that we didn't use any 'eat' char yet, if */
+ while (*bw == 'A') /* there were eat-en chars the head would be over */
+ bw++, eat++; /* Happens only at end of head, and eat is still 0 */
+ if (!*bw)
+ return 0;
+ if (*bw != 'Z')
+ {
+ eat = 0;
+ if (!(br < rz))
+ { /* If we failed because we got the end of head */
+ trash -= (br - rx); /* it makes no sense to rollback, just trash */
+ if (--trash < 0) /* all the rest of the head wich isn't long */
+ return 1; /* enough for this chunk and go out of this */
+ break; /* loop, then we try with the chunks of rm */
+ };
+ if (--trash < 0)
+ return 1;
+ }
+ else
+ {
+ w = bw;
+ rx = br;
+ };
+ };
+
+ /* Match the unused chunks of wm against the chunks of rm */
+ rx = r;
+ for (; *r && (*r != 'Z'); r++);
+ rz = r;
+ if (*r++)
+ {
+ while (*r)
+ {
+ bw = w;
+ while (eat && *r) /* the '?' we had eated make us skip as many chars */
+ if (*r++ != 'Z') /* here, but can't skip stars or trailing zero */
+ eat--;
+ for (bw++; (*r) && (*bw != *r); r++)
+ if ((*r != 'Z') && (--trash < 0))
+ return 1;
+ if (!*r)
+ break;
+ for ((br = ++r), bw++;
+ (*br) && (*br != 'Z') && ((*bw == *br) || (*bw == 'A')); br++, bw++);
+ if (*br == 'Z')
+ while (*bw == 'A')
+ bw++, eat++;
+ if (!*bw)
+ return 0;
+ if (*bw != 'Z')
+ {
+ eat = 0;
+ if ((!*br) || (*r == 'Z'))
+ { /* If we hit the end of rm or a star in it */
+ trash -= (br - r); /* makes no sense to rollback within this */
+ if (trash < 0) /* same chunk of br, skip it all and then */
+ return 1; /* either rollback or break this loop if */
+ if (!*br) /* it was the end of rm */
+ break;
+ r = br;
+ };
+ if (--trash < 0)
+ return 1;
+ }
+ else
+ {
+ r = br;
+ w = bw;
+ };
+ };
+ };
+
+ /* match the remaining chunks of wm against what remains of the tail of rm */
+ r = rz - eat - 1; /* can't have <nul> or 'Z'within the tail, so just move r */
+ while (r >= rx)
+ {
+ bw = w;
+ for (bw++; (*bw != *r); r--)
+ if (--trash < 0)
+ return 1;
+ if (!(r >= rx))
+ return 1;
+ for ((br = --r), bw++;
+ (*bw) && (br >= rx) && ((*bw == *br) || (*bw == 'A')); br--, bw++);
+ if (!*bw)
+ return 0;
+ if (!(br >= rx))
+ return 1;
+ if (*bw != 'Z')
+ {
+ if (--trash < 0)
+ return 1;
+ }
+ else
+ {
+ r = br;
+ w = bw;
+ };
+ };
+ return 1; /* Auch... something left out ? Fail */
+}
+
+/*
+ * matchcompIP()
+ * Compiles an IP mask into an in_mask structure
+ * The given <mask> can either be:
+ * - An usual irc type mask, containing * and or ?
+ * - An ip number plus a /bitnumber part, that will only consider
+ * the first "bitnumber" bits of the IP (bitnumber must be in 0-31 range)
+ * - An ip numer plus a /ip.bit.mask.values that will consider
+ * only the bits marked as 1 in the ip.bit.mask.values
+ * In the last two cases both the ip number and the bitmask can specify
+ * less than 4 bytes, the missing bytes then default to zero, note that
+ * this is *different* from the way inet_aton() does and that this does
+ * NOT happen for normal IPmasks (not containing '/')
+ * If the returned value is zero the produced in_mask might match some IP,
+ * if it's nonzero it will never match anything (and the imask struct is
+ * set so that always fails).
+ *
+ * The returned structure contains 3 fields whose meaning is the following:
+ * im.mask = The bits considered significative in the IP
+ * im.bits = What these bits should look like to have a match
+ * im.fall = If zero means that the above information used as
+ * ((IP & im.mask) == im.bits) is enough to tell if the compiled
+ * mask matches the given IP, nonzero means that it is needed,
+ * in case they did match, to call also the usual text match
+ * functions, because the mask wasn't "completely compiled"
+ *
+ * They should be used like:
+ * matchcompIP(&im, mask);
+ * if ( ((IP & im.mask)!=im.bits)) || (im.fall&&match(mask,inet_ntoa(IP))) )
+ * { handle_non_match } else { handle_match };
+ * instead of:
+ * if ( match(mask, inet_ntoa(IP)) )
+ * { handle_non_match } else { handle_match };
+ *
+ * Note: This function could be smarter when dealing with complex masks,
+ * this implementation is quite lazy and understands only very simple
+ * cases, whatever contains a ? anywhere or contains a '*' that isn't
+ * part of a trailing '.*' will fallback to text-match, this could be
+ * avoided for masks like 12?3.5.6 12.*.3.4 1.*.*.2 72?72?72?72 and
+ * so on that "could" be completely compiled to IP masks.
+ * If you try to improve this be aware of the fact that ? and *
+ * could match both dots and digits and we _must_ always reject
+ * what doesn't match in textform (like leading zeros and so on),
+ * so it's a LOT more tricky than it might seem. By now most common
+ * cases are optimized.
+ */
+
+int matchcompIP(struct in_mask *imask, const char *mask)
+{
+ register const char *m = mask;
+ register unsigned int bits = 0;
+ register unsigned int filt = 0;
+ register int unco = 0;
+ register int digits = 0;
+ register int shift = 24;
+ register int tmp = 0;
+
+ do
+ {
+ switch (*m)
+ {
+ case '\\':
+ if ((m[1] == '\\') || (m[1] == '*') || (m[1] == '?')
+ || (m[1] == '\000'))
+ break;
+ continue;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digits && !tmp) /* Leading zeros */
+ break;
+ digits++;
+ tmp *= 10;
+ tmp += (*m - '0'); /* Can't overflow, INT_MAX > 2559 */
+ if (tmp > 255)
+ break;
+ continue;
+ case '\000':
+ filt = 0xFFFFFFFF;
+ /* Intentional fallthrough */
+ case '.':
+ if ((!shift) != (!*m))
+ break;
+ /* Intentional fallthrough */
+ case '/':
+ bits |= (tmp << shift);
+ shift -= 8;
+ digits = 0;
+ tmp = 0;
+ if (*m != '/')
+ continue;
+ shift = 24;
+ do
+ {
+ m++;
+ if (isDigit(*m))
+ {
+ if (digits && !tmp) /* Leading zeros */
+ break;
+ digits++;
+ tmp *= 10;
+ tmp += (*m - '0'); /* Can't overflow, INT_MAX > 2559 */
+ if (tmp > 255)
+ break;
+ }
+ else
+ {
+ switch (*m)
+ {
+ case '.':
+ case '\000':
+ if ((!shift) && (*m))
+ break;
+ filt |= (tmp << shift);
+ shift -= 8;
+ tmp = 0;
+ digits = 0;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ while (*m);
+ if (*m)
+ break;
+ if (filt && (!(shift < 16)) && (!(filt & 0xE0FFFFFF)))
+ filt = 0xFFFFFFFF << (32 - ((filt >> 24)));
+ bits &= filt;
+ continue;
+ case '?':
+ unco = 1;
+ /* Intentional fallthrough */
+ case '*':
+ if (digits)
+ unco = 1;
+ filt = (0xFFFFFFFF << (shift)) << 8;
+ while (*++m)
+ {
+ if (isDigit(*m))
+ unco = 1;
+ else
+ {
+ switch (*m)
+ {
+ case '.':
+ if (m[1] != '*')
+ unco = 1;
+ if (!shift)
+ break;
+ shift -= 8;
+ continue;
+ case '?':
+ unco = 1;
+ case '*':
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ if (*m)
+ break;
+ continue;
+ default:
+ break;
+ }
+
+ /* If we get here there is some error and this can't ever match */
+ filt = 0;
+ bits = ~0;
+ unco = 0;
+ break; /* This time break the loop :) */
+
+ }
+ while (*m++);
+
+ imask->bits.s_addr = htonl(bits);
+ imask->mask.s_addr = htonl(filt);
+ imask->fall = unco;
+ return ((bits & ~filt) ? -1 : 0);
+
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/channel.c
+ * Copyright (C) 1996 Carlo Wood (I wish this was C++ - this sucks :/)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "numnicks.h"
+
+#include "sys.h"
+#include "h.h"
+#include "s_serv.h"
+#include "struct.h"
+#include "common.h"
+#include "ircd.h"
+#include "s_misc.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "s_debug.h"
+
+#include <assert.h>
+
+RCSTAG_CC("$Id$");
+
+/*
+ * Numeric nicks are new as of version ircu2.10.00beta1.
+ *
+ * The idea is as follows:
+ * In most messages (for protocol 10+) the original nick will be
+ * replaced by a 3 character string: YXX
+ * Where 'Y' represents the server, and 'XX' the nick on that server.
+ *
+ * 'YXX' should not interfer with the input parser, and therefore is
+ * not allowed to contain spaces or a ':'.
+ * Also, 'Y' can't start with a '+' because of m_server().
+ *
+ * We keep the characters printable for debugging reasons too.
+ *
+ * The 'XX' value can be larger then the maximum number of clients
+ * per server, we use a mask (struct Server::nn_mask) to get the real
+ * client numeric. The overhead is used to have some redundancy so
+ * just-disconnected-client aren't confused with just-connected ones.
+ */
+
+/*
+ * when n2k comes, define this for more capacity
+ */
+#undef EXTENDED_NUMERICS
+
+/* These must be the same on ALL servers ! Do not change ! */
+
+#define NUMNICKLOG 6
+#define NUMNICKMAXCHAR 'z' /* See convert2n[] */
+#define NUMNICKBASE 64 /* (2 << NUMNICKLOG) */
+#define NUMNICKMASK 63 /* (NUMNICKBASE-1) */
+#define NN_MAX_SERVER 4096 /* (NUMNICKBASE * NUMNICKBASE) */
+
+/*
+ * The internal counter for the 'XX' of local clients
+ */
+static unsigned int lastNNServer = 0;
+static struct Client *server_list[NN_MAX_SERVER];
+
+/* *INDENT-OFF* */
+
+/*
+ * convert2y[] converts a numeric to the corresponding character.
+ * The following characters are currently known to be forbidden:
+ *
+ * '\0' : Because we use '\0' as end of line.
+ *
+ * ' ' : Because parse_*() uses this as parameter seperator.
+ * ':' : Because parse_server() uses this to detect if a prefix is a
+ * numeric or a name.
+ * '+' : Because m_nick() uses this to determine if parv[6] is a
+ * umode or not.
+ * '&', '#', '+', '$', '@' and '%' :
+ * Because m_message() matches these characters to detect special cases.
+ */
+static const char convert2y[] = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
+};
+
+static const unsigned int convert2n[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
+ 0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* *INDENT-ON* */
+
+
+unsigned int base64toint(const char *s)
+{
+ unsigned int i = convert2n[(unsigned char)*s++];
+ while (*s)
+ {
+ i <<= NUMNICKLOG;
+ i += convert2n[(unsigned char)*s++];
+ }
+ return i;
+}
+
+const char *inttobase64(char *buf, unsigned int v, size_t count)
+{
+ buf[count] = '\0';
+ while (count > 0)
+ {
+ buf[--count] = convert2y[(v & NUMNICKMASK)];
+ v >>= NUMNICKLOG;
+ }
+ return buf;
+}
+
+static struct Client *FindXNServer(const char *numeric)
+{
+ char buf[3];
+ buf[0] = *numeric++;
+ buf[1] = *numeric;
+ buf[2] = '\0';
+ Debug((DEBUG_DEBUG, "FindXNServer: %s(%d)", buf, base64toint(buf)));
+ return server_list[base64toint(buf)];
+}
+
+struct Client *FindNServer(const char *numeric)
+{
+ size_t len = strlen(numeric);
+ if (len < 3)
+ {
+ Debug((DEBUG_DEBUG, "FindNServer: %s(%d)", numeric, base64toint(numeric)));
+ return server_list[base64toint(numeric)];
+ }
+ else if (len == 3)
+ {
+ Debug((DEBUG_DEBUG, "FindNServer: %c(%d)", *numeric,
+ convert2n[(unsigned char)*numeric]));
+ return server_list[convert2n[(unsigned char)*numeric]];
+ }
+ return FindXNServer(numeric);
+}
+
+void RemoveYXXClient(struct Client *server, const char *yxx)
+{
+ assert(0 != server);
+ assert(0 != yxx);
+ if (*yxx)
+ {
+ unsigned int index = 0;
+
+ if (strlen(yxx) < 3)
+ index = base64toint(yxx) & server->serv->nn_mask;
+ else
+ index = base64toint(yxx);
+
+ Debug((DEBUG_DEBUG, "RemoveYXXClient: %s(%d)", yxx, index));
+
+ if (index < (server->serv->nn_mask + 1))
+ server->serv->client_list[index] = NULL;
+ }
+}
+
+void SetServerYXX(struct Client *cptr, struct Client *server, const char *yxx)
+{
+ unsigned int index;
+#ifndef NO_PROTOCOL9
+ /* Use cptr, because we do protocol 9 -> 10 translation for numeric nicks ! */
+ if (Protocol(cptr) > 9)
+ {
+#endif
+ if (5 == strlen(yxx))
+ {
+ strncpy(server->yxx, yxx, 2);
+ strncpy(server->serv->nn_capacity, yxx + 2, 3);
+ }
+ else
+ {
+ server->yxx[0] = yxx[0];
+ server->serv->nn_capacity[0] = yxx[1];
+ server->serv->nn_capacity[1] = yxx[2];
+ }
+ server->serv->nn_mask = base64toint(server->serv->nn_capacity);
+
+#ifndef NO_PROTOCOL9
+ }
+ else
+ {
+ static const struct ServerNameNumeric {
+ const char *name;
+ unsigned int numeric;
+ } server_table[] = {
+ {
+ "Uworld.undernet.org", 22},
+ {
+ "Uworld2.undernet.org", 23},
+ {
+ "channels.undernet.org", 30},
+ {
+ "channels2.undernet.org", 31},
+ {
+ 0, 0}
+ };
+ int i;
+ for (i = 0; i < 4; ++i)
+ {
+ if (!strCasediff(server_table[i].name, server->name))
+ {
+ /*
+ * XXX - just use the old format for services for now
+ */
+ *server->yxx = convert2y[server_table[i].numeric];
+ inttobase64(server->serv->nn_capacity, 63, 2);
+ server->serv->nn_mask = 63;
+ break;
+ }
+ }
+ }
+#endif
+ index = base64toint(server->yxx);
+ if (index >= lastNNServer)
+ lastNNServer = index + 1;
+ server_list[index] = server;
+
+ /* Note, exit_one_client uses the fact that `client_list' != NULL to
+ * determine that SetServerYXX has been called - and then calls
+ * ClearServerYXX. However, freeing the allocation happens in free_client() */
+ server->serv->client_list =
+ (struct Client **)RunCalloc(server->serv->nn_mask + 1,
+ sizeof(struct Client *));
+}
+
+void SetYXXCapacity(struct Client *c, size_t capacity)
+{
+ unsigned int max_clients;
+#if defined(EXTENDED_NUMERICS)
+ max_clients = capacity - 1;
+ inttobase64(c->serv->nn_capacity, max_clients, 3);
+#else
+ max_clients = 16;
+ /*
+ * Calculate mask to be used for the maximum number of clients
+ */
+ while (capacity >= max_clients)
+ max_clients = max_clients << 1;
+ /*
+ * Sanity checks
+ */
+ if (max_clients > NN_MAX_SERVER)
+ {
+ fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d "
+ "too large ! Please decrease this value.\n",
+ max_clients - NN_MAX_SERVER);
+ exit(-1);
+ }
+ --max_clients;
+ inttobase64(c->serv->nn_capacity, max_clients, 2);
+#endif
+ c->serv->nn_mask = max_clients; /* Our Numeric Nick mask */
+ c->serv->client_list = (struct Client **)RunCalloc(max_clients + 1,
+ sizeof(struct Client *));
+ server_list[base64toint(c->yxx)] = c;
+}
+
+void SetYXXServerName(struct Client *c, unsigned int numeric)
+{
+ assert(0 != c);
+ assert(numeric < NN_MAX_SERVER);
+
+#if defined(EXTENDED_NUMERICS)
+ inttobase64(c->yxx, numeric, 2);
+#else
+ assert(numeric < NUMNICKBASE);
+ c->yxx[0] = convert2y[numeric];
+#endif
+ if (numeric >= lastNNServer)
+ lastNNServer = numeric + 1;
+ server_list[numeric] = c;
+}
+
+void ClearServerYXX(const struct Client *server)
+{
+ unsigned int index = base64toint(server->yxx);
+ if (server_list[index] == server) /* Sanity check */
+ server_list[index] = NULL;
+}
+
+/*
+ * SetRemoteNumNick()
+ *
+ * Register numeric of new, remote, client.
+ * Add it to the appropriate client_list.
+ */
+int SetRemoteNumNick(struct Client *acptr, const char *yxx)
+{
+ struct Client **acptrp;
+ struct Client *server = acptr->user->server;
+ unsigned int index = 0;
+
+ if (5 == strlen(yxx))
+ {
+ strcpy(acptr->yxx, yxx + 2);
+ index = base64toint(acptr->yxx);
+ }
+ else
+ {
+ acptr->yxx[0] = *++yxx;
+ acptr->yxx[1] = *++yxx;
+ acptr->yxx[2] = 0;
+ index = base64toint(acptr->yxx) & server->serv->nn_mask;
+ }
+
+ Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", acptr->yxx, index));
+ if (index > server->serv->nn_mask)
+ return 0;
+
+ assert(index <= server->serv->nn_mask);
+
+ acptrp = &server->serv->client_list[index];
+ if (*acptrp)
+ {
+ /*
+ * this exits the old client in the array, not the client
+ * that is being set
+ */
+ exit_client(acptr->from, *acptrp, server, "Numeric nick collision (Ghost)");
+ }
+ *acptrp = acptr;
+ return 1;
+}
+
+
+/*
+ * SetLocalNumNick()
+ *
+ * Register numeric of new, local, client. Add it to our client_list.
+ */
+void SetLocalNumNick(struct Client *cptr)
+{
+ static unsigned int last_nn = 0;
+ struct Client **client_list = me.serv->client_list;
+ unsigned int capacity = me.serv->nn_mask + 1;
+ unsigned int count = 0;
+
+ assert(cptr->user->server == &me);
+
+ while (client_list[last_nn])
+ {
+ if (++count == capacity)
+ {
+ assert(count < capacity);
+ return;
+ }
+ if (++last_nn == capacity)
+ last_nn = 0;
+ }
+ client_list[last_nn] = cptr; /* Reserve the numeric ! */
+
+#if defined(EXTENDED_NUMERICS)
+ inttobase64(cptr->yxx, last_nn, 3);
+#else
+ inttobase64(cptr->yxx, last_nn, 2);
+#endif
+}
+
+struct Client *findNUser(const char *yxx)
+{
+ struct Client *server = 0;
+ unsigned int index = 0;
+ if (5 == strlen(yxx))
+ {
+ if (0 != (server = FindXNServer(yxx)))
+ {
+ Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx,
+ base64toint(yxx + 2), server));
+ if ((index = base64toint(yxx + 2)) <= server->serv->nn_mask)
+ return server->serv->client_list[index];
+ }
+ }
+ else if (0 != (server = FindNServer(yxx)))
+ {
+ index = base64toint(yxx + 1) & server->serv->nn_mask;
+ Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx, index, server));
+ return server->serv->client_list[index];
+ }
+ return 0;
+}
+
+/*
+ * markMatchexServer()
+ * Mark all servers whose name matches the given (compiled) mask
+ * and return their count, abusing FLAGS_MAP for this :)
+ */
+int markMatchexServer(const char *cmask, int minlen)
+{
+ int cnt = 0;
+ int i;
+ struct Client *acptr;
+
+ for (i = 0; i < lastNNServer; i++)
+ {
+ if ((acptr = server_list[i]))
+ {
+ if (matchexec(acptr->name, cmask, minlen))
+ acptr->flags &= ~FLAGS_MAP;
+ else
+ {
+ acptr->flags |= FLAGS_MAP;
+ cnt++;
+ }
+ }
+ }
+ return cnt;
+}
+
+struct Client *find_match_server(char *mask)
+{
+ struct Client *acptr;
+ int i;
+
+ if (!(BadPtr(mask)))
+ {
+ collapse(mask);
+ for (i = 0; i < lastNNServer; i++)
+ {
+ if ((acptr = server_list[i]) && (!match(mask, acptr->name)))
+ return acptr;
+ }
+ }
+ return NULL;
+}
+
+
+#ifndef NO_PROTOCOL9
+
+/******************************************************************************
+ *
+ * The following functions can be removed as soon as all servers have upgraded
+ * to ircu2.10.
+ */
+
+/*
+ * CreateNNforProtocol9server
+ *
+ * We do not receive numeric nicks from servers behind a protocol 9 link
+ * so we generate it ourselfs.
+ */
+const char *CreateNNforProtocol9server(const struct Client *server)
+{
+ static char YXX[4];
+ struct Server *serv = server->serv;
+ unsigned int count = 0;
+
+ assert(IsServer(server));
+ assert(9 == Protocol(server));
+
+ YXX[0] = *server->yxx;
+ YXX[3] = 0;
+
+ while (serv->client_list[serv->nn_last])
+ {
+ if (++count == NUMNICKBASE)
+ {
+ assert(count < NUMNICKBASE);
+ return NULL;
+ }
+ if (++serv->nn_last == NUMNICKBASE)
+ serv->nn_last = 0;
+ }
+ inttobase64(YXX + 1, serv->nn_last, 2);
+ return YXX;
+}
+
+#endif /* !NO_PROTOCOL9 */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/opercmds.c (formerly ircd/s_serv.c)
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "opercmds.h"
+#include "struct.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "match.h"
+#include "s_misc.h"
+#include "s_conf.h"
+#include "class.h"
+#include "s_user.h"
+#include "common.h"
+#include "msg.h"
+#include "sprintf_irc.h"
+#include "userload.h"
+#include "parse.h"
+#include "numnicks.h"
+#include "crule.h"
+#include "version.h"
+#include "support.h"
+#include "s_serv.h"
+#include "hash.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * m_squit
+ *
+ * parv[0] = sender prefix
+ * parv[1] = server name
+ * parv[2] = timestamp
+ * parv[parc-1] = comment
+ */
+int m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 aConfItem *aconf;
+ char *server;
+ Reg2 aClient *acptr;
+ char *comment = (parc > ((!IsServer(cptr)) ? 2 : 3) &&
+ !BadPtr(parv[parc - 1])) ? parv[parc - 1] : cptr->name;
+
+ if (!IsPrivileged(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+
+ if (parc > (IsServer(cptr) ? 2 : 1))
+ {
+ server = parv[1];
+ /*
+ * To accomodate host masking, a squit for a masked server
+ * name is expanded if the incoming mask is the same as
+ * the server name for that link to the name of link.
+ */
+ if ((*server == '*') && IsServer(cptr) && (aconf = cptr->serv->nline) &&
+ !strCasediff(server, my_name_for_link(me.name, aconf)))
+ {
+ server = cptr->name;
+ acptr = cptr;
+ }
+ else
+ {
+ /*
+ * The following allows wild cards in SQUIT. Only usefull
+ * when the command is issued by an oper.
+ */
+ for (acptr = client; (acptr = next_client(acptr, server));
+ acptr = acptr->next)
+ if (IsServer(acptr) || IsMe(acptr))
+ break;
+
+ if (acptr)
+ {
+ if (IsMe(acptr))
+ {
+ if (IsServer(cptr))
+ {
+ acptr = cptr;
+ server = cptr->sockhost;
+ }
+ else
+ acptr = NULL;
+ }
+ else
+ {
+ /*
+ * Look for a matching server that is closer,
+ * that way we won't accidently squit two close
+ * servers like davis.* and davis-r.* when typing
+ * /SQUIT davis*
+ */
+ aClient *acptr2;
+ for (acptr2 = acptr->serv->up; acptr2 != &me;
+ acptr2 = acptr2->serv->up)
+ if (!match(server, acptr2->name))
+ acptr = acptr2;
+ }
+ }
+ }
+ /* If atoi(parv[2]) == 0 we must indeed squit !
+ * It wil be our neighbour.
+ */
+ if (acptr && IsServer(cptr) &&
+ atoi(parv[2]) && atoi(parv[2]) != acptr->serv->timestamp)
+ {
+ Debug((DEBUG_NOTICE, "Ignoring SQUIT with wrong timestamp"));
+ return 0;
+ }
+ }
+ else
+ {
+ sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SQUIT");
+ if (IsServer(cptr))
+ {
+ /*
+ * This is actually protocol error. But, well, closing
+ * the link is very proper answer to that...
+ */
+ server = cptr->sockhost;
+ acptr = cptr;
+ }
+ else
+ return 0;
+ }
+ if (!acptr)
+ {
+ if (IsUser(sptr))
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], server);
+ return 0;
+ }
+ if (IsLocOp(sptr) && !MyConnect(acptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+
+ return exit_client(cptr, acptr, sptr, comment);
+}
+
+/*
+ * m_stats/stats_conf
+ *
+ * Report N/C-configuration lines from this server. This could
+ * report other configuration lines too, but converting the
+ * status back to "char" is a bit akward--not worth the code
+ * it needs...
+ *
+ * Note: The info is reported in the order the server uses
+ * it--not reversed as in ircd.conf!
+ */
+
+static unsigned int report_array[17][3] = {
+ {CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
+ {CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
+ {CONF_CLIENT, RPL_STATSILINE, 'I'},
+ {CONF_KILL, RPL_STATSKLINE, 'K'},
+ {CONF_IPKILL, RPL_STATSKLINE, 'k'},
+ {CONF_LEAF, RPL_STATSLLINE, 'L'},
+ {CONF_OPERATOR, RPL_STATSOLINE, 'O'},
+ {CONF_HUB, RPL_STATSHLINE, 'H'},
+ {CONF_LOCOP, RPL_STATSOLINE, 'o'},
+ {CONF_CRULEALL, RPL_STATSDLINE, 'D'},
+ {CONF_CRULEAUTO, RPL_STATSDLINE, 'd'},
+ {CONF_UWORLD, RPL_STATSULINE, 'U'},
+ {CONF_TLINES, RPL_STATSTLINE, 'T'},
+ {CONF_LISTEN_PORT, RPL_STATSPLINE, 'P'},
+ {0, 0}
+};
+
+static void report_configured_links(aClient *sptr, int mask)
+{
+ static char null[] = "<NULL>";
+ aConfItem *tmp;
+ unsigned int *p;
+ unsigned short int port;
+ char c, *host, *pass, *name;
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ if ((tmp->status & mask))
+ {
+ for (p = &report_array[0][0]; *p; p += 3)
+ if (*p == tmp->status)
+ break;
+ if (!*p)
+ continue;
+ c = (char)*(p + 2);
+ host = BadPtr(tmp->host) ? null : tmp->host;
+ pass = BadPtr(tmp->passwd) ? null : tmp->passwd;
+ name = BadPtr(tmp->name) ? null : tmp->name;
+ port = tmp->port;
+ /*
+ * On K line the passwd contents can be
+ * displayed on STATS reply. -Vesa
+ */
+ /* Special-case 'k' or 'K' lines as appropriate... -Kev */
+ if ((tmp->status & CONF_KLINE))
+ sendto_one(sptr, rpl_str(p[1]), me.name,
+ sptr->name, c, host, pass, name, port, get_conf_class(tmp));
+ /* connect rules are classless */
+ else if ((tmp->status & CONF_CRULE))
+ sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, name);
+ else if ((tmp->status & CONF_TLINES))
+ sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, pass);
+ else if ((tmp->status & CONF_LISTEN_PORT))
+ sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, port,
+ tmp->clients, tmp->status);
+ else if ((tmp->status & CONF_UWORLD))
+ sendto_one(sptr, rpl_str(p[1]),
+ me.name, sptr->name, c, host, pass, name, port,
+ get_conf_class(tmp));
+ else
+ sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, name,
+ port, get_conf_class(tmp));
+ }
+ return;
+}
+
+/*
+ * m_stats
+ *
+ * parv[0] = sender prefix
+ * parv[1] = statistics selector (defaults to Message frequency)
+ * parv[2] = target server (current server defaulted, if omitted)
+ * And 'stats l' and 'stats' L:
+ * parv[3] = server mask ("*" defaulted, if omitted)
+ * Or for stats p,P:
+ * parv[3] = port mask (returns p-lines when its port is matched by this)
+ * Or for stats k,K,i and I:
+ * parv[3] = [user@]host.name (returns which K/I-lines match this)
+ * or [user@]host.mask (returns which K/I-lines are mmatched by this)
+ * (defaults to old reply if ommitted, when local or Oper)
+ * A remote mask (something containing wildcards) is only
+ * allowed for IRC Operators.
+ * Or for stats M:
+ * parv[3] = time param
+ * parv[4] = time param
+ * (see report_memleak_stats() in runmalloc.c for details)
+ *
+ * This function is getting really ugly. -Ghostwolf
+ */
+int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
+ "RcveM RcveKBytes :Open since";
+ static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
+ aMessage *mptr;
+ aClient *acptr;
+ aGline *agline, *a2gline;
+ aConfItem *aconf;
+ char stat = parc > 1 ? parv[1][0] : '\0';
+ Reg1 int i;
+
+/* m_stats is so obnoxiously full of special cases that the different
+ * hunt_server() possiblites were becoming very messy. It now uses a
+ * switch() so as to be easier to read and update as params change.
+ * -Ghostwolf
+ */
+ switch (stat)
+ {
+ /* open to all, standard # of params */
+ case 'U':
+ case 'u':
+ {
+ if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+ != HUNTED_ISME)
+ return 0;
+ break;
+ }
+
+ /* open to all, varying # of params */
+ case 'k':
+ case 'K':
+ case 'i':
+ case 'I':
+ case 'p':
+ case 'P':
+ {
+ if (parc > 3)
+ {
+ if (hunt_server(0, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
+ != HUNTED_ISME)
+ return 0;
+ }
+ else
+ {
+ if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+ != HUNTED_ISME)
+ return 0;
+ }
+ break;
+ }
+
+ /* oper only, varying # of params */
+ case 'l':
+ case 'L':
+ case 'M':
+ {
+ if (parc == 4)
+ {
+ if (hunt_server(1, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
+ != HUNTED_ISME)
+ return 0;
+ }
+ else if (parc > 4)
+ {
+ if (hunt_server(1, cptr, sptr, ":%s STATS %s %s %s :%s", 2, parc,
+ parv) != HUNTED_ISME)
+ return 0;
+ }
+ else if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+ != HUNTED_ISME)
+ return 0;
+ break;
+ }
+
+ /* oper only, standard # of params */
+ default:
+ {
+ if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+ != HUNTED_ISME)
+ return 0;
+ break;
+ }
+ }
+
+ switch (stat)
+ {
+ case 'L':
+ case 'l':
+ {
+ int doall = 0, wilds = 0;
+ char *name = "*";
+ if (parc > 3 && *parv[3])
+ {
+ char *p;
+ name = parv[3];
+ wilds = (*name == '*' || *name == '?');
+ for (p = name + 1; *p; ++p)
+ if ((*p == '*' || *p == '?') && p[-1] != '\\')
+ {
+ wilds = 1;
+ break;
+ }
+ }
+ else
+ doall = 1;
+ /*
+ * Send info about connections which match, or all if the
+ * mask matches me.name. Only restrictions are on those who
+ * are invisible not being visible to 'foreigners' who use
+ * a wild card based search to list it.
+ */
+ sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = loc_clients[i]))
+ continue;
+ /* Don't return clients when this is a request for `all' */
+ if (doall && IsUser(acptr))
+ continue;
+ /* Don't show invisible people to unauthorized people when using
+ * wildcards -- Is this still needed now /stats is oper only ? */
+ if (IsInvisible(acptr) && (doall || wilds) &&
+ !(MyConnect(sptr) && IsOper(sptr)) &&
+ !IsAnOper(acptr) && (acptr != sptr))
+ continue;
+ /* Only show the ones that match the given mask - if any */
+ if (!doall && wilds && match(name, acptr->name))
+ continue;
+ /* Skip all that do not match the specific query */
+ if (!(doall || wilds) && strCasediff(name, acptr->name))
+ continue;
+ sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
+ (isUpper(stat)) ?
+ get_client_name(acptr, TRUE) : get_client_name(acptr, FALSE),
+ (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
+ (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
+ time(NULL) - acptr->firsttime);
+ }
+ break;
+ }
+ case 'C':
+ case 'c':
+ report_configured_links(sptr,
+ CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER);
+ break;
+ case 'G':
+ case 'g':
+ /* send glines */
+ for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+ {
+ if (agline->expire <= TStime())
+ { /* handle expired glines */
+ free_gline(agline, a2gline);
+ agline = a2gline ? a2gline : gline; /* make sure to splice
+ list together */
+ if (!agline)
+ break; /* last gline; break out of loop */
+ continue; /* continue! */
+ }
+ sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
+ sptr->name, 'G', agline->name, agline->host,
+ agline->expire, agline->reason);
+ a2gline = agline;
+ }
+ break;
+ case 'H':
+ case 'h':
+ report_configured_links(sptr, CONF_HUB | CONF_LEAF);
+ break;
+ case 'I':
+ case 'i':
+ case 'K':
+ case 'k': /* display CONF_IPKILL as well
+ as CONF_KILL -Kev */
+ {
+ int wilds, count;
+ char *user, *host, *p;
+ int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
+ if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
+ {
+ report_configured_links(sptr, conf_status);
+ break;
+ }
+ if (parc < 4 || *parv[3] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+ (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
+ return 0;
+ }
+ wilds = 0;
+ for (p = parv[3]; *p; p++)
+ {
+ if (*p == '\\')
+ {
+ if (!*++p)
+ break;
+ continue;
+ }
+ if (*p == '?' || *p == '*')
+ {
+ wilds = 1;
+ break;
+ }
+ }
+ if (!(MyConnect(sptr) || IsOper(sptr)))
+ {
+ wilds = 0;
+ count = 3;
+ }
+ else
+ count = 1000;
+
+ if (conf_status == CONF_CLIENT)
+ {
+ user = NULL; /* Not used, but to avoid compiler warning. */
+
+ host = parv[3];
+ }
+ else
+ {
+ if ((host = strchr(parv[3], '@')))
+ {
+ user = parv[3];
+ *host++ = 0;;
+ }
+ else
+ {
+ user = NULL;
+ host = parv[3];
+ }
+ }
+ for (aconf = conf; aconf; aconf = aconf->next)
+ {
+ if ((aconf->status & conf_status))
+ {
+ if (conf_status == CONF_KLINE)
+ {
+ if ((!wilds && ((user || aconf->host[1]) &&
+ !match(aconf->host, host) &&
+ (!user || !match(aconf->name, user)))) ||
+ (wilds && !mmatch(host, aconf->host) &&
+ (!user || !mmatch(user, aconf->name))))
+ {
+ sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
+ sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
+ aconf->port, get_conf_class(aconf));
+ if (--count == 0)
+ break;
+ }
+ }
+ else if (conf_status == CONF_CLIENT)
+ {
+ if ((!wilds && (!match(aconf->host, host) ||
+ !match(aconf->name, host))) ||
+ (wilds && (!mmatch(host, aconf->host) ||
+ !mmatch(host, aconf->name))))
+ {
+ sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
+ sptr->name, 'I', aconf->host, aconf->name,
+ aconf->port, get_conf_class(aconf));
+ if (--count == 0)
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case 'M':
+#ifdef MEMSIZESTATS
+ sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
+ me.name, parv[0], get_mem_size(), get_alloc_cnt());
+#endif
+#ifdef MEMLEAKSTATS
+ report_memleak_stats(sptr, parc, parv);
+#endif
+#if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
+ sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
+ "is not enabled on this server", me.name, parv[0]);
+#endif
+ break;
+ case 'm':
+ for (mptr = msgtab; mptr->cmd; mptr++)
+ if (mptr->count)
+ sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
+ me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
+ break;
+ case 'o':
+ case 'O':
+ report_configured_links(sptr, CONF_OPS);
+ break;
+ case 'p':
+ case 'P':
+ {
+ int count = 100;
+ char port[6];
+ if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
+ {
+ report_configured_links(sptr, CONF_LISTEN_PORT);
+ break;
+ }
+ if (!(MyConnect(sptr) || IsOper(sptr)))
+ count = 3;
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status == CONF_LISTEN_PORT)
+ {
+ if (parc >= 4 && *parv[3] != '\0')
+ {
+ sprintf_irc(port, "%u", aconf->port);
+ if (match(parv[3], port))
+ continue;
+ }
+ sendto_one(sptr, rpl_str(RPL_STATSPLINE), me.name, sptr->name, 'P',
+ aconf->port, aconf->clients, aconf->status);
+ if (--count == 0)
+ break;
+ }
+ break;
+ }
+ case 'R':
+ case 'r':
+#ifdef DEBUGMODE
+ send_usage(sptr, parv[0]);
+#endif
+ break;
+ case 'D':
+ report_configured_links(sptr, CONF_CRULEALL);
+ break;
+ case 'd':
+ report_configured_links(sptr, CONF_CRULE);
+ break;
+ case 't':
+ tstats(sptr, parv[0]);
+ break;
+ case 'T':
+ report_configured_links(sptr, CONF_TLINES);
+ break;
+ case 'U':
+ report_configured_links(sptr, CONF_UWORLD);
+ break;
+ case 'u':
+ {
+ register time_t nowr;
+
+ nowr = now - me.since;
+ sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
+ nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
+ sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
+ max_connection_count, max_client_count);
+ break;
+ }
+ case 'W':
+ case 'w':
+ calc_load(sptr);
+ break;
+ case 'X':
+ case 'x':
+#ifdef DEBUGMODE
+ send_listinfo(sptr, parv[0]);
+#endif
+ break;
+ case 'Y':
+ case 'y':
+ report_classes(sptr);
+ break;
+ case 'Z':
+ case 'z':
+ count_memory(sptr, parv[0]);
+ break;
+ default:
+ stat = '*';
+ break;
+ }
+ sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
+ return 0;
+}
+
+/*
+ * m_connect - Added by Jto 11 Feb 1989
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ * parv[2] = port number
+ * parv[3] = remote server
+ */
+int m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ int retval;
+ unsigned short int port, tmpport;
+ aConfItem *aconf, *cconf;
+ aClient *acptr;
+
+ if (!IsPrivileged(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return -1;
+ }
+
+ if (IsLocOp(sptr) && parc > 3) /* Only allow LocOps to make */
+ return 0; /* local CONNECTS --SRB */
+
+ if (parc > 3 && MyUser(sptr))
+ {
+ aClient *acptr2, *acptr3;
+ if (!(acptr3 = find_match_server(parv[3])))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[3]);
+ return 0;
+ }
+
+ /* Look for closest matching server */
+ for (acptr2 = acptr3; acptr2 != &me; acptr2 = acptr2->serv->up)
+ if (!match(parv[3], acptr2->name))
+ acptr3 = acptr2;
+
+ parv[3] = acptr3->name;
+ }
+
+ if (hunt_server(1, cptr, sptr, ":%s CONNECT %s %s :%s", 3, parc, parv) !=
+ HUNTED_ISME)
+ return 0;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "CONNECT");
+ return -1;
+ }
+
+ if ((acptr = FindServer(parv[1])))
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
+ me.name, parv[0], parv[1], "already exists from", acptr->from->name);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :Connect: Server %s %s %s.",
+ NumServ(&me), NumNick(sptr), parv[1], "already exists from",
+ acptr->from->name);
+ return 0;
+ }
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status == CONF_CONNECT_SERVER &&
+ match(parv[1], aconf->name) == 0)
+ break;
+ /* Checked first servernames, then try hostnames. */
+ if (!aconf)
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status == CONF_CONNECT_SERVER &&
+ (match(parv[1], aconf->host) == 0 ||
+ match(parv[1], strchr(aconf->host, '@') + 1) == 0))
+ break;
+
+ if (!aconf)
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr,
+ ":%s NOTICE %s :Connect: Host %s not listed in ircd.conf",
+ me.name, parv[0], parv[1]);
+ else
+ sendto_one(sptr,
+ "%s NOTICE %s%s :Connect: Host %s not listed in ircd.conf",
+ NumServ(&me), NumNick(sptr), parv[1]);
+ return 0;
+ }
+ /*
+ * Get port number from user, if given. If not specified,
+ * use the default from configuration structure. If missing
+ * from there, then use the precompiled default.
+ */
+ tmpport = port = aconf->port;
+ if (parc > 2 && !BadPtr(parv[2]))
+ {
+ if ((port = atoi(parv[2])) == 0)
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr,
+ ":%s NOTICE %s :Connect: Invalid port number", me.name, parv[0]);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :Connect: Invalid port number",
+ NumServ(&me), NumNick(sptr));
+ return 0;
+ }
+ }
+ else if (port == 0 && (port = PORTNUM) == 0)
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
+ me.name, parv[0]);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :Connect: missing port number",
+ NumServ(&me), NumNick(sptr));
+ return 0;
+ }
+
+ /*
+ * Evaluate connection rules... If no rules found, allow the
+ * connect. Otherwise stop with the first true rule (ie: rules
+ * are ored together. Oper connects are effected only by D
+ * lines (CRULEALL) not d lines (CRULEAUTO).
+ */
+ for (cconf = conf; cconf; cconf = cconf->next)
+ if ((cconf->status == CONF_CRULEALL) &&
+ (match(cconf->host, aconf->name) == 0))
+ if (crule_eval(cconf->passwd))
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :Connect: Disallowed by rule: %s",
+ me.name, parv[0], cconf->name);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :Connect: Disallowed by rule: %s",
+ NumServ(&me), NumNick(sptr), cconf->name);
+ return 0;
+ }
+
+ /*
+ * Notify all operators about remote connect requests
+ */
+ if (!IsAnOper(cptr))
+ {
+ sendto_ops_butone(NULL, &me, ":%s WALLOPS :Remote CONNECT %s %s from %s",
+ me.name, parv[1], parv[2] ? parv[2] : "", get_client_name(sptr, FALSE));
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECT)
+ syslog(LOG_DEBUG,
+ "CONNECT From %s : %s %d", parv[0], parv[1], parv[2] ? parv[2] : "");
+#endif
+ }
+ aconf->port = port;
+ switch (retval = connect_server(aconf, sptr, NULL))
+ {
+ case 0:
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr,
+ ":%s NOTICE %s :*** Connecting to %s[%s].",
+ me.name, parv[0], aconf->host, aconf->name);
+ else
+ sendto_one(sptr,
+ "%s NOTICE %s%s :*** Connecting to %s[%s].",
+ NumServ(&me), NumNick(sptr), aconf->host, aconf->name);
+ break;
+ case -1:
+ /* Comments already sent */
+ break;
+ case -2:
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
+ me.name, parv[0], aconf->host);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :*** Host %s is unknown.",
+ NumServ(&me), NumNick(sptr), aconf->host);
+ break;
+ default:
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr,
+ ":%s NOTICE %s :*** Connection to %s failed: %s",
+ me.name, parv[0], aconf->host, strerror(retval));
+ else
+ sendto_one(sptr,
+ "%s NOTICE %s%s :*** Connection to %s failed: %s",
+ NumServ(&me), NumNick(sptr), aconf->host, strerror(retval));
+ }
+ aconf->port = tmpport;
+ return 0;
+}
+
+/*
+ * m_wallops
+ *
+ * Writes to all +w users currently online
+ *
+ * parv[0] = sender prefix
+ * parv[1] = message text
+ */
+int m_wallops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ char *message;
+
+ message = parc > 1 ? parv[1] : NULL;
+
+ if (BadPtr(message))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "WALLOPS");
+ return 0;
+ }
+
+ if (!IsServer(sptr) && MyConnect(sptr) && !IsAnOper(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+ sendto_ops_butone(IsServer(cptr) ? cptr : NULL, sptr,
+ ":%s WALLOPS :%s", parv[0], message);
+ return 0;
+}
+
+/*
+ * m_time
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ */
+int m_time(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ if (hunt_server(0, cptr, sptr, ":%s TIME :%s", 1, parc, parv) == HUNTED_ISME)
+ sendto_one(sptr, rpl_str(RPL_TIME), me.name,
+ parv[0], me.name, TStime(), TSoffset, date((long)0));
+ return 0;
+}
+
+/*
+ * m_settime
+ *
+ * parv[0] = sender prefix
+ * parv[1] = new time
+ * parv[2] = servername (Only used when sptr is an Oper).
+ */
+int m_settime(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ time_t t;
+ long int dt;
+ static char tbuf[11];
+ Dlink *lp;
+
+ if (!IsPrivileged(sptr))
+ return 0;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SETTIME");
+ return 0;
+ }
+
+ if (parc == 2 && MyUser(sptr))
+ parv[parc++] = me.name;
+
+ t = atoi(parv[1]);
+ dt = TStime() - t;
+
+ if (t < 779557906 || dt < -9000000)
+ {
+ sendto_one(sptr, ":%s NOTICE %s :SETTIME: Bad value", me.name, parv[0]);
+ return 0;
+ }
+
+ if (IsServer(sptr)) /* send to unlagged servers */
+ {
+#ifdef RELIABLE_CLOCK
+ sprintf_irc(tbuf, TIME_T_FMT, TStime());
+ parv[1] = tbuf;
+#endif
+ for (lp = me.serv->down; lp; lp = lp->next)
+ if (cptr != lp->value.cptr && DBufLength(&lp->value.cptr->sendQ) < 8000)
+ sendto_one(lp->value.cptr, ":%s SETTIME %s", parv[0], parv[1]);
+ }
+ else
+ {
+ sprintf_irc(tbuf, TIME_T_FMT, TStime());
+ parv[1] = tbuf;
+ if (hunt_server(1, cptr, sptr, ":%s SETTIME %s %s", 2, parc, parv) !=
+ HUNTED_ISME)
+ return 0;
+ }
+
+#ifdef RELIABLE_CLOCK
+ if ((dt > 600) || (dt < -600))
+ sendto_serv_butone((aClient *)NULL,
+ ":%s WALLOPS :Bad SETTIME from %s: " TIME_T_FMT, me.name, sptr->name,
+ t);
+ if (IsUser(sptr))
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :clock is not set %ld seconds %s : "
+ "RELIABLE_CLOCK is defined", me.name, parv[0],
+ (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :clock is not set %ld seconds %s : "
+ "RELIABLE_CLOCK is defined", NumServ(&me), NumNick(sptr),
+ (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+ }
+#else
+ sendto_ops("SETTIME from %s, clock is set %ld seconds %s",
+ get_client_name(sptr, FALSE), (dt < 0) ? -dt : dt,
+ (dt < 0) ? "forwards" : "backwards");
+ TSoffset -= dt;
+ if (IsUser(sptr))
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :clock is set %ld seconds %s", me.name,
+ parv[0], (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :clock is set %ld seconds %s",
+ NumServ(&me), NumNick(sptr),
+ (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+ }
+#endif
+ return 0;
+}
+
+static char *militime(char *sec, char *usec)
+{
+ struct timeval tv;
+ static char timebuf[18];
+
+ gettimeofday(&tv, NULL);
+ if (sec && usec)
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+ sprintf(timebuf, "%ld",
+ (tv.tv_sec - atoi(sec)) * 1000 + (tv.tv_usec - atoi(usec)) / 1000);
+#else
+ sprintf_irc(timebuf, "%d",
+ (tv.tv_sec - atoi(sec)) * 1000 + (tv.tv_usec - atoi(usec)) / 1000);
+#endif
+ else
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+ sprintf(timebuf, "%ld %ld", tv.tv_sec, tv.tv_usec);
+#else
+ sprintf_irc(timebuf, "%d %d", tv.tv_sec, tv.tv_usec);
+#endif
+ return timebuf;
+}
+
+/*
+ * m_rping -- by Run
+ *
+ * parv[0] = sender (sptr->name thus)
+ * if sender is a person: (traveling towards start server)
+ * parv[1] = pinged server[mask]
+ * parv[2] = start server (current target)
+ * parv[3] = optional remark
+ * if sender is a server: (traveling towards pinged server)
+ * parv[1] = pinged server (current target)
+ * parv[2] = original sender (person)
+ * parv[3] = start time in s
+ * parv[4] = start time in us
+ * parv[5] = the optional remark
+ */
+int m_rping(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr;
+
+ if (!IsPrivileged(sptr))
+ return 0;
+
+ if (parc < (IsAnOper(sptr) ? (MyConnect(sptr) ? 2 : 3) : 6))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "RPING");
+ return 0;
+ }
+ if (MyUser(sptr))
+ {
+ if (parc == 2)
+ parv[parc++] = me.name;
+ else if (!(acptr = find_match_server(parv[2])))
+ {
+ parv[3] = parv[2];
+ parv[2] = me.name;
+ parc++;
+ }
+ else
+ parv[2] = acptr->name;
+ if (parc == 3)
+ parv[parc++] = "<No client start time>";
+ }
+
+ if (IsAnOper(sptr))
+ {
+ if (hunt_server(1, cptr, sptr, ":%s RPING %s %s :%s", 2, parc, parv) !=
+ HUNTED_ISME)
+ return 0;
+ if (!(acptr = find_match_server(parv[1])) || !IsServer(acptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
+ return 0;
+ }
+ if (Protocol(acptr->from) < 10)
+ sendto_one(acptr, ":%s RPING %s %s %s :%s",
+ me.name, acptr->name, sptr->name, militime(NULL, NULL), parv[3]);
+ else
+ sendto_one(acptr, ":%s RPING %s %s %s :%s",
+ me.name, NumServ(acptr), sptr->name, militime(NULL, NULL), parv[3]);
+ }
+ else
+ {
+ if (hunt_server(1, cptr, sptr, ":%s RPING %s %s %s %s :%s", 1, parc, parv)
+ != HUNTED_ISME)
+ return 0;
+ sendto_one(cptr, ":%s RPONG %s %s %s %s :%s", me.name, parv[0],
+ parv[2], parv[3], parv[4], parv[5]);
+ }
+ return 0;
+}
+
+/*
+ * m_rpong -- by Run too :)
+ *
+ * parv[0] = sender prefix
+ * parv[1] = from pinged server: start server; from start server: sender
+ * parv[2] = from pinged server: sender; from start server: pinged server
+ * parv[3] = pingtime in ms
+ * parv[4] = client info (for instance start time)
+ */
+int m_rpong(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr;
+
+ if (!IsServer(sptr))
+ return 0;
+
+ if (parc < 5)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "RPING");
+ return 0;
+ }
+
+ if (!(acptr = FindClient(parv[1])))
+ return 0;
+
+ if (!IsMe(acptr))
+ {
+ if (IsServer(acptr) && parc > 5)
+ {
+ sendto_one(acptr, ":%s RPONG %s %s %s %s :%s",
+ parv[0], parv[1], parv[2], parv[3], parv[4], parv[5]);
+ return 0;
+ }
+ }
+ else
+ {
+ parv[1] = parv[2];
+ parv[2] = sptr->name;
+ parv[0] = me.name;
+ parv[3] = militime(parv[3], parv[4]);
+ parv[4] = parv[5];
+ if (!(acptr = FindUser(parv[1])))
+ return 0; /* No bouncing between servers ! */
+ }
+
+ sendto_one(acptr, ":%s RPONG %s %s %s :%s",
+ parv[0], parv[1], parv[2], parv[3], parv[4]);
+ return 0;
+}
+
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+/*
+ * m_rehash
+ */
+int m_rehash(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+#ifndef LOCOP_REHASH
+ if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_REHASH
+ if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+ if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+ sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0], configfile);
+ sendto_ops("%s is rehashing Server config file", parv[0]);
+#ifdef USE_SYSLOG
+ syslog(LOG_INFO, "REHASH From %s\n", get_client_name(sptr, FALSE));
+#endif
+ return rehash(cptr, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0);
+}
+#endif
+
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+/*
+ * m_restart
+ */
+int m_restart(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc),
+ char *parv[])
+{
+#ifndef LOCOP_RESTART
+ if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_RESTART
+ if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+ if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, "Server RESTART by %s\n", get_client_name(sptr, FALSE));
+#endif
+ server_reboot();
+ return 0;
+}
+#endif
+
+/*
+ * m_trace
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nick or servername
+ * parv[2] = 'target' servername
+ */
+int m_trace(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 int i;
+ Reg2 aClient *acptr;
+ aConfClass *cltmp;
+ char *tname;
+ int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
+ int cnt = 0, wilds, dow;
+
+ if (parc < 2 || BadPtr(parv[1]))
+ {
+ /* just "TRACE" without parameters. Must be from local client */
+ parc = 1;
+ acptr = &me;
+ tname = me.name;
+ i = HUNTED_ISME;
+ }
+ else if (parc < 3 || BadPtr(parv[2]))
+ {
+ /* No target specified. Make one before propagating. */
+ parc = 2;
+ tname = parv[1];
+ if ((acptr = find_match_server(parv[1])) ||
+ ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
+ {
+ if (IsUser(acptr))
+ parv[2] = acptr->user->server->name;
+ else
+ parv[2] = acptr->name;
+ parc = 3;
+ parv[3] = NULL;
+ if ((i = hunt_server(IsServer(acptr), cptr, sptr,
+ ":%s TRACE %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
+ return 0;
+ }
+ else
+ i = HUNTED_ISME;
+ }
+ else
+ {
+ /* Got "TRACE <tname> :<target>" */
+ parc = 3;
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ acptr = find_match_server(parv[2]);
+ else
+ acptr = FindNServer(parv[2]);
+ if ((i = hunt_server(0, cptr, sptr,
+ ":%s TRACE %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
+ return 0;
+ tname = parv[1];
+ }
+
+ if (i == HUNTED_PASS)
+ {
+ if (!acptr)
+ acptr = next_client(client, tname);
+ else
+ acptr = acptr->from;
+ sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
+#ifndef GODMODE
+ version, debugmode, tname, acptr ? acptr->from->name : "<No_match>");
+#else /* GODMODE */
+ version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
+ (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0);
+#endif /* GODMODE */
+ return 0;
+ }
+
+ doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : TRUE;
+ wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
+ dow = wilds || doall;
+
+ /* Don't give (long) remote listings to lusers */
+ if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
+ return 0;
+
+ for (i = 0; i < MAXCONNECTIONS; i++)
+ link_s[i] = 0, link_u[i] = 0;
+
+ if (doall)
+ {
+ for (acptr = client; acptr; acptr = acptr->next)
+ if (IsUser(acptr))
+ link_u[acptr->from->fd]++;
+ else if (IsServer(acptr))
+ link_s[acptr->from->fd]++;
+ }
+
+ /* report all direct connections */
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ char *name;
+ unsigned int conClass;
+
+ if (!(acptr = loc_clients[i])) /* Local Connection? */
+ continue;
+ if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
+ !IsAnOper(acptr) && (acptr != sptr))
+ continue;
+ if (!doall && wilds && match(tname, acptr->name))
+ continue;
+ if (!dow && strCasediff(tname, acptr->name))
+ continue;
+ name = get_client_name(acptr, FALSE);
+ conClass = get_client_class(acptr);
+
+ switch (acptr->status)
+ {
+ case STAT_CONNECTING:
+ sendto_one(sptr, rpl_str(RPL_TRACECONNECTING),
+ me.name, parv[0], conClass, name);
+ cnt++;
+ break;
+ case STAT_HANDSHAKE:
+ sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE),
+ me.name, parv[0], conClass, name);
+ cnt++;
+ break;
+ case STAT_ME:
+ break;
+ case STAT_UNKNOWN:
+ case STAT_UNKNOWN_USER:
+ case STAT_UNKNOWN_SERVER:
+ sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
+ me.name, parv[0], conClass, name);
+ cnt++;
+ break;
+ case STAT_USER:
+ /* Only opers see users if there is a wildcard
+ but anyone can see all the opers. */
+ if ((IsAnOper(sptr) && (MyUser(sptr) ||
+ !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr))
+ {
+ if (IsAnOper(acptr))
+ sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
+ me.name, parv[0], conClass, name, now - acptr->lasttime);
+ else
+ sendto_one(sptr, rpl_str(RPL_TRACEUSER),
+ me.name, parv[0], conClass, name, now - acptr->lasttime);
+ cnt++;
+ }
+ break;
+ /*
+ * Connection is a server
+ *
+ * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
+ *
+ * class Class the server is in
+ * nS Number of servers reached via this link
+ * nC Number of clients reached via this link
+ * name Name of the server linked
+ * ConnBy Who established this link
+ * last Seconds since we got something from this link
+ * age Seconds this link has been alive
+ *
+ * Additional comments etc...... -Cym-<cym@acrux.net>
+ */
+
+ case STAT_SERVER:
+ if (acptr->serv->user)
+ sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+ me.name, parv[0], conClass, link_s[i],
+ link_u[i], name, acptr->serv->by,
+ acptr->serv->user->username,
+ acptr->serv->user->host,
+ now - acptr->lasttime, now - acptr->serv->timestamp);
+ else
+ sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+ me.name, parv[0], conClass, link_s[i],
+ link_u[i], name, *(acptr->serv->by) ?
+ acptr->serv->by : "*", "*", me.name,
+ now - acptr->lasttime, now - acptr->serv->timestamp);
+ cnt++;
+ break;
+ case STAT_LOG:
+ sendto_one(sptr, rpl_str(RPL_TRACELOG),
+ me.name, parv[0], LOGFILE, acptr->port);
+ cnt++;
+ break;
+ case STAT_PING:
+ sendto_one(sptr, rpl_str(RPL_TRACEPING), me.name,
+ parv[0], name, (acptr->acpt) ? acptr->acpt->name : "<null>");
+ break;
+ default: /* We actually shouldn't come here, -msa */
+ sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name, parv[0], name);
+ cnt++;
+ break;
+ }
+ }
+ /*
+ * Add these lines to summarize the above which can get rather long
+ * and messy when done remotely - Avalon
+ */
+ if (!IsAnOper(sptr) || !cnt)
+ {
+ if (!cnt)
+ /* let the user have some idea that its at the end of the trace */
+ sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+ me.name, parv[0], 0, link_s[me.fd],
+ link_u[me.fd], "<No_match>", *(me.serv->by) ?
+ me.serv->by : "*", "*", me.name, 0, 0);
+ return 0;
+ }
+ for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
+ if (Links(cltmp) > 0)
+ sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
+ parv[0], ConClass(cltmp), Links(cltmp));
+ return 0;
+}
+
+/*
+ * m_close - added by Darren Reed Jul 13 1992.
+ */
+int m_close(aClient *cptr, aClient *sptr, int UNUSED(parc), char *parv[])
+{
+ Reg1 aClient *acptr;
+ Reg2 int i;
+ int closed = 0;
+
+ if (!MyOper(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+
+ for (i = highest_fd; i; i--)
+ {
+ if (!(acptr = loc_clients[i]))
+ continue;
+ if (!IsUnknown(acptr) && !IsConnecting(acptr) && !IsHandshake(acptr))
+ continue;
+ sendto_one(sptr, rpl_str(RPL_CLOSING), me.name, parv[0],
+ get_client_name(acptr, TRUE), acptr->status);
+ exit_client(cptr, acptr, &me, "Oper Closing");
+ closed++;
+ }
+ sendto_one(sptr, rpl_str(RPL_CLOSEEND), me.name, parv[0], closed);
+ return 0;
+}
+
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+/*
+ * m_die
+ */
+int m_die(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc), char *parv[])
+{
+ Reg1 aClient *acptr;
+ Reg2 int i;
+
+#ifndef LOCOP_DIE
+ if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_DIE
+ if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+ if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = loc_clients[i]))
+ continue;
+ if (IsUser(acptr))
+ sendto_one(acptr, ":%s NOTICE %s :Server Terminating. %s",
+ me.name, acptr->name, get_client_name(sptr, TRUE));
+ else if (IsServer(acptr))
+ sendto_one(acptr, ":%s ERROR :Terminated by %s",
+ me.name, get_client_name(sptr, TRUE));
+ }
+#ifdef __cplusplus
+ s_die(0);
+#else
+ s_die();
+#endif
+ return 0;
+}
+#endif
+
+static void add_gline(aClient *sptr, int ip_mask, char *host, char *comment,
+ char *user, time_t expire, int local)
+{
+ aClient *acptr;
+ aGline *agline;
+ int fd;
+
+ /* Inform ops */
+ sendto_op_mask(SNO_GLINE,
+ "%s adding %sGLINE for %s@%s, expiring at " TIME_T_FMT ": %s", sptr->name,
+ local ? "local " : "", user, host, expire, comment);
+
+#ifdef GPATH
+ write_log(GPATH,
+ "# " TIME_T_FMT " %s adding %s GLINE for %s@%s, expiring at " TIME_T_FMT
+ ": %s\n", TStime(), sptr->name, local ? "local" : "global",
+ user, host, expire, comment);
+
+ /* this can be inserted into the conf */
+ write_log(GPATH, "%c:%s:%s:%s\n", ip_mask ? 'k' : 'K', host, comment, user);
+#endif /* GPATH */
+
+ agline = make_gline(ip_mask, host, comment, user, expire);
+ if (local)
+ SetGlineIsLocal(agline);
+
+ for (fd = highest_fd; fd >= 0; --fd) /* get the users! */
+ if ((acptr = loc_clients[fd]) && !IsMe(acptr))
+ {
+
+ if (!acptr->user || strlen(acptr->sockhost) > (size_t)HOSTLEN ||
+ (acptr->user->username ? strlen(acptr->user->username) : 0) >
+ (size_t)HOSTLEN)
+ continue; /* these tests right out of
+ find_kill for safety's sake */
+
+ if ((GlineIsIpMask(agline) ?
+ match(agline->host, inetntoa(acptr->ip)) :
+ match(agline->host, acptr->sockhost)) == 0 &&
+ (!acptr->user->username ||
+ match(agline->name, acptr->user->username) == 0))
+ {
+
+ /* ok, he was the one that got G-lined */
+ sendto_one(acptr, ":%s %d %s :*** %s.", me.name,
+ ERR_YOUREBANNEDCREEP, acptr->name, agline->reason);
+
+ /* let the ops know about my first kill */
+ sendto_op_mask(SNO_GLINE, "G-line active for %s",
+ get_client_name(acptr, FALSE));
+
+ /* and get rid of him */
+ if (sptr != acptr)
+ exit_client(sptr->from, acptr, &me, "G-lined");
+ }
+ }
+}
+
+/*
+ * m_gline
+ *
+ * parv[0] = Send prefix
+ *
+ * From server:
+ *
+ * parv[1] = Target: server numeric
+ * parv[2] = [+|-]<G-line mask>
+ * parv[3] = Expiration offset
+ * parv[4] = Comment
+ *
+ * From client:
+ *
+ * parv[1] = [+|-]<G-line mask>
+ * parv[2] = Expiration offset
+ * parv[3] = Comment
+ *
+ */
+int m_gline(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr = NULL; /* Init. to avoid compiler warning. */
+
+ aGline *agline, *a2gline;
+ char *user, *host;
+ int active, ip_mask;
+ time_t expire = 0;
+
+ /* Remove expired G-lines */
+ for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+ {
+ if (agline->expire <= TStime())
+ {
+ free_gline(agline, a2gline);
+ agline = a2gline ? a2gline : gline;
+ if (!agline)
+ break;
+ continue;
+ }
+ a2gline = agline;
+ }
+
+ if (IsServer(cptr))
+ {
+ if (find_conf_host(cptr->confs, sptr->name, CONF_UWORLD))
+ {
+ if (parc < 3 || (*parv[2] != '-' && (parc < 5 || *parv[4] == '\0')))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+ "GLINE");
+ return 0;
+ }
+
+ if (*parv[2] == '-') /* add mode or delete mode? */
+ active = 0;
+ else
+ active = 1;
+
+ if (*parv[2] == '+' || *parv[2] == '-')
+ parv[2]++; /* step past mode indicator */
+
+ /* forward the message appropriately */
+ if (!strCasediff(parv[1], "*"))
+ sendto_serv_butone(cptr, active ? ":%s GLINE %s +%s %s :%s" :
+ ":%s GLINE %s -%s", parv[0], parv[1], parv[2], parv[3], parv[4]); /* global! */
+ else if ((
+#if 1
+ /*
+ * REMOVE THIS after all servers upgraded to 2.10.01 and
+ * Uworld uses a numeric too
+ */
+ (strlen(parv[1]) != 1 && !(acptr = FindClient(parv[1])))) ||
+ (strlen(parv[1]) == 1 &&
+#endif
+ !(acptr = FindNServer(parv[1]))))
+ return 0; /* no such server/user exists; forget it */
+ else
+#if 1
+/*
+ * REMOVE THIS after all servers upgraded to 2.10.01 and
+ * Uworld uses a numeric too
+ */
+ if (IsServer(acptr) || !MyConnect(acptr))
+#endif
+ {
+ sendto_one(acptr, active ? ":%s GLINE %s +%s %s :%s" :
+ ":%s GLINE %s -%s", parv[0], parv[1], parv[2], parv[3], parv[4]); /* single destination */
+ return 0; /* only the intended destination
+ should add this gline */
+ }
+
+ if (!(host = strchr(parv[2], '@')))
+ { /* convert user@host */
+ user = "*"; /* no @'s; assume username is '*' */
+ host = parv[2];
+ }
+ else
+ {
+ user = parv[2];
+ *(host++) = '\0'; /* break up string at the '@' */
+ }
+ ip_mask = check_if_ipmask(host); /* Store this boolean */
+
+ for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+ {
+ if (!strCasediff(agline->name, user)
+ && !strCasediff(agline->host, host))
+ break;
+ a2gline = agline;
+ }
+
+ if (!active && agline)
+ { /* removing the gline */
+ sendto_op_mask(SNO_GLINE, "%s removing GLINE for %s@%s", parv[0],
+ agline->name, agline->host); /* notify opers */
+
+#ifdef GPATH
+ write_log(GPATH, "# " TIME_T_FMT " %s removing GLINE for %s@%s\n",
+ TStime(), parv[0], agline->name, agline->host);
+#endif /* GPATH */
+
+ free_gline(agline, a2gline); /* remove the gline */
+ }
+ else if (active)
+ { /* must be adding a gline */
+ expire = atoi(parv[3]) + TStime(); /* expire time? */
+ if (agline && agline->expire < expire)
+ { /* new expire time? */
+ /* yes, notify the opers */
+ sendto_op_mask(SNO_GLINE,
+ "%s resetting expiration time on GLINE for %s@%s to " TIME_T_FMT,
+ parv[0], agline->name, agline->host, expire);
+
+#ifdef GPATH
+ write_log(GPATH, "# " TIME_T_FMT " %s resetting expiration time "
+ "on GLINE for %s@%s to " TIME_T_FMT "\n",
+ TStime(), parv[0], agline->name, agline->host, expire);
+#endif /* GPATH */
+
+ agline->expire = expire; /* reset the expire time */
+ }
+ else if (!agline)
+ { /* create gline */
+ for (agline = gline; agline; agline = agline->next)
+ if (!mmatch(agline->name, user) &&
+ (ip_mask ? GlineIsIpMask(agline) : !GlineIsIpMask(agline)) &&
+ !mmatch(agline->host, host))
+ return 0; /* found an existing G-line that matches */
+
+ /* add the line: */
+ add_gline(sptr, ip_mask, host, parv[4], user, expire, 0);
+ }
+ }
+ }
+ }
+ else if (parc < 2 || *parv[1] == '\0')
+ {
+ /* Not enough args and a user; list glines */
+ for (agline = gline; agline; agline = agline->next)
+ sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0],
+ agline->name, agline->host, agline->expire, agline->reason,
+ GlineIsActive(agline) ? (GlineIsLocal(agline) ? " (local)" : "") :
+ " (Inactive)");
+ sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
+ }
+ else
+ {
+ int priv;
+
+#ifdef LOCOP_LGLINE
+ priv = IsAnOper(cptr);
+#else
+ priv = IsOper(cptr);
+#endif
+
+ if (priv)
+ { /* non-oper not permitted to change things */
+ if (*parv[1] == '-')
+ { /* oper wants to deactivate the gline */
+ active = 0;
+ parv[1]++;
+ }
+ else if (*parv[1] == '+')
+ { /* oper wants to activate inactive gline */
+ active = 1;
+ parv[1]++;
+ }
+ else
+ active = -1;
+
+ if (parc > 2)
+ expire = atoi(parv[2]) + TStime(); /* oper wants to reset
+ expire TS */
+ }
+ else
+ active = -1;
+
+ if (!(host = strchr(parv[1], '@')))
+ {
+ user = "*"; /* no @'s; assume username is '*' */
+ host = parv[1];
+ }
+ else
+ {
+ user = parv[1];
+ *(host++) = '\0'; /* break up string at the '@' */
+ }
+ ip_mask = check_if_ipmask(host); /* Store this boolean */
+
+ for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+ {
+ if (!mmatch(agline->name, user) &&
+ (ip_mask ? GlineIsIpMask(agline) : !GlineIsIpMask(agline)) &&
+ !mmatch(agline->host, host))
+ break;
+ a2gline = agline;
+ }
+
+ if (!agline)
+ {
+#ifdef OPER_LGLINE
+ if (priv && active && expire > now)
+ {
+ /* Add local G-line */
+ if (parc < 4 || !strchr(parv[3], ' '))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+ me.name, parv[0], "GLINE");
+ return 0;
+ }
+ add_gline(sptr, ip_mask, host, parv[3], user, expire, 1);
+ }
+ else
+#endif
+ sendto_one(cptr, err_str(ERR_NOSUCHGLINE), me.name, parv[0], user,
+ host);
+
+ return 0;
+ }
+
+ if (expire <= agline->expire)
+ expire = 0;
+
+ if ((active == -1 ||
+ (active ? GlineIsActive(agline) : !GlineIsActive(agline))) &&
+ expire == 0)
+ {
+ /* oper wants a list of one gline only */
+ sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0], agline->name,
+ agline->host, agline->expire, agline->reason,
+ GlineIsActive(agline) ? "" : " (Inactive)");
+ sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
+ return 0;
+ }
+
+ if (active != -1 &&
+ (active ? !GlineIsActive(agline) : GlineIsActive(agline)))
+ {
+ if (active) /* reset activation on gline */
+ SetActive(agline);
+#ifdef OPER_LGLINE
+ else if (GlineIsLocal(agline))
+ {
+ /* Remove local G-line */
+ sendto_op_mask(SNO_GLINE, "%s removed local GLINE for %s@%s",
+ parv[0], agline->name, agline->host);
+#ifdef GPATH
+ write_log(GPATH, "# " TIME_T_FMT
+ " %s!%s@%s removed local GLINE for %s@%s\n",
+ TStime(), parv[0], cptr->user->username, cptr->user->host,
+ agline->name, agline->host);
+#endif /* GPATH */
+ free_gline(agline, a2gline); /* remove the gline */
+ return 0;
+ }
+#endif
+ else
+ ClearActive(agline);
+ }
+ else
+ active = -1; /* for later sendto_ops and logging functions */
+
+ if (expire)
+ agline->expire = expire; /* reset expiration time */
+
+ /* inform the operators what's up */
+ if (active != -1)
+ { /* changing the activation */
+ sendto_op_mask(SNO_GLINE, !expire ? "%s %sactivating GLINE for %s@%s" :
+ "%s %sactivating GLINE for %s@%s and "
+ "resetting expiration time to " TIME_T_FMT,
+ parv[0], active ? "re" : "de", agline->name,
+ agline->host, agline->expire);
+#ifdef GPATH
+ write_log(GPATH, !expire ? "# " TIME_T_FMT " %s!%s@%s %sactivating "
+ "GLINE for %s@%s\n" : "# " TIME_T_FMT " %s!%s@%s %sactivating GLINE "
+ "for %s@%s and resetting expiration time to " TIME_T_FMT "\n",
+ TStime(), parv[0], cptr->user->username, cptr->user->host,
+ active ? "re" : "de", agline->name, agline->host, agline->expire);
+#endif /* GPATH */
+
+ }
+ else if (expire)
+ { /* changing only the expiration */
+ sendto_op_mask(SNO_GLINE,
+ "%s resetting expiration time on GLINE for %s@%s to " TIME_T_FMT,
+ parv[0], agline->name, agline->host, agline->expire);
+#ifdef GPATH
+ write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s resetting expiration "
+ "time on GLINE for %s@%s to " TIME_T_FMT "\n", TStime(), parv[0],
+ cptr->user->username, cptr->user->host, agline->name,
+ agline->host, agline->expire);
+#endif /* GPATH */
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, common/packet.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "msg.h"
+#include "parse.h"
+#include "send.h"
+#include "packet.h"
+#include "s_serv.h"
+
+#include <assert.h>
+
+RCSTAG_CC("$Id$");
+
+/*
+ * dopacket
+ *
+ * cptr - pointer to client structure for which the buffer data
+ * applies.
+ * buffer - pointer to the buffer containing the newly read data
+ * length - number of valid bytes of data in the buffer
+ *
+ * Note:
+ * It is implicitly assumed that dopacket is called only
+ * with cptr of "local" variation, which contains all the
+ * necessary fields (buffer etc..)
+ */
+int dopacket(aClient *cptr, char *buffer, int length)
+{
+ Reg1 char *ch1;
+ Reg2 char *ch2;
+ register char *cptrbuf;
+ aClient *acpt = cptr->acpt;
+
+ cptrbuf = cptr->buffer;
+ me.receiveB += length; /* Update bytes received */
+ cptr->receiveB += length;
+ if (cptr->receiveB > 1023)
+ {
+ cptr->receiveK += (cptr->receiveB >> 10);
+ cptr->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
+ }
+ if (acpt != &me)
+ {
+ acpt->receiveB += length;
+ if (acpt->receiveB > 1023)
+ {
+ acpt->receiveK += (acpt->receiveB >> 10);
+ acpt->receiveB &= 0x03ff;
+ }
+ }
+ else if (me.receiveB > 1023)
+ {
+ me.receiveK += (me.receiveB >> 10);
+ me.receiveB &= 0x03ff;
+ }
+ ch1 = cptrbuf + cptr->count;
+ ch2 = buffer;
+ while (--length >= 0)
+ {
+ register char g;
+ g = (*ch1 = *ch2++);
+ /*
+ * Yuck. Stuck. To make sure we stay backward compatible,
+ * we must assume that either CR or LF terminates the message
+ * and not CR-LF. By allowing CR or LF (alone) into the body
+ * of messages, backward compatibility is lost and major
+ * problems will arise. - Avalon
+ */
+ if (g < '\16' && (g == '\n' || g == '\r'))
+ {
+ if (ch1 == cptrbuf)
+ continue; /* Skip extra LF/CR's */
+ *ch1 = '\0';
+ me.receiveM += 1; /* Update messages received */
+ cptr->receiveM += 1;
+ if (cptr->acpt != &me)
+ cptr->acpt->receiveM += 1;
+ if (IsServer(cptr))
+ {
+ if (parse_server(cptr, cptr->buffer, ch1) == CPTR_KILLED)
+ return CPTR_KILLED;
+ }
+ else if (parse_client(cptr, cptr->buffer, ch1) == CPTR_KILLED)
+ return CPTR_KILLED;
+ /*
+ * Socket is dead so exit
+ */
+ if (IsDead(cptr))
+ return exit_client(cptr, cptr, &me, LastDeadComment(cptr));
+ ch1 = cptrbuf;
+ }
+ else if (ch1 < cptrbuf + (sizeof(cptr->buffer) - 1))
+ ch1++; /* There is always room for the null */
+ }
+ cptr->count = ch1 - cptr->buffer;
+ return 0;
+}
+
+/*
+ * client_dopacket - handle client messages
+ */
+int client_dopacket(aClient *cptr, size_t length)
+{
+ assert(0 != cptr);
+
+ me.receiveB += length; /* Update bytes received */
+ cptr->receiveB += length;
+
+ if (cptr->receiveB > 1023)
+ {
+ cptr->receiveK += (cptr->receiveB >> 10);
+ cptr->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
+ }
+ if (me.receiveB > 1023)
+ {
+ me.receiveK += (me.receiveB >> 10);
+ me.receiveB &= 0x03ff;
+ }
+ cptr->count = 0;
+
+ ++me.receiveM; /* Update messages received */
+ ++cptr->receiveM;
+
+ if (CPTR_KILLED == parse_client(cptr, cptr->buffer, cptr->buffer + length))
+ return CPTR_KILLED;
+ else if (IsDead(cptr))
+ return exit_client(cptr, cptr, &me, LastDeadComment(cptr));
+
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, common/parse.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "send.h"
+#include "parse.h"
+#include "common.h"
+#include "s_bsd.h"
+#include "msg.h"
+#include "s_user.h"
+#include "s_serv.h"
+#include "channel.h"
+#include "whowas.h"
+#include "s_ping.h"
+#include "s_conf.h"
+#include "res.h"
+#include "map.h"
+#include "hash.h"
+#include "numeric.h"
+#include "ircd.h"
+#include "s_misc.h"
+#include "common.h"
+#include "s_numeric.h"
+#include "numnicks.h"
+#include "opercmds.h"
+#include "querycmds.h"
+#include "whocmds.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+aMessage msgtab[] = {
+ {CLASS_PRIVATE, MSG_PRIVATE, TOK_PRIVATE, m_private, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_NICK, MSG_NICK, TOK_NICK, m_nick, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_NOTICE, MSG_NOTICE, TOK_NOTICE, m_notice, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_WALLCHOPS, MSG_WALLCHOPS, TOK_WALLCHOPS, m_wallchops, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_CPRIVMSG, MSG_CPRIVMSG, TOK_CPRIVMSG, m_cprivmsg, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_CNOTICE, MSG_CNOTICE, TOK_CNOTICE, m_cnotice, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_JOIN, MSG_JOIN, TOK_JOIN, m_join, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_MODE, MSG_MODE, TOK_MODE, m_mode, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_BURST, MSG_BURST, TOK_BURST, m_burst, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_CREATE, MSG_CREATE, TOK_CREATE, m_create, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_DESTRUCT, MSG_DESTRUCT, TOK_DESTRUCT, m_destruct, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_QUIT, MSG_QUIT, TOK_QUIT, m_quit, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_PART, MSG_PART, TOK_PART, m_part, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_TOPIC, MSG_TOPIC, TOK_TOPIC, m_topic, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_INVITE, MSG_INVITE, TOK_INVITE, m_invite, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_KICK, MSG_KICK, TOK_KICK, m_kick, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_WALLOPS, MSG_WALLOPS, TOK_WALLOPS, m_wallops, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_DESYNCH, MSG_DESYNCH, TOK_DESYNCH, m_desynch, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_PING, MSG_PING, TOK_PING, m_ping, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_PONG, MSG_PONG, TOK_PONG, m_pong, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_ERROR, MSG_ERROR, TOK_ERROR, m_error, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_KILL, MSG_KILL, TOK_KILL, m_kill, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_USER, MSG_USER, TOK_USER, m_user, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_AWAY, MSG_AWAY, TOK_AWAY, m_away, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_ISON, MSG_ISON, TOK_ISON, m_ison, 0, 1, MFLG_SLOW, 0L},
+ {CLASS_SERVER, MSG_SERVER, TOK_SERVER, m_server, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_SQUIT, MSG_SQUIT, TOK_SQUIT, m_squit, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_WHOIS, MSG_WHOIS, TOK_WHOIS, m_whois, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_WHO, MSG_WHO, TOK_WHO, m_who, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_WHOWAS, MSG_WHOWAS, TOK_WHOWAS, m_whowas, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_LIST, MSG_LIST, TOK_LIST, m_list, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_NAMES, MSG_NAMES, TOK_NAMES, m_names, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_USERHOST, MSG_USERHOST, TOK_USERHOST, m_userhost, 0, 1, MFLG_SLOW, 0L},
+ {CLASS_USERIP, MSG_USERIP, TOK_USERIP, m_userip, 0, 1, MFLG_SLOW, 0L},
+ {CLASS_TRACE, MSG_TRACE, TOK_TRACE, m_trace, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_PASS, MSG_PASS, TOK_PASS, m_pass, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_LUSERS, MSG_LUSERS, TOK_LUSERS, m_lusers, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_TIME, MSG_TIME, TOK_TIME, m_time, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_SETTIME, MSG_SETTIME, TOK_SETTIME, m_settime, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_RPING, MSG_RPING, TOK_RPING, m_rping, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_RPONG, MSG_RPONG, TOK_RPONG, m_rpong, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_OPER, MSG_OPER, TOK_OPER, m_oper, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_CONNECT, MSG_CONNECT, TOK_CONNECT, m_connect, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_UPING, MSG_UPING, TOK_UPING, m_uping, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_MAP, MSG_MAP, TOK_MAP, m_map, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_VERSION, MSG_VERSION, TOK_VERSION, m_version, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_STATS, MSG_STATS, TOK_STATS, m_stats, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_LINKS, MSG_LINKS, TOK_LINKS, m_links, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_ADMIN, MSG_ADMIN, TOK_ADMIN, m_admin, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_HELP, MSG_HELP, TOK_HELP, m_help, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_INFO, MSG_INFO, TOK_INFO, m_info, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_MOTD, MSG_MOTD, TOK_MOTD, m_motd, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_CLOSE, MSG_CLOSE, TOK_CLOSE, m_close, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_SILENCE, MSG_SILENCE, TOK_SILENCE, m_silence, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_GLINE, MSG_GLINE, TOK_GLINE, m_gline, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_END_OF_BURST, MSG_END_OF_BURST, TOK_END_OF_BURST, m_end_of_burst, 0, MAXPARA, MFLG_SLOW, 0L},
+ {CLASS_END_OF_BURST_ACK, MSG_END_OF_BURST_ACK, TOK_END_OF_BURST_ACK, m_end_of_burst_ack, 0, MAXPARA, 1, 0L},
+ {CLASS_HASH, MSG_HASH, TOK_HASH, m_hash, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
+ {CLASS_DNS, MSG_DNS, TOK_DNS, m_dns, 0, MAXPARA, MFLG_SLOW, 0L},
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+ {CLASS_REHASH, MSG_REHASH, TOK_REHASH, m_rehash, 0, MAXPARA, MFLG_SLOW, 0L},
+#endif
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+ {CLASS_RESTART, MSG_RESTART, TOK_RESTART, m_restart, 0, MAXPARA, MFLG_SLOW, 0L},
+#endif
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+ {CLASS_DIE, MSG_DIE, TOK_DIE, m_die, 0, MAXPARA, MFLG_SLOW, 0L},
+#endif
+ {0, (char *)0, (char *)0, (int (*)(aClient *, aClient *, int, char **))0, 0, 0, 0, 0L}
+} ;
+/* *INDENT-ON* */
+
+#ifdef GODMODE
+extern int sdbflag;
+#endif /* GODMODE */
+
+static char *para[MAXPARA + 2]; /* leave room for prefix and null */
+
+/*
+ * Message Tree stuff mostly written by orabidoo, with changes by Dianora.
+ * Adapted to Undernet, adding token support, etc by comstud 10/06/97
+ */
+
+static aMessageTree msg_tree_cmd;
+static aMessageTree msg_tree_tok;
+
+/*
+ * Guts of making the token tree...
+ */
+static aMessage **do_msg_tree_tok(aMessageTree *mtree, char *prefix,
+ aMessage **mptr)
+{
+ char newprefix[64]; /* Must be longer than every command name */
+ int c, c2, lp;
+ aMessageTree *mtree1;
+
+ lp = strlen(prefix);
+ if (!lp || !strncmp((*mptr)->tok, prefix, lp))
+ {
+ if (!mptr[1] || (lp && strncmp(mptr[1]->tok, prefix, lp)))
+ {
+ /* last command in the message struct or last command in this prefix */
+ mtree->final = (*mptr)->tok + lp;
+ mtree->msg = *mptr;
+ for (c = 0; c < 26; ++c)
+ mtree->pointers[c] = NULL;
+ return mptr + 1;
+ }
+ /* command in this prefix */
+ if (!strCasediff((*mptr)->tok, prefix))
+ {
+ mtree->final = "";
+ mtree->msg = *mptr++;
+ }
+ else
+ mtree->final = NULL;
+
+ for (c = 'A'; c <= 'Z'; ++c)
+ {
+ if ((*mptr)->tok[lp] == c)
+ {
+ mtree1 = (aMessageTree *)RunMalloc(sizeof(aMessageTree));
+ mtree1->final = NULL;
+ mtree->pointers[c - 'A'] = mtree1;
+ strcpy(newprefix, prefix);
+ newprefix[lp] = c;
+ newprefix[lp + 1] = '\0';
+ mptr = do_msg_tree_tok(mtree1, newprefix, mptr);
+ if (!*mptr || strncmp((*mptr)->tok, prefix, lp))
+ {
+ for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
+ mtree->pointers[c2] = NULL;
+ return mptr;
+ }
+ }
+ else
+ mtree->pointers[c - 'A'] = NULL;
+ }
+ return mptr;
+ }
+ MyCoreDump; /* This should never happen */
+ exit(1);
+}
+
+/*
+ * Guts of making the command tree...
+ */
+static aMessage *do_msg_tree_cmd(aMessageTree *mtree, char *prefix,
+ aMessage *mptr)
+{
+ char newprefix[64]; /* Must be longer than every command name */
+ int c, c2, lp;
+ aMessageTree *mtree1;
+
+ lp = strlen(prefix);
+ if (!lp || !strncmp(mptr->cmd, prefix, lp))
+ {
+ if (!mptr[1].cmd || (lp && strncmp(mptr[1].cmd, prefix, lp)))
+ {
+ /* last command in the message struct or last command in this prefix */
+ mtree->final = mptr->cmd + lp;
+ mtree->msg = mptr;
+ for (c = 0; c < 26; ++c)
+ mtree->pointers[c] = NULL;
+ return mptr + 1;
+ }
+ /* command in this prefix */
+ if (!strCasediff(mptr->cmd, prefix))
+ {
+ mtree->final = "";
+ mtree->msg = mptr++;
+ }
+ else
+ mtree->final = NULL;
+
+ for (c = 'A'; c <= 'Z'; ++c)
+ {
+ if (mptr->cmd[lp] == c)
+ {
+ mtree1 = (aMessageTree *)RunMalloc(sizeof(aMessageTree));
+ mtree1->final = NULL;
+ mtree->pointers[c - 'A'] = mtree1;
+ strcpy(newprefix, prefix);
+ newprefix[lp] = c;
+ newprefix[lp + 1] = '\0';
+ mptr = do_msg_tree_cmd(mtree1, newprefix, mptr);
+ if (!mptr->cmd || strncmp(mptr->cmd, prefix, lp))
+ {
+ for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
+ mtree->pointers[c2] = NULL;
+ return mptr;
+ }
+ }
+ else
+ mtree->pointers[c - 'A'] = NULL;
+ }
+ return mptr;
+ }
+ MyCoreDump; /* This should never happen */
+ exit(1);
+}
+
+static int mcmdcmp(const struct Message *m1, const struct Message *m2)
+{
+ return strcmp(m1->cmd, m2->cmd);
+}
+
+static int mtokcmp(const struct Message **m1, const struct Message **m2)
+{
+ return strcmp((*m1)->tok, (*m2)->tok);
+}
+
+/*
+ * Sort the command names.
+ * Create table of pointers into msgtab for tokens.
+ * Create trees for ->cmd and ->tok and free the token pointers.
+ */
+void initmsgtree(void)
+{
+ Reg1 int i;
+ Reg2 aMessage *msg = msgtab;
+ Reg3 int ii;
+ aMessage **msgtab_tok;
+ aMessage **msgtok;
+
+ for (i = 0; msg->cmd; ++i, ++msg)
+ continue;
+ qsort(msgtab, i, sizeof(aMessage),
+ (int (*)(const void *, const void *))mcmdcmp);
+ msgtab_tok = (aMessage **)RunMalloc((i + 1) * sizeof(aMessage *));
+ for (ii = 0; ii < i; ++ii)
+ msgtab_tok[ii] = msgtab + ii;
+ msgtab_tok[i] = NULL; /* Needed by `do_msg_tree_tok' */
+ qsort(msgtab_tok, i, sizeof(aMessage *),
+ (int (*)(const void *, const void *))mtokcmp);
+ msg = do_msg_tree_cmd(&msg_tree_cmd, "", msgtab);
+ msgtok = do_msg_tree_tok(&msg_tree_tok, "", msgtab_tok);
+ RunFree(msgtab_tok);
+}
+
+/*
+ * Generic tree parser which works for both commands and tokens.
+ * Optimized by Run.
+ */
+static struct Message *msg_tree_parse(register char *cmd, aMessageTree *root)
+{
+ register aMessageTree *mtree;
+ register unsigned char r = (0xdf & (unsigned char)*cmd) - 'A';
+ if (r > 25 || !(mtree = root->pointers[r]))
+ return NULL;
+ for (;;)
+ {
+ r = 0xdf & (unsigned char)*++cmd;
+ if (mtree->final && *mtree->final == r)
+ return mtree->msg;
+ if ((r -= 'A') > 25 || !(mtree = mtree->pointers[r]))
+ return NULL;
+ }
+}
+
+/*
+ * This one is identical to the one above, but it is slower because it
+ * makes sure that `cmd' matches the _full_ command, exactly.
+ * This is to avoid confusion with commands like /quake on clients
+ * that send unknown commands directly to the server.
+ */
+static struct Message *msg_tree_parse_client(register char *cmd,
+ aMessageTree *root)
+{
+ register aMessageTree *mtree;
+ register unsigned char q = (0xdf & (unsigned char)*cmd) - 'A';
+ if (q > 25 || !(mtree = root->pointers[q]))
+ return NULL;
+ for (;;)
+ {
+ q = 0xdf & (unsigned char)*++cmd;
+ if (mtree->final && !strCasediff(mtree->final, cmd))
+ return mtree->msg;
+ if ((q -= 'A') > 25 || !(mtree = mtree->pointers[q]))
+ return NULL;
+ }
+}
+
+/*
+ * parse a buffer.
+ *
+ * NOTE: parse_*() should not be called recusively by any other fucntions!
+ */
+int parse_client(aClient *cptr, char *buffer, char *bufend)
+{
+ Reg1 aClient *from = cptr;
+ Reg2 char *ch, *s;
+ Reg3 int i, paramcount, noprefix = 0;
+ aMessage *mptr;
+
+ Debug((DEBUG_DEBUG, "Parsing: %s", buffer));
+ StoreBuffer((buffer, cptr)); /* Store the buffer now, before
+ we start working on it */
+
+ if (IsDead(cptr))
+ return 0;
+
+ para[0] = from->name;
+ for (ch = buffer; *ch == ' '; ch++); /* Eat leading spaces */
+ if (*ch == ':') /* Is any client doing this ? */
+ {
+ for (++ch; *ch && *ch != ' '; ++ch); /* Ignore sender prefix from client */
+ while (*ch == ' ')
+ ch++; /* Advance to command */
+ }
+ else
+ noprefix = 1;
+ if (*ch == '\0')
+ {
+ ircstp->is_empt++;
+ Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
+ cptr->name, from->name));
+ return (-1);
+ }
+
+ if ((s = strchr(ch, ' ')))
+ *s++ = '\0';
+
+ /*
+ * This is a client/unregistered entity.
+ * Check long command list only.
+ */
+ if (!(mptr = msg_tree_parse_client(ch, &msg_tree_cmd)))
+ {
+ /*
+ * Note: Give error message *only* to recognized
+ * persons. It's a nightmare situation to have
+ * two programs sending "Unknown command"'s or
+ * equivalent to each other at full blast....
+ * If it has got to person state, it at least
+ * seems to be well behaving. Perhaps this message
+ * should never be generated, though... --msa
+ * Hm, when is the buffer empty -- if a command
+ * code has been found ?? -Armin
+ */
+ if (buffer[0] != '\0')
+ {
+ if (IsUser(from))
+ sendto_one(from, ":%s %d %s %s :Unknown command",
+ me.name, ERR_UNKNOWNCOMMAND, from->name, ch);
+ Debug((DEBUG_ERROR, "Unknown (%s) from %s",
+ ch, get_client_name(cptr, TRUE)));
+ }
+ ircstp->is_unco++;
+ return (-1);
+ }
+ LogMessage((cptr, mptr->msgclass));
+
+ paramcount = mptr->parameters;
+ i = bufend - ((s) ? s : ch);
+ mptr->bytes += i;
+ if ((mptr->flags & MFLG_SLOW))
+ cptr->since += (2 + i / 120);
+ /*
+ * Allow only 1 msg per 2 seconds
+ * (on average) to prevent dumping.
+ * to keep the response rate up,
+ * bursts of up to 5 msgs are allowed
+ * -SRB
+ */
+
+ /*
+ * Must the following loop really be so devious? On
+ * surface it splits the message to parameters from
+ * blank spaces. But, if paramcount has been reached,
+ * the rest of the message goes into this last parameter
+ * (about same effect as ":" has...) --msa
+ */
+
+ /* Note initially true: s==NULL || *(s-1) == '\0' !! */
+
+ i = 0;
+ if (s)
+ {
+ if (paramcount > MAXPARA)
+ paramcount = MAXPARA;
+ for (;;)
+ {
+ /*
+ * Never "FRANCE " again!! ;-) Clean
+ * out *all* blanks.. --msa
+ */
+ while (*s == ' ')
+ *s++ = '\0';
+
+ if (*s == '\0')
+ break;
+ if (*s == ':')
+ {
+ /*
+ * The rest is single parameter--can
+ * include blanks also.
+ */
+ para[++i] = s + 1;
+ break;
+ }
+ para[++i] = s;
+ if (i >= paramcount)
+ break;
+ for (; *s != ' ' && *s; s++);
+ }
+ }
+ para[++i] = NULL;
+ mptr->count++;
+ /* The "unregistered command check" was ugly and mildly inefficient.
+ * I fixed it. :) --Shadow
+ */
+ if (!IsUser(cptr) && !(mptr->flags & MFLG_UNREG))
+ {
+ sendto_one(from, ":%s %d * %s :Register first.",
+ me.name, ERR_NOTREGISTERED, ch);
+ return -1;
+ }
+ if (IsUser(cptr) &&
+#ifdef IDLE_FROM_MSG
+ mptr->func == m_private)
+#else
+ mptr->func != m_ping && mptr->func != m_pong)
+#endif
+ from->user->last = now;
+
+ return (*mptr->func) (cptr, from, i, para);
+}
+
+int parse_server(aClient *cptr, char *buffer, char *bufend)
+{
+ Reg1 aClient *from = cptr;
+ Reg2 char *ch = buffer, *s;
+ Reg3 int len, i, numeric = 0, paramcount;
+ aMessage *mptr;
+
+ Debug((DEBUG_DEBUG, "Parsing: %s", buffer));
+ StoreBuffer((buffer, cptr)); /* Store the buffer now, before
+ * we start working on it. */
+
+#ifdef GODMODE
+ len = strlen(buffer);
+ sdbflag = 1;
+ if (len > 402)
+ {
+ char c = buffer[200];
+ buffer[200] = 0;
+ sendto_ops("RCV:%-8.8s(%.4d): \"%s...%s\"",
+ cptr->name, len, buffer, &buffer[len - 200]);
+ buffer[200] = c;
+ }
+ else
+ sendto_ops("RCV:%-8.8s(%.4d): \"%s\"", cptr->name, len, buffer);
+ sdbflag = 0;
+#endif /* GODMODE */
+
+ if (IsDead(cptr))
+ return 0;
+
+ para[0] = from->name;
+
+ /*
+ * A server ALWAYS sends a prefix. When it starts with a ':' it's the
+ * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric
+ * nick or server
+ */
+ if (*ch == ':')
+ {
+ /* Let para[0] point to the name of the sender */
+ para[0] = ch + 1;
+ if (!(ch = strchr(ch, ' ')))
+ return -1;
+ *ch++ = '\0';
+
+ /* And let `from' point to its client structure,
+ opps.. a server is _also_ a client --Nem */
+ from = FindClient(para[0]);
+
+ /*
+ * If the client corresponding to the
+ * prefix is not found. We must ignore it,
+ * it is simply a lagged message travelling
+ * upstream a SQUIT that removed the client
+ * --Run
+ */
+ if (!from)
+ {
+ Debug((DEBUG_NOTICE, "Unknown prefix (%s)(%s) from (%s)",
+ para[0], buffer, cptr->name));
+ ircstp->is_unpf++;
+ while (*ch == ' ')
+ ch++;
+ /*
+ * However, the only thing that MUST be
+ * allowed to travel upstream against an
+ * squit, is an SQUIT itself (the timestamp
+ * protects us from being used wrong)
+ */
+ if (ch[1] == 'Q')
+ {
+ para[0] = cptr->name;
+ from = cptr;
+ }
+ else
+ return 0;
+ }
+ else if (from->from != cptr)
+ {
+ ircstp->is_wrdi++;
+ Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
+ buffer, cptr->name));
+ return 0;
+ }
+ }
+ else if (Protocol(cptr) > 9) /* Well, not ALWAYS, 2.9 can send no prefix */
+ {
+ char numeric_prefix[6];
+ int i;
+ for (i = 0; i < 5; ++i)
+ {
+ if ('\0' == ch[i] || ' ' == (numeric_prefix[i] = ch[i]))
+ {
+ break;
+ }
+ }
+ numeric_prefix[i] = '\0';
+ /*
+ * We got a numeric nick as prefix
+ * 1 or 2 character prefixes are from servers
+ * 3 or 5 chars are from clients
+ */
+ if (' ' == ch[1] || ' ' == ch[2])
+ from = FindNServer(numeric_prefix);
+ else
+ from = findNUser(numeric_prefix);
+
+ do
+ {
+ ++ch;
+ }
+ while (*ch != ' ' && *ch);
+
+ /*
+ * If the client corresponding to the
+ * prefix is not found. We must ignore it,
+ * it is simply a lagged message travelling
+ * upstream a SQUIT that removed the client
+ * --Run
+ * There turned out to be other reasons that
+ * a prefix is unknown, needing an upstream
+ * KILL. Also, next to an SQUIT we better
+ * allow a KILL to pass too.
+ * --Run
+ */
+ if (!from)
+ {
+ ircstp->is_unpf++;
+ while (*ch == ' ')
+ ch++;
+ if (*ch == 'N' && (ch[1] == ' ' || ch[1] == 'I'))
+ /* Only sent a KILL for a nick change */
+ {
+ aClient *server;
+ /* Kill the unknown numeric prefix upstream if
+ * it's server still exists: */
+ if ((server = FindNServer(numeric_prefix)) && server->from == cptr)
+ sendto_one(cptr, "%s KILL %s :%s (Unknown numeric nick)",
+ NumServ(&me), numeric_prefix, me.name);
+ }
+ /*
+ * Things that must be allowed to travel
+ * upstream against an squit:
+ */
+ if (ch[1] == 'Q' || (*ch == 'D' && ch[1] == ' ') ||
+ (*ch == 'K' && ch[2] == 'L'))
+ from = cptr;
+ else
+ return 0;
+ }
+
+ /* Let para[0] point to the name of the sender */
+ para[0] = from->name;
+
+ if (from->from != cptr)
+ {
+ ircstp->is_wrdi++;
+ Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
+ buffer, cptr->name));
+ return 0;
+ }
+ }
+
+ while (*ch == ' ')
+ ch++;
+ if (*ch == '\0')
+ {
+ ircstp->is_empt++;
+ Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
+ cptr->name, from->name));
+ return (-1);
+ }
+
+ /*
+ * Extract the command code from the packet. Point s to the end
+ * of the command code and calculate the length using pointer
+ * arithmetic. Note: only need length for numerics and *all*
+ * numerics must have parameters and thus a space after the command
+ * code. -avalon
+ */
+ s = strchr(ch, ' '); /* s -> End of the command code */
+ len = (s) ? (s - ch) : 0;
+ if (len == 3 && isDigit(*ch))
+ {
+ numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0');
+ paramcount = MAXPARA;
+ ircstp->is_num++;
+ mptr = NULL; /* Init. to avoid stupid compiler warning :/ */
+ }
+ else
+ {
+ if (s)
+ *s++ = '\0';
+
+ /* Version Receive Send
+ * 2.9 Long Long
+ * 2.10.0 Tkn/Long Long
+ * 2.10.10 Tkn/Long Tkn
+ * 2.10.20 Tkn Tkn
+ *
+ * Clients/unreg servers always receive/
+ * send long commands -record
+ */
+
+ /*
+ * This is a server. Check the token command list.
+ * -record!jegelhof@cloud9.net
+ */
+ mptr = msg_tree_parse(ch, &msg_tree_tok);
+
+#if 1 /* for 2.10.0/2.10.10 */
+ /*
+ * This code supports 2.9 and 2.10.0 sending long commands.
+ * It makes more calls to strCasediff() than the above
+ * so it will be somewhat slower.
+ */
+ if (!mptr)
+ mptr = msg_tree_parse(ch, &msg_tree_cmd);
+#endif /* 1 */
+
+ if (!mptr)
+ {
+ /*
+ * Note: Give error message *only* to recognized
+ * persons. It's a nightmare situation to have
+ * two programs sending "Unknown command"'s or
+ * equivalent to each other at full blast....
+ * If it has got to person state, it at least
+ * seems to be well behaving. Perhaps this message
+ * should never be generated, though... --msa
+ * Hm, when is the buffer empty -- if a command
+ * code has been found ?? -Armin
+ */
+#ifdef DEBUGMODE
+ if (buffer[0] != '\0')
+ {
+ Debug((DEBUG_ERROR, "Unknown (%s) from %s",
+ ch, get_client_name(cptr, TRUE)));
+ }
+#endif
+ ircstp->is_unco++;
+ return (-1);
+ }
+ LogMessage((cptr, mptr->msgclass));
+
+ paramcount = mptr->parameters;
+ i = bufend - ((s) ? s : ch);
+ mptr->bytes += i;
+ }
+ /*
+ * Must the following loop really be so devious? On
+ * surface it splits the message to parameters from
+ * blank spaces. But, if paramcount has been reached,
+ * the rest of the message goes into this last parameter
+ * (about same effect as ":" has...) --msa
+ */
+
+ /* Note initially true: s==NULL || *(s-1) == '\0' !! */
+
+ i = 0;
+ if (s)
+ {
+ if (paramcount > MAXPARA)
+ paramcount = MAXPARA;
+ for (;;)
+ {
+ /*
+ * Never "FRANCE " again!! ;-) Clean
+ * out *all* blanks.. --msa
+ */
+ while (*s == ' ')
+ *s++ = '\0';
+
+ if (*s == '\0')
+ break;
+ if (*s == ':')
+ {
+ /*
+ * The rest is single parameter--can
+ * include blanks also.
+ */
+ para[++i] = s + 1;
+ break;
+ }
+ para[++i] = s;
+ if (i >= paramcount)
+ break;
+ for (; *s != ' ' && *s; s++);
+ }
+ }
+ para[++i] = NULL;
+ if (numeric)
+ return (do_numeric(numeric, (*buffer != ':'), cptr, from, i, para));
+ mptr->count++;
+
+ return (*mptr->func) (cptr, from, i, para);
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/querycmds.c (formerly ircd/s_serv.c)
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "parse.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "ircd.h"
+#include "s_user.h"
+#include "version.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "match.h"
+#include "s_serv.h"
+#include "msg.h"
+#include "channel.h"
+#include "numnicks.h"
+#include "userload.h"
+#include "s_conf.h"
+#include "support.h"
+#include "querycmds.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * m_functions execute protocol messages on this server:
+ *
+ * cptr is always NON-NULL, pointing to a *LOCAL* client
+ * structure (with an open socket connected!). This
+ * identifies the physical socket where the message
+ * originated (or which caused the m_function to be
+ * executed--some m_functions may call others...).
+ *
+ * sptr is the source of the message, defined by the
+ * prefix part of the message if present. If not
+ * or prefix not found, then sptr==cptr.
+ *
+ * (!IsServer(cptr)) => (cptr == sptr), because
+ * prefixes are taken *only* from servers...
+ *
+ * (IsServer(cptr))
+ * (sptr == cptr) => the message didn't
+ * have the prefix.
+ *
+ * (sptr != cptr && IsServer(sptr) means
+ * the prefix specified servername. (?)
+ *
+ * (sptr != cptr && !IsServer(sptr) means
+ * that message originated from a remote
+ * user (not local).
+ *
+ * combining
+ *
+ * (!IsServer(sptr)) means that, sptr can safely
+ * taken as defining the target structure of the
+ * message in this server.
+ *
+ * *Always* true (if 'parse' and others are working correct):
+ *
+ * 1) sptr->from == cptr (note: cptr->from == cptr)
+ *
+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+ * *cannot* be a local connection, unless it's
+ * actually cptr!). [MyConnect(x) should probably
+ * be defined as (x == x->from) --msa ]
+ *
+ * parc number of variable parameter strings (if zero,
+ * parv is allowed to be NULL)
+ *
+ * parv a NULL terminated list of parameter pointers,
+ *
+ * parv[0], sender (prefix string), if not present
+ * this points to an empty string.
+ * parv[1]...parv[parc-1]
+ * pointers to additional parameters
+ * parv[parc] == NULL, *always*
+ *
+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
+ * non-NULL pointers.
+ */
+
+/*
+ * m_version
+ *
+ * parv[0] = sender prefix
+ * parv[1] = remote server
+ */
+int m_version(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 aClient *acptr;
+
+ if (MyConnect(sptr) && parc > 1)
+ {
+ if (!(acptr = find_match_server(parv[1])))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
+ return 0;
+ }
+ parv[1] = acptr->name;
+ }
+
+ if (hunt_server(0, cptr, sptr, ":%s VERSION :%s", 1, parc, parv) ==
+ HUNTED_ISME)
+ sendto_one(sptr, rpl_str(RPL_VERSION),
+ me.name, parv[0], version, debugmode, me.name, serveropts);
+
+ return 0;
+}
+
+/*
+ * m_info
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ */
+int m_info(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ const char **text = infotext;
+
+ if (hunt_server(1, cptr, sptr, ":%s INFO :%s", 1, parc, parv) == HUNTED_ISME)
+ {
+ while (text[2])
+ {
+ if (!IsOper(sptr))
+ sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], *text);
+ text++;
+ }
+ if (IsOper(sptr))
+ {
+ while (*text)
+ sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], *text++);
+ sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], "");
+ }
+ sendto_one(sptr, ":%s %d %s :Birth Date: %s, compile # %s",
+ me.name, RPL_INFO, parv[0], creation, generation);
+ sendto_one(sptr, ":%s %d %s :On-line since %s",
+ me.name, RPL_INFO, parv[0], myctime(me.firsttime));
+ sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
+ }
+ return 0;
+}
+
+/*
+ * m_links
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername mask
+ *
+ * or
+ *
+ * parv[0] = sender prefix
+ * parv[1] = server to query
+ * parv[2] = servername mask
+ */
+int m_links(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ char *mask;
+ aClient *acptr;
+
+ if (parc > 2)
+ {
+ if (hunt_server(1, cptr, sptr, ":%s LINKS %s :%s", 1, parc, parv) !=
+ HUNTED_ISME)
+ return 0;
+ mask = parv[2];
+ }
+ else
+ mask = parc < 2 ? NULL : parv[1];
+
+ for (acptr = client, collapse(mask); acptr; acptr = acptr->next)
+ {
+ if (!IsServer(acptr) && !IsMe(acptr))
+ continue;
+ if (!BadPtr(mask) && match(mask, acptr->name))
+ continue;
+ sendto_one(sptr, rpl_str(RPL_LINKS),
+ me.name, parv[0], acptr->name, acptr->serv->up->name,
+#ifndef GODMODE
+ acptr->hopcount, acptr->serv->prot,
+#else /* GODMODE */
+ acptr->hopcount, acptr->serv->prot, acptr->serv->timestamp,
+ NumServ(acptr),
+#endif /* GODMODE */
+ (acptr->info[0] ? acptr->info : "(Unknown Location)"));
+ }
+
+ sendto_one(sptr, rpl_str(RPL_ENDOFLINKS), me.name, parv[0],
+ BadPtr(mask) ? "*" : mask);
+ return 0;
+}
+
+/*
+ * m_help
+ *
+ * parv[0] = sender prefix
+ */
+int m_help(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc), char *parv[])
+{
+ int i;
+
+ for (i = 0; msgtab[i].cmd; i++)
+ sendto_one(sptr, ":%s NOTICE %s :%s", me.name, parv[0], msgtab[i].cmd);
+ return 0;
+}
+
+/* Counters of client/servers etc. */
+struct lusers_st nrof;
+
+void init_counters(void)
+{
+ memset(&nrof, 0, sizeof(nrof));
+ nrof.servers = 1;
+}
+
+/*
+ * m_lusers
+ *
+ * parv[0] = sender
+ * parv[1] = ignored
+ * parv[2] = server to query
+ */
+int m_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ if (parc > 2)
+ if (hunt_server(1, cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv) !=
+ HUNTED_ISME)
+ return 0;
+
+ sendto_one(sptr, rpl_str(RPL_LUSERCLIENT), me.name, parv[0],
+ nrof.clients - nrof.inv_clients, nrof.inv_clients, nrof.servers);
+ if (nrof.opers)
+ sendto_one(sptr, rpl_str(RPL_LUSEROP), me.name, parv[0], nrof.opers);
+ if (nrof.unknowns > 0)
+ sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN), me.name, parv[0],
+ nrof.unknowns);
+ if (nrof.channels > 0)
+ sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS), me.name, parv[0],
+ nrof.channels);
+ sendto_one(sptr, rpl_str(RPL_LUSERME), me.name, parv[0], nrof.local_clients,
+ nrof.local_servers);
+
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr,
+ ":%s NOTICE %s :Highest connection count: %d (%d clients)",
+ me.name, parv[0], max_connection_count, max_client_count);
+ else
+ sendto_one(sptr,
+ "%s NOTICE %s%s :Highest connection count: %d (%d clients)",
+ NumServ(&me), NumNick(sptr), max_connection_count, max_client_count);
+
+ return 0;
+}
+
+/*
+ * m_admin
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ */
+int m_admin(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aConfItem *aconf;
+
+ if (MyConnect(sptr) && parc > 1)
+ {
+ aClient *acptr;
+ if (!(acptr = find_match_server(parv[1])))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
+ return 0;
+ }
+ parv[1] = acptr->name;
+ }
+ if (hunt_server(0, cptr, sptr, ":%s ADMIN :%s", 1, parc, parv) != HUNTED_ISME)
+ return 0;
+ if ((aconf = find_admin()))
+ {
+ sendto_one(sptr, rpl_str(RPL_ADMINME), me.name, parv[0], me.name);
+ sendto_one(sptr, rpl_str(RPL_ADMINLOC1), me.name, parv[0], aconf->host);
+ sendto_one(sptr, rpl_str(RPL_ADMINLOC2), me.name, parv[0], aconf->passwd);
+ sendto_one(sptr, rpl_str(RPL_ADMINEMAIL), me.name, parv[0], aconf->name);
+ }
+ else
+ sendto_one(sptr, err_str(ERR_NOADMININFO), me.name, parv[0], me.name);
+ return 0;
+}
+
+/*
+ * m_motd
+ *
+ * parv[0] - sender prefix
+ * parv[1] - servername
+ *
+ * modified 30 mar 1995 by flux (cmlambertus@ucdavis.edu)
+ * T line patch - display motd based on hostmask
+ * modified again 7 sep 97 by Ghostwolf with code and ideas
+ * stolen from comstud & Xorath. All motd files are read into
+ * memory in read_motd() in s_conf.c
+ *
+ * When NODEFAULTMOTD is defined, then it is possible that
+ * sptr == NULL, which means that this function is called from
+ * register_user.
+ */
+int m_motd(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ struct tm *tm = &motd_tm; /* Default: Most general case */
+ atrecord *ptr;
+ int count;
+ register aMotdItem *temp;
+
+#ifdef NODEFAULTMOTD
+ int no_motd;
+
+ if (sptr)
+ {
+ no_motd = 0;
+#endif
+ if (hunt_server(0, cptr, sptr, ":%s MOTD :%s", 1, parc,
+ parv) != HUNTED_ISME)
+ return 0;
+#ifdef NODEFAULTMOTD
+ }
+ else
+ {
+ sptr = cptr;
+ no_motd = 1;
+ }
+#endif
+
+ /*
+ * Find out if this is a remote query or if we have a T line for our hostname
+ */
+ if (IsServer(cptr))
+ {
+ tm = NULL; /* Remote MOTD */
+ temp = rmotd;
+ }
+ else
+ {
+ for (ptr = tdata; ptr; ptr = ptr->next)
+ {
+ if (!match(ptr->hostmask, cptr->sockhost))
+ break;
+ }
+ if (ptr)
+ {
+ temp = ptr->tmotd;
+ tm = &ptr->tmotd_tm;
+ }
+ else
+ temp = motd;
+ }
+ if (temp == NULL)
+ {
+ sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv[0]);
+ return 0;
+ }
+#ifdef NODEFAULTMOTD
+ if (!no_motd)
+ {
+#endif
+ if (tm) /* Not remote? */
+ {
+ sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);
+ sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", me.name, RPL_MOTD,
+ parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
+ tm->tm_hour, tm->tm_min);
+ count = 100;
+ }
+ else
+ count = 3;
+ for (; temp; temp = temp->next)
+ {
+ sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv[0], temp->line);
+ if (--count == 0)
+ break;
+ }
+#ifdef NODEFAULTMOTD
+ }
+ else
+ {
+ sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);
+ sendto_one(sptr, ":%s %d %s :%s", me.name, RPL_MOTD, parv[0],
+ "\ 2Type /MOTD to read the AUP before continuing using this service.\ 2");
+ sendto_one(sptr,
+ ":%s %d %s :The message of the day was last changed: %d/%d/%d", me.name,
+ RPL_MOTD, parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year);
+ }
+#endif
+ sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/random.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include "random.h"
+
+RCSTAG_CC("$Id$");
+
+char localkey[9] = RANDOM_SEED;
+
+/*
+ * MD5 transform algorithm, taken from code written by Colin Plumb,
+ * and put into the public domain
+ *
+ * Kev: Taken from Ted T'so's /dev/random random.c code and modified to
+ * be slightly simpler. That code is released under a BSD-style copyright
+ * OR under the terms of the GNU Public License, which should be included
+ * at the top of this source file.
+ *
+ * record: Cleaned up to work with ircd. RANDOM_TOKEN is defined in
+ * setup.h by the make script; if people start to "guess" your cookies,
+ * consider recompiling your server with a different random token.
+ */
+
+/* The four core functions - F1 is optimized somewhat */
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ *
+ * original comment left in; this used to be called MD5Transform and took
+ * two arguments; I've internalized those arguments, creating the character
+ * array "localkey," which should contain 8 bytes of data. The function also
+ * originally returned nothing; now it returns an unsigned long that is the
+ * random number. It appears to be reallyrandom, so... -Kev
+ *
+ * I don't really know what this does. I tried to figure it out and got
+ * a headache. If you know what's good for you, you'll leave this stuff
+ * for the smart people and do something else. -record
+ */
+unsigned int ircrandom(void)
+{
+ unsigned int a, b, c, d;
+ unsigned char in[16];
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ memcpy((void *)in, (void *)localkey, 8);
+ memcpy((void *)(in + 8), (void *)&tv.tv_sec, 4);
+ memcpy((void *)(in + 12), (void *)&tv.tv_usec, 4);
+
+ a = 0x67452301;
+ b = 0xefcdab89;
+ c = 0x98badcfe;
+ d = 0x10325476;
+
+ MD5STEP(F1, a, b, c, d, (int)in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, (int)in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, (int)in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, (int)in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, (int)in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, (int)in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, (int)in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, (int)in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, (int)in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, (int)in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, (int)in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, (int)in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, (int)in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, (int)in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, (int)in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, (int)in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, (int)in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, (int)in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, (int)in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, (int)in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, (int)in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, (int)in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, (int)in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, (int)in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, (int)in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, (int)in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, (int)in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, (int)in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, (int)in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, (int)in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, (int)in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, (int)in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, (int)in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, (int)in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, (int)in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, (int)in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, (int)in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, (int)in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, (int)in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, (int)in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, (int)in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, (int)in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, (int)in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, (int)in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, (int)in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, (int)in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, (int)in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, (int)in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, (int)in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, (int)in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, (int)in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, (int)in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, (int)in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, (int)in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, (int)in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, (int)in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, (int)in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, (int)in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, (int)in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, (int)in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, (int)in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, (int)in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, (int)in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, (int)in[9] + 0xeb86d391, 21);
+
+ /*
+ * We have 4 unsigned longs generated by the above sequence; this scrambles
+ * them together so that if there is any pattern, it will be obscured.
+ */
+ return (a ^ b ^ c ^ d);
+}
--- /dev/null
+/*
+ * ircd/res.c (C)opyright 1992, 1993, 1994 Darren Reed. All rights reserved.
+ * This file may not be distributed without the author's prior permission in
+ * any shape or form. The author takes no responsibility for any damage or
+ * loss of property which results from the use of this software. Distribution
+ * of this file must include this notice.
+ */
+
+#include "sys.h"
+#include <signal.h>
+#include <sys/socket.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+/* dn_skipname is really an internal function,
+ we shouldn't be using it in res.c */
+#if !defined(dn_skipname) && !defined(__dn_skipname)
+extern int dn_skipname(const unsigned char *, const unsigned char *);
+#endif
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "s_ping.h"
+#include "support.h"
+#include "common.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+#define MAXPACKET 1024
+
+#define RES_MAXADDRS 35
+#define RES_MAXALIASES 35
+
+#define ALIASBLEN ((RES_MAXALIASES + 1) * sizeof(char *))
+#define ADDRSBLEN ((RES_MAXADDRS + 1) * sizeof(struct in_addr *))
+#define ADDRSDLEN (RES_MAXADDRS * sizeof(struct in_addr))
+#define ALIASDLEN (MAXPACKET)
+#define MAXGETHOSTLEN (ALIASBLEN + ADDRSBLEN + ADDRSDLEN + ALIASDLEN)
+
+#define AR_TTL 600 /* TTL in seconds for dns cache entries */
+
+#define ARES_CACSIZE 512
+#define MAXCACHED 2048
+
+#ifndef INT16SZ
+#define INT16SZ 2
+#endif
+#ifndef INT32SZ
+#define INT32SZ 4
+#endif
+
+/*
+ * Building the Hostent
+ * The Hostent struct is arranged like this:
+ * +-------------------------------+
+ * Hostent: | struct hostent h |
+ * |-------------------------------|
+ * | char *buf |
+ * +-------------------------------+
+ *
+ * allocated:
+ *
+ * +-------------------------------+
+ * buf: | h_aliases pointer array | Max size: ALIASBLEN;
+ * | NULL | contains `char *'s
+ * |-------------------------------|
+ * | h_addr_list pointer array | Max size: ADDRSBLEN;
+ * | NULL | contains `struct in_addr *'s
+ * |-------------------------------|
+ * | h_addr_list addresses | Max size: ADDRSDLEN;
+ * | | contains `struct in_addr's
+ * |-------------------------------|
+ * | storage for hostname strings | Max size: ALIASDLEN;
+ * +-------------------------------+ contains `char's
+ *
+ * For requests the size of the h_aliases, and h_addr_list pointer
+ * array sizes are set to MAXALISES and MAXADDRS respectively, and
+ * buf is a fixed size with enough space to hold the largest expected
+ * reply from a nameserver, see RFC 1034 and RFC 1035.
+ * For cached entries the sizes are dependent on the actual number
+ * of aliases and addresses. If new aliases and addresses are found
+ * for cached entries, the buffer is grown and the new entries are added.
+ * The hostent struct is filled in with the addresses of the entries in
+ * the Hostent buf as follows:
+ * h_name - contains a pointer to the start of the hostname string area,
+ * or NULL if none is set. The h_name is followed by the
+ * aliases, in the storage for hostname strings area.
+ * h_aliases - contains a pointer to the start of h_aliases pointer array.
+ * This array contains pointers to the storage for hostname
+ * strings area and is terminated with a NULL. The first alias
+ * is stored directly after the h_name.
+ * h_addr_list - contains a pointer to the start of h_addr_list pointer array.
+ * This array contains pointers to in_addr structures in the
+ * h_addr_list addresses area and is terminated with a NULL.
+ *
+ * Filling the buffer this way allows for proper alignment of the h_addr_list
+ * addresses.
+ *
+ * This arrangement allows us to alias a Hostent struct pointer as a
+ * real struct hostent* without lying. It also allows us to change the
+ * values contained in the cached entries and requests without changing
+ * the actual hostent pointer, which is saved in a client struct and can't
+ * be changed without blowing things up or a lot more fiddling around.
+ * It also allows for defered allocation of the fixed size buffers until
+ * they are really needed.
+ * Nov. 17, 1997 --Bleep
+ */
+
+typedef struct Hostent {
+ struct hostent h;
+ char *buf;
+} aHostent;
+
+typedef struct reslist {
+ int id;
+ int sent; /* number of requests sent */
+ int srch;
+ time_t ttl;
+ char type;
+ char retries; /* retry counter */
+ char sends; /* number of sends (>1 means resent) */
+ char resend; /* send flag. 0 == dont resend */
+ time_t sentat;
+ time_t timeout;
+ struct in_addr addr;
+ char *name;
+ struct reslist *next;
+ Link cinfo;
+ aHostent he;
+} ResRQ;
+
+typedef struct cache {
+ time_t expireat;
+ time_t ttl;
+ aHostent he;
+ struct cache *hname_next, *hnum_next, *list_next;
+} aCache;
+
+typedef struct cachetable {
+ aCache *num_list;
+ aCache *name_list;
+} CacheTable;
+
+extern int resfd; /* defined in s_bsd.c */
+
+static char hostbuf[HOSTLEN + 1];
+static char dot[] = ".";
+static int incache = 0;
+static CacheTable hashtable[ARES_CACSIZE];
+static aCache *cachetop = NULL;
+static ResRQ *last, *first;
+
+static void rem_cache(aCache *);
+static void rem_request(ResRQ *);
+static int do_query_name(Link *, char *, ResRQ *);
+static int do_query_number(Link *, struct in_addr *, ResRQ *);
+static void resend_query(ResRQ *);
+static int proc_answer(ResRQ *, HEADER *, unsigned char *, unsigned char *);
+static int query_name(char *, int, int, ResRQ *);
+static aCache *make_cache(ResRQ *);
+static aCache *find_cache_name(char *);
+static aCache *find_cache_number(ResRQ *, struct in_addr *);
+static int add_request(ResRQ *);
+static ResRQ *make_request(Link *);
+static int send_res_msg(char *, int, int);
+static ResRQ *find_id(int);
+static int hash_number(unsigned char *);
+static void update_list(ResRQ *, aCache *);
+static int hash_name(const char *);
+
+static struct cacheinfo {
+ int ca_adds;
+ int ca_dels;
+ int ca_expires;
+ int ca_lookups;
+ int ca_na_hits;
+ int ca_nu_hits;
+ int ca_updates;
+} cainfo;
+
+static struct resinfo {
+ int re_errors;
+ int re_nu_look;
+ int re_na_look;
+ int re_replies;
+ int re_requests;
+ int re_resends;
+ int re_sent;
+ int re_timeouts;
+ int re_shortttl;
+ int re_unkrep;
+} reinfo;
+
+int init_resolver(void)
+{
+ int on = 1;
+ int fd = -1;
+
+ memset(&reinfo, 0, sizeof(reinfo));
+ memset(&cainfo, 0, sizeof(cainfo));
+ memset(hashtable, 0, sizeof(hashtable));
+
+ first = last = NULL;
+
+ /* res_init() always returns 0 */
+ (void)res_init();
+
+ if (!_res.nscount)
+ {
+ _res.nscount = 1;
+ _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("127.0.0.1");
+ }
+#ifdef DEBUGMODE
+ _res.options |= RES_DEBUG;
+#endif
+
+ alarm(2);
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ alarm(0);
+ if (fd < 0)
+ {
+ if (errno == EMFILE || errno == ENOBUFS)
+ {
+ /*
+ * Only try this one more time, if we can't create the resolver
+ * socket at initialization time, it's pointless to continue.
+ */
+ alarm(2);
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ alarm(0);
+ Debug((DEBUG_ERROR, "init_resolver: socket: No more sockets"));
+ return -1;
+ }
+ alarm(0);
+ }
+ else
+ {
+ Debug((DEBUG_ERROR, "init_resolver: socket: %s", strerror(errno)));
+ return -1;
+ }
+ }
+ setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (OPT_TYPE *)&on, sizeof(on));
+ return fd;
+}
+
+static int add_request(ResRQ *new_request)
+{
+ if (!new_request)
+ return -1;
+ if (!first)
+ first = last = new_request;
+ else
+ {
+ last->next = new_request;
+ last = new_request;
+ }
+ new_request->next = NULL;
+ reinfo.re_requests++;
+ return 0;
+}
+
+/*
+ * Remove a request from the list. This must also free any memory that has
+ * been allocated for temporary storage of DNS results.
+ */
+static void rem_request(ResRQ *old_request)
+{
+ ResRQ **rptr;
+ ResRQ *r2ptr = NULL;
+
+ if (old_request)
+ {
+ for (rptr = &first; *rptr; r2ptr = *rptr, rptr = &(*rptr)->next)
+ {
+ if (*rptr == old_request)
+ {
+ *rptr = old_request->next;
+ if (last == old_request)
+ last = r2ptr;
+ break;
+ }
+ }
+ Debug((DEBUG_DNS, "rem_request:Remove %p at %p %p",
+ old_request, *rptr, r2ptr));
+
+ if (old_request->he.buf)
+ RunFree(old_request->he.buf);
+ if (old_request->name)
+ RunFree(old_request->name);
+ RunFree(old_request);
+ }
+}
+
+/*
+ * Create a DNS request record for the server.
+ */
+static ResRQ *make_request(Link *lp)
+{
+ ResRQ *nreq;
+
+ if ((nreq = (ResRQ *)RunMalloc(sizeof(ResRQ))) == NULL)
+ return NULL;
+ memset(nreq, 0, sizeof(ResRQ));
+ nreq->sentat = now;
+ nreq->retries = 3;
+ nreq->resend = 1;
+ nreq->srch = -1;
+ if (lp)
+ memcpy(&nreq->cinfo, lp, sizeof(Link));
+ else
+ memset(&nreq->cinfo, 0, sizeof(Link));
+ nreq->timeout = 4; /* start at 4 and exponential inc. */
+ nreq->addr.s_addr = INADDR_NONE;
+
+ nreq->he.h.h_addrtype = AF_INET;
+ nreq->he.h.h_length = sizeof(struct in_addr);
+ add_request(nreq);
+ return nreq;
+}
+
+/*
+ * Remove queries from the list which have been there too long without
+ * being resolved.
+ */
+time_t timeout_query_list(void)
+{
+ ResRQ *rptr;
+ ResRQ *r2ptr;
+ time_t next = 0;
+ time_t tout = 0;
+ aClient *cptr;
+
+ Debug((DEBUG_DNS, "timeout_query_list at %s", myctime(now)));
+ for (rptr = first; rptr; rptr = r2ptr)
+ {
+ r2ptr = rptr->next;
+ tout = rptr->sentat + rptr->timeout;
+ if (now >= tout)
+ {
+ if (--rptr->retries <= 0)
+ {
+ Debug((DEBUG_DNS, "timeout %p now " TIME_T_FMT " cptr %p",
+ rptr, now, rptr->cinfo.value.cptr));
+ reinfo.re_timeouts++;
+ cptr = rptr->cinfo.value.cptr;
+ switch (rptr->cinfo.flags)
+ {
+ case ASYNC_CLIENT:
+ ClearDNS(cptr);
+ if (!DoingAuth(cptr))
+ SetAccess(cptr);
+ break;
+ case ASYNC_PING:
+ sendto_ops("Host %s unknown", rptr->name);
+ end_ping(cptr);
+ break;
+ case ASYNC_CONNECT:
+ sendto_ops("Host %s unknown", rptr->name);
+ break;
+ }
+ rem_request(rptr);
+ rptr = NULL;
+ continue;
+ }
+ else
+ {
+ rptr->sentat = now;
+ rptr->timeout += rptr->timeout;
+ resend_query(rptr);
+ tout = now + rptr->timeout;
+ Debug((DEBUG_DNS, "r %p now " TIME_T_FMT " retry %d c %p",
+ rptr, now, rptr->retries, rptr->cinfo.value.cptr));
+ }
+ }
+ if (!next || tout < next)
+ next = tout;
+ }
+ Debug((DEBUG_DNS, "Next timeout_query_list() at %s, %ld",
+ myctime((next > now) ? next : (now + AR_TTL)),
+ (next > now) ? (next - now) : AR_TTL));
+ return (next > now) ? next : (now + AR_TTL);
+}
+
+/*
+ * del_queries
+ *
+ * Called by the server to cleanup outstanding queries for
+ * which there no longer exist clients or conf lines.
+ */
+void del_queries(char *cp)
+{
+ ResRQ *rptr, *r2ptr;
+
+ for (rptr = first; rptr; rptr = r2ptr)
+ {
+ r2ptr = rptr->next;
+ if (cp == rptr->cinfo.value.cp)
+ rem_request(rptr);
+ }
+}
+
+/*
+ * send_res_msg
+ *
+ * sends msg to all nameservers found in the "_res" structure.
+ * This should reflect /etc/resolv.conf. We will get responses
+ * which arent needed but is easier than checking to see if nameserver
+ * isnt present. Returns number of messages successfully sent to
+ * nameservers or -1 if no successful sends.
+ */
+static int send_res_msg(char *msg, int len, int rcount)
+{
+ int i;
+ int sent = 0, max;
+
+ if (!msg)
+ return -1;
+
+ max = MIN(_res.nscount, rcount);
+ if (_res.options & RES_PRIMARY)
+ max = 1;
+ if (!max)
+ max = 1;
+
+ for (i = 0; i < max; ++i)
+ {
+ _res.nsaddr_list[i].sin_family = AF_INET;
+ if (sendto(resfd, msg, len, 0, (struct sockaddr *)&(_res.nsaddr_list[i]),
+ sizeof(struct sockaddr)) == len)
+ {
+ reinfo.re_sent++;
+ sent++;
+ }
+ else
+ Debug((DEBUG_ERROR, "s_r_m:sendto: %s on %d", strerror(errno), resfd));
+ }
+ return (sent) ? sent : -1;
+}
+
+/*
+ * find a dns request id (id is determined by dn_mkquery)
+ */
+static ResRQ *find_id(int id)
+{
+ ResRQ *rptr;
+
+ for (rptr = first; rptr; rptr = rptr->next)
+ if (rptr->id == id)
+ return rptr;
+ return NULL;
+}
+
+/*
+ * add_local_domain
+ *
+ * Add the domain to hostname, if it is missing
+ * (as suggested by eps@TOASTER.SFSU.EDU)
+ */
+void add_local_domain(char *hname, int size)
+{
+ /* try to fix up unqualified names */
+ if (!strchr(hname, '.'))
+ {
+ if (_res.defdname[0] && size > 0)
+ {
+ strcat(hname, ".");
+ strncat(hname, _res.defdname, size - 1);
+ }
+ }
+}
+
+struct hostent *gethost_byname(char *name, Link *lp)
+{
+ aCache *cp;
+
+ reinfo.re_na_look++;
+ if ((cp = find_cache_name(name)))
+ return &cp->he.h;
+ if (lp)
+ do_query_name(lp, name, NULL);
+ return NULL;
+}
+
+struct hostent *gethost_byaddr(struct in_addr *addr, Link *lp)
+{
+ aCache *cp;
+
+ reinfo.re_nu_look++;
+ if ((cp = find_cache_number(NULL, addr)))
+ return &cp->he.h;
+ if (!lp)
+ return NULL;
+ do_query_number(lp, addr, NULL);
+ return NULL;
+}
+
+static int do_query_name(Link *lp, char *name, ResRQ *rptr)
+{
+ char hname[HOSTLEN + 1];
+ int len;
+
+ strncpy(hname, name, sizeof(hname) - 1);
+ hname[sizeof(hname) - 1] = 0;
+ len = strlen(hname);
+
+ if (rptr && !strchr(hname, '.') && _res.options & RES_DEFNAMES)
+ {
+ strncat(hname, dot, sizeof(hname) - len - 1);
+ len++;
+ strncat(hname, _res.defdname, sizeof(hname) - len - 1);
+ }
+
+ /*
+ * Store the name passed as the one to lookup and generate other host
+ * names to pass onto the nameserver(s) for lookups.
+ */
+ if (!rptr)
+ {
+ if ((rptr = make_request(lp)) == NULL)
+ return -1;
+ rptr->type = T_A;
+ rptr->name = (char *)RunMalloc(strlen(name) + 1);
+ strcpy(rptr->name, name);
+ }
+ return (query_name(hname, C_IN, T_A, rptr));
+}
+
+/*
+ * Use this to do reverse IP# lookups.
+ */
+static int do_query_number(Link *lp, struct in_addr *numb, ResRQ *rptr)
+{
+ char ipbuf[32];
+ Reg2 unsigned char *cp = (unsigned char *)&numb->s_addr;
+
+ sprintf_irc(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
+ (unsigned int)(cp[3]), (unsigned int)(cp[2]),
+ (unsigned int)(cp[1]), (unsigned int)(cp[0]));
+
+ if (!rptr)
+ {
+ if ((rptr = make_request(lp)) == NULL)
+ return -1;
+ rptr->type = T_PTR;
+ rptr->addr.s_addr = numb->s_addr;
+ }
+ return (query_name(ipbuf, C_IN, T_PTR, rptr));
+}
+
+/*
+ * generate a query based on class, type and name.
+ */
+static int query_name(char *name, int q_class, int type, ResRQ *rptr)
+{
+ struct timeval tv;
+ char buf[MAXPACKET];
+ int r, s, k = 0;
+ HEADER *hptr;
+
+ Debug((DEBUG_DNS, "query_name: na %s cl %d ty %d", name, q_class, type));
+ memset(buf, 0, sizeof(buf));
+ r = res_mkquery(QUERY, name, q_class, type, NULL, 0, NULL,
+ (unsigned char *)buf, sizeof(buf));
+ if (r <= 0)
+ {
+ h_errno = NO_RECOVERY;
+ return r;
+ }
+ hptr = (HEADER *) buf;
+ gettimeofday(&tv, NULL);
+ do
+ {
+ /* htons/ntohs can be assembler macros, which cannot
+ be nested. Thus two lines. -Vesa */
+ unsigned short int nstmp = ntohs(hptr->id) + k
+ + (unsigned short int)(tv.tv_usec & 0xffff);
+ hptr->id = htons(nstmp);
+ k++;
+ }
+ while (find_id(ntohs(hptr->id)));
+ rptr->id = ntohs(hptr->id);
+ rptr->sends++;
+ s = send_res_msg(buf, r, rptr->sends);
+ if (s == -1)
+ {
+ h_errno = TRY_AGAIN;
+ return -1;
+ }
+ else
+ rptr->sent += s;
+ return 0;
+}
+
+static void resend_query(ResRQ *rptr)
+{
+ if (rptr->resend == 0)
+ return;
+ reinfo.re_resends++;
+ switch (rptr->type)
+ {
+ case T_PTR:
+ do_query_number(NULL, &rptr->addr, rptr);
+ break;
+ case T_A:
+ do_query_name(NULL, rptr->name, rptr);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/*
+ * proc_answer
+ *
+ * Process name server reply.
+ */
+static int proc_answer(ResRQ *rptr, HEADER * hptr, unsigned char *buf,
+ unsigned char *eob)
+{
+ unsigned char *cp = buf + sizeof(HEADER);
+ char **alias;
+ char **addr;
+ char *p; /* pointer to strings */
+ char *a; /* pointer to address list */
+ char *endp; /* end of our buffer */
+ struct hostent *hp = &rptr->he.h;
+ int addr_class, type, dlen, ans = 0, n;
+ int addr_count = 0;
+ int alias_count = 0;
+
+ /*
+ * Lazy allocation of rptr->he.buf, we don't allocate a buffer
+ * unless there's something to put in it.
+ */
+ if (!rptr->he.buf)
+ {
+ if ((rptr->he.buf = (char *)RunMalloc(MAXGETHOSTLEN)) == NULL)
+ return 0;
+ /*
+ * Array of alias list pointers starts at beginning of buf
+ */
+ rptr->he.h.h_aliases = (char **)rptr->he.buf;
+ rptr->he.h.h_aliases[0] = NULL;
+ /*
+ * Array of address list pointers starts after alias list pointers.
+ * The actual addresses follow the address list pointers.
+ */
+ rptr->he.h.h_addr_list = (char **)(rptr->he.buf + ALIASBLEN);
+ a = (char *)rptr->he.h.h_addr_list + ADDRSBLEN;
+ /*
+ * don't copy the host address to the beginning of h_addr_list
+ * make it just a little bit harder for the script kiddies
+ */
+ rptr->he.h.h_addr_list[0] = NULL;
+ }
+ endp = &rptr->he.buf[MAXGETHOSTLEN];
+
+ /* find the end of the address list */
+ addr = hp->h_addr_list;
+ while (*addr)
+ {
+ ++addr;
+ ++addr_count;
+ }
+ /* make 'a' point to the first available empty address slot */
+ a = (char *)hp->h_addr_list + ADDRSBLEN +
+ (addr_count * sizeof(struct in_addr));
+
+ /* find the end of the alias list */
+ alias = hp->h_aliases;
+ while (*alias)
+ {
+ ++alias;
+ ++alias_count;
+ }
+ /* make p point to the first available space in rptr->buf */
+ if (alias_count > 0)
+ {
+ p = (char *)hp->h_aliases[alias_count - 1];
+ p += (strlen(p) + 1);
+ }
+ else if (hp->h_name)
+ p = (char *)(hp->h_name + strlen(hp->h_name) + 1);
+ else
+ p = (char *)rptr->he.h.h_addr_list + ADDRSBLEN + ADDRSDLEN;
+
+ /*
+ * Skip past query's
+ */
+#ifdef SOL2 /* brain damaged compiler (Solaris2) it seems */
+ for (; hptr->qdcount > 0; hptr->qdcount--)
+#else
+ while (hptr->qdcount-- > 0)
+#endif
+ {
+ if ((n = dn_skipname(cp, eob)) == -1)
+ break;
+ else
+ cp += (n + QFIXEDSZ);
+ }
+ /*
+ * Proccess each answer sent to us blech.
+ */
+ while (hptr->ancount-- > 0 && cp && cp < eob && p < endp)
+ {
+ if ((n = dn_expand(buf, eob, cp, hostbuf, sizeof(hostbuf))) <= 0)
+ {
+ Debug((DEBUG_DNS, "dn_expand failed"));
+ break;
+ }
+
+ cp += n;
+ /* XXX magic numbers, this checks for truncated packets */
+ if ((cp + INT16SZ + INT16SZ + INT32SZ + INT16SZ) >= eob)
+ break;
+
+ /*
+ * I have no idea why - maybe a bug in the linker? But _getshort
+ * and _getlong don't work anymore. So lets do it ourselfs:
+ * --Run
+ */
+
+ type = ((u_int16_t) cp[0] << 8) | ((u_int16_t) cp[1]);
+ cp += INT16SZ;
+ addr_class = ((u_int16_t) cp[0] << 8) | ((u_int16_t) cp[1]);
+ cp += INT16SZ;
+ rptr->ttl =
+ ((u_int32_t) cp[0] << 24) | ((u_int32_t) cp[1] << 16) | ((u_int32_t)
+ cp[2] << 8) | ((u_int32_t) cp[3]);
+ cp += INT32SZ;
+ dlen = ((u_int16_t) cp[0] << 8) | ((u_int16_t) cp[1]);
+ cp += INT16SZ;
+
+ rptr->type = type;
+
+ /* check for bad dlen */
+ if ((cp + dlen) > eob)
+ break;
+
+ /*
+ * Add default domain name to returned host name if host name
+ * doesn't contain any dot separators.
+ * Name server never returns with trailing '.'
+ */
+ if (!strchr(hostbuf, '.') && (_res.options & RES_DEFNAMES))
+ {
+ strcat(hostbuf, dot);
+ strncat(hostbuf, _res.defdname, HOSTLEN - strlen(hostbuf));
+ hostbuf[HOSTLEN] = 0;
+ }
+
+ switch (type)
+ {
+ case T_A:
+ /* check for invalid dlen or too many addresses */
+ if (dlen != sizeof(struct in_addr) || ++addr_count >= RES_MAXADDRS)
+ break;
+ if (ans == 1)
+ hp->h_addrtype = (addr_class == C_IN) ? AF_INET : AF_UNSPEC;
+
+ memcpy(a, cp, sizeof(struct in_addr));
+ *addr++ = a;
+ *addr = 0;
+ a += sizeof(struct in_addr);
+
+ if (!hp->h_name)
+ {
+ strncpy(p, hostbuf, endp - p);
+ hp->h_name = p;
+ p += (strlen(p) + 1);
+ }
+ cp += dlen;
+ Debug((DEBUG_DNS, "got ip # %s for %s",
+ inetntoa(*((struct in_addr *)hp->h_addr_list[addr_count - 1])),
+ hostbuf));
+ ans++;
+ break;
+ case T_PTR:
+ if ((n = dn_expand(buf, eob, cp, hostbuf, sizeof(hostbuf))) < 0)
+ {
+ cp = NULL;
+ break;
+ }
+ cp += n;
+ Debug((DEBUG_DNS, "got host %s", hostbuf));
+ /*
+ * Copy the returned hostname into the host name or alias field if
+ * there is a known hostname already.
+ */
+ if (hp->h_name)
+ {
+ if (++alias_count >= RES_MAXALIASES)
+ break;
+ strncpy(p, hostbuf, endp - p);
+ endp[-1] = 0;
+ *alias++ = p;
+ *alias = NULL;
+ }
+ else
+ {
+ strncpy(p, hostbuf, endp - p);
+ hp->h_name = p;
+ }
+ p += (strlen(p) + 1);
+ ans++;
+ break;
+ case T_CNAME:
+ cp += dlen;
+ Debug((DEBUG_DNS, "got cname %s", hostbuf));
+ if (++alias_count >= RES_MAXALIASES)
+ break;
+ strncpy(p, hostbuf, endp - p);
+ endp[-1] = 0;
+ *alias++ = p;
+ *alias = NULL;
+ p += (strlen(p) + 1);
+ ans++;
+ break;
+ default:
+ Debug((DEBUG_DNS, "proc_answer: type:%d for:%s", type, hostbuf));
+ break;
+ }
+ }
+ return ans;
+}
+
+/*
+ * Read a dns reply from the nameserver and process it.
+ */
+struct hostent *get_res(char *lp)
+{
+ static unsigned char buf[sizeof(HEADER) + MAXPACKET];
+ Reg1 HEADER *hptr;
+ Reg2 ResRQ *rptr = NULL;
+ aCache *cp = NULL;
+ struct sockaddr_in sin;
+ int a, max;
+ size_t rc, len = sizeof(sin);
+
+ alarm(4);
+ rc = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len);
+ alarm(0);
+
+ if (rc <= sizeof(HEADER))
+ return NULL;
+ /*
+ * Convert DNS reply reader from Network byte order to CPU byte order.
+ */
+ hptr = (HEADER *) buf;
+ hptr->id = ntohs(hptr->id);
+ hptr->ancount = ntohs(hptr->ancount);
+ hptr->qdcount = ntohs(hptr->qdcount);
+ hptr->nscount = ntohs(hptr->nscount);
+ hptr->arcount = ntohs(hptr->arcount);
+#ifdef DEBUG
+ Debug((DEBUG_NOTICE, "get_res:id = %d rcode = %d ancount = %d",
+ hptr->id, hptr->rcode, hptr->ancount));
+#endif
+ reinfo.re_replies++;
+ /*
+ * Response for an id which we have already received an answer for
+ * just ignore this response.
+ */
+ if ((rptr = find_id(hptr->id)) == NULL)
+ {
+ Debug((DEBUG_DNS, "find_id %d failed", hptr->id));
+ return NULL;
+ }
+ /*
+ * Check against possibly fake replies
+ */
+ max = MIN(_res.nscount, rptr->sends);
+ if (!max)
+ max = 1;
+
+ for (a = 0; a < max; a++)
+ {
+ if (!_res.nsaddr_list[a].sin_addr.s_addr ||
+ !memcmp((char *)&sin.sin_addr, (char *)&_res.nsaddr_list[a].sin_addr,
+ sizeof(struct in_addr)))
+ break;
+ }
+ if (a == max)
+ {
+ reinfo.re_unkrep++;
+ Debug((DEBUG_DNS, "got response from unknown ns"));
+ goto getres_err;
+ }
+
+ if ((hptr->rcode != NOERROR) || (hptr->ancount == 0))
+ {
+ switch (hptr->rcode)
+ {
+ case NXDOMAIN:
+ h_errno = TRY_AGAIN;
+ break;
+ case SERVFAIL:
+ h_errno = TRY_AGAIN;
+ break;
+ case NOERROR:
+ h_errno = NO_DATA;
+ break;
+ case FORMERR:
+ case NOTIMP:
+ case REFUSED:
+ default:
+ h_errno = NO_RECOVERY;
+ break;
+ }
+ reinfo.re_errors++;
+ /*
+ * If a bad error was returned, we stop here and dont send
+ * send any more (no retries granted).
+ */
+ if (h_errno != TRY_AGAIN)
+ {
+ Debug((DEBUG_DNS, "Fatal DNS error %d for %d", h_errno, hptr->rcode));
+ rptr->resend = 0;
+ rptr->retries = 0;
+ }
+ goto getres_err;
+ }
+ /*
+ * If this fails we didn't get a buffer to hold the hostent or
+ * there was an error decoding the received packet, try it again
+ * and hope it works the next time.
+ */
+ a = proc_answer(rptr, hptr, buf, &buf[rc]);
+ Debug((DEBUG_DNS, "get_res:Proc answer = %d", a));
+
+ if (a && rptr->type == T_PTR)
+ {
+ struct hostent *hp2 = NULL;
+
+ if (BadPtr(rptr->he.h.h_name)) /* Kludge! 960907/Vesa */
+ goto getres_err;
+
+ Debug((DEBUG_DNS, "relookup %s <-> %s", rptr->he.h.h_name,
+ inetntoa(rptr->addr)));
+ /*
+ * Lookup the 'authoritive' name that we were given for the
+ * ip#. By using this call rather than regenerating the
+ * type we automatically gain the use of the cache with no
+ * extra kludges.
+ */
+ if ((hp2 = gethost_byname((char *)rptr->he.h.h_name, &rptr->cinfo)))
+ {
+ if (lp)
+ memcpy(lp, &rptr->cinfo, sizeof(Link));
+ }
+ /*
+ * If name wasn't found, a request has been queued and it will
+ * be the last one queued. This is rather nasty way to keep
+ * a host alias with the query. -avalon
+ */
+ else if (*rptr->he.h.h_aliases)
+ {
+ if (last->he.buf)
+ RunFree(last->he.buf);
+ last->he.buf = rptr->he.buf;
+ rptr->he.buf = NULL;
+ memcpy(&last->he.h, &rptr->he.h, sizeof(struct hostent));
+ }
+ rem_request(rptr);
+ return hp2;
+ }
+
+ if (a > 0)
+ {
+ if (lp)
+ memcpy(lp, &rptr->cinfo, sizeof(Link));
+ cp = make_cache(rptr);
+ Debug((DEBUG_DNS, "get_res:cp=%p rptr=%p (made)", cp, rptr));
+ rem_request(rptr);
+ }
+ else if (!rptr->sent)
+ rem_request(rptr);
+ return cp ? &cp->he.h : NULL;
+
+getres_err:
+ /*
+ * Reprocess an error if the nameserver didnt tell us to "TRY_AGAIN".
+ */
+ if (rptr)
+ {
+ if (h_errno != TRY_AGAIN)
+ {
+ /*
+ * If we havent tried with the default domain and its
+ * set, then give it a try next.
+ */
+ if (_res.options & RES_DEFNAMES && ++rptr->srch == 0)
+ {
+ rptr->retries = _res.retry;
+ rptr->sends = 0;
+ rptr->resend = 1;
+ resend_query(rptr);
+ }
+ else
+ resend_query(rptr);
+ }
+ else if (lp)
+ memcpy(lp, &rptr->cinfo, sizeof(Link));
+ }
+ return NULL;
+}
+
+/*
+ * Duplicate a hostent struct, allocate only enough memory for
+ * the data we're putting in it.
+ */
+static int dup_hostent(aHostent *new_hp, struct hostent *hp)
+{
+ char *p;
+ char **ap;
+ char **pp;
+ int alias_count = 0;
+ int addr_count = 0;
+ size_t bytes_needed = 0;
+
+ if (!new_hp || !hp)
+ return 0;
+
+ /* how much buffer do we need? */
+ bytes_needed += (strlen(hp->h_name) + 1);
+
+ pp = hp->h_aliases;
+ while (*pp)
+ {
+ bytes_needed += (strlen(*pp++) + 1 + sizeof(void *));
+ ++alias_count;
+ }
+ pp = hp->h_addr_list;
+ while (*pp++)
+ {
+ bytes_needed += (hp->h_length + sizeof(void *));
+ ++addr_count;
+ }
+ /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+ bytes_needed += (2 * sizeof(void *));
+
+ /* Allocate memory */
+ if ((new_hp->buf = (char *)RunMalloc(bytes_needed)) == NULL)
+ return -1;
+
+ new_hp->h.h_addrtype = hp->h_addrtype;
+ new_hp->h.h_length = hp->h_length;
+
+ /* first write the address list */
+ pp = hp->h_addr_list;
+ ap = new_hp->h.h_addr_list =
+ (char **)(new_hp->buf + ((alias_count + 1) * sizeof(void *)));
+ p = (char *)ap + ((addr_count + 1) * sizeof(void *));
+ while (*pp)
+ {
+ *ap++ = p;
+ memcpy(p, *pp++, hp->h_length);
+ p += hp->h_length;
+ }
+ *ap = 0;
+ /* next write the name */
+ new_hp->h.h_name = p;
+ strcpy(p, hp->h_name);
+ p += (strlen(p) + 1);
+
+ /* last write the alias list */
+ pp = hp->h_aliases;
+ ap = new_hp->h.h_aliases = (char **)new_hp->buf;
+ while (*pp)
+ {
+ *ap++ = p;
+ strcpy(p, *pp++);
+ p += (strlen(p) + 1);
+ }
+ *ap = 0;
+
+ return 0;
+}
+
+/*
+ * Add records to a Hostent struct in place.
+ */
+static int update_hostent(aHostent *hp, char **addr, char **alias)
+{
+ char *p;
+ char **ap;
+ char **pp;
+ int alias_count = 0;
+ int addr_count = 0;
+ char *buf = NULL;
+ size_t bytes_needed = 0;
+
+ if (!hp || !hp->buf)
+ return -1;
+
+ /* how much buffer do we need? */
+ bytes_needed = strlen(hp->h.h_name) + 1;
+ pp = hp->h.h_aliases;
+ while (*pp)
+ {
+ bytes_needed += (strlen(*pp++) + 1 + sizeof(void *));
+ ++alias_count;
+ }
+ if (alias)
+ {
+ pp = alias;
+ while (*pp)
+ {
+ bytes_needed += (strlen(*pp++) + 1 + sizeof(void *));
+ ++alias_count;
+ }
+ }
+ pp = hp->h.h_addr_list;
+ while (*pp++)
+ {
+ bytes_needed += (hp->h.h_length + sizeof(void *));
+ ++addr_count;
+ }
+ if (addr)
+ {
+ pp = addr;
+ while (*pp++)
+ {
+ bytes_needed += (hp->h.h_length + sizeof(void *));
+ ++addr_count;
+ }
+ }
+ /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+ bytes_needed += 2 * sizeof(void *);
+
+ /* Allocate memory */
+ if ((buf = (char *)RunMalloc(bytes_needed)) == NULL)
+ return -1;
+
+ /* first write the address list */
+ pp = hp->h.h_addr_list;
+ ap = hp->h.h_addr_list =
+ (char **)(buf + ((alias_count + 1) * sizeof(void *)));
+ p = (char *)ap + ((addr_count + 1) * sizeof(void *));
+ while (*pp)
+ {
+ memcpy(p, *pp++, hp->h.h_length);
+ *ap++ = p;
+ p += hp->h.h_length;
+ }
+ if (addr)
+ {
+ while (*addr)
+ {
+ memcpy(p, *addr++, hp->h.h_length);
+ *ap++ = p;
+ p += hp->h.h_length;
+ }
+ }
+ *ap = 0;
+
+ /* next write the name */
+ strcpy(p, hp->h.h_name);
+ hp->h.h_name = p;
+ p += (strlen(p) + 1);
+
+ /* last write the alias list */
+ pp = hp->h.h_aliases;
+ ap = hp->h.h_aliases = (char **)buf;
+ while (*pp)
+ {
+ strcpy(p, *pp++);
+ *ap++ = p;
+ p += (strlen(p) + 1);
+ }
+ if (alias)
+ {
+ while (*alias)
+ {
+ strcpy(p, *alias++);
+ *ap++ = p;
+ p += (strlen(p) + 1);
+ }
+ }
+ *ap = 0;
+ /* release the old buffer */
+ p = hp->buf;
+ hp->buf = buf;
+ RunFree(p);
+ return 0;
+}
+
+static int hash_number(unsigned char *ip)
+{
+ unsigned int hashv = 0;
+
+ /* could use loop but slower */
+ hashv += (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv %= ARES_CACSIZE;
+ return (hashv);
+}
+
+static int hash_name(const char *name)
+{
+ unsigned int hashv = 0;
+
+ for (; *name && *name != '.'; name++)
+ hashv += *name;
+ hashv %= ARES_CACSIZE;
+ return (hashv);
+}
+
+/*
+ * Add a new cache item to the queue and hash table.
+ */
+static aCache *add_to_cache(aCache *ocp)
+{
+ aCache *cp = NULL;
+ int hashv;
+
+ Debug((DEBUG_DNS,
+ "add_to_cache:ocp %p he %p name %p addrl %p 0 %p",
+ ocp, &ocp->he, ocp->he.h.h_name, ocp->he.h.h_addr_list,
+ ocp->he.h.h_addr_list[0]));
+
+ ocp->list_next = cachetop;
+ cachetop = ocp;
+
+ hashv = hash_name(ocp->he.h.h_name);
+ ocp->hname_next = hashtable[hashv].name_list;
+ hashtable[hashv].name_list = ocp;
+
+ hashv = hash_number((unsigned char *)ocp->he.h.h_addr_list[0]);
+ ocp->hnum_next = hashtable[hashv].num_list;
+ hashtable[hashv].num_list = ocp;
+
+ Debug((DEBUG_DNS, "add_to_cache:added %s[%p] cache %p.",
+ ocp->he.h.h_name, ocp->he.h.h_addr_list[0], ocp));
+ Debug((DEBUG_DNS,
+ "add_to_cache:h1 %d h2 %#x lnext %p namnext %p numnext %p",
+ hash_name(ocp->he.h.h_name), hashv, ocp->list_next,
+ ocp->hname_next, ocp->hnum_next));
+
+ /*
+ * LRU deletion of excessive cache entries.
+ */
+ if (++incache > MAXCACHED)
+ {
+ for (cp = cachetop; cp->list_next; cp = cp->list_next);
+ rem_cache(cp);
+ }
+ cainfo.ca_adds++;
+
+ return ocp;
+}
+
+/*
+ * update_list
+ *
+ * Does not alter the cache structure passed. It is assumed that
+ * it already contains the correct expire time, if it is a new entry. Old
+ * entries have the expirey time updated.
+ */
+static void update_list(ResRQ *rptr, aCache *cp)
+{
+ aCache **cpp;
+ char *s;
+ char **ap;
+ const char *t;
+ int i, j;
+ static char *addrs[RES_MAXADDRS + 1];
+ static char *aliases[RES_MAXALIASES + 1];
+
+ /*
+ * Search for the new cache item in the cache list by hostname.
+ * If found, move the entry to the top of the list and return.
+ */
+ cainfo.ca_updates++;
+
+ for (cpp = &cachetop; *cpp; cpp = &((*cpp)->list_next))
+ {
+ if (cp == *cpp)
+ break;
+ }
+ if (!*cpp)
+ return;
+ *cpp = cp->list_next;
+ cp->list_next = cachetop;
+ cachetop = cp;
+ if (!rptr)
+ return;
+
+ Debug((DEBUG_DNS, "u_l:cp %p na %p al %p ad %p",
+ cp, cp->he.h.h_name, cp->he.h.h_aliases, cp->he.h.h_addr_list[0]));
+ Debug((DEBUG_DNS, "u_l:rptr %p h_n %p", rptr, rptr->he.h.h_name));
+ /*
+ * Compare the cache entry against the new record. Add any
+ * previously missing names for this entry.
+ */
+
+ *aliases = 0;
+ ap = aliases;
+ for (i = 0, s = (char *)rptr->he.h.h_name; s; s = rptr->he.h.h_aliases[i++])
+ {
+ for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++])
+ {
+ if (!strCasediff(t, s))
+ break;
+ }
+ if (!t)
+ {
+ *ap++ = s;
+ *ap = 0;
+ }
+ }
+
+ /*
+ * Do the same again for IP#'s.
+ */
+ *addrs = 0;
+ ap = addrs;
+ for (i = 0; (s = rptr->he.h.h_addr_list[i]); i++)
+ {
+ for (j = 0; (t = cp->he.h.h_addr_list[j]); j++)
+ {
+ if (!memcmp(t, s, sizeof(struct in_addr)))
+ break;
+ }
+ if (!t)
+ {
+ *ap++ = s;
+ *ap = 0;
+ }
+ }
+ if (*addrs || *aliases)
+ update_hostent(&cp->he, addrs, aliases);
+}
+
+static aCache *find_cache_name(char *name)
+{
+ aCache *cp;
+ const char *s;
+ int hashv;
+ int i;
+
+ hashv = hash_name(name);
+
+ cp = hashtable[hashv].name_list;
+ Debug((DEBUG_DNS, "find_cache_name:find %s : hashv = %d", name, hashv));
+
+ for (; cp; cp = cp->hname_next)
+ {
+ for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++])
+ {
+ if (strCasediff(s, name) == 0)
+ {
+ cainfo.ca_na_hits++;
+ update_list(0, cp);
+ return cp;
+ }
+ }
+ }
+
+ for (cp = cachetop; cp; cp = cp->list_next)
+ {
+ /*
+ * If no aliases or the hash value matches, we've already
+ * done this entry and all possiblilities concerning it.
+ */
+ if (!*cp->he.h.h_aliases)
+ continue;
+ if (hashv == hash_name(cp->he.h.h_name))
+ continue;
+ for (i = 0; (s = cp->he.h.h_aliases[i]); ++i)
+ {
+ if (!strCasediff(name, s))
+ {
+ cainfo.ca_na_hits++;
+ update_list(0, cp);
+ return cp;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Find a cache entry by ip# and update its expire time
+ */
+static aCache *find_cache_number(ResRQ *rptr, struct in_addr *numb)
+{
+ Reg1 aCache *cp;
+ Reg2 int hashv, i;
+
+ hashv = hash_number((unsigned char *)numb);
+
+ cp = hashtable[hashv].num_list;
+ Debug((DEBUG_DNS, "find_cache_number:find %s[%08x]: hashv = %d",
+ inetntoa(*numb), ntohl(numb->s_addr), hashv));
+
+ for (; cp; cp = cp->hnum_next)
+ {
+ for (i = 0; cp->he.h.h_addr_list[i]; ++i)
+ {
+ if (!memcmp(cp->he.h.h_addr_list[i], (char *)numb,
+ sizeof(struct in_addr)))
+ {
+ cainfo.ca_nu_hits++;
+ update_list(rptr, cp);
+ return cp;
+ }
+ }
+ }
+
+ for (cp = cachetop; cp; cp = cp->list_next)
+ {
+ /*
+ * Single address entry...would have been done by hashed search above...
+ */
+ if (!cp->he.h.h_addr_list[1])
+ continue;
+ /*
+ * If the first IP# has the same hashnumber as the IP# we
+ * are looking for, its been done already.
+ */
+ if (hashv == hash_number((unsigned char *)cp->he.h.h_addr_list[0]))
+ continue;
+ for (i = 1; cp->he.h.h_addr_list[i]; ++i)
+ {
+ if (!memcmp(cp->he.h.h_addr_list[i], (char *)numb,
+ sizeof(struct in_addr)))
+ {
+ cainfo.ca_nu_hits++;
+ update_list(rptr, cp);
+ return cp;
+ }
+ }
+ }
+ return NULL;
+}
+
+static aCache *make_cache(ResRQ *rptr)
+{
+ aCache *cp;
+ int i;
+ struct hostent *hp = &rptr->he.h;
+
+ /*
+ * Shouldn't happen but it just might...
+ */
+ if (!hp->h_name || !hp->h_addr_list[0])
+ return NULL;
+ /*
+ * Make cache entry. First check to see if the cache already exists
+ * and if so, return a pointer to it.
+ */
+ for (i = 0; hp->h_addr_list[i]; ++i)
+ {
+ if ((cp = find_cache_number(rptr, (struct in_addr *)hp->h_addr_list[i])))
+ return cp;
+ }
+
+ /*
+ * A matching entry wasnt found in the cache so go and make one up.
+ */
+ if ((cp = (aCache *)RunMalloc(sizeof(aCache))) == NULL)
+ return NULL;
+ memset(cp, 0, sizeof(aCache));
+ dup_hostent(&cp->he, hp);
+
+ if (rptr->ttl < 600)
+ {
+ reinfo.re_shortttl++;
+ cp->ttl = 600;
+ }
+ else
+ cp->ttl = rptr->ttl;
+ cp->expireat = now + cp->ttl;
+ Debug((DEBUG_INFO, "make_cache:made cache %p", cp));
+ return add_to_cache(cp);
+}
+
+/*
+ * rem_cache
+ *
+ * Delete a cache entry from the cache structures and lists and return
+ * all memory used for the cache back to the memory pool.
+ */
+static void rem_cache(aCache *ocp)
+{
+ aCache **cp;
+ struct hostent *hp = &ocp->he.h;
+ int hashv;
+ aClient *cptr;
+
+ Debug((DEBUG_DNS, "rem_cache: ocp %p hp %p l_n %p aliases %p",
+ ocp, hp, ocp->list_next, hp->h_aliases));
+
+ /*
+ * Cleanup any references to this structure by destroying the pointer.
+ */
+ for (hashv = highest_fd; hashv >= 0; --hashv)
+ {
+ if ((cptr = loc_clients[hashv]) && (cptr->hostp == hp))
+ cptr->hostp = NULL;
+ }
+ /*
+ * Remove cache entry from linked list.
+ */
+ for (cp = &cachetop; *cp; cp = &((*cp)->list_next))
+ {
+ if (*cp == ocp)
+ {
+ *cp = ocp->list_next;
+ break;
+ }
+ }
+ /*
+ * Remove cache entry from hashed name lists.
+ */
+ hashv = hash_name(hp->h_name);
+
+ Debug((DEBUG_DNS, "rem_cache: h_name %s hashv %d next %p first %p",
+ hp->h_name, hashv, ocp->hname_next, hashtable[hashv].name_list));
+
+ for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next))
+ {
+ if (*cp == ocp)
+ {
+ *cp = ocp->hname_next;
+ break;
+ }
+ }
+ /*
+ * Remove cache entry from hashed number list
+ */
+ hashv = hash_number((unsigned char *)hp->h_addr_list[0]);
+
+ Debug((DEBUG_DNS, "rem_cache: h_addr %s hashv %d next %p first %p",
+ inetntoa(*((struct in_addr *)hp->h_addr_list[0])), hashv,
+ ocp->hnum_next, hashtable[hashv].num_list));
+ for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next))
+ {
+ if (*cp == ocp)
+ {
+ *cp = ocp->hnum_next;
+ break;
+ }
+ }
+
+ if (ocp->he.buf)
+ RunFree(ocp->he.buf);
+ RunFree((char *)ocp);
+
+ incache--;
+ cainfo.ca_dels++;
+}
+
+/*
+ * Removes entries from the cache which are older than their expirey times.
+ * returns the time at which the server should next poll the cache.
+ */
+time_t expire_cache(void)
+{
+ Reg1 aCache *cp, *cp2;
+ Reg2 time_t next = 0;
+
+ for (cp = cachetop; cp; cp = cp2)
+ {
+ cp2 = cp->list_next;
+
+ if (now >= cp->expireat)
+ {
+ cainfo.ca_expires++;
+ rem_cache(cp);
+ }
+ else if (!next || next > cp->expireat)
+ next = cp->expireat;
+ }
+ return (next > now) ? next : (now + AR_TTL);
+}
+
+/*
+ * Remove all dns cache entries.
+ */
+void flush_cache(void)
+{
+ Reg1 aCache *cp;
+
+ while ((cp = cachetop))
+ rem_cache(cp);
+}
+
+int m_dns(aClient *cptr, aClient *sptr, int UNUSED(parc), char *parv[])
+{
+ aCache *cp;
+ int i;
+ struct hostent *h;
+
+ if (parv[1] && *parv[1] == 'l')
+ {
+ if (!IsAnOper(cptr))
+ {
+ return 0;
+ }
+ for (cp = cachetop; cp; cp = cp->list_next)
+ {
+ h = &cp->he.h;
+ sendto_one(sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)",
+ parv[0], (int)(cp->expireat - now), (int)cp->ttl,
+ h->h_name, inetntoa(*((struct in_addr *)h->h_addr_list[0])));
+ for (i = 0; h->h_aliases[i]; i++)
+ sendto_one(sptr, "NOTICE %s : %s = %s (CN)",
+ parv[0], h->h_name, h->h_aliases[i]);
+ for (i = 1; h->h_addr_list[i]; i++)
+ sendto_one(sptr, "NOTICE %s : %s = %s (IP)", parv[0],
+ h->h_name, inetntoa(*((struct in_addr *)h->h_addr_list[i])));
+ }
+ return 0;
+ }
+ sendto_one(sptr, "NOTICE %s :Ca %d Cd %d Ce %d Cl %d Ch %d:%d Cu %d",
+ sptr->name, cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
+ cainfo.ca_lookups, cainfo.ca_na_hits, cainfo.ca_nu_hits,
+ cainfo.ca_updates);
+ sendto_one(sptr, "NOTICE %s :Re %d Rl %d/%d Rp %d Rq %d",
+ sptr->name, reinfo.re_errors, reinfo.re_nu_look,
+ reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
+ sendto_one(sptr, "NOTICE %s :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr->name,
+ reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
+ reinfo.re_resends, reinfo.re_timeouts);
+ return 0;
+}
+
+size_t cres_mem(aClient *sptr)
+{
+ aCache *c = cachetop;
+ struct hostent *h;
+ int i;
+ size_t nm = 0, im = 0, sm = 0, ts = 0;
+
+ for (; c; c = c->list_next)
+ {
+ sm += sizeof(*c);
+ h = &c->he.h;
+ for (i = 0; h->h_addr_list[i]; i++)
+ {
+ im += sizeof(char *);
+ im += sizeof(struct in_addr);
+ }
+ im += sizeof(char *);
+ for (i = 0; h->h_aliases[i]; i++)
+ {
+ nm += sizeof(char *);
+ nm += strlen(h->h_aliases[i]);
+ }
+ nm += i - 1;
+ nm += sizeof(char *);
+ if (h->h_name)
+ nm += strlen(h->h_name);
+ }
+ ts = ARES_CACSIZE * sizeof(CacheTable);
+ sendto_one(sptr, ":%s %d %s :RES table " SIZE_T_FMT,
+ me.name, RPL_STATSDEBUG, sptr->name, ts);
+ sendto_one(sptr, ":%s %d %s :Structs " SIZE_T_FMT
+ " IP storage " SIZE_T_FMT " Name storage " SIZE_T_FMT,
+ me.name, RPL_STATSDEBUG, sptr->name, sm, im, nm);
+ return ts + sm + im + nm;
+}
--- /dev/null
+/*
+ * Run's malloc/realloc/calloc/free DEBUG tools v2.0
+ *
+ * (c) Copyright 1996, 1997
+ *
+ * Author:
+ *
+ * 1024/624ACAD5 1997/01/26 Carlo Wood, Run on IRC <carlo@runaway.xs4all.nl>
+ * Key fingerprint = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
+ * Get key from pgp-public-keys server or
+ * finger carlo@runaway.xs4all.nl for public key (dialin, try at 21-22h GMT).
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+
+#ifdef DEBUGMALLOC
+#include <stdlib.h>
+#include "h.h"
+
+RCSTAG_CC("$Id$");
+
+#define MALLOC_HASHTABLE_SIZE 16384
+#define MallocHash(x) \
+ ((unsigned int)(((((long int)(x) >> 4) * 0xDEECE66D) >> 16) & (long int)0x3fff))
+#define MAGIC_PREFIX 0xe4c483a1
+#define MAGIC_POSTFIX 0x435bd0fa
+
+#ifdef MEMLEAKSTATS
+typedef struct {
+ const char *filename;
+ int line;
+ int number_of_allocations;
+#ifdef MEMSIZESTATS
+ size_t size;
+#endif
+} location_st;
+
+#define LOCSIZE 1024 /* Maximum of 256 different locations */
+static location_st location[LOCSIZE];
+static unsigned int locations; /* Counter */
+
+static unsigned int find_location(const char *filename, int line)
+{
+ register unsigned int hash;
+ hash = line & 0xff;
+ while (location[hash].filename && (location[hash].line != line ||
+ location[hash].filename != filename))
+ if (++hash == LOCSIZE)
+ hash = 0;
+ if (!location[hash].filename)
+ {
+ /* New location */
+ ++locations;
+ location[hash].filename = filename;
+ location[hash].line = line;
+ }
+ return hash;
+}
+#endif
+
+#ifdef MEMMAGICNUMS
+/* The size of this struct should be a multiple of 4 bytes, just in case... */
+typedef struct {
+#ifdef MEMMAGICNUMS
+ unsigned int prefix_magicnumber;
+#endif
+} prefix_blk_st;
+
+#define SIZEOF_PREFIX sizeof(prefix_blk_st)
+#else
+typedef void prefix_blk_st;
+#define SIZEOF_PREFIX 0
+#endif
+
+#ifdef MEMMAGICNUMS
+typedef struct {
+ unsigned int postfix_magicnumber;
+} postfix_blk_st;
+
+#define SIZEOF_POSTFIX sizeof(postfix_blk_st)
+#define HAS_POSTFIX
+#else
+#define SIZEOF_POSTFIX 0
+#endif
+
+typedef struct hash_entry_st {
+ struct hash_entry_st *next;
+ prefix_blk_st *ptr;
+#ifdef MEMSIZESTATS
+ size_t size;
+#endif
+#ifdef MEMLEAKSTATS
+ unsigned int location;
+#ifdef MEMTIMESTATS
+ time_t when;
+#endif
+#endif
+} hash_entry_st;
+
+#define memblkp(prefix_ptr) \
+ ((void *)((size_t)prefix_ptr + SIZEOF_PREFIX))
+#define prefixp(memblk_ptr) \
+ ((prefix_blk_st *)((size_t)memblk_ptr - SIZEOF_PREFIX))
+#define postfixp(memblk_ptr, size) \
+ ((postfix_blk_st *)((size_t)memblk_ptr + size))
+
+static hash_entry_st *hashtable[MALLOC_HASHTABLE_SIZE];
+#ifdef MEMSIZESTATS
+static size_t mem_size = 0; /* Number of allocated bytes */
+static unsigned int alloc_cnt = 0; /* Number of allocated blocks */
+#endif
+
+#ifdef MEMLEAKSTATS
+#include "struct.h"
+#include "send.h"
+#include "numeric.h"
+#include "s_err.h"
+#include "ircd.h"
+#include "s_serv.h"
+#include "numnicks.h"
+
+void report_memleak_stats(aClient *sptr, int parc, char *parv[])
+{
+ unsigned int hash;
+ location_st *loc = location;
+
+#ifdef MEMTIMESTATS
+ time_t till = now;
+ time_t from = me.since;
+ if (parc > 3)
+ {
+ location_st tmp_loc[LOCSIZE];
+ hash_entry_st **start;
+ memset(tmp_loc, 0, sizeof(tmp_loc));
+ if (parc > 3)
+ till -= atoi(parv[3]);
+ if (parc > 4)
+ from += atoi(parv[4]);
+ for (start = &hashtable[0];
+ start < &hashtable[MALLOC_HASHTABLE_SIZE]; ++start)
+ {
+ hash_entry_st *hash_entry;
+ for (hash_entry = *start; hash_entry; hash_entry = hash_entry->next)
+ if (hash_entry->when >= from && hash_entry->when <= till)
+ {
+#ifdef MEMSIZESTATS
+ tmp_loc[hash_entry->location].size += hash_entry->size;
+#endif
+ tmp_loc[hash_entry->location].number_of_allocations++;
+ }
+ }
+ loc = tmp_loc;
+ if (MyUser(sptr) || Protocol(sptr->from) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :Memory allocated between " TIME_T_FMT
+ " (server start + %s s) and " TIME_T_FMT " (now - %s s):",
+ me.name, parv[0], from, parc > 4 ? parv[4] : "0", till,
+ parc > 3 ? parv[3] : "0");
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :Memory allocated between " TIME_T_FMT
+ " (server start + %s s) and " TIME_T_FMT " (now - %s s):",
+ NumServ(&me), NumNick(sptr), from, parc > 4 ? parv[4] : "0", till,
+ parc > 3 ? parv[3] : "0");
+ }
+#endif
+ for (hash = 0; hash < LOCSIZE; ++hash)
+ if (loc[hash].number_of_allocations > 0)
+ sendto_one(sptr, rpl_str(RPL_STATMEM), me.name, parv[0],
+ loc[hash].number_of_allocations,
+ location[hash].line, location[hash].filename
+#ifdef MEMSIZESTATS
+ , loc[hash].size
+#endif
+ );
+}
+
+void *RunMalloc_memleak(size_t size, int line, const char *filename)
+#else
+void *RunMalloc(size_t size)
+#endif
+{
+ register prefix_blk_st *ptr;
+ register hash_entry_st *hash_entry;
+ register hash_entry_st **hashtablep;
+
+#ifdef HAS_POSTFIX
+ size += 3;
+ size &= ~3;
+#endif
+
+ if (!((ptr = (prefix_blk_st *)
+ malloc(SIZEOF_PREFIX + size + SIZEOF_POSTFIX)) &&
+ (hash_entry = (hash_entry_st *) malloc(sizeof(hash_entry_st)))))
+ {
+ if (ptr)
+ free(ptr);
+ Debug((DEBUG_FATAL, "Out of memory !"));
+ return NULL;
+ }
+
+ hashtablep = &hashtable[MallocHash(ptr)];
+ hash_entry->next = *hashtablep;
+ *hashtablep = hash_entry;
+ hash_entry->ptr = ptr;
+#ifdef MEMLEAKSTATS
+#ifdef MEMTIMESTATS
+ hash_entry->when = now;
+#endif
+ location[(hash_entry->location =
+ find_location(filename, line))].number_of_allocations++;
+#endif
+#ifdef MEMSIZESTATS
+ hash_entry->size = size;
+#ifdef MEMLEAKSTATS
+ location[hash_entry->location].size += size;
+#endif
+ mem_size += size;
+ ++alloc_cnt;
+#endif
+#ifdef MEMMAGICNUMS
+ ptr->prefix_magicnumber = MAGIC_PREFIX;
+ postfixp(memblkp(ptr), size)->postfix_magicnumber = MAGIC_POSTFIX;
+#endif
+
+ Debug((DEBUG_DEBUG, "RunMalloc(%u) = %p", size, memblkp(ptr)));
+
+ return memblkp(ptr);
+}
+
+#ifdef MEMLEAKSTATS
+void *RunCalloc_memleak(size_t nmemb, size_t size,
+ int line, const char *filename)
+#else
+void *RunCalloc(size_t nmemb, size_t size)
+#endif
+{
+ void *ptr;
+ size *= nmemb;
+#ifdef MEMLEAKSTATS
+ if ((ptr = RunMalloc_memleak(size, line, filename)))
+#else
+ if ((ptr = RunMalloc(size)))
+#endif
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+int RunFree_test(void *memblk_ptr)
+{
+ register prefix_blk_st *prefix_ptr = prefixp(memblk_ptr);
+ register hash_entry_st *hash_entry;
+ for (hash_entry = hashtable[MallocHash(prefix_ptr)];
+ hash_entry && hash_entry->ptr != prefix_ptr;
+ hash_entry = hash_entry->next);
+ return hash_entry ? 1 : 0;
+}
+
+void RunFree(void *memblk_ptr)
+{
+ register prefix_blk_st *prefix_ptr = prefixp(memblk_ptr);
+ register hash_entry_st *hash_entry, *prev_hash_entry = NULL;
+ unsigned int hash = MallocHash(prefix_ptr);
+
+ Debug((DEBUG_DEBUG, "RunFree(%p)", memblk_ptr));
+
+ if (!memblk_ptr)
+ return;
+
+ for (hash_entry = hashtable[hash];
+ hash_entry && hash_entry->ptr != prefix_ptr;
+ prev_hash_entry = hash_entry, hash_entry = hash_entry->next);
+ if (!hash_entry)
+ {
+ Debug((DEBUG_FATAL, "FREEING NON MALLOC PTR !!!"));
+ MyCoreDump;
+ }
+#ifdef MEMMAGICNUMS
+ if (prefix_ptr->prefix_magicnumber != MAGIC_PREFIX)
+ {
+ Debug((DEBUG_FATAL, "MAGIC_PREFIX CORRUPT !"));
+ MyCoreDump;
+ }
+ prefix_ptr->prefix_magicnumber = 12345678;
+ if (postfixp(memblk_ptr, hash_entry->size)->postfix_magicnumber
+ != MAGIC_POSTFIX)
+ {
+ Debug((DEBUG_FATAL, "MAGIC_POSTFIX CORRUPT !"));
+ MyCoreDump;
+ }
+ postfixp(memblk_ptr, hash_entry->size)->postfix_magicnumber = 87654321;
+#endif
+
+ if (prev_hash_entry)
+ prev_hash_entry->next = hash_entry->next;
+ else
+ hashtable[hash] = hash_entry->next;
+
+#ifdef MEMLEAKSTATS
+ location[hash_entry->location].number_of_allocations--;
+#endif
+
+#ifdef MEMSIZESTATS
+ mem_size -= hash_entry->size;
+ --alloc_cnt;
+#ifdef MEMLEAKSTATS
+ location[hash_entry->location].size -= hash_entry->size;
+#endif
+#ifdef DEBUGMODE
+ /* Put 0xfefefefe.. in freed memory */
+#ifndef memset
+ memset(prefix_ptr, 0xfe, hash_entry->size + SIZEOF_PREFIX);
+#else
+ {
+ register char *p = prefix_ptr;
+ size_t len = hash_entry->size + SIZEOF_PREFIX;
+ for (; len; --len)
+ *p++ = 0xfe;
+ }
+#endif
+#endif
+#endif
+
+ free(hash_entry);
+ free(prefix_ptr);
+}
+
+#ifdef MEMLEAKSTATS
+void *RunRealloc_memleak(void *memblk_ptr, size_t size,
+ int line, const char *filename)
+#else
+void *RunRealloc(void *memblk_ptr, size_t size)
+#endif
+{
+ register prefix_blk_st *ptr;
+ register prefix_blk_st *prefix_ptr = prefixp(memblk_ptr);
+ register hash_entry_st *hash_entry, *prev_hash_entry = NULL;
+ register hash_entry_st **hashtablep;
+ unsigned int hash;
+
+ if (!memblk_ptr)
+#ifdef MEMLEAKSTATS
+ return RunMalloc_memleak(size, line, filename);
+#else
+ return RunMalloc(size);
+#endif
+ if (!size)
+ {
+ RunFree(memblk_ptr);
+ return NULL;
+ }
+
+ for (hash_entry = hashtable[(hash = MallocHash(prefix_ptr))];
+ hash_entry && hash_entry->ptr != prefix_ptr;
+ prev_hash_entry = hash_entry, hash_entry = hash_entry->next);
+ if (!hash_entry)
+ {
+ Debug((DEBUG_FATAL, "REALLOCATING NON MALLOC PTR !!!"));
+ MyCoreDump;
+ }
+
+#ifdef MEMMAGICNUMS
+ if (prefix_ptr->prefix_magicnumber != MAGIC_PREFIX)
+ {
+ Debug((DEBUG_FATAL, "MAGIC_PREFIX CORRUPT !"));
+ MyCoreDump;
+ }
+ if (postfixp(memblk_ptr, hash_entry->size)->postfix_magicnumber
+ != MAGIC_POSTFIX)
+ {
+ Debug((DEBUG_FATAL, "MAGIC_POSTFIX CORRUPT !"));
+ MyCoreDump;
+ }
+#endif
+
+#ifdef HAS_POSTFIX
+ size += 3;
+ size &= ~3;
+#endif
+
+#ifdef MEMMAGICNUMS
+ postfixp(memblkp(prefix_ptr), hash_entry->size)->postfix_magicnumber = 123456;
+#endif
+#ifdef MEMLEAKSTATS
+ location[hash_entry->location].number_of_allocations--;
+#ifdef MEMSIZESTATS
+ location[hash_entry->location].size -= hash_entry->size;
+#endif
+#endif
+
+ if (!(ptr =
+ (prefix_blk_st *) realloc(prefix_ptr,
+ SIZEOF_PREFIX + size + SIZEOF_POSTFIX)))
+ {
+ Debug((DEBUG_FATAL, "RunRealloc: Out of memory :"));
+ return NULL;
+ }
+
+ if (prev_hash_entry)
+ prev_hash_entry->next = hash_entry->next;
+ else
+ hashtable[hash] = hash_entry->next;
+
+ hashtablep = &hashtable[MallocHash(ptr)];
+ hash_entry->next = *hashtablep;
+ *hashtablep = hash_entry;
+ hash_entry->ptr = ptr;
+#ifdef MEMLEAKSTATS
+#ifdef MEMTIMESTATS
+ hash_entry->when = now;
+#endif
+ location[(hash_entry->location =
+ find_location(filename, line))].number_of_allocations++;
+#endif
+#ifdef MEMSIZESTATS
+ mem_size += size - hash_entry->size;
+ hash_entry->size = size;
+#ifdef MEMLEAKSTATS
+ location[hash_entry->location].size += size;
+#endif
+#endif
+#ifdef MEMMAGICNUMS
+ postfixp(memblkp(ptr), size)->postfix_magicnumber = MAGIC_POSTFIX;
+#endif
+
+ Debug((DEBUG_DEBUG, ": RunRealloc(%p, %u) = %p",
+ memblk_ptr, size, memblkp(ptr)));
+
+ return memblkp(ptr);
+}
+
+#ifdef MEMSIZESTATS
+unsigned int get_alloc_cnt(void)
+{
+ return alloc_cnt;
+}
+
+size_t get_mem_size(void)
+{
+ return mem_size;
+}
+#endif
+
+#endif /* DEBUGMALLOC */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_auth.c
+ * Copyright (C) 1992 Darren Reed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/socket.h>
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef UNIXPORT
+#include <sys/un.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HPUX
+#include <arpa/inet.h>
+#endif /* HPUX */
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "common.h"
+#include "send.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "support.h"
+#include "ircd.h"
+#include "s_auth.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * start_auth
+ *
+ * Flag the client to show that an attempt to contact the ident server on
+ * the client's host. The connect and subsequently the socket are all put
+ * into 'non-blocking' mode. Should the connect or any later phase of the
+ * identifing process fail, it is aborted and the user is given a username
+ * of "unknown".
+ */
+void start_auth(aClient *cptr)
+{
+ struct sockaddr_in sock;
+ int err;
+
+ Debug((DEBUG_NOTICE, "start_auth(%p) fd %d status %d",
+ cptr, cptr->fd, cptr->status));
+
+ alarm(2);
+ cptr->authfd = socket(AF_INET, SOCK_STREAM, 0);
+ err = errno;
+ alarm(0);
+ if (cptr->authfd < 0 && err == EAGAIN)
+ sendto_ops("Can't allocate fd for auth on %s : socket: No more sockets",
+ get_client_name(cptr, TRUE));
+
+ if (cptr->authfd < 0)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "Unable to create auth socket for %s:%m",
+ get_client_name(cptr, TRUE));
+#endif
+ Debug((DEBUG_ERROR, "Unable to create auth socket for %s:%s",
+ get_client_name(cptr, TRUE), strerror(get_sockerr(cptr))));
+ if (!DoingDNS(cptr))
+ SetAccess(cptr);
+ ircstp->is_abad++;
+ return;
+ }
+ if (cptr->authfd >= (MAXCONNECTIONS - 2))
+ {
+ sendto_ops("Can't allocate fd for auth on %s", get_client_name(cptr, TRUE));
+ close(cptr->authfd);
+ return;
+ }
+
+ set_non_blocking(cptr->authfd, cptr);
+
+#ifdef VIRTUAL_HOST
+ if (bind(cptr->authfd, (struct sockaddr *)&vserv, sizeof(vserv)) == -1)
+ {
+ report_error("binding auth stream socket %s: %s", cptr);
+ close(cptr->fd);
+ return;
+ }
+#endif
+ memcpy(&sock.sin_addr, &cptr->ip, sizeof(struct in_addr));
+
+ sock.sin_port = htons(113);
+ sock.sin_family = AF_INET;
+
+ alarm((unsigned)4);
+ if (connect(cptr->authfd, (struct sockaddr *)&sock,
+ sizeof(sock)) == -1 && errno != EINPROGRESS)
+ {
+ ircstp->is_abad++;
+ /*
+ * No error report from this...
+ */
+ alarm((unsigned)0);
+ close(cptr->authfd);
+ cptr->authfd = -1;
+ if (!DoingDNS(cptr))
+ SetAccess(cptr);
+ return;
+ }
+ alarm((unsigned)0);
+ cptr->flags |= (FLAGS_WRAUTH | FLAGS_AUTH);
+ if (cptr->authfd > highest_fd)
+ highest_fd = cptr->authfd;
+ return;
+}
+
+/*
+ * send_authports
+ *
+ * Send the ident server a query giving "theirport , ourport".
+ * The write is only attempted *once* so it is deemed to be a fail if the
+ * entire write doesn't write all the data given. This shouldnt be a
+ * problem since the socket should have a write buffer far greater than
+ * this message to store it in should problems arise. -avalon
+ */
+void send_authports(aClient *cptr)
+{
+ struct sockaddr_in us, them;
+ char authbuf[32];
+ size_t ulen, tlen;
+
+ Debug((DEBUG_NOTICE, "write_authports(%p) fd %d authfd %d stat %d",
+ cptr, cptr->fd, cptr->authfd, cptr->status));
+ tlen = ulen = sizeof(us);
+ if (getsockname(cptr->fd, (struct sockaddr *)&us, &ulen) ||
+ getpeername(cptr->fd, (struct sockaddr *)&them, &tlen))
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "auth get{sock,peer}name error for %s:%m",
+ get_client_name(cptr, TRUE));
+#endif
+ goto authsenderr;
+ }
+
+ sprintf_irc(authbuf, "%u , %u\r\n", (unsigned int)ntohs(them.sin_port),
+ (unsigned int)ntohs(us.sin_port));
+
+ Debug((DEBUG_SEND, "sending [%s] to auth port %s.113",
+ authbuf, inetntoa(them.sin_addr)));
+ if (write(cptr->authfd, authbuf, strlen(authbuf)) != (int)strlen(authbuf))
+ {
+ authsenderr:
+ ircstp->is_abad++;
+ close(cptr->authfd);
+ if (cptr->authfd == highest_fd)
+ while (!loc_clients[highest_fd])
+ highest_fd--;
+ cptr->authfd = -1;
+ cptr->flags &= ~FLAGS_AUTH;
+ if (!DoingDNS(cptr))
+ SetAccess(cptr);
+ }
+ cptr->flags &= ~FLAGS_WRAUTH;
+ return;
+}
+
+/*
+ * read_authports
+ *
+ * read the reply (if any) from the ident server we connected to.
+ * The actual read processijng here is pretty weak - no handling of the reply
+ * if it is fragmented by IP.
+ */
+void read_authports(aClient *cptr)
+{
+ Reg1 char *s, *t;
+ Reg2 int len;
+ char ruser[USERLEN + 1], system[8];
+ unsigned short int remp = 0, locp = 0;
+
+ *system = *ruser = '\0';
+ Debug((DEBUG_NOTICE, "read_authports(%p) fd %d authfd %d stat %d",
+ cptr, cptr->fd, cptr->authfd, cptr->status));
+ /*
+ * Nasty. Cant allow any other reads from client fd while we're
+ * waiting on the authfd to return a full valid string. Use the
+ * client's input buffer to buffer the authd reply.
+ * Oh. this is needed because an authd reply may come back in more
+ * than 1 read! -avalon
+ */
+ if ((len = read(cptr->authfd, cptr->buffer + cptr->count,
+ sizeof(cptr->buffer) - 1 - cptr->count)) >= 0)
+ {
+ cptr->count += len;
+ cptr->buffer[cptr->count] = '\0';
+ }
+
+ cptr->lasttime = now;
+ if ((len > 0) && (cptr->count != (sizeof(cptr->buffer) - 1)) &&
+ (sscanf(cptr->buffer, "%hd , %hd : USERID : %*[^:]: %10s",
+ &remp, &locp, ruser) == 3))
+ {
+ s = strrchr(cptr->buffer, ':');
+ *s++ = '\0';
+ for (t = (strrchr(cptr->buffer, ':') + 1); *t; t++)
+ if (!isSpace(*t))
+ break;
+ strncpy(system, t, sizeof(system) - 1);
+ system[sizeof(system) - 1] = 0;
+ for (t = ruser; *s && (t < ruser + sizeof(ruser)); s++)
+ if (!isSpace(*s) && *s != ':' && *s != '@')
+ *t++ = *s;
+ *t = '\0';
+ Debug((DEBUG_INFO, "auth reply ok [%s] [%s]", system, ruser));
+ }
+ else if (len != 0)
+ {
+ if (!strchr(cptr->buffer, '\n') && !strchr(cptr->buffer, '\r'))
+ return;
+ Debug((DEBUG_ERROR, "local %d remote %d", locp, remp));
+ Debug((DEBUG_ERROR, "bad auth reply in [%s]", cptr->buffer));
+ *ruser = '\0';
+ }
+ close(cptr->authfd);
+ if (cptr->authfd == highest_fd)
+ while (!loc_clients[highest_fd])
+ highest_fd--;
+ cptr->count = 0;
+ cptr->authfd = -1;
+ ClearAuth(cptr);
+ if (!DoingDNS(cptr))
+ SetAccess(cptr);
+ if (len > 0)
+ Debug((DEBUG_INFO, "ident reply: [%s]", cptr->buffer));
+
+ if (!locp || !remp || !*ruser)
+ {
+ ircstp->is_abad++;
+ return;
+ }
+ ircstp->is_asuc++;
+ strncpy(cptr->username, ruser, USERLEN);
+ cptr->username[USERLEN] = 0; /* This is the LA bug --Run */
+ if (strncmp(system, "OTHER", 5))
+ cptr->flags |= FLAGS_GOTID;
+ Debug((DEBUG_INFO, "got username [%s]", ruser));
+ return;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_bsd.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdlib.h>
+#include <sys/socket.h>
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef SOL2
+#include <sys/filio.h>
+#endif
+#ifdef UNIXPORT
+#include <sys/un.h>
+#endif
+#include <stdio.h>
+#ifdef USE_POLL
+#ifndef HAVE_POLL_H
+#undef USE_POLL
+#else /* HAVE_POLL_H */
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#include <poll.h>
+#endif /* HAVE_POLL_H */
+#endif /* USE_POLL */
+#include <signal.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <utmp.h>
+#include <sys/resource.h>
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include <sys/utsname.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "s_bsd.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "hash.h"
+#include "s_err.h"
+#include "ircd.h"
+#include "support.h"
+#include "s_auth.h"
+#include "class.h"
+#include "packet.h"
+#include "s_ping.h"
+#include "channel.h"
+#include "version.h"
+#include "parse.h"
+#include "common.h"
+#include "bsd.h"
+#include "numnicks.h"
+#include "s_user.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+RCSTAG_CC("$Id$");
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET 0x7f
+#endif
+
+aClient *loc_clients[MAXCONNECTIONS];
+int highest_fd = 0, udpfd = -1, resfd = -1;
+unsigned int readcalls = 0;
+static struct sockaddr_in mysk;
+static void polludp();
+
+static struct sockaddr *connect_inet(aConfItem *, aClient *, int *);
+static int completed_connection(aClient *);
+static int check_init(aClient *, char *);
+static void do_dns_async(), set_sock_opts(int, aClient *);
+#ifdef UNIXPORT
+static struct sockaddr *connect_unix(aConfItem *, aClient *, int *);
+static void add_unixconnection(aClient *, int);
+static char unixpath[256];
+#endif
+static char readbuf[8192];
+#ifdef USE_POLL
+static struct pollfd poll_fds[MAXCONNECTIONS + 1];
+static aClient *poll_cptr[MAXCONNECTIONS + 1];
+#endif /* USE_POLL */
+#ifdef VIRTUAL_HOST
+struct sockaddr_in vserv;
+#endif
+static int running_in_background;
+
+#ifdef GODMODE
+#ifndef NODNS
+#define NODNS
+#endif
+#ifndef NOFLOODCONTROL
+#define NOFLOODCONTROL
+#endif
+#endif
+
+/*
+ * Try and find the correct name to use with getrlimit() for setting the max.
+ * number of files allowed to be open by this process.
+ */
+#ifdef RLIMIT_FDMAX
+#define RLIMIT_FD_MAX RLIMIT_FDMAX
+#else
+#ifdef RLIMIT_NOFILE
+#define RLIMIT_FD_MAX RLIMIT_NOFILE
+#else
+#ifdef RLIMIT_OPEN_MAX
+#define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
+#else
+#undef RLIMIT_FD_MAX
+#endif
+#endif
+#endif
+
+#if !defined(USE_POLL)
+#if FD_SETSIZE < (MAXCONNECTIONS + 4)
+/*
+ * Sanity check
+ *
+ * All operating systems work when MAXCONNECTIONS <= 252.
+ * Most operating systems work when MAXCONNECTIONS <= 1020 and FD_SETSIZE is
+ * updated correctly in the system headers (on BSD systems our sys.h has
+ * defined FD_SETSIZE to MAXCONNECTIONS+4 before including the system's headers
+ * but sys/types.h might have abruptly redefined it so the check is still
+ * done), you might already need to recompile your kernel.
+ * For larger FD_SETSIZE your milage may vary (kernel patches may be needed).
+ * The check is _NOT_ done if we will not use FD_SETS at all (USE_POLL)
+ */
+#error "FD_SETSIZE is too small or MAXCONNECTIONS too large."
+#endif
+#endif
+
+/*
+ * Cannot use perror() within daemon. stderr is closed in
+ * ircd and cannot be used. And, worse yet, it might have
+ * been reassigned to a normal connection...
+ */
+
+/*
+ * report_error
+ *
+ * This a replacement for perror(). Record error to log and
+ * also send a copy to all *LOCAL* opers online.
+ *
+ * text is a *format* string for outputting error. It must
+ * contain only two '%s', the first will be replaced
+ * by the sockhost from the cptr, and the latter will
+ * be taken from sys_errlist[errno].
+ *
+ * cptr if not NULL, is the *LOCAL* client associated with
+ * the error.
+ */
+void report_error(char *text, aClient *cptr)
+{
+ Reg1 int errtmp = errno; /* debug may change 'errno' */
+ Reg2 char *host;
+ int err;
+ size_t len = sizeof(err);
+
+ host = (cptr) ? get_client_name(cptr, FALSE) : "";
+
+ Debug((DEBUG_ERROR, text, host, strerror(errtmp)));
+
+ /*
+ * Get the *real* error from the socket (well try to anyway..).
+ * This may only work when SO_DEBUG is enabled but its worth the
+ * gamble anyway.
+ */
+#if defined(SO_ERROR) && !defined(SOL2)
+ if (cptr && !IsMe(cptr) && cptr->fd >= 0)
+ if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&err, &len))
+ if (err)
+ errtmp = err;
+#endif
+ sendto_ops(text, host, strerror(errtmp));
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, text, host, strerror(errtmp));
+#endif
+ if (!running_in_background)
+ {
+ fprintf(stderr, text, host, strerror(errtmp));
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ return;
+}
+
+/*
+ * inetport
+ *
+ * Create a socket in the AF_INET domain, bind it to the port given in
+ * 'port' and listen to it. Connections are accepted to this socket
+ * depending on the IP# mask given by 'name'. Returns the fd of the
+ * socket created or -1 on error.
+ */
+int inetport(aClient *cptr, char *name, unsigned short int port)
+{
+ static struct sockaddr_in server;
+ int ad[4], opt;
+ size_t len = sizeof(server);
+ char ipname[20];
+
+ ad[0] = ad[1] = ad[2] = ad[3] = 0;
+
+ /*
+ * do it this way because building ip# from separate values for each
+ * byte requires endian knowledge or some nasty messing. Also means
+ * easy conversion of "*" 0.0.0.0 or 134.* to 134.0.0.0 :-)
+ */
+ sscanf(name, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
+ sprintf_irc(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
+
+ if (cptr != &me)
+ {
+ sprintf(cptr->sockhost, "%-.42s.%u", name, port);
+ strcpy(cptr->name, me.name);
+ }
+ /*
+ * At first, open a new socket
+ */
+ if (cptr->fd == -1)
+ {
+ alarm(2);
+ cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
+ alarm(0);
+ if (cptr->fd < 0 && errno == EAGAIN)
+ {
+ sendto_ops("opening stream socket %s: No more sockets",
+ get_client_name(cptr, TRUE));
+ return -1;
+ }
+ }
+ if (cptr->fd < 0)
+ {
+ report_error("opening stream socket %s: %s", cptr);
+ return -1;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_ops("No more connections allowed (%s)", cptr->name);
+ close(cptr->fd);
+ return -1;
+ }
+
+ opt = 1;
+ setsockopt(cptr->fd, SOL_SOCKET, SO_REUSEADDR, (OPT_TYPE *)&opt, sizeof(opt));
+
+ /*
+ * Bind a port to listen for new connections if port is non-null,
+ * else assume it is already open and try get something from it.
+ */
+ if (port)
+ {
+ server.sin_family = AF_INET;
+#ifndef VIRTUAL_HOST
+ server.sin_addr.s_addr = INADDR_ANY;
+#else
+ if (vserv.sin_addr.s_addr == 0) /* Not already initialised ? */
+ {
+ struct hostent *hep;
+ memset(&vserv, 0, sizeof(vserv));
+ vserv.sin_family = AF_INET;
+ hep = gethostbyname(me.name); /* Use name from M: line */
+ if (hep && hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
+ !hep->h_addr_list[1])
+ memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
+ else
+ {
+ report_error("Error creating virtual host %s: %s", cptr);
+ return -1;
+ }
+ }
+ server.sin_addr = vserv.sin_addr;
+#endif
+#ifdef TESTNET
+ server.sin_port = htons(port + 10000);
+#else
+ server.sin_port = htons(port);
+#endif
+ if (bind(cptr->fd, (struct sockaddr *)&server, sizeof(server)) == -1)
+ {
+ report_error("binding stream socket %s: %s", cptr);
+ close(cptr->fd);
+ return -1;
+ }
+ }
+ if (getsockname(cptr->fd, (struct sockaddr *)&server, &len))
+ {
+ report_error("getsockname failed for %s: %s", cptr);
+ close(cptr->fd);
+ return -1;
+ }
+
+ if (cptr == &me) /* KLUDGE to get it work... */
+ {
+ char buf[1024];
+
+#ifdef TESTNET
+ sprintf_irc(buf, rpl_str(RPL_MYPORTIS), me.name, "*",
+ ntohs(server.sin_port) - 10000);
+#else
+ sprintf_irc(buf, rpl_str(RPL_MYPORTIS), me.name, "*",
+ ntohs(server.sin_port));
+#endif
+ write(1, buf, strlen(buf));
+ }
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+ cptr->ip.s_addr = inet_addr(ipname);
+#ifdef TESTNET
+ cptr->port = ntohs(server.sin_port) - 10000;
+#else
+ cptr->port = ntohs(server.sin_port);
+#endif
+ listen(cptr->fd, 128); /* Use listen port backlog of 128 */
+ loc_clients[cptr->fd] = cptr;
+
+ return 0;
+}
+
+#ifdef UNIXPORT
+/*
+ * unixport
+ *
+ * Create a socket and bind it to a filename which is comprised of the path
+ * (directory where file is placed) and port (actual filename created).
+ * Set directory permissions as rwxr-xr-x so other users can connect to the
+ * file which is 'forced' to rwxrwxrwx (different OS's have different need of
+ * modes so users can connect to the socket).
+ */
+int unixport(aClient *cptr, char *path, unsigned short int port)
+{
+ struct sockaddr_un un;
+
+ alarm(2);
+ cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ alarm(0);
+ if (cptr->fd == -1 && errno == EAGAIN)
+ {
+ sendto_ops("error opening unix domain socket %s: No more sockets",
+ get_client_name(cptr, TRUE));
+ return -1;
+ }
+ if (cptr->fd == -1)
+ {
+ report_error("error opening unix domain socket %s: %s", cptr);
+ return -1;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_ops("No more connections allowed (%s)", cptr->name);
+ close(cptr->fd);
+ cptr->fd = -1;
+ return -1;
+ }
+
+ un.sun_family = AF_UNIX;
+#if HAVE_MKDIR
+ mkdir(path, 0755);
+#else
+ if (chmod(path, 0755) == -1)
+ {
+ sendto_ops("error 'chmod 0755 %s': %s", path, strerror(errno));
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, "error 'chmod 0755 %s': %s", path, strerror(errno));
+#endif
+ close(cptr->fd);
+ cptr->fd = -1;
+ return -1;
+ }
+#endif
+ sprintf_irc(unixpath, "%s/%u", path, port);
+ unlink(unixpath);
+ strncpy(un.sun_path, unixpath, sizeof(un.sun_path) - 1);
+ un.sun_path[sizeof(un.sun_path) - 1] = 0;
+ strcpy(cptr->name, me.name);
+ errno = 0;
+ get_sockhost(cptr, unixpath);
+
+ if (bind(cptr->fd, (struct sockaddr *)&un, strlen(unixpath) + 2) == -1)
+ {
+ report_error("error binding unix socket %s: %s", cptr);
+ close(cptr->fd);
+ return -1;
+ }
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+ listen(cptr->fd, 5);
+ chmod(unixpath, 0777);
+ cptr->flags |= FLAGS_UNIX;
+ cptr->port = 0;
+ loc_clients[cptr->fd] = cptr;
+
+ return 0;
+}
+#endif
+
+/*
+ * add_listener
+ *
+ * Create a new client which is essentially the stub like 'me' to be used
+ * for a socket that is passive (listen'ing for connections to be accepted).
+ */
+int add_listener(aConfItem *aconf)
+{
+ aClient *cptr;
+
+ cptr = make_client(NULL, STAT_ME);
+ cptr->flags = FLAGS_LISTEN;
+ cptr->acpt = cptr;
+ cptr->from = cptr;
+ strncpy(cptr->name, aconf->host, sizeof(cptr->name) - 1);
+ cptr->name[sizeof(cptr->name) - 1] = 0;
+#ifdef UNIXPORT
+ if (*aconf->host == '/')
+ {
+ if (unixport(cptr, aconf->host, aconf->port))
+ cptr->fd = -2;
+ }
+ else
+#endif
+ if (inetport(cptr, aconf->host, aconf->port))
+ cptr->fd = -2;
+
+ if (cptr->fd >= 0)
+ {
+ cptr->confs = make_link();
+ cptr->confs->next = NULL;
+ cptr->confs->value.aconf = aconf;
+ set_non_blocking(cptr->fd, cptr);
+ }
+ else
+ free_client(cptr);
+ return 0;
+}
+
+/*
+ * close_listeners
+ *
+ * Close and free all clients which are marked as having their socket open
+ * and in a state where they can accept connections. Unix sockets have
+ * the path to the socket unlinked for cleanliness.
+ */
+void close_listeners(void)
+{
+ Reg1 aClient *cptr;
+ Reg2 int i;
+ Reg3 aConfItem *aconf;
+
+ /*
+ * close all 'extra' listening ports we have and unlink the file
+ * name if it was a unix socket.
+ */
+ for (i = highest_fd; i >= 0; i--)
+ {
+ if (!(cptr = loc_clients[i]))
+ continue;
+ if (!IsMe(cptr) || cptr == &me || !IsListening(cptr))
+ continue;
+ aconf = cptr->confs->value.aconf;
+
+ if (IsIllegal(aconf) && aconf->clients == 0)
+ {
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ sprintf_irc(unixpath, "%s/%u", aconf->host, aconf->port);
+ unlink(unixpath);
+ }
+#endif
+ close_connection(cptr);
+ }
+ }
+}
+
+/*
+ * init_sys
+ */
+void init_sys(void)
+{
+ Reg1 int fd;
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX)
+ struct rlimit limit;
+
+ if (!getrlimit(RLIMIT_FD_MAX, &limit))
+ {
+#ifdef pyr
+ if (limit.rlim_cur < MAXCONNECTIONS)
+#else
+ if (limit.rlim_max < MAXCONNECTIONS)
+#endif
+ {
+ fprintf(stderr, "ircd fd table too big\n");
+ fprintf(stderr, "Hard Limit: " LIMIT_FMT " IRC max: %d\n",
+#ifdef pyr
+ limit.rlim_cur,
+#else
+ limit.rlim_max,
+#endif
+ (int)MAXCONNECTIONS);
+ fprintf(stderr, "Fix MAXCONNECTIONS\n");
+ exit(-1);
+ }
+#ifndef pyr
+ limit.rlim_cur = limit.rlim_max; /* make soft limit the max */
+ if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
+ {
+ fprintf(stderr, "error setting max fd's to " LIMIT_FMT "\n",
+ limit.rlim_cur);
+ exit(-1);
+ }
+#endif
+ }
+#endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */
+#ifdef DEBUGMODE
+ if (1)
+ {
+ static char logbuf[BUFSIZ];
+#if SETVBUF_REVERSED
+ setvbuf(stderr, _IOLBF, logbuf, sizeof(logbuf));
+#else
+ setvbuf(stderr, logbuf, _IOLBF, sizeof(logbuf));
+#endif
+ }
+#endif
+
+ for (fd = 3; fd < MAXCONNECTIONS; fd++)
+ {
+ close(fd);
+ loc_clients[fd] = NULL;
+ }
+ loc_clients[1] = NULL;
+ close(1);
+
+ if (bootopt & BOOT_TTY) /* debugging is going to a tty */
+ goto init_dgram;
+ if (!(bootopt & BOOT_DEBUG))
+ close(2);
+
+ if (((bootopt & BOOT_CONSOLE) || isatty(0)) &&
+ !(bootopt & (BOOT_INETD | BOOT_OPER)))
+ {
+ if (fork())
+ exit(0);
+ running_in_background = 1;
+#ifdef TIOCNOTTY
+ if ((fd = open("/dev/tty", O_RDWR)) >= 0)
+ {
+ ioctl(fd, TIOCNOTTY, (char *)NULL);
+ close(fd);
+ }
+#endif
+#if defined(HPUX) || defined(SOL2) || defined(_SEQUENT_) || \
+ defined(_POSIX_SOURCE) || defined(SVR4)
+ setsid();
+#else
+ setpgid(0, 0);
+#endif
+ close(0); /* fd 0 opened by inetd */
+ loc_clients[0] = NULL;
+ }
+init_dgram:
+ resfd = init_resolver();
+
+ return;
+}
+
+void write_pidfile(void)
+{
+#ifdef PPATH
+ int fd;
+ char buff[20];
+ if ((fd = open(PPATH, O_CREAT | O_WRONLY, 0600)) >= 0)
+ {
+ memset(buff, 0, sizeof(buff));
+ sprintf(buff, "%5d\n", (int)getpid());
+ if (write(fd, buff, strlen(buff)) == -1)
+ Debug((DEBUG_NOTICE, "Error writing to pid file %s", PPATH));
+ close(fd);
+ return;
+ }
+#ifdef DEBUGMODE
+ else
+ Debug((DEBUG_NOTICE, "Error opening pid file \"%s\": %s",
+ PPATH, strerror(errno)));
+#endif
+#endif
+}
+
+/*
+ * Initialize the various name strings used to store hostnames. This is set
+ * from either the server's sockhost (if client fd is a tty or localhost)
+ * or from the ip# converted into a string. 0 = success, -1 = fail.
+ */
+static int check_init(aClient *cptr, char *sockn)
+{
+ struct sockaddr_in sk;
+ size_t len = sizeof(struct sockaddr_in);
+ sockn[HOSTLEN] = 0;
+
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ strncpy(sockn, cptr->acpt->sockhost, HOSTLEN);
+ get_sockhost(cptr, sockn);
+ return 0;
+ }
+#endif
+
+ /* If descriptor is a tty, special checking... */
+ if (isatty(cptr->fd))
+ {
+ strncpy(sockn, me.sockhost, HOSTLEN);
+ memset(&sk, 0, sizeof(struct sockaddr_in));
+ }
+ else if (getpeername(cptr->fd, (struct sockaddr *)&sk, &len) == -1)
+ {
+ report_error("connect failure: %s %s", cptr);
+ return -1;
+ }
+ strcpy(sockn, inetntoa(sk.sin_addr));
+ if (inet_netof(sk.sin_addr) == IN_LOOPBACKNET)
+ {
+ cptr->hostp = NULL;
+ strncpy(sockn, me.sockhost, HOSTLEN);
+ }
+ memcpy(&cptr->ip, &sk.sin_addr, sizeof(struct in_addr));
+#ifdef TESTNET
+ cptr->port = ntohs(sk.sin_port) - 10000;
+#else
+ cptr->port = ntohs(sk.sin_port);
+#endif
+
+ return 0;
+}
+
+/*
+ * Ordinary client access check. Look for conf lines which have the same
+ * status as the flags passed.
+ */
+enum AuthorizationCheckResult check_client(aClient *cptr)
+{
+ static char sockname[HOSTLEN + 1];
+ Reg2 struct hostent *hp = NULL;
+ Reg3 int i;
+ enum AuthorizationCheckResult acr;
+
+ ClearAccess(cptr);
+ Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+ cptr->name, inetntoa(cptr->ip)));
+
+ if (check_init(cptr, sockname))
+ return ACR_BAD_SOCKET;
+
+ if (!IsUnixSocket(cptr))
+ hp = cptr->hostp;
+ /*
+ * Verify that the host to ip mapping is correct both ways and that
+ * the ip#(s) for the socket is listed for the host.
+ */
+ if (hp)
+ {
+ for (i = 0; hp->h_addr_list[i]; i++)
+ if (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr)))
+ break;
+ if (!hp->h_addr_list[i])
+ {
+ sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]",
+ inetntoa(cptr->ip), hp->h_name, *((unsigned int *)hp->h_addr));
+ hp = NULL;
+ }
+ }
+
+ if ((acr = attach_Iline(cptr, hp, sockname)))
+ {
+ Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", cptr->name, sockname));
+ return acr;
+ }
+
+ Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]", cptr->name, sockname));
+
+ if (inet_netof(cptr->ip) == IN_LOOPBACKNET || IsUnixSocket(cptr) ||
+ inet_netof(cptr->ip) == inet_netof(mysk.sin_addr))
+ {
+ ircstp->is_loc++;
+ cptr->flags |= FLAGS_LOCAL;
+ }
+ return ACR_OK;
+}
+
+#define CFLAG CONF_CONNECT_SERVER
+#define NFLAG CONF_NOCONNECT_SERVER
+
+/*
+ * check_server()
+ *
+ * Check access for a server given its name (passed in cptr struct).
+ * Must check for all C/N lines which have a name which matches the
+ * name given and a host which matches. A host alias which is the
+ * same as the server name is also acceptable in the host field of a
+ * C/N line.
+ *
+ * Returns
+ * 0 = Success
+ * -1 = Access denied
+ * -2 = Bad socket.
+ */
+int check_server(aClient *cptr)
+{
+ Reg1 const char *name;
+ Reg2 aConfItem *c_conf = NULL, *n_conf = NULL;
+ struct hostent *hp = NULL;
+ Link *lp;
+ char abuff[HOSTLEN + USERLEN + 2];
+ char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1];
+ int i;
+
+ name = cptr->name;
+ Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", name, cptr->sockhost));
+
+ if (IsUnknown(cptr) && !attach_confs(cptr, name, CFLAG | NFLAG))
+ {
+ Debug((DEBUG_DNS, "No C/N lines for %s", name));
+ return -1;
+ }
+ lp = cptr->confs;
+ /*
+ * We initiated this connection so the client should have a C and N
+ * line already attached after passing through the connec_server()
+ * function earlier.
+ */
+ if (IsConnecting(cptr) || IsHandshake(cptr))
+ {
+ c_conf = find_conf(lp, name, CFLAG);
+ n_conf = find_conf(lp, name, NFLAG);
+ if (!c_conf || !n_conf)
+ {
+ sendto_ops("Connecting Error: %s[%s]", name, cptr->sockhost);
+ det_confs_butmask(cptr, 0);
+ return -1;
+ }
+ }
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ if (!c_conf)
+ c_conf = find_conf(lp, name, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf(lp, name, NFLAG);
+ }
+#endif
+
+ /*
+ * If the servername is a hostname, either an alias (CNAME) or
+ * real name, then check with it as the host. Use gethostbyname()
+ * to check for servername as hostname.
+ */
+ if (!IsUnixSocket(cptr) && !cptr->hostp)
+ {
+ Reg1 aConfItem *aconf;
+
+ aconf = count_cnlines(lp);
+ if (aconf)
+ {
+ Reg1 char *s;
+ Link lin;
+
+ /*
+ * Do a lookup for the CONF line *only* and not
+ * the server connection else we get stuck in a
+ * nasty state since it takes a SERVER message to
+ * get us here and we cant interrupt that very well.
+ */
+ ClearAccess(cptr);
+ lin.value.aconf = aconf;
+ lin.flags = ASYNC_CONF;
+ nextdnscheck = 1;
+ if ((s = strchr(aconf->host, '@')))
+ s++;
+ else
+ s = aconf->host;
+ Debug((DEBUG_DNS, "sv_ci:cache lookup (%s)", s));
+ hp = gethost_byname(s, &lin);
+ }
+ }
+
+ lp = cptr->confs;
+
+ ClearAccess(cptr);
+ if (check_init(cptr, sockname))
+ return -2;
+
+check_serverback:
+ if (hp)
+ {
+ for (i = 0; hp->h_addr_list[i]; i++)
+ if (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr)))
+ break;
+ if (!hp->h_addr_list[i])
+ {
+ sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]",
+ inetntoa(cptr->ip), hp->h_name, *((unsigned int *)hp->h_addr));
+ hp = NULL;
+ }
+ }
+ else if (cptr->hostp)
+ {
+ hp = cptr->hostp;
+ goto check_serverback;
+ }
+
+ if (hp)
+ /*
+ * If we are missing a C or N line from above, search for
+ * it under all known hostnames we have for this ip#.
+ */
+ for (i = 0, name = hp->h_name; name; name = hp->h_aliases[i++])
+ {
+ strncpy(fullname, name, sizeof(fullname) - 1);
+ fullname[sizeof(fullname) - 1] = 0;
+ add_local_domain(fullname, HOSTLEN - strlen(fullname));
+ Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s", sockname, fullname));
+ sprintf_irc(abuff, "%s@%s", cptr->username, fullname);
+ if (!c_conf)
+ c_conf = find_conf_host(lp, abuff, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_host(lp, abuff, NFLAG);
+ if (c_conf && n_conf)
+ {
+ get_sockhost(cptr, fullname);
+ break;
+ }
+ }
+ name = cptr->name;
+
+ /*
+ * Check for C and N lines with the hostname portion the ip number
+ * of the host the server runs on. This also checks the case where
+ * there is a server connecting from 'localhost'.
+ */
+ if (IsUnknown(cptr) && (!c_conf || !n_conf))
+ {
+ sprintf_irc(abuff, "%s@%s", cptr->username, sockname);
+ if (!c_conf)
+ c_conf = find_conf_host(lp, abuff, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_host(lp, abuff, NFLAG);
+ }
+ /*
+ * Attach by IP# only if all other checks have failed.
+ * It is quite possible to get here with the strange things that can
+ * happen when using DNS in the way the irc server does. -avalon
+ */
+ if (!hp)
+ {
+ if (!c_conf)
+ c_conf = find_conf_ip(lp, (char *)&cptr->ip, cptr->username, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_ip(lp, (char *)&cptr->ip, cptr->username, NFLAG);
+ }
+ else
+ for (i = 0; hp->h_addr_list[i]; i++)
+ {
+ if (!c_conf)
+ c_conf = find_conf_ip(lp, hp->h_addr_list[i], cptr->username, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_ip(lp, hp->h_addr_list[i], cptr->username, NFLAG);
+ }
+ /*
+ * detach all conf lines that got attached by attach_confs()
+ */
+ det_confs_butmask(cptr, 0);
+ /*
+ * if no C or no N lines, then deny access
+ */
+ if (!c_conf || !n_conf)
+ {
+ get_sockhost(cptr, sockname);
+ Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s] c %p n %p",
+ name, cptr->username, cptr->sockhost, c_conf, n_conf));
+ return -1;
+ }
+ /*
+ * attach the C and N lines to the client structure for later use.
+ */
+ attach_conf(cptr, n_conf);
+ attach_conf(cptr, c_conf);
+ attach_confs(cptr, name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
+
+ if ((c_conf->ipnum.s_addr == INADDR_NONE) && !IsUnixSocket(cptr))
+ memcpy(&c_conf->ipnum, &cptr->ip, sizeof(struct in_addr));
+ if (!IsUnixSocket(cptr))
+ get_sockhost(cptr, c_conf->host);
+
+ Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", name, cptr->sockhost));
+ return 0;
+}
+#undef CFLAG
+#undef NFLAG
+
+/*
+ * completed_connection
+ *
+ * Complete non-blocking connect()-sequence. Check access and
+ * terminate connection, if trouble detected.
+ *
+ * Return TRUE, if successfully completed
+ * FALSE, if failed and ClientExit
+ */
+static int completed_connection(aClient *cptr)
+{
+ aConfItem *aconf;
+ time_t newts;
+ aClient *acptr;
+ int i;
+
+ aconf = find_conf(cptr->confs, cptr->name, CONF_CONNECT_SERVER);
+ if (!aconf)
+ {
+ sendto_ops("Lost C-Line for %s", get_client_name(cptr, FALSE));
+ return -1;
+ }
+ if (!BadPtr(aconf->passwd))
+ sendto_one(cptr, "PASS :%s", aconf->passwd);
+
+ aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
+ if (!aconf)
+ {
+ sendto_ops("Lost N-Line for %s", get_client_name(cptr, FALSE));
+ return -1;
+ }
+ make_server(cptr);
+ /* Create a unique timestamp */
+ newts = TStime();
+ for (i = highest_fd; i >= 0; i--)
+ {
+ if (!(acptr = loc_clients[i]) || (!IsServer(acptr) && !IsHandshake(acptr)))
+ continue;
+ if (acptr->serv->timestamp >= newts)
+ newts = acptr->serv->timestamp + 1;
+ }
+ cptr->serv->timestamp = newts;
+ SetHandshake(cptr);
+ /* Make us timeout after twice the timeout for DNS look ups */
+ cptr->lasttime = now;
+ cptr->flags |= FLAGS_PINGSENT;
+ sendto_one(cptr, "SERVER %s 1 " TIME_T_FMT " " TIME_T_FMT " J%s %s%s :%s",
+ my_name_for_link(me.name, aconf), me.serv->timestamp,
+ newts, MAJOR_PROTOCOL, NumServCap(&me), me.info);
+ if (!IsDead(cptr))
+ start_auth(cptr);
+
+ return (IsDead(cptr)) ? -1 : 0;
+}
+
+/*
+ * close_connection
+ *
+ * Close the physical connection. This function must make
+ * MyConnect(cptr) == FALSE, and set cptr->from == NULL.
+ */
+void close_connection(aClient *cptr)
+{
+ Reg1 aConfItem *aconf;
+ Reg2 int i, j;
+ int empty = cptr->fd;
+
+ if (IsServer(cptr))
+ {
+ ircstp->is_sv++;
+ ircstp->is_sbs += cptr->sendB;
+ ircstp->is_sbr += cptr->receiveB;
+ ircstp->is_sks += cptr->sendK;
+ ircstp->is_skr += cptr->receiveK;
+ ircstp->is_sti += now - cptr->firsttime;
+ if (ircstp->is_sbs > 1023)
+ {
+ ircstp->is_sks += (ircstp->is_sbs >> 10);
+ ircstp->is_sbs &= 0x3ff;
+ }
+ if (ircstp->is_sbr > 1023)
+ {
+ ircstp->is_skr += (ircstp->is_sbr >> 10);
+ ircstp->is_sbr &= 0x3ff;
+ }
+ }
+ else if (IsUser(cptr))
+ {
+ ircstp->is_cl++;
+ ircstp->is_cbs += cptr->sendB;
+ ircstp->is_cbr += cptr->receiveB;
+ ircstp->is_cks += cptr->sendK;
+ ircstp->is_ckr += cptr->receiveK;
+ ircstp->is_cti += now - cptr->firsttime;
+ if (ircstp->is_cbs > 1023)
+ {
+ ircstp->is_cks += (ircstp->is_cbs >> 10);
+ ircstp->is_cbs &= 0x3ff;
+ }
+ if (ircstp->is_cbr > 1023)
+ {
+ ircstp->is_ckr += (ircstp->is_cbr >> 10);
+ ircstp->is_cbr &= 0x3ff;
+ }
+ }
+ else
+ ircstp->is_ni++;
+
+ /*
+ * Remove outstanding DNS queries.
+ */
+ del_queries((char *)cptr);
+ /*
+ * If the connection has been up for a long amount of time, schedule
+ * a 'quick' reconnect, else reset the next-connect cycle.
+ */
+
+ if ((aconf = find_conf_exact(cptr->name, cptr->username,
+ cptr->sockhost, CONF_CONNECT_SERVER)))
+ {
+ /*
+ * Reschedule a faster reconnect, if this was a automaticly
+ * connected configuration entry. (Note that if we have had
+ * a rehash in between, the status has been changed to
+ * CONF_ILLEGAL). But only do this if it was a "good" link.
+ */
+ aconf->hold = now;
+ aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ?
+ HANGONRETRYDELAY : ConfConFreq(aconf);
+ if (nextconnect > aconf->hold)
+ nextconnect = aconf->hold;
+ }
+
+ if (cptr->authfd >= 0)
+ close(cptr->authfd);
+
+ if (cptr->fd >= 0)
+ {
+ flush_connections(cptr->fd);
+ loc_clients[cptr->fd] = NULL;
+ close(cptr->fd);
+ cptr->fd = -2;
+ }
+
+ DBufClear(&cptr->sendQ);
+ DBufClear(&cptr->recvQ);
+ memset(cptr->passwd, 0, sizeof(cptr->passwd));
+ set_snomask(cptr, 0, SNO_SET);
+ /*
+ * Clean up extra sockets from P-lines which have been discarded.
+ */
+ if (cptr->acpt != &me && cptr->acpt != cptr)
+ {
+ aconf = cptr->acpt->confs->value.aconf;
+ if (aconf->clients > 0)
+ aconf->clients--;
+ if (!aconf->clients && IsIllegal(aconf))
+ close_connection(cptr->acpt);
+ }
+
+ for (; highest_fd > 0; highest_fd--)
+ if (loc_clients[highest_fd])
+ break;
+
+ det_confs_butmask(cptr, 0);
+
+ /*
+ * fd remap to keep loc_clients[i] filled at the bottom.
+ */
+ if (empty > 0)
+ if ((j = highest_fd) > (i = empty) && !IsLog(loc_clients[j]))
+ {
+ if (IsListening(loc_clients[j]))
+ return;
+ if (dup2(j, i) == -1)
+ return;
+ loc_clients[i] = loc_clients[j];
+ loc_clients[i]->fd = i;
+ loc_clients[j] = NULL;
+ close(j);
+ while (!loc_clients[highest_fd])
+ highest_fd--;
+ }
+
+ return;
+}
+
+/*
+ * set_sock_opts
+ */
+static void set_sock_opts(int fd, aClient *cptr)
+{
+ size_t opt;
+#ifdef SO_REUSEADDR
+ opt = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (OPT_TYPE *)&opt, sizeof(opt)) < 0)
+ report_error("setsockopt(SO_REUSEADDR) %s: %s", cptr);
+#endif
+#ifdef SO_USELOOPBACK
+ opt = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK,
+ (OPT_TYPE *)&opt, sizeof(opt)) < 0)
+ report_error("setsockopt(SO_USELOOPBACK) %s: %s", cptr);
+#endif
+#ifdef SO_RCVBUF
+ opt = 8192;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
+ report_error("setsockopt(SO_RCVBUF) %s: %s", cptr);
+#endif
+#ifdef SO_SNDBUF
+#ifdef _SEQUENT_
+/*
+ * Seems that Sequent freezes up if the receving buffer is a different size
+ * to the sending buffer (maybe a tcp window problem too).
+ */
+ opt = 8192;
+#else
+ opt = 8192;
+#endif
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
+ report_error("setsockopt(SO_SNDBUF) %s: %s", cptr);
+#endif
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+ {
+ char *s = readbuf, *t = readbuf + sizeof(readbuf) / 2;
+
+ opt = sizeof(readbuf) / 8;
+ if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *)t, &opt) < 0)
+ report_error("getsockopt(IP_OPTIONS) %s: %s", cptr);
+ else if (opt > 0 && opt != sizeof(readbuf) / 8)
+ {
+ for (*readbuf = '\0'; opt > 0; opt--, s += 3)
+ sprintf(s, "%02x:", *t++);
+ *s = '\0';
+ sendto_ops("Connection %s using IP opts: (%s)",
+ get_client_name(cptr, TRUE), readbuf);
+ }
+ if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *)NULL, 0) < 0)
+ report_error("setsockopt(IP_OPTIONS) %s: %s", cptr);
+ }
+#endif
+}
+
+int get_sockerr(aClient *cptr)
+{
+ int errtmp = errno, err = 0;
+ size_t len = sizeof(err);
+#if defined(SO_ERROR) && !defined(SOL2)
+ if (cptr->fd >= 0)
+ if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&err, &len))
+ if (err)
+ errtmp = err;
+#endif
+ return errtmp;
+}
+
+/*
+ * set_non_blocking
+ *
+ * Set the client connection into non-blocking mode. If your
+ * system doesn't support this, you can make this a dummy
+ * function (and get all the old problems that plagued the
+ * blocking version of IRC--not a problem if you are a
+ * lightly loaded node...)
+ */
+void set_non_blocking(int fd, aClient *cptr)
+{
+ int res;
+#ifndef NBLOCK_SYSV
+ int nonb = 0;
+#endif
+
+ /*
+ * NOTE: consult ALL your relevant manual pages *BEFORE* changing
+ * these ioctl's. There are quite a few variations on them,
+ * as can be seen by the PCS one. They are *NOT* all the same.
+ * Heed this well. - Avalon.
+ */
+#ifdef NBLOCK_POSIX
+ nonb |= O_NONBLOCK;
+#endif
+#ifdef NBLOCK_BSD
+ nonb |= O_NDELAY;
+#endif
+#ifdef NBLOCK_SYSV
+ /* This portion of code might also apply to NeXT. -LynX */
+ res = 1;
+
+ if (ioctl(fd, FIONBIO, &res) < 0)
+ report_error("ioctl(fd,FIONBIO) failed for %s: %s", cptr);
+#else
+ if ((res = fcntl(fd, F_GETFL, 0)) == -1)
+ report_error("fcntl(fd, F_GETFL) failed for %s: %s", cptr);
+ else if (fcntl(fd, F_SETFL, res | nonb) == -1)
+ report_error("fcntl(fd, F_SETL, nonb) failed for %s: %s", cptr);
+#endif
+ return;
+}
+
+extern unsigned short server_port;
+
+/*
+ * Creates a client which has just connected to us on the given fd.
+ * The sockhost field is initialized with the ip# of the host.
+ * The client is added to the linked list of clients but isnt added to any
+ * hash tables yet since it doesn't have a name.
+ */
+aClient *add_connection(aClient *cptr, int fd, int type)
+{
+ Link lin;
+ aClient *acptr;
+ aConfItem *aconf = NULL;
+ acptr =
+ make_client(NULL,
+ (cptr->port == server_port) ? STAT_UNKNOWN_SERVER : STAT_UNKNOWN_USER);
+
+ if (cptr != &me)
+ aconf = cptr->confs->value.aconf;
+ /*
+ * Removed preliminary access check. Full check is performed in
+ * m_server and m_user instead. Also connection time out help to
+ * get rid of unwanted connections.
+ */
+ if (type == ADCON_TTY) /* If descriptor is a tty,
+ special checking... */
+ get_sockhost(acptr, cptr->sockhost);
+ else
+ {
+ Reg1 char *s, *t;
+ struct sockaddr_in addr;
+ size_t len = sizeof(struct sockaddr_in);
+
+ if (getpeername(fd, (struct sockaddr *)&addr, &len) == -1)
+ {
+ report_error("Failed in connecting to %s: %s", cptr);
+ add_con_refuse:
+ ircstp->is_ref++;
+ acptr->fd = -2;
+ free_client(acptr);
+ close(fd);
+ return NULL;
+ }
+ /* Don't want to add "Failed in connecting to" here.. */
+ if (aconf && IsIllegal(aconf))
+ goto add_con_refuse;
+ /*
+ * Copy ascii address to 'sockhost' just in case. Then we
+ * have something valid to put into error messages...
+ */
+ get_sockhost(acptr, inetntoa(addr.sin_addr));
+ memcpy(&acptr->ip, &addr.sin_addr, sizeof(struct in_addr));
+#ifdef TESTNET
+ acptr->port = ntohs(addr.sin_port) - 10000;
+#else
+ acptr->port = ntohs(addr.sin_port);
+#endif
+
+ /*
+ * Check that this socket (client) is allowed to accept
+ * connections from this IP#.
+ */
+ for (s = (char *)&cptr->ip, t = (char *)&acptr->ip, len = 4;
+ len > 0; len--, s++, t++)
+ {
+ if (!*s)
+ continue;
+ if (*s != *t)
+ break;
+ }
+
+ if (len)
+ goto add_con_refuse;
+
+ lin.flags = ASYNC_CLIENT;
+ lin.value.cptr = acptr;
+#ifdef NODNS
+ if (!strcmp("127.0.0.1", inetntoa(addr.sin_addr)))
+ {
+ static struct hostent lhe = { "localhost", NULL, 0, 0, NULL };
+ acptr->hostp = &lhe;
+ if (!DoingAuth(acptr))
+ SetAccess(acptr);
+ }
+ else
+ {
+#endif
+ Debug((DEBUG_DNS, "lookup %s", inetntoa(addr.sin_addr)));
+ acptr->hostp = gethost_byaddr(&acptr->ip, &lin);
+ if (!acptr->hostp)
+ SetDNS(acptr);
+ nextdnscheck = 1;
+#ifdef NODNS
+ }
+#endif
+ }
+
+ if (aconf)
+ aconf->clients++;
+ acptr->fd = fd;
+ if (fd > highest_fd)
+ highest_fd = fd;
+ loc_clients[fd] = acptr;
+ acptr->acpt = cptr;
+ Count_newunknown(nrof);
+ add_client_to_list(acptr);
+ set_non_blocking(acptr->fd, acptr);
+ set_sock_opts(acptr->fd, acptr);
+
+ /*
+ * Add this local client to the IPcheck registry.
+ * If it is a connection to a user port and if the site has been throttled,
+ * reject the user.
+ */
+ if (IPcheck_local_connect(acptr) == -1 && IsUserPort(acptr))
+ {
+ ircstp->is_ref++;
+ exit_client(cptr, acptr, &me,
+ "Your host is trying to (re)connect too fast -- throttled");
+ return NULL;
+ }
+
+ start_auth(acptr);
+ return acptr;
+}
+
+#ifdef UNIXPORT
+static void add_unixconnection(aClient *cptr, int fd)
+{
+ aClient *acptr;
+ aConfItem *aconf = NULL;
+
+ acptr = make_client(NULL, STAT_UNKNOWN);
+
+ /*
+ * Copy ascii address to 'sockhost' just in case. Then we
+ * have something valid to put into error messages...
+ */
+ get_sockhost(acptr, me.sockhost);
+ if (cptr != &me)
+ aconf = cptr->confs->value.aconf;
+ if (aconf)
+ {
+ if (IsIllegal(aconf))
+ {
+ ircstp->is_ref++;
+ acptr->fd = -2;
+ free_client(acptr);
+ close(fd);
+ return;
+ }
+ else
+ aconf->clients++;
+ }
+ acptr->fd = fd;
+ if (fd > highest_fd)
+ highest_fd = fd;
+ loc_clients[fd] = acptr;
+ acptr->acpt = cptr;
+ SetUnixSock(acptr);
+ memcpy(&acptr->ip, &me.ip, sizeof(struct in_addr));
+
+ Count_newunknown(nrof);
+ add_client_to_list(acptr);
+ set_non_blocking(acptr->fd, acptr);
+ set_sock_opts(acptr->fd, acptr);
+ SetAccess(acptr);
+ return;
+}
+#endif
+
+/*
+ * select/poll convert macro's by Run.
+ *
+ * The names are chosen to reflect what they means when NOT using poll().
+ */
+#ifndef USE_POLL
+typedef fd_set *fd_setp_t;
+#define RFD_ISSET(fd, rfd, index) FD_ISSET((fd), (rfd))
+#define WFD_ISSET(fd, wfd, index) FD_ISSET((fd), (wfd))
+#define RFD_SET(fd, rfd, index, cptr) FD_SET((fd), (rfd))
+#define WFD_SET(fd, wfd, index, cptr) FD_SET((fd), (wfd))
+#define RWFD_SET(fd, wfd, index) FD_SET((fd), (wfd))
+#define RFD_CLR_OUT(fd, rfd, index) FD_CLR((fd), (rfd))
+#define WFD_CLR_OUT(fd, wfd, index) FD_CLR((fd), (wfd))
+#define LOC_FD(index) (index)
+#define LOC_CLIENTS(index) loc_clients[index]
+#define HIGHEST_INDEX highest_fd
+#else /* USE_POLL */
+typedef unsigned int fd_setp_t; /* Actually, an index to poll_fds[] */
+#ifdef _AIX
+#define POLLREADFLAGS (POLLIN|POLLMSG)
+#else
+# if defined(POLLMSG) && defined(POLLIN) && defined(POLLRDNORM)
+# define POLLREADFLAGS (POLLMSG|POLLIN|POLLRDNORM)
+# else
+# if defined(POLLIN) && defined(POLLRDNORM)
+# define POLLREADFLAGS (POLLIN|POLLRDNORM)
+# else
+# if defined(POLLIN)
+# define POLLREADFLAGS POLLIN
+# else
+# if defined(POLLRDNORM)
+# define POLLREADFLAGS POLLRDNORM
+# endif
+# endif
+# endif
+# endif
+#endif
+#if defined(POLLOUT) && defined(POLLWRNORM)
+#define POLLWRITEFLAGS (POLLOUT|POLLWRNORM)
+#else
+# if defined(POLLOUT)
+# define POLLWRITEFLAGS POLLOUT
+# else
+# if defined(POLLWRNORM)
+# define POLLWRITEFLAGS POLLWRNORM
+# endif
+# endif
+#endif
+#ifdef POLLHUP
+#define POLLERRORS (POLLHUP|POLLERR)
+#else
+#define POLLERRORS POLLERR
+#endif
+#define RFD_ISSET(fd, rfd, index) \
+ ((poll_fds[index].revents & POLLREADFLAGS) || \
+ ((poll_fds[index].events & POLLREADFLAGS) && \
+ (poll_fds[index].revents & POLLERRORS)))
+#define WFD_ISSET(fd, wfd, index) \
+ ((poll_fds[index].revents & POLLWRITEFLAGS) || \
+ ((poll_fds[index].events & POLLWRITEFLAGS) && \
+ (poll_fds[index].revents & POLLERRORS)))
+#define RFD_SET(fdes, rfd, index, cptr) \
+ do { \
+ poll_fds[index].fd = fdes; \
+ poll_cptr[index] = cptr; \
+ poll_fds[index].events = POLLREADFLAGS; \
+ added = TRUE; \
+ } while(0)
+#define WFD_SET(fdes, wfd, index, cptr) \
+ do { \
+ poll_fds[index].fd = fdes; \
+ poll_cptr[index] = cptr; \
+ if (added) \
+ poll_fds[index].events |= POLLWRITEFLAGS; \
+ else \
+ { \
+ poll_fds[index].events = POLLWRITEFLAGS; \
+ added = TRUE; \
+ } \
+ } while(0)
+/* This identical to WFD_SET() when used after a call to RFD_SET(): */
+#define RWFD_SET(fd, wfd, index) poll_fds[index].events |= POLLWRITEFLAGS
+/* [RW]FD_CLR_OUT() clears revents, not events */
+#define RFD_CLR_OUT(fd, rfd, index) poll_fds[index].revents &= ~POLLREADFLAGS
+#define WFD_CLR_OUT(fd, wfd, index) poll_fds[index].revents &= ~POLLWRITEFLAGS
+#define LOC_FD(index) (poll_fds[index].fd)
+#define LOC_CLIENTS(index) (poll_cptr[index])
+#define HIGHEST_INDEX (currfd_index - 1)
+#endif /* USE_POLL */
+
+/*
+ * read_packet
+ *
+ * Read a 'packet' of data from a connection and process it. Read in 8k
+ * chunks to give a better performance rating (for server connections).
+ * Do some tricky stuff for client connections to make sure they don't do
+ * any flooding >:-) -avalon
+ */
+static int read_packet(aClient *cptr, fd_setp_t rfd)
+{
+ size_t dolen = 0;
+ int length = 0;
+ int done;
+
+ if (RFD_ISSET(cptr->fd, rfd, rfd) &&
+ !(IsUser(cptr) && DBufLength(&cptr->recvQ) > 6090))
+ {
+ errno = 0;
+ length = recv(cptr->fd, readbuf, sizeof(readbuf), 0);
+
+ cptr->lasttime = now;
+ if (cptr->lasttime > cptr->since)
+ cptr->since = cptr->lasttime;
+ cptr->flags &= ~(FLAGS_PINGSENT | FLAGS_NONL);
+ /*
+ * If not ready, fake it so it isnt closed
+ */
+ if (length == -1 && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
+ return 1;
+ if (length <= 0)
+ return length;
+ }
+
+ /*
+ * For server connections, we process as many as we can without
+ * worrying about the time of day or anything :)
+ */
+ if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
+ {
+ if (length > 0)
+ if ((done = dopacket(cptr, readbuf, length)))
+ return done;
+ }
+ else
+ {
+ /*
+ * Before we even think of parsing what we just read, stick
+ * it on the end of the receive queue and do it when its
+ * turn comes around.
+ */
+ if (!dbuf_put(&cptr->recvQ, readbuf, length))
+ return exit_client(cptr, cptr, &me, "dbuf_put fail");
+
+#ifndef NOFLOODCONTROL
+ if (IsUser(cptr) && DBufLength(&cptr->recvQ) > CLIENT_FLOOD)
+ return exit_client(cptr, cptr, &me, "Excess Flood");
+#endif
+
+ while (DBufLength(&cptr->recvQ) && !NoNewLine(cptr)
+#ifndef NOFLOODCONTROL
+ && (IsTrusted(cptr) || cptr->since - now < 10)
+#endif
+ )
+ {
+ /*
+ * If it has become registered as a Server
+ * then skip the per-message parsing below.
+ */
+ if (IsServer(cptr))
+ {
+ /*
+ * XXX - this blindly deletes data if no cr/lf is received at
+ * the end of a lot of messages and the data stored in the
+ * dbuf is greater than sizeof(readbuf)
+ */
+ dolen = dbuf_get(&cptr->recvQ, readbuf, sizeof(readbuf));
+ if (0 == dolen)
+ break;
+ if ((done = dopacket(cptr, readbuf, dolen)))
+ return done;
+ break;
+ }
+ dolen = dbuf_getmsg(&cptr->recvQ, cptr->buffer, BUFSIZE);
+ /*
+ * Devious looking...whats it do ? well..if a client
+ * sends a *long* message without any CR or LF, then
+ * dbuf_getmsg fails and we pull it out using this
+ * loop which just gets the next 512 bytes and then
+ * deletes the rest of the buffer contents.
+ * -avalon
+ */
+ if (0 == dolen)
+ {
+ if (DBufLength(&cptr->recvQ) < 510)
+ {
+ cptr->flags |= FLAGS_NONL;
+ break;
+ }
+ DBufClear(&cptr->recvQ);
+ break;
+ }
+ else if (CPTR_KILLED == client_dopacket(cptr, dolen))
+ return CPTR_KILLED;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Check all connections for new connections and input data that is to be
+ * processed. Also check for connections with data queued and whether we can
+ * write it out.
+ *
+ * Don't ever use ZERO for `delay', unless you mean to poll and then
+ * you have to have sleep/wait somewhere else in the code.--msa
+ */
+int read_message(time_t delay)
+{
+ Reg1 aClient *cptr;
+ Reg2 int nfds;
+ struct timeval wait;
+#ifdef pyr
+ struct timeval nowt;
+ unsigned long us;
+#endif
+ time_t delay2 = delay;
+ unsigned long usec = 0;
+ int res, length, fd, i;
+ int auth = 0, ping = 0;
+#ifndef USE_POLL
+ fd_set read_set, write_set;
+#else /* USE_POLL */
+ unsigned int currfd_index = 0;
+ unsigned int udpfdindex = 0;
+ unsigned int resfdindex = 0;
+ unsigned long timeout;
+ int added;
+#endif /* USE_POLL */
+
+#ifdef pyr
+ gettimeofday(&nowt, NULL);
+ now = nowt.tv_sec;
+#endif
+
+ for (res = 0;;)
+ {
+#ifndef USE_POLL
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+#endif /* not USE_POLL */
+ for (i = highest_fd; i >= 0; i--)
+ {
+#ifdef USE_POLL
+ added = FALSE;
+#endif /* USE_POLL */
+ if (!(cptr = loc_clients[i]))
+ continue;
+ if (IsLog(cptr))
+ continue;
+ if (DoingAuth(cptr))
+ {
+ auth++;
+ Debug((DEBUG_NOTICE, "auth on %p %d", cptr, i));
+ RFD_SET(cptr->authfd, &read_set, currfd_index, cptr);
+ if (cptr->flags & FLAGS_WRAUTH)
+ RWFD_SET(cptr->authfd, &write_set, currfd_index);
+ }
+ if (IsPing(cptr))
+ {
+ ping++;
+ Debug((DEBUG_NOTICE, "open ping on %p %d", cptr, i));
+ if (!cptr->firsttime || now <= cptr->firsttime)
+ {
+ RFD_SET(i, &read_set, currfd_index, cptr);
+ delay2 = 1;
+ if (DoPing(cptr) && now > cptr->lasttime)
+ RWFD_SET(i, &write_set, currfd_index);
+ }
+ else
+ {
+ del_queries((char *)cptr);
+ end_ping(cptr);
+ }
+#ifdef USE_POLL
+ if (added)
+ currfd_index++;
+#endif /* USE_POLL */
+ continue;
+ }
+ if (DoingDNS(cptr) || DoingAuth(cptr))
+ {
+#ifdef USE_POLL
+ if (added)
+ currfd_index++;
+#endif /* USE_POLL */
+ continue;
+ }
+ if (IsMe(cptr) && IsListening(cptr))
+ RFD_SET(i, &read_set, currfd_index, cptr);
+ else if (!IsMe(cptr))
+ {
+ if (DBufLength(&cptr->recvQ) && delay2 > 2)
+ delay2 = 1;
+ if (DBufLength(&cptr->recvQ) < 4088)
+ RFD_SET(i, &read_set, currfd_index, cptr);
+ if (DBufLength(&cptr->sendQ) || IsConnecting(cptr) ||
+ (cptr->listing && DBufLength(&cptr->sendQ) < 2048))
+#ifndef pyr
+ WFD_SET(i, &write_set, currfd_index, cptr);
+#else /* pyr */
+ {
+ if (!(cptr->flags & FLAGS_BLOCKED))
+ WFD_SET(i, &write_set, currfd_index, cptr);
+ else
+ delay2 = 0, usec = 500000;
+ }
+ if (now - cptr->lw.tv_sec && nowt.tv_usec - cptr->lw.tv_usec < 0)
+ us = 1000000;
+ else
+ us = 0;
+ us += nowt.tv_usec;
+ if (us - cptr->lw.tv_usec > 500000)
+ cptr->flags &= ~FLAGS_BLOCKED;
+#endif /* pyr */
+ }
+#ifdef USE_POLL
+ if (added)
+ currfd_index++;
+#endif /* USE_POLL */
+ }
+
+ if (udpfd >= 0)
+ {
+ RFD_SET(udpfd, &read_set, currfd_index, NULL);
+#ifdef USE_POLL
+ udpfdindex = currfd_index;
+ currfd_index++;
+#endif /* USE_POLL */
+ }
+ if (resfd >= 0)
+ {
+ RFD_SET(resfd, &read_set, currfd_index, NULL);
+#ifdef USE_POLL
+ resfdindex = currfd_index;
+ currfd_index++;
+#endif /* USE_POLL */
+ }
+
+ wait.tv_sec = MIN(delay2, delay);
+ wait.tv_usec = usec;
+#ifndef USE_POLL
+#ifdef HPUX
+ nfds = select(FD_SETSIZE, (int *)&read_set, (int *)&write_set, 0, &wait);
+#else
+ nfds = select(FD_SETSIZE, &read_set, &write_set, 0, &wait);
+#endif
+#else /* USE_POLL */
+ timeout = (wait.tv_sec * 1000) + (wait.tv_usec / 1000);
+ nfds = poll(poll_fds, currfd_index, timeout);
+#endif /* USE_POLL */
+ now = time(NULL);
+ if (nfds == -1 && errno == EINTR)
+ return -1;
+ else if (nfds >= 0)
+ break;
+ report_error("select %s: %s", &me);
+ res++;
+ if (res > 5)
+ restart("too many select errors");
+ sleep(10);
+ now += 10;
+ }
+
+ if (udpfd >= 0 && RFD_ISSET(udpfd, &read_set, udpfdindex))
+ {
+ polludp();
+ nfds--;
+ RFD_CLR_OUT(udpfd, &read_set, udpfdindex);
+ }
+ /*
+ * Check fd sets for the ping fd's (if set and valid!) first
+ * because these can not be processed using the normal loops below.
+ * And we want them to be as fast as possible.
+ * -Run
+ */
+ for (i = HIGHEST_INDEX; (ping > 0) && (i >= 0); i--)
+ {
+ if (!(cptr = LOC_CLIENTS(i)))
+ continue;
+ if (!IsPing(cptr))
+ continue;
+ ping--;
+ if ((nfds > 0) && RFD_ISSET(cptr->fd, &read_set, i))
+ {
+ nfds--;
+ RFD_CLR_OUT(cptr->fd, &read_set, i);
+ read_ping(cptr); /* This can RunFree(cptr) ! */
+ }
+ else if ((nfds > 0) && WFD_ISSET(cptr->fd, &write_set, i))
+ {
+ nfds--;
+ cptr->lasttime = now;
+ WFD_CLR_OUT(cptr->fd, &write_set, i);
+ send_ping(cptr); /* This can RunFree(cptr) ! */
+ }
+ }
+ if (resfd >= 0 && RFD_ISSET(resfd, &read_set, resfdindex))
+ {
+ do_dns_async();
+ nfds--;
+ RFD_CLR_OUT(resfd, &read_set, resfdindex);
+ }
+ /*
+ * Check fd sets for the auth fd's (if set and valid!) first
+ * because these can not be processed using the normal loops below.
+ * -avalon
+ */
+ for (i = HIGHEST_INDEX; (auth > 0) && (i >= 0); i--)
+ {
+ if (!(cptr = LOC_CLIENTS(i)))
+ continue;
+ if (cptr->authfd < 0)
+ continue;
+ auth--;
+ if ((nfds > 0) && WFD_ISSET(cptr->authfd, &write_set, i))
+ {
+ nfds--;
+ send_authports(cptr);
+ }
+ else if ((nfds > 0) && RFD_ISSET(cptr->authfd, &read_set, i))
+ {
+ nfds--;
+ read_authports(cptr);
+ }
+ }
+ for (i = HIGHEST_INDEX; i >= 0; i--)
+ if ((cptr = LOC_CLIENTS(i)) && RFD_ISSET(i, &read_set, i) &&
+ IsListening(cptr))
+ {
+ RFD_CLR_OUT(i, &read_set, i);
+ nfds--;
+ cptr->lasttime = now;
+ /*
+ * There may be many reasons for error return, but
+ * in otherwise correctly working environment the
+ * probable cause is running out of file descriptors
+ * (EMFILE, ENFILE or others?). The man pages for
+ * accept don't seem to list these as possible,
+ * although it's obvious that it may happen here.
+ * Thus no specific errors are tested at this
+ * point, just assume that connections cannot
+ * be accepted until some old is closed first.
+ */
+ if ((fd = accept(LOC_FD(i), NULL, NULL)) < 0)
+ {
+ if (errno != EWOULDBLOCK)
+ report_error("accept() failed%s: %s", NULL);
+ break;
+ }
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECTS)
+ { /* get an early log of all connections --dl */
+ static struct sockaddr_in peer;
+ static int len;
+ len = sizeof(peer);
+ getpeername(fd, (struct sockaddr *)&peer, &len);
+ syslog(LOG_DEBUG, "Conn: %s", inetntoa(peer.sin_addr));
+ }
+#endif
+ ircstp->is_ac++;
+ if (fd >= MAXCLIENTS)
+ {
+ /* Don't send more messages then one every 10 minutes */
+ static int count;
+ static time_t last_time;
+ ircstp->is_ref++;
+ ++count;
+ if (last_time < now - (time_t) 600)
+ {
+ if (count > 0)
+ {
+ if (!last_time)
+ last_time = me.since;
+ sendto_ops
+ ("All connections in use! Had to refuse %d clients in the last "
+ STIME_T_FMT " minutes", count, (now - last_time) / 60);
+ }
+ else
+ sendto_ops("All connections in use. (%s)", get_client_name(cptr,
+ TRUE));
+ count = 0;
+ last_time = now;
+ }
+ send(fd, "ERROR :All connections in use\r\n", 32, 0);
+ close(fd);
+ break;
+ }
+ /*
+ * Use of add_connection (which never fails :) meLazy
+ */
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ add_unixconnection(cptr, fd);
+ else
+#endif
+ if (!add_connection(cptr, fd, ADCON_SOCKET))
+ continue;
+ nextping = now;
+ if (!cptr->acpt)
+ cptr->acpt = &me;
+ }
+
+ for (i = HIGHEST_INDEX; i >= 0; i--)
+ {
+ if (!(cptr = LOC_CLIENTS(i)) || IsMe(cptr))
+ continue;
+#ifdef USE_POLL
+ if (DoingDNS(cptr) || DoingAuth(cptr) || !(cptr = loc_clients[LOC_FD(i)]))
+ continue;
+#endif /* USE_POLL */
+#ifdef DEBUGMODE
+ if (IsLog(cptr))
+ continue;
+#endif
+ if (WFD_ISSET(i, &write_set, i))
+ {
+ int write_err = 0;
+ nfds--;
+ /*
+ * ...room for writing, empty some queue then...
+ */
+ cptr->flags &= ~FLAGS_BLOCKED;
+ if (IsConnecting(cptr))
+ write_err = completed_connection(cptr);
+ if (!write_err)
+ {
+ if (cptr->listing && DBufLength(&cptr->sendQ) < 2048)
+ list_next_channels(cptr, 64);
+ send_queued(cptr);
+ }
+ if (IsDead(cptr) || write_err)
+ {
+ deadsocket:
+ if (RFD_ISSET(i, &read_set, i))
+ {
+ nfds--;
+ RFD_CLR_OUT(i, &read_set, i);
+ }
+ exit_client(cptr, cptr, &me,
+ IsDead(cptr) ? LastDeadComment(cptr) : strerror(get_sockerr(cptr)));
+ continue;
+ }
+ }
+ length = 1; /* for fall through case */
+ if ((!NoNewLine(cptr) || RFD_ISSET(i, &read_set, i)) && !IsDead(cptr))
+#ifndef USE_POLL
+ length = read_packet(cptr, &read_set);
+#else /* USE_POLL */
+ length = read_packet(cptr, i);
+#endif /* USE_POLL */
+#if 0
+ /* Bullshit, why would we want to flush sockets while using non-blocking?
+ * This uses > 4% cpu! --Run */
+ if (length > 0)
+ flush_connections(LOC_FD(i));
+#endif
+ if ((length != CPTR_KILLED) && IsDead(cptr))
+ goto deadsocket;
+ if (!RFD_ISSET(i, &read_set, i) && length > 0)
+ continue;
+ nfds--;
+ readcalls++;
+ if (length > 0)
+ continue;
+
+ /*
+ * ...hmm, with non-blocking sockets we might get
+ * here from quite valid reasons, although.. why
+ * would select report "data available" when there
+ * wasn't... So, this must be an error anyway... --msa
+ * actually, EOF occurs when read() returns 0 and
+ * in due course, select() returns that fd as ready
+ * for reading even though it ends up being an EOF. -avalon
+ */
+ Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d %d", LOC_FD(i), errno, length));
+
+ if (length == CPTR_KILLED)
+ continue;
+
+ if ((IsServer(cptr) || IsHandshake(cptr)) && errno == 0 && length == 0)
+ exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)",
+ get_client_name(cptr, FALSE), cptr->serv->last_error_msg);
+ else
+ exit_client_msg(cptr, cptr, &me, "Read error to %s: %s",
+ get_client_name(cptr, FALSE), (length < 0) ?
+ strerror(get_sockerr(cptr)) : "EOF from client");
+ }
+ return 0;
+}
+
+/*
+ * connect_server
+ */
+int connect_server(aConfItem *aconf, aClient *by, struct hostent *hp)
+{
+ Reg1 struct sockaddr *svp;
+ Reg2 aClient *cptr, *c2ptr;
+ Reg3 char *s;
+ int errtmp, len;
+
+ Debug((DEBUG_NOTICE, "Connect to %s[%s] @%s",
+ aconf->name, aconf->host, inetntoa(aconf->ipnum)));
+
+ if ((c2ptr = FindClient(aconf->name)))
+ {
+ if (IsServer(c2ptr) || IsMe(c2ptr))
+ {
+ sendto_ops("Server %s already present from %s",
+ aconf->name, c2ptr->from->name);
+ if (by && IsUser(by) && !MyUser(by))
+ {
+#ifndef NO_PROTOCOL9
+ if (Protocol(by->from) < 10)
+ sendto_one(by, ":%s NOTICE %s :Server %s already present from %s",
+ me.name, by->name, aconf->name, c2ptr->from->name);
+ else
+#endif
+ sendto_one(by, "%s NOTICE %s%s :Server %s already present from %s",
+ NumServ(&me), NumNick(by), aconf->name, c2ptr->from->name);
+ }
+ return -1;
+ }
+ else if (IsHandshake(c2ptr) || IsConnecting(c2ptr))
+ {
+ if (by && IsUser(by))
+ {
+ if (MyUser(by) || Protocol(by->from) < 10)
+ sendto_one(by, ":%s NOTICE %s :Connection to %s already in progress",
+ me.name, by->name, get_client_name(c2ptr, TRUE));
+ else
+ sendto_one(by,
+ "%s NOTICE %s%s :Connection to %s already in progress",
+ NumServ(&me), NumNick(by), get_client_name(c2ptr, TRUE));
+ }
+ return -1;
+ }
+ }
+
+ /*
+ * If we dont know the IP# for this host and itis a hostname and
+ * not a ip# string, then try and find the appropriate host record.
+ */
+ if ((!aconf->ipnum.s_addr)
+#ifdef UNIXPORT
+ && ((aconf->host[2]) != '/') /* needed for Unix domain -- dl */
+#endif
+ )
+ {
+ Link lin;
+
+ lin.flags = ASYNC_CONNECT;
+ lin.value.aconf = aconf;
+ nextdnscheck = 1;
+ s = strchr(aconf->host, '@');
+ s++; /* should NEVER be NULL */
+ if ((aconf->ipnum.s_addr = inet_addr(s)) == INADDR_NONE)
+ {
+ aconf->ipnum.s_addr = INADDR_ANY;
+ hp = gethost_byname(s, &lin);
+ Debug((DEBUG_NOTICE, "co_sv: hp %p ac %p na %s ho %s",
+ hp, aconf, aconf->name, s));
+ if (!hp)
+ return 0;
+ memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+ }
+ }
+ cptr = make_client(NULL, STAT_UNKNOWN);
+ cptr->hostp = hp;
+ /*
+ * Copy these in so we have something for error detection.
+ */
+ strncpy(cptr->name, aconf->name, sizeof(cptr->name) - 1);
+ cptr->name[sizeof(cptr->name) - 1] = 0;
+ strncpy(cptr->sockhost, aconf->host, HOSTLEN);
+ cptr->sockhost[HOSTLEN] = 0;
+
+#ifdef UNIXPORT
+ if (aconf->host[2] == '/') /* (/ starts a 2), Unix domain -- dl */
+ svp = connect_unix(aconf, cptr, &len);
+ else
+ svp = connect_inet(aconf, cptr, &len);
+#else
+ svp = connect_inet(aconf, cptr, &len);
+#endif
+
+ if (!svp)
+ {
+ if (cptr->fd >= 0)
+ close(cptr->fd);
+ cptr->fd = -2;
+ if (by && IsUser(by) && !MyUser(by))
+ {
+#ifndef NO_PROTOCOL9
+ if (Protocol(by->from) < 10)
+ sendto_one(by, ":%s NOTICE %s :Couldn't connect to %s",
+ me.name, by->name, get_client_name(cptr, TRUE));
+ else
+#endif
+ sendto_one(by, "%s NOTICE %s%s :Couldn't connect to %s",
+ NumServ(&me), NumNick(by), get_client_name(cptr, TRUE));
+ }
+ free_client(cptr);
+ return -1;
+ }
+
+ set_non_blocking(cptr->fd, cptr);
+ set_sock_opts(cptr->fd, cptr);
+ signal(SIGALRM, dummy);
+ alarm(4);
+ if (connect(cptr->fd, svp, len) < 0 && errno != EINPROGRESS)
+ {
+ int err = get_sockerr(cptr);
+ errtmp = errno; /* other system calls may eat errno */
+ alarm(0);
+ report_error("Connect to host %s failed: %s", cptr);
+ if (by && IsUser(by) && !MyUser(by))
+ {
+#ifndef NO_PROTOCOL9
+ if (Protocol(by->from) < 10)
+ sendto_one(by, ":%s NOTICE %s :Connect to host %s failed: %s",
+ me.name, by->name, get_client_name(cptr, TRUE), strerror(err));
+ else
+#endif
+ sendto_one(by, "%s NOTICE %s%s :Connect to host %s failed: %s",
+ NumServ(&me), NumNick(by), get_client_name(cptr, TRUE),
+ strerror(err));
+ }
+ close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ errno = errtmp;
+ if (errno == EINTR)
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ alarm(0);
+
+ /*
+ * Attach config entries to client here rather than in
+ * completed_connection. This to avoid null pointer references
+ * when name returned by gethostbyaddr matches no C lines
+ * (could happen in 2.6.1a when host and servername differ).
+ * No need to check access and do gethostbyaddr calls.
+ * There must at least be one as we got here C line... meLazy
+ */
+ attach_confs_host(cptr, aconf->host,
+ CONF_NOCONNECT_SERVER | CONF_CONNECT_SERVER);
+
+ if (!find_conf_host(cptr->confs, aconf->host, CONF_NOCONNECT_SERVER) ||
+ !find_conf_host(cptr->confs, aconf->host, CONF_CONNECT_SERVER))
+ {
+ sendto_ops("Host %s is not enabled for connecting:no C/N-line",
+ aconf->host);
+ if (by && IsUser(by) && !MyUser(by))
+ {
+#ifndef NO_PROTOCOL9
+ if (Protocol(by->from) < 10)
+ sendto_one(by,
+ ":%s NOTICE %s :Connect to host %s failed: no C/N-lines",
+ me.name, by->name, get_client_name(cptr, TRUE));
+ else
+#endif
+ sendto_one(by,
+ "%s NOTICE %s%s :Connect to host %s failed: no C/N-lines",
+ NumServ(&me), NumNick(by), get_client_name(cptr, TRUE));
+ }
+ det_confs_butmask(cptr, 0);
+ close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ return (-1);
+ }
+ /*
+ * The socket has been connected or connect is in progress.
+ */
+ make_server(cptr);
+ if (by && IsUser(by))
+ {
+ sprintf_irc(cptr->serv->by, "%s%s", NumNick(by));
+ if (cptr->serv->user)
+ free_user(cptr->serv->user, NULL);
+ cptr->serv->user = by->user;
+ by->user->refcnt++;
+ }
+ else
+ {
+ *cptr->serv->by = '\0';
+ if (cptr->serv->user)
+ free_user(cptr->serv->user, NULL);
+ cptr->serv->user = NULL;
+ }
+ cptr->serv->up = &me;
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+ loc_clients[cptr->fd] = cptr;
+ cptr->acpt = &me;
+ SetConnecting(cptr);
+
+ get_sockhost(cptr, aconf->host);
+ Count_newunknown(nrof);
+ add_client_to_list(cptr);
+ hAddClient(cptr);
+ nextping = now;
+
+ return 0;
+}
+
+static struct sockaddr *connect_inet(aConfItem *aconf, aClient *cptr, int *lenp)
+{
+ static struct sockaddr_in server;
+ Reg3 struct hostent *hp;
+
+ /*
+ * Might as well get sockhost from here, the connection is attempted
+ * with it so if it fails its useless.
+ */
+ alarm(2);
+ cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
+ alarm(0);
+ if (cptr->fd == -1 && errno == EAGAIN)
+ {
+ sendto_ops("opening stream socket to server %s: No more sockets",
+ get_client_name(cptr, TRUE));
+ return NULL;
+ }
+ if (cptr->fd == -1)
+ {
+ report_error("opening stream socket to server %s: %s", cptr);
+ return NULL;
+ }
+ if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_ops("No more connections allowed (%s)", cptr->name);
+ return NULL;
+ }
+ mysk.sin_port = 0;
+ memset(&server, 0, sizeof(server));
+ server.sin_family = AF_INET;
+ get_sockhost(cptr, aconf->host);
+
+#ifdef VIRTUAL_HOST
+ mysk.sin_addr = vserv.sin_addr;
+#endif
+
+ /*
+ * Bind to a local IP# (with unknown port - let unix decide) so
+ * we have some chance of knowing the IP# that gets used for a host
+ * with more than one IP#.
+ */
+ /* No we don't bind it, not all OS's can handle connecting with
+ * an already bound socket, different ip# might occur anyway
+ * leading to a freezing select() on this side for some time.
+ * I had this on my Linux 1.1.88 --Run
+ */
+#ifdef VIRTUAL_HOST
+ /*
+ * No, we do bind it if we have virtual host support. If we don't
+ * explicitly bind it, it will default to IN_ADDR_ANY and we lose
+ * due to the other server not allowing our base IP --smg
+ */
+ if (bind(cptr->fd, (struct sockaddr *)&mysk, sizeof(mysk)) == -1)
+ {
+ report_error("error binding to local port for %s: %s", cptr);
+ return NULL;
+ }
+#endif
+
+ /*
+ * By this point we should know the IP# of the host listed in the
+ * conf line, whether as a result of the hostname lookup or the ip#
+ * being present instead. If we dont know it, then the connect fails.
+ */
+ if (isDigit(*aconf->host) && (aconf->ipnum.s_addr == INADDR_NONE))
+ aconf->ipnum.s_addr = inet_addr(aconf->host);
+ if (aconf->ipnum.s_addr == INADDR_NONE)
+ {
+ hp = cptr->hostp;
+ if (!hp)
+ {
+ Debug((DEBUG_FATAL, "%s: unknown host", aconf->host));
+ return NULL;
+ }
+ memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+ }
+ memcpy(&server.sin_addr, &aconf->ipnum, sizeof(struct in_addr));
+ memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
+#ifdef TESTNET
+ server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum) + 10000);
+#else
+ server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum));
+#endif
+ *lenp = sizeof(server);
+ return (struct sockaddr *)&server;
+}
+
+#ifdef UNIXPORT
+/*
+ * connect_unix
+ *
+ * Build a socket structure for cptr so that it can connet to the unix
+ * socket defined by the conf structure aconf.
+ */
+static struct sockaddr *connect_unix(aConfItem *aconf, aClient *cptr, int *lenp)
+{
+ static struct sockaddr_un sock;
+
+ alarm(2);
+ cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ alarm(0);
+ if (cptr->fd == -1 && errno == EAGAIN)
+ {
+ sendto_ops("Unix domain connect to host %s failed: No more sockets",
+ get_client_name(cptr, TRUE));
+ return NULL;
+ }
+ if (cptr->fd == -1)
+ {
+ report_error("Unix domain connect to host %s failed: %s", cptr);
+ return NULL;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_ops("No more connections allowed (%s)", cptr->name);
+ return NULL;
+ }
+
+ get_sockhost(cptr, aconf->host);
+ /* +2 needed for working Unix domain -- dl */
+ strncpy(sock.sun_path, aconf->host + 2, sizeof(sock.sun_path) - 1);
+ sock.sun_path[sizeof(sock.sun_path) - 1] = 0;
+ sock.sun_family = AF_UNIX;
+ *lenp = strlen(sock.sun_path) + 2;
+
+ SetUnixSock(cptr);
+ return (struct sockaddr *)&sock;
+}
+
+#endif
+
+/*
+ * Find the real hostname for the host running the server (or one which
+ * matches the server's name) and its primary IP#. Hostname is stored
+ * in the client structure passed as a pointer.
+ */
+void get_my_name(aClient *cptr, char *name, size_t len)
+{
+ static char tmp[HOSTLEN + 1];
+#if HAVE_UNAME
+ struct utsname utsn;
+#endif
+ struct hostent *hp;
+ char *cname = cptr->name;
+ size_t len2;
+
+ /*
+ * Setup local socket structure to use for binding to.
+ */
+ memset(&mysk, 0, sizeof(mysk));
+ mysk.sin_family = AF_INET;
+
+#if HAVE_UNAME
+ if (uname(&utsn) == -1)
+ return;
+ len2 = strlen(utsn.nodename);
+ if (len2 > len)
+ len2 = len;
+ strncpy(name, utsn.nodename, len2);
+#else /* HAVE_GETHOSTNAME */
+ if (gethostname(name, len) == -1)
+ return;
+#endif
+ name[len] = '\0';
+
+ /* Assume that a name containing '.' is a FQDN */
+ if (!strchr(name, '.'))
+ add_local_domain(name, len - strlen(name));
+
+ /*
+ * If hostname gives another name than cname, then check if there is
+ * a CNAME record for cname pointing to hostname. If so accept
+ * cname as our name. meLazy
+ */
+ if (BadPtr(cname))
+ return;
+ if (
+#ifndef NODNS
+ /* I don't have DNS while testing, this delays too much */
+ (hp = gethostbyname(cname)) ||
+#endif
+ (hp = gethostbyname(name)))
+ {
+ const char *hname;
+ int i = 0;
+
+ for (hname = hp->h_name; hname; hname = hp->h_aliases[i++])
+ {
+ strncpy(tmp, hname, sizeof(tmp) - 1);
+ add_local_domain(tmp, sizeof(tmp) - 1 - strlen(tmp));
+
+ /*
+ * Copy the matching name over and store the
+ * 'primary' IP# as 'myip' which is used
+ * later for making the right one is used
+ * for connecting to other hosts.
+ */
+ if (!strCasediff(me.name, tmp))
+ break;
+ }
+ if (strCasediff(me.name, tmp))
+ strncpy(name, hp->h_name, len);
+ else
+ strncpy(name, tmp, len);
+ memcpy(&mysk.sin_addr, hp->h_addr, sizeof(struct in_addr));
+ Debug((DEBUG_DEBUG, "local name is %s", get_client_name(&me, TRUE)));
+ }
+ return;
+}
+
+/*
+ * Setup a UDP socket and listen for incoming packets
+ */
+int setup_ping(void)
+{
+ struct sockaddr_in from;
+ int on = 1;
+
+ memset(&from, 0, sizeof(from));
+#ifdef VIRTUAL_HOST
+ from.sin_addr = vserv.sin_addr;
+#else
+ from.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
+#ifdef TESTNET
+ from.sin_port = htons(atoi(UDP_PORT) + 10000);
+#else
+ from.sin_port = htons(atoi(UDP_PORT));
+#endif
+ from.sin_family = AF_INET;
+
+ if ((udpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ {
+ Debug((DEBUG_ERROR, "socket udp : %s", strerror(errno)));
+ return -1;
+ }
+ if (setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR,
+ (OPT_TYPE *)&on, sizeof(on)) == -1)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "setsockopt udp fd %d : %m", udpfd);
+#endif
+ Debug((DEBUG_ERROR, "setsockopt so_reuseaddr : %s", strerror(errno)));
+ close(udpfd);
+ udpfd = -1;
+ return -1;
+ }
+ on = 0;
+ setsockopt(udpfd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on));
+ if (bind(udpfd, (struct sockaddr *)&from, sizeof(from)) == -1)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "bind udp.%d fd %d : %m", from.sin_port, udpfd);
+#endif
+ Debug((DEBUG_ERROR, "bind : %s", strerror(errno)));
+ close(udpfd);
+ udpfd = -1;
+ return -1;
+ }
+ if (fcntl(udpfd, F_SETFL, FNDELAY) == -1)
+ {
+ Debug((DEBUG_ERROR, "fcntl fndelay : %s", strerror(errno)));
+ close(udpfd);
+ udpfd = -1;
+ return -1;
+ }
+ return udpfd;
+}
+
+/*
+ * max # of pings set to 15/sec.
+ */
+static void polludp(void)
+{
+ Reg1 char *s;
+ struct sockaddr_in from;
+ int n;
+ size_t fromlen = sizeof(from);
+ static time_t last = 0;
+ static int cnt = 0, mlen = 0;
+
+ /*
+ * find max length of data area of packet.
+ */
+ if (!mlen)
+ {
+ mlen = sizeof(readbuf) - strlen(me.name) - strlen(version);
+ mlen -= 6;
+ if (mlen < 0)
+ mlen = 0;
+ }
+ Debug((DEBUG_DEBUG, "udp poll"));
+
+ n = recvfrom(udpfd, readbuf, mlen, 0, (struct sockaddr *)&from, &fromlen);
+ if (now == last)
+ if (++cnt > 14)
+ return;
+ cnt = 0;
+ last = now;
+
+ if (n == -1)
+ {
+ if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
+ return;
+ else
+ {
+ report_error("udp port recvfrom (%s): %s", &me);
+ return;
+ }
+ }
+ ircstp->is_udp++;
+ if (n < 19)
+ return;
+
+ s = readbuf + n;
+ /*
+ * attach my name and version for the reply
+ */
+ *readbuf |= 1;
+ strcpy(s, me.name);
+ s += strlen(s) + 1;
+ strcpy(s, version);
+ s += strlen(s);
+ sendto(udpfd, readbuf, s - readbuf, 0,
+ (struct sockaddr *)&from, sizeof(from));
+ return;
+}
+
+/*
+ * do_dns_async
+ *
+ * Called when the fd returned from init_resolver() has been selected for
+ * reading.
+ */
+static void do_dns_async(void)
+{
+ static Link ln;
+ aClient *cptr;
+ aConfItem *aconf;
+ struct hostent *hp;
+
+ ln.flags = ASYNC_NONE;
+ hp = get_res((char *)&ln);
+
+ Debug((DEBUG_DNS, "%p = get_res(%d,%p)", hp, ln.flags, ln.value.cptr));
+
+ switch (ln.flags)
+ {
+ case ASYNC_NONE:
+ /*
+ * No reply was processed that was outstanding or had a client
+ * still waiting.
+ */
+ break;
+ case ASYNC_CLIENT:
+ if ((cptr = ln.value.cptr))
+ {
+ del_queries((char *)cptr);
+ ClearDNS(cptr);
+ if (!DoingAuth(cptr))
+ SetAccess(cptr);
+ cptr->hostp = hp;
+ }
+ break;
+ case ASYNC_CONNECT:
+ aconf = ln.value.aconf;
+ if (hp && aconf)
+ {
+ memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+ connect_server(aconf, NULL, hp);
+ }
+ else
+ sendto_ops("Connect to %s failed: host lookup",
+ (aconf) ? aconf->host : "unknown");
+ break;
+ case ASYNC_PING:
+ cptr = ln.value.cptr;
+ del_queries((char *)cptr);
+ if (hp)
+ {
+ memcpy(&cptr->ip, hp->h_addr, sizeof(struct in_addr));
+ if (ping_server(cptr) == -1)
+ end_ping(cptr);
+ }
+ else
+ {
+ sendto_ops("Udp ping to %s failed: host lookup", cptr->sockhost);
+ end_ping(cptr);
+ }
+ break;
+ case ASYNC_CONF:
+ aconf = ln.value.aconf;
+ if (hp && aconf)
+ memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+ break;
+ default:
+ break;
+ }
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_conf.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/socket.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#include <sys/stat.h>
+#ifdef R_LINES
+#include <signal.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "opercmds.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "class.h"
+#include "s_misc.h"
+#include "match.h"
+#include "common.h"
+#include "s_err.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "crule.h"
+#include "res.h"
+#include "support.h"
+#include "parse.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "IPcheck.h"
+#include "hash.h"
+#include "fileio.h"
+
+RCSTAG_CC("$Id$");
+
+static int check_time_interval(char *, char *);
+static int lookup_confhost(aConfItem *);
+static int is_comment(char *);
+static void killcomment(aClient *sptr, char *parv, char *filename);
+
+aConfItem *conf = NULL;
+aGline *gline = NULL;
+aMotdItem *motd = NULL;
+aMotdItem *rmotd = NULL;
+atrecord *tdata = NULL;
+struct tm motd_tm;
+
+/*
+ * field breakup for ircd.conf file.
+ */
+static char *gfline = NULL;
+char *getfield(char *newline, char fs)
+{
+ char *end, *field;
+
+ if (newline)
+ gfline = newline;
+
+ if (gfline == NULL)
+ return NULL;
+
+ end = field = gfline;
+
+ if (fs != ':')
+ {
+ if (*end == fs)
+ ++end;
+ else
+ fs = ':';
+ }
+ do
+ {
+ while (*end != fs)
+ {
+ if (!*end)
+ {
+ end = NULL;
+ break;
+ }
+ ++end;
+ }
+ }
+ while (end && fs != ':' && *++end != ':' && *end != '\n');
+
+ if (end == NULL)
+ {
+ gfline = NULL;
+ if ((end = strchr(field, '\n')) == NULL)
+ end = field + strlen(field);
+ }
+ else
+ gfline = end + 1;
+
+ *end = '\0';
+
+ return field;
+}
+
+/*
+ * Remove all conf entries from the client except those which match
+ * the status field mask.
+ */
+void det_confs_butmask(aClient *cptr, int mask)
+{
+ Reg1 Link *tmp, *tmp2;
+
+ for (tmp = cptr->confs; tmp; tmp = tmp2)
+ {
+ tmp2 = tmp->next;
+ if ((tmp->value.aconf->status & mask) == 0)
+ detach_conf(cptr, tmp->value.aconf);
+ }
+}
+
+/*
+ * Find the first (best) I line to attach.
+ */
+enum AuthorizationCheckResult attach_Iline(aClient *cptr, struct hostent *hp,
+ char *sockhost)
+{
+ Reg1 aConfItem *aconf;
+ Reg3 const char *hname;
+ Reg4 int i;
+ static char uhost[HOSTLEN + USERLEN + 3];
+ static char fullname[HOSTLEN + 1];
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ {
+ if (aconf->status != CONF_CLIENT)
+ continue;
+ if (aconf->port && aconf->port != cptr->acpt->port)
+ continue;
+ if (!aconf->host || !aconf->name)
+ continue;
+ if (hp)
+ for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++])
+ {
+ size_t fullnamelen = 0;
+ size_t label_count = 0;
+ int error;
+
+ strncpy(fullname, hname, HOSTLEN);
+ fullname[HOSTLEN] = 0;
+
+ /*
+ * Disallow a hostname label to contain anything but a [-a-zA-Z0-9].
+ * It may not start or end on a '.'.
+ * A label may not end on a '-', the maximum length of a label is
+ * 63 characters.
+ * On top of that (which seems to be the RFC) we demand that the
+ * top domain does not contain any digits.
+ */
+ error = (*hname == '.') ? 1 : 0; /* May not start with a '.' */
+ if (!error)
+ {
+ char *p;
+ for (p = fullname; *p; ++p, ++fullnamelen)
+ {
+ if (*p == '.')
+ {
+ if (p[-1] == '-' /* Label may not end on '-' */
+ || p[1] == 0) /* May not end on a '.' */
+ {
+ error = 1;
+ break;
+ }
+ label_count = 0;
+ error = 0; /* Was not top domain */
+ continue;
+ }
+ if (++label_count > 63) /* Label not longer then 63 */
+ {
+ error = 1;
+ break;
+ }
+ if (*p >= '0' && *p <= '9')
+ {
+ error = 1; /* In case this is top domain */
+ continue;
+ }
+ if (!(*p >= 'a' && *p <= 'z')
+ && !(*p >= 'A' && *p <= 'Z') && *p != '-')
+ {
+ error = 1;
+ break;
+ }
+ }
+ }
+ if (error)
+ {
+ hp = NULL;
+ break;
+ }
+
+ add_local_domain(fullname, HOSTLEN - fullnamelen);
+ Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname));
+ if (strchr(aconf->name, '@'))
+ {
+ strcpy(uhost, cptr->username);
+ strcat(uhost, "@");
+ }
+ else
+ *uhost = '\0';
+ strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
+ uhost[sizeof(uhost) - 1] = 0;
+ if (!match(aconf->name, uhost))
+ {
+ if (strchr(uhost, '@'))
+ cptr->flags |= FLAGS_DOID;
+ goto attach_iline;
+ }
+ }
+
+ if (strchr(aconf->host, '@'))
+ {
+ strncpy(uhost, cptr->username, sizeof(uhost) - 2);
+ uhost[sizeof(uhost) - 2] = 0;
+ strcat(uhost, "@");
+ }
+ else
+ *uhost = '\0';
+ strncat(uhost, sockhost, sizeof(uhost) - 1 - strlen(uhost));
+ uhost[sizeof(uhost) - 1] = 0;
+ if (match(aconf->host, uhost))
+ continue;
+ if (strchr(uhost, '@'))
+ cptr->flags |= FLAGS_DOID;
+ if (hp && hp->h_name)
+ {
+ strncpy(uhost, hp->h_name, HOSTLEN);
+ uhost[HOSTLEN] = 0;
+ add_local_domain(uhost, HOSTLEN - strlen(uhost));
+ }
+ attach_iline:
+ get_sockhost(cptr, uhost);
+
+ if (aconf->passwd)
+ {
+ if (isDigit(*aconf->passwd) && !aconf->passwd[1]) /* Special case: exactly one digit */
+ {
+ /* Refuse connections when there are already <digit> clients connected with the same IP number */
+ unsigned short nr = *aconf->passwd - '0';
+ if (IPcheck_nr(cptr) > nr)
+ return ACR_TOO_MANY_FROM_IP; /* Already got nr with that ip# */
+ }
+#ifdef USEONE
+ else if (!strcmp(aconf->passwd, "ONE"))
+ {
+ for (i = highest_fd; i >= 0; i--)
+ if (loc_clients[i] && MyUser(loc_clients[i]) &&
+ loc_clients[i]->ip.s_addr == cptr->ip.s_addr)
+ return ACR_TOO_MANY_FROM_IP; /* Already got one with that ip# */
+ }
+#endif
+ }
+ return attach_conf(cptr, aconf);
+ }
+ return ACR_NO_AUTHORIZATION;
+}
+
+/*
+ * Find the single N line and return pointer to it (from list).
+ * If more than one then return NULL pointer.
+ */
+aConfItem *count_cnlines(Link *lp)
+{
+ Reg1 aConfItem *aconf, *cline = NULL, *nline = NULL;
+
+ for (; lp; lp = lp->next)
+ {
+ aconf = lp->value.aconf;
+ if (!(aconf->status & CONF_SERVER_MASK))
+ continue;
+ if (aconf->status == CONF_CONNECT_SERVER && !cline)
+ cline = aconf;
+ else if (aconf->status == CONF_NOCONNECT_SERVER && !nline)
+ nline = aconf;
+ }
+ return nline;
+}
+
+/*
+ * detach_conf
+ *
+ * Disassociate configuration from the client.
+ */
+int detach_conf(aClient *cptr, aConfItem *aconf)
+{
+ Reg1 Link **lp, *tmp;
+
+ lp = &(cptr->confs);
+
+ while (*lp)
+ {
+ if ((*lp)->value.aconf == aconf)
+ {
+ if (aconf && (aconf->confClass)
+ && (aconf->status & CONF_CLIENT_MASK) && ConfLinks(aconf) > 0)
+ --ConfLinks(aconf);
+ if (aconf && !--aconf->clients && IsIllegal(aconf))
+ free_conf(aconf);
+ tmp = *lp;
+ *lp = tmp->next;
+ free_link(tmp);
+ return 0;
+ }
+ else
+ lp = &((*lp)->next);
+ }
+ return -1;
+}
+
+static int is_attached(aConfItem *aconf, aClient *cptr)
+{
+ Reg1 Link *lp;
+
+ for (lp = cptr->confs; lp; lp = lp->next)
+ if (lp->value.aconf == aconf)
+ break;
+
+ return (lp) ? 1 : 0;
+}
+
+/*
+ * attach_conf
+ *
+ * Associate a specific configuration entry to a *local*
+ * client (this is the one which used in accepting the
+ * connection). Note, that this automaticly changes the
+ * attachment if there was an old one...
+ */
+enum AuthorizationCheckResult attach_conf(aClient *cptr, aConfItem *aconf)
+{
+ Reg1 Link *lp;
+
+ if (is_attached(aconf, cptr))
+ return ACR_ALREADY_AUTHORIZED;
+ if (IsIllegal(aconf))
+ return ACR_NO_AUTHORIZATION;
+ if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
+ ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
+ return ACR_TOO_MANY_IN_CLASS; /* Use this for printing error message */
+ lp = make_link();
+ lp->next = cptr->confs;
+ lp->value.aconf = aconf;
+ cptr->confs = lp;
+ aconf->clients++;
+ if (aconf->status & CONF_CLIENT_MASK)
+ ConfLinks(aconf)++;
+ return ACR_OK;
+}
+
+aConfItem *find_admin(void)
+{
+ Reg1 aConfItem *aconf;
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status & CONF_ADMIN)
+ break;
+
+ return (aconf);
+}
+
+aConfItem *find_me(void)
+{
+ Reg1 aConfItem *aconf;
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status & CONF_ME)
+ break;
+
+ return (aconf);
+}
+
+/*
+ * attach_confs
+ *
+ * Attach a CONF line to a client if the name passed matches that for
+ * the conf file (for non-C/N lines) or is an exact match (C/N lines
+ * only). The difference in behaviour is to stop C:*::* and N:*::*.
+ */
+aConfItem *attach_confs(aClient *cptr, const char *name, int statmask)
+{
+ Reg1 aConfItem *tmp;
+ aConfItem *first = NULL;
+ int len = strlen(name);
+
+ if (!name || len > HOSTLEN)
+ return NULL;
+ for (tmp = conf; tmp; tmp = tmp->next)
+ {
+ if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+ ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0) &&
+ tmp->name && !match(tmp->name, name))
+ {
+ if (attach_conf(cptr, tmp) == ACR_OK && !first)
+ first = tmp;
+ }
+ else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+ (tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
+ tmp->name && !strCasediff(tmp->name, name))
+ {
+ if (attach_conf(cptr, tmp) == ACR_OK && !first)
+ first = tmp;
+ }
+ }
+ return (first);
+}
+
+/*
+ * Added for new access check meLazy
+ */
+aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask)
+{
+ Reg1 aConfItem *tmp;
+ aConfItem *first = NULL;
+ int len = strlen(host);
+
+ if (!host || len > HOSTLEN)
+ return NULL;
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ {
+ if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+ (tmp->status & CONF_SERVER_MASK) == 0 &&
+ (!tmp->host || match(tmp->host, host) == 0))
+ {
+ if (attach_conf(cptr, tmp) == ACR_OK && !first)
+ first = tmp;
+ }
+ else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+ (tmp->status & CONF_SERVER_MASK) &&
+ (tmp->host && strCasediff(tmp->host, host) == 0))
+ {
+ if (attach_conf(cptr, tmp) == ACR_OK && !first)
+ first = tmp;
+ }
+ }
+ return (first);
+}
+
+/*
+ * find a conf entry which matches the hostname and has the same name.
+ */
+aConfItem *find_conf_exact(char *name, char *user, char *host, int statmask)
+{
+ Reg1 aConfItem *tmp;
+ char userhost[USERLEN + HOSTLEN + 3];
+
+ sprintf_irc(userhost, "%s@%s", user, host);
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ {
+ if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
+ strCasediff(tmp->name, name))
+ continue;
+ /*
+ * Accept if the *real* hostname (usually sockecthost)
+ * socket host) matches *either* host or name field
+ * of the configuration.
+ */
+ if (match(tmp->host, userhost))
+ continue;
+ if (tmp->status & (CONF_OPERATOR | CONF_LOCOP))
+ {
+ if (tmp->clients < MaxLinks(tmp->confClass))
+ return tmp;
+ else
+ continue;
+ }
+ else
+ return tmp;
+ }
+ return NULL;
+}
+
+aConfItem *find_conf(Link *lp, const char *name, int statmask)
+{
+ Reg1 aConfItem *tmp;
+ int namelen = name ? strlen(name) : 0;
+
+ if (namelen > HOSTLEN)
+ return (aConfItem *)0;
+
+ for (; lp; lp = lp->next)
+ {
+ tmp = lp->value.aconf;
+ if ((tmp->status & statmask) &&
+ (((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
+ tmp->name && !strCasediff(tmp->name, name)) ||
+ ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0 &&
+ tmp->name && !match(tmp->name, name))))
+ return tmp;
+ }
+ return NULL;
+}
+
+/*
+ * Added for new access check meLazy
+ */
+aConfItem *find_conf_host(Link *lp, char *host, int statmask)
+{
+ Reg1 aConfItem *tmp;
+ int hostlen = host ? strlen(host) : 0;
+
+ if (hostlen > HOSTLEN || BadPtr(host))
+ return (aConfItem *)NULL;
+ for (; lp; lp = lp->next)
+ {
+ tmp = lp->value.aconf;
+ if (tmp->status & statmask &&
+ (!(tmp->status & CONF_SERVER_MASK || tmp->host) ||
+ (tmp->host && !match(tmp->host, host))))
+ return tmp;
+ }
+ return NULL;
+}
+
+/*
+ * find_conf_ip
+ *
+ * Find a conf line using the IP# stored in it to search upon.
+ * Added 1/8/92 by Avalon.
+ */
+aConfItem *find_conf_ip(Link *lp, char *ip, char *user, int statmask)
+{
+ Reg1 aConfItem *tmp;
+ Reg2 char *s;
+
+ for (; lp; lp = lp->next)
+ {
+ tmp = lp->value.aconf;
+ if (!(tmp->status & statmask))
+ continue;
+ s = strchr(tmp->host, '@');
+ *s = '\0';
+ if (match(tmp->host, user))
+ {
+ *s = '@';
+ continue;
+ }
+ *s = '@';
+ if (!memcmp(&tmp->ipnum, ip, sizeof(struct in_addr)))
+ return tmp;
+ }
+ return NULL;
+}
+
+/*
+ * find_conf_entry
+ *
+ * - looks for a match on all given fields.
+ */
+static aConfItem *find_conf_entry(aConfItem *aconf, unsigned int mask)
+{
+ Reg1 aConfItem *bconf;
+
+ for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next)
+ {
+ if (!(bconf->status & mask) || (bconf->port != aconf->port))
+ continue;
+
+ if ((BadPtr(bconf->host) && !BadPtr(aconf->host)) ||
+ (BadPtr(aconf->host) && !BadPtr(bconf->host)))
+ continue;
+ if (!BadPtr(bconf->host) && strCasediff(bconf->host, aconf->host))
+ continue;
+
+ if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) ||
+ (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd)))
+ continue;
+ if (!BadPtr(bconf->passwd) && (!isDigit(*bconf->passwd) || bconf->passwd[1])
+#ifdef USEONE
+ && strCasediff(bconf->passwd, "ONE")
+#endif
+ && strCasediff(bconf->passwd, aconf->passwd))
+ continue;
+
+ if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) ||
+ (BadPtr(aconf->name) && !BadPtr(bconf->name)))
+ continue;
+ if (!BadPtr(bconf->name) && strCasediff(bconf->name, aconf->name))
+ continue;
+ break;
+ }
+ return bconf;
+}
+
+/*
+ * rehash
+ *
+ * Actual REHASH service routine. Called with sig == 0 if it has been called
+ * as a result of an operator issuing this command, else assume it has been
+ * called as a result of the server receiving a HUP signal.
+ */
+int rehash(aClient *cptr, int sig)
+{
+ Reg1 aConfItem **tmp = &conf, *tmp2;
+ Reg2 aConfClass *cltmp;
+ Reg1 aClient *acptr;
+ Reg2 aMotdItem *temp;
+ Reg2 int i;
+ int ret = 0, found_g;
+
+ if (sig == 1)
+ sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
+
+ for (i = 0; i <= highest_fd; i++)
+ if ((acptr = loc_clients[i]) && !IsMe(acptr))
+ {
+ /*
+ * Nullify any references from client structures to
+ * this host structure which is about to be freed.
+ * Could always keep reference counts instead of
+ * this....-avalon
+ */
+ acptr->hostp = NULL;
+ }
+
+ while ((tmp2 = *tmp))
+ if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT)
+ {
+ /*
+ * Configuration entry is still in use by some
+ * local clients, cannot delete it--mark it so
+ * that it will be deleted when the last client
+ * exits...
+ */
+ if (!(tmp2->status & (CONF_LISTEN_PORT | CONF_CLIENT)))
+ {
+ *tmp = tmp2->next;
+ tmp2->next = NULL;
+ }
+ else
+ tmp = &tmp2->next;
+ tmp2->status |= CONF_ILLEGAL;
+ }
+ else
+ {
+ *tmp = tmp2->next;
+ /* free expression trees of connect rules */
+ if ((tmp2->status & (CONF_CRULEALL | CONF_CRULEAUTO)) &&
+ (tmp2->passwd != NULL))
+ crule_free(&(tmp2->passwd));
+ free_conf(tmp2);
+ }
+
+ /*
+ * We don't delete the class table, rather mark all entries
+ * for deletion. The table is cleaned up by check_class(). - avalon
+ */
+ for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp))
+ MarkDelete(cltmp);
+
+ /*
+ * delete the juped nicks list
+ */
+ clearNickJupes();
+
+ if (sig != 2)
+ flush_cache();
+ if (initconf(0) == -1) /* This calls check_class(), */
+ check_class(); /* unless it fails */
+ close_listeners();
+
+ /*
+ * Flush out deleted I and P lines although still in use.
+ */
+ for (tmp = &conf; (tmp2 = *tmp);)
+ if (!(tmp2->status & CONF_ILLEGAL))
+ tmp = &tmp2->next;
+ else
+ {
+ *tmp = tmp2->next;
+ tmp2->next = NULL;
+ if (!tmp2->clients)
+ free_conf(tmp2);
+ }
+
+ for (i = 0; i <= highest_fd; i++) {
+ if ((acptr = loc_clients[i]) && !IsMe(acptr)) {
+ if (IsServer(acptr)) {
+ det_confs_butmask(acptr,
+ ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
+ attach_confs(acptr, acptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
+ }
+ if ((found_g = find_kill(acptr))) {
+ sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
+ found_g == -2 ? "G-line active for %s" : "K-line active for %s",
+ get_client_name(acptr, FALSE));
+ if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
+ "K-lined") == CPTR_KILLED)
+ ret = CPTR_KILLED;
+ }
+#if defined(R_LINES) && defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN)
+ if (find_restrict(acptr)) {
+ sendto_ops("Restricting %s, closing lp", get_client_name(acptr, FALSE));
+ if (exit_client(cptr, acptr, &me, "R-lined") == CPTR_KILLED)
+ ret = CPTR_KILLED;
+ }
+#endif
+ }
+ }
+
+ /* free old motd structs */
+ while (motd) {
+ temp = motd->next;
+ RunFree(motd);
+ motd = temp;
+ }
+ while (rmotd) {
+ temp = rmotd->next;
+ RunFree(rmotd);
+ rmotd = temp;
+ }
+ /* reload motd files */
+ read_tlines();
+ rmotd = read_motd(RPATH);
+ motd = read_motd(MPATH);
+
+ return ret;
+}
+
+/*
+ * initconf
+ *
+ * Read configuration file.
+ *
+ * returns -1, if file cannot be opened
+ * 0, if file opened
+ */
+
+#define MAXCONFLINKS 150
+
+unsigned short server_port;
+
+int initconf(int opt)
+{
+ static char quotes[9][2] = {
+ {'b', '\b'},
+ {'f', '\f'},
+ {'n', '\n'},
+ {'r', '\r'},
+ {'t', '\t'},
+ {'v', '\v'},
+ {'\\', '\\'},
+ {0, 0}
+ };
+ Reg1 char *tmp, *s;
+ FBFILE *file;
+ int i;
+ char line[512];
+ int ccount = 0, ncount = 0;
+ aConfItem *aconf = NULL;
+
+ Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
+ if (NULL == (file = fbopen(configfile, "r")))
+ {
+ return -1;
+ }
+ while (fbgets(line, sizeof(line) - 1, file))
+ {
+ if ((tmp = strchr(line, '\n')))
+ *tmp = '\0';
+ /*
+ * Do quoting of characters and # detection.
+ */
+ for (tmp = line; *tmp; tmp++)
+ {
+ if (*tmp == '\\')
+ {
+ for (i = 0; quotes[i][0]; i++)
+ if (quotes[i][0] == *(tmp + 1))
+ {
+ *tmp = quotes[i][1];
+ break;
+ }
+ if (!quotes[i][0])
+ *tmp = *(tmp + 1);
+ if (!*(tmp + 1))
+ break;
+ else
+ for (s = tmp; (*s = *(s + 1)); s++)
+ ;
+ }
+ else if (*tmp == '#')
+ *tmp = '\0';
+ }
+ if (!*line || line[0] == '#' || line[0] == '\n' ||
+ line[0] == ' ' || line[0] == '\t')
+ continue;
+ /* Could we test if it's conf line at all? -Vesa */
+ if (line[1] != ':')
+ {
+ Debug((DEBUG_ERROR, "Bad config line: %s", line));
+ continue;
+ }
+ if (aconf)
+ free_conf(aconf);
+ aconf = make_conf();
+
+ tmp = getfield(line, ':');
+ if (!tmp)
+ continue;
+ switch (*tmp)
+ {
+ case 'A': /* Name, e-mail address of administrator */
+ case 'a': /* of this server. */
+ aconf->status = CONF_ADMIN;
+ break;
+ case 'C': /* Server where I should try to connect */
+ case 'c': /* in case of lp failures */
+ ccount++;
+ aconf->status = CONF_CONNECT_SERVER;
+ break;
+ /* Connect rule */
+ case 'D':
+ aconf->status = CONF_CRULEALL;
+ break;
+ /* Connect rule - autos only */
+ case 'd':
+ aconf->status = CONF_CRULEAUTO;
+ break;
+ case 'H': /* Hub server line */
+ case 'h':
+ aconf->status = CONF_HUB;
+ break;
+ case 'I': /* Just plain normal irc client trying */
+ case 'i': /* to connect me */
+ aconf->status = CONF_CLIENT;
+ break;
+ case 'K': /* Kill user line on irc.conf */
+ aconf->status = CONF_KILL;
+ break;
+ case 'k': /* Kill user line based on IP in ircd.conf */
+ aconf->status = CONF_IPKILL;
+ break;
+ /* Operator. Line should contain at least */
+ /* password and host where connection is */
+ case 'L': /* guaranteed leaf server */
+ case 'l':
+ aconf->status = CONF_LEAF;
+ break;
+ /* Me. Host field is name used for this host */
+ /* and port number is the number of the port */
+ case 'M':
+ case 'm':
+ aconf->status = CONF_ME;
+ break;
+ case 'N': /* Server where I should NOT try to */
+ case 'n': /* connect in case of lp failures */
+ /* but which tries to connect ME */
+ ++ncount;
+ aconf->status = CONF_NOCONNECT_SERVER;
+ break;
+ case 'O':
+ aconf->status = CONF_OPERATOR;
+ break;
+ /* Local Operator, (limited privs --SRB) */
+ case 'o':
+ aconf->status = CONF_LOCOP;
+ break;
+ case 'P': /* listen port line */
+ case 'p':
+ aconf->status = CONF_LISTEN_PORT;
+ break;
+#ifdef R_LINES
+ case 'R': /* extended K line */
+ case 'r': /* Offers more options of how to restrict */
+ aconf->status = CONF_RESTRICT;
+ break;
+#endif
+ case 'T': /* print out different motd's */
+ case 't': /* based on hostmask */
+ aconf->status = CONF_TLINES;
+ break;
+ case 'U': /* Underworld server, allowed to hack modes */
+ case 'u': /* *Every* server on the net must define the same !!! */
+ aconf->status = CONF_UWORLD;
+ break;
+ case 'Y':
+ case 'y':
+ aconf->status = CONF_CLASS;
+ break;
+ default:
+ Debug((DEBUG_ERROR, "Error in config file: %s", line));
+ break;
+ }
+ if (IsIllegal(aconf))
+ continue;
+
+ for (;;) /* Fake loop, that I can use break here --msa */
+ {
+ if ((tmp = getfield(NULL, ':')) == NULL)
+ break;
+ DupString(aconf->host, tmp);
+ if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
+ || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
+ break;
+ DupString(aconf->passwd, tmp);
+ if ((tmp = getfield(NULL, ':')) == NULL)
+ break;
+ DupString(aconf->name, tmp);
+ if ((tmp = getfield(NULL, ':')) == NULL)
+ break;
+ aconf->port = atoi(tmp);
+ tmp = getfield(NULL, ':');
+ if (aconf->status & CONF_ME)
+ {
+ server_port = aconf->port;
+ if (!tmp)
+ {
+ Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
+ "assigned to you by routing-com, behind the port number!\n"));
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, "Your M: line must have the Numeric, "
+ "assigned to you by routing-com, behind the port number!\n");
+#endif
+ exit(-1);
+ }
+ SetYXXServerName(&me, atoi(tmp)); /* Our Numeric Nick */
+ }
+ else if (tmp)
+ aconf->confClass = find_class(atoi(tmp));
+ break;
+ }
+ /*
+ * If conf line is a class definition, create a class entry
+ * for it and make the conf_line illegal and delete it.
+ */
+ if (aconf->status & CONF_CLASS)
+ {
+ add_class(atoi(aconf->host), atoi(aconf->passwd),
+ atoi(aconf->name), aconf->port, tmp ? atoi(tmp) : 0);
+ continue;
+ }
+ /*
+ * Associate each conf line with a class by using a pointer
+ * to the correct class record. -avalon
+ */
+ if (aconf->status & (CONF_CLIENT_MASK | CONF_LISTEN_PORT))
+ {
+ if (aconf->confClass == 0)
+ aconf->confClass = find_class(0);
+ }
+ if (aconf->status & (CONF_LISTEN_PORT | CONF_CLIENT))
+ {
+ aConfItem *bconf;
+
+ if ((bconf = find_conf_entry(aconf, aconf->status)))
+ {
+ delist_conf(bconf);
+ bconf->status &= ~CONF_ILLEGAL;
+ if (aconf->status == CONF_CLIENT)
+ {
+ char *passwd = bconf->passwd;
+ bconf->passwd = aconf->passwd;
+ aconf->passwd = passwd;
+ ConfLinks(bconf) -= bconf->clients;
+ bconf->confClass = aconf->confClass;
+ if (bconf->confClass)
+ ConfLinks(bconf) += bconf->clients;
+ }
+ free_conf(aconf);
+ aconf = bconf;
+ }
+ else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
+ add_listener(aconf);
+ }
+ if (aconf->status & CONF_SERVER_MASK)
+ if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
+ !aconf->host || strchr(aconf->host, '*') ||
+ strchr(aconf->host, '?') || !aconf->name)
+ continue;
+
+ if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
+ if (!strchr(aconf->host, '@') && *aconf->host != '/')
+ {
+ char *newhost;
+ int len = 3; /* *@\0 = 3 */
+
+ len += strlen(aconf->host);
+ newhost = (char *)RunMalloc(len);
+ sprintf_irc(newhost, "*@%s", aconf->host);
+ RunFree(aconf->host);
+ aconf->host = newhost;
+ }
+ if (aconf->status & CONF_SERVER_MASK)
+ {
+ if (BadPtr(aconf->passwd))
+ continue;
+ else if (!(opt & BOOT_QUICK))
+ lookup_confhost(aconf);
+ }
+
+ /* Create expression tree from connect rule...
+ * If there's a parsing error, nuke the conf structure */
+ if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO))
+ {
+ RunFree(aconf->passwd);
+ if ((aconf->passwd = (char *)crule_parse(aconf->name)) == NULL)
+ {
+ free_conf(aconf);
+ aconf = NULL;
+ continue;
+ }
+ }
+
+ /*
+ * Own port and name cannot be changed after the startup.
+ * (or could be allowed, but only if all links are closed first).
+ * Configuration info does not override the name and port
+ * if previously defined. Note, that "info"-field can be
+ * changed by "/rehash".
+ */
+ if (aconf->status == CONF_ME)
+ {
+ strncpy(me.info, aconf->name, sizeof(me.info) - 1);
+ if (me.name[0] == '\0' && aconf->host[0])
+ strncpy(me.name, aconf->host, sizeof(me.name) - 1);
+ if (portnum == 0)
+ portnum = aconf->port;
+ }
+
+ /*
+ * Juped nicks are listed in the 'password' field of U:lines,
+ * the list is comma separated and might be empty and/or contain
+ * empty elements... the only limit is that it MUST be shorter
+ * than 512 chars, or they will be cutted out :)
+ */
+ if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd))
+ addNickJupes(aconf->passwd);
+
+ if (aconf->status & CONF_ADMIN)
+ if (!aconf->host || !aconf->passwd || !aconf->name)
+ {
+ Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n"));
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, "Your A: line must have 4 fields!\n");
+#endif
+ exit(-1);
+ }
+
+ collapse(aconf->host);
+ collapse(aconf->name);
+ Debug((DEBUG_NOTICE,
+ "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)",
+ aconf->status, aconf->host, aconf->passwd,
+ aconf->name, aconf->port, aconf->confClass));
+ aconf->next = conf;
+ conf = aconf;
+ aconf = NULL;
+ }
+ if (aconf)
+ free_conf(aconf);
+ fbclose(file);
+ check_class();
+ nextping = nextconnect = now;
+ return 0;
+}
+
+/*
+ * lookup_confhost
+ *
+ * Do (start) DNS lookups of all hostnames in the conf line and convert
+ * an IP addresses in a.b.c.d number for to IP#s.
+ */
+static int lookup_confhost(aConfItem *aconf)
+{
+ Reg2 char *s;
+ Reg3 struct hostent *hp;
+ Link ln;
+
+ if (BadPtr(aconf->host) || BadPtr(aconf->name))
+ goto badlookup;
+ if ((s = strchr(aconf->host, '@')))
+ s++;
+ else
+ s = aconf->host;
+ /*
+ * Do name lookup now on hostnames given and store the
+ * ip numbers in conf structure.
+ */
+ if (!isAlpha(*s) && !isDigit(*s))
+ goto badlookup;
+
+ /*
+ * Prepare structure in case we have to wait for a
+ * reply which we get later and store away.
+ */
+ ln.value.aconf = aconf;
+ ln.flags = ASYNC_CONF;
+
+ if (isDigit(*s))
+ aconf->ipnum.s_addr = inet_addr(s);
+ else if ((hp = gethost_byname(s, &ln)))
+ memcpy(&(aconf->ipnum), hp->h_addr, sizeof(struct in_addr));
+
+ if (aconf->ipnum.s_addr == INADDR_NONE)
+ goto badlookup;
+ return 0;
+badlookup:
+ if (aconf->ipnum.s_addr == INADDR_NONE)
+ memset(&aconf->ipnum, 0, sizeof(struct in_addr));
+ Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
+ aconf->host, aconf->name));
+ return -1;
+}
+
+/* read_tlines
+ * Read info from T:lines into trecords which include the file
+ * timestamp, the hostmask, and the contents of the motd file
+ * -Ghostwolf 7sep97
+ */
+void read_tlines()
+{
+ aConfItem *tmp;
+ atrecord *temp, *last = NULL; /* Init. to avoid compiler warning */
+ aMotdItem *amotd;
+
+ /* Free the old trecords and the associated motd contents first */
+ while (tdata)
+ {
+ last = tdata->next;
+ while (tdata->tmotd)
+ {
+ amotd = tdata->tmotd->next;
+ RunFree(tdata->tmotd);
+ tdata->tmotd = amotd;
+ }
+ RunFree(tdata);
+ tdata = last;
+ }
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd)
+ {
+ temp = (atrecord *) RunMalloc(sizeof(atrecord));
+ if (!temp)
+ outofmemory();
+ temp->hostmask = tmp->host;
+ temp->tmotd = read_motd(tmp->passwd);
+ temp->tmotd_tm = motd_tm;
+ temp->next = NULL;
+ if (!tdata)
+ tdata = temp;
+ else
+ last->next = temp;
+ last = temp;
+ }
+}
+
+int find_kill(aClient *cptr)
+{
+ char reply[256], *host, *name;
+ aConfItem *tmp;
+ aGline *agline = NULL;
+
+ if (!cptr->user)
+ return 0;
+
+ host = cptr->sockhost;
+ name = cptr->user->username;
+
+ if (strlen(host) > (size_t)HOSTLEN ||
+ (name ? strlen(name) : 0) > (size_t)HOSTLEN)
+ return (0);
+
+ reply[0] = '\0';
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ /* Added a check against the user's IP address as well.
+ * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only
+ * if it's CONF_IPKILL, check the IP address as well (the && below will
+ * short circuit and the match won't even get run) -Kev
+ */
+ if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name &&
+ (match(tmp->host, host) == 0 ||
+ ((tmp->status == CONF_IPKILL) &&
+ match(tmp->host, inetntoa(cptr->ip)) == 0)) &&
+ (!name || match(tmp->name, name) == 0) &&
+ (!tmp->port || (tmp->port == cptr->acpt->port)))
+ {
+ /*
+ * Can short-circuit evaluation - not taking chances
+ * because check_time_interval destroys tmp->passwd
+ * - Mmmm
+ */
+ if (BadPtr(tmp->passwd))
+ break;
+ else if (is_comment(tmp->passwd))
+ break;
+ else if (check_time_interval(tmp->passwd, reply))
+ break;
+ }
+
+ if (reply[0])
+ sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name);
+ else if (tmp)
+ {
+ if (BadPtr(tmp->passwd))
+ sendto_one(cptr,
+ ":%s %d %s :Connection from your host is refused on this server.",
+ me.name, ERR_YOUREBANNEDCREEP, cptr->name);
+ else
+ {
+ if (*tmp->passwd == '"')
+ {
+ char *sbuf =
+ sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP,
+ cptr->name, &tmp->passwd[1]);
+ sbuf[-1] = '.'; /* Overwrite last quote with a dot */
+ sendbufto_one(cptr);
+ }
+ else if (*tmp->passwd == '!')
+ killcomment(cptr, cptr->name, &tmp->passwd[1]);
+ else
+#ifdef COMMENT_IS_FILE
+ killcomment(cptr, cptr->name, tmp->passwd);
+#else
+ sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
+ cptr->name, tmp->passwd);
+#endif
+ }
+ }
+
+ /* find active glines */
+ /* added a check against the user's IP address to find_gline() -Kev */
+ else if ((agline = find_gline(cptr, NULL)) && GlineIsActive(agline))
+ sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
+ cptr->name, agline->reason);
+ else
+ agline = NULL; /* if a gline was found, it was inactive */
+
+ return (tmp ? -1 : (agline ? -2 : 0));
+}
+
+#ifdef R_LINES
+/*
+ * find_restrict
+ *
+ * Works against host/name and calls an outside program
+ * to determine whether a client is allowed to connect. This allows
+ * more freedom to determine who is legal and who isn't, for example
+ * machine load considerations. The outside program is expected to
+ * return a reply line where the first word is either 'Y' or 'N' meaning
+ * "Yes Let them in" or "No don't let them in." If the first word
+ * begins with neither 'Y' or 'N' the default is to let the person on.
+ * It returns a value of 0 if the user is to be let through -Hoppie
+ */
+int find_restrict(aClient *cptr)
+{
+ aConfItem *tmp;
+ char reply[80], temprpl[80];
+ char *rplhold = reply, *host, *name, *s;
+ char rplchar = 'Y';
+ int pi[2], rc = 0, n;
+ FBFILE *file = NULL;
+
+ if (!cptr->user)
+ return 0;
+ name = cptr->user->username;
+ host = cptr->sockhost;
+ Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host));
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ {
+ if (tmp->status != CONF_RESTRICT ||
+ (tmp->host && host && match(tmp->host, host)) ||
+ (tmp->name && name && match(tmp->name, name)))
+ continue;
+
+ if (BadPtr(tmp->passwd))
+ {
+ sendto_ops("Program missing on R-line %s/%s, ignoring", name, host);
+ continue;
+ }
+
+ if (pipe(pi) == -1)
+ {
+ report_error("Error creating pipe for R-line %s: %s", &me);
+ return 0;
+ }
+ switch (rc = fork())
+ {
+ case -1:
+ report_error("Error forking for R-line %s: %s", &me);
+ return 0;
+ case 0:
+ {
+ Reg1 int i;
+
+ close(pi[0]);
+ for (i = 2; i < MAXCONNECTIONS; i++)
+ if (i != pi[1])
+ close(i);
+ if (pi[1] != 2)
+ dup2(pi[1], 2);
+ dup2(2, 1);
+ if (pi[1] != 2 && pi[1] != 1)
+ close(pi[1]);
+ execlp(tmp->passwd, tmp->passwd, name, host, 0);
+ exit(-1);
+ }
+ default:
+ close(pi[1]);
+ break;
+ }
+ *reply = '\0';
+ file = fdbopen(pi[0], "r");
+ while (fbgets(temprpl, sizeof(temprpl) - 1, file))
+ {
+ if ((s = strchr(temprpl, '\n')))
+ *s = '\0';
+ if (strlen(temprpl) + strlen(reply) < sizeof(reply) - 2)
+ sprintf_irc(rplhold, "%s %s", rplhold, temprpl);
+ else
+ {
+ sendto_ops("R-line %s/%s: reply too long!", name, host);
+ break;
+ }
+ }
+ fbclose(file);
+ kill(rc, SIGKILL); /* cleanup time */
+ wait(0);
+
+ rc = 0;
+ while (*rplhold == ' ')
+ rplhold++;
+ rplchar = *rplhold; /* Pull out the yes or no */
+ while (*rplhold != ' ')
+ rplhold++;
+ while (*rplhold == ' ')
+ rplhold++;
+ strcpy(reply, rplhold);
+ rplhold = reply;
+
+ if ((rc = (rplchar == 'n' || rplchar == 'N')))
+ break;
+ }
+ if (rc)
+ {
+ sendto_one(cptr, ":%s %d %s :Restriction: %s",
+ me.name, ERR_YOUREBANNEDCREEP, cptr->name, reply);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * output the reason for being k lined from a file - Mmmm
+ * sptr is server
+ * parv is the sender prefix
+ * filename is the file that is to be output to the K lined client
+ */
+static void killcomment(aClient *sptr, char *parv, char *filename)
+{
+ FBFILE *file = NULL;
+ char line[80];
+ Reg1 char *tmp;
+ struct stat sb;
+ struct tm *tm;
+
+ if (NULL == (file = fbopen(filename, "r")))
+ {
+ sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv);
+ sendto_one(sptr,
+ ":%s %d %s :Connection from your host is refused on this server.",
+ me.name, ERR_YOUREBANNEDCREEP, parv);
+ return;
+ }
+ fbstat(&sb, file);
+ tm = localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
+ while (fbgets(line, sizeof(line) - 1, file))
+ {
+ if ((tmp = strchr(line, '\n')))
+ *tmp = '\0';
+ if ((tmp = strchr(line, '\r')))
+ *tmp = '\0';
+ /* sendto_one(sptr,
+ * ":%s %d %s : %s.",
+ * me.name, ERR_YOUREBANNEDCREEP, parv,line); */
+ sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv, line);
+ }
+ sendto_one(sptr,
+ ":%s %d %s :Connection from your host is refused on this server.",
+ me.name, ERR_YOUREBANNEDCREEP, parv);
+ fbclose(file);
+ return;
+}
+
+/*
+ * is the K line field an interval or a comment? - Mmmm
+ */
+static int is_comment(char *comment)
+{
+ size_t i;
+ for (i = 0; i < strlen(comment); i++)
+ if ((comment[i] != ' ') && (comment[i] != '-')
+ && (comment[i] != ',') && ((comment[i] < '0') || (comment[i] > '9')))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * check against a set of time intervals
+ */
+static int check_time_interval(char *interval, char *reply)
+{
+ struct tm *tptr;
+ char *p;
+ int perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes;
+ int nowm, perm_min, perm_max;
+
+ tptr = localtime(&now);
+ nowm = tptr->tm_hour * 60 + tptr->tm_min;
+
+ while (interval)
+ {
+ p = strchr(interval, ',');
+ if (p)
+ *p = '\0';
+ if (sscanf(interval, "%2d%2d-%2d%2d", &perm_min_hours, &perm_min_minutes,
+ &perm_max_hours, &perm_max_minutes) != 4)
+ {
+ if (p)
+ *p = ',';
+ return (0);
+ }
+ if (p)
+ *(p++) = ',';
+ perm_min = 60 * perm_min_hours + perm_min_minutes;
+ perm_max = 60 * perm_max_hours + perm_max_minutes;
+ /*
+ * The following check allows intervals over midnight ...
+ */
+ if ((perm_min < perm_max)
+ ? (perm_min <= nowm && nowm <= perm_max)
+ : (perm_min <= nowm || nowm <= perm_max))
+ {
+ printf(reply,
+ ":%%s %%d %%s :%s %d:%02d to %d:%02d.",
+ "You are not allowed to connect from",
+ perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes);
+ return (ERR_YOUREBANNEDCREEP);
+ }
+ if ((perm_min < perm_max)
+ ? (perm_min <= nowm + 5 && nowm + 5 <= perm_max)
+ : (perm_min <= nowm + 5 || nowm + 5 <= perm_max))
+ {
+ sprintf_irc(reply, ":%%s %%d %%s :%d minute%s%s",
+ perm_min - nowm, (perm_min - nowm) > 1 ? "s " : " ",
+ "and you will be denied for further access");
+ return (ERR_YOUWILLBEBANNED);
+ }
+ interval = p;
+ }
+ return (0);
+}
+
+aMotdItem *read_motd(char *motdfile)
+{
+ FBFILE *file = NULL;
+ register aMotdItem *temp, *newmotd, *last;
+ struct stat sb;
+ char line[80];
+ register char *tmp;
+
+ if (NULL == (file = fbopen(motdfile, "r")))
+ {
+ Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno)));
+ return NULL;
+ }
+ if (-1 == fbstat(&sb, file))
+ {
+ return NULL;
+ }
+ newmotd = last = NULL;
+ motd_tm = *localtime((time_t *) & sb.st_mtime); /* NetBSD needs cast */
+ while (fbgets(line, sizeof(line) - 1, file))
+ {
+ if ((tmp = (char *)strchr(line, '\n')))
+ *tmp = '\0';
+ if ((tmp = (char *)strchr(line, '\r')))
+ *tmp = '\0';
+ temp = (aMotdItem *) RunMalloc(sizeof(aMotdItem));
+ if (!temp)
+ outofmemory();
+ strcpy(temp->line, line);
+ temp->next = NULL;
+ if (!newmotd)
+ newmotd = temp;
+ else
+ last->next = temp;
+ last = temp;
+ }
+ fbclose(file);
+ return newmotd;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_debug.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if defined(HPUX) && HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HPUX
+#include <sys/syscall.h>
+#define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b)
+#endif
+#if HAVE_GETRUSAGE
+#include <sys/resource.h>
+#else
+#if HAVE_TIMES
+#include <sys/times.h>
+#endif
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdarg.h>
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "hash.h"
+#include "send.h"
+#include "s_conf.h"
+#include "class.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "bsd.h"
+#include "whowas.h"
+#include "s_serv.h"
+#include "res.h"
+#include "channel.h"
+#include "numnicks.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+/*
+ * Option string. Must be before #ifdef DEBUGMODE.
+ */
+char serveropts[] = {
+#if BUFFERPOOL < 1000000
+ 'b',
+#if BUFFERPOOL > 99999
+ (char)('0' + (BUFFERPOOL/100000)),
+#endif
+#if BUFFERPOOL > 9999
+ (char)('0' + (BUFFERPOOL/10000) % 10),
+#endif
+ (char)('0' + (BUFFERPOOL/1000) % 10),
+#else
+ 'B',
+#if BUFFERPOOL > 99999999
+ (char)('0' + (BUFFERPOOL/100000000)),
+#endif
+#if BUFFERPOOL > 9999999
+ (char)('0' + (BUFFERPOOL/10000000) % 10),
+#endif
+ (char)('0' + (BUFFERPOOL/1000000) % 10),
+#endif
+#ifdef CHROOTDIR
+ 'c',
+#endif
+#ifdef CMDLINE_CONFIG
+ 'C',
+#endif
+#ifdef DO_ID
+ 'd',
+#endif
+#ifdef DEBUGMODE
+ 'D',
+#endif
+#ifdef LOCOP_REHASH
+ 'e',
+#endif
+#ifdef OPER_REHASH
+ 'E',
+#endif
+#ifdef HUB
+ 'H',
+#endif
+#if defined(SHOW_INVISIBLE_USERS) || defined(SHOW_ALL_INVISIBLE_USERS)
+#ifdef SHOW_ALL_INVISIBLE_USERS
+ 'I',
+#else
+ 'i',
+#endif
+#endif
+#ifdef OPER_KILL
+#ifdef LOCAL_KILL_ONLY
+ 'k',
+#else
+ 'K',
+#endif
+#endif
+#ifdef LEAST_IDLE
+ 'L',
+#endif
+#ifdef IDLE_FROM_MSG
+ 'M',
+#endif
+#ifdef USEONE
+ 'O',
+#endif
+#ifdef CRYPT_OPER_PASSWORD
+ 'p',
+#endif
+#ifdef CRYPT_LINK_PASSWORD
+ 'P',
+#endif
+#ifdef DEBUGMALLOC
+#ifdef MEMLEAKSTATS
+ 'Q',
+#else
+ 'q',
+#endif
+#endif
+#ifdef RELIABLE_CLOCK
+ 'R',
+#endif
+#ifdef LOCOP_RESTART
+ 's',
+#endif
+#ifdef OPER_RESTART
+ 'S',
+#endif
+#ifdef OPER_REMOTE
+ 't',
+#endif
+#if defined(USE_POLL) && defined(HAVE_POLL_H)
+ 'U',
+#endif
+#ifdef VIRTUAL_HOST
+ 'v',
+#endif
+#ifdef UNIXPORT
+ 'X',
+#endif
+#ifdef USE_SYSLOG
+ 'Y',
+#endif
+ '\0'
+};
+
+/* *INDENT-ON* */
+
+#ifdef DEBUGMODE
+static char debugbuf[1024];
+
+void vdebug(int level, const char *form, va_list vl)
+{
+ int err = errno;
+
+ if ((debuglevel >= 0) && (level <= debuglevel))
+ {
+ vsprintf(debugbuf, form, vl);
+ if (loc_clients[2])
+ {
+ loc_clients[2]->sendM++;
+ loc_clients[2]->sendB += strlen(debugbuf);
+ }
+ fprintf(stderr, "%s", debugbuf);
+ fputc('\n', stderr);
+ }
+ errno = err;
+}
+
+void debug(int level, const char *form, ...)
+{
+ va_list vl;
+ va_start(vl, form);
+ vdebug(level, form, vl);
+ va_end(vl);
+}
+
+/*
+ * This is part of the STATS replies. There is no offical numeric for this
+ * since this isnt an official command, in much the same way as HASH isnt.
+ * It is also possible that some systems wont support this call or have
+ * different field names for "struct rusage".
+ * -avalon
+ */
+void send_usage(aClient *cptr, char *nick)
+{
+
+#if HAVE_GETRUSAGE
+ struct rusage rus;
+ time_t secs, rup;
+#ifdef hz
+#define hzz hz
+#else
+#ifdef HZ
+#define hzz HZ
+#else
+ int hzz = 1;
+#ifdef HPUX
+ hzz = (int)sysconf(_SC_CLK_TCK);
+#endif
+#endif
+#endif
+
+ if (getrusage(RUSAGE_SELF, &rus) == -1)
+ {
+ if (MyUser(cptr) || Protocol(cptr->from) < 10)
+ sendto_one(cptr, ":%s NOTICE %s :Getruseage error: %s.",
+ me.name, nick, sys_errlist[errno]);
+ else
+ sendto_one(cptr, "%s NOTICE %s%s :Getruseage error: %s.",
+ NumServ(&me), NumNick(cptr), sys_errlist[errno]);
+ return;
+ }
+ secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
+ rup = now - me.since;
+ if (secs == 0)
+ secs = 1;
+
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+ sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %ld:%ld System %ld:%ld",
+#else
+ sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %d:%d System %d:%d",
+#endif
+ me.name, RPL_STATSDEBUG, nick, secs / 60, secs % 60,
+ rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
+ rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
+ sendto_one(cptr, ":%s %d %s :RSS %ld ShMem %ld Data %ld Stack %ld",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_maxrss,
+ rus.ru_ixrss / (rup * hzz), rus.ru_idrss / (rup * hzz),
+ rus.ru_isrss / (rup * hzz));
+ sendto_one(cptr, ":%s %d %s :Swaps %ld Reclaims %ld Faults %ld",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_nswap,
+ rus.ru_minflt, rus.ru_majflt);
+ sendto_one(cptr, ":%s %d %s :Block in %ld out %ld",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_inblock, rus.ru_oublock);
+ sendto_one(cptr, ":%s %d %s :Msg Rcv %ld Send %ld",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_msgrcv, rus.ru_msgsnd);
+ sendto_one(cptr, ":%s %d %s :Signals %ld Context Vol. %ld Invol %ld",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_nsignals,
+ rus.ru_nvcsw, rus.ru_nivcsw);
+#else /* HAVE_GETRUSAGE */
+#if HAVE_TIMES
+ struct tms tmsbuf;
+ time_t secs, mins;
+ int hzz = 1, ticpermin;
+ int umin, smin, usec, ssec;
+
+#ifdef HPUX
+ hzz = sysconf(_SC_CLK_TCK);
+#endif
+ ticpermin = hzz * 60;
+
+ umin = tmsbuf.tms_utime / ticpermin;
+ usec = (tmsbuf.tms_utime % ticpermin) / (float)hzz;
+ smin = tmsbuf.tms_stime / ticpermin;
+ ssec = (tmsbuf.tms_stime % ticpermin) / (float)hzz;
+ secs = usec + ssec;
+ mins = (secs / 60) + umin + smin;
+ secs %= hzz;
+
+ if (times(&tmsbuf) == -1)
+ {
+ sendto_one(cptr, ":%s %d %s :times(2) error: %s.",
+ me.name, RPL_STATSDEBUG, nick, strerror(errno));
+ return;
+ }
+ secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
+
+ sendto_one(cptr, ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
+ me.name, RPL_STATSDEBUG, nick, mins, secs, umin, usec, smin, ssec);
+#endif /* HAVE_TIMES */
+#endif /* HAVE_GETRUSAGE */
+ sendto_one(cptr, ":%s %d %s :Reads %d Writes %d",
+ me.name, RPL_STATSDEBUG, nick, readcalls, writecalls);
+ sendto_one(cptr, ":%s %d %s :DBUF alloc %d used %d",
+ me.name, RPL_STATSDEBUG, nick, DBufAllocCount, DBufUsedCount);
+ sendto_one(cptr,
+ ":%s %d %s :Writes: <0 %d 0 %d <16 %d <32 %d <64 %d",
+ me.name, RPL_STATSDEBUG, nick,
+ writeb[0], writeb[1], writeb[2], writeb[3], writeb[4]);
+ sendto_one(cptr,
+ ":%s %d %s :<128 %d <256 %d <512 %d <1024 %d >1024 %d",
+ me.name, RPL_STATSDEBUG, nick,
+ writeb[5], writeb[6], writeb[7], writeb[8], writeb[9]);
+ return;
+}
+#endif /* DEBUGMODE */
+
+void count_memory(aClient *cptr, char *nick)
+{
+ Reg1 aClient *acptr;
+ Reg2 Link *link;
+ Reg3 aChannel *chptr;
+ Reg4 aConfItem *aconf;
+ Reg5 aConfClass *cltmp;
+
+ int lc = 0, /* local clients */
+ ch = 0, /* channels */
+ lcc = 0, /* local client conf links */
+ rc = 0, /* remote clients */
+ us = 0, /* user structs */
+ chu = 0, /* channel users */
+ chi = 0, /* channel invites */
+ chb = 0, /* channel bans */
+ wwu = 0, /* whowas users */
+ cl = 0, /* classes */
+ co = 0; /* conf lines */
+
+ int usi = 0, /* users invited */
+ usc = 0, /* users in channels */
+ aw = 0, /* aways set */
+ wwa = 0; /* whowas aways */
+
+ size_t chm = 0, /* memory used by channels */
+ chbm = 0, /* memory used by channel bans */
+ lcm = 0, /* memory used by local clients */
+ rcm = 0, /* memory used by remote clients */
+ awm = 0, /* memory used by aways */
+ wwam = 0, /* whowas away memory used */
+ wwm = 0, /* whowas array memory used */
+ com = 0, /* memory used by conf lines */
+ dbufs_allocated = 0, /* memory used by dbufs */
+ dbufs_used = 0, /* memory used by dbufs */
+ rm = 0, /* res memory used */
+ totcl = 0, totch = 0, totww = 0, tot = 0;
+
+ count_whowas_memory(&wwu, &wwm, &wwa, &wwam);
+ wwm += sizeof(aWhowas) * NICKNAMEHISTORYLENGTH;
+ wwm += sizeof(aWhowas *) * WW_MAX;
+
+ for (acptr = client; acptr; acptr = acptr->next)
+ {
+ if (IsPing(acptr))
+ continue;
+ if (MyConnect(acptr))
+ {
+ lc++;
+ for (link = acptr->confs; link; link = link->next)
+ lcc++;
+ }
+ else
+ rc++;
+ if (acptr->user)
+ {
+ us++;
+ for (link = acptr->user->invited; link; link = link->next)
+ usi++;
+ for (link = acptr->user->channel; link; link = link->next)
+ usc++;
+ if (acptr->user->away)
+ {
+ aw++;
+ awm += (strlen(acptr->user->away) + 1);
+ }
+ }
+ }
+ lcm = lc * CLIENT_LOCAL_SIZE;
+ rcm = rc * CLIENT_REMOTE_SIZE;
+
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ {
+ ch++;
+ chm += (strlen(chptr->chname) + sizeof(aChannel));
+ for (link = chptr->members; link; link = link->next)
+ chu++;
+ for (link = chptr->invites; link; link = link->next)
+ chi++;
+ for (link = chptr->banlist; link; link = link->next)
+ {
+ chb++;
+ chbm += (strlen(link->value.cp) + 1 + sizeof(Link));
+ }
+ }
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ {
+ co++;
+ com += aconf->host ? strlen(aconf->host) + 1 : 0;
+ com += aconf->passwd ? strlen(aconf->passwd) + 1 : 0;
+ com += aconf->name ? strlen(aconf->name) + 1 : 0;
+ com += sizeof(aConfItem);
+ }
+
+ for (cltmp = classes; cltmp; cltmp = cltmp->next)
+ cl++;
+
+ sendto_one(cptr, ":%s %d %s :Client Local %d(" SIZE_T_FMT
+ ") Remote %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, lc, lcm, rc, rcm);
+ sendto_one(cptr, ":%s %d %s :Users %d(" SIZE_T_FMT
+ ") Invites %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, us, us * sizeof(anUser), usi,
+ usi * sizeof(Link));
+ sendto_one(cptr, ":%s %d %s :User channels %d(" SIZE_T_FMT
+ ") Aways %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, usc, usc * sizeof(Link), aw, awm);
+ sendto_one(cptr, ":%s %d %s :Attached confs %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, lcc, lcc * sizeof(Link));
+
+ totcl = lcm + rcm + us * sizeof(anUser) + usc * sizeof(Link) + awm;
+ totcl += lcc * sizeof(Link) + usi * sizeof(Link);
+
+ sendto_one(cptr, ":%s %d %s :Conflines %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, co, com);
+
+ sendto_one(cptr, ":%s %d %s :Classes %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, cl, cl * sizeof(aConfClass));
+
+ sendto_one(cptr, ":%s %d %s :Channels %d(" SIZE_T_FMT
+ ") Bans %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, ch, chm, chb, chbm);
+ sendto_one(cptr, ":%s %d %s :Channel membrs %d(" SIZE_T_FMT
+ ") invite %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, chu, chu * sizeof(Link),
+ chi, chi * sizeof(Link));
+
+ totch = chm + chbm + chu * sizeof(Link) + chi * sizeof(Link);
+
+ sendto_one(cptr, ":%s %d %s :Whowas users %d(" SIZE_T_FMT
+ ") away %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, wwu, wwu * sizeof(anUser), wwa, wwam);
+ sendto_one(cptr, ":%s %d %s :Whowas array %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, NICKNAMEHISTORYLENGTH, wwm);
+
+ totww = wwu * sizeof(anUser) + wwam + wwm;
+
+ sendto_one(cptr, ":%s %d %s :Hash: client %d(" SIZE_T_FMT
+ "), chan is the same",
+ me.name, RPL_STATSDEBUG, nick, HASHSIZE, sizeof(void *) * HASHSIZE);
+
+ /*
+ * NOTE: this count will be accurate only for the exact instant that this
+ * message is being sent, so the count is affected by the dbufs that
+ * are being used to send this message out. If this is not desired, move
+ * the dbuf_count_memory call to a place before we start sending messages
+ * and cache DBufAllocCount and DBufUsedCount in variables until they
+ * are sent.
+ */
+ dbuf_count_memory(&dbufs_allocated, &dbufs_used);
+ sendto_one(cptr,
+ ":%s %d %s :DBufs allocated %d(" SIZE_T_FMT ") used %d(" SIZE_T_FMT ")",
+ me.name, RPL_STATSDEBUG, nick, DBufAllocCount, dbufs_allocated,
+ DBufUsedCount, dbufs_used);
+
+ rm = cres_mem(cptr);
+
+ tot =
+ totww + totch + totcl + com + cl * sizeof(aConfClass) + dbufs_allocated +
+ rm;
+ tot += sizeof(void *) * HASHSIZE * 3;
+
+ sendto_one(cptr, ":%s %d %s :Total: ww " SIZE_T_FMT " ch " SIZE_T_FMT
+ " cl " SIZE_T_FMT " co " SIZE_T_FMT " db " SIZE_T_FMT,
+ me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, com, dbufs_allocated);
+ return;
+}
+
+#ifdef MSGLOG_ENABLED
+
+/* Define here what level of messages you want to log */
+#define LOG_MASK_LEVEL LEVEL_MODE /* This that change some data */
+
+static struct log_entry log_table[MSGLOG_SIZE];
+
+static int unused_log_entries = MSGLOG_SIZE;
+static int last_log_entry = -1; /* Nothing stored yet */
+static int entry_stored_forlog = 0; /* Just a flag */
+
+/*
+ * RollBackMsgLog
+ *
+ * Just a little utility function used to retract
+ * an half stored Message log entry
+ */
+void RollBackMsgLog(void)
+{
+ /* We won't log this, abort and free the entry */
+ last_log_entry--;
+ unused_log_entries++;
+ return;
+}
+
+/*
+ * Log_Message (macroed as LogMessage)
+ *
+ * Permanently stores a log entry into the recent log memory area
+ * Store_Buffer MUST have been called before calling Log_Message
+ */
+void Log_Message(aClient *sptr, int msgclass)
+{
+ register int n = last_log_entry;
+
+ /* Clear our flag, since we are going to
+ * finish the processing of this entry */
+ entry_stored_forlog = 0;
+
+ /* Check if the level of this message is high enough */
+ if (msgclass < LOG_MASK_LEVEL)
+ {
+ RollBackMsgLog();
+ return;
+ }
+
+ /* Check if we wanna log the type of connection from
+ * where this message did come from */
+ if (!((0x8000 >> (8 + log_table[n].cptr_status)) & LOG_MASK_TYPE))
+ {
+ RollBackMsgLog();
+ return;
+ }
+
+ /* Complete the entry */
+ if (sptr)
+ {
+ log_table[n].sptr_status = sptr->status;
+ strncpy(log_table[n].sptr_name, sptr->name, HOSTLEN);
+ log_table[n].sptr_name[HOSTLEN] = '\0';
+ strncpy(log_table[n].sptr_yxx, sptr->yxx, 4);
+ log_table[n].sptr = sptr;
+
+ if (sptr->from)
+ {
+ strncpy(log_table[n].sptr_from_name, sptr->name, HOSTLEN);
+ log_table[n].sptr_from_name[HOSTLEN] = '\0';
+ }
+ else
+ {
+ memset(log_table[n].sptr_from_name, 0, HOSTLEN);
+ }
+ }
+ else
+ {
+ log_table[n].sptr_status = 0xFF; /* Dummy value */
+ memset(&log_table[n].sptr_name, 0, HOSTLEN);
+ memset(log_table[n].sptr_yxx, 0, 4);
+
+ log_table[n].sptr = 0;
+ memset(&log_table[n].sptr_from_name, 0, HOSTLEN);
+ }
+}
+
+/*
+ * Store_Buffer (macroed as StoreBuffer)
+ *
+ * Saves the buffer and cptr info at the very first stage
+ * of parsing, if Log_Message doesn't get called between
+ * two Store_Buffer calls this function assumes that the parser
+ * has rejected the message and therefore calls Log_Message
+ * as if the message class was 0 and the sptr null
+ */
+void Store_Buffer(char *buf, aClient *cptr)
+{
+ register int n;
+
+ /* Check if we have an entry pending, if so
+ * complete it's processing */
+ if (entry_stored_forlog)
+ Log_Message((aClient *)NULL, 0);
+
+ /* Update the "half used entry" flag */
+ entry_stored_forlog = 1;
+
+ /* First update the free entries counter */
+ if (unused_log_entries)
+ unused_log_entries--;
+
+ /* Get an entry */
+ n = (last_log_entry + 1) % MSGLOG_SIZE;
+
+ /* Update the last_log_entry index */
+ last_log_entry = n;
+
+ /* Store what we have by now in it */
+ log_table[n].cptr_status = cptr->status;
+ strncpy(log_table[n].cptr_name, cptr->name, HOSTLEN);
+ log_table[n].cptr_name[HOSTLEN] = '\0';
+ strncpy(log_table[n].cptr_yxx, cptr->yxx, 4);
+ log_table[n].cptr_fd = cptr->fd;
+ log_table[n].cptr = cptr; /* No checking for this, is lossy */
+ strncpy(log_table[n].buffer, buf, 511);
+}
+
+#endif /* MSGLOG_ENABLED */
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_err.c
+ * Copyright (C) 1992 Darren Reed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "numeric.h"
+#include "s_err.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+typedef struct {
+ int num_val;
+ char *num_form;
+} Numeric;
+
+/* *INDENT-OFF* */
+
+static Numeric local_replies[] = {
+/* 000 */
+ {0, (char *)NULL},
+/* 001 */
+ {RPL_WELCOME, ":Welcome to the Internet Relay Network %s"},
+/* 002 */
+ {RPL_YOURHOST, ":Your host is %s, running version %s"},
+/* 003 */
+ {RPL_CREATED, ":This server was created %s"},
+/* 004 */
+ {RPL_MYINFO, "%s %s dioswkg biklmnopstv"},
+/* 005 */
+ {RPL_MAP, ":%s%s"},
+/* 006 */
+ {RPL_MAPMORE, ":%s%s --> *more*"},
+/* 007 */
+ {RPL_MAPEND, ":End of /MAP"},
+/* 008 */
+ {RPL_SNOMASK, "%d :: Server notice mask (%#x)"},
+/* 009 */
+ {RPL_STATMEMTOT, "%u %u :Bytes Blocks"},
+/* 010 */
+#ifdef MEMSIZESTATS
+ {RPL_STATMEM, "%u %u %s %u"},
+#else
+ {RPL_STATMEM, "%u %u %s"},
+#endif
+ {0, (char *)NULL}
+};
+
+static Numeric numeric_errors[] = {
+/* 401 */
+ {ERR_NOSUCHNICK, "%s :No such nick"},
+/* 402 */
+ {ERR_NOSUCHSERVER, "%s :No such server"},
+/* 403 */
+ {ERR_NOSUCHCHANNEL, "%s :No such channel"},
+/* 404 */
+ {ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel"},
+/* 405 */
+ {ERR_TOOMANYCHANNELS, "%s :You have joined too many channels"},
+/* 406 */
+ {ERR_WASNOSUCHNICK, "%s :There was no such nickname"},
+/* 407 */
+ {ERR_TOOMANYTARGETS, "%s :Duplicate recipients. No message delivered"},
+/* 408 */
+ {0, (char *)NULL},
+/* 409 */
+ {ERR_NOORIGIN, ":No origin specified"},
+/* 410 */
+ {0, (char *)NULL},
+/* 411 */
+ {ERR_NORECIPIENT, ":No recipient given (%s)"},
+/* 412 */
+ {ERR_NOTEXTTOSEND, ":No text to send"},
+/* 413 */
+ {ERR_NOTOPLEVEL, "%s :No toplevel domain specified"},
+/* 414 */
+ {ERR_WILDTOPLEVEL, "%s :Wildcard in toplevel Domain"},
+/* 415 */
+ {0, (char *)NULL},
+/* 416 */
+ {ERR_QUERYTOOLONG, "%s :Too many lines in the output, restrict your query"},
+/* 417 */
+ {0, (char *)NULL},
+/* 418 */
+ {0, (char *)NULL},
+/* 419 */
+ {0, (char *)NULL},
+/* 420 */
+ {0, (char *)NULL},
+/* 421 */
+ {ERR_UNKNOWNCOMMAND, "%s :Unknown command"},
+/* 422 */
+ {ERR_NOMOTD, ":MOTD File is missing"},
+/* 423 */
+ {ERR_NOADMININFO, "%s :No administrative info available"},
+/* 424 */
+ {0, (char *)NULL},
+/* 425 */
+ {0, (char *)NULL},
+/* 426 */
+ {0, (char *)NULL},
+/* 427 */
+ {0, (char *)NULL},
+/* 428 */
+ {0, (char *)NULL},
+/* 429 */
+ {0, (char *)NULL},
+/* 430 */
+ {0, (char *)NULL},
+/* 431 */
+ {ERR_NONICKNAMEGIVEN, ":No nickname given"},
+/* 432 */
+ {ERR_ERRONEUSNICKNAME, "%s :Erroneus Nickname"},
+/* 433 */
+ {ERR_NICKNAMEINUSE, "%s :Nickname is already in use."},
+/* 434 */
+ {0, (char *)NULL},
+/* 435 */
+ {0, (char *)NULL},
+/* 436 */
+ {ERR_NICKCOLLISION, "%s :Nickname collision KILL"},
+/* 437 */
+ {ERR_BANNICKCHANGE, "%s :Cannot change nickname while banned on channel"},
+/* 438 */
+ {ERR_NICKTOOFAST, "%s :Nick change too fast. Please wait %d seconds."},
+/* 439 */
+ {ERR_TARGETTOOFAST, "%s :Target change too fast. Please wait %d seconds."},
+/* 440 */
+ {0, (char *)NULL},
+/* 441 */
+ {ERR_USERNOTINCHANNEL, "%s %s :They aren't on that channel"},
+/* 442 */
+ {ERR_NOTONCHANNEL, "%s :You're not on that channel"},
+/* 443 */
+ {ERR_USERONCHANNEL, "%s %s :is already on channel"},
+/* 444 */
+ {0, (char *)NULL},
+/* 445 */
+ {0, (char *)NULL},
+/* 446 */
+ {0, (char *)NULL},
+/* 447 */
+ {0, (char *)NULL},
+/* 448 */
+ {0, (char *)NULL},
+/* 449 */
+ {0, (char *)NULL},
+/* 450 */
+ {0, (char *)NULL},
+/* 451 */
+ {ERR_NOTREGISTERED, ":You have not registered"},
+/* 452 */
+ {0, (char *)NULL},
+/* 453 */
+ {0, (char *)NULL},
+/* 454 */
+ {0, (char *)NULL},
+/* 455 */
+ {0, (char *)NULL},
+/* 456 */
+ {0, (char *)NULL},
+/* 457 */
+ {0, (char *)NULL},
+/* 458 */
+ {0, (char *)NULL},
+/* 459 */
+ {0, (char *)NULL},
+/* 460 */
+ {0, (char *)NULL},
+/* 461 */
+ {ERR_NEEDMOREPARAMS, "%s :Not enough parameters"},
+/* 462 */
+ {ERR_ALREADYREGISTRED, ":You may not reregister"},
+/* 463 */
+ {ERR_NOPERMFORHOST, ":Your host isn't among the privileged"},
+/* 464 */
+ {ERR_PASSWDMISMATCH, ":Password Incorrect"},
+/* 465 */
+ {ERR_YOUREBANNEDCREEP, ":You are banned from this server"},
+/* 466 */
+ {ERR_YOUWILLBEBANNED, (char *)NULL},
+/* 467 */
+ {ERR_KEYSET, "%s :Channel key already set"},
+/* 468 */
+ {ERR_INVALIDUSERNAME, (char *)NULL},
+/* 469 */
+ {0, (char *)NULL},
+/* 470 */
+ {0, (char *)NULL},
+/* 471 */
+ {ERR_CHANNELISFULL, "%s :Cannot join channel (+l)"},
+/* 472 */
+ {ERR_UNKNOWNMODE, "%c :is unknown mode char to me"},
+/* 473 */
+ {ERR_INVITEONLYCHAN, "%s :Cannot join channel (+i)"},
+/* 474 */
+ {ERR_BANNEDFROMCHAN, "%s :Cannot join channel (+b)"},
+/* 475 */
+ {ERR_BADCHANNELKEY, "%s :Cannot join channel (+k)"},
+/* 476 */
+ {ERR_BADCHANMASK, "%s :Bad Channel Mask"},
+/* 477 */
+ {0, (char *)NULL},
+/* 478 */
+ {ERR_BANLISTFULL, "%s %s :Channel ban/ignore list is full"},
+/* 479 */
+ {0, (char *)NULL},
+/* 480 */
+ {0, (char *)NULL},
+/* 481 */
+ {ERR_NOPRIVILEGES, ":Permission Denied- You're not an IRC operator"},
+/* 482 */
+ {ERR_CHANOPRIVSNEEDED, "%s :You're not channel operator"},
+/* 483 */
+ {ERR_CANTKILLSERVER, ":You cant kill a server!"},
+/* 484 */
+ {ERR_ISCHANSERVICE, "%s %s :Cannot kill, kick or deop channel service"},
+/* 485 */
+ {0, (char *)NULL},
+/* 486 */
+ {0, (char *)NULL},
+/* 487 */
+ {0, (char *)NULL},
+/* 488 */
+ {0, (char *)NULL},
+/* 489 */
+ {ERR_VOICENEEDED, "%s :You're neither voiced nor channel operator"},
+/* 490 */
+ {0, (char *)NULL},
+/* 491 */
+ {ERR_NOOPERHOST, ":No O-lines for your host"},
+/* 492 */
+ {0, (char *)NULL},
+/* 493 */
+ {0, (char *)NULL},
+/* 494 */
+ {0, (char *)NULL},
+/* 495 */
+ {0, (char *)NULL},
+/* 496 */
+ {0, (char *)NULL},
+/* 497 */
+ {0, (char *)NULL},
+/* 498 */
+ {0, (char *)NULL},
+/* 499 */
+ {0, (char *)NULL},
+/* 500 */
+ {0, (char *)NULL},
+/* 501 */
+ {ERR_UMODEUNKNOWNFLAG, ":Unknown MODE flag"},
+/* 502 */
+ {ERR_USERSDONTMATCH, ":Cant change mode for other users"},
+/* 503 */
+ {0, (char *)NULL},
+/* 504 */
+ {0, (char *)NULL},
+/* 505 */
+ {0, (char *)NULL},
+/* 506 */
+ {0, (char *)NULL},
+/* 507 */
+ {0, (char *)NULL},
+/* 508 */
+ {0, (char *)NULL},
+/* 509 */
+ {0, (char *)NULL},
+/* 510 */
+ {0, (char *)NULL},
+/* 511 */
+ {ERR_SILELISTFULL, "%s :Your silence list is full"},
+/* 512 */
+ {ERR_NOSUCHGLINE, "%s@%s :No such gline"},
+/* 513 */
+ {ERR_BADPING, (char *)NULL}
+};
+
+static Numeric numeric_replies[] = {
+/* 300 */
+ {RPL_NONE, (char *)NULL},
+/* 301 */
+ {RPL_AWAY, "%s :%s"},
+/* 302 */
+ {RPL_USERHOST, ":"},
+/* 303 */
+ {RPL_ISON, ":"},
+/* 304 */
+ {RPL_TEXT, (char *)NULL},
+/* 305 */
+ {RPL_UNAWAY, ":You are no longer marked as being away"},
+/* 306 */
+ {RPL_NOWAWAY, ":You have been marked as being away"},
+/* 307 */
+ {RPL_USERIP, ":"},
+/* 308 */
+ {0, (char *)NULL},
+/* 309 */
+ {0, (char *)NULL},
+/* 310 */
+ {0, (char *)NULL},
+/* 311 */
+ {RPL_WHOISUSER, "%s %s %s * :%s"},
+/* 312 */
+ {RPL_WHOISSERVER, "%s %s :%s"},
+/* 313 */
+ {RPL_WHOISOPERATOR, "%s :is an IRC Operator"},
+/* 314 */
+ {RPL_WHOWASUSER, "%s %s %s * :%s"},
+/* 315 */
+ {RPL_ENDOFWHO, "%s :End of /WHO list."},
+/* 316 */
+ {0, (char *)NULL},
+/* 317 */
+ {RPL_WHOISIDLE, "%s %ld %ld :seconds idle, signon time"},
+/* 318 */
+ {RPL_ENDOFWHOIS, "%s :End of /WHOIS list."},
+/* 319 */
+ {RPL_WHOISCHANNELS, "%s :%s"},
+/* 320 */
+ {0, (char *)NULL},
+/* 321 */
+ {RPL_LISTSTART, "Channel :Users Name"},
+/* 322 */
+ {RPL_LIST, "%s %d :%s"},
+/* 323 */
+ {RPL_LISTEND, ":End of /LIST"},
+/* 324 */
+ {RPL_CHANNELMODEIS, "%s %s %s"},
+/* 325 */
+ {0, (char *)NULL},
+/* 326 */
+ {0, (char *)NULL},
+/* 327 */
+ {0, (char *)NULL},
+/* 328 */
+ {0, (char *)NULL},
+/* 329 */
+ {RPL_CREATIONTIME, "%s " TIME_T_FMT},
+/* 330 */
+ {0, (char *)NULL},
+/* 331 */
+ {RPL_NOTOPIC, "%s :No topic is set."},
+/* 332 */
+ {RPL_TOPIC, "%s :%s"},
+/* 333 */
+ {RPL_TOPICWHOTIME, "%s %s " TIME_T_FMT},
+/* 334 */
+ {RPL_LISTUSAGE, ":%s"},
+/* 335 */
+ {0, (char *)NULL},
+/* 336 */
+ {0, (char *)NULL},
+/* 337 */
+ {0, (char *)NULL},
+/* 338 */
+ {0, (char *)NULL},
+/* 339 */
+ {0, (char *)NULL},
+/* 340 */
+ {0, (char *)NULL},
+/* 341 */
+ {RPL_INVITING, "%s %s"},
+/* 342 */
+ {0, (char *)NULL},
+/* 343 */
+ {0, (char *)NULL},
+/* 344 */
+ {0, (char *)NULL},
+/* 345 */
+ {0, (char *)NULL},
+/* 346 */
+ {0, (char *)NULL},
+/* 347 */
+ {0, (char *)NULL},
+/* 348 */
+ {0, (char *)NULL},
+/* 349 */
+ {0, (char *)NULL},
+/* 350 */
+ {0, (char *)NULL},
+/* 351 */
+ {RPL_VERSION, "%s.%s %s :%s"},
+/* 352 */
+ {RPL_WHOREPLY, "%s"},
+/* 353 */
+ {RPL_NAMREPLY, "%s"},
+/* 354 */
+ {RPL_WHOSPCRPL, "%s"},
+/* 355 */
+ {0, (char *)NULL},
+/* 356 */
+ {0, (char *)NULL},
+/* 357 */
+ {0, (char *)NULL},
+/* 358 */
+ {0, (char *)NULL},
+/* 359 */
+ {0, (char *)NULL},
+/* 360 */
+ {0, (char *)NULL},
+/* 361 */
+ {RPL_KILLDONE, (char *)NULL},
+/* 362 */
+ {RPL_CLOSING, "%s :Closed. Status = %d"},
+/* 363 */
+ {RPL_CLOSEEND, "%d: Connections Closed"},
+/* 364 */
+#ifndef GODMODE
+ {RPL_LINKS, "%s %s :%d P%u %s"},
+#else /* GODMODE */
+ {RPL_LINKS, "%s %s :%d P%u " TIME_T_FMT " (%s) %s"},
+#endif /* GODMODE */
+/* 365 */
+ {RPL_ENDOFLINKS, "%s :End of /LINKS list."},
+/* 366 */
+ {RPL_ENDOFNAMES, "%s :End of /NAMES list."},
+/* 367 */
+ {RPL_BANLIST, "%s %s %s " TIME_T_FMT},
+/* 368 */
+ {RPL_ENDOFBANLIST, "%s :End of Channel Ban List"},
+/* 369 */
+ {RPL_ENDOFWHOWAS, "%s :End of WHOWAS"},
+/* 370 */
+ {0, (char *)NULL},
+/* 371 */
+ {RPL_INFO, ":%s"},
+/* 372 */
+ {RPL_MOTD, ":- %s"},
+/* 373 */
+ {RPL_INFOSTART, ":Server INFO"},
+/* 374 */
+ {RPL_ENDOFINFO, ":End of /INFO list."},
+/* 375 */
+ {RPL_MOTDSTART, ":- %s Message of the Day - "},
+/* 376 */
+ {RPL_ENDOFMOTD, ":End of /MOTD command."},
+/* 377 */
+ {0, (char *)NULL},
+/* 378 */
+ {0, (char *)NULL},
+/* 379 */
+ {0, (char *)NULL},
+/* 380 */
+ {0, (char *)NULL},
+/* 381 */
+ {RPL_YOUREOPER, ":You are now an IRC Operator"},
+/* 382 */
+ {RPL_REHASHING, "%s :Rehashing"},
+/* 383 */
+ {0, (char *)NULL},
+/* 384 */
+ {RPL_MYPORTIS, "%d :Port to local server is\r\n"},
+/* 385 */
+ {RPL_NOTOPERANYMORE, (char *)NULL},
+/* 386 */
+ {0, (char *)NULL},
+/* 387 */
+ {0, (char *)NULL},
+/* 388 */
+ {0, (char *)NULL},
+/* 389 */
+ {0, (char *)NULL},
+/* 390 */
+ {0, (char *)NULL},
+/* 391 */
+ {RPL_TIME, "%s " TIME_T_FMT " %ld :%s"},
+/* 392 */
+ {0, (char *)NULL},
+/* 393 */
+ {0, (char *)NULL},
+/* 394 */
+ {0, (char *)NULL},
+/* 395 */
+ {0, (char *)NULL},
+/* 396 */
+ {0, (char *)NULL},
+/* 397 */
+ {0, (char *)NULL},
+/* 398 */
+ {0, (char *)NULL},
+/* 399 */
+ {0, (char *)NULL},
+/* 200 */
+#ifndef GODMODE
+ {RPL_TRACELINK, "Link %s%s %s %s"},
+#else /* GODMODE */
+ {RPL_TRACELINK, "Link %s%s %s %s " TIME_T_FMT},
+#endif /* GODMODE */
+/* 201 */
+ {RPL_TRACECONNECTING, "Try. %d %s"},
+/* 202 */
+ {RPL_TRACEHANDSHAKE, "H.S. %d %s"},
+/* 203 */
+ {RPL_TRACEUNKNOWN, "???? %d %s"},
+/* 204 */
+ {RPL_TRACEOPERATOR, "Oper %d %s %ld"},
+/* 205 */
+ {RPL_TRACEUSER, "User %d %s %ld"},
+/* 206 */
+ {RPL_TRACESERVER, "Serv %d %dS %dC %s %s!%s@%s %ld %ld"},
+/* 207 */
+ {0, (char *)NULL},
+/* 208 */
+ {RPL_TRACENEWTYPE, "<newtype> 0 %s"},
+/* 209 */
+ {RPL_TRACECLASS, "Class %d %d"},
+/* 210 */
+ {0, (char *)NULL},
+/* 211 */
+ {RPL_STATSLINKINFO, (char *)NULL},
+/* 212 */
+ {RPL_STATSCOMMANDS, "%s %u %u"},
+/* 213 */
+ {RPL_STATSCLINE, "%c %s * %s %d %d"},
+/* 214 */
+ {RPL_STATSNLINE, "%c %s * %s %d %d"},
+/* 215 */
+ {RPL_STATSILINE, "%c %s * %s %d %d"},
+/* 216 */
+ {RPL_STATSKLINE, "%c %s %s %s %d %d"},
+/* 217 */
+ {RPL_STATSPLINE, "%c %d %d %#x"},
+/* 218 */
+ {RPL_STATSYLINE, "%c %d %d %d %d %ld"},
+/* 219 */
+ {RPL_ENDOFSTATS, "%c :End of /STATS report"},
+/* 220 */
+ {0, (char *)NULL},
+/* 221 */
+ {RPL_UMODEIS, "%s"},
+/* 222 */
+ {0, (char *)NULL},
+/* 223 */
+ {0, (char *)NULL},
+/* 224 */
+ {0, (char *)NULL},
+/* 225 */
+ {0, (char *)NULL},
+/* 226 */
+ {0, (char *)NULL},
+/* 227 */
+ {0, (char *)NULL},
+/* 228 */
+ {0, (char *)NULL},
+/* 229 */
+ {0, (char *)NULL},
+/* 230 */
+ {0, (char *)NULL},
+/* 231 */
+ {RPL_SERVICEINFO, (char *)NULL},
+/* 232 */
+ {RPL_ENDOFSERVICES, (char *)NULL},
+/* 233 */
+ {RPL_SERVICE, (char *)NULL},
+/* 234 */
+ {RPL_SERVLIST, (char *)NULL},
+/* 235 */
+ {RPL_SERVLISTEND, (char *)NULL},
+/* 236 */
+ {0, (char *)NULL},
+/* 237 */
+ {0, (char *)NULL},
+/* 238 */
+ {0, (char *)NULL},
+/* 239 */
+ {0, (char *)NULL},
+/* 240 */
+ {0, (char *)NULL},
+/* 241 */
+ {RPL_STATSLLINE, "%c %s * %s %d %d"},
+/* 242 */
+ {RPL_STATSUPTIME, ":Server Up %d days, %d:%02d:%02d"},
+/* 243 */
+ {RPL_STATSOLINE, "%c %s * %s %d %d"},
+/* 244 */
+ {RPL_STATSHLINE, "%c %s * %s %d %d"},
+/* 245 */
+ {0, (char *)NULL},
+/* 246 */
+ {RPL_STATSTLINE, "%c %s %s"},
+/* 247 */
+ {RPL_STATSGLINE, "%c %s@%s " TIME_T_FMT " :%s"},
+/* 248 */
+ {RPL_STATSULINE, "%c %s %s %s %d %d"},
+/* 249 */
+ {0, (char *)NULL},
+/* 250 */
+ {RPL_STATSCONN, ":Highest connection count: %d (%d clients)"},
+/* 251 */
+ {RPL_LUSERCLIENT, ":There are %d users and %d invisible on %d servers"},
+/* 252 */
+ {RPL_LUSEROP, "%d :operator(s) online"},
+/* 253 */
+ {RPL_LUSERUNKNOWN, "%d :unknown connection(s)"},
+/* 254 */
+ {RPL_LUSERCHANNELS, "%d :channels formed"},
+/* 255 */
+ {RPL_LUSERME, ":I have %d clients and %d servers"},
+/* 256 */
+ {RPL_ADMINME, ":Administrative info about %s"},
+/* 257 */
+ {RPL_ADMINLOC1, ":%s"},
+/* 258 */
+ {RPL_ADMINLOC2, ":%s"},
+/* 259 */
+ {RPL_ADMINEMAIL, ":%s"},
+/* 260 */
+ {0, (char *)NULL},
+/* 261 */
+ {RPL_TRACELOG, "File %s %d"},
+/* 262 */
+ {RPL_TRACEPING, "Ping %s %s"},
+/* 263 */
+ {0, (char *)NULL},
+/* 264 */
+ {0, (char *)NULL},
+/* 265 */
+ {0, (char *)NULL},
+/* 266 */
+ {0, (char *)NULL},
+/* 267 */
+ {0, (char *)NULL},
+/* 268 */
+ {0, (char *)NULL},
+/* 269 */
+ {0, (char *)NULL},
+/* 270 */
+ {0, (char *)NULL},
+/* 271 */
+ {RPL_SILELIST, "%s %s"},
+/* 272 */
+ {RPL_ENDOFSILELIST, "%s :End of Silence List"},
+/* 273 */
+ {0, (char *)NULL},
+/* 274 */
+ {0, (char *)NULL},
+/* 275 */
+ {RPL_STATSDLINE, "%c %s %s"},
+/* 276 */
+ {0, (char *)NULL},
+/* 277 */
+ {0, (char *)NULL},
+/* 278 */
+ {0, (char *)NULL},
+/* 279 */
+ {0, (char *)NULL},
+/* 280 */
+ {RPL_GLIST, "%s@%s " TIME_T_FMT " %s%s"},
+/* 281 */
+ {RPL_ENDOFGLIST, ":End of G-line List"}
+};
+
+/* *INDENT-ON* */
+
+static char numbuff[512];
+
+/* "inline" */
+#define prepbuf(buffer, num, tail) \
+{ \
+ register char *s = buffer + 4; \
+ register const char *ap = atoi_tab + (num << 2); \
+ \
+ strcpy(buffer, ":%s 000 %s "); \
+ *s++ = *ap++; \
+ *s++ = *ap++; \
+ *s = *ap; \
+ strcpy(s + 5, tail); \
+}
+
+char *err_str(int numeric)
+{
+ Reg1 Numeric *nptr;
+ Reg2 int num = numeric;
+
+ num -= numeric_errors[0].num_val;
+
+#ifdef DEBUGMODE
+ if (num < 0 || num > ERR_USERSDONTMATCH)
+ sprintf_irc(numbuff,
+ ":%%s %d %%s :INTERNAL ERROR: BAD NUMERIC! %d", numeric, num);
+ else
+ {
+ nptr = &numeric_errors[num];
+ if (!nptr->num_form || !nptr->num_val)
+ sprintf_irc(numbuff, ":%%s %d %%s :NO ERROR FOR NUMERIC ERROR %d",
+ numeric, num);
+ else
+ prepbuf(numbuff, nptr->num_val, nptr->num_form);
+ }
+#else
+ nptr = &numeric_errors[num];
+ prepbuf(numbuff, nptr->num_val, nptr->num_form);
+#endif
+
+ return numbuff;
+}
+
+char *rpl_str(int numeric)
+{
+ Reg1 Numeric *nptr;
+ Reg2 int num = numeric;
+
+ if (num > (int)(sizeof(local_replies) / sizeof(Numeric) - 2))
+ num -= (num > 300) ? 300 : 100;
+
+#ifdef DEBUGMODE
+ if (num < 0 || num > 200)
+ sprintf_irc(numbuff, ":%%s %d %%s :INTERNAL REPLY ERROR: BAD NUMERIC! %d",
+ numeric, num);
+ else
+ {
+ if (numeric > 99)
+ nptr = &numeric_replies[num];
+ else
+ nptr = &local_replies[num];
+ Debug((DEBUG_NUM, "rpl_str: numeric %d num %d nptr %p %d %p",
+ numeric, num, nptr, nptr->num_val, nptr->num_form));
+ if (!nptr->num_form || !nptr->num_val)
+ sprintf_irc(numbuff, ":%%s %d %%s :NO REPLY FOR NUMERIC ERROR %d",
+ numeric, num);
+ else
+ prepbuf(numbuff, nptr->num_val, nptr->num_form);
+ }
+#else
+ if (numeric > 99)
+ nptr = &numeric_replies[num];
+ else
+ nptr = &local_replies[num];
+ prepbuf(numbuff, nptr->num_val, nptr->num_form);
+#endif
+
+ return numbuff;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_misc.c (formerly ircd/date.c)
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "common.h"
+#include "match.h"
+#include "hash.h"
+#include "s_bsd.h"
+#include "res.h"
+#include "list.h"
+#include "ircd.h"
+#include "s_ping.h"
+#include "channel.h"
+#include "s_err.h"
+#include "support.h"
+#include "userload.h"
+#include "parse.h"
+#include "s_user.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+#include <assert.h>
+
+RCSTAG_CC("$Id$");
+
+static void exit_one_client(aClient *, char *);
+
+static char *months[] = {
+ "January", "February", "March", "April",
+ "May", "June", "July", "August",
+ "September", "October", "November", "December"
+};
+
+static char *weekdays[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+};
+
+/*
+ * stats stuff
+ */
+struct stats ircst, *ircstp = &ircst;
+
+char *date(time_t clock)
+{
+ static char buf[80], plus;
+ Reg1 struct tm *lt, *gm;
+ struct tm gmbuf;
+ int minswest;
+
+ if (!clock)
+ clock = now;
+ gm = gmtime(&clock);
+ memcpy(&gmbuf, gm, sizeof(gmbuf));
+ gm = &gmbuf;
+ lt = localtime(&clock);
+
+ if (lt->tm_yday == gm->tm_yday)
+ minswest = (gm->tm_hour - lt->tm_hour) * 60 + (gm->tm_min - lt->tm_min);
+ else if (lt->tm_yday > gm->tm_yday)
+ minswest = (gm->tm_hour - (lt->tm_hour + 24)) * 60;
+ else
+ minswest = ((gm->tm_hour + 24) - lt->tm_hour) * 60;
+
+ plus = (minswest > 0) ? '-' : '+';
+ if (minswest < 0)
+ minswest = -minswest;
+
+ sprintf(buf, "%s %s %d %d -- %02d:%02d %c%02d:%02d",
+ weekdays[lt->tm_wday], months[lt->tm_mon], lt->tm_mday,
+ 1900 + lt->tm_year, lt->tm_hour, lt->tm_min,
+ plus, minswest / 60, minswest % 60);
+
+ return buf;
+}
+
+/*
+ * myctime
+ *
+ * This is like standard ctime()-function, but it zaps away
+ * the newline from the end of that string. Also, it takes
+ * the time value as parameter, instead of pointer to it.
+ * Note that it is necessary to copy the string to alternate
+ * buffer (who knows how ctime() implements it, maybe it statically
+ * has newline there and never 'refreshes' it -- zapping that
+ * might break things in other places...)
+ */
+char *myctime(time_t value)
+{
+ static char buf[28];
+ Reg1 char *p;
+
+ strcpy(buf, ctime(&value));
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0';
+
+ return buf;
+}
+
+/*
+ * get_client_name
+ * Return the name of the client for various tracking and
+ * admin purposes. The main purpose of this function is to
+ * return the "socket host" name of the client, if that
+ * differs from the advertised name (other than case).
+ * But, this can be used to any client structure.
+ *
+ * Returns:
+ * "name[user@ip#.port]" if 'showip' is true;
+ * "name[sockethost]", if name and sockhost are different and
+ * showip is false; else
+ * "name".
+ *
+ * NOTE 1:
+ * Watch out the allocation of "nbuf", if either sptr->name
+ * or sptr->sockhost gets changed into pointers instead of
+ * directly allocated within the structure...
+ *
+ * NOTE 2:
+ * Function return either a pointer to the structure (sptr) or
+ * to internal buffer (nbuf). *NEVER* use the returned pointer
+ * to modify what it points!!!
+ */
+char *get_client_name(aClient *sptr, int showip)
+{
+ static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+ if (MyConnect(sptr))
+ {
+ if (IsUnixSocket(sptr))
+ {
+ if (showip)
+ sprintf_irc(nbuf, "%s[%s]", sptr->name, sptr->sockhost);
+ else
+ sprintf_irc(nbuf, "%s[%s]", sptr->name, me.sockhost);
+ }
+ else
+ {
+ if (showip)
+ sprintf_irc(nbuf, "%s[%s@%s]", sptr->name,
+ (!(sptr->flags & FLAGS_GOTID)) ? "" :
+ sptr->username, inetntoa(sptr->ip));
+ else
+ {
+ if (strCasediff(sptr->name, sptr->sockhost))
+ sprintf_irc(nbuf, "%s[%s]", sptr->name, sptr->sockhost);
+ else
+ return sptr->name;
+ }
+ }
+ return nbuf;
+ }
+ return sptr->name;
+}
+
+char *get_client_host(aClient *cptr)
+{
+ static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+ if (!MyConnect(cptr))
+ return cptr->name;
+ if (!cptr->hostp)
+ return get_client_name(cptr, FALSE);
+ if (IsUnixSocket(cptr))
+ sprintf_irc(nbuf, "%s[%s]", cptr->name, me.name);
+ else
+ sprintf(nbuf, "%s[%-.*s@%-.*s]", cptr->name, USERLEN,
+ (!(cptr->flags & FLAGS_GOTID)) ? "" : cptr->username,
+ HOSTLEN, cptr->hostp->h_name);
+ return nbuf;
+}
+
+/*
+ * Form sockhost such that if the host is of form user@host, only the host
+ * portion is copied.
+ */
+void get_sockhost(aClient *cptr, char *host)
+{
+ Reg3 char *s;
+ if ((s = strchr(host, '@')))
+ s++;
+ else
+ s = host;
+ strncpy(cptr->sockhost, s, sizeof(cptr->sockhost) - 1);
+}
+
+/*
+ * Return wildcard name of my server name according to given config entry
+ * --Jto
+ */
+char *my_name_for_link(char *name, aConfItem *aconf)
+{
+ static char namebuf[HOSTLEN];
+ register int count = aconf->port;
+ register char *start = name;
+
+ if (count <= 0 || count > 5)
+ return start;
+
+ while (count-- && name)
+ {
+ name++;
+ name = strchr(name, '.');
+ }
+ if (!name)
+ return start;
+
+ namebuf[0] = '*';
+ strncpy(&namebuf[1], name, HOSTLEN - 1);
+ namebuf[HOSTLEN - 1] = '\0';
+
+ return namebuf;
+}
+
+/*
+ * exit_downlinks - added by Run 25-9-94
+ *
+ * Removes all clients and downlinks (+clients) of any server
+ * QUITs are generated and sent to local users.
+ *
+ * cptr : server that must have all dependents removed
+ * sptr : source who thought that this was a good idea
+ * comment : comment sent as sign off message to local clients
+ */
+static void exit_downlinks(aClient *cptr, aClient *sptr, char *comment)
+{
+ Reg1 aClient *acptr;
+ Reg2 Dlink *next;
+ Reg3 Dlink *lp;
+ aClient **acptrp;
+ int i;
+
+ /* Run over all its downlinks */
+ for (lp = cptr->serv->down; lp; lp = next)
+ {
+ next = lp->next;
+ acptr = lp->value.cptr;
+ /* Remove the downlinks and client of the downlink */
+ exit_downlinks(acptr, sptr, comment);
+ /* Remove the downlink itself */
+ exit_one_client(acptr, me.name);
+ }
+ /* Remove all clients of this server */
+ acptrp = cptr->serv->client_list;
+ for (i = 0; i <= cptr->serv->nn_mask; ++acptrp, ++i)
+ if (*acptrp)
+ exit_one_client(*acptrp, comment);
+}
+
+/*
+ * exit_client, rewritten 25-9-94 by Run
+ *
+ * This function exits a client of *any* type (user, server, etc)
+ * from this server. Also, this generates all necessary prototol
+ * messages that this exit may cause.
+ *
+ * This function implicitly exits all other clients depending on
+ * this connection.
+ *
+ * For convenience, this function returns a suitable value for
+ * m_funtion return value:
+ *
+ * CPTR_KILLED if (cptr == bcptr)
+ * 0 if (cptr != bcptr)
+ *
+ * This function can be called in two ways:
+ * 1) From before or in parse(), exitting the 'cptr', in which case it was
+ * invoked as exit_client(cptr, cptr, &me,...), causing it to always
+ * return CPTR_KILLED.
+ * 2) Via parse from a m_function call, in which case it was invoked as
+ * exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client
+ * that generated the message in a way that we can assume he already
+ * did remove acptr from memory himself (or in other cases we don't mind
+ * because he will be delinked.) Or invoked as:
+ * exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should
+ * be removed.
+ * In general: No generated SQUIT or QUIT should be sent to source link
+ * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too).
+ *
+ * --Run
+ */
+int exit_client(aClient *cptr, /* Connection being handled by
+ read_message right now */
+ aClient *bcptr, /* Client being killed */
+ aClient *sptr, /* The client that made the decision
+ to remove this one, never NULL */
+ char *comment) /* Reason for the exit */
+{
+ Reg1 aClient *acptr;
+ Reg3 Dlink *dlp;
+#ifdef FNAME_USERLOG
+ time_t on_for;
+#endif
+ char comment1[HOSTLEN + HOSTLEN + 2];
+
+ if (MyConnect(bcptr))
+ {
+ bcptr->flags |= FLAGS_CLOSING;
+#ifdef ALLOW_SNO_CONNEXIT
+#ifdef SNO_CONNEXIT_IP
+ if (IsUser(bcptr))
+ {
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- Client exiting: %s (%s@%s) [%s] [%s]",
+ me.name, bcptr->name, bcptr->user->username, bcptr->user->host,
+ comment, inetntoa(bcptr->ip));
+ sendbufto_op_mask(SNO_CONNEXIT);
+ }
+#else /* SNO_CONNEXIT_IP */
+ if (IsUser(bcptr))
+ {
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- Client exiting: %s (%s@%s) [%s]",
+ me.name, bcptr->name, bcptr->user->username, bcptr->user->host,
+ comment);
+ sendbufto_op_mask(SNO_CONNEXIT);
+ }
+#endif /* SNO_CONNEXIT_IP */
+#endif /* ALLOW_SNO_CONNEXIT */
+ update_load();
+#ifdef FNAME_USERLOG
+ on_for = now - bcptr->firsttime;
+#if defined(USE_SYSLOG) && defined(SYSLOG_USERS)
+ if (IsUser(bcptr))
+ syslog(LOG_NOTICE, "%s (%3d:%02d:%02d): %s@%s (%s)\n",
+ myctime(bcptr->firsttime), on_for / 3600, (on_for % 3600) / 60,
+ on_for % 60, bcptr->user->username, bcptr->sockhost, bcptr->name);
+#else
+ if (IsUser(bcptr))
+ write_log(FNAME_USERLOG,
+ "%s (%3d:%02d:%02d): %s@%s [%s]\n",
+ myctime(bcptr->firsttime),
+ on_for / 3600, (on_for % 3600) / 60,
+ on_for % 60,
+ bcptr->user->username, bcptr->user->host, bcptr->username);
+#endif
+#endif
+ if (bcptr != sptr->from /* The source knows already */
+ && IsClient(bcptr)) /* Not a Ping struct or Log file */
+ {
+ if (IsServer(bcptr) || IsHandshake(bcptr))
+ sendto_one(bcptr, ":%s SQUIT %s 0 :%s", sptr->name, me.name, comment);
+ else if (!IsConnecting(bcptr))
+ sendto_one(bcptr, "ERROR :Closing Link: %s by %s (%s)",
+ get_client_name(bcptr, FALSE), sptr->name, comment);
+ if ((IsServer(bcptr) || IsHandshake(bcptr) || IsConnecting(bcptr)) &&
+ (sptr == &me || (IsServer(sptr) &&
+ (strncmp(comment, "Leaf-only link", 14) ||
+ strncmp(comment, "Non-Hub link", 12)))))
+ {
+ if (bcptr->serv->user && *bcptr->serv->by &&
+ (acptr = findNUser(bcptr->serv->by)) &&
+ acptr->user == bcptr->serv->user)
+ {
+ if (MyUser(acptr) || Protocol(acptr->from) < 10)
+ sendto_one(acptr,
+ ":%s NOTICE %s :Link with %s cancelled: %s",
+ me.name, acptr->name, bcptr->name, comment);
+ else
+ sendto_one(acptr,
+ "%s NOTICE %s%s :Link with %s cancelled: %s",
+ NumServ(&me), NumNick(acptr), bcptr->name, comment);
+ }
+ else
+ acptr = NULL;
+ if (sptr == &me)
+ sendto_lops_butone(acptr, "Link with %s cancelled: %s",
+ bcptr->name, comment);
+ }
+ }
+ /*
+ * Close the Client connection first.
+ */
+ close_connection(bcptr);
+ }
+
+ if (IsServer(bcptr))
+ {
+ strcpy(comment1, bcptr->serv->up->name);
+ strcat(comment1, " ");
+ strcat(comment1, bcptr->name);
+ if (IsUser(sptr))
+ sendto_lops_butone(sptr, "%s SQUIT by %s [%s]:",
+ (sptr->user->server == bcptr ||
+ sptr->user->server == bcptr->serv->up) ? "Local" : "Remote",
+ get_client_name(sptr, TRUE), sptr->user->server->name);
+ else if (sptr != &me && bcptr->serv->up != sptr)
+ sendto_ops("Received SQUIT %s from %s :", bcptr->name,
+ IsServer(sptr) ? sptr->name : get_client_name(sptr, TRUE));
+ sendto_op_mask(SNO_NETWORK, "Net break: %s (%s)", comment1, comment);
+ }
+
+ /*
+ * First generate the needed protocol for the other server links
+ * except the source:
+ */
+ for (dlp = me.serv->down; dlp; dlp = dlp->next)
+ if (dlp->value.cptr != sptr->from && dlp->value.cptr != bcptr)
+ {
+ if (IsServer(bcptr))
+ sendto_one(dlp->value.cptr, ":%s SQUIT %s " TIME_T_FMT " :%s",
+ sptr->name, bcptr->name, bcptr->serv->timestamp, comment);
+ else if (IsUser(bcptr) && (bcptr->flags & FLAGS_KILLED) == 0)
+ sendto_one(dlp->value.cptr, ":%s QUIT :%s", bcptr->name, comment);
+ }
+
+ /* Then remove the client structures */
+ if (IsServer(bcptr))
+ exit_downlinks(bcptr, sptr, comment1);
+ exit_one_client(bcptr, comment);
+
+ /*
+ * cptr can only have been killed if it was cptr itself that got killed here,
+ * because cptr can never have been a dependant of bcptr --Run
+ */
+ return (cptr == bcptr) ? CPTR_KILLED : 0;
+}
+
+/*
+ * Exit client with formatted message, added 25-9-94 by Run
+ */
+int vexit_client_msg(aClient *cptr, aClient *bcptr, aClient *sptr,
+ char *pattern, va_list vl)
+{
+ char msgbuf[1024];
+ vsprintf_irc(msgbuf, pattern, vl);
+ return exit_client(cptr, bcptr, sptr, msgbuf);
+}
+
+int exit_client_msg(aClient *cptr, aClient *bcptr,
+ aClient *sptr, char *pattern, ...)
+{
+ va_list vl;
+ char msgbuf[1024];
+
+ va_start(vl, pattern);
+ vsprintf_irc(msgbuf, pattern, vl);
+ va_end(vl);
+
+ return exit_client(cptr, bcptr, sptr, msgbuf);
+}
+
+/*
+ * Exit one client, local or remote. Assuming for local client that
+ * all dependants already have been removed, and socket is closed.
+ *
+ * Rewritten by Run - 24 sept 94
+ *
+ * bcptr : client being (s)quitted
+ * sptr : The source (prefix) of the QUIT or SQUIT
+ *
+ * --Run
+ */
+static void exit_one_client(aClient *bcptr, char *comment)
+{
+ Link *lp;
+
+ if (bcptr->serv && bcptr->serv->client_list) /* Was SetServerYXX called ? */
+ ClearServerYXX(bcptr); /* Removes server from server_list[] */
+ if (IsUser(bcptr))
+ {
+ /* Stop a running /LIST clean */
+ if (MyUser(bcptr) && bcptr->listing)
+ {
+ bcptr->listing->chptr->mode.mode &= ~MODE_LISTED;
+ RunFree(bcptr->listing);
+ bcptr->listing = NULL;
+ }
+
+ if (AskedPing(bcptr))
+ cancel_ping(bcptr, NULL);
+ /*
+ * If a person is on a channel, send a QUIT notice
+ * to every client (person) on the same channel (so
+ * that the client can show the "**signoff" message).
+ * (Note: The notice is to the local clients *only*)
+ */
+ sendto_common_channels(bcptr, ":%s QUIT :%s", bcptr->name, comment);
+
+ while ((lp = bcptr->user->channel))
+ remove_user_from_channel(bcptr, lp->value.chptr);
+
+ /* Clean up invitefield */
+ while ((lp = bcptr->user->invited))
+ del_invite(bcptr, lp->value.chptr);
+
+ /* Clean up silencefield */
+ while ((lp = bcptr->user->silence))
+ del_silence(bcptr, lp->value.cp);
+
+ if (IsInvisible(bcptr))
+ --nrof.inv_clients;
+ if (IsOper(bcptr))
+ --nrof.opers;
+ if (MyConnect(bcptr))
+ Count_clientdisconnects(bcptr, nrof);
+ else
+ Count_remoteclientquits(nrof);
+ }
+ else if (IsServer(bcptr))
+ {
+ /* Remove downlink list node of uplink */
+ remove_dlink(&bcptr->serv->up->serv->down, bcptr->serv->updown);
+
+ if (MyConnect(bcptr))
+ Count_serverdisconnects(nrof);
+ else
+ Count_remoteserverquits(nrof);
+ }
+ else if (IsPing(bcptr)) /* Apperently, we are closing ALL links */
+ {
+ del_queries((char *)bcptr);
+ end_ping(bcptr);
+ return;
+ }
+ else if (IsMe(bcptr))
+ {
+ sendto_ops("ERROR: tried to exit me! : %s", comment);
+ return; /* ...must *never* exit self! */
+ }
+ else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr))
+ Count_unknowndisconnects(nrof);
+
+ /* Update IPregistry */
+ if (IsIPChecked(bcptr))
+ IPcheck_disconnect(bcptr);
+
+ /*
+ * Remove from serv->client_list
+ * NOTE: user is *always* NULL if this is a server
+ */
+ if (bcptr->user)
+ {
+ assert(!IsServer(bcptr));
+ /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
+ RemoveYXXClient(bcptr->user->server, bcptr->yxx);
+ }
+
+ /* Remove bcptr from the client list */
+#ifdef DEBUGMODE
+ if (hRemClient(bcptr) != 0)
+ Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p",
+ bcptr, bcptr->name, bcptr->from ? bcptr->from->sockhost : "??host",
+ bcptr->from, bcptr->next, bcptr->prev, bcptr->fd,
+ bcptr->status, bcptr->user));
+#else
+ hRemClient(bcptr);
+#endif
+ remove_client_from_list(bcptr);
+ return;
+}
+
+void checklist(void)
+{
+ Reg1 aClient *acptr;
+ Reg2 int i, j;
+
+ if (!(bootopt & BOOT_AUTODIE))
+ return;
+ for (j = i = 0; i <= highest_fd; i++)
+ if (!(acptr = loc_clients[i]))
+ continue;
+ else if (IsUser(acptr))
+ j++;
+ if (!j)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, "ircd exiting: autodie");
+#endif
+ exit(0);
+ }
+ return;
+}
+
+void initstats(void)
+{
+ memset(&ircst, 0, sizeof(ircst));
+}
+
+void tstats(aClient *cptr, char *name)
+{
+ Reg1 aClient *acptr;
+ Reg2 int i;
+ Reg3 struct stats *sp;
+ struct stats tmp;
+
+ sp = &tmp;
+ memcpy(sp, ircstp, sizeof(*sp));
+ for (i = 0; i < MAXCONNECTIONS; i++)
+ {
+ if (!(acptr = loc_clients[i]))
+ continue;
+ if (IsServer(acptr))
+ {
+ sp->is_sbs += acptr->sendB;
+ sp->is_sbr += acptr->receiveB;
+ sp->is_sks += acptr->sendK;
+ sp->is_skr += acptr->receiveK;
+ sp->is_sti += now - acptr->firsttime;
+ sp->is_sv++;
+ if (sp->is_sbs > 1023)
+ {
+ sp->is_sks += (sp->is_sbs >> 10);
+ sp->is_sbs &= 0x3ff;
+ }
+ if (sp->is_sbr > 1023)
+ {
+ sp->is_skr += (sp->is_sbr >> 10);
+ sp->is_sbr &= 0x3ff;
+ }
+ }
+ else if (IsUser(acptr))
+ {
+ sp->is_cbs += acptr->sendB;
+ sp->is_cbr += acptr->receiveB;
+ sp->is_cks += acptr->sendK;
+ sp->is_ckr += acptr->receiveK;
+ sp->is_cti += now - acptr->firsttime;
+ sp->is_cl++;
+ if (sp->is_cbs > 1023)
+ {
+ sp->is_cks += (sp->is_cbs >> 10);
+ sp->is_cbs &= 0x3ff;
+ }
+ if (sp->is_cbr > 1023)
+ {
+ sp->is_ckr += (sp->is_cbr >> 10);
+ sp->is_cbr &= 0x3ff;
+ }
+ }
+ else if (IsUnknown(acptr))
+ sp->is_ni++;
+ }
+
+ sendto_one(cptr, ":%s %d %s :accepts %u refused %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_ac, sp->is_ref);
+ sendto_one(cptr, ":%s %d %s :unknown commands %u prefixes %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_unco, sp->is_unpf);
+ sendto_one(cptr, ":%s %d %s :nick collisions %u unknown closes %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_kill, sp->is_ni);
+ sendto_one(cptr, ":%s %d %s :wrong direction %u empty %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_wrdi, sp->is_empt);
+ sendto_one(cptr, ":%s %d %s :numerics seen %u mode fakes %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_num, sp->is_fake);
+ sendto_one(cptr, ":%s %d %s :auth successes %u fails %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_asuc, sp->is_abad);
+ sendto_one(cptr, ":%s %d %s :local connections %u udp packets %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_loc, sp->is_udp);
+ sendto_one(cptr, ":%s %d %s :Client Server", me.name, RPL_STATSDEBUG, name);
+ sendto_one(cptr, ":%s %d %s :connected %u %u",
+ me.name, RPL_STATSDEBUG, name, sp->is_cl, sp->is_sv);
+ sendto_one(cptr, ":%s %d %s :bytes sent %u.%uK %u.%uK",
+ me.name, RPL_STATSDEBUG, name,
+ sp->is_cks, sp->is_cbs, sp->is_sks, sp->is_sbs);
+ sendto_one(cptr, ":%s %d %s :bytes recv %u.%uK %u.%uK",
+ me.name, RPL_STATSDEBUG, name,
+ sp->is_ckr, sp->is_cbr, sp->is_skr, sp->is_sbr);
+ sendto_one(cptr, ":%s %d %s :time connected " TIME_T_FMT " " TIME_T_FMT,
+ me.name, RPL_STATSDEBUG, name, sp->is_cti, sp->is_sti);
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_numeric.c
+ * Copyright (C) 1990 Jarkko Oikarinen
+ *
+ * Numerous fixes by Markku Savela
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "s_bsd.h"
+#include "send.h"
+#include "support.h"
+#include "parse.h"
+#include "numeric.h"
+#include "channel.h"
+#include "ircd.h"
+#include "hash.h"
+#include "numnicks.h"
+#include "s_numeric.h"
+
+RCSTAG_CC("$Id$");
+
+static char buffer[1024];
+
+/*
+ * do_numeric()
+ * Rewritten by Nemesi, Jan 1999, to support numeric nicks in parv[1]
+ *
+ * Called when we get a numeric message from a remote _server_ and we are
+ * supposed to forward it somewhere. Note that we always ignore numerics sent
+ * to 'me' and simply drop the message if we can't handle with this properly:
+ * the savy approach is NEVER generate an error in response to an... error :)
+ */
+
+int do_numeric(int numeric, int nnn, aClient *cptr, aClient *sptr,
+ int parc, char *parv[])
+{
+ aClient *acptr = NULL;
+ aChannel *achptr = NULL;
+ char *p, *b;
+ int i;
+
+ /* Avoid trash, we need it to come from a server and have a target */
+ if ((parc < 2) || !IsServer(sptr))
+ return 0;
+
+ /* Who should receive this message ? Will we do something with it ?
+ Note that we use findUser functions, so the target can't be neither
+ a server, nor a channel (?) nor a list of targets (?) .. u2.10
+ should never generate numeric replies to non-users anyway
+ Ahem... it can be a channel actually, csc bots use it :\ --Nem */
+
+ if (IsChannelName(parv[1]))
+ achptr = FindChannel(parv[1]);
+ else
+ acptr = (nnn) ? (findNUser(parv[1])) : (FindUser(parv[1]));
+
+ if (((!acptr) || (acptr->from == cptr)) && !achptr)
+ return 0;
+
+ /* Remap low number numerics, not that I understand WHY.. --Nemesi */
+ if (numeric < 100)
+ numeric += 100;
+
+ /* Rebuild the buffer with all the parv[] without wasting cycles :) */
+ b = buffer;
+ if (parc > 2)
+ {
+ for (i = 2; i < (parc - 1); i++)
+ for (*b++ = ' ', p = parv[i]; *p; p++)
+ *b++ = *p;
+ for (*b++ = ' ', *b++ = ':', p = parv[parc - 1]; *p; p++)
+ *b++ = *p;
+ }
+ *b = '\000';
+
+ /* Since .06 this will implicitly use numeric nicks when needed */
+
+ if (acptr)
+ sendto_prefix_one(acptr, sptr, ":%s %d %s%s",
+ sptr->name, numeric, acptr->name, buffer);
+ else
+ sendto_channel_butone(cptr, sptr, achptr, ":%s %d %s%s",
+ sptr->name, numeric, achptr->chname, buffer);
+
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_ping.c
+ * Copyright (C) 1994 Carlo Wood ( Run @ undernet.org )
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/socket.h>
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef UNIXPORT
+#include <sys/un.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "h.h"
+#include "struct.h"
+#include "send.h"
+#include "s_conf.h"
+#include "match.h"
+#include "res.h"
+#include "s_bsd.h"
+#include "s_serv.h"
+#include "ircd.h"
+#include "s_ping.h"
+#include "support.h"
+#include "numeric.h"
+#include "s_user.h"
+#include "s_err.h"
+#include "common.h"
+#include "s_user.h"
+#include "numnicks.h"
+
+RCSTAG_CC("$Id$");
+
+#define UPINGBUFSIZE 2000 /* Lot bigger then 1024,
+ bit smaller then 2048 */
+#define UPINGTIMEOUT 120 /* Timeout waitting for first ping response */
+
+/*
+ * start_ping
+ *
+ * As for now, I am abusing the client structure for a ping connection.
+ * .... And I really don't like this solution --Nemesi
+ * Used members are:
+ * These are used by existing routines as well, and do have their own meaning:
+ * fd : The socket file descriptor.
+ * status : To flag that this IS one of these abused ping structures
+ * sockhost : Name of requested server to ping (aconf->host).
+ * name : aconf->name
+ * ip : ip#
+ * These have more or less their own meaning,
+ * but are not used by existing routines:
+ * flags : To flag that a next ping is requested.
+ * port : Requested remote port.
+ * These are only used by the 'uping' routines
+ * and have totally different meanings:
+ * buffer : buffer hold pingtimes of received packets
+ * confs : recv/send (char *) buffer.
+ * hopcount : Total number of requested pings
+ * sendB : Number of pings left to send.
+ * receiveB : Number of pings left to be received.
+ * acpt : client asking for this ping
+ * lasttime : last time a ping was sent
+ * firsttime: recvfrom timeout
+ * since : timeout in seconds to next recvfrom
+ * receiveK : minimum in ms
+ * sendM : average in ms
+ * receiveM : maximum in ms
+ */
+int start_ping(aClient *cptr)
+{
+ struct sockaddr_in remote_addr;
+
+ Debug((DEBUG_NOTICE, "start_ping(%p) status %d", cptr, cptr->status));
+
+ if (!(cptr->acpt))
+ return -1;
+
+ memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
+#ifdef TESTNET
+ remote_addr.sin_port = htons(cptr->port + 10000);
+#else
+ remote_addr.sin_port = htons(cptr->port);
+#endif
+ remote_addr.sin_family = AF_INET;
+
+ if (MyUser(cptr->acpt) || Protocol(cptr->acpt->from) < 10)
+ {
+ sendto_one(cptr->acpt,
+ ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %u",
+ me.name, cptr->acpt->name, cptr->hopcount,
+ (cptr->hopcount == 1) ? "" : "s", cptr->name,
+#ifdef TESTNET
+ inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
+#else
+ inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
+#endif
+ }
+ else
+ {
+ sendto_one(cptr->acpt,
+ "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %u",
+ NumServ(&me), NumNick(cptr->acpt), cptr->hopcount,
+ (cptr->hopcount == 1) ? "" : "s", cptr->name,
+#ifdef TESTNET
+ inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
+#else
+ inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
+#endif
+ }
+
+ cptr->firsttime = now + UPINGTIMEOUT;
+ cptr->since = UPINGTIMEOUT;
+ cptr->flags |= (FLAGS_PING);
+
+ return 0;
+}
+
+/*
+ * send_ping
+ *
+ */
+void send_ping(aClient *cptr)
+{
+ struct sockaddr_in remote_addr;
+ struct timeval tv;
+
+ memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
+#ifdef TESTNET
+ remote_addr.sin_port = htons(cptr->port + 10000);
+#else
+ remote_addr.sin_port = htons(cptr->port);
+#endif
+ remote_addr.sin_family = AF_INET;
+
+ gettimeofday(&tv, NULL);
+#if defined(__sun__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+ sprintf((char *)cptr->confs, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
+#else
+ sprintf((char *)cptr->confs, " %10u%c%6u", tv.tv_sec, '\0', tv.tv_usec);
+#endif
+
+ Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
+ (char *)cptr->confs, (char *)cptr->confs + 12,
+ inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port), cptr->fd));
+
+ if (sendto(cptr->fd, (char *)cptr->confs, 1024, 0,
+ (struct sockaddr *)&remote_addr, sizeof(struct sockaddr_in)) != 1024)
+ {
+#ifdef DEBUGMODE
+ int err = errno;
+#endif
+ if (cptr->acpt)
+ {
+ if (MyUser(cptr->acpt)
+#ifndef NO_PROTOCOL9
+ || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
+#endif
+ )
+ sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: sendto() failed: %s",
+ me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
+ else
+ sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING: sendto() failed: %s",
+ NumServ(&me), NumNick(cptr->acpt), strerror(get_sockerr(cptr)));
+ }
+ Debug((DEBUG_SEND, "send_ping: sendto failed on %d (%d)", cptr->fd, err));
+ end_ping(cptr);
+ }
+ else if (--(cptr->sendB) <= 0)
+ {
+ ClearPing(cptr);
+ if (cptr->receiveB <= 0)
+ end_ping(cptr);
+ }
+
+ return;
+}
+
+/*
+ * read_ping
+ */
+void read_ping(aClient *cptr)
+{
+ size_t addr_len = sizeof(struct sockaddr_in);
+ struct sockaddr_in remote_addr;
+ struct timeval tv;
+ int len;
+ unsigned long int pingtime;
+ char *s;
+
+ memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
+#ifdef TESTNET
+ remote_addr.sin_port = htons(cptr->port + 10000);
+#else
+ remote_addr.sin_port = htons(cptr->port);
+#endif
+ remote_addr.sin_family = AF_INET;
+
+ gettimeofday(&tv, NULL);
+
+ if ((len = recvfrom(cptr->fd, (char *)cptr->confs, UPINGBUFSIZE, 0,
+ (struct sockaddr *)&remote_addr, &addr_len)) == -1)
+ {
+ int err = errno;
+ if (MyUser(cptr->acpt)
+#ifndef NO_PROTOCOL9
+ || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
+#endif
+ )
+ sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: recvfrom: %s",
+ me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
+ else
+ sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING: recvfrom: %s",
+ NumServ(&me), NumNick(cptr->acpt), strerror(get_sockerr(cptr)));
+ Debug((DEBUG_SEND, "read_ping: recvfrom: %d", err));
+ if (err != EAGAIN)
+ end_ping(cptr);
+ return;
+ }
+
+ if (len < 19)
+ return; /* Broken packet */
+
+ pingtime = (tv.tv_sec - atoi((char *)cptr->confs + 1)) * 1000 +
+ (tv.tv_usec - atoi((char *)cptr->confs + strlen((char *)cptr->confs) +
+ 1)) / 1000;
+ cptr->sendM += pingtime;
+ if (!(cptr->receiveK) || (cptr->receiveK > pingtime))
+ cptr->receiveK = pingtime;
+ if (pingtime > cptr->receiveM)
+ cptr->receiveM = pingtime;
+ /* Wait at most 10 times the average pingtime for the next one: */
+ if ((cptr->since =
+ cptr->sendM / (100 * (cptr->hopcount - cptr->receiveB + 1))) < 2)
+ cptr->since = 2;
+ cptr->firsttime = tv.tv_sec + cptr->since;
+
+ Debug((DEBUG_SEND, "read_ping: %d bytes, ti " TIME_T_FMT ": [%s %s] %lu ms",
+ len, cptr->since, (char *)cptr->confs,
+ (char *)cptr->confs + strlen((char *)cptr->confs) + 1, pingtime));
+
+ s = cptr->buffer + strlen(cptr->buffer);
+ sprintf(s, " %lu", pingtime);
+
+ if ((--(cptr->receiveB) <= 0 && !DoPing(cptr)) || !(cptr->acpt))
+ end_ping(cptr);
+
+ return;
+}
+
+int ping_server(aClient *cptr)
+{
+ if ((!cptr->ip.s_addr)
+#ifdef UNIXPORT
+ && ((cptr->sockhost[2]) != '/')
+#endif
+ )
+ {
+ struct hostent *hp;
+ char *s;
+ Link lin;
+
+ if (!(cptr->acpt))
+ return -1; /* Oper left already */
+
+ lin.flags = ASYNC_PING;
+ lin.value.cptr = cptr;
+ nextdnscheck = 1;
+ s = strchr(cptr->sockhost, '@');
+ s++; /* should never be NULL;
+ cptr->sockhost is actually a conf->host */
+ if ((cptr->ip.s_addr = inet_addr(s)) == INADDR_NONE)
+ {
+ cptr->ip.s_addr = INADDR_ANY;
+ hp = gethost_byname(s, &lin);
+ Debug((DEBUG_NOTICE, "ping_sv: hp %p ac %p ho %s", hp, cptr, s));
+ if (!hp)
+ return 0;
+ memcpy(&cptr->ip, hp->h_addr, sizeof(struct in_addr));
+ }
+ }
+
+ return start_ping(cptr);
+}
+
+/*
+ * m_uping -- by Run
+ *
+ * parv[0] = sender prefix
+ * parv[1] = pinged server
+ * parv[2] = port
+ * parv[3] = hunted server
+ * parv[4] = number of requested pings
+ */
+int m_uping(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aConfItem *aconf;
+ unsigned short int port;
+ int fd, opt;
+
+ if (!IsPrivileged(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return -1;
+ }
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
+ return 0;
+ }
+
+ if (MyUser(sptr))
+ {
+ if (parc == 2)
+ {
+ parv[parc++] = UDP_PORT;
+ parv[parc++] = me.name;
+ parv[parc++] = "5";
+ }
+ else if (parc == 3)
+ {
+ if (isDigit(*parv[2]))
+ parv[parc++] = me.name;
+ else
+ {
+ parv[parc++] = parv[2];
+ parv[2] = UDP_PORT;
+ }
+ parv[parc++] = "5";
+ }
+ else if (parc == 4)
+ {
+ if (isDigit(*parv[2]))
+ {
+ if (isDigit(*parv[3]))
+ {
+ parv[parc++] = parv[3];
+ parv[3] = me.name;
+ }
+ else
+ parv[parc++] = "5";
+ }
+ else
+ {
+ parv[parc++] = parv[3];
+ parv[3] = parv[2];
+ parv[2] = UDP_PORT;
+ }
+ }
+ }
+ if (hunt_server(1, cptr, sptr,
+ ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
+ return 0;
+
+ if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :UPING: Invalid number of packets: %s",
+ me.name, parv[0], parv[4]);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :UPING: Invalid number of packets: %s",
+ NumServ(&me), NumNick(sptr), parv[4]);
+ return 0;
+ }
+
+ /* Check if a CONNECT would be possible at all (adapted from m_connect) */
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status == CONF_CONNECT_SERVER &&
+ match(parv[1], aconf->name) == 0)
+ break;
+ if (!aconf)
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status == CONF_CONNECT_SERVER &&
+ (match(parv[1], aconf->host) == 0 ||
+ match(parv[1], strchr(aconf->host, '@') + 1) == 0))
+ break;
+ if (!aconf)
+ {
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
+ me.name, parv[0], parv[1]);
+ else
+ sendto_one(sptr,
+ "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
+ NumServ(&me), NumNick(sptr), parv[1]);
+ return 0;
+ }
+
+ if (AskedPing(sptr))
+ cancel_ping(sptr, sptr); /* Cancel previous ping request */
+
+ /*
+ * Determine port: First user supplied, then default : 7007
+ */
+ if (BadPtr(parv[2]) || (port = atoi(parv[2])) <= 0)
+ port = atoi(UDP_PORT);
+
+ alarm(2);
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ {
+ int err = errno;
+ alarm(0);
+ sendto_ops("m_uping: socket: %s", (err != EAGAIN) ?
+ strerror(err) : "No more sockets");
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :UPING: Unable to create udp ping socket",
+ me.name, parv[0]);
+ else
+ sendto_one(sptr,
+ "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
+ NumServ(&me), NumNick(sptr));
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "Unable to create udp ping socket");
+#endif
+ return 0;
+ }
+ alarm(0);
+
+ if (fcntl(fd, F_SETFL, FNDELAY) == -1)
+ {
+ sendto_ops("m_uping: fcntl FNDELAY: %s", strerror(errno));
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
+ me.name, parv[0]);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
+ NumServ(&me), NumNick(sptr));
+ close(fd);
+ return 0;
+ }
+ /*
+ * On some systems, receive and send buffers must be equal in size.
+ * Others block select() when the buffers are too small
+ * (Linux 1.1.50 blocks when < 2048) --Run
+ */
+ opt = 2048;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt,
+ sizeof(opt)) < 0 ||
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
+ {
+ int err = errno;
+ sendto_ops("m_uping: setsockopt SO_SNDBUF|SO_RCVBUF: %s", strerror(err));
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :UPING: error in setsockopt: %s",
+ me.name, parv[0], strerror(err));
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :UPING: error in setsockopt: %s",
+ NumServ(&me), NumNick(sptr), strerror(err));
+ close(fd);
+ return 0;
+ }
+
+ if (fd >= MAXCONNECTIONS)
+ {
+ sendto_ops("Can't allocate fd for uping (all connections in use)");
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, ":%s NOTICE %s :UPING: All connections in use",
+ me.name, parv[0]);
+ else
+ sendto_one(sptr, "%s NOTICE %s%s :UPING: All connections in use",
+ NumServ(&me), NumNick(sptr));
+ close(fd);
+ return 0;
+ }
+
+ if (fd > highest_fd)
+ highest_fd = fd;
+ loc_clients[fd] = cptr = make_client(NULL, STAT_PING);
+ cptr->confs = (Link *)RunMalloc(UPINGBUFSIZE); /* Really a (char *) */
+ cptr->fd = fd;
+ cptr->port = port;
+ cptr->hopcount = cptr->receiveB = cptr->sendB = MIN(20, atoi(parv[4]));
+ strcpy(cptr->sockhost, aconf->host);
+ cptr->acpt = sptr;
+ SetAskedPing(sptr);
+ memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
+ strcpy(cptr->name, aconf->name);
+ cptr->firsttime = 0;
+
+ switch (ping_server(cptr))
+ {
+ case 0:
+ break;
+ case -1:
+ del_queries((char *)cptr);
+ end_ping(cptr);
+ break;
+ }
+ return 0;
+}
+
+void end_ping(aClient *cptr)
+{
+ Debug((DEBUG_DEBUG, "end_ping: %p", cptr));
+ if (cptr->acpt)
+ {
+ if (MyUser(cptr->acpt)
+ || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10))
+ {
+ if (cptr->firsttime) /* Started at all ? */
+ {
+ if (cptr->receiveB != cptr->hopcount) /* Received any pings at all? */
+ {
+ sendto_one(cptr->acpt, ":%s NOTICE %s :UPING %s%s",
+ me.name, cptr->acpt->name, cptr->name, cptr->buffer);
+ sendto_one(cptr->acpt,
+ ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
+ "min/avg/max = %u/%u/%u ms",
+ me.name, cptr->acpt->name, cptr->hopcount - cptr->sendB,
+ cptr->hopcount - cptr->receiveB, cptr->receiveK,
+ (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
+ (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
+ }
+ else
+ sendto_one(cptr->acpt,
+ ":%s NOTICE %s :UPING: no response from %s within %d seconds",
+ me.name, cptr->acpt->name, cptr->name,
+ (int)(now + cptr->since - cptr->firsttime));
+ }
+ else
+ sendto_one(cptr->acpt,
+ ":%s NOTICE %s :UPING: Could not start ping to %s %u",
+ me.name, cptr->acpt->name, cptr->name, cptr->port);
+ }
+ else
+ {
+ if (cptr->firsttime) /* Started at all ? */
+ {
+ if (cptr->receiveB != cptr->hopcount) /* Received any pings at all? */
+ {
+ sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING %s%s",
+ NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->buffer);
+ sendto_one(cptr->acpt,
+ "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
+ "min/avg/max = %u/%u/%u ms",
+ NumServ(&me), NumNick(cptr->acpt), cptr->hopcount - cptr->sendB,
+ cptr->hopcount - cptr->receiveB, cptr->receiveK,
+ (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
+ (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
+ }
+ else
+ sendto_one(cptr->acpt,
+ "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
+ NumServ(&me), NumNick(cptr->acpt), cptr->name,
+ (int)(now + cptr->since - cptr->firsttime));
+ }
+ else
+ sendto_one(cptr->acpt,
+ "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
+ NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->port);
+ }
+ }
+ close(cptr->fd);
+ loc_clients[cptr->fd] = NULL;
+ if (cptr->acpt)
+ ClearAskedPing(cptr->acpt);
+ RunFree((char *)cptr->confs);
+ free_client(cptr);
+}
+
+void cancel_ping(aClient *sptr, aClient *acptr)
+{
+ int i;
+ aClient *cptr;
+
+ Debug((DEBUG_DEBUG, "Cancelling uping for %p (%s)", sptr, sptr->name));
+ for (i = highest_fd; i >= 0; i--)
+ if ((cptr = loc_clients[i]) && IsPing(cptr) && cptr->acpt == sptr)
+ {
+ cptr->acpt = acptr;
+ del_queries((char *)cptr);
+ end_ping(cptr);
+ break;
+ }
+
+ ClearAskedPing(sptr);
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_serv.c (formerly ircd/s_msg.c)
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdlib.h>
+#include "h.h"
+#include "struct.h"
+#include "ircd.h"
+#include "s_serv.h"
+#include "s_misc.h"
+#include "sprintf_irc.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "s_bsd.h"
+#include "s_conf.h"
+#include "hash.h"
+#include "common.h"
+#include "match.h"
+#include "crule.h"
+#include "parse.h"
+#include "numnicks.h"
+#include "userload.h"
+#include "s_user.h"
+#include "channel.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+RCSTAG_CC("$Id$");
+
+static int exit_new_server(aClient *cptr, aClient *sptr,
+ char *host, time_t timestamp, char *fmt, ...)
+ __attribute__ ((format(printf, 5, 6)));
+
+/* *INDENT-OFF* */
+
+#ifdef CRYPT_LINK_PASSWORD
+__BEGIN_DECLS
+/* This is not ANSI, but we have it anyway... */
+char *crypt(const char *key, const char *salt);
+__END_DECLS
+#endif /* CRYPT_LINK_PASSWORD */
+
+/* *INDENT-ON* */
+
+unsigned int max_connection_count = 0, max_client_count = 0;
+
+static int exit_new_server(aClient *cptr, aClient *sptr,
+ char *host, time_t timestamp, char *fmt, ...)
+{
+ va_list vl;
+ char *buf =
+ (char *)RunMalloc(strlen(me.name) + strlen(host) + 22 + strlen(fmt));
+ va_start(vl, fmt);
+ if (!IsServer(sptr))
+ return vexit_client_msg(cptr, cptr, &me, fmt, vl);
+ sprintf_irc(buf, ":%s SQUIT %s " TIME_T_FMT " :", me.name, host, timestamp);
+ strcat(buf, fmt);
+ vsendto_one(cptr, buf, vl);
+ va_end(vl);
+ RunFree(buf);
+ return 0;
+}
+
+static int a_kills_b_too(aClient *a, aClient *b)
+{
+ for (; b != a && b != &me; b = b->serv->up);
+ return (a == b ? 1 : 0);
+}
+
+extern unsigned short server_port;
+
+/*
+ * m_server
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ * parv[2] = hopcount
+ * parv[3] = start timestamp
+ * parv[4] = link timestamp
+ * parv[5] = major protocol version: P09/P10
+ * parv[parc-1] = serverinfo
+ * If cptr is P10:
+ * parv[6] = "YMM", where 'Y' is the server numeric and "MM" is the
+ * numeric nick mask of this server.
+ * parv[7] = 0 (not used yet, mandatory unsigned int after u2.10.06)
+ */
+int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 char *ch;
+ Reg2 int i;
+ char info[REALLEN + 1], *inpath, *host, *s;
+ aClient *acptr, *bcptr, *LHcptr = NULL;
+ aConfItem *aconf = NULL, *bconf = NULL, *cconf, *lhconf = NULL;
+ int hop, ret, active_lh_line = 0;
+ unsigned short int prot;
+ time_t start_timestamp, timestamp = 0, recv_time, ghost = 0;
+
+ if (IsUser(cptr))
+ {
+ sendto_one(cptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]);
+ return 0;
+ }
+
+ if (IsUserPort(cptr))
+ return exit_client_msg(cptr, cptr, &me,
+ "You cannot connect a server to a user port; connect to %s port %u",
+ me.name, server_port);
+
+ recv_time = TStime();
+ info[0] = '\0';
+ inpath = get_client_name(cptr, TRUE);
+ if (parc < 7)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SERVER");
+ return exit_client(cptr, cptr, &me, "Need more parameters");
+ }
+ host = parv[1];
+ /* Detect protocol */
+ if (strlen(parv[5]) != 3 || (parv[5][0] != 'P' && parv[5][0] != 'J'))
+ return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
+ if (!IsServer(cptr)) /* Don't allow silently connecting a server */
+ *parv[5] = 'J';
+ prot = atoi(parv[5] + 1);
+ if (prot > atoi(MAJOR_PROTOCOL))
+ prot = atoi(MAJOR_PROTOCOL);
+ /* Because the previous test is only in 2.10, the following is needed
+ * till all servers are 2.10: */
+ if (IsServer(cptr) && prot > Protocol(cptr))
+ prot = Protocol(cptr);
+ hop = atoi(parv[2]);
+ start_timestamp = atoi(parv[3]);
+ timestamp = atoi(parv[4]);
+ Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age " TIME_T_FMT " ("
+ TIME_T_FMT ")", host, parv[4], start_timestamp, me.serv->timestamp));
+ if ((timestamp < 780000000 || (hop == 1 && start_timestamp < 780000000)))
+ {
+ return exit_client_msg(cptr, sptr, &me,
+ "Bogus timestamps (%s %s)", parv[3], parv[4]);
+ }
+ strncpy(info, parv[parc - 1], REALLEN);
+ info[REALLEN] = 0;
+ if (prot < atoi(MINOR_PROTOCOL))
+ {
+ sendto_ops("Got incompatible protocol version (%s) from %s",
+ parv[5], get_client_name(cptr, TRUE));
+ return exit_new_server(cptr, sptr, host, timestamp,
+ "Incompatible protocol: %s", parv[5]);
+ }
+ /*
+ * Check for "FRENCH " infection ;-) (actually this should
+ * be replaced with routine to check the hostname syntax in
+ * general). [ This check is still needed, even after the parse
+ * is fixed, because someone can send "SERVER :foo bar " ].
+ * Also, changed to check other "difficult" characters, now
+ * that parse lets all through... --msa
+ */
+ if (strlen(host) > HOSTLEN)
+ host[HOSTLEN] = '\0';
+ for (ch = host; *ch; ch++)
+ if (*ch <= ' ' || *ch > '~')
+ break;
+ if (*ch || !strchr(host, '.'))
+ {
+ sendto_ops("Bogus server name (%s) from %s",
+ host, get_client_name(cptr, TRUE));
+ return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
+ }
+
+ if (IsServer(cptr))
+ {
+ /*
+ * A local server introduces a new server behind this link.
+ * Check if this is allowed according L:, H: and Q: lines.
+ */
+ if (info[0] == '\0')
+ return exit_client_msg(cptr, cptr, &me,
+ "No server info specified for %s", host);
+
+ /*
+ * See if the newly found server is behind a guaranteed
+ * leaf (L-line). If so, close the link.
+ */
+ if ((lhconf = find_conf_host(cptr->confs, host, CONF_LEAF)) &&
+ (!lhconf->port || (hop > lhconf->port)))
+ {
+ /*
+ * L: lines normally come in pairs, here we try to
+ * make sure that the oldest link is squitted, not
+ * both.
+ */
+ active_lh_line = 1;
+ if (timestamp <= cptr->serv->timestamp)
+ LHcptr = NULL; /* Kill incoming server */
+ else
+ LHcptr = cptr; /* Squit ourselfs */
+ }
+ else if (!(lhconf = find_conf_host(cptr->confs, host, CONF_HUB)) ||
+ (lhconf->port && (hop > lhconf->port)))
+ {
+ aClient *ac3ptr;
+ active_lh_line = 2;
+ /* Look for net junction causing this: */
+ LHcptr = NULL; /* incoming server */
+ if (*parv[5] != 'J')
+ for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up)
+ if (IsJunction(ac3ptr))
+ {
+ LHcptr = ac3ptr;
+ break;
+ }
+ }
+ }
+
+ if (IsUnknown(cptr) || IsHandshake(cptr))
+ {
+ char *encr;
+
+ /*
+ * A local link that is still in undefined state wants
+ * to be a SERVER. Check if this is allowed and change
+ * status accordingly...
+ */
+ /*
+ * If there is more then one server on the same machine
+ * that we try to connect to, it could be that the /CONNECT
+ * <mask> caused this connect to be put at the wrong place
+ * in the hashtable. --Run
+ * Same thing for Unknown connections that first send NICK.
+ * --Xorath
+ * Better check if the two strings are (caseless) identical
+ * and not mess with hash internals.
+ * --Nemesi
+ */
+ if ((!(BadPtr(cptr->name)))
+ && (IsUnknown(cptr) || IsHandshake(cptr))
+ && strCasediff(cptr->name, host))
+ hChangeClient(cptr, host);
+ strncpy(cptr->name, host, sizeof(cptr->name) - 1);
+ strncpy(cptr->info, info[0] ? info : me.name, sizeof(cptr->info) - 1);
+ cptr->hopcount = hop;
+
+ /* check connection rules */
+ for (cconf = conf; cconf; cconf = cconf->next)
+ if ((cconf->status == CONF_CRULEALL) && (match(cconf->host, host) == 0))
+ if (crule_eval(cconf->passwd))
+ {
+ ircstp->is_ref++;
+ sendto_ops("Refused connection from %s.", get_client_host(cptr));
+ return exit_client(cptr, cptr, &me, "Disallowed by connection rule");
+ }
+
+ if (check_server(cptr))
+ {
+ ircstp->is_ref++;
+ sendto_ops("Received unauthorized connection from %s.",
+ get_client_host(cptr));
+ return exit_client(cptr, cptr, &me, "No C/N conf lines");
+ }
+
+ host = cptr->name;
+
+ update_load();
+
+ if (!(aconf = find_conf(cptr->confs, host, CONF_NOCONNECT_SERVER)))
+ {
+ ircstp->is_ref++;
+#ifndef GODMODE
+ sendto_ops("Access denied. No N line for server %s", inpath);
+ return exit_client_msg(cptr, cptr, &me,
+ "Access denied. No N line for server %s", inpath);
+#else /* GODMODE */
+ sendto_ops("General C/N: line active: No N line for server %s", inpath);
+ aconf =
+ find_conf(cptr->confs, "general.undernet.org", CONF_NOCONNECT_SERVER);
+ bconf =
+ find_conf(cptr->confs, "general.undernet.org", CONF_CONNECT_SERVER);
+ if (!aconf || !bconf)
+ {
+ sendto_ops("Neither C/N lines for server %s nor "
+ "\"general.undernet.org\"", inpath);
+ return exit_client_msg(cptr, cptr, &me,
+ "No C/N lines for server %s", inpath);
+ }
+#endif /* GODMODE */
+ }
+ else if (!(bconf = find_conf(cptr->confs, host, CONF_CONNECT_SERVER)))
+ {
+ ircstp->is_ref++;
+ sendto_ops("Only N (no C) field for server %s", inpath);
+ return exit_client_msg(cptr, cptr, &me,
+ "Only N (no C) field for server %s", inpath);
+ }
+
+#ifdef CRYPT_LINK_PASSWORD
+ /* passwd may be NULL. Head it off at the pass... */
+ if (*cptr->passwd)
+ {
+ char salt[3];
+
+ salt[0] = aconf->passwd[0];
+ salt[1] = aconf->passwd[1];
+ salt[2] = '\0';
+ encr = crypt(cptr->passwd, salt);
+ }
+ else
+ encr = "";
+#else
+ encr = cptr->passwd;
+#endif /* CRYPT_LINK_PASSWORD */
+#ifndef GODMODE
+ if (*aconf->passwd && !!strcmp(aconf->passwd, encr))
+ {
+ ircstp->is_ref++;
+ sendto_ops("Access denied (passwd mismatch) %s", inpath);
+ return exit_client_msg(cptr, cptr, &me,
+ "No Access (passwd mismatch) %s", inpath);
+ }
+#endif /* not GODMODE */
+ memset(cptr->passwd, 0, sizeof(cptr->passwd));
+
+#ifndef HUB
+ for (i = 0; i <= highest_fd; i++)
+ if (loc_clients[i] && IsServer(loc_clients[i]))
+ {
+ active_lh_line = 3;
+ LHcptr = NULL;
+ break;
+ }
+#endif
+ if (!IsUnknown(cptr))
+ {
+ s = strchr(aconf->host, '@');
+ *s = '\0'; /* should never be NULL */
+ Debug((DEBUG_INFO, "Check Usernames [%s]vs[%s]",
+ aconf->host, cptr->username));
+ if (match(aconf->host, cptr->username))
+ {
+ *s = '@';
+ ircstp->is_ref++;
+ sendto_ops("Username mismatch [%s]v[%s] : %s",
+ aconf->host, cptr->username, get_client_name(cptr, TRUE));
+ return exit_client(cptr, cptr, &me, "Bad Username");
+ }
+ *s = '@';
+ }
+ }
+
+ /*
+ * We want to find IsConnecting() and IsHandshake() too,
+ * use FindClient().
+ * The second finds collisions with numeric representation of existing
+ * servers - these shouldn't happen anymore when all upgraded to 2.10.
+ * -- Run
+ */
+ while ((acptr = FindClient(host)) ||
+ (parc > 7 && (acptr = FindNServer(parv[6]))))
+ {
+ /*
+ * This link is trying feed me a server that I already have
+ * access through another path
+ *
+ * Do not allow Uworld to do this.
+ * Do not allow servers that are juped.
+ * Do not allow servers that have older link timestamps
+ * then this try.
+ * Do not allow servers that use the same numeric as an existing
+ * server, but have a different name.
+ *
+ * If my ircd.conf sucks, I can try to connect to myself:
+ */
+ if (acptr == &me)
+ return exit_client_msg(cptr, cptr, &me,
+ "nick collision with me (%s)", host);
+ /*
+ * Detect wrong numeric.
+ */
+ if (strCasediff(acptr->name, host))
+ {
+ sendto_serv_butone(cptr,
+ ":%s WALLOPS :SERVER Numeric Collision: %s != %s",
+ me.name, acptr->name, host);
+ return exit_client_msg(cptr, cptr, &me,
+ "NUMERIC collision between %s and %s."
+ " Is your server numeric correct ?", host, acptr->name);
+ }
+ /*
+ * Kill our try, if we had one.
+ */
+ if (IsConnecting(acptr))
+ {
+ if (!active_lh_line && exit_client(cptr, acptr, &me,
+ "Just connected via another link") == CPTR_KILLED)
+ return CPTR_KILLED;
+ /*
+ * We can have only ONE 'IsConnecting', 'IsHandshake' or
+ * 'IsServer', because new 'IsConnecting's are refused to
+ * the same server if we already had it.
+ */
+ break;
+ }
+ /*
+ * Avoid other nick collisions...
+ * This is a doubtfull test though, what else would it be
+ * when it has a server.name ?
+ */
+ else if (!IsServer(acptr) && !IsHandshake(acptr))
+ return exit_client_msg(cptr, cptr, &me,
+ "Nickname %s already exists!", host);
+ /*
+ * Our new server might be a juped server,
+ * or someone trying abuse a second Uworld:
+ */
+ else if (IsServer(acptr) && (strnCasecmp(acptr->info, "JUPE", 4) == 0 ||
+ find_conf_host(cptr->confs, acptr->name, CONF_UWORLD)))
+ {
+ if (!IsServer(sptr))
+ return exit_client(cptr, sptr, &me, acptr->info);
+ sendto_one(cptr, ":%s WALLOPS :Received :%s SERVER %s from %s !?!",
+ me.name, parv[0], parv[1], cptr->name);
+ return exit_new_server(cptr, sptr, host, timestamp, "%s", acptr->info);
+ }
+ /*
+ * Of course we find the handshake this link was before :)
+ */
+ else if (IsHandshake(acptr) && acptr == cptr)
+ break;
+ /*
+ * Here we have a server nick collision...
+ * We don't want to kill the link that was last /connected,
+ * but we neither want to kill a good (old) link.
+ * Therefor we kill the second youngest link.
+ */
+ if (1)
+ {
+ aClient *c2ptr = NULL, *c3ptr = acptr;
+ aClient *ac2ptr, *ac3ptr;
+
+ /* Search youngest link: */
+ for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up)
+ if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp)
+ c3ptr = ac3ptr;
+ if (IsServer(sptr))
+ {
+ for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up)
+ if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp)
+ c3ptr = ac3ptr;
+ }
+ if (timestamp > c3ptr->serv->timestamp)
+ {
+ c3ptr = NULL;
+ c2ptr = acptr; /* Make sure they differ */
+ }
+ /* Search second youngest link: */
+ for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up)
+ if (ac2ptr != c3ptr &&
+ ac2ptr->serv->timestamp >
+ (c2ptr ? c2ptr->serv->timestamp : timestamp))
+ c2ptr = ac2ptr;
+ if (IsServer(sptr))
+ {
+ for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up)
+ if (ac2ptr != c3ptr &&
+ ac2ptr->serv->timestamp >
+ (c2ptr ? c2ptr->serv->timestamp : timestamp))
+ c2ptr = ac2ptr;
+ }
+ if (c3ptr && timestamp > (c2ptr ? c2ptr->serv->timestamp : timestamp))
+ c2ptr = NULL;
+ /* If timestamps are equal, decide which link to break
+ * by name.
+ */
+ if ((c2ptr ? c2ptr->serv->timestamp : timestamp) ==
+ (c3ptr ? c3ptr->serv->timestamp : timestamp))
+ {
+ char *n2, *n2up;
+ char *n3, *n3up;
+ if (c2ptr)
+ {
+ n2 = c2ptr->name;
+ n2up = MyConnect(c2ptr) ? me.name : c2ptr->serv->up->name;
+ }
+ else
+ {
+ n2 = host;
+ n2up = IsServer(sptr) ? sptr->name : me.name;
+ }
+ if (c3ptr)
+ {
+ n3 = c3ptr->name;
+ n3up = MyConnect(c3ptr) ? me.name : c3ptr->serv->up->name;
+ }
+ else
+ {
+ n3 = host;
+ n3up = IsServer(sptr) ? sptr->name : me.name;
+ }
+ if (strcmp(n2, n2up) > 0)
+ n2 = n2up;
+ if (strcmp(n3, n3up) > 0)
+ n3 = n3up;
+ if (strcmp(n3, n2) > 0)
+ {
+ ac2ptr = c2ptr;
+ c2ptr = c3ptr;
+ c3ptr = ac2ptr;
+ }
+ }
+ /* Now squit the second youngest link: */
+ if (!c2ptr)
+ return exit_new_server(cptr, sptr, host, timestamp,
+ "server %s already exists and is %ld seconds younger.",
+ host, (long)acptr->serv->timestamp - (long)timestamp);
+ else if (c2ptr->from == cptr || IsServer(sptr))
+ {
+ aClient *killedptrfrom = c2ptr->from;
+ if (active_lh_line)
+ {
+ /*
+ * If the L: or H: line also gets rid of this link,
+ * we sent just one squit.
+ */
+ if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
+ break;
+ /*
+ * If breaking the loop here solves the L: or H:
+ * line problem, we don't squit that.
+ */
+ if (c2ptr->from == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
+ active_lh_line = 0;
+ else
+ {
+ /*
+ * If we still have a L: or H: line problem,
+ * we prefer to squit the new server, solving
+ * loop and L:/H: line problem with only one squit.
+ */
+ LHcptr = NULL;
+ break;
+ }
+ }
+ /*
+ * If the new server was introduced by a server that caused a
+ * Ghost less then 20 seconds ago, this is probably also
+ * a Ghost... (20 seconds is more then enough because all
+ * SERVER messages are at the beginning of a net.burst). --Run
+ */
+ if (now - cptr->serv->ghost < 20)
+ {
+ killedptrfrom = acptr->from;
+ if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
+ return CPTR_KILLED;
+ }
+ else if (exit_client_msg(cptr, c2ptr, &me,
+ "Loop <-- %s (new link is %ld seconds younger)", host,
+ (c3ptr ? (long)c3ptr->serv->timestamp : timestamp) -
+ (long)c2ptr->serv->timestamp) == CPTR_KILLED)
+ return CPTR_KILLED;
+ /*
+ * Did we kill the incoming server off already ?
+ */
+ if (killedptrfrom == cptr)
+ return 0;
+ }
+ else
+ {
+ if (active_lh_line)
+ {
+ if (LHcptr && a_kills_b_too(LHcptr, acptr))
+ break;
+ if (acptr->from == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
+ active_lh_line = 0;
+ else
+ {
+ LHcptr = NULL;
+ break;
+ }
+ }
+ /*
+ * We can't believe it is a lagged server message
+ * when it directly connects to us...
+ * kill the older link at the ghost, rather then
+ * at the second youngest link, assuming it isn't
+ * a REAL loop.
+ */
+ ghost = now; /* Mark that it caused a ghost */
+ if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
+ return CPTR_KILLED;
+ break;
+ }
+ }
+ }
+
+ if (active_lh_line)
+ {
+ if (LHcptr == NULL)
+ return exit_new_server(cptr, sptr, host, timestamp,
+ (active_lh_line == 2) ?
+ "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
+ get_client_name(cptr, TRUE), host,
+ lhconf ? (lhconf->host ? lhconf->host : "*") : "!");
+ else
+ {
+ register int killed = a_kills_b_too(LHcptr, sptr);
+ if (active_lh_line < 3)
+ {
+ if (exit_client_msg(cptr, LHcptr, &me,
+ (active_lh_line == 2) ?
+ "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
+ get_client_name(cptr, TRUE), host,
+ lhconf ? (lhconf->host ? lhconf->host : "*") : "!") == CPTR_KILLED)
+ return CPTR_KILLED;
+ }
+ else
+ {
+ ircstp->is_ref++;
+ if (exit_client(cptr, LHcptr, &me, "I'm a leaf") == CPTR_KILLED)
+ return CPTR_KILLED;
+ }
+ /*
+ * Did we kill the incoming server off already ?
+ */
+ if (killed)
+ return 0;
+ }
+ }
+
+ if (IsServer(cptr))
+ {
+ /*
+ * Server is informing about a new server behind
+ * this link. Create REMOTE server structure,
+ * add it to list and propagate word to my other
+ * server links...
+ */
+
+ acptr = make_client(cptr, STAT_SERVER);
+ make_server(acptr);
+ acptr->serv->prot = prot;
+ acptr->serv->timestamp = timestamp;
+ acptr->hopcount = hop;
+ strncpy(acptr->name, host, sizeof(acptr->name) - 1);
+ strncpy(acptr->info, info, sizeof(acptr->info) - 1);
+ acptr->serv->up = sptr;
+ acptr->serv->updown = add_dlink(&sptr->serv->down, acptr);
+ /* Use cptr, because we do protocol 9 -> 10 translation
+ for numeric nicks ! */
+ SetServerYXX(cptr, acptr, parv[6]);
+ Count_newremoteserver(nrof);
+ if (Protocol(acptr) < 10)
+ acptr->flags |= FLAGS_TS8;
+ add_client_to_list(acptr);
+ hAddClient(acptr);
+ if (*parv[5] == 'J')
+ {
+ if (Protocol(acptr) > 9)
+ SetBurst(acptr);
+ sendto_op_mask(SNO_NETWORK, "Net junction: %s %s",
+ sptr->name, acptr->name);
+ SetJunction(acptr);
+ }
+ /*
+ * Old sendto_serv_but_one() call removed because we now need to send
+ * different names to different servers (domain name matching).
+ */
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(bcptr = loc_clients[i]) || !IsServer(bcptr) ||
+ bcptr == cptr || IsMe(bcptr))
+ continue;
+ if (!(cconf = bcptr->serv->nline))
+ {
+ sendto_ops("Lost N-line for %s on %s. Closing",
+ get_client_name(cptr, TRUE), host);
+ return exit_client(cptr, cptr, &me, "Lost N line");
+ }
+ if (match(my_name_for_link(me.name, cconf), acptr->name) == 0)
+ continue;
+ if (Protocol(bcptr) > 9)
+ sendto_one(bcptr, "%s SERVER %s %d 0 %s %s %s%s 0 :%s",
+ NumServ(sptr), acptr->name, hop + 1, parv[4], parv[5],
+ NumServCap(acptr), acptr->info);
+ else
+ sendto_one(bcptr, ":%s SERVER %s %d 0 %s %s %s%s 0 :%s",
+ parv[0], acptr->name, hop + 1, parv[4], parv[5],
+ NumServCap(acptr), acptr->info);
+ }
+ return 0;
+ }
+
+ if (IsUnknown(cptr) || IsHandshake(cptr))
+ {
+ make_server(cptr);
+ cptr->serv->timestamp = timestamp;
+ cptr->serv->prot = prot;
+ cptr->serv->ghost = ghost;
+ SetServerYXX(cptr, cptr, parv[6]);
+ if (start_timestamp > 780000000)
+ {
+#ifndef RELIABLE_CLOCK
+#ifdef TESTNET
+ sendto_ops("Debug: my start time: " TIME_T_FMT " ; others start time: "
+ TIME_T_FMT, me.serv->timestamp, start_timestamp);
+ sendto_ops("Debug: receive time: " TIME_T_FMT " ; received timestamp: "
+ TIME_T_FMT " ; difference %ld",
+ recv_time, timestamp, timestamp - recv_time);
+#endif
+ if (start_timestamp < me.serv->timestamp)
+ {
+ sendto_ops("got earlier start time: " TIME_T_FMT " < " TIME_T_FMT,
+ start_timestamp, me.serv->timestamp);
+ me.serv->timestamp = start_timestamp;
+ TSoffset += timestamp - recv_time;
+ sendto_ops("clock adjusted by adding %d", (int)(timestamp - recv_time));
+ }
+ else if ((start_timestamp > me.serv->timestamp) && IsUnknown(cptr))
+ cptr->serv->timestamp = TStime();
+
+ else if (timestamp != recv_time)
+ /* Equal start times, we have a collision. Let the connected-to server
+ decide. This assumes leafs issue more than half of the connection
+ attempts. */
+ {
+ if (IsUnknown(cptr))
+ cptr->serv->timestamp = TStime();
+ else if (IsHandshake(cptr))
+ {
+ sendto_ops("clock adjusted by adding %d",
+ (int)(timestamp - recv_time));
+ TSoffset += timestamp - recv_time;
+ }
+ }
+#else /* RELIABLE CLOCK IS TRUE, we _always_ use our own clock */
+ if (start_timestamp < me.serv->timestamp)
+ me.serv->timestamp = start_timestamp;
+ if (IsUnknown(cptr))
+ cptr->serv->timestamp = TStime();
+#endif
+ }
+
+ ret = m_server_estab(cptr, aconf, bconf);
+ }
+ else
+ ret = 0;
+#ifdef RELIABLE_CLOCK
+ if (abs(cptr->serv->timestamp - recv_time) > 30)
+ {
+ sendto_ops("Connected to a net with a timestamp-clock"
+ " difference of " STIME_T_FMT " seconds! Used SETTIME to correct"
+ " this.", timestamp - recv_time);
+ sendto_one(cptr, ":%s SETTIME " TIME_T_FMT " :%s",
+ me.name, TStime(), me.name);
+ }
+#endif
+
+ return ret;
+}
+
+/*
+ * m_server_estab
+ *
+ * May only be called after a SERVER was received from cptr,
+ * and thus make_server was called, and serv->prot set. --Run
+ */
+int m_server_estab(aClient *cptr, aConfItem *aconf, aConfItem *bconf)
+{
+ Reg3 aClient *acptr;
+ char *inpath, *host;
+ int split, i;
+
+ split = (strCasediff(cptr->name, cptr->sockhost)
+ && strnCasecmp(cptr->info, "JUPE", 4));
+ inpath = get_client_name(cptr, TRUE);
+ host = cptr->name;
+
+ if (IsUnknown(cptr))
+ {
+ if (bconf->passwd[0])
+ sendto_one(cptr, "PASS :%s", bconf->passwd);
+ /*
+ * Pass my info to the new server
+ */
+ sendto_one(cptr, "SERVER %s 1 " TIME_T_FMT " " TIME_T_FMT " J%s %s%s :%s",
+ my_name_for_link(me.name, aconf), me.serv->timestamp,
+ cptr->serv->timestamp, MAJOR_PROTOCOL, NumServCap(&me),
+ (me.info[0]) ? (me.info) : "IRCers United");
+
+ IPcheck_connect_fail(cptr); /* Don't charge this IP# for connecting */
+ }
+
+ det_confs_butmask(cptr,
+ CONF_LEAF | CONF_HUB | CONF_NOCONNECT_SERVER | CONF_UWORLD);
+
+ if (!IsHandshake(cptr))
+ hAddClient(cptr);
+ SetServer(cptr);
+ Count_unknownbecomesserver(nrof);
+ if (Protocol(cptr) > 9)
+ SetBurst(cptr);
+ else
+ cptr->flags |= FLAGS_TS8;
+ nextping = now;
+ if (cptr->serv->user && *cptr->serv->by &&
+ (acptr = findNUser(cptr->serv->by)) && acptr->user == cptr->serv->user)
+ {
+ if (MyUser(acptr) || Protocol(acptr->from) < 10)
+ sendto_one(acptr, ":%s NOTICE %s :Link with %s established.",
+ me.name, acptr->name, inpath);
+ else
+ sendto_one(acptr, "%s NOTICE %s%s :Link with %s established.",
+ NumServ(&me), NumNick(acptr), inpath);
+ }
+ else
+ acptr = NULL;
+ sendto_lops_butone(acptr, "Link with %s established.", inpath);
+ cptr->serv->up = &me;
+ cptr->serv->updown = add_dlink(&me.serv->down, cptr);
+ cptr->serv->nline = aconf;
+ sendto_op_mask(SNO_NETWORK, "Net junction: %s %s", me.name, cptr->name);
+ SetJunction(cptr);
+ /*
+ * Old sendto_serv_but_one() call removed because we now
+ * need to send different names to different servers
+ * (domain name matching) Send new server to other servers.
+ */
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = loc_clients[i]) || !IsServer(acptr) ||
+ acptr == cptr || IsMe(acptr))
+ continue;
+ if ((aconf = acptr->serv->nline) &&
+ !match(my_name_for_link(me.name, aconf), cptr->name))
+ continue;
+ if (split)
+ {
+ if (Protocol(acptr) > 9)
+ sendto_one(acptr,
+ "%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s",
+ NumServ(&me), cptr->name, cptr->serv->timestamp,
+ (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr), NumServCap(cptr),
+ cptr->sockhost, cptr->info);
+ else
+ sendto_one(acptr,
+ ":%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s", me.name,
+ cptr->name, cptr->serv->timestamp,
+ (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr), NumServCap(cptr),
+ cptr->sockhost, cptr->info);
+ }
+ else
+ {
+ if (Protocol(acptr) > 9)
+ sendto_one(acptr, "%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+ NumServ(&me), cptr->name, cptr->serv->timestamp,
+ (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr),
+ NumServCap(cptr), cptr->info);
+ else
+ sendto_one(acptr, ":%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+ me.name, cptr->name, cptr->serv->timestamp,
+ (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr),
+ NumServCap(cptr), cptr->info);
+ }
+ }
+
+ /*
+ * Pass on my client information to the new server
+ *
+ * First, pass only servers (idea is that if the link gets
+ * cancelled beacause the server was already there,
+ * there are no NICK's to be cancelled...). Of course,
+ * if cancellation occurs, all this info is sent anyway,
+ * and I guess the link dies when a read is attempted...? --msa
+ *
+ * Note: Link cancellation to occur at this point means
+ * that at least two servers from my fragment are building
+ * up connection this other fragment at the same time, it's
+ * a race condition, not the normal way of operation...
+ */
+
+ aconf = cptr->serv->nline;
+ for (acptr = &me; acptr; acptr = acptr->prev)
+ {
+ /* acptr->from == acptr for acptr == cptr */
+ if (acptr->from == cptr)
+ continue;
+ if (IsServer(acptr))
+ {
+ char *protocol_str =
+ (Protocol(acptr) > 9) ? (IsBurst(acptr) ? "J" : "P") : "P0";
+ if (match(my_name_for_link(me.name, aconf), acptr->name) == 0)
+ continue;
+ split = (MyConnect(acptr) && strCasediff(acptr->name, acptr->sockhost) &&
+ strnCasecmp(acptr->info, "JUPE", 4));
+ if (split)
+ {
+ if (Protocol(cptr) > 9)
+ sendto_one(cptr,
+ "%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s",
+ NumServ(acptr->serv->up), acptr->name,
+ acptr->hopcount + 1, acptr->serv->timestamp,
+ protocol_str, Protocol(acptr),
+ NumServCap(acptr), acptr->sockhost, acptr->info);
+ else
+ sendto_one(cptr,
+ ":%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s",
+ acptr->serv->up->name, acptr->name,
+ acptr->hopcount + 1, acptr->serv->timestamp,
+ protocol_str, Protocol(acptr),
+ NumServCap(acptr), acptr->sockhost, acptr->info);
+ }
+ else
+ {
+ if (Protocol(cptr) > 9)
+ sendto_one(cptr,
+ "%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+ NumServ(acptr->serv->up), acptr->name,
+ acptr->hopcount + 1, acptr->serv->timestamp,
+ protocol_str, Protocol(acptr), NumServCap(acptr), acptr->info);
+ else
+ sendto_one(cptr,
+ ":%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+ acptr->serv->up->name, acptr->name,
+ acptr->hopcount + 1, acptr->serv->timestamp,
+ protocol_str, Protocol(acptr), NumServCap(acptr), acptr->info);
+ }
+ }
+ }
+
+ for (acptr = &me; acptr; acptr = acptr->prev)
+ {
+ /* acptr->from == acptr for acptr == cptr */
+ if (acptr->from == cptr)
+ continue;
+ if (IsUser(acptr))
+ {
+ if (Protocol(cptr) < 10)
+ {
+ /*
+ * IsUser(x) is true only *BOTH* NICK and USER have
+ * been received. -avalon
+ * Or only NICK in new format. --Run
+ */
+ sendto_one(cptr, ":%s NICK %s %d " TIME_T_FMT " %s %s %s :%s",
+ acptr->user->server->name,
+ acptr->name, acptr->hopcount + 1, acptr->lastnick,
+ acptr->user->username, acptr->user->host,
+ acptr->user->server->name, acptr->info);
+ send_umode(cptr, acptr, 0, SEND_UMODES);
+ send_user_joins(cptr, acptr);
+ }
+ else
+ {
+ char xxx_buf[8];
+ char *s = umode_str(acptr);
+ sendto_one(cptr, *s ?
+ "%s NICK %s %d " TIME_T_FMT " %s %s +%s %s %s%s :%s" :
+ "%s NICK %s %d " TIME_T_FMT " %s %s %s%s %s%s :%s",
+ NumServ(acptr->user->server),
+ acptr->name, acptr->hopcount + 1, acptr->lastnick,
+ acptr->user->username, acptr->user->host,
+ s, inttobase64(xxx_buf, ntohl(acptr->ip.s_addr), 6),
+ NumNick(acptr), acptr->info);
+ }
+ }
+ }
+ /*
+ * Last, send the BURST.
+ * (Or for 2.9 servers: pass all channels plus statuses)
+ */
+ {
+ Reg1 aChannel *chptr;
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ send_channel_modes(cptr, chptr);
+ }
+ if (Protocol(cptr) >= 10)
+ sendto_one(cptr, "%s END_OF_BURST", NumServ(&me));
+ return 0;
+}
+
+/*
+ * m_error
+ *
+ * parv[0] = sender prefix
+ * parv[parc-1] = text
+ */
+int m_error(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 char *para;
+
+ para = (parc > 1 && *parv[parc - 1] != '\0') ? parv[parc - 1] : "<>";
+
+ Debug((DEBUG_ERROR, "Received ERROR message from %s: %s", sptr->name, para));
+ /*
+ * Ignore error messages generated by normal user clients
+ * (because ill-behaving user clients would flood opers
+ * screen otherwise). Pass ERROR's from other sources to
+ * the local operator...
+ */
+ if (IsUser(cptr))
+ return 0;
+ if (IsUnknown(cptr))
+ return exit_client_msg(cptr, cptr, &me, "Register first");
+
+ if (cptr == sptr)
+ sendto_ops("ERROR :from %s -- %s", get_client_name(cptr, FALSE), para);
+ else
+ sendto_ops("ERROR :from %s via %s -- %s",
+ sptr->name, get_client_name(cptr, FALSE), para);
+
+ if (sptr->serv)
+ {
+ RunFree(sptr->serv->last_error_msg);
+ DupString(sptr->serv->last_error_msg, para);
+ }
+
+ return 0;
+}
+
+/*
+ * m_end_of_burst - Added Xorath 6-14-96, rewritten by Run 24-7-96
+ * - and fixed by record and Kev 8/1/96
+ * - and really fixed by Run 15/8/96 :p
+ * This the last message in a net.burst.
+ * It clears a flag for the server sending the burst.
+ *
+ * parv[0] - sender prefix
+ */
+int m_end_of_burst(aClient *cptr, aClient *sptr, int UNUSED(parc),
+ char **UNUSED(parv))
+{
+ if (!IsServer(sptr))
+ return 0;
+
+ sendto_op_mask(SNO_NETWORK, "Completed net.burst from %s.", sptr->name);
+#ifdef NO_PROTOCOL9
+ sendto_serv_butone(cptr, "%s END_OF_BURST", NumServ(sptr));
+#else
+ sendto_highprot_butone(cptr, 10, "%s END_OF_BURST", NumServ(sptr));
+#endif
+ ClearBurst(sptr);
+ SetBurstAck(sptr);
+ if (MyConnect(sptr))
+ sendto_one(sptr, "%s EOB_ACK", NumServ(&me));
+
+ return 0;
+}
+
+/*
+ * m_end_of_burst_ack
+ *
+ * This the acknowledge message of the `END_OF_BURST' message.
+ * It clears a flag for the server receiving the burst.
+ *
+ * parv[0] - sender prefix
+ */
+int m_end_of_burst_ack(aClient *cptr, aClient *sptr, int UNUSED(parc),
+ char **UNUSED(parv))
+{
+ if (!IsServer(sptr))
+ return 0;
+
+ sendto_op_mask(SNO_NETWORK, "%s acknowledged end of net.burst.", sptr->name);
+#ifdef NO_PROTOCOL9
+ sendto_serv_butone(cptr, "%s EOB_ACK", NumServ(sptr));
+#else
+ sendto_highprot_butone(cptr, 10, "%s EOB_ACK", NumServ(sptr));
+#endif
+ ClearBurstAck(sptr);
+
+ return 0;
+}
+
+/*
+ * m_desynch
+ *
+ * Writes to all +g users; for sending wall type debugging/anti-hack info.
+ * Added 23 Apr 1998 --Run
+ *
+ * parv[0] - sender prefix
+ * parv[parc-1] - message text
+ */
+int m_desynch(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ if (IsServer(sptr) && parc >= 2)
+ {
+ int i;
+ aClient *acptr;
+ /* Send message to local +g clients as if it were a wallops */
+ sprintf_irc(sendbuf, ":%s WALLOPS :%s", parv[0], parv[parc - 1]);
+ for (i = 0; i <= highest_fd; i++)
+ if ((acptr = loc_clients[i]) && !IsServer(acptr) && !IsMe(acptr) &&
+ SendDebug(acptr))
+ sendbufto_one(acptr);
+ /* Send message to remote +g clients */
+ sendto_g_serv_butone(cptr, "%s DESYNCH :%s", NumServ(sptr), parv[parc - 1]);
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.c)
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "match.h"
+#include "hash.h"
+#include "s_bsd.h"
+#include "whowas.h"
+#include "list.h"
+#include "s_err.h"
+#include "parse.h"
+#include "ircd.h"
+#include "s_user.h"
+#include "support.h"
+#include "s_user.h"
+#include "channel.h"
+#include "random.h"
+#include "version.h"
+#include "msg.h"
+#include "userload.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+#include "class.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+/* This is not ANSI, but we have it anyway... */
+__BEGIN_DECLS
+char *crypt(const char *key, const char *salt);
+__END_DECLS
+
+/* *INDENT-ON* */
+
+static char buf[BUFSIZE], buf2[BUFSIZE];
+
+/*
+ * m_functions execute protocol messages on this server:
+ *
+ * cptr is always NON-NULL, pointing to a *LOCAL* client
+ * structure (with an open socket connected!). This
+ * identifies the physical socket where the message
+ * originated (or which caused the m_function to be
+ * executed--some m_functions may call others...).
+ *
+ * sptr is the source of the message, defined by the
+ * prefix part of the message if present. If not
+ * or prefix not found, then sptr==cptr.
+ *
+ * (!IsServer(cptr)) => (cptr == sptr), because
+ * prefixes are taken *only* from servers...
+ *
+ * (IsServer(cptr))
+ * (sptr == cptr) => the message didn't
+ * have the prefix.
+ *
+ * (sptr != cptr && IsServer(sptr) means
+ * the prefix specified servername. (?)
+ *
+ * (sptr != cptr && !IsServer(sptr) means
+ * that message originated from a remote
+ * user (not local).
+ *
+ * combining
+ *
+ * (!IsServer(sptr)) means that, sptr can safely
+ * taken as defining the target structure of the
+ * message in this server.
+ *
+ * *Always* true (if 'parse' and others are working correct):
+ *
+ * 1) sptr->from == cptr (note: cptr->from == cptr)
+ *
+ * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+ * *cannot* be a local connection, unless it's
+ * actually cptr!). [MyConnect(x) should probably
+ * be defined as (x == x->from) --msa ]
+ *
+ * parc number of variable parameter strings (if zero,
+ * parv is allowed to be NULL)
+ *
+ * parv a NULL terminated list of parameter pointers,
+ *
+ * parv[0], sender (prefix string), if not present
+ * this points to an empty string.
+ * parv[1]...parv[parc-1]
+ * pointers to additional parameters
+ * parv[parc] == NULL, *always*
+ *
+ * note: it is guaranteed that parv[0]..parv[parc-1] are all
+ * non-NULL pointers.
+ */
+
+/*
+ * next_client
+ *
+ * Local function to find the next matching client. The search
+ * can be continued from the specified client entry. Normal
+ * usage loop is:
+ *
+ * for (x = client; x = next_client(x,mask); x = x->next)
+ * HandleMatchingClient;
+ *
+ */
+aClient *next_client(aClient *next, char *ch)
+{
+ Reg3 aClient *tmp = next;
+
+ if (!tmp)
+ return NULL;
+
+ next = FindClient(ch);
+ next = next ? next : tmp;
+ if (tmp->prev == next)
+ return NULL;
+ if (next != tmp)
+ return next;
+ for (; next; next = next->next)
+ if (!match(ch, next->name))
+ break;
+ return next;
+}
+
+/*
+ * hunt_server
+ *
+ * Do the basic thing in delivering the message (command)
+ * across the relays to the specific server (server) for
+ * actions.
+ *
+ * Note: The command is a format string and *MUST* be
+ * of prefixed style (e.g. ":%s COMMAND %s ...").
+ * Command can have only max 8 parameters.
+ *
+ * server parv[server] is the parameter identifying the
+ * target server.
+ *
+ * *WARNING*
+ * parv[server] is replaced with the pointer to the
+ * real servername from the matched client (I'm lazy
+ * now --msa).
+ *
+ * returns: (see #defines)
+ */
+int hunt_server(int MustBeOper, aClient *cptr, aClient *sptr, char *command,
+ int server, int parc, char *parv[])
+{
+ aClient *acptr;
+ char y[8];
+
+ /* Assume it's me, if no server or an unregistered client */
+ if (parc <= server || BadPtr(parv[server]) || IsUnknown(sptr))
+ return (HUNTED_ISME);
+
+ /* Make sure it's a server */
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ {
+ /* Make sure it's a server */
+ if (!strchr(parv[server], '*'))
+ {
+ if (0 == (acptr = FindClient(parv[server])))
+ return HUNTED_NOSUCH;
+ if (acptr->user)
+ acptr = acptr->user->server;
+ }
+ else if (!(acptr = find_match_server(parv[server])))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
+ me.name, parv[0], parv[server]);
+ return (HUNTED_NOSUCH);
+ }
+ }
+ else if (!(acptr = FindNServer(parv[server])))
+ return (HUNTED_NOSUCH); /* Server broke off in the meantime */
+
+ if (IsMe(acptr))
+ return (HUNTED_ISME);
+
+ if (MustBeOper && !IsPrivileged(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name);
+ return HUNTED_NOSUCH;
+ }
+
+#ifdef NO_PROTOCOL9
+ if (MyUser(sptr))
+ {
+ strcpy(y, acptr->yxx);
+ parv[server] = y;
+ }
+#else
+ if (Protocol(acptr->from) > 9)
+ {
+ strcpy(y, acptr->yxx);
+ parv[server] = y;
+ }
+ else
+ parv[server] = acptr->name;
+#endif
+
+ sendto_one(acptr, command, parv[0], parv[1], parv[2], parv[3], parv[4],
+ parv[5], parv[6], parv[7], parv[8]);
+
+ return (HUNTED_PASS);
+}
+
+/*
+ * 'do_nick_name' ensures that the given parameter (nick) is really a proper
+ * string for a nickname (note, the 'nick' may be modified in the process...)
+ *
+ * RETURNS the length of the final NICKNAME (0, if nickname is invalid)
+ *
+ * Nickname characters are in range 'A'..'}', '_', '-', '0'..'9'
+ * anything outside the above set will terminate nickname.
+ * In addition, the first character cannot be '-' or a Digit.
+ *
+ * Note:
+ * The '~'-character should be allowed, but a change should be global,
+ * some confusion would result if only few servers allowed it...
+ */
+
+static int do_nick_name(char *nick)
+{
+ Reg1 char *ch;
+
+ if (*nick == '-' || isDigit(*nick)) /* first character in [0..9-] */
+ return 0;
+
+ for (ch = nick; *ch && (ch - nick) < NICKLEN; ch++)
+ if (!isIrcNk(*ch))
+ break;
+
+ *ch = '\0';
+
+ return (ch - nick);
+}
+
+/*
+ * canonize
+ *
+ * reduce a string of duplicate list entries to contain only the unique
+ * items. Unavoidably O(n^2).
+ */
+char *canonize(char *buffer)
+{
+ static char cbuf[BUFSIZ];
+ register char *s, *t, *cp = cbuf;
+ register int l = 0;
+ char *p = NULL, *p2;
+
+ *cp = '\0';
+
+ for (s = strtoken(&p, buffer, ","); s; s = strtoken(&p, NULL, ","))
+ {
+ if (l)
+ {
+ p2 = NULL;
+ for (t = strtoken(&p2, cbuf, ","); t; t = strtoken(&p2, NULL, ","))
+ if (!strCasediff(s, t))
+ break;
+ else if (p2)
+ p2[-1] = ',';
+ }
+ else
+ t = NULL;
+ if (!t)
+ {
+ if (l)
+ *(cp - 1) = ',';
+ else
+ l = 1;
+ strcpy(cp, s);
+ if (p)
+ cp += (p - s);
+ }
+ else if (p2)
+ p2[-1] = ',';
+ }
+ return cbuf;
+}
+
+/*
+ * clean_user_id
+ *
+ * Copy `source' to `dest', replacing all occurances of '~' and characters that
+ * are not `isIrcUi' by an underscore.
+ * Copies at most USERLEN - 1 characters or up till the first control character.
+ * If `tilde' is true, then a tilde is prepended to `dest'.
+ * Note that `dest' and `source' can point to the same area or to different
+ * non-overlapping areas.
+ */
+
+static char *clean_user_id(char *dest, char *source, int tilde)
+{
+ register char ch;
+ char *d = dest;
+ char *s = source;
+ int rlen = USERLEN;
+
+ ch = *s++; /* Store first character to copy: */
+ if (tilde)
+ {
+ *d++ = '~'; /* If `dest' == `source', then this overwrites `ch' */
+ --rlen;
+ }
+ while (ch && !isCntrl(ch) && rlen--)
+ {
+ register char nch = *s++; /* Store next character to copy */
+ *d++ = isIrcUi(ch) ? ch : '_'; /* This possibly overwrites it */
+ if (nch == '~')
+ ch = '_';
+ else
+ ch = nch;
+ }
+ *d = 0;
+ return dest;
+}
+
+/*
+ * register_user
+ *
+ * This function is called when both NICK and USER messages
+ * have been accepted for the client, in whatever order. Only
+ * after this the USER message is propagated.
+ *
+ * NICK's must be propagated at once when received, although
+ * it would be better to delay them too until full info is
+ * available. Doing it is not so simple though, would have
+ * to implement the following:
+ *
+ * 1) user telnets in and gives only "NICK foobar" and waits
+ * 2) another user far away logs in normally with the nick
+ * "foobar" (quite legal, as this server didn't propagate it).
+ * 3) now this server gets nick "foobar" from outside, but
+ * has already the same defined locally. Current server
+ * would just issue "KILL foobar" to clean out dups. But,
+ * this is not fair. It should actually request another
+ * nick from local user or kill him/her...
+ */
+
+static int register_user(aClient *cptr, aClient *sptr,
+ char *nick, char *username)
+{
+ Reg1 aConfItem *aconf;
+ char *parv[3], *tmpstr, *tmpstr2;
+ char c = 0 /* not alphanum */ , d = 'a' /* not a digit */ ;
+ short oldstatus_ismaster = IsMaster(sptr), upper = 0, lower = 0;
+ short pos = 0, leadcaps = 0, other = 0, digits = 0, badid = 0;
+ short digitgroups = 0;
+ anUser *user = sptr->user;
+ Dlink *lp;
+ char ip_base64[8];
+
+ user->last = now;
+ parv[0] = sptr->name;
+ parv[1] = parv[2] = NULL;
+
+ if (MyConnect(sptr))
+ {
+ static time_t last_too_many1, last_too_many2;
+ switch (check_client(sptr))
+ {
+ case ACR_OK:
+ break;
+ case ACR_NO_AUTHORIZATION:
+ sendto_op_mask(SNO_UNAUTH, "Unauthorized connection from %s.",
+ get_client_host(sptr));
+ ircstp->is_ref++;
+ return exit_client(cptr, sptr, &me,
+ "No Authorization - use another server");
+ case ACR_TOO_MANY_IN_CLASS:
+ if (now - last_too_many1 >= (time_t) 60)
+ {
+ last_too_many1 = now;
+ sendto_op_mask(SNO_TOOMANY, "Too many connections in class for %s.",
+ get_client_host(sptr));
+ }
+ IPcheck_connect_fail(sptr);
+ ircstp->is_ref++;
+ return exit_client(cptr, sptr, &me,
+ "Sorry, your connection class is full - try again later or try another server");
+ case ACR_TOO_MANY_FROM_IP:
+ if (now - last_too_many2 >= (time_t) 60)
+ {
+ last_too_many2 = now;
+ sendto_op_mask(SNO_TOOMANY,
+ "Too many connections from same IP for %s.",
+ get_client_host(sptr));
+ }
+ ircstp->is_ref++;
+ return exit_client(cptr, sptr, &me,
+ "Too many connections from your host");
+ case ACR_ALREADY_AUTHORIZED:
+ /* Can this ever happen? */
+ case ACR_BAD_SOCKET:
+ IPcheck_connect_fail(sptr);
+ return exit_client(cptr, sptr, &me, "Unknown error -- Try again");
+ }
+ if (IsUnixSocket(sptr))
+ strncpy(user->host, me.sockhost, HOSTLEN);
+ else
+ strncpy(user->host, sptr->sockhost, HOSTLEN);
+ aconf = sptr->confs->value.aconf;
+
+ clean_user_id(user->username,
+ (sptr->flags & FLAGS_GOTID) ? sptr->username : username,
+ (sptr->flags & FLAGS_DOID) && !(sptr->flags & FLAGS_GOTID));
+
+ if ((user->username[0] == '\000')
+ || ((user->username[0] == '~') && (user->username[1] == '\000')))
+ return exit_client(cptr, sptr, &me, "USER: Bogus userid.");
+
+ if (!BadPtr(aconf->passwd)
+ && !(isDigit(*aconf->passwd) && !aconf->passwd[1])
+#ifdef USEONE
+ && strcmp("ONE", aconf->passwd)
+#endif
+ && strcmp(sptr->passwd, aconf->passwd))
+ {
+ ircstp->is_ref++;
+ sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
+ IPcheck_connect_fail(sptr);
+ return exit_client(cptr, sptr, &me, "Bad Password");
+ }
+ memset(sptr->passwd, 0, sizeof(sptr->passwd));
+ /*
+ * following block for the benefit of time-dependent K:-lines
+ */
+ if (find_kill(sptr))
+ {
+ ircstp->is_ref++;
+ return exit_client(cptr, sptr, &me, "K-lined");
+ }
+#ifdef R_LINES
+ if (find_restrict(sptr))
+ {
+ ircstp->is_ref++;
+ return exit_client(cptr, sptr, &me, "R-lined");
+ }
+#endif
+
+ /*
+ * Check for mixed case usernames, meaning probably hacked. Jon2 3-94
+ * Summary of rules now implemented in this patch: Ensor 11-94
+ * In a mixed-case name, if first char is upper, one more upper may
+ * appear anywhere. (A mixed-case name *must* have an upper first
+ * char, and may have one other upper.)
+ * A third upper may appear if all 3 appear at the beginning of the
+ * name, separated only by "others" (-/_/.).
+ * A single group of digits is allowed anywhere.
+ * Two groups of digits are allowed if at least one of the groups is
+ * at the beginning or the end.
+ * Only one '-', '_', or '.' is allowed (or two, if not consecutive).
+ * But not as the first or last char.
+ * No other special characters are allowed.
+ * Name must contain at least one letter.
+ */
+ tmpstr2 = tmpstr = (username[0] == '~' ? &username[1] : username);
+ while (*tmpstr && !badid)
+ {
+ pos++;
+ c = *tmpstr;
+ tmpstr++;
+ if (isLower(c))
+ {
+ lower++;
+ }
+ else if (isUpper(c))
+ {
+ upper++;
+ if ((leadcaps || pos == 1) && !lower && !digits)
+ leadcaps++;
+ }
+ else if (isDigit(c))
+ {
+ digits++;
+ if (pos == 1 || !isDigit(d))
+ {
+ digitgroups++;
+ if (digitgroups > 2)
+ badid = 1;
+ }
+ }
+ else if (c == '-' || c == '_' || c == '.')
+ {
+ other++;
+ if (pos == 1)
+ badid = 1;
+ else if (d == '-' || d == '_' || d == '.' || other > 2)
+ badid = 1;
+ }
+ else
+ badid = 1;
+ d = c;
+ }
+ if (!badid)
+ {
+ if (lower && upper && (!leadcaps || leadcaps > 3 ||
+ (upper > 2 && upper > leadcaps)))
+ badid = 1;
+ else if (digitgroups == 2 && !(isDigit(tmpstr2[0]) || isDigit(c)))
+ badid = 1;
+ else if ((!lower && !upper) || !isAlnum(c))
+ badid = 1;
+ }
+ if (badid && (!(sptr->flags & FLAGS_GOTID) ||
+ strcmp(sptr->username, username) != 0))
+ {
+ ircstp->is_ref++;
+ sendto_one(cptr, ":%s %d %s :Your username is invalid.",
+ me.name, ERR_INVALIDUSERNAME, cptr->name);
+ sendto_one(cptr,
+ ":%s %d %s :Connect with your real username, in lowercase.",
+ me.name, ERR_INVALIDUSERNAME, cptr->name);
+ sendto_one(cptr, ":%s %d %s :If your mail address were foo@bar.com, "
+ "your username would be foo.",
+ me.name, ERR_INVALIDUSERNAME, cptr->name);
+ return exit_client(cptr, sptr, &me, "USER: Bad username");
+ }
+
+ if (oldstatus_ismaster && MyConnect(sptr))
+ m_oper(&me, sptr, 1, parv);
+
+ Count_unknownbecomesclient(sptr, nrof);
+ }
+ else
+ {
+ strncpy(user->username, username, USERLEN);
+ Count_newremoteclient(nrof);
+ }
+ SetUser(sptr);
+ if (IsInvisible(sptr))
+ ++nrof.inv_clients;
+ if (IsOper(sptr))
+ ++nrof.opers;
+
+ if (MyConnect(sptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_WELCOME), me.name, nick, nick);
+ /* This is a duplicate of the NOTICE but see below... */
+ sendto_one(sptr, rpl_str(RPL_YOURHOST), me.name, nick,
+ get_client_name(&me, FALSE), version);
+ sendto_one(sptr, rpl_str(RPL_CREATED), me.name, nick, creation);
+ sendto_one(sptr, rpl_str(RPL_MYINFO), me.name, parv[0], me.name, version);
+ m_lusers(sptr, sptr, 1, parv);
+ update_load();
+#ifdef NODEFAULTMOTD
+ m_motd(sptr, NULL, 1, parv);
+#else
+ m_motd(sptr, sptr, 1, parv);
+#endif
+ nextping = now;
+ if (sptr->snomask & SNO_NOISY)
+ set_snomask(sptr, sptr->snomask & SNO_NOISY, SNO_ADD);
+#ifdef ALLOW_SNO_CONNEXIT
+#ifdef SNO_CONNEXIT_IP
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- Client connecting: %s (%s@%s) [%s] {%d}",
+ me.name, nick, user->username, user->host, inetntoa(sptr->ip),
+ get_client_class(sptr));
+ sendbufto_op_mask(SNO_CONNEXIT);
+#else /* SNO_CONNEXIT_IP */
+ sprintf_irc(sendbuf,
+ ":%s NOTICE * :*** Notice -- Client connecting: %s (%s@%s)",
+ me.name, nick, user->username, user->host);
+ sendbufto_op_mask(SNO_CONNEXIT);
+#endif /* SNO_CONNEXIT_IP */
+#endif /* ALLOW_SNO_CONNEXIT */
+ IPcheck_connect_succeeded(sptr);
+ }
+ else
+ /* if (IsServer(cptr)) */
+ {
+ aClient *acptr;
+
+ acptr = user->server;
+ if (acptr->from != sptr->from)
+ {
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s KILL %s :%s (%s != %s[%s])",
+ me.name, sptr->name, me.name, user->server->name, acptr->from->name,
+ acptr->from->sockhost);
+ else
+ sendto_one(cptr, "%s KILL %s%s :%s (%s != %s[%s])",
+ NumServ(&me), NumNick(sptr), me.name, user->server->name,
+ acptr->from->name, acptr->from->sockhost);
+ sptr->flags |= FLAGS_KILLED;
+ return exit_client(cptr, sptr, &me, "NICK server wrong direction");
+ }
+ else
+ sptr->flags |= (acptr->flags & FLAGS_TS8);
+
+ /*
+ * Check to see if this user is being propogated
+ * as part of a net.burst, or is using protocol 9.
+ * FIXME: This can be speeded up - its stupid to check it for
+ * every NICK message in a burst again --Run.
+ */
+ for (acptr = user->server; acptr != &me; acptr = acptr->serv->up)
+ if (IsBurst(acptr) || Protocol(acptr) < 10)
+ break;
+ if (IPcheck_remote_connect(sptr, user->host, (acptr != &me)) == -1)
+ /* We ran out of bits to count this */
+ return exit_client(cptr, sptr, &me,
+ "More then 255 connections from this IP number");
+ }
+#ifdef NO_PROTOCOL9 /* Use this when all servers are 2.10 (but test it first) --Run */
+
+ tmpstr = umode_str(sptr);
+ sendto_serv_butone(cptr, *tmpstr ?
+ "%s NICK %s %d %d %s %s +%s %s %s%s :%s" :
+ "%s NICK %s %d %d %s %s %s%s %s%s :%s",
+ NumServ(user->server), nick, sptr->hopcount + 1, sptr->lastnick,
+ user->username, user->host, tmpstr,
+ inttobase64(ip_base64, ntohl(sptr->ip.s_addr), 6),
+ NumNick(sptr), sptr->info);
+
+#else /* Remove the following when all servers are 2.10 */
+
+ /* First send message to all 2.9 servers */
+ sprintf_irc(sendbuf, ":%s NICK %s %d " TIME_T_FMT " %s %s %s :%s",
+ user->server->name, nick, sptr->hopcount + 1, sptr->lastnick,
+ user->username, user->host, user->server->name, sptr->info);
+ for (lp = me.serv->down; lp; lp = lp->next)
+ {
+ if (lp->value.cptr == cptr)
+ continue;
+ if (Protocol(lp->value.cptr) < 10)
+ sendbufto_one(lp->value.cptr);
+ }
+
+ /* If the user has no umode, no need to generate a user MODE */
+ if (*(tmpstr = umode_str(sptr)) && (MyConnect(sptr) || Protocol(cptr) > 9))
+ /* Is it necessary to generate an user MODE message ? */
+ {
+ for (lp = me.serv->down; lp; lp = lp->next)
+ {
+ if (lp->value.cptr == cptr)
+ continue;
+ if (Protocol(lp->value.cptr) < 10)
+ sendto_one(lp->value.cptr, ":%s MODE %s :%s", sptr->name,
+ sptr->name, tmpstr);
+ }
+ }
+
+ /* Now send message to all 2.10 servers */
+ sprintf_irc(sendbuf, *tmpstr ?
+ "%s NICK %s %d %d %s %s +%s %s %s%s :%s" :
+ "%s NICK %s %d %d %s %s %s%s %s%s :%s",
+ NumServ(user->server), nick, sptr->hopcount + 1, sptr->lastnick,
+ user->username, user->host, tmpstr,
+ inttobase64(ip_base64, ntohl(sptr->ip.s_addr), 6),
+ NumNick(sptr), sptr->info);
+ for (lp = me.serv->down; lp; lp = lp->next)
+ {
+ if (lp->value.cptr == cptr || Protocol(lp->value.cptr) < 10)
+ continue;
+ sendbufto_one(lp->value.cptr);
+ }
+
+#endif
+
+ /* Send umode to client */
+ if (MyUser(sptr))
+ {
+ send_umode(cptr, sptr, 0, ALL_UMODES);
+ if (sptr->snomask != SNO_DEFAULT && (sptr->flags & FLAGS_SERVNOTICE))
+ sendto_one(sptr, rpl_str(RPL_SNOMASK), me.name, sptr->name,
+ sptr->snomask, sptr->snomask);
+ }
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+
+static int user_modes[] = {
+ FLAGS_OPER, 'o',
+ FLAGS_LOCOP, 'O',
+ FLAGS_INVISIBLE, 'i',
+ FLAGS_WALLOP, 'w',
+ FLAGS_SERVNOTICE, 's',
+ FLAGS_DEAF, 'd',
+ FLAGS_CHSERV, 'k',
+ FLAGS_DEBUG, 'g',
+ 0, 0
+};
+
+/* *INDENT-ON* */
+
+#define COOKIE_VERIFIED ((unsigned int)-1)
+
+/*
+ * m_nick
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nickname
+ *
+ * If from server, source is client:
+ * parv[2] = timestamp
+ *
+ * Source is server:
+ * parv[2] = hopcount
+ * parv[3] = timestamp
+ * parv[4] = username
+ * parv[5] = hostname
+ * parv[6] = umode (optional)
+ * parv[parc-3] = IP# <- Only Protocol >= 10
+ * parv[parc-2] = YXX, numeric nick <- Only Protocol >= 10
+ * parv[parc-1] = info
+ * parv[0] = server
+ */
+int m_nick(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr;
+ aClient *server = NULL;
+ char nick[NICKLEN + 2];
+ char *s;
+ Link *lp;
+ time_t lastnick = (time_t) 0;
+ int differ = 1;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+ return 0;
+ }
+ else if ((IsServer(sptr) && parc < 8) || (IsServer(cptr) && parc < 3))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "NICK");
+ sendto_ops("bad NICK param count for %s from %s",
+ parv[1], get_client_name(cptr, FALSE));
+ return 0;
+ }
+ if (MyConnect(sptr) && (s = strchr(parv[1], '~')))
+ *s = '\0';
+ strncpy(nick, parv[1], NICKLEN + 1);
+ nick[sizeof(nick) - 1] = 0;
+ /*
+ * If do_nick_name() returns a null name OR if the server sent a nick
+ * name and do_nick_name() changed it in some way (due to rules of nick
+ * creation) then reject it. If from a server and we reject it,
+ * and KILL it. -avalon 4/4/92
+ */
+ if (do_nick_name(nick) == 0 || (IsServer(cptr) && strcmp(nick, parv[1])))
+ {
+ sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, parv[0], parv[1]);
+
+ if (IsServer(cptr))
+ {
+ ircstp->is_kill++;
+ sendto_ops("Bad Nick: %s From: %s %s",
+ parv[1], parv[0], get_client_name(cptr, FALSE));
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s KILL %s :%s (%s <- %s[%s])",
+ me.name, parv[1], me.name, parv[1], nick, cptr->name);
+ else
+ sendto_one(cptr, "%s KILL %s :%s (%s <- %s[%s])",
+ NumServ(&me), IsServer(sptr) ? parv[parc - 2] : parv[0], me.name,
+ parv[1], nick, cptr->name);
+ if (!IsServer(sptr)) /* bad nick _change_ */
+ {
+ sendto_lowprot_butone(cptr, 9, ":%s KILL %s :%s (%s <- %s!%s@%s)",
+ me.name, parv[0], me.name, get_client_name(cptr, FALSE), parv[0],
+ sptr->user ? sptr->username : "",
+ sptr->user ? sptr->user->server->name : cptr->name);
+ sendto_highprot_butone(cptr, 10, "%s KILL %s :%s (%s <- %s!%s@%s)",
+ NumServ(&me), parv[0], me.name, get_client_name(cptr, FALSE),
+ parv[0], sptr->user ? sptr->username : "",
+ sptr->user ? sptr->user->server->name : cptr->name);
+ sptr->flags |= FLAGS_KILLED;
+ return exit_client(cptr, sptr, &me, "BadNick");
+ }
+ }
+ return 0;
+ }
+
+ /*
+ * Check if this is a LOCAL user trying to use a reserved (Juped)
+ * nick, if so tell him that it's a nick in use...
+ */
+
+ if ((!IsServer(cptr)) && isNickJuped(nick))
+ {
+ sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name,
+ /* parv[0] is empty when connecting */
+ BadPtr(parv[0]) ? "*" : parv[0], nick);
+ return 0; /* NICK message ignored */
+ }
+
+ /*
+ * Check against nick name collisions.
+ *
+ * Put this 'if' here so that the nesting goes nicely on the screen :)
+ * We check against server name list before determining if the nickname
+ * is present in the nicklist (due to the way the below for loop is
+ * constructed). -avalon
+ */
+ if ((acptr = FindServer(nick)))
+ if (MyConnect(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name,
+ BadPtr(parv[0]) ? "*" : parv[0], nick);
+ return 0; /* NICK message ignored */
+ }
+ /*
+ * acptr already has result from previous FindServer()
+ */
+ if (acptr)
+ {
+ /*
+ * We have a nickname trying to use the same name as
+ * a server. Send out a nick collision KILL to remove
+ * the nickname. As long as only a KILL is sent out,
+ * there is no danger of the server being disconnected.
+ * Ultimate way to jupiter a nick ? >;-). -avalon
+ */
+ sendto_ops("Nick collision on %s(%s <- %s)",
+ sptr->name, acptr->from->name, get_client_name(cptr, FALSE));
+ ircstp->is_kill++;
+ if (Protocol(cptr) < 10)
+ sendto_one(cptr, ":%s KILL %s :%s (%s <- %s)",
+ me.name, sptr->name, me.name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ else
+ sendto_one(cptr, "%s KILL %s%s :%s (%s <- %s)",
+ NumServ(&me), NumNick(sptr), me.name, acptr->from->name,
+ /*
+ * NOTE: Cannot use get_client_name twice here, it returns static
+ * string pointer--the other info would be lost.
+ */
+ get_client_name(cptr, FALSE));
+ sptr->flags |= FLAGS_KILLED;
+ return exit_client(cptr, sptr, &me, "Nick/Server collision");
+ }
+
+ if (!(acptr = FindClient(nick)))
+ goto nickkilldone; /* No collisions, all clear... */
+ /*
+ * If acptr == sptr, then we have a client doing a nick
+ * change between *equivalent* nicknames as far as server
+ * is concerned (user is changing the case of his/her
+ * nickname or somesuch)
+ */
+ if (acptr == sptr)
+ {
+ if (strcmp(acptr->name, nick) != 0)
+ /*
+ * Allows change of case in his/her nick
+ */
+ goto nickkilldone; /* -- go and process change */
+ else
+ /*
+ * This is just ':old NICK old' type thing.
+ * Just forget the whole thing here. There is
+ * no point forwarding it to anywhere,
+ * especially since servers prior to this
+ * version would treat it as nick collision.
+ */
+ return 0; /* NICK Message ignored */
+ }
+
+ /*
+ * Note: From this point forward it can be assumed that
+ * acptr != sptr (point to different client structures).
+ */
+ /*
+ * If the older one is "non-person", the new entry is just
+ * allowed to overwrite it. Just silently drop non-person,
+ * and proceed with the nick. This should take care of the
+ * "dormant nick" way of generating collisions...
+ */
+ if (IsUnknown(acptr) && MyConnect(acptr))
+ {
+ IPcheck_connect_fail(acptr);
+ exit_client(cptr, acptr, &me, "Overridden by other sign on");
+ goto nickkilldone;
+ }
+ /*
+ * Decide, we really have a nick collision and deal with it
+ */
+ if (!IsServer(cptr))
+ {
+ /*
+ * NICK is coming from local client connection. Just
+ * send error reply and ignore the command.
+ */
+ sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name,
+ /* parv[0] is empty when connecting */
+ BadPtr(parv[0]) ? "*" : parv[0], nick);
+ return 0; /* NICK message ignored */
+ }
+ /*
+ * NICK was coming from a server connection.
+ * This means we have a race condition (two users signing on
+ * at the same time), or two net fragments reconnecting with the same nick.
+ * The latter can happen because two different users connected
+ * or because one and the same user switched server during a net break.
+ * If the TimeStamps are equal, we kill both (or only 'new'
+ * if it was a ":server NICK new ...").
+ * Otherwise we kill the youngest when user@host differ,
+ * or the oldest when they are the same.
+ * We treat user and ~user as different, because if it wasn't
+ * a faked ~user the AUTH wouldn't have added the '~'.
+ * --Run
+ *
+ */
+ if (IsServer(sptr))
+ {
+ /*
+ * A new NICK being introduced by a neighbouring
+ * server (e.g. message type ":server NICK new ..." received)
+ */
+ lastnick = atoi(parv[3]);
+ differ = (strCasediff(acptr->user->username, parv[4]) ||
+ strCasediff(acptr->user->host, parv[5]));
+ sendto_ops("Nick collision on %s (%s " TIME_T_FMT " <- %s " TIME_T_FMT
+ " (%s user@host))", acptr->name, acptr->from->name, acptr->lastnick,
+ get_client_name(cptr, FALSE), lastnick, differ ? "Different" : "Same");
+ }
+ else
+ {
+ /*
+ * A NICK change has collided (e.g. message type ":old NICK new").
+ */
+ lastnick = atoi(parv[2]);
+ differ = (strCasediff(acptr->user->username, sptr->user->username) ||
+ strCasediff(acptr->user->host, sptr->user->host));
+ sendto_ops("Nick change collision from %s to %s (%s " TIME_T_FMT " <- %s "
+ TIME_T_FMT ")", sptr->name, acptr->name, acptr->from->name,
+ acptr->lastnick, get_client_name(cptr, FALSE), lastnick);
+ }
+ /*
+ * Now remove (kill) the nick on our side if it is the youngest.
+ * If no timestamp was received, we ignore the incoming nick
+ * (and expect a KILL for our legit nick soon ):
+ * When the timestamps are equal we kill both nicks. --Run
+ * acptr->from != cptr should *always* be true (?).
+ */
+ if (acptr->from != cptr)
+ {
+ if ((differ && lastnick >= acptr->lastnick) ||
+ (!differ && lastnick <= acptr->lastnick))
+ {
+ if (!IsServer(sptr))
+ {
+ ircstp->is_kill++;
+ sendto_lowprot_butone(cptr, 9, /* Kill old from outgoing servers */
+ ":%s KILL %s :%s (%s <- %s (Nick collision))",
+ me.name, sptr->name, me.name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ sendto_highprot_butone(cptr, 10, /* Kill old from outgoing servers */
+ "%s KILL %s%s :%s (%s <- %s (Nick collision))",
+ NumServ(&me), NumNick(sptr), me.name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ if (MyConnect(sptr) && IsServer(cptr) && Protocol(cptr) > 9)
+ sendto_one(cptr, "%s KILL %s%s :%s (Ghost2)",
+ NumServ(&me), NumNick(sptr), me.name);
+ sptr->flags |= FLAGS_KILLED;
+ exit_client(cptr, sptr, &me, "Nick collision (you're a ghost)");
+ }
+ if (lastnick != acptr->lastnick)
+ return 0; /* Ignore the NICK */
+ }
+ sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, nick);
+ }
+ ircstp->is_kill++;
+ acptr->flags |= FLAGS_KILLED;
+ if (differ)
+ {
+ sendto_lowprot_butone(cptr, 9, /* Kill our old from outgoing servers */
+ ":%s KILL %s :%s (%s <- %s (older nick overruled))",
+ me.name, acptr->name, me.name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ sendto_highprot_butone(cptr, 10, /* Kill our old from outgoing servers */
+ "%s KILL %s%s :%s (%s <- %s (older nick overruled))",
+ NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
+ sendto_one(cptr, "%s%s QUIT :Local kill by %s (Ghost)",
+ NumNick(acptr), me.name);
+ exit_client(cptr, acptr, &me, "Nick collision (older nick overruled)");
+ }
+ else
+ {
+ sendto_lowprot_butone(cptr, 9, /* Kill our old from outgoing servers */
+ ":%s KILL %s :%s (%s <- %s (nick collision from same user@host))",
+ me.name, acptr->name, me.name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ sendto_highprot_butone(cptr, 10, /* Kill our old from outgoing servers */
+ "%s KILL %s%s :%s (%s <- %s (nick collision from same user@host))",
+ NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
+ sendto_one(cptr,
+ "%s%s QUIT :Local kill by %s (Ghost: switched servers too fast)",
+ NumNick(acptr), me.name);
+ exit_client(cptr, acptr, &me, "Nick collision (You collided yourself)");
+ }
+ if (lastnick == acptr->lastnick)
+ return 0;
+
+nickkilldone:
+ if (IsServer(sptr))
+ {
+ int flag, *s;
+ char *p;
+#ifndef NO_PROTOCOL9
+ const char *nnp9 = NULL; /* Init. to avoid compiler warning */
+#endif
+
+ /* A server introducing a new client, change source */
+ if (!server)
+ server = sptr;
+#ifndef NO_PROTOCOL9
+ /*
+ * Numeric Nicks does, in contrast to all other protocol enhancements,
+ * translation from protocol 9 -> protocol 10 !
+ * The reason is that I just can't know what protocol it is when I
+ * receive a "MODE #channel +o Run", because I can't 'find' "Run"
+ * before I know the protocol, and I can't know the protocol if I
+ * first have to find the server of "Run".
+ * Therefore, in THIS case, the protocol is determined by the Connected
+ * server: cptr.
+ */
+ if (Protocol(cptr) < 10 && !(nnp9 = CreateNNforProtocol9server(server)))
+ return exit_client_msg(cptr, server, &me,
+ "Too many clients (> %d) from P09 server (%s)", 64, server->name);
+#endif
+ sptr = make_client(cptr, STAT_UNKNOWN);
+ sptr->hopcount = atoi(parv[2]);
+ sptr->lastnick = atoi(parv[3]);
+ if (Protocol(cptr) > 9 && parc > 7 && *parv[6] == '+')
+ for (p = parv[6] + 1; *p; p++)
+ for (s = user_modes; (flag = *s); s += 2)
+ if (((char)*(s + 1)) == *p)
+ {
+ sptr->flags |= flag;
+ break;
+ }
+ /*
+ * Set new nick name.
+ */
+ strcpy(sptr->name, nick);
+ sptr->user = make_user(sptr);
+ sptr->user->server = server;
+#ifndef NO_PROTOCOL9
+ if (Protocol(cptr) < 10)
+ {
+ if (!SetRemoteNumNick(sptr, nnp9))
+ {
+ /*
+ * if this fails squit the server and free the client
+ */
+ free_client(sptr);
+ return exit_client_msg(cptr, server, &me, "Invalid numeric index");
+ }
+ sptr->ip.s_addr = 0;
+ }
+ else
+ {
+#endif
+ if (!SetRemoteNumNick(sptr, parv[parc - 2]))
+ {
+ /*
+ * if this fails squit the server and free the client
+ */
+ free_client(sptr);
+ return exit_client_msg(cptr, server, &me, "Invalid numeric index");
+ }
+ sptr->ip.s_addr = htonl(base64toint(parv[parc - 3]));
+ /* IP# of remote client */
+#ifndef NO_PROTOCOL9
+ }
+#endif
+ add_client_to_list(sptr);
+ hAddClient(sptr);
+
+ server->serv->ghost = 0; /* :server NICK means end of net.burst */
+ strncpy(sptr->info, parv[parc - 1], sizeof(sptr->info) - 1);
+ strncpy(sptr->user->host, parv[5], sizeof(sptr->user->host) - 1);
+ return register_user(cptr, sptr, sptr->name, parv[4]);
+ }
+ else if (sptr->name[0])
+ {
+ /*
+ * Client changing its nick
+ *
+ * If the client belongs to me, then check to see
+ * if client is on any channels where it is currently
+ * banned. If so, do not allow the nick change to occur.
+ */
+ if (MyUser(sptr))
+ {
+ for (lp = cptr->user->channel; lp; lp = lp->next)
+ if (can_send(cptr, lp->value.chptr) == MODE_BAN)
+ {
+ sendto_one(cptr, err_str(ERR_BANNICKCHANGE), me.name, parv[0],
+ lp->value.chptr->chname);
+ return 0;
+ }
+ /*
+ * Refuse nick change if the last nick change was less
+ * then 30 seconds ago. This is intended to get rid of
+ * clone bots doing NICK FLOOD. -SeKs
+ * If someone didn't change their nick for more then 60 seconds
+ * however, allow to do two nick changes immedately after another
+ * before limiting the nick flood. -Run
+ */
+ if (now < cptr->nextnick)
+ {
+ cptr->nextnick += 2;
+ sendto_one(cptr, err_str(ERR_NICKTOOFAST),
+ me.name, parv[0], parv[1], cptr->nextnick - now);
+ /* Send error message */
+ sendto_prefix_one(cptr, cptr, ":%s NICK %s", parv[0], parv[0]);
+ /* bounce NICK to user */
+ return 0; /* ignore nick change! */
+ }
+ else
+ {
+ /* Limit total to 1 change per NICK_DELAY seconds: */
+ cptr->nextnick += NICK_DELAY;
+ /* However allow _maximal_ 1 extra consecutive nick change: */
+ if (cptr->nextnick < now)
+ cptr->nextnick = now;
+ }
+ }
+ /*
+ * Also set 'lastnick' to current time, if changed.
+ */
+ if (strCasediff(parv[0], nick))
+ sptr->lastnick = (sptr == cptr) ? TStime() : atoi(parv[2]);
+
+ /*
+ * Client just changing his/her nick. If he/she is
+ * on a channel, send note of change to all clients
+ * on that channel. Propagate notice to other servers.
+ */
+ if (IsUser(sptr))
+ {
+ sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
+ add_history(sptr, 1);
+#ifdef NO_PROTOCOL9
+ sendto_serv_butone(cptr,
+ "%s%s NICK %s " TIME_T_FMT, NumNick(sptr), nick, sptr->lastnick);
+#else
+ sendto_lowprot_butone(cptr, 9,
+ ":%s NICK %s " TIME_T_FMT, parv[0], nick, sptr->lastnick);
+ sendto_highprot_butone(cptr, 10,
+ "%s%s NICK %s " TIME_T_FMT, NumNick(sptr), nick, sptr->lastnick);
+#endif
+ }
+ else
+ sendto_one(sptr, ":%s NICK :%s", parv[0], nick);
+ if (sptr->name[0])
+ hRemClient(sptr);
+ strcpy(sptr->name, nick);
+ hAddClient(sptr);
+ }
+ else
+ {
+ /* Local client setting NICK the first time */
+
+ strcpy(sptr->name, nick);
+ if (!sptr->user)
+ {
+ sptr->user = make_user(sptr);
+ sptr->user->server = &me;
+ }
+ SetLocalNumNick(sptr);
+ hAddClient(sptr);
+
+ /*
+ * If the client hasn't gotten a cookie-ping yet,
+ * choose a cookie and send it. -record!jegelhof@cloud9.net
+ */
+ if (!sptr->cookie)
+ {
+ do
+ sptr->cookie = (ircrandom() & 0x7fffffff);
+ while (!sptr->cookie);
+ sendto_one(cptr, "PING :%u", sptr->cookie);
+ }
+ else if (*sptr->user->host && sptr->cookie == COOKIE_VERIFIED)
+ {
+ /*
+ * USER and PONG already received, now we have NICK.
+ * register_user may reject the client and call exit_client
+ * for it - must test this and exit m_nick too !
+ */
+ sptr->lastnick = TStime(); /* Always local client */
+ if (register_user(cptr, sptr, nick, sptr->user->username) == CPTR_KILLED)
+ return CPTR_KILLED;
+ }
+ }
+ return 0;
+}
+
+/*
+ * add_target
+ *
+ * sptr must be a local client!
+ *
+ * Cannonifies target for client `sptr'.
+ */
+void add_target(aClient *sptr, void *target)
+{
+ register unsigned char *p;
+ register unsigned int tmp = ((size_t)target & 0xffff00) >> 8;
+ unsigned char hash = (tmp * tmp) >> 12;
+ if (sptr->targets[0] == hash) /* Last person that we messaged ourself? */
+ return;
+ for (p = sptr->targets; p < &sptr->targets[MAXTARGETS - 1];)
+ if (*++p == hash)
+ return; /* Already in table */
+
+ /* New target */
+ memmove(&sptr->targets[RESERVEDTARGETS + 1],
+ &sptr->targets[RESERVEDTARGETS], MAXTARGETS - RESERVEDTARGETS - 1);
+ sptr->targets[RESERVEDTARGETS] = hash;
+ return;
+}
+
+/*
+ * check_target_limit
+ *
+ * sptr must be a local client !
+ *
+ * Returns 'true' (1) when too many targets are addressed.
+ * Returns 'false' (0) when it's ok to send to this target.
+ */
+int check_target_limit(aClient *sptr, void *target, const char *name,
+ int created)
+{
+ register unsigned char *p;
+ register unsigned int tmp = ((size_t)target & 0xffff00) >> 8;
+ unsigned char hash = (tmp * tmp) >> 12;
+ if (sptr->targets[0] == hash) /* Same target as last time ? */
+ return 0;
+ for (p = sptr->targets; p < &sptr->targets[MAXTARGETS - 1];)
+ if (*++p == hash)
+ {
+ memmove(&sptr->targets[1], &sptr->targets[0], p - sptr->targets);
+ sptr->targets[0] = hash;
+ return 0;
+ }
+
+ /* New target */
+ if (!created)
+ {
+ if (now < sptr->nexttarget)
+ {
+ if (sptr->nexttarget - now < TARGET_DELAY + 8) /* No server flooding */
+ {
+ sptr->nexttarget += 2;
+ sendto_one(sptr, err_str(ERR_TARGETTOOFAST),
+ me.name, sptr->name, name, sptr->nexttarget - now);
+ }
+ return 1;
+ }
+ else
+ {
+#ifdef GODMODE
+ sendto_one(sptr, ":%s NOTICE %s :New target: %s; ft " TIME_T_FMT,
+ me.name, sptr->name, name, (now - sptr->nexttarget) / TARGET_DELAY);
+#endif
+ sptr->nexttarget += TARGET_DELAY;
+ if (sptr->nexttarget < now - (TARGET_DELAY * (MAXTARGETS - 1)))
+ sptr->nexttarget = now - (TARGET_DELAY * (MAXTARGETS - 1));
+ }
+ }
+ memmove(&sptr->targets[1], &sptr->targets[0], MAXTARGETS - 1);
+ sptr->targets[0] = hash;
+ return 0;
+}
+
+/*
+ * m_message (used in m_private() and m_notice())
+ *
+ * The general function to deliver MSG's between users/channels
+ *
+ * parv[0] = sender prefix
+ * parv[1] = receiver list
+ * parv[parc-1] = message text
+ *
+ * massive cleanup
+ * rev argv 6/91
+ */
+static int m_message(aClient *cptr, aClient *sptr,
+ int parc, char *parv[], int notice)
+{
+ Reg1 aClient *acptr;
+ Reg2 char *s;
+ aChannel *chptr;
+ char *nick, *server, *p, *cmd, *host;
+
+ sptr->flags &= ~FLAGS_TS8;
+
+ cmd = notice ? MSG_NOTICE : MSG_PRIVATE;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NORECIPIENT), me.name, parv[0], cmd);
+ return -1;
+ }
+
+ if (parc < 3 || *parv[parc - 1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+ return -1;
+ }
+
+ if (MyUser(sptr))
+ parv[1] = canonize(parv[1]);
+ for (p = NULL, nick = strtoken(&p, parv[1], ","); nick;
+ nick = strtoken(&p, NULL, ","))
+ {
+ /*
+ * channel msg?
+ */
+ if (IsChannelName(nick))
+ {
+ if ((chptr = FindChannel(nick)))
+ {
+ if (can_send(sptr, chptr) == 0 /* This first: Almost never a server/service */
+ || IsChannelService(sptr) || IsServer(sptr))
+ {
+ if (MyUser(sptr) && (chptr->mode.mode & MODE_NOPRIVMSGS) &&
+ check_target_limit(sptr, chptr, chptr->chname, 0))
+ continue;
+ sendto_channel_butone(cptr, sptr, chptr,
+ ":%s %s %s :%s", parv[0], cmd, chptr->chname, parv[parc - 1]);
+ }
+ else if (!notice)
+ sendto_one(sptr, err_str(ERR_CANNOTSENDTOCHAN),
+ me.name, parv[0], chptr->chname);
+ continue;
+ }
+ }
+ else if (*nick != '$' && !strchr(nick, '@'))
+ {
+ /*
+ * nickname addressed?
+ */
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ acptr = FindUser(nick);
+ else if ((acptr = findNUser(nick)) && !IsUser(acptr))
+ acptr = NULL;
+ if (acptr)
+ {
+ if (MyUser(sptr) && check_target_limit(sptr, acptr, acptr->name, 0))
+ continue;
+ if (!is_silenced(sptr, acptr))
+ {
+ if (!notice && MyConnect(sptr) && acptr->user && acptr->user->away)
+ sendto_one(sptr, rpl_str(RPL_AWAY),
+ me.name, parv[0], acptr->name, acptr->user->away);
+ if (MyUser(acptr) || Protocol(acptr->from) < 10)
+ {
+ if (MyUser(acptr))
+ add_target(acptr, sptr);
+ sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
+ parv[0], cmd, acptr->name, parv[parc - 1]);
+ }
+ else
+ sendto_prefix_one(acptr, sptr, ":%s %s %s%s :%s",
+ parv[0], cmd, NumNick(acptr), parv[parc - 1]);
+ }
+ }
+ else if (MyUser(sptr) || Protocol(cptr) < 10)
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
+ else
+ sendto_one(sptr,
+ ":%s %d %s * :Target left UnderNet. Failed to deliver: [%.50s]",
+ me.name, ERR_NOSUCHNICK, sptr->name, parv[parc - 1]);
+ continue;
+ }
+ /*
+ * The following two cases allow masks in NOTICEs
+ * (for OPERs only)
+ *
+ * Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
+ */
+ if ((*nick == '$' || *nick == '#') && IsAnOper(sptr))
+ {
+ if (MyConnect(sptr))
+ {
+ if (!(s = strrchr(nick, '.')))
+ {
+ sendto_one(sptr, err_str(ERR_NOTOPLEVEL), me.name, parv[0], nick);
+ continue;
+ }
+ while (*++s)
+ if (*s == '.' || *s == '*' || *s == '?')
+ break;
+ if (*s == '*' || *s == '?')
+ {
+ sendto_one(sptr, err_str(ERR_WILDTOPLEVEL), me.name, parv[0], nick);
+ continue;
+ }
+ }
+ sendto_match_butone(IsServer(cptr) ? cptr : NULL,
+ sptr, nick + 1, (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
+ ":%s %s %s :%s", parv[0], cmd, nick, parv[parc - 1]);
+ continue;
+ }
+ else if ((server = strchr(nick, '@')) && (acptr = FindServer(server + 1)))
+ {
+ /*
+ * NICK[%host]@server addressed? See if <server> is me first
+ */
+ if (!IsMe(acptr))
+ {
+ sendto_one(acptr, ":%s %s %s :%s", parv[0], cmd, nick, parv[parc - 1]);
+ continue;
+ }
+
+ /* Look for an user whose NICK is equal to <nick> and then
+ * check if it's hostname matches <host> and if it's a local
+ * user. */
+ *server = '\0';
+ if ((host = strchr(nick, '%')))
+ *host++ = '\0';
+
+ if ((!(acptr = FindUser(nick))) ||
+ (!(MyUser(acptr))) ||
+ ((!(BadPtr(host))) && match(host, acptr->user->host)))
+ acptr = NULL;
+
+ *server = '@';
+ if (host)
+ *--host = '%';
+
+ if (acptr)
+ {
+ if (!(is_silenced(sptr, acptr)))
+ sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
+ parv[0], cmd, nick, parv[parc - 1]);
+ continue;
+ }
+ }
+ if (IsChannelName(nick))
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], nick);
+ else
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
+ }
+ return 0;
+}
+
+/*
+ * m_private
+ *
+ * parv[0] = sender prefix
+ * parv[1] = receiver list
+ * parv[parc-1] = message text
+ */
+int m_private(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ return m_message(cptr, sptr, parc, parv, 0);
+}
+
+/*
+ * m_notice
+ *
+ * parv[0] = sender prefix
+ * parv[1] = receiver list
+ * parv[parc-1] = notice text
+ */
+int m_notice(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ if (MyUser(sptr) && parv[1] && parv[1][0] == '@' &&
+ IsChannelName(&parv[1][1]))
+ {
+ parv[1]++; /* Get rid of '@' */
+ return m_wallchops(cptr, sptr, parc, parv);
+ }
+ return m_message(cptr, sptr, parc, parv, 1);
+}
+
+
+/*
+ * whisper - called from m_cnotice and m_cprivmsg.
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nick
+ * parv[2] = #channel
+ * parv[3] = Private message text
+ *
+ * Added 971023 by Run.
+ * Reason: Allows channel operators to sent an arbitrary number of private
+ * messages to users on their channel, avoiding the max.targets limit.
+ * Building this into m_private would use too much cpu because we'd have
+ * to a cross channel lookup for every private message!
+ * Note that we can't allow non-chan ops to use this command, it would be
+ * abused by mass advertisers.
+ */
+int whisper(aClient *sptr, int parc, char *parv[], int notice)
+{
+ int s_is_member = 0, s_is_voiced = 0, t_is_member = 0;
+ aClient *tcptr;
+ aChannel *chptr;
+ register Link *lp;
+
+ if (!MyUser(sptr))
+ return 0;
+ if (parc < 4 || BadPtr(parv[3]))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+ me.name, parv[0], notice ? "CNOTICE" : "CPRIVMSG");
+ return 0;
+ }
+ if (!(chptr = FindChannel(parv[2])))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[2]);
+ return 0;
+ }
+ if (!(tcptr = FindUser(parv[1])))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
+ return 0;
+ }
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ register aClient *mcptr = lp->value.cptr;
+ if (mcptr == sptr)
+ {
+ s_is_member = 1;
+ if ((lp->flags & (CHFL_CHANOP | CHFL_VOICE)))
+ s_is_voiced = 1;
+ else
+ break;
+ if (t_is_member)
+ break;
+ }
+ if (mcptr == tcptr)
+ {
+ t_is_member = 1;
+ if (s_is_voiced)
+ break;
+ }
+ }
+ if (!s_is_voiced)
+ {
+ sendto_one(sptr, err_str(s_is_member ? ERR_VOICENEEDED : ERR_NOTONCHANNEL),
+ me.name, parv[0], chptr->chname);
+ return 0;
+ }
+ if (!t_is_member)
+ {
+ sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
+ me.name, parv[0], tcptr->name, chptr->chname);
+ return 0;
+ }
+ if (is_silenced(sptr, tcptr))
+ return 0;
+
+ if (tcptr->user && tcptr->user->away)
+ sendto_one(sptr, rpl_str(RPL_AWAY),
+ me.name, parv[0], tcptr->name, tcptr->user->away);
+ if (MyUser(tcptr) || Protocol(tcptr->from) < 10)
+ sendto_prefix_one(tcptr, sptr, ":%s %s %s :%s",
+ parv[0], notice ? "NOTICE" : "PRIVMSG", tcptr->name, parv[3]);
+ else
+ sendto_prefix_one(tcptr, sptr, ":%s %s %s%s :%s",
+ parv[0], notice ? "NOTICE" : "PRIVMSG", NumNick(tcptr), parv[3]);
+
+ return 0;
+}
+
+int m_cnotice(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ return whisper(sptr, parc, parv, 1);
+}
+
+int m_cprivmsg(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ return whisper(sptr, parc, parv, 0);
+}
+
+/*
+ * m_wallchops
+ *
+ * parv[0] = sender prefix
+ * parv[1] = target channel
+ * parv[parc - 1] = wallchops text
+ */
+int m_wallchops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aChannel *chptr;
+
+ sptr->flags &= ~FLAGS_TS8;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NORECIPIENT), me.name, parv[0], "WALLCHOPS");
+ return -1;
+ }
+
+ if (parc < 3 || *parv[parc - 1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+ return -1;
+ }
+
+ if (MyUser(sptr))
+ parv[1] = canonize(parv[1]);
+
+ if (IsChannelName(parv[1]))
+ {
+ if ((chptr = FindChannel(parv[1])))
+ {
+ if (can_send(sptr, chptr) == 0)
+ {
+ if (MyUser(sptr) && (chptr->mode.mode & MODE_NOPRIVMSGS) &&
+ check_target_limit(sptr, chptr, chptr->chname, 0))
+ return 0;
+ /* Send to local clients: */
+ sendto_lchanops_butone(cptr, sptr, chptr,
+ ":%s NOTICE @%s :%s", parv[0], parv[1], parv[parc - 1]);
+#ifdef NO_PROTOCOL9
+ /* And to other servers: */
+ sendto_chanopsserv_butone(cptr, sptr, chptr,
+ ":%s WC %s :%s", parv[0], parv[1], parv[parc - 1]);
+#else
+ /*
+ * WARNING: `sendto_chanopsserv_butone' is heavily hacked when
+ * `NO_PROTOCOL9' is not defined ! Therefore this is the ONLY
+ * place you may use `sendto_chanopsserv_butone', until all
+ * servers are 2.10.
+ */
+ sendto_chanopsserv_butone(cptr, sptr, chptr,
+ ":%s WC %s :%s", parv[0], parv[1], parv[parc - 1]);
+#endif
+ }
+ else
+ sendto_one(sptr, err_str(ERR_CANNOTSENDTOCHAN),
+ me.name, parv[0], parv[1]);
+ }
+ }
+ else
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]);
+
+ return 0;
+}
+
+/*
+ * m_user
+ *
+ * parv[0] = sender prefix
+ * parv[1] = username (login name, account)
+ * parv[2] = umode mask
+ * parv[3] = server notice mask
+ * parv[4] = users real name info
+ */
+int m_user(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+#define UFLAGS (FLAGS_INVISIBLE|FLAGS_WALLOP|FLAGS_SERVNOTICE)
+ char *username, *host, *server, *realname;
+ anUser *user;
+
+ if (IsServer(cptr))
+ return 0;
+
+ if (IsServerPort(cptr))
+ return exit_client(cptr, cptr, &me, "Use a different port");
+
+ if (parc > 2 && (username = strchr(parv[1], '@')))
+ *username = '\0';
+ if (parc < 5 || *parv[1] == '\0' || *parv[2] == '\0' ||
+ *parv[3] == '\0' || *parv[4] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USER");
+ return 0;
+ }
+
+ /* Copy parameters into better documenting variables */
+
+ username = (parc < 2 || BadPtr(parv[1])) ? "<bad-boy>" : parv[1];
+ host = (parc < 3 || BadPtr(parv[2])) ? "<nohost>" : parv[2];
+ server = (parc < 4 || BadPtr(parv[3])) ? "<noserver>" : parv[3];
+ realname = (parc < 5 || BadPtr(parv[4])) ? "<bad-realname>" : parv[4];
+
+ user = make_user(sptr);
+
+ if (!IsUnknown(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]);
+ return 0;
+ }
+
+ if (!strchr(host, '.')) /* Not an IP# as hostname ? */
+ sptr->flags |= (UFLAGS & atoi(host));
+ if ((sptr->flags & FLAGS_SERVNOTICE))
+ set_snomask(sptr, (isDigit(*server) && !strchr(server, '.')) ?
+ (atoi(server) & SNO_USER) : SNO_DEFAULT, SNO_SET);
+ user->server = &me;
+ strncpy(sptr->info, realname, sizeof(sptr->info) - 1);
+ if (sptr->name[0] && sptr->cookie == COOKIE_VERIFIED)
+ /* NICK and PONG already received, now we have USER... */
+ return register_user(cptr, sptr, sptr->name, username);
+ else
+ {
+ strncpy(sptr->user->username, username, USERLEN);
+ strncpy(user->host, host, sizeof(user->host) - 1);
+ }
+ return 0;
+}
+
+/*
+ * m_quit
+ *
+ * parv[0] = sender prefix
+ * parv[parc-1] = comment
+ */
+int m_quit(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ register char *comment = (parc > 1
+ && parv[parc - 1]) ? parv[parc - 1] : cptr->name;
+
+ if (MyUser(sptr))
+ {
+ if (!strncmp("Local Kill", comment, 10) || !strncmp(comment, "Killed", 6))
+ comment = parv[0];
+ if (sptr->user)
+ {
+ Link *lp;
+ for (lp = sptr->user->channel; lp; lp = lp->next)
+ if (can_send(sptr, lp->value.chptr) != 0)
+ return exit_client(cptr, sptr, sptr, "Signed off");
+ }
+ }
+ if (strlen(comment) > (size_t)TOPICLEN)
+ comment[TOPICLEN] = '\0';
+ return IsServer(sptr) ? 0 : exit_client(cptr, sptr, sptr, comment);
+}
+
+/*
+ * m_kill
+ *
+ * parv[0] = sender prefix
+ * parv[1] = kill victim
+ * parv[parc-1] = kill path
+ */
+int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr;
+ char *inpath = get_client_name(cptr, FALSE);
+ char *user, *path, *killer;
+ int chasing = 0;
+
+ if (parc < 3 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL");
+ return 0;
+ }
+
+ user = parv[1];
+ path = parv[parc - 1]; /* Either defined or NULL (parc >= 3) */
+
+#ifdef OPER_KILL
+ if (!IsPrivileged(cptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+#else
+ if (!IsServer(cptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+#endif
+ if (IsAnOper(cptr))
+ {
+ if (BadPtr(path))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL");
+ return 0;
+ }
+ if (strlen(path) > (size_t)TOPICLEN)
+ path[TOPICLEN] = '\0';
+ }
+
+ if (MyUser(sptr) || Protocol(cptr) < 10)
+ {
+ if (!(acptr = FindClient(user)))
+ {
+ /*
+ * If the user has recently changed nick, we automaticly
+ * rewrite the KILL for this new nickname--this keeps
+ * servers in synch when nick change and kill collide
+ */
+ if (!(acptr = get_history(user, (long)15)))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], user);
+ return 0;
+ }
+ sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s",
+ me.name, parv[0], user, acptr->name);
+ chasing = 1;
+ }
+ }
+ else if (!(acptr = findNUser(user)))
+ {
+ if (Protocol(cptr) < 10 && IsUser(sptr))
+ sendto_one(sptr,
+ ":%s NOTICE %s :KILL target disconnected before I got him :(",
+ me.name, parv[0]);
+ else if (IsUser(sptr))
+ sendto_one(sptr,
+ "%s NOTICE %s%s :KILL target disconnected before I got him :(",
+ NumServ(&me), NumNick(sptr));
+ return 0;
+ }
+ if (!MyConnect(acptr) && IsLocOp(cptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+ if (IsServer(acptr) || IsMe(acptr))
+ {
+ sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]);
+ return 0;
+ }
+
+ /* if the user is +k, prevent a kill from local user */
+ if (IsChannelService(acptr) && MyUser(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name,
+ parv[0], "KILL", acptr->name);
+ return 0;
+ }
+
+#ifdef LOCAL_KILL_ONLY
+ if (MyConnect(sptr) && !MyConnect(acptr))
+ {
+ sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server",
+ me.name, parv[0], acptr->name);
+ return 0;
+ }
+#endif
+ if (!IsServer(cptr))
+ {
+ /*
+ * The kill originates from this server, initialize path.
+ * (In which case the 'path' may contain user suplied
+ * explanation ...or some nasty comment, sigh... >;-)
+ *
+ * ...!operhost!oper
+ * ...!operhost!oper (comment)
+ */
+ if (IsUnixSocket(cptr)) /* Don't use get_client_name syntax */
+ inpath = me.sockhost;
+ else
+ inpath = cptr->sockhost;
+ if (!BadPtr(path))
+ {
+ sprintf_irc(buf,
+ "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path);
+ path = buf;
+ }
+ else
+ path = cptr->name;
+ }
+ else if (BadPtr(path))
+ path = "*no-path*"; /* Bogus server sending??? */
+ /*
+ * Notify all *local* opers about the KILL (this includes the one
+ * originating the kill, if from this server--the special numeric
+ * reply message is not generated anymore).
+ *
+ * Note: "acptr->name" is used instead of "user" because we may
+ * have changed the target because of the nickname change.
+ */
+ if (IsLocOp(sptr) && !MyConnect(acptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+ return 0;
+ }
+ sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
+ "Received KILL message for %s. From %s Path: %s!%s",
+ acptr->name, parv[0], inpath, path);
+#if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
+ if (MyUser(acptr))
+ { /* get more infos when your local
+ clients are killed -- _dl */
+ if (IsServer(sptr))
+ syslog(LOG_DEBUG,
+ "A local client %s!%s@%s KILLED from %s [%s] Path: %s!%s)",
+ acptr->name, acptr->user->username, acptr->user->host,
+ parv[0], sptr->name, inpath, path);
+ else
+ syslog(LOG_DEBUG,
+ "A local client %s!%s@%s KILLED by %s [%s!%s@%s] (%s!%s)",
+ acptr->name, acptr->user->username, acptr->user->host,
+ parv[0], sptr->name, sptr->user->username, sptr->user->host,
+ inpath, path);
+ }
+ else if (IsOper(sptr))
+ syslog(LOG_DEBUG, "KILL From %s For %s Path %s!%s",
+ parv[0], acptr->name, inpath, path);
+#endif
+ /*
+ * And pass on the message to other servers. Note, that if KILL
+ * was changed, the message has to be sent to all links, also
+ * back.
+ * Suicide kills are NOT passed on --SRB
+ */
+ if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr))
+ {
+ sendto_lowprot_butone(cptr, 9, ":%s KILL %s :%s!%s",
+ parv[0], acptr->name, inpath, path);
+ sendto_highprot_butone(cptr, 10, ":%s KILL %s%s :%s!%s",
+ parv[0], NumNick(acptr), inpath, path);
+#ifndef NO_PROTOCOL9
+ if (chasing && IsServer(cptr)) /* Can be removed when all are Protocol 10 */
+ sendto_one(cptr, ":%s KILL %s :%s!%s",
+ me.name, acptr->name, inpath, path);
+#endif
+ /* We *can* have crossed a NICK with this numeric... --Run */
+ /* Note the following situation:
+ * KILL SAA --> X
+ * <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
+ * Where the KILL reaches point X before the QUIT does.
+ * This would then *still* cause an orphan because the KILL doesn't reach S
+ * (because of the SQUIT), the QUIT is ignored (because of the KILL)
+ * and the second NICK ... SAA causes an orphan on the server at the
+ * right (which then isn't removed when the SQUIT arrives).
+ * Therefore we still need to detect numeric nick collisions too.
+ */
+ if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
+ sendto_one(cptr, "%s KILL %s%s :%s!%s (Ghost5)",
+ NumServ(&me), NumNick(acptr), inpath, path);
+ acptr->flags |= FLAGS_KILLED;
+ }
+
+ /*
+ * Tell the victim she/he has been zapped, but *only* if
+ * the victim is on current server--no sense in sending the
+ * notification chasing the above kill, it won't get far
+ * anyway (as this user don't exist there any more either)
+ */
+ if (MyConnect(acptr))
+ sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s",
+ parv[0], acptr->name, inpath, path);
+ /*
+ * Set FLAGS_KILLED. This prevents exit_one_client from sending
+ * the unnecessary QUIT for this. (This flag should never be
+ * set in any other place)
+ */
+ if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr))
+ sprintf_irc(buf2, "Local kill by %s (%s)", sptr->name,
+ BadPtr(parv[parc - 1]) ? sptr->name : parv[parc - 1]);
+ else
+ {
+ if ((killer = strchr(path, ' ')))
+ {
+ while (*killer && *killer != '!')
+ killer--;
+ if (!*killer)
+ killer = path;
+ else
+ killer++;
+ }
+ else
+ killer = path;
+ sprintf_irc(buf2, "Killed (%s)", killer);
+ }
+ return exit_client(cptr, acptr, sptr, buf2);
+}
+
+/*
+ * m_away - Added 14 Dec 1988 by jto.
+ *
+ * parv[0] = sender prefix
+ * parv[1] = away message
+ */
+int m_away(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 char *away, *awy2 = parv[1];
+
+ away = sptr->user->away;
+
+ if (parc < 2 || !*awy2)
+ {
+ /* Marking as not away */
+ if (away)
+ {
+ RunFree(away);
+ sptr->user->away = NULL;
+ }
+ sendto_serv_butone(cptr, ":%s AWAY", parv[0]);
+ if (MyConnect(sptr))
+ sendto_one(sptr, rpl_str(RPL_UNAWAY), me.name, parv[0]);
+ return 0;
+ }
+
+ /* Marking as away */
+
+ if (strlen(awy2) > (size_t)TOPICLEN)
+ awy2[TOPICLEN] = '\0';
+ sendto_serv_butone(cptr, ":%s AWAY :%s", parv[0], awy2);
+
+ if (away)
+ away = (char *)RunRealloc(away, strlen(awy2) + 1);
+ else
+ away = (char *)RunMalloc(strlen(awy2) + 1);
+
+ sptr->user->away = away;
+ strcpy(away, awy2);
+ if (MyConnect(sptr))
+ sendto_one(sptr, rpl_str(RPL_NOWAWAY), me.name, parv[0]);
+ return 0;
+}
+
+/*
+ * m_ping
+ *
+ * parv[0] = sender prefix
+ * parv[1] = origin
+ * parv[2] = destination
+ */
+int m_ping(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr;
+ char *origin, *destination;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOORIGIN), me.name, parv[0]);
+ return 0;
+ }
+ origin = parv[1];
+ destination = parv[2]; /* Will get NULL or pointer (parc >= 2!!) */
+
+ acptr = FindClient(origin);
+ if (acptr && acptr != sptr)
+ origin = cptr->name;
+
+ if (!BadPtr(destination) && strCasediff(destination, me.name) != 0)
+ {
+ if ((acptr = FindServer(destination)))
+ sendto_one(acptr, ":%s PING %s :%s", parv[0], origin, destination);
+ else
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
+ me.name, parv[0], destination);
+ return 0;
+ }
+ }
+ else
+ sendto_one(sptr, ":%s PONG %s :%s", me.name, me.name, origin);
+ return 0;
+}
+
+/*
+ * m_pong
+ *
+ * parv[0] = sender prefix
+ * parv[1] = origin
+ * parv[2] = destination
+ */
+int m_pong(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aClient *acptr;
+ char *origin, *destination;
+
+ if (MyUser(sptr))
+ return 0;
+
+ /* Check to see if this is a PONG :cookie reply from an
+ * unregistered user. If so, process it. -record */
+
+ if ((!IsRegistered(sptr)) && (sptr->cookie != 0) &&
+ (sptr->cookie != COOKIE_VERIFIED) && (parc > 1))
+ {
+ if (atol(parv[parc - 1]) == (long)sptr->cookie)
+ {
+ sptr->cookie = COOKIE_VERIFIED;
+ if (sptr->user && *sptr->user->host && sptr->name[0]) /* NICK and
+ USER OK */
+ return register_user(cptr, sptr, sptr->name, sptr->user->username);
+ }
+ else
+ sendto_one(sptr, ":%s %d %s :To connect, type /QUOTE PONG %u",
+ me.name, ERR_BADPING, sptr->name, sptr->cookie);
+
+ return 0;
+ }
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOORIGIN), me.name, parv[0]);
+ return 0;
+ }
+
+ origin = parv[1];
+ destination = parv[2];
+ cptr->flags &= ~FLAGS_PINGSENT;
+ sptr->flags &= ~FLAGS_PINGSENT;
+
+ if (!BadPtr(destination) && strCasediff(destination, me.name) != 0)
+ {
+ if ((acptr = FindClient(destination)))
+ sendto_one(acptr, ":%s PONG %s %s", parv[0], origin, destination);
+ else
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
+ me.name, parv[0], destination);
+ return 0;
+ }
+ }
+#ifdef DEBUGMODE
+ else
+ Debug((DEBUG_NOTICE, "PONG: %s %s",
+ origin, destination ? destination : "*"));
+#endif
+ return 0;
+}
+
+static char umode_buf[2 * sizeof(user_modes) / sizeof(int)];
+
+/*
+ * added Sat Jul 25 07:30:42 EST 1992
+ */
+static void send_umode_out(aClient *cptr, aClient *sptr, int old)
+{
+ Reg1 int i;
+ Reg2 aClient *acptr;
+
+ send_umode(NULL, sptr, old, SEND_UMODES);
+
+ for (i = highest_fd; i >= 0; i--)
+ if ((acptr = loc_clients[i]) && IsServer(acptr) &&
+ (acptr != cptr) && (acptr != sptr) && *umode_buf)
+ sendto_one(acptr, ":%s MODE %s :%s", sptr->name, sptr->name, umode_buf);
+
+ if (cptr && MyUser(cptr))
+ send_umode(cptr, sptr, old, ALL_UMODES);
+}
+
+/*
+ * m_oper
+ * parv[0] = sender prefix
+ * parv[1] = oper name
+ * parv[2] = oper password
+ */
+int m_oper(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ aConfItem *aconf;
+ char *name, *password, *encr;
+#ifdef CRYPT_OPER_PASSWORD
+ char salt[3];
+#endif /* CRYPT_OPER_PASSWORD */
+
+ name = parc > 1 ? parv[1] : NULL;
+ password = parc > 2 ? parv[2] : NULL;
+
+ if (!IsServer(cptr) && (BadPtr(name) || BadPtr(password)))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "OPER");
+ return 0;
+ }
+
+ /* if message arrived from server, trust it, and set to oper */
+
+ if ((IsServer(cptr) || IsMe(cptr)) && !IsOper(sptr))
+ {
+ ++nrof.opers;
+ sptr->flags |= FLAGS_OPER;
+ sendto_serv_butone(cptr, ":%s MODE %s :+o", parv[0], parv[0]);
+ if (IsMe(cptr))
+ sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+ return 0;
+ }
+ else if (IsAnOper(sptr))
+ {
+ if (MyConnect(sptr))
+ sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+ return 0;
+ }
+ if (!(aconf = find_conf_exact(name, sptr->username, sptr->sockhost,
+ CONF_OPS)) && !(aconf = find_conf_exact(name, sptr->username,
+ inetntoa(cptr->ip), CONF_OPS)))
+ {
+ sendto_one(sptr, err_str(ERR_NOOPERHOST), me.name, parv[0]);
+ sendto_realops("Failed OPER attempt by %s (%s@%s)",
+ parv[0], sptr->user->username, sptr->sockhost);
+ return 0;
+ }
+#ifdef CRYPT_OPER_PASSWORD
+ /* use first two chars of the password they send in as salt */
+
+ /* passwd may be NULL. Head it off at the pass... */
+ salt[0] = '\0';
+ if (password && aconf->passwd)
+ {
+ salt[0] = aconf->passwd[0];
+ salt[1] = aconf->passwd[1];
+ salt[2] = '\0';
+ encr = crypt(password, salt);
+ }
+ else
+ encr = "";
+#else
+ encr = password;
+#endif /* CRYPT_OPER_PASSWORD */
+
+ if ((aconf->status & CONF_OPS) &&
+ !strcmp(encr, aconf->passwd) && attach_conf(sptr, aconf) == ACR_OK)
+ {
+ int old = (sptr->flags & ALL_UMODES);
+ char *s;
+
+ s = strchr(aconf->host, '@');
+ *s++ = '\0';
+#ifdef OPER_REMOTE
+ if (aconf->status == CONF_LOCOP)
+ {
+#else
+ if ((match(s, me.sockhost) && !IsLocal(sptr)) ||
+ aconf->status == CONF_LOCOP)
+ {
+#endif
+ ClearOper(sptr);
+ SetLocOp(sptr);
+ }
+ else
+ {
+ /* prevent someone from being both oper and local oper */
+ ClearLocOp(sptr);
+ SetOper(sptr);
+ ++nrof.opers;
+ }
+ *--s = '@';
+ sendto_ops("%s (%s@%s) is now operator (%c)", parv[0],
+ sptr->user->username, sptr->sockhost, IsOper(sptr) ? 'O' : 'o');
+ sptr->flags |= (FLAGS_WALLOP | FLAGS_SERVNOTICE | FLAGS_DEBUG);
+ set_snomask(sptr, SNO_OPERDEFAULT, SNO_ADD);
+ send_umode_out(cptr, sptr, old);
+ sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+#if !defined(CRYPT_OPER_PASSWORD) && (defined(FNAME_OPERLOG) ||\
+ (defined(USE_SYSLOG) && defined(SYSLOG_OPER)))
+ encr = "";
+#endif
+#if defined(USE_SYSLOG) && defined(SYSLOG_OPER)
+ syslog(LOG_INFO, "OPER (%s) (%s) by (%s!%s@%s)",
+ name, encr, parv[0], sptr->user->username, sptr->sockhost);
+#endif
+#ifdef FNAME_OPERLOG
+ if (IsUser(sptr))
+ write_log(FNAME_OPERLOG,
+ "%s OPER (%s) (%s) by (%s!%s@%s)\n", myctime(now), name,
+ encr, parv[0], sptr->user->username, sptr->sockhost);
+#endif
+ }
+ else
+ {
+ detach_conf(sptr, aconf);
+ sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
+ sendto_realops("Failed OPER attempt by %s (%s@%s)",
+ parv[0], sptr->user->username, sptr->sockhost);
+ }
+ return 0;
+}
+
+/*
+ * m_pass
+ *
+ * parv[0] = sender prefix
+ * parv[1] = password
+ */
+int m_pass(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ char *password = parc > 1 ? parv[1] : NULL;
+
+ if (BadPtr(password))
+ {
+ sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "PASS");
+ return 0;
+ }
+ if (!MyConnect(sptr) || (!IsUnknown(cptr) && !IsHandshake(cptr)))
+ {
+ sendto_one(cptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]);
+ return 0;
+ }
+ strncpy(cptr->passwd, password, sizeof(cptr->passwd) - 1);
+ return 0;
+}
+
+/*
+ * m_userhost
+ *
+ * Added by Darren Reed 13/8/91 to aid clients and reduce the need for
+ * complicated requests like WHOIS.
+ *
+ * Returns user/host information only (no spurious AWAY labels or channels).
+ *
+ * Rewritten to speed it up by Carlo Wood 3/8/97.
+ */
+int m_userhost(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ Reg1 char *s;
+ Reg2 int i, j = 5;
+ char *p = NULL, *sbuf;
+ aClient *acptr;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USERHOST");
+ return 0;
+ }
+
+ sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERHOST), me.name, parv[0]);
+ for (i = j, s = strtoken(&p, parv[1], " "); i && s;
+ s = strtoken(&p, (char *)NULL, " "), i--)
+ if ((acptr = FindUser(s)))
+ {
+ if (i < j)
+ *sbuf++ = ' ';
+ sbuf = sprintf_irc(sbuf, "%s%s=%c%s@%s", acptr->name,
+ IsAnOper(acptr) ? "*" : "", (acptr->user->away) ? '-' : '+',
+ acptr->user->username, acptr->user->host);
+ }
+ else
+ {
+ if (i < j)
+ sendbufto_one(sptr);
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], s);
+ sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERHOST), me.name, parv[0]);
+ j = i - 1;
+ }
+ if (j)
+ sendbufto_one(sptr);
+ return 0;
+}
+
+/*
+ * m_userip added by Carlo Wood 3/8/97.
+ *
+ * The same as USERHOST, but with the IP-number instead of the hostname.
+ */
+int m_userip(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ Reg1 char *s;
+ Reg3 int i, j = 5;
+ char *p = NULL, *sbuf;
+ aClient *acptr;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USERIP");
+ return 0;
+ }
+
+ sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERIP), me.name, parv[0]);
+ for (i = j, s = strtoken(&p, parv[1], " "); i && s;
+ s = strtoken(&p, (char *)NULL, " "), i--)
+ if ((acptr = FindUser(s)))
+ {
+ if (i < j)
+ *sbuf++ = ' ';
+ sbuf = sprintf_irc(sbuf, "%s%s=%c%s@%s", acptr->name,
+ IsAnOper(acptr) ? "*" : "", (acptr->user->away) ? '-' : '+',
+ acptr->user->username, inetntoa(acptr->ip));
+ }
+ else
+ {
+ if (i < j)
+ sendbufto_one(sptr);
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], s);
+ sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERIP), me.name, parv[0]);
+ j = i - 1;
+ }
+ if (i < j)
+ sendbufto_one(sptr);
+ return 0;
+}
+
+/*
+ * m_ison
+ *
+ * Added by Darren Reed 13/8/91 to act as an efficent user indicator
+ * with respect to cpu/bandwidth used. Implemented for NOTIFY feature in
+ * clients. Designed to reduce number of whois requests. Can process
+ * nicknames in batches as long as the maximum buffer length.
+ *
+ * format:
+ * ISON :nicklist
+ */
+
+int m_ison(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ Reg1 aClient *acptr;
+ Reg2 char *s, **pav = parv;
+ Reg3 size_t len;
+ char *p = NULL;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "ISON");
+ return 0;
+ }
+
+ sprintf_irc(buf, rpl_str(RPL_ISON), me.name, *parv);
+ len = strlen(buf);
+ buf[sizeof(buf) - 1] = 0;
+
+ for (s = strtoken(&p, *++pav, " "); s; s = strtoken(&p, NULL, " "))
+ if ((acptr = FindUser(s)))
+ {
+ strncat(buf, acptr->name, sizeof(buf) - 1 - len);
+ len += strlen(acptr->name);
+ if (len >= sizeof(buf) - 1)
+ break;
+ strcat(buf, " ");
+ len++;
+ }
+ sendto_one(sptr, "%s", buf);
+ return 0;
+}
+
+/*
+ * m_umode() added 15/10/91 By Darren Reed.
+ *
+ * parv[0] - sender
+ * parv[1] - username to change mode for
+ * parv[2] - modes to change
+ */
+int m_umode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg1 int flag;
+ Reg2 int *s;
+ Reg3 char **p, *m;
+ aClient *acptr;
+ int what, setflags;
+ snomask_t tmpmask = 0;
+ int snomask_given = 0;
+
+ what = MODE_ADD;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "MODE");
+ return 0;
+ }
+
+ if (!(acptr = FindUser(parv[1])))
+ {
+ if (MyConnect(sptr))
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]);
+ return 0;
+ }
+
+ if (IsServer(sptr) || sptr != acptr)
+ {
+ if (IsServer(cptr))
+ sendto_ops_butone(NULL, &me, ":%s WALLOPS :MODE for User %s From %s!%s",
+ me.name, parv[1], get_client_name(cptr, FALSE), sptr->name);
+ else
+ sendto_one(sptr, err_str(ERR_USERSDONTMATCH), me.name, parv[0]);
+ return 0;
+ }
+
+ if (parc < 3)
+ {
+ m = buf;
+ *m++ = '+';
+ for (s = user_modes; (flag = *s) && (m - buf < BUFSIZE - 4); s += 2)
+ if (sptr->flags & flag)
+ *m++ = (char)(*(s + 1));
+ *m = '\0';
+ sendto_one(sptr, rpl_str(RPL_UMODEIS), me.name, parv[0], buf);
+ if ((sptr->flags & FLAGS_SERVNOTICE) && MyConnect(sptr)
+ && sptr->snomask !=
+ (unsigned int)(IsOper(sptr) ? SNO_OPERDEFAULT : SNO_DEFAULT))
+ sendto_one(sptr, rpl_str(RPL_SNOMASK), me.name, parv[0], sptr->snomask,
+ sptr->snomask);
+ return 0;
+ }
+
+ /* find flags already set for user */
+ setflags = 0;
+ for (s = user_modes; (flag = *s); s += 2)
+ if (sptr->flags & flag)
+ setflags |= flag;
+ if (MyConnect(sptr))
+ tmpmask = sptr->snomask;
+
+ /*
+ * parse mode change string(s)
+ */
+ for (p = &parv[2]; *p; p++) /* p is changed in loop too */
+ for (m = *p; *m; m++)
+ switch (*m)
+ {
+ case '+':
+ what = MODE_ADD;
+ break;
+ case '-':
+ what = MODE_DEL;
+ break;
+ case 's':
+ if (*(p + 1) && is_snomask(*(p + 1)))
+ {
+ snomask_given = 1;
+ tmpmask = umode_make_snomask(tmpmask, *++p, what);
+ tmpmask &= (IsAnOper(sptr) ? SNO_ALL : SNO_USER);
+ }
+ else
+ tmpmask = (what == MODE_ADD) ?
+ (IsAnOper(sptr) ? SNO_OPERDEFAULT : SNO_DEFAULT) : 0;
+ if (tmpmask)
+ sptr->flags |= FLAGS_SERVNOTICE;
+ else
+ sptr->flags &= ~FLAGS_SERVNOTICE;
+ break;
+ /*
+ * We may not get these, but they shouldnt be in default:
+ */
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ break;
+ default:
+ for (s = user_modes; (flag = *s); s += 2)
+ if (*m == (char)(*(s + 1)))
+ {
+ if (what == MODE_ADD)
+ sptr->flags |= flag;
+ else if ((flag & (FLAGS_OPER | FLAGS_LOCOP)))
+ {
+ sptr->flags &= ~(FLAGS_OPER | FLAGS_LOCOP);
+ if (MyConnect(sptr))
+ tmpmask = sptr->snomask & ~SNO_OPER;
+ }
+ /* allow either -o or -O to reset all operator status's... */
+ else
+ sptr->flags &= ~flag;
+ break;
+ }
+ if (flag == 0 && MyConnect(sptr))
+ sendto_one(sptr, err_str(ERR_UMODEUNKNOWNFLAG), me.name, parv[0]);
+ break;
+ }
+ /*
+ * Stop users making themselves operators too easily:
+ */
+ if (!(setflags & FLAGS_OPER) && IsOper(sptr) && !IsServer(cptr))
+ ClearOper(sptr);
+ if (!(setflags & FLAGS_LOCOP) && IsLocOp(sptr) && !IsServer(cptr))
+ sptr->flags &= ~FLAGS_LOCOP;
+ if ((setflags & (FLAGS_OPER | FLAGS_LOCOP)) && !IsAnOper(sptr) &&
+ MyConnect(sptr))
+ det_confs_butmask(sptr, CONF_CLIENT & ~CONF_OPS);
+ /* new umode; servers can set it, local users cannot;
+ * prevents users from /kick'ing or /mode -o'ing */
+ if (!(setflags & FLAGS_CHSERV) && !IsServer(cptr))
+ sptr->flags &= ~FLAGS_CHSERV;
+ /*
+ * Compare new flags with old flags and send string which
+ * will cause servers to update correctly.
+ */
+ if ((setflags & FLAGS_OPER) && !IsOper(sptr))
+ --nrof.opers;
+ if (!(setflags & FLAGS_OPER) && IsOper(sptr))
+ ++nrof.opers;
+ if ((setflags & FLAGS_INVISIBLE) && !IsInvisible(sptr))
+ --nrof.inv_clients;
+ if (!(setflags & FLAGS_INVISIBLE) && IsInvisible(sptr))
+ ++nrof.inv_clients;
+ send_umode_out(cptr, sptr, setflags);
+
+ if (MyConnect(sptr))
+ {
+ if (tmpmask != sptr->snomask)
+ set_snomask(sptr, tmpmask, SNO_SET);
+ if (sptr->snomask && snomask_given)
+ sendto_one(sptr, rpl_str(RPL_SNOMASK), me.name, sptr->name,
+ sptr->snomask, sptr->snomask);
+ }
+
+ return 0;
+}
+
+/*
+ * Build umode string for BURST command
+ * --Run
+ */
+char *umode_str(aClient *cptr)
+{
+ char *m = umode_buf; /* Maximum string size: "owidg\0" */
+ int *s, flag, c_flags;
+
+ c_flags = cptr->flags & SEND_UMODES; /* cleaning up the original code */
+
+ for (s = user_modes; (flag = *s); s += 2)
+ if ((c_flags & flag))
+ *m++ = *(s + 1);
+ *m = '\0';
+
+ return umode_buf; /* Note: static buffer, gets
+ overwritten by send_umode() */
+}
+
+/*
+ * Send the MODE string for user (user) to connection cptr
+ * -avalon
+ */
+void send_umode(aClient *cptr, aClient *sptr, int old, int sendmask)
+{
+ Reg1 int *s, flag;
+ Reg2 char *m;
+ int what = MODE_NULL;
+
+ /*
+ * Build a string in umode_buf to represent the change in the user's
+ * mode between the new (sptr->flag) and 'old'.
+ */
+ m = umode_buf;
+ *m = '\0';
+ for (s = user_modes; (flag = *s); s += 2)
+ {
+ if (MyUser(sptr) && !(flag & sendmask))
+ continue;
+ if ((flag & old) && !(sptr->flags & flag))
+ {
+ if (what == MODE_DEL)
+ *m++ = *(s + 1);
+ else
+ {
+ what = MODE_DEL;
+ *m++ = '-';
+ *m++ = *(s + 1);
+ }
+ }
+ else if (!(flag & old) && (sptr->flags & flag))
+ {
+ if (what == MODE_ADD)
+ *m++ = *(s + 1);
+ else
+ {
+ what = MODE_ADD;
+ *m++ = '+';
+ *m++ = *(s + 1);
+ }
+ }
+ }
+ *m = '\0';
+ if (*umode_buf && cptr)
+ sendto_one(cptr, ":%s MODE %s :%s", sptr->name, sptr->name, umode_buf);
+}
+
+/*
+ * Check to see if this resembles a sno_mask. It is if 1) there is
+ * at least one digit and 2) The first digit occurs before the first
+ * alphabetic character.
+ */
+int is_snomask(char *word)
+{
+ if (word)
+ {
+ for (; *word; word++)
+ if (isDigit(*word))
+ return 1;
+ else if (isAlpha(*word))
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * If it begins with a +, count this as an additive mask instead of just
+ * a replacement. If what == MODE_DEL, "+" has no special effect.
+ */
+snomask_t umode_make_snomask(snomask_t oldmask, char *arg, int what)
+{
+ snomask_t sno_what;
+ snomask_t newmask;
+ if (*arg == '+')
+ {
+ arg++;
+ if (what == MODE_ADD)
+ sno_what = SNO_ADD;
+ else
+ sno_what = SNO_DEL;
+ }
+ else if (*arg == '-')
+ {
+ arg++;
+ if (what == MODE_ADD)
+ sno_what = SNO_DEL;
+ else
+ sno_what = SNO_ADD;
+ }
+ else
+ sno_what = (what == MODE_ADD) ? SNO_SET : SNO_DEL;
+ /* pity we don't have strtoul everywhere */
+ newmask = (snomask_t)atoi(arg);
+ if (sno_what == SNO_DEL)
+ newmask = oldmask & ~newmask;
+ else if (sno_what == SNO_ADD)
+ newmask |= oldmask;
+ return newmask;
+}
+
+/*
+ * This function sets a Client's server notices mask, according to
+ * the parameter 'what'. This could be even faster, but the code
+ * gets mighty hard to read :)
+ */
+void delfrom_list(aClient *, Link **);
+void set_snomask(aClient *cptr, snomask_t newmask, int what)
+{
+ snomask_t oldmask, diffmask; /* unsigned please */
+ int i;
+ Link *tmp;
+
+ oldmask = cptr->snomask;
+
+ if (what == SNO_ADD)
+ newmask |= oldmask;
+ else if (what == SNO_DEL)
+ newmask = oldmask & ~newmask;
+ else if (what != SNO_SET) /* absolute set, no math needed */
+ sendto_ops("setsnomask called with %d ?!", what);
+
+ newmask &= (IsAnOper(cptr) ? SNO_ALL : SNO_USER);
+
+ diffmask = oldmask ^ newmask;
+
+ for (i = 0; diffmask >> i; i++)
+ if (((diffmask >> i) & 1))
+ {
+ if (((newmask >> i) & 1))
+ {
+ tmp = make_link();
+ tmp->next = opsarray[i];
+ tmp->value.cptr = cptr;
+ opsarray[i] = tmp;
+ }
+ else
+ /* not real portable :( */
+ delfrom_list(cptr, &opsarray[i]);
+ }
+ cptr->snomask = newmask;
+}
+
+void delfrom_list(aClient *cptr, Link **list)
+{
+ Link *tmp, *prv = NULL;
+ for (tmp = *list; tmp; tmp = tmp->next)
+ {
+ if (tmp->value.cptr == cptr)
+ {
+ if (prv)
+ prv->next = tmp->next;
+ else
+ *list = tmp->next;
+ free_link(tmp);
+ break;
+ }
+ prv = tmp;
+ }
+}
+
+/*
+ * is_silenced : Does the actual check wether sptr is allowed
+ * to send a message to acptr.
+ * Both must be registered persons.
+ * If sptr is silenced by acptr, his message should not be propagated,
+ * but more over, if this is detected on a server not local to sptr
+ * the SILENCE mask is sent upstream.
+ */
+int is_silenced(aClient *sptr, aClient *acptr)
+{
+ Reg1 Link *lp;
+ Reg2 anUser *user;
+ static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+ static char senderip[16 + NICKLEN + USERLEN + 5];
+
+ if (!(acptr->user) || !(lp = acptr->user->silence) || !(user = sptr->user))
+ return 0;
+ sprintf_irc(sender, "%s!%s@%s", sptr->name, user->username, user->host);
+ sprintf_irc(senderip, "%s!%s@%s", sptr->name, user->username,
+ inetntoa(sptr->ip));
+ for (; lp; lp = lp->next)
+ {
+ if ((!(lp->flags & CHFL_SILENCE_IPMASK) && !match(lp->value.cp, sender)) ||
+ ((lp->flags & CHFL_SILENCE_IPMASK) && !match(lp->value.cp, senderip)))
+ {
+ if (!MyConnect(sptr))
+ {
+ if (Protocol(sptr->from) < 10)
+ sendto_one(sptr->from, ":%s SILENCE %s %s", acptr->name,
+ sptr->name, lp->value.cp);
+ else
+ sendto_one(sptr->from, ":%s SILENCE %s%s %s", acptr->name,
+ NumNick(sptr), lp->value.cp);
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * del_silence
+ *
+ * Removes all silence masks from the list of sptr that fall within `mask'
+ * Returns -1 if none where found, 0 otherwise.
+ */
+int del_silence(aClient *sptr, char *mask)
+{
+ Reg1 Link **lp;
+ Reg2 Link *tmp;
+ int ret = -1;
+
+ for (lp = &sptr->user->silence; *lp;)
+ if (!mmatch(mask, (*lp)->value.cp))
+ {
+ tmp = *lp;
+ *lp = tmp->next;
+ RunFree(tmp->value.cp);
+ free_link(tmp);
+ ret = 0;
+ }
+ else
+ lp = &(*lp)->next;
+
+ return ret;
+}
+
+static int add_silence(aClient *sptr, char *mask)
+{
+ Reg1 Link *lp, **lpp;
+ Reg3 int cnt = 0, len = strlen(mask);
+ char *ip_start;
+
+ for (lpp = &sptr->user->silence, lp = *lpp; lp;)
+ {
+ if (!strCasediff(mask, lp->value.cp))
+ return -1;
+ if (!mmatch(mask, lp->value.cp))
+ {
+ Link *tmp = lp;
+ *lpp = lp = lp->next;
+ RunFree(tmp->value.cp);
+ free_link(tmp);
+ continue;
+ }
+ if (MyUser(sptr))
+ {
+ len += strlen(lp->value.cp);
+ if ((len > MAXSILELENGTH) || (++cnt >= MAXSILES))
+ {
+ sendto_one(sptr, err_str(ERR_SILELISTFULL), me.name, sptr->name, mask);
+ return -1;
+ }
+ else if (!mmatch(lp->value.cp, mask))
+ return -1;
+ }
+ lpp = &lp->next;
+ lp = *lpp;
+ }
+ lp = make_link();
+ memset(lp, 0, sizeof(Link));
+ lp->next = sptr->user->silence;
+ lp->value.cp = (char *)RunMalloc(strlen(mask) + 1);
+ strcpy(lp->value.cp, mask);
+ if ((ip_start = strrchr(mask, '@')) && check_if_ipmask(ip_start + 1))
+ lp->flags = CHFL_SILENCE_IPMASK;
+ sptr->user->silence = lp;
+ return 0;
+}
+
+/*
+ * m_silence() - Added 19 May 1994 by Run.
+ *
+ * parv[0] = sender prefix
+ * From local client:
+ * parv[1] = mask (NULL sends the list)
+ * From remote client:
+ * parv[1] = Numeric nick that must be silenced
+ * parv[2] = mask
+ */
+int m_silence(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Link *lp;
+ aClient *acptr;
+ char c, *cp;
+
+ if (MyUser(sptr))
+ {
+ acptr = sptr;
+ if (parc < 2 || *parv[1] == '\0' || (acptr = FindUser(parv[1])))
+ {
+ if (!(acptr->user))
+ return 0;
+ for (lp = acptr->user->silence; lp; lp = lp->next)
+ sendto_one(sptr, rpl_str(RPL_SILELIST), me.name,
+ sptr->name, acptr->name, lp->value.cp);
+ sendto_one(sptr, rpl_str(RPL_ENDOFSILELIST), me.name, sptr->name,
+ acptr->name);
+ return 0;
+ }
+ cp = parv[1];
+ c = *cp;
+ if (c == '-' || c == '+')
+ cp++;
+ else if (!(strchr(cp, '@') || strchr(cp, '.') ||
+ strchr(cp, '!') || strchr(cp, '*')))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
+ return -1;
+ }
+ else
+ c = '+';
+ cp = pretty_mask(cp);
+ if ((c == '-' && !del_silence(sptr, cp)) ||
+ (c != '-' && !add_silence(sptr, cp)))
+ {
+ sendto_prefix_one(sptr, sptr, ":%s SILENCE %c%s", parv[0], c, cp);
+ if (c == '-')
+ sendto_serv_butone(NULL, ":%s SILENCE * -%s", sptr->name, cp);
+ }
+ }
+ else if (parc < 3 || *parv[2] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SILENCE");
+ return -1;
+ }
+ else
+ {
+ if (Protocol(cptr) < 10)
+ acptr = FindClient(parv[1]); /* In case of NOTE notice, parv[1] */
+ else if (parv[1][1]) /* can be a server */
+ acptr = findNUser(parv[1]);
+ else
+ acptr = FindNServer(parv[1]);
+
+ if (*parv[2] == '-')
+ {
+ if (!del_silence(sptr, parv[2] + 1))
+ sendto_serv_butone(cptr, ":%s SILENCE * %s", parv[0], parv[2]);
+ }
+ else
+ {
+ add_silence(sptr, parv[2]);
+ if (acptr && IsServer(acptr->from))
+ {
+ if (Protocol(acptr->from) < 10)
+ sendto_one(acptr, ":%s SILENCE %s %s", parv[0], acptr->name, parv[2]);
+ else if (IsServer(acptr))
+ sendto_one(acptr, ":%s SILENCE %s %s",
+ parv[0], NumServ(acptr), parv[2]);
+ else
+ sendto_one(acptr, ":%s SILENCE %s%s %s",
+ parv[0], NumNick(acptr), parv[2]);
+ }
+ }
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, common/send.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdio.h>
+#include "h.h"
+#include "struct.h"
+#include "s_bsd.h"
+#include "s_serv.h"
+#include "send.h"
+#include "s_misc.h"
+#include "common.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "list.h"
+#include "ircd.h"
+#include "channel.h"
+#include "bsd.h"
+#include "class.h"
+#include "s_user.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+char sendbuf[2048];
+static int sentalong[MAXCONNECTIONS];
+static int sentalong_marker;
+struct SLink *opsarray[32]; /* don't use highest bit unless you change
+ atoi to strtoul in sendto_op_mask() */
+#ifdef GODMODE
+char sendbuf2[2048];
+int sdbflag;
+#endif /* GODMODE */
+
+/*
+ * dead_link
+ *
+ * An error has been detected. The link *must* be closed,
+ * but *cannot* call ExitClient (m_bye) from here.
+ * Instead, mark it with FLAGS_DEADSOCKET. This should
+ * generate ExitClient from the main loop.
+ *
+ * If 'notice' is not NULL, it is assumed to be a format
+ * for a message to local opers. I can contain only one
+ * '%s', which will be replaced by the sockhost field of
+ * the failing link.
+ *
+ * Also, the notice is skipped for "uninteresting" cases,
+ * like Persons and yet unknown connections...
+ */
+
+static void dead_link(aClient *to, char *notice)
+{
+ to->flags |= FLAGS_DEADSOCKET;
+ /*
+ * If because of BUFFERPOOL problem then clean dbuf's now so that
+ * notices don't hurt operators below.
+ */
+ DBufClear(&to->recvQ);
+ DBufClear(&to->sendQ);
+
+ /* Keep a copy of the last comment, for later use... */
+ strncpy(LastDeadComment(to), notice, sizeof(LastDeadComment(to)));
+ LastDeadComment(to)[sizeof(LastDeadComment(to)) - 1] = 0;
+
+ if (!IsUser(to) && !IsUnknown(to) && !(to->flags & FLAGS_CLOSING))
+ sendto_ops("%s for %s", LastDeadComment(to), get_client_name(to, FALSE));
+ Debug((DEBUG_ERROR, LastDeadComment(to)));
+}
+
+/*
+ * flush_connections
+ *
+ * Used to empty all output buffers for all connections. Should only
+ * be called once per scan of connections. There should be a select in
+ * here perhaps but that means either forcing a timeout or doing a poll.
+ * When flushing, all we do is empty the obuffer array for each local
+ * client and try to send it. if we cant send it, it goes into the sendQ
+ * -avalon
+ */
+void flush_connections(int fd)
+{
+ Reg1 int i;
+ Reg2 aClient *cptr;
+
+ if (fd == me.fd)
+ {
+ for (i = highest_fd; i >= 0; i--)
+ if ((cptr = loc_clients[i]) && DBufLength(&cptr->sendQ) > 0)
+ send_queued(cptr);
+ }
+ else if (fd >= 0 && (cptr = loc_clients[fd]) && DBufLength(&cptr->sendQ) > 0)
+ send_queued(cptr);
+}
+
+/*
+ * send_queued
+ *
+ * This function is called from the main select-loop (or whatever)
+ * when there is a chance that some output would be possible. This
+ * attempts to empty the send queue as far as possible...
+ */
+void send_queued(aClient *to)
+{
+#ifndef pyr
+ if (to->flags & FLAGS_BLOCKED)
+ return; /* Don't bother */
+#endif
+ /*
+ * Once socket is marked dead, we cannot start writing to it,
+ * even if the error is removed...
+ */
+ if (IsDead(to))
+ {
+ /*
+ * Actually, we should *NEVER* get here--something is
+ * not working correct if send_queued is called for a
+ * dead socket... --msa
+ *
+ * But we DO get here since flush_connections() is called
+ * from the main loop when a server still had remaining data
+ * in its buffer (not ending on a new-line).
+ * I rather leave the test here then move it to the main loop
+ * though: It wouldn't save cpu and it might introduce a bug :/.
+ * --Run
+ */
+ return;
+ }
+ while (DBufLength(&to->sendQ) > 0)
+ {
+ const char *msg;
+ size_t len, rlen;
+ int tmp;
+
+ msg = dbuf_map(&to->sendQ, &len);
+ /* Returns always len > 0 */
+ if ((tmp = deliver_it(to, msg, len)) < 0)
+ {
+ dead_link(to, "Write error, closing link");
+ return;
+ }
+ rlen = tmp;
+ dbuf_delete(&to->sendQ, rlen);
+ to->lastsq = DBufLength(&to->sendQ) / 1024;
+ if (rlen < len)
+ {
+ to->flags |= FLAGS_BLOCKED; /* Wait till select() says
+ we can write again */
+ break;
+ }
+ }
+
+ return;
+}
+
+/*
+ * send message to single client
+ */
+void sendto_one(aClient *to, char *pattern, ...)
+{
+ va_list vl;
+ va_start(vl, pattern);
+ vsendto_one(to, pattern, vl);
+ va_end(vl);
+}
+
+void vsendto_one(aClient *to, char *pattern, va_list vl)
+{
+ vsprintf_irc(sendbuf, pattern, vl);
+ sendbufto_one(to);
+}
+
+void sendbufto_one(aClient *to)
+{
+ int len;
+
+ Debug((DEBUG_SEND, "Sending [%s] to %s", sendbuf, to->name));
+
+ if (to->from)
+ to = to->from;
+ if (IsDead(to))
+ return; /* This socket has already
+ been marked as dead */
+ if (to->fd < 0)
+ {
+ /* This is normal when 'to' was being closed (via exit_client
+ * and close_connection) --Run
+ * Print the debug message anyway...
+ */
+ Debug((DEBUG_ERROR, "Local socket %s with negative fd %d... AARGH!",
+ to->name, to->fd));
+ return;
+ }
+
+ len = strlen(sendbuf);
+ if (sendbuf[len - 1] != '\n')
+ {
+ if (len > 510)
+ len = 510;
+ sendbuf[len++] = '\r';
+ sendbuf[len++] = '\n';
+ sendbuf[len] = '\0';
+ }
+
+ if (IsMe(to))
+ {
+ char tmp_sendbuf[sizeof(sendbuf)];
+
+ strcpy(tmp_sendbuf, sendbuf);
+ sendto_ops("Trying to send [%s] to myself!", tmp_sendbuf);
+ return;
+ }
+
+ if (DBufLength(&to->sendQ) > get_sendq(to))
+ {
+ if (IsServer(to))
+ sendto_ops("Max SendQ limit exceeded for %s: "
+ SIZE_T_FMT " > " SIZE_T_FMT,
+ get_client_name(to, FALSE), DBufLength(&to->sendQ), get_sendq(to));
+ dead_link(to, "Max sendQ exceeded");
+ return;
+ }
+
+ else if (!dbuf_put(&to->sendQ, sendbuf, len))
+ {
+ dead_link(to, "Buffer allocation error");
+ return;
+ }
+#ifdef GODMODE
+
+ if (!sdbflag && !IsUser(to))
+ {
+ size_t len = strlen(sendbuf) - 2; /* Remove "\r\n" */
+ sdbflag = 1;
+ strncpy(sendbuf2, sendbuf, len);
+ sendbuf2[len] = '\0';
+ if (len > 402)
+ {
+ char c = sendbuf2[200];
+ sendbuf2[200] = 0;
+ sendto_ops("SND:%-8.8s(%.4d): \"%s...%s\"",
+ to->name, len, sendbuf2, &sendbuf2[len - 200]);
+ sendbuf2[200] = c;
+ }
+ else
+ sendto_ops("SND:%-8.8s(%.4d): \"%s\"", to->name, len, sendbuf2);
+ strcpy(sendbuf, sendbuf2);
+ strcat(sendbuf, "\r\n");
+ sdbflag = 0;
+ }
+
+#endif /* GODMODE */
+ /*
+ * Update statistics. The following is slightly incorrect
+ * because it counts messages even if queued, but bytes
+ * only really sent. Queued bytes get updated in SendQueued.
+ */
+ to->sendM += 1;
+ me.sendM += 1;
+ if (to->acpt != &me)
+ to->acpt->sendM += 1;
+ /*
+ * This little bit is to stop the sendQ from growing too large when
+ * there is no need for it to. Thus we call send_queued() every time
+ * 2k has been added to the queue since the last non-fatal write.
+ * Also stops us from deliberately building a large sendQ and then
+ * trying to flood that link with data (possible during the net
+ * relinking done by servers with a large load).
+ */
+ if (DBufLength(&to->sendQ) / 1024 > to->lastsq)
+ send_queued(to);
+}
+
+static void vsendto_prefix_one(register aClient *to, register aClient *from,
+ char *pattern, va_list vl)
+{
+ if (to && from && MyUser(to) && IsUser(from))
+ {
+ static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+ char *par;
+ int flag = 0;
+ Reg3 anUser *user = from->user;
+
+ par = va_arg(vl, char *);
+ strcpy(sender, from->name);
+ if (user)
+ {
+ if (*user->username)
+ {
+ strcat(sender, "!");
+ strcat(sender, user->username);
+ }
+ if (*user->host && !MyConnect(from))
+ {
+ strcat(sender, "@");
+ strcat(sender, user->host);
+ flag = 1;
+ }
+ }
+ /*
+ * Flag is used instead of strchr(sender, '@') for speed and
+ * also since username/nick may have had a '@' in them. -avalon
+ */
+ if (!flag && MyConnect(from) && *user->host)
+ {
+ strcat(sender, "@");
+ if (IsUnixSocket(from))
+ strcat(sender, user->host);
+ else
+ strcat(sender, from->sockhost);
+ }
+ *sendbuf = ':';
+ strcpy(&sendbuf[1], sender);
+ /* Assuming 'pattern' always starts with ":%s ..." */
+ vsprintf_irc(sendbuf + strlen(sendbuf), &pattern[3], vl);
+ }
+ else
+ vsprintf_irc(sendbuf, pattern, vl);
+ sendbufto_one(to);
+}
+
+void sendto_channel_butone(aClient *one, aClient *from, aChannel *chptr,
+ char *pattern, ...)
+{
+ va_list vl;
+ Reg1 Link *lp;
+ Reg2 aClient *acptr;
+ Reg3 int i;
+
+ va_start(vl, pattern);
+
+ ++sentalong_marker;
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ acptr = lp->value.cptr;
+ if (acptr->from == one || /* ...was the one I should skip */
+ (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+ continue;
+ if (MyConnect(acptr)) /* (It is always a client) */
+ vsendto_prefix_one(acptr, from, pattern, vl);
+ else if (sentalong[(i = acptr->from->fd)] != sentalong_marker)
+ {
+ sentalong[i] = sentalong_marker;
+ /* Don't send channel messages to links that are still eating
+ the net.burst: -- Run 2/1/1997 */
+ if (!IsBurstOrBurstAck(acptr->from))
+ vsendto_prefix_one(acptr, from, pattern, vl);
+ }
+ }
+ va_end(vl);
+ return;
+}
+
+void sendto_lchanops_butone(aClient *one, aClient *from, aChannel *chptr,
+ char *pattern, ...)
+{
+ va_list vl;
+ Reg1 Link *lp;
+ Reg2 aClient *acptr;
+
+ va_start(vl, pattern);
+
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ acptr = lp->value.cptr;
+ if (acptr == one || /* ...was the one I should skip */
+ !(lp->flags & CHFL_CHANOP) || /* Skip non chanops */
+ (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+ continue;
+ if (MyConnect(acptr)) /* (It is always a client) */
+ vsendto_prefix_one(acptr, from, pattern, vl);
+ }
+ va_end(vl);
+ return;
+}
+
+void sendto_chanopsserv_butone(aClient *one, aClient *from, aChannel *chptr,
+ char *pattern, ...)
+{
+ va_list vl;
+ Reg1 Link *lp;
+ Reg2 aClient *acptr;
+ Reg3 int i;
+#ifndef NO_PROTOCOL9
+ char target[128];
+ char *source, *tp, *msg;
+#endif
+
+ va_start(vl, pattern);
+
+ ++sentalong_marker;
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ acptr = lp->value.cptr;
+ if (acptr->from == acptr || /* Skip local clients */
+#ifndef NO_PROTOCOL9
+ Protocol(acptr->from) < 10 || /* Skip P09 links */
+#endif
+ acptr->from == one || /* ...was the one I should skip */
+ !(lp->flags & CHFL_CHANOP) || /* Skip non chanops */
+ (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+ continue;
+ if (sentalong[(i = acptr->from->fd)] != sentalong_marker)
+ {
+ sentalong[i] = sentalong_marker;
+ /* Don't send channel messages to links that are
+ still eating the net.burst: -- Run 2/1/1997 */
+ if (!IsBurstOrBurstAck(acptr->from))
+ vsendto_prefix_one(acptr, from, pattern, vl);
+ }
+ }
+
+#ifndef NO_PROTOCOL9
+ /* Send message to all 2.9 servers */
+ /* This is a hack, because it assumes that we know how `vl' is build up */
+ source = va_arg(vl, char *);
+ tp = va_arg(vl, char *); /* Channel */
+ msg = va_arg(vl, char *);
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ acptr = lp->value.cptr;
+ if (acptr->from == acptr || /* Skip local clients */
+ Protocol(acptr->from) > 9 || /* Skip P10 servers */
+ acptr->from == one || /* ...was the one I should skip */
+ !(lp->flags & CHFL_CHANOP) || /* Skip non chanops */
+ (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+ continue;
+ if (sentalong[(i = acptr->from->fd)] != sentalong_marker)
+ {
+ sentalong[i] = sentalong_marker;
+ /* Don't send channel messages to links that are
+ still eating the net.burst: -- Run 2/1/1997 */
+ if (!IsBurstOrBurstAck(acptr->from))
+ {
+ Link *lp2;
+ aClient *acptr2;
+ tp = target;
+ *tp = 0;
+ /* Find all chanops in this direction: */
+ for (lp2 = chptr->members; lp2; lp2 = lp2->next)
+ {
+ acptr2 = lp2->value.cptr;
+ if (acptr2->from == acptr->from && acptr2->from != one &&
+ (lp2->flags & CHFL_CHANOP) && !(lp2->flags & CHFL_ZOMBIE) &&
+ !IsDeaf(acptr2))
+ {
+ int len = strlen(acptr2->name);
+ if (tp + len + 2 > target + sizeof(target))
+ {
+ sendto_prefix_one(acptr, from,
+ ":%s NOTICE %s :%s", source, target, msg);
+ tp = target;
+ *tp = 0;
+ }
+ if (*target)
+ strcpy(tp++, ",");
+ strcpy(tp, acptr2->name);
+ tp += len;
+ }
+ }
+ sendto_prefix_one(acptr, from,
+ ":%s NOTICE %s :%s", source, target, msg);
+ }
+ }
+ }
+#endif
+
+ va_end(vl);
+ return;
+}
+
+/*
+ * sendto_server_butone
+ *
+ * Send a message to all connected servers except the client 'one'.
+ */
+void sendto_serv_butone(aClient *one, char *pattern, ...)
+{
+ va_list vl;
+ Reg1 Dlink *lp;
+
+ va_start(vl, pattern);
+ vsprintf_irc(sendbuf, pattern, vl);
+ va_end(vl);
+
+ for (lp = me.serv->down; lp; lp = lp->next)
+ {
+ if (one && lp->value.cptr == one->from)
+ continue;
+ sendbufto_one(lp->value.cptr);
+ }
+
+}
+
+/*
+ * sendbufto_serv_butone()
+ *
+ * Send prepared sendbuf to all connected servers except the client 'one'
+ * -Ghostwolf 18-May-97
+ */
+void sendbufto_serv_butone(aClient *one)
+{
+ Reg1 Dlink *lp;
+
+ for (lp = me.serv->down; lp; lp = lp->next)
+ {
+ if (one && lp->value.cptr == one->from)
+ continue;
+ sendbufto_one(lp->value.cptr);
+ }
+}
+
+/*
+ * sendto_common_channels()
+ *
+ * Sends a message to all people (inclusing `acptr') on local server
+ * who are in same channel with client `acptr'.
+ */
+void sendto_common_channels(aClient *acptr, char *pattern, ...)
+{
+ va_list vl;
+ Reg1 Link *chan;
+ Reg2 Link *member;
+
+ va_start(vl, pattern);
+
+ ++sentalong_marker;
+ if (acptr->fd >= 0)
+ sentalong[acptr->fd] = sentalong_marker;
+ /* loop through acptr's channels, and the members on their channels */
+ if (acptr->user)
+ for (chan = acptr->user->channel; chan; chan = chan->next)
+ for (member = chan->value.chptr->members; member; member = member->next)
+ {
+ Reg3 aClient *cptr = member->value.cptr;
+ if (MyConnect(cptr) && sentalong[cptr->fd] != sentalong_marker)
+ {
+ sentalong[cptr->fd] = sentalong_marker;
+ vsendto_prefix_one(cptr, acptr, pattern, vl);
+ }
+ }
+ if (MyConnect(acptr))
+ vsendto_prefix_one(acptr, acptr, pattern, vl);
+ va_end(vl);
+ return;
+}
+
+/*
+ * sendto_channel_butserv
+ *
+ * Send a message to all members of a channel that
+ * are connected to this server.
+ */
+void sendto_channel_butserv(aChannel *chptr, aClient *from, char *pattern, ...)
+{
+ va_list vl;
+ Reg1 Link *lp;
+ Reg2 aClient *acptr;
+
+ for (va_start(vl, pattern), lp = chptr->members; lp; lp = lp->next)
+ if (MyConnect(acptr = lp->value.cptr) && !(lp->flags & CHFL_ZOMBIE))
+ vsendto_prefix_one(acptr, from, pattern, vl);
+ va_end(vl);
+ return;
+}
+
+/*
+ * Send a msg to all ppl on servers/hosts that match a specified mask
+ * (used for enhanced PRIVMSGs)
+ *
+ * addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de)
+ */
+
+static int match_it(aClient *one, char *mask, int what)
+{
+ switch (what)
+ {
+ case MATCH_HOST:
+ return (match(mask, one->user->host) == 0);
+ case MATCH_SERVER:
+ default:
+ return (match(mask, one->user->server->name) == 0);
+ }
+}
+
+/*
+ * sendto_match_butone
+ *
+ * Send to all clients which match the mask in a way defined on 'what';
+ * either by user hostname or user servername.
+ */
+void sendto_match_butone(aClient *one, aClient *from,
+ char *mask, int what, char *pattern, ...)
+{
+ va_list vl;
+ Reg1 int i;
+ Reg2 aClient *cptr, *acptr;
+
+ va_start(vl, pattern);
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(cptr = loc_clients[i]))
+ continue; /* that clients are not mine */
+ if (cptr == one) /* must skip the origin !! */
+ continue;
+ if (IsServer(cptr))
+ {
+ for (acptr = client; acptr; acptr = acptr->next)
+ if (IsUser(acptr) && match_it(acptr, mask, what) && acptr->from == cptr)
+ break;
+ /* a person on that server matches the mask, so we
+ * send *one* msg to that server ...
+ */
+ if (acptr == NULL)
+ continue;
+ /* ... but only if there *IS* a matching person */
+ }
+ /* my client, does he match ? */
+ else if (!(IsUser(cptr) && match_it(cptr, mask, what)))
+ continue;
+ vsendto_prefix_one(cptr, from, pattern, vl);
+ }
+ va_end(vl);
+
+ return;
+}
+
+/*
+ * sendto_lops_butone
+ *
+ * Send to *local* ops but one.
+ */
+void sendto_lops_butone(aClient *one, char *pattern, ...)
+{
+ va_list vl;
+ Reg1 aClient *cptr;
+ aClient **cptrp;
+ int i;
+ char nbuf[1024];
+
+ sprintf_irc(nbuf, ":%s NOTICE %%s :*** Notice -- ", me.name);
+ va_start(vl, pattern);
+ vsprintf_irc(nbuf + strlen(nbuf), pattern, vl);
+ va_end(vl);
+ for (cptrp = me.serv->client_list, i = 0; i <= me.serv->nn_mask; ++cptrp, ++i)
+ if ((cptr = *cptrp) && cptr != one && SendServNotice(cptr))
+ {
+ sprintf_irc(sendbuf, nbuf, cptr->name);
+ sendbufto_one(cptr);
+ }
+ return;
+}
+
+/*
+ * sendto_op_mask
+ *
+ * Sends message to the list indicated by the bitmask field.
+ * Don't try to send to more than one list! That is not supported.
+ * Xorath 5/1/97
+ */
+void vsendto_op_mask(register snomask_t mask, const char *pattern, va_list vl)
+{
+ static char fmt[1024];
+ char *fmt_target;
+ register int i = 0; /* so that 1 points to opsarray[0] */
+ Link *opslist;
+
+ while ((mask >>= 1))
+ i++;
+ if (!(opslist = opsarray[i]))
+ return;
+
+ fmt_target = sprintf_irc(fmt, ":%s NOTICE ", me.name);
+ do
+ {
+ strcpy(fmt_target, opslist->value.cptr->name);
+ strcat(fmt_target, " :*** Notice -- ");
+ strcat(fmt_target, pattern);
+ vsendto_one(opslist->value.cptr, fmt, vl);
+ opslist = opslist->next;
+ }
+ while (opslist);
+}
+
+/*
+ * sendbufto_op_mask
+ *
+ * Send a prepared sendbuf to the list indicated by the bitmask field.
+ * Ghostwolf 16-May-97
+ */
+void sendbufto_op_mask(snomask_t mask)
+{
+ register int i = 0; /* so that 1 points to opsarray[0] */
+ Link *opslist;
+ while ((mask >>= 1))
+ i++;
+ if (!(opslist = opsarray[i]))
+ return;
+ do
+ {
+ sendbufto_one(opslist->value.cptr);
+ opslist = opslist->next;
+ }
+ while (opslist);
+}
+
+
+/*
+ * sendto_ops
+ *
+ * Send to *local* ops only.
+ */
+void vsendto_ops(const char *pattern, va_list vl)
+{
+ Reg1 aClient *cptr;
+ Reg2 int i;
+ char fmt[1024];
+ char *fmt_target;
+
+ fmt_target = sprintf_irc(fmt, ":%s NOTICE ", me.name);
+
+ for (i = 0; i <= highest_fd; i++)
+ if ((cptr = loc_clients[i]) && !IsServer(cptr) && !IsMe(cptr) &&
+ SendServNotice(cptr))
+ {
+ strcpy(fmt_target, cptr->name);
+ strcat(fmt_target, " :*** Notice -- ");
+ strcat(fmt_target, pattern);
+ vsendto_one(cptr, fmt, vl);
+ }
+
+ return;
+}
+
+void sendto_op_mask(snomask_t mask, const char *pattern, ...)
+{
+ va_list vl;
+ va_start(vl, pattern);
+ vsendto_op_mask(mask, pattern, vl);
+ va_end(vl);
+}
+
+void sendto_ops(const char *pattern, ...)
+{
+ va_list vl;
+ va_start(vl, pattern);
+ vsendto_op_mask(SNO_OLDSNO, pattern, vl);
+ va_end(vl);
+}
+
+/*
+ * sendto_ops_butone
+ *
+ * Send message to all operators.
+ * one - client not to send message to
+ * from- client which message is from *NEVER* NULL!!
+ */
+void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...)
+{
+ va_list vl;
+ Reg1 int i;
+ Reg2 aClient *cptr;
+
+ va_start(vl, pattern);
+ ++sentalong_marker;
+ for (cptr = client; cptr; cptr = cptr->next)
+ {
+ if (!SendWallops(cptr))
+ continue;
+ i = cptr->from->fd; /* find connection oper is on */
+ if (sentalong[i] == sentalong_marker) /* sent message along it already ? */
+ continue;
+ if (cptr->from == one)
+ continue; /* ...was the one I should skip */
+ sentalong[i] = sentalong_marker;
+ vsendto_prefix_one(cptr->from, from, pattern, vl);
+ }
+ va_end(vl);
+
+ return;
+}
+
+/*
+ * sendto_g_serv_butone
+ *
+ * Send message to all remote +g users (server links).
+ *
+ * one - server not to send message to.
+ */
+void sendto_g_serv_butone(aClient *one, char *pattern, ...)
+{
+ va_list vl;
+ aClient *cptr;
+ int i;
+
+ va_start(vl, pattern);
+ ++sentalong_marker;
+ vsprintf_irc(sendbuf, pattern, vl);
+ for (cptr = client; cptr; cptr = cptr->next)
+ {
+ if (!SendDebug(cptr))
+ continue;
+ i = cptr->from->fd; /* find connection user is on */
+ if (sentalong[i] == sentalong_marker) /* sent message along it already ? */
+ continue;
+ if (MyConnect(cptr))
+ continue;
+ sentalong[i] = sentalong_marker;
+ if (cptr->from == one)
+ continue;
+ sendbufto_one(cptr);
+ }
+ va_end(vl);
+
+ return;
+}
+
+/*
+ * sendto_prefix_one
+ *
+ * to - destination client
+ * from - client which message is from
+ *
+ * NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!!
+ * -avalon
+ */
+void sendto_prefix_one(Reg1 aClient *to, Reg2 aClient *from, char *pattern, ...)
+{
+ va_list vl;
+ va_start(vl, pattern);
+ vsendto_prefix_one(to, from, pattern, vl);
+ va_end(vl);
+}
+
+/*
+ * sendto_realops
+ *
+ * Send to *local* ops only but NOT +s nonopers.
+ */
+void sendto_realops(const char *pattern, ...)
+{
+ va_list vl;
+
+ va_start(vl, pattern);
+ vsendto_op_mask(SNO_OLDREALOP, pattern, vl);
+
+ va_end(vl);
+ return;
+}
+
+/*
+ * Send message to all servers of protocol 'p' and lower.
+ */
+void sendto_lowprot_butone(aClient *cptr, int p, char *pattern, ...)
+{
+ va_list vl;
+ Dlink *lp;
+ va_start(vl, pattern);
+ for (lp = me.serv->down; lp; lp = lp->next)
+ if (lp->value.cptr != cptr && Protocol(lp->value.cptr) <= p)
+ vsendto_one(lp->value.cptr, pattern, vl);
+ va_end(vl);
+}
+
+/*
+ * Send message to all servers of protocol 'p' and higher.
+ */
+void sendto_highprot_butone(aClient *cptr, int p, char *pattern, ...)
+{
+ va_list vl;
+ Dlink *lp;
+ va_start(vl, pattern);
+ for (lp = me.serv->down; lp; lp = lp->next)
+ if (lp->value.cptr != cptr && Protocol(lp->value.cptr) >= p)
+ vsendto_one(lp->value.cptr, pattern, vl);
+ va_end(vl);
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/s_ping.c
+ *
+ * (C) Copyright 1997
+ *
+ * Author:
+ *
+ * 1024/624ACAD5 1997/01/26 Carlo Wood, Run on IRC <carlo@runaway.xs4all.nl>
+ * Key fingerprint = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
+ * Get key from pgp-public-keys server or
+ * finger carlo@runaway.xs4all.nl for public key (dialin, try at 21-22h GMT).
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdio.h>
+#include "h.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+const char atoi_tab[4000] = {
+ '0','0','0',0, '0','0','1',0, '0','0','2',0, '0','0','3',0, '0','0','4',0,
+ '0','0','5',0, '0','0','6',0, '0','0','7',0, '0','0','8',0, '0','0','9',0,
+ '0','1','0',0, '0','1','1',0, '0','1','2',0, '0','1','3',0, '0','1','4',0,
+ '0','1','5',0, '0','1','6',0, '0','1','7',0, '0','1','8',0, '0','1','9',0,
+ '0','2','0',0, '0','2','1',0, '0','2','2',0, '0','2','3',0, '0','2','4',0,
+ '0','2','5',0, '0','2','6',0, '0','2','7',0, '0','2','8',0, '0','2','9',0,
+ '0','3','0',0, '0','3','1',0, '0','3','2',0, '0','3','3',0, '0','3','4',0,
+ '0','3','5',0, '0','3','6',0, '0','3','7',0, '0','3','8',0, '0','3','9',0,
+ '0','4','0',0, '0','4','1',0, '0','4','2',0, '0','4','3',0, '0','4','4',0,
+ '0','4','5',0, '0','4','6',0, '0','4','7',0, '0','4','8',0, '0','4','9',0,
+ '0','5','0',0, '0','5','1',0, '0','5','2',0, '0','5','3',0, '0','5','4',0,
+ '0','5','5',0, '0','5','6',0, '0','5','7',0, '0','5','8',0, '0','5','9',0,
+ '0','6','0',0, '0','6','1',0, '0','6','2',0, '0','6','3',0, '0','6','4',0,
+ '0','6','5',0, '0','6','6',0, '0','6','7',0, '0','6','8',0, '0','6','9',0,
+ '0','7','0',0, '0','7','1',0, '0','7','2',0, '0','7','3',0, '0','7','4',0,
+ '0','7','5',0, '0','7','6',0, '0','7','7',0, '0','7','8',0, '0','7','9',0,
+ '0','8','0',0, '0','8','1',0, '0','8','2',0, '0','8','3',0, '0','8','4',0,
+ '0','8','5',0, '0','8','6',0, '0','8','7',0, '0','8','8',0, '0','8','9',0,
+ '0','9','0',0, '0','9','1',0, '0','9','2',0, '0','9','3',0, '0','9','4',0,
+ '0','9','5',0, '0','9','6',0, '0','9','7',0, '0','9','8',0, '0','9','9',0,
+ '1','0','0',0, '1','0','1',0, '1','0','2',0, '1','0','3',0, '1','0','4',0,
+ '1','0','5',0, '1','0','6',0, '1','0','7',0, '1','0','8',0, '1','0','9',0,
+ '1','1','0',0, '1','1','1',0, '1','1','2',0, '1','1','3',0, '1','1','4',0,
+ '1','1','5',0, '1','1','6',0, '1','1','7',0, '1','1','8',0, '1','1','9',0,
+ '1','2','0',0, '1','2','1',0, '1','2','2',0, '1','2','3',0, '1','2','4',0,
+ '1','2','5',0, '1','2','6',0, '1','2','7',0, '1','2','8',0, '1','2','9',0,
+ '1','3','0',0, '1','3','1',0, '1','3','2',0, '1','3','3',0, '1','3','4',0,
+ '1','3','5',0, '1','3','6',0, '1','3','7',0, '1','3','8',0, '1','3','9',0,
+ '1','4','0',0, '1','4','1',0, '1','4','2',0, '1','4','3',0, '1','4','4',0,
+ '1','4','5',0, '1','4','6',0, '1','4','7',0, '1','4','8',0, '1','4','9',0,
+ '1','5','0',0, '1','5','1',0, '1','5','2',0, '1','5','3',0, '1','5','4',0,
+ '1','5','5',0, '1','5','6',0, '1','5','7',0, '1','5','8',0, '1','5','9',0,
+ '1','6','0',0, '1','6','1',0, '1','6','2',0, '1','6','3',0, '1','6','4',0,
+ '1','6','5',0, '1','6','6',0, '1','6','7',0, '1','6','8',0, '1','6','9',0,
+ '1','7','0',0, '1','7','1',0, '1','7','2',0, '1','7','3',0, '1','7','4',0,
+ '1','7','5',0, '1','7','6',0, '1','7','7',0, '1','7','8',0, '1','7','9',0,
+ '1','8','0',0, '1','8','1',0, '1','8','2',0, '1','8','3',0, '1','8','4',0,
+ '1','8','5',0, '1','8','6',0, '1','8','7',0, '1','8','8',0, '1','8','9',0,
+ '1','9','0',0, '1','9','1',0, '1','9','2',0, '1','9','3',0, '1','9','4',0,
+ '1','9','5',0, '1','9','6',0, '1','9','7',0, '1','9','8',0, '1','9','9',0,
+ '2','0','0',0, '2','0','1',0, '2','0','2',0, '2','0','3',0, '2','0','4',0,
+ '2','0','5',0, '2','0','6',0, '2','0','7',0, '2','0','8',0, '2','0','9',0,
+ '2','1','0',0, '2','1','1',0, '2','1','2',0, '2','1','3',0, '2','1','4',0,
+ '2','1','5',0, '2','1','6',0, '2','1','7',0, '2','1','8',0, '2','1','9',0,
+ '2','2','0',0, '2','2','1',0, '2','2','2',0, '2','2','3',0, '2','2','4',0,
+ '2','2','5',0, '2','2','6',0, '2','2','7',0, '2','2','8',0, '2','2','9',0,
+ '2','3','0',0, '2','3','1',0, '2','3','2',0, '2','3','3',0, '2','3','4',0,
+ '2','3','5',0, '2','3','6',0, '2','3','7',0, '2','3','8',0, '2','3','9',0,
+ '2','4','0',0, '2','4','1',0, '2','4','2',0, '2','4','3',0, '2','4','4',0,
+ '2','4','5',0, '2','4','6',0, '2','4','7',0, '2','4','8',0, '2','4','9',0,
+ '2','5','0',0, '2','5','1',0, '2','5','2',0, '2','5','3',0, '2','5','4',0,
+ '2','5','5',0, '2','5','6',0, '2','5','7',0, '2','5','8',0, '2','5','9',0,
+ '2','6','0',0, '2','6','1',0, '2','6','2',0, '2','6','3',0, '2','6','4',0,
+ '2','6','5',0, '2','6','6',0, '2','6','7',0, '2','6','8',0, '2','6','9',0,
+ '2','7','0',0, '2','7','1',0, '2','7','2',0, '2','7','3',0, '2','7','4',0,
+ '2','7','5',0, '2','7','6',0, '2','7','7',0, '2','7','8',0, '2','7','9',0,
+ '2','8','0',0, '2','8','1',0, '2','8','2',0, '2','8','3',0, '2','8','4',0,
+ '2','8','5',0, '2','8','6',0, '2','8','7',0, '2','8','8',0, '2','8','9',0,
+ '2','9','0',0, '2','9','1',0, '2','9','2',0, '2','9','3',0, '2','9','4',0,
+ '2','9','5',0, '2','9','6',0, '2','9','7',0, '2','9','8',0, '2','9','9',0,
+ '3','0','0',0, '3','0','1',0, '3','0','2',0, '3','0','3',0, '3','0','4',0,
+ '3','0','5',0, '3','0','6',0, '3','0','7',0, '3','0','8',0, '3','0','9',0,
+ '3','1','0',0, '3','1','1',0, '3','1','2',0, '3','1','3',0, '3','1','4',0,
+ '3','1','5',0, '3','1','6',0, '3','1','7',0, '3','1','8',0, '3','1','9',0,
+ '3','2','0',0, '3','2','1',0, '3','2','2',0, '3','2','3',0, '3','2','4',0,
+ '3','2','5',0, '3','2','6',0, '3','2','7',0, '3','2','8',0, '3','2','9',0,
+ '3','3','0',0, '3','3','1',0, '3','3','2',0, '3','3','3',0, '3','3','4',0,
+ '3','3','5',0, '3','3','6',0, '3','3','7',0, '3','3','8',0, '3','3','9',0,
+ '3','4','0',0, '3','4','1',0, '3','4','2',0, '3','4','3',0, '3','4','4',0,
+ '3','4','5',0, '3','4','6',0, '3','4','7',0, '3','4','8',0, '3','4','9',0,
+ '3','5','0',0, '3','5','1',0, '3','5','2',0, '3','5','3',0, '3','5','4',0,
+ '3','5','5',0, '3','5','6',0, '3','5','7',0, '3','5','8',0, '3','5','9',0,
+ '3','6','0',0, '3','6','1',0, '3','6','2',0, '3','6','3',0, '3','6','4',0,
+ '3','6','5',0, '3','6','6',0, '3','6','7',0, '3','6','8',0, '3','6','9',0,
+ '3','7','0',0, '3','7','1',0, '3','7','2',0, '3','7','3',0, '3','7','4',0,
+ '3','7','5',0, '3','7','6',0, '3','7','7',0, '3','7','8',0, '3','7','9',0,
+ '3','8','0',0, '3','8','1',0, '3','8','2',0, '3','8','3',0, '3','8','4',0,
+ '3','8','5',0, '3','8','6',0, '3','8','7',0, '3','8','8',0, '3','8','9',0,
+ '3','9','0',0, '3','9','1',0, '3','9','2',0, '3','9','3',0, '3','9','4',0,
+ '3','9','5',0, '3','9','6',0, '3','9','7',0, '3','9','8',0, '3','9','9',0,
+ '4','0','0',0, '4','0','1',0, '4','0','2',0, '4','0','3',0, '4','0','4',0,
+ '4','0','5',0, '4','0','6',0, '4','0','7',0, '4','0','8',0, '4','0','9',0,
+ '4','1','0',0, '4','1','1',0, '4','1','2',0, '4','1','3',0, '4','1','4',0,
+ '4','1','5',0, '4','1','6',0, '4','1','7',0, '4','1','8',0, '4','1','9',0,
+ '4','2','0',0, '4','2','1',0, '4','2','2',0, '4','2','3',0, '4','2','4',0,
+ '4','2','5',0, '4','2','6',0, '4','2','7',0, '4','2','8',0, '4','2','9',0,
+ '4','3','0',0, '4','3','1',0, '4','3','2',0, '4','3','3',0, '4','3','4',0,
+ '4','3','5',0, '4','3','6',0, '4','3','7',0, '4','3','8',0, '4','3','9',0,
+ '4','4','0',0, '4','4','1',0, '4','4','2',0, '4','4','3',0, '4','4','4',0,
+ '4','4','5',0, '4','4','6',0, '4','4','7',0, '4','4','8',0, '4','4','9',0,
+ '4','5','0',0, '4','5','1',0, '4','5','2',0, '4','5','3',0, '4','5','4',0,
+ '4','5','5',0, '4','5','6',0, '4','5','7',0, '4','5','8',0, '4','5','9',0,
+ '4','6','0',0, '4','6','1',0, '4','6','2',0, '4','6','3',0, '4','6','4',0,
+ '4','6','5',0, '4','6','6',0, '4','6','7',0, '4','6','8',0, '4','6','9',0,
+ '4','7','0',0, '4','7','1',0, '4','7','2',0, '4','7','3',0, '4','7','4',0,
+ '4','7','5',0, '4','7','6',0, '4','7','7',0, '4','7','8',0, '4','7','9',0,
+ '4','8','0',0, '4','8','1',0, '4','8','2',0, '4','8','3',0, '4','8','4',0,
+ '4','8','5',0, '4','8','6',0, '4','8','7',0, '4','8','8',0, '4','8','9',0,
+ '4','9','0',0, '4','9','1',0, '4','9','2',0, '4','9','3',0, '4','9','4',0,
+ '4','9','5',0, '4','9','6',0, '4','9','7',0, '4','9','8',0, '4','9','9',0,
+ '5','0','0',0, '5','0','1',0, '5','0','2',0, '5','0','3',0, '5','0','4',0,
+ '5','0','5',0, '5','0','6',0, '5','0','7',0, '5','0','8',0, '5','0','9',0,
+ '5','1','0',0, '5','1','1',0, '5','1','2',0, '5','1','3',0, '5','1','4',0,
+ '5','1','5',0, '5','1','6',0, '5','1','7',0, '5','1','8',0, '5','1','9',0,
+ '5','2','0',0, '5','2','1',0, '5','2','2',0, '5','2','3',0, '5','2','4',0,
+ '5','2','5',0, '5','2','6',0, '5','2','7',0, '5','2','8',0, '5','2','9',0,
+ '5','3','0',0, '5','3','1',0, '5','3','2',0, '5','3','3',0, '5','3','4',0,
+ '5','3','5',0, '5','3','6',0, '5','3','7',0, '5','3','8',0, '5','3','9',0,
+ '5','4','0',0, '5','4','1',0, '5','4','2',0, '5','4','3',0, '5','4','4',0,
+ '5','4','5',0, '5','4','6',0, '5','4','7',0, '5','4','8',0, '5','4','9',0,
+ '5','5','0',0, '5','5','1',0, '5','5','2',0, '5','5','3',0, '5','5','4',0,
+ '5','5','5',0, '5','5','6',0, '5','5','7',0, '5','5','8',0, '5','5','9',0,
+ '5','6','0',0, '5','6','1',0, '5','6','2',0, '5','6','3',0, '5','6','4',0,
+ '5','6','5',0, '5','6','6',0, '5','6','7',0, '5','6','8',0, '5','6','9',0,
+ '5','7','0',0, '5','7','1',0, '5','7','2',0, '5','7','3',0, '5','7','4',0,
+ '5','7','5',0, '5','7','6',0, '5','7','7',0, '5','7','8',0, '5','7','9',0,
+ '5','8','0',0, '5','8','1',0, '5','8','2',0, '5','8','3',0, '5','8','4',0,
+ '5','8','5',0, '5','8','6',0, '5','8','7',0, '5','8','8',0, '5','8','9',0,
+ '5','9','0',0, '5','9','1',0, '5','9','2',0, '5','9','3',0, '5','9','4',0,
+ '5','9','5',0, '5','9','6',0, '5','9','7',0, '5','9','8',0, '5','9','9',0,
+ '6','0','0',0, '6','0','1',0, '6','0','2',0, '6','0','3',0, '6','0','4',0,
+ '6','0','5',0, '6','0','6',0, '6','0','7',0, '6','0','8',0, '6','0','9',0,
+ '6','1','0',0, '6','1','1',0, '6','1','2',0, '6','1','3',0, '6','1','4',0,
+ '6','1','5',0, '6','1','6',0, '6','1','7',0, '6','1','8',0, '6','1','9',0,
+ '6','2','0',0, '6','2','1',0, '6','2','2',0, '6','2','3',0, '6','2','4',0,
+ '6','2','5',0, '6','2','6',0, '6','2','7',0, '6','2','8',0, '6','2','9',0,
+ '6','3','0',0, '6','3','1',0, '6','3','2',0, '6','3','3',0, '6','3','4',0,
+ '6','3','5',0, '6','3','6',0, '6','3','7',0, '6','3','8',0, '6','3','9',0,
+ '6','4','0',0, '6','4','1',0, '6','4','2',0, '6','4','3',0, '6','4','4',0,
+ '6','4','5',0, '6','4','6',0, '6','4','7',0, '6','4','8',0, '6','4','9',0,
+ '6','5','0',0, '6','5','1',0, '6','5','2',0, '6','5','3',0, '6','5','4',0,
+ '6','5','5',0, '6','5','6',0, '6','5','7',0, '6','5','8',0, '6','5','9',0,
+ '6','6','0',0, '6','6','1',0, '6','6','2',0, '6','6','3',0, '6','6','4',0,
+ '6','6','5',0, '6','6','6',0, '6','6','7',0, '6','6','8',0, '6','6','9',0,
+ '6','7','0',0, '6','7','1',0, '6','7','2',0, '6','7','3',0, '6','7','4',0,
+ '6','7','5',0, '6','7','6',0, '6','7','7',0, '6','7','8',0, '6','7','9',0,
+ '6','8','0',0, '6','8','1',0, '6','8','2',0, '6','8','3',0, '6','8','4',0,
+ '6','8','5',0, '6','8','6',0, '6','8','7',0, '6','8','8',0, '6','8','9',0,
+ '6','9','0',0, '6','9','1',0, '6','9','2',0, '6','9','3',0, '6','9','4',0,
+ '6','9','5',0, '6','9','6',0, '6','9','7',0, '6','9','8',0, '6','9','9',0,
+ '7','0','0',0, '7','0','1',0, '7','0','2',0, '7','0','3',0, '7','0','4',0,
+ '7','0','5',0, '7','0','6',0, '7','0','7',0, '7','0','8',0, '7','0','9',0,
+ '7','1','0',0, '7','1','1',0, '7','1','2',0, '7','1','3',0, '7','1','4',0,
+ '7','1','5',0, '7','1','6',0, '7','1','7',0, '7','1','8',0, '7','1','9',0,
+ '7','2','0',0, '7','2','1',0, '7','2','2',0, '7','2','3',0, '7','2','4',0,
+ '7','2','5',0, '7','2','6',0, '7','2','7',0, '7','2','8',0, '7','2','9',0,
+ '7','3','0',0, '7','3','1',0, '7','3','2',0, '7','3','3',0, '7','3','4',0,
+ '7','3','5',0, '7','3','6',0, '7','3','7',0, '7','3','8',0, '7','3','9',0,
+ '7','4','0',0, '7','4','1',0, '7','4','2',0, '7','4','3',0, '7','4','4',0,
+ '7','4','5',0, '7','4','6',0, '7','4','7',0, '7','4','8',0, '7','4','9',0,
+ '7','5','0',0, '7','5','1',0, '7','5','2',0, '7','5','3',0, '7','5','4',0,
+ '7','5','5',0, '7','5','6',0, '7','5','7',0, '7','5','8',0, '7','5','9',0,
+ '7','6','0',0, '7','6','1',0, '7','6','2',0, '7','6','3',0, '7','6','4',0,
+ '7','6','5',0, '7','6','6',0, '7','6','7',0, '7','6','8',0, '7','6','9',0,
+ '7','7','0',0, '7','7','1',0, '7','7','2',0, '7','7','3',0, '7','7','4',0,
+ '7','7','5',0, '7','7','6',0, '7','7','7',0, '7','7','8',0, '7','7','9',0,
+ '7','8','0',0, '7','8','1',0, '7','8','2',0, '7','8','3',0, '7','8','4',0,
+ '7','8','5',0, '7','8','6',0, '7','8','7',0, '7','8','8',0, '7','8','9',0,
+ '7','9','0',0, '7','9','1',0, '7','9','2',0, '7','9','3',0, '7','9','4',0,
+ '7','9','5',0, '7','9','6',0, '7','9','7',0, '7','9','8',0, '7','9','9',0,
+ '8','0','0',0, '8','0','1',0, '8','0','2',0, '8','0','3',0, '8','0','4',0,
+ '8','0','5',0, '8','0','6',0, '8','0','7',0, '8','0','8',0, '8','0','9',0,
+ '8','1','0',0, '8','1','1',0, '8','1','2',0, '8','1','3',0, '8','1','4',0,
+ '8','1','5',0, '8','1','6',0, '8','1','7',0, '8','1','8',0, '8','1','9',0,
+ '8','2','0',0, '8','2','1',0, '8','2','2',0, '8','2','3',0, '8','2','4',0,
+ '8','2','5',0, '8','2','6',0, '8','2','7',0, '8','2','8',0, '8','2','9',0,
+ '8','3','0',0, '8','3','1',0, '8','3','2',0, '8','3','3',0, '8','3','4',0,
+ '8','3','5',0, '8','3','6',0, '8','3','7',0, '8','3','8',0, '8','3','9',0,
+ '8','4','0',0, '8','4','1',0, '8','4','2',0, '8','4','3',0, '8','4','4',0,
+ '8','4','5',0, '8','4','6',0, '8','4','7',0, '8','4','8',0, '8','4','9',0,
+ '8','5','0',0, '8','5','1',0, '8','5','2',0, '8','5','3',0, '8','5','4',0,
+ '8','5','5',0, '8','5','6',0, '8','5','7',0, '8','5','8',0, '8','5','9',0,
+ '8','6','0',0, '8','6','1',0, '8','6','2',0, '8','6','3',0, '8','6','4',0,
+ '8','6','5',0, '8','6','6',0, '8','6','7',0, '8','6','8',0, '8','6','9',0,
+ '8','7','0',0, '8','7','1',0, '8','7','2',0, '8','7','3',0, '8','7','4',0,
+ '8','7','5',0, '8','7','6',0, '8','7','7',0, '8','7','8',0, '8','7','9',0,
+ '8','8','0',0, '8','8','1',0, '8','8','2',0, '8','8','3',0, '8','8','4',0,
+ '8','8','5',0, '8','8','6',0, '8','8','7',0, '8','8','8',0, '8','8','9',0,
+ '8','9','0',0, '8','9','1',0, '8','9','2',0, '8','9','3',0, '8','9','4',0,
+ '8','9','5',0, '8','9','6',0, '8','9','7',0, '8','9','8',0, '8','9','9',0,
+ '9','0','0',0, '9','0','1',0, '9','0','2',0, '9','0','3',0, '9','0','4',0,
+ '9','0','5',0, '9','0','6',0, '9','0','7',0, '9','0','8',0, '9','0','9',0,
+ '9','1','0',0, '9','1','1',0, '9','1','2',0, '9','1','3',0, '9','1','4',0,
+ '9','1','5',0, '9','1','6',0, '9','1','7',0, '9','1','8',0, '9','1','9',0,
+ '9','2','0',0, '9','2','1',0, '9','2','2',0, '9','2','3',0, '9','2','4',0,
+ '9','2','5',0, '9','2','6',0, '9','2','7',0, '9','2','8',0, '9','2','9',0,
+ '9','3','0',0, '9','3','1',0, '9','3','2',0, '9','3','3',0, '9','3','4',0,
+ '9','3','5',0, '9','3','6',0, '9','3','7',0, '9','3','8',0, '9','3','9',0,
+ '9','4','0',0, '9','4','1',0, '9','4','2',0, '9','4','3',0, '9','4','4',0,
+ '9','4','5',0, '9','4','6',0, '9','4','7',0, '9','4','8',0, '9','4','9',0,
+ '9','5','0',0, '9','5','1',0, '9','5','2',0, '9','5','3',0, '9','5','4',0,
+ '9','5','5',0, '9','5','6',0, '9','5','7',0, '9','5','8',0, '9','5','9',0,
+ '9','6','0',0, '9','6','1',0, '9','6','2',0, '9','6','3',0, '9','6','4',0,
+ '9','6','5',0, '9','6','6',0, '9','6','7',0, '9','6','8',0, '9','6','9',0,
+ '9','7','0',0, '9','7','1',0, '9','7','2',0, '9','7','3',0, '9','7','4',0,
+ '9','7','5',0, '9','7','6',0, '9','7','7',0, '9','7','8',0, '9','7','9',0,
+ '9','8','0',0, '9','8','1',0, '9','8','2',0, '9','8','3',0, '9','8','4',0,
+ '9','8','5',0, '9','8','6',0, '9','8','7',0, '9','8','8',0, '9','8','9',0,
+ '9','9','0',0, '9','9','1',0, '9','9','2',0, '9','9','3',0, '9','9','4',0,
+ '9','9','5',0, '9','9','6',0, '9','9','7',0, '9','9','8',0, '9','9','9',0 };
+
+/* *INDENT-ON* */
+
+static char scratch_buffer[32];
+
+/*
+ * sprintf_irc
+ *
+ * sprintf_irc is optimized for the formats: %c, %s, %lu, %d and %u.
+ * Where %lu actually equals %l09u (for printing time_t timestamps).
+ *
+ * sprintf_irc is NOT optimized for any other format and resorts to using
+ * the normal sprintf when it encounters a format it doesn't understand
+ * (including padding, width, precision etc).
+ *
+ * The following benchmark was measured on a PPro200 with linux 2.0.30,
+ * libc 5.4.28, compiled with gcc-2.7.2.1 with -O3.
+ *
+ * Format sprintf sprintf_irc Speed up factor
+ *
+ * "12345678901234567890" 3.385 us 0.859 us 3.94
+ * "%s", buffer (20 chars) 3.780 us 0.547 us 6.91
+ * "%c%c%c", c1, c2, c3 3.640 us 0.376 us 9.68
+ * "%lu", (time_t)t 5.851 us 0.842 us 6.95
+ *
+ * Less important:
+ * "%d", 31337 4.487 us 0.852 us 5.27
+ * "%u.%u.%u.%u", 9.069 us 2.431 us 3.73
+ *
+ * Not used:
+ * "%03d", 401 4.348 us
+ * "%N" 0.216 us 20.13
+ *
+ * --Run
+ */
+char *vsprintf_irc(register char *str,
+ register const char *format, register va_list vl)
+{
+ register char c;
+
+ while ((c = *format++))
+ {
+ if (c == '%')
+ {
+ c = *format++; /* May never be '\0' ! */
+ if (c == 'c')
+ {
+ *str++ = (char)va_arg(vl, int);
+ continue;
+ }
+ if (c == 's')
+ {
+ register const char *p1 = va_arg(vl, const char *);
+ if ((*str = *p1))
+ while ((*++str = *++p1));
+ continue;
+ }
+ if (c == 'l' && *format == 'u') /* Prints time_t value in interval
+ [ 100000000 , 4294967295 ]
+ Actually prints like "%09lu" */
+ {
+ register unsigned long v1, v2;
+ register const char *ap;
+ ++format;
+ v1 = va_arg(vl, unsigned long);
+ if (v1 > 999999999L)
+ {
+ v2 = v1 / 1000000000;
+ v1 -= v2 * 1000000000;
+ *str++ = '0' + v2;
+ }
+ v2 = v1 / 1000000;
+ v1 -= v2 * 1000000;
+ ap = atoi_tab + (v2 << 2);
+ *str++ = *ap++;
+ *str++ = *ap++;
+ *str++ = *ap;
+ v2 = v1 / 1000;
+ v1 -= v2 * 1000;
+ ap = atoi_tab + (v2 << 2);
+ *str++ = *ap++;
+ *str++ = *ap++;
+ *str++ = *ap;
+ ap = atoi_tab + (v1 << 2);
+ *str++ = *ap++;
+ *str++ = *ap++;
+ *str++ = *ap;
+ continue;
+ }
+#if 0 /* Not used */
+ if (c == 'N') /* Prints "%03u" a numeric value in the
+ range [ 0, 999 ], padded with zero's */
+ {
+ register unsigned int v1;
+ register const char *ap;
+ v1 = va_arg(vl, unsigned int);
+ ap = atoi_tab + (v1 << 2);
+ *str++ = *ap++;
+ *str++ = *ap++;
+ *str++ = *ap;
+ continue;
+ }
+#endif
+ if (c == 'd')
+ {
+ register unsigned int v1, v2;
+ register const char *ap;
+ register char *s = &scratch_buffer[sizeof(scratch_buffer) - 2];
+ v1 = va_arg(vl, int);
+ if ((int)v1 <= 0)
+ {
+ if (v1 == 0)
+ {
+ *str++ = '0';
+ continue;
+ }
+ *str++ = '-';
+ v1 = -v1;
+ }
+ do
+ {
+ v2 = v1 / 1000;
+ ap = atoi_tab + 2 + ((v1 - 1000 * v2) << 2);
+ *s-- = *ap--;
+ *s-- = *ap--;
+ *s-- = *ap;
+ }
+ while ((v1 = v2) > 0);
+ while ('0' == *++s);
+ *str = *s;
+ while ((*++str = *++s));
+ continue;
+ }
+ if (c == 'u')
+ {
+ register unsigned int v1, v2;
+ register const char *ap;
+ register char *s = &scratch_buffer[sizeof(scratch_buffer) - 2];
+ v1 = va_arg(vl, unsigned int);
+ if (v1 == 0)
+ {
+ *str++ = '0';
+ continue;
+ }
+ do
+ {
+ v2 = v1 / 1000;
+ ap = atoi_tab + 2 + ((v1 - 1000 * v2) << 2);
+ *s-- = *ap--;
+ *s-- = *ap--;
+ *s-- = *ap;
+ }
+ while ((v1 = v2) > 0);
+ while ('0' == *++s);
+ *str = *s;
+ while ((*++str = *++s));
+ continue;
+ }
+ if (c != '%')
+ {
+ format -= 2;
+ str += vsprintf(str, format, vl);
+ break;
+ }
+ }
+ *str++ = c;
+ }
+ *str = 0;
+ return str;
+}
+
+char *sprintf_irc(register char *str, const char *format, ...)
+{
+ register va_list vl;
+ register char *ret;
+ va_start(vl, format);
+ ret = vsprintf_irc(str, format, vl);
+ va_end(vl);
+ return ret;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, common/support.c
+ * Copyright (C) 1990, 1991 Armin Gruner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#ifdef _SEQUENT_
+#include <sys/timers.h>
+#include <stddef.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdarg.h>
+#include <signal.h>
+#include "h.h"
+#include "send.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "support.h"
+#include "sprintf_irc.h"
+#include "common.h"
+#include "fileio.h"
+
+RCSTAG_CC("$Id$");
+
+#ifndef HAVE_STRTOKEN
+/*
+ * strtoken.c
+ *
+ * Walk through a string of tokens, using a set of separators.
+ * -argv 9/90
+ */
+char *strtoken(char **save, char *str, char *fs)
+{
+ char *pos = *save; /* keep last position across calls */
+ Reg1 char *tmp;
+
+ if (str)
+ pos = str; /* new string scan */
+
+ while (pos && *pos && strchr(fs, *pos) != NULL)
+ pos++; /* skip leading separators */
+
+ if (!pos || !*pos)
+ return (pos = *save = NULL); /* string contains only sep's */
+
+ tmp = pos; /* now, keep position of the token */
+
+ while (*pos && strchr(fs, *pos) == NULL)
+ pos++; /* skip content of the token */
+
+ if (*pos)
+ *pos++ = '\0'; /* remove first sep after the token */
+ else
+ pos = NULL; /* end of string */
+
+ *save = pos;
+ return (tmp);
+}
+#endif /* !HAVE_STRTOKEN */
+
+#ifndef HAVE_STRERROR
+/*
+ * strerror
+ *
+ * Returns an appropriate system error string to a given errno.
+ * -argv 11/90
+ */
+
+char *strerror(int err_no)
+{
+ static char buff[40];
+ char *errp;
+
+ errp = (err_no > sys_nerr ? (char *)NULL : sys_errlist[err_no]);
+
+ if (errp == (char *)NULL)
+ {
+ errp = buff;
+ sprintf_irc(errp, "Unknown Error %d", err_no);
+ }
+ return errp;
+}
+
+#endif /* !HAVE_STRERROR */
+
+/*
+ * inetntoa -- Changed the parameter to NOT take a pointer.
+ * -Run 4/8/97
+ * inetntoa -- Changed name to remove collision possibility and
+ * so behaviour is garanteed to take a pointer arg.
+ * -avalon 23/11/92
+ * inet_ntoa -- Returned the dotted notation of a given
+ * internet number (some ULTRIX don't have this)
+ * -argv 11/90.
+ * inet_ntoa -- Its broken on some Ultrix/Dynix too. -avalon
+ */
+char *inetntoa(struct in_addr in)
+{
+ static char buf[16];
+ Reg1 unsigned char *s = (unsigned char *)&in.s_addr;
+ Reg2 unsigned char a, b, c, d;
+
+ a = *s++;
+ b = *s++;
+ c = *s++;
+ d = *s++;
+ sprintf_irc(buf, "%u.%u.%u.%u", a, b, c, d);
+
+ return buf;
+}
+
+#ifndef HAVE_INET_NETOF
+/*
+ * inet_netof -- return the net portion of an internet number
+ * argv 11/90
+ */
+int inet_netof(struct in_addr in)
+{
+ int addr = in.s_net;
+
+ if (addr & 0x80 == 0)
+ return ((int)in.s_net);
+
+ if (addr & 0x40 == 0)
+ return ((int)in.s_net * 256 + in.s_host);
+
+ return ((int)in.s_net * 256 + in.s_host * 256 + in.s_lh);
+}
+
+#endif /* !HAVE_INET_NETOF */
+
+#ifndef HAVE_GETTIMEOFDAY
+/* This is copied from ircu3.0.0 (with permission), not vica versa. */
+int gettimeofday(struct timeval *tv, void * /*UNUSED*/)
+{
+ register int ret;
+ static struct timespec tp;
+
+ if ((ret = getclock(TIMEOFDAY, &tp)))
+ return ret;
+ tv->tv_sec = (long)tp.tv_sec;
+ tv->tv_usec = (tp.tv_nsec + 500) / 1000;
+ return 0;
+}
+#endif /* !HAVE_GETTIMEOFDAY */
+
+#ifdef DEBUGMODE
+
+void dumpcore(const char *pattern, ...)
+{
+ va_list vl;
+ static time_t lastd = 0;
+ static int dumps = 0;
+ char corename[12];
+ time_t now;
+ int p;
+
+ va_start(vl, pattern);
+
+ now = time(NULL);
+
+ if (!lastd)
+ lastd = now;
+ else if (now - lastd < 60 && dumps > 2)
+#ifdef __cplusplus
+ s_die(0);
+#else
+ s_die();
+#endif
+ if (now - lastd > 60)
+ {
+ lastd = now;
+ dumps = 1;
+ }
+ else
+ dumps++;
+ p = getpid();
+ if (fork() > 0)
+ {
+ kill(p, 3);
+ kill(p, 9);
+ }
+ write_pidfile();
+ sprintf_irc(corename, "core.%d", p);
+ rename("core", corename);
+ Debug((DEBUG_FATAL, "Dumped core : core.%d", p));
+ sendto_ops("Dumped core : core.%d", p);
+ vdebug(DEBUG_FATAL, pattern, vl);
+ vsendto_ops(pattern, vl);
+#ifdef __cplusplus
+ s_die(0);
+#else
+ s_die();
+#endif
+
+ va_end(vl);
+}
+#endif
+
+int check_if_ipmask(const char *mask)
+{
+ int has_digit = 0;
+ register const char *p;
+
+ for (p = mask; *p; ++p)
+ if (*p != '*' && *p != '?' && *p != '.')
+ {
+ if (!isDigit(*p))
+ return 0;
+ has_digit = -1;
+ }
+
+ return has_digit;
+}
+
+/* Moved from logf() in whocmds.c to here. Modified a
+ * bit and used for most logging now.
+ * -Ghostwolf 12-Jul-99
+ */
+
+extern void write_log(const char *filename, const char *pattern, ...)
+{
+ FBFILE *logfile;
+ va_list vl;
+ static char logbuf[1024];
+
+ logfile = fbopen(filename, "a");
+
+ if (logfile)
+ {
+ va_start(vl, pattern);
+ vsprintf_irc(logbuf, pattern, vl);
+ va_end(vl);
+
+ fbputs(logbuf, logfile);
+ fbclose(logfile);
+ }
+}
--- /dev/null
+/*
+ * Userload module by Michael L. VanLoon (mlv) <michaelv@iastate.edu>
+ * Written 2/93. Originally grafted into irc2.7.2g 4/93.
+ *
+ * Rewritten 9/97 by Carlo Wood (Run) <carlo@runaway.xs4all.nl>
+ * because previous version used ridiculous amounts of memory
+ * (stored all loads of the passed three days ~ 8 megs).
+ *
+ * IRC - Internet Relay Chat, ircd/userload.c
+ * Copyright (C) 1990 University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <stdio.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include "h.h"
+#include "struct.h"
+#include "send.h"
+#include "s_misc.h"
+#include "userload.h"
+#include "ircd.h"
+#include "numnicks.h"
+#include "s_serv.h"
+#include "querycmds.h"
+
+RCSTAG_CC("$Id$");
+
+struct current_load_st current_load; /* The current load */
+
+static struct current_load_st cspm_sum; /* Number of connections times number
+ of seconds per minute. */
+static struct current_load_st csph_sum; /* Number of connections times number
+ of seconds per hour. */
+static struct current_load_st cspm[60]; /* Last 60 minutes */
+static struct current_load_st csph[72]; /* Last 72 hours */
+
+static int m_index, h_index; /* Array indexes */
+
+/*
+ * update_load
+ *
+ * A new connection was added or removed.
+ */
+void update_load(void)
+{
+ static struct tm tm_now; /* Current time. */
+ static time_t last_sec; /* Seconds of last time that
+ update_load() called. */
+ static time_t last_min;
+ static time_t last; /* Last time that update_load() was called. */
+ static struct current_load_st last_load; /* The load last time that
+ update_load() was called. */
+ static int initialized; /* Boolean, set when initialized. */
+ register int diff_time; /* Temp. variable used to hold time intervals
+ in seconds, minutes or hours. */
+
+ /* Update `current_load' */
+ current_load.client_count = nrof.local_clients;
+ current_load.conn_count = nrof.local_clients + nrof.local_servers;
+
+ /* Nothing needed when still in the same second */
+ if (!(diff_time = now - last))
+ {
+ last_load = current_load; /* Update last_load to be the load last
+ time that update_load() was called. */
+ return;
+ }
+
+ /* If we get here we entered a new second */
+
+ /*
+ * Make sure we keep the accurate time in 'tm_now'
+ */
+ if ((tm_now.tm_sec += diff_time) > 59)
+ {
+ /* This is done once every minute */
+ diff_time = tm_now.tm_sec / 60;
+ tm_now.tm_sec -= 60 * diff_time;
+ if ((tm_now.tm_min += diff_time) > 59)
+ {
+ /* This is done once every hour */
+ diff_time = tm_now.tm_min / 60;
+ tm_now.tm_min -= 60 * diff_time;
+ if ((tm_now.tm_hour += diff_time) > 23)
+ {
+ tm_now = *localtime(&now); /* Only called once a day */
+ if (!initialized)
+ {
+ initialized = 1;
+ last_sec = 60;
+ last_min = tm_now.tm_min;
+ }
+ }
+ }
+
+ /* If we get here we entered a new minute */
+
+ /* Finish the calculation of cspm of the last minute first: */
+ diff_time = 60 - last_sec;
+ cspm_sum.conn_count += last_load.conn_count * diff_time;
+ cspm_sum.client_count += last_load.client_count * diff_time;
+ cspm_sum.local_count += last_load.local_count * diff_time;
+
+ /* Add the completed minute to the Connections*Seconds/Hour sum */
+ csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count;
+ csph_sum.client_count += cspm_sum.client_count - cspm[m_index].client_count;
+ csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count;
+
+ /* Store the completed minute in an array */
+ cspm[m_index] = cspm_sum;
+
+ /* How long did last_cspm last ? */
+ diff_time = tm_now.tm_min - last_min;
+ last_min = tm_now.tm_min;
+
+ if (diff_time < 0)
+ diff_time += 60; /* update_load() must be called at
+ _least_ once an hour */
+
+ if (diff_time > 1) /* Did more then one minute pass ? */
+ {
+ /* Calculate the constant load during those extra minutes */
+ cspm_sum.conn_count = last_load.conn_count * 60;
+ cspm_sum.client_count = last_load.client_count * 60;
+ cspm_sum.local_count = last_load.local_count * 60;
+ }
+
+ for (;;)
+ {
+ /* Increase minute index */
+ if (++m_index == 60)
+ {
+ m_index = 0;
+ /* Keep a list of the last 72 hours */
+ csph[h_index] = csph_sum;
+ if (++h_index == 72)
+ h_index = 0;
+ }
+
+ if (--diff_time <= 0) /* '<' to prevent endless loop if update_load()
+ was not called once an hour :/ */
+ break;
+
+ /* Add extra minutes to the Connections*Seconds/Hour sum */
+ csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count;
+ csph_sum.client_count +=
+ cspm_sum.client_count - cspm[m_index].client_count;
+ csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count;
+
+ /* Store extra minutes in the array */
+ cspm[m_index] = cspm_sum;
+ }
+
+ /* Now start the calculation of the new minute: */
+ last_sec = tm_now.tm_sec;
+ cspm_sum.conn_count = last_load.conn_count * last_sec;
+ cspm_sum.client_count = last_load.client_count * last_sec;
+ cspm_sum.local_count = last_load.local_count * last_sec;
+ }
+ else
+ {
+ /* A new second, but the same minute as last time */
+ /* How long did last_load last ? */
+ diff_time = tm_now.tm_sec - last_sec;
+ last_sec = tm_now.tm_sec;
+ if (diff_time == 1) /* Just one second ? */
+ {
+ cspm_sum.conn_count += last_load.conn_count;
+ cspm_sum.client_count += last_load.client_count;
+ cspm_sum.local_count += last_load.local_count;
+ }
+ else
+ {
+ /* More then one second */
+ /* At most 3 integer multiplication per second */
+ cspm_sum.conn_count += last_load.conn_count * diff_time;
+ cspm_sum.client_count += last_load.client_count * diff_time;
+ cspm_sum.local_count += last_load.local_count * diff_time;
+ }
+ }
+ last_load = current_load; /* Update last_load to be the load last
+ time that update_load() was called. */
+ last = now;
+}
+
+void calc_load(aClient *sptr)
+{
+ /* *INDENT-OFF* */
+ static const char *header =
+ /* ----.- ----.- ---- ---- ---- ------------ */
+ "Minute Hour Day Yest. YYest. Userload for:";
+ /* *INDENT-ON* */
+ static const char *what[3] = {
+ DOMAINNAME " clients",
+ "total clients",
+ "total connections"
+ };
+ int i, j, times[5][3]; /* [min,hour,day,Yest,YYest]
+ [local,client,conn] */
+ int last_m_index = m_index, last_h_index = h_index;
+
+ update_load(); /* We want stats accurate as of *now* */
+
+ if (--last_m_index < 0)
+ last_m_index = 59;
+ times[0][0] = (cspm[last_m_index].local_count + 3) / 6;
+ times[0][1] = (cspm[last_m_index].client_count + 3) / 6;
+ times[0][2] = (cspm[last_m_index].conn_count + 3) / 6;
+
+ times[1][0] = (csph_sum.local_count + 180) / 360;
+ times[1][1] = (csph_sum.client_count + 180) / 360;
+ times[1][2] = (csph_sum.conn_count + 180) / 360;
+
+ for (i = 2; i < 5; ++i)
+ {
+ times[i][0] = 43200;
+ times[i][1] = 43200;
+ times[i][2] = 43200;
+ for (j = 0; j < 24; ++j)
+ {
+ if (--last_h_index < 0)
+ last_h_index = 71;
+ times[i][0] += csph[last_h_index].local_count;
+ times[i][1] += csph[last_h_index].client_count;
+ times[i][2] += csph[last_h_index].conn_count;
+ }
+ times[i][0] /= 86400;
+ times[i][1] /= 86400;
+ times[i][2] /= 86400;
+ }
+
+ if (MyUser(sptr) || Protocol(sptr->from) < 10)
+ {
+ sendto_one(sptr, ":%s NOTICE %s :%s", me.name, sptr->name, header);
+ for (i = 0; i < 3; ++i)
+ sendto_one(sptr,
+ ":%s NOTICE %s :%4d.%1d %4d.%1d %4d %4d %4d %s",
+ me.name, sptr->name,
+ times[0][i] / 10, times[0][i] % 10,
+ times[1][i] / 10, times[1][i] % 10,
+ times[2][i], times[3][i], times[4][i], what[i]);
+ }
+ else
+ {
+ sendto_one(sptr, "%s NOTICE %s%s :%s", NumServ(&me), NumNick(sptr), header);
+ for (i = 0; i < 3; ++i)
+ sendto_one(sptr,
+ "%s NOTICE %s%s :%4d.%1d %4d.%1d %4d %4d %4d %s",
+ NumServ(&me), NumNick(sptr),
+ times[0][i] / 10, times[0][i] % 10,
+ times[1][i] / 10, times[1][i] % 10,
+ times[2][i], times[3][i], times[4][i], what[i]);
+ }
+}
+
+void initload(void)
+{
+ memset(¤t_load, 0, sizeof(current_load));
+ update_load(); /* Initialize the load list */
+}
--- /dev/null
+echo "Extracting ircd/version.c ..."
+
+if test -r version.c
+then
+ generation=`sed -n 's/^char \*generation = \"\(.*\)\";/\1/p' < version.c`
+ if test ! "$generation" ; then generation=0; fi
+else
+ generation=0
+fi
+
+generation=`expr $generation + 1`
+
+sum=sum
+if $sum s_serv.c 1> /dev/null 2>&1; then
+:
+else
+ sum=cksum
+fi
+sumsserv=`$sum s_serv.c 2> /dev/null`;
+sumsuser=`$sum s_user.c 2> /dev/null`;
+sumchan=`$sum channel.c 2> /dev/null`;
+sumsbsd=`$sum s_bsd.c 2> /dev/null`;
+sumhash=`$sum hash.c 2> /dev/null`;
+sumsmisc=`$sum s_misc.c 2> /dev/null`;
+sumircd=`$sum ircd.c 2> /dev/null`;
+
+creation=`date | \
+awk '{if (NF == 6) \
+ { print $1 " " $2 " " $3 " " $6 " at " $4 " " $5 } \
+else \
+ { print $1 " " $2 " " $3 " " $7 " at " $4 " " $5 " " $6 }}'`
+
+cvsversion=`cat ../.patches | \
+ awk -F. '{ \
+ if ($(NF)~/\+$/) \
+ printf(".0"); \
+ else \
+ printf(".%d.(%s)", NF - 3, $(NF)); \
+ }'`
+
+/bin/cat >version.c <<!SUB!THIS!
+/*
+ * IRC - Internet Relay Chat, ircd/version.c
+ * Copyright (C) 1990 Chelsea Ashley Dyerman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file is generated by version.c.SH. Any changes made will go away.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "patchlevel.h"
+#include "version.h"
+
+const char *generation = "$generation";
+const char *creation = "$creation";
+const char *version = BASE_VERSION PATCH1 "$cvsversion" PATCH2 PATCH3 PATCH4 PATCH5 PATCH6\
+ PATCH7 PATCH8 PATCH9 PATCH10 PATCH11 PATCH12 PATCH13 PATCH14 PATCH15\
+ PATCH16 PATCH17 PATCH18 PATCH19 PATCH20 PATCH21 PATCH22 PATCH23 PATCH24\
+ PATCH25 PATCH26 PATCH27 PATCH28 PATCH29 PATCH30 PATCH31 PATCH32;
+
+const char *infotext[] = {
+ "IRC --",
+ "Based on the original code written by Jarkko Oikarinen, version 2.6:",
+ "Copyright 1988, 1989, 1990, 1991 University of Oulu, Computing Center",
+ "",
+ "Wiz Jarkko Oikarinen <jto@tolsun.oulu.fi>",
+ "",
+ "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, or",
+ "(at your option) any later version.",
+ "",
+ "The UnderNet code is based upon version 2.8.10.",
+ "The main developers of version 2.7 and 2.8 are:",
+ "",
+ "Avalon Darren Reed <avalon@coombs.anu.edu.au>",
+ "msa Markku Savela <msa@tel4.tel.vtt.fi>",
+ "Wumpus Greg Lindahl <lindahl@pbm.com>",
+ "",
+ "The main developer of version u2.9 and u2.10 is:",
+ "",
+ "Run Carlo Wood <carlo@runaway.xs4all.nl>",
+ "",
+ "Thanks goes to all other people who contributed to any version.",
+ "A full listing of all coders can be found in doc/Authors in the",
+ "source. Contributers to version u2.10 can be found on",
+ "http://coder-com.undernet.org/posters.html",
+ "Thanks also to those who provided me with accounts; the kind sys",
+ "admins who let me and others continue to develop IRC.",
+ "",
+ "[$sumsserv] [$sumchan] [$sumsbsd] [$sumsuser]",
+ "[$sumhash] [$sumsmisc] [$sumircd]",
+ 0,
+};
+!SUB!THIS!
--- /dev/null
+
+/*
+ * IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.c)
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "match.h"
+#include "hash.h"
+#include "s_bsd.h"
+#include "whowas.h"
+#include "list.h"
+#include "s_err.h"
+#include "parse.h"
+#include "ircd.h"
+#include "s_user.h"
+#include "support.h"
+#include "s_user.h"
+#include "channel.h"
+#include "random.h"
+#include "version.h"
+#include "msg.h"
+#include "userload.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * m_who()
+ * m_who with support routines rewritten by Nemesi, August 1997
+ * - Alghoritm have been flattened (no more recursive)
+ * - Several bug fixes
+ * - Strong performance improvement
+ * - Added possibility to have specific fields in the output
+ * See readme.who for further details.
+ */
+
+/* Macros used only in here by m_who and its support functions */
+
+#define WHOSELECT_OPER 1
+#define WHOSELECT_EXTRA 2
+
+#define WHO_FIELD_QTY 1
+#define WHO_FIELD_CHA 2
+#define WHO_FIELD_UID 4
+#define WHO_FIELD_NIP 8
+#define WHO_FIELD_HOS 16
+#define WHO_FIELD_SER 32
+#define WHO_FIELD_NIC 64
+#define WHO_FIELD_FLA 128
+#define WHO_FIELD_DIS 256
+#define WHO_FIELD_REN 512
+
+#define WHO_FIELD_DEF ( WHO_FIELD_NIC | WHO_FIELD_UID | WHO_FIELD_HOS | WHO_FIELD_SER )
+
+#define IS_VISIBLE_USER(s,ac) ((s==ac) || (!IsInvisible(ac)))
+
+#if defined(SHOW_INVISIBLE_LUSERS) || defined(SHOW_ALL_INVISIBLE_USERS)
+#define SEE_LUSER(s, ac, b) (IS_VISIBLE_USER(s, ac) || ((b & WHOSELECT_EXTRA) && MyConnect(ac) && IsAnOper(s)))
+#else
+#define SEE_LUSER(s, ac, b) (IS_VISIBLE_USER(s, ac))
+#endif
+
+#ifdef SHOW_ALL_INVISIBLE_USERS
+#define SEE_USER(s, ac, b) (SEE_LUSER(s, ac, b) || ((b & WHOSELECT_EXTRA) && IsOper(s)))
+#else
+#define SEE_USER(s, ac, b) (SEE_LUSER(s, ac, b))
+#endif
+
+#ifdef UNLIMIT_OPER_QUERY
+#define SHOW_MORE(sptr, counter) (IsAnOper(sptr) || (!(counter-- < 0)) )
+#else
+#define SHOW_MORE(sptr, counter) (!(counter-- < 0))
+#endif
+
+#ifdef OPERS_SEE_IN_SECRET_CHANNELS
+#ifdef LOCOP_SEE_IN_SECRET_CHANNELS
+#define SEE_CHANNEL(s, chptr, b) (!SecretChannel(chptr) || ((b & WHOSELECT_EXTRA) && IsAnOper(s)))
+#else
+#define SEE_CHANNEL(s, chptr, b) (!SecretChannel(chptr) || ((b & WHOSELECT_EXTRA) && IsOper(s)))
+#endif
+#else
+#define SEE_CHANNEL(s, chptr, b) (!SecretChannel(chptr))
+#endif
+
+
+/*
+ * A little spin-marking utility to tell us wich clients we have already
+ * processed and wich not
+ */
+static int who_marker = 0;
+static void move_marker(void)
+{
+ if (!++who_marker)
+ {
+ aClient *cptr = client;
+ while (cptr)
+ {
+ cptr->marker = 0;
+ cptr = cptr->next;
+ }
+ who_marker++;
+ }
+}
+#define CheckMark(x, y) ((x == y) ? 0 : (x = y))
+#define Process(cptr) CheckMark(cptr->marker, who_marker)
+
+/*
+ * The function that actually prints out the WHO reply for a client found
+ */
+static void do_who(aClient *sptr, aClient *acptr, aChannel *repchan,
+ int fields, char *qrt)
+{
+ Reg1 char *p1;
+ Reg2 aChannel *chptr;
+
+ static char buf1[512];
+ /* NOTE: with current fields list and sizes this _cannot_ overrun,
+ and also the message finally sent shouldn't ever be truncated */
+
+ p1 = buf1;
+ chptr = repchan;
+ buf1[1] = '\0';
+
+ /* If we don't have a channel and we need one... try to find it,
+ unless the listing is for a channel service, we already know
+ that there are no common channels, thus use PubChannel and not
+ SeeChannel */
+ if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) &&
+ !IsChannelService(acptr))
+ {
+ Reg3 Link *lp;
+ for (lp = acptr->user->channel; lp && !chptr; lp = lp->next)
+ if (PubChannel(lp->value.chptr) &&
+ (acptr == sptr || !is_zombie(acptr, chptr)))
+ chptr = lp->value.chptr;
+ }
+
+ /* Place the fields one by one in the buffer and send it
+ note that fields == NULL means "default query" */
+
+ if (fields & WHO_FIELD_QTY) /* Query type */
+ {
+ *(p1++) = ' ';
+ if (BadPtr(qrt))
+ *(p1++) = '0';
+ else
+ while ((*qrt) && (*(p1++) = *(qrt++)));
+ }
+
+ if (!fields || (fields & WHO_FIELD_CHA))
+ {
+ Reg3 char *p2;
+ *(p1++) = ' ';
+ if ((p2 = (chptr ? chptr->chname : NULL)))
+ while ((*p2) && (*(p1++) = *(p2++)));
+ else
+ *(p1++) = '*';
+ }
+
+ if (!fields || (fields & WHO_FIELD_UID))
+ {
+ Reg3 char *p2 = acptr->user->username;
+ *(p1++) = ' ';
+ while ((*p2) && (*(p1++) = *(p2++)));
+ }
+
+ if (fields & WHO_FIELD_NIP)
+ {
+ Reg3 char *p2;
+ p2 = inetntoa(acptr->ip);
+ *(p1++) = ' ';
+ while ((*p2) && (*(p1++) = *(p2++)));
+ }
+
+ if (!fields || (fields & WHO_FIELD_HOS))
+ {
+ Reg3 char *p2 = acptr->user->host;
+ *(p1++) = ' ';
+ while ((*p2) && (*(p1++) = *(p2++)));
+ }
+
+ if (!fields || (fields & WHO_FIELD_SER))
+ {
+ Reg3 char *p2 = acptr->user->server->name;
+ *(p1++) = ' ';
+ while ((*p2) && (*(p1++) = *(p2++)));
+ }
+
+ if (!fields || (fields & WHO_FIELD_NIC))
+ {
+ Reg3 char *p2 = acptr->name;
+ *(p1++) = ' ';
+ while ((*p2) && (*(p1++) = *(p2++)));
+ }
+
+ if (!fields || (fields & WHO_FIELD_FLA))
+ {
+ *(p1++) = ' ';
+ if (acptr->user->away)
+ *(p1++) = 'G';
+ else
+ *(p1++) = 'H';
+ if (IsAnOper(acptr))
+ *(p1++) = '*';
+ if (chptr && is_chan_op(acptr, chptr))
+ *(p1++) = '@';
+ else if (chptr && has_voice(acptr, chptr))
+ *(p1++) = '+';
+ else if (chptr && is_zombie(acptr, chptr))
+ *(p1++) = '!';
+ if (IsDeaf(acptr))
+ *(p1++) = 'd';
+ if (IsAnOper(sptr))
+ {
+ if (IsInvisible(acptr))
+ *(p1++) = 'i';
+ if (SendWallops(acptr))
+ *(p1++) = 'w';
+ if (SendDebug(acptr))
+ *(p1++) = 'g';
+ }
+ }
+
+ if (!fields || (fields & WHO_FIELD_DIS))
+ {
+ *p1++ = ' ';
+ if (!fields)
+ *p1++ = ':'; /* Place colon here for default reply */
+ p1 = sprintf_irc(p1, "%d", acptr->hopcount);
+ }
+
+ if (!fields || (fields & WHO_FIELD_REN))
+ {
+ Reg3 char *p2 = acptr->info;
+ *p1++ = ' ';
+ if (fields)
+ *p1++ = ':'; /* Place colon here for special reply */
+ while ((*p2) && (*(p1++) = *(p2++)));
+ }
+
+ /* The first char will always be an useless blank and we
+ need to terminate buf1 */
+ *p1 = '\0';
+ p1 = buf1;
+ sendto_one(sptr, rpl_str(fields ? RPL_WHOSPCRPL : RPL_WHOREPLY),
+ me.name, sptr->name, ++p1);
+}
+
+/*
+ * m_who
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nickname mask list
+ * parv[2] = additional selection flag, only 'o' for now.
+ * and %flags to specify what fields to output
+ * plus a ,querytype if the t flag is specified
+ * so the final thing will be like o%tnchu,777
+ * parv[3] = _optional_ parameter that overrides parv[1]
+ * This can be used as "/quote who foo % :The Black Hacker
+ * to find me, parv[3] _can_ contain spaces !.
+ */
+
+int m_who(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+ Reg1 char *mask; /* The mask we are looking for */
+ Reg2 char ch; /* Scratch char register */
+ Reg3 Link *lp, *lp2;
+ Reg4 aChannel *chptr; /* Channel to show */
+ Reg5 aClient *acptr; /* Client to show */
+
+ int bitsel; /* Mask of selectors to apply */
+ int matchsel; /* Wich fields the match should apply on */
+ int counter; /* Query size counter,
+ initially used to count fields */
+ int commas; /* Does our mask contain any comma ?
+ If so is a list.. */
+ int fields; /* Mask of fields to show */
+ int isthere = 0; /* When this set the user is member of chptr */
+ char *nick; /* Single element extracted from
+ the mask list */
+ char *p; /* Scratch char pointer */
+ char *qrt; /* Pointer to the query type */
+ static char mymask[512]; /* To save the mask before corrupting it */
+
+ /* Let's find where is our mask, and if actually contains something */
+ mask = ((parc > 1) ? parv[1] : NULL);
+ if (parc > 3 && parv[3])
+ mask = parv[3];
+ if (mask && ((mask[0] == '\0') ||
+ (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*')))))
+ mask = NULL;
+
+ /* Evaluate the flags now, we consider the second parameter
+ as "matchFlags%fieldsToInclude,querytype" */
+ bitsel = fields = counter = matchsel = 0;
+ qrt = NULL;
+ if (parc > 2 && parv[2] && *parv[2])
+ {
+ p = parv[2];
+ while (((ch = *(p++))) && (ch != '%') && (ch != ','))
+ switch (ch)
+ {
+ case 'o':
+ case 'O':
+ bitsel |= WHOSELECT_OPER;
+ continue;
+ case 'x':
+ case 'X':
+ bitsel |= WHOSELECT_EXTRA;
+#ifdef WPATH
+ if (IsAnOper(sptr))
+ write_log(WPATH, "# " TIME_T_FMT " %s!%s@%s WHO %s %s\n",
+ now, sptr->name, sptr->user->username, sptr->user->host,
+ (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]);
+#endif /* WPATH */
+ continue;
+ case 'n':
+ case 'N':
+ matchsel |= WHO_FIELD_NIC;
+ continue;
+ case 'u':
+ case 'U':
+ matchsel |= WHO_FIELD_UID;
+ continue;
+ case 'h':
+ case 'H':
+ matchsel |= WHO_FIELD_HOS;
+ continue;
+ case 'i':
+ case 'I':
+ matchsel |= WHO_FIELD_NIP;
+ continue;
+ case 's':
+ case 'S':
+ matchsel |= WHO_FIELD_SER;
+ continue;
+ case 'r':
+ case 'R':
+ matchsel |= WHO_FIELD_REN;
+ continue;
+ }
+ if (ch == '%')
+ while ((ch = *p++) && (ch != ','))
+ {
+ counter++;
+ switch (ch)
+ {
+ case 'c':
+ case 'C':
+ fields |= WHO_FIELD_CHA;
+ break;
+ case 'd':
+ case 'D':
+ fields |= WHO_FIELD_DIS;
+ break;
+ case 'f':
+ case 'F':
+ fields |= WHO_FIELD_FLA;
+ break;
+ case 'h':
+ case 'H':
+ fields |= WHO_FIELD_HOS;
+ break;
+ case 'i':
+ case 'I':
+ fields |= WHO_FIELD_NIP;
+ break;
+ case 'n':
+ case 'N':
+ fields |= WHO_FIELD_NIC;
+ break;
+ case 'r':
+ case 'R':
+ fields |= WHO_FIELD_REN;
+ break;
+ case 's':
+ case 'S':
+ fields |= WHO_FIELD_SER;
+ break;
+ case 't':
+ case 'T':
+ fields |= WHO_FIELD_QTY;
+ break;
+ case 'u':
+ case 'U':
+ fields |= WHO_FIELD_UID;
+ break;
+ default:
+ break;
+ }
+ };
+ if (ch)
+ qrt = p;
+ }
+
+ if (!matchsel)
+ matchsel = WHO_FIELD_DEF;
+ if (!fields)
+ counter = 7;
+
+ if (qrt && (fields & WHO_FIELD_QTY))
+ {
+ p = qrt;
+ if (!((*p > '9') || (*p < '0')))
+ p++;
+ if (!((*p > '9') || (*p < '0')))
+ p++;
+ if (!((*p > '9') || (*p < '0')))
+ p++;
+ *p = '\0';
+ }
+ else
+ qrt = NULL;
+
+ /* I'd love to add also a check on the number of matches fields per time */
+ counter = (2048 / (counter + 4));
+ if (mask && (strlen(mask) > 510))
+ mask[510] = '\0';
+ move_marker();
+ commas = (mask && strchr(mask, ','));
+
+ /* First treat mask as a list of plain nicks/channels */
+ if (mask)
+ {
+ strcpy(mymask, mask);
+ for (p = NULL, nick = strtoken(&p, mymask, ","); nick;
+ nick = strtoken(&p, NULL, ","))
+ {
+ if (IsChannelName(nick) && (chptr = FindChannel(nick)))
+ {
+ isthere = (IsMember(sptr, chptr) != NULL);
+ if (isthere || SEE_CHANNEL(sptr, chptr, bitsel))
+ {
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ acptr = lp->value.cptr;
+ if ((bitsel & WHOSELECT_OPER) && !(IsAnOper(acptr)))
+ continue;
+ if ((acptr != sptr) && (lp->flags & CHFL_ZOMBIE))
+ continue;
+ if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
+ continue;
+ if (!Process(acptr)) /* This can't be moved before other checks */
+ continue;
+ if (!(isthere || (SHOW_MORE(sptr, counter))))
+ break;
+ do_who(sptr, acptr, chptr, fields, qrt);
+ }
+ }
+ }
+ else
+ {
+ if ((acptr = FindUser(nick)) &&
+ ((!(bitsel & WHOSELECT_OPER)) || IsAnOper(acptr)) &&
+ Process(acptr) && SHOW_MORE(sptr, counter))
+ {
+ do_who(sptr, acptr, NULL, fields, qrt);
+ }
+ }
+ }
+ }
+
+ /* If we didn't have any comma in the mask treat it as a
+ real mask and try to match all relevant fields */
+ if (!(commas || (counter < 1)))
+ {
+ int minlen, cset;
+ static struct in_mask imask;
+ if (mask)
+ {
+ matchcomp(mymask, &minlen, &cset, mask);
+ if (matchcompIP(&imask, mask))
+ matchsel &= ~WHO_FIELD_NIP;
+ if ((minlen > NICKLEN) || !(cset & NTL_IRCNK))
+ matchsel &= ~WHO_FIELD_NIC;
+ if ((matchsel & WHO_FIELD_SER) &&
+ ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN))
+ || (!markMatchexServer(mymask, minlen))))
+ matchsel &= ~WHO_FIELD_SER;
+ if ((minlen > USERLEN) || !(cset & NTL_IRCUI))
+ matchsel &= ~WHO_FIELD_UID;
+ if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN))
+ matchsel &= ~WHO_FIELD_HOS;
+ }
+
+ /* First of all loop through the clients in common channels */
+ if ((!(counter < 1)) && matchsel)
+ for (lp = sptr->user->channel; lp; lp = lp->next)
+ for (chptr = lp->value.chptr, lp2 = chptr->members; lp2;
+ lp2 = lp2->next)
+ {
+ acptr = lp2->value.cptr;
+ if (!(IsUser(acptr) && Process(acptr)))
+ continue; /* Now Process() is at the beginning, if we fail
+ we'll never have to show this acptr in this query */
+ if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
+ continue;
+ if ((mask) &&
+ ((!(matchsel & WHO_FIELD_NIC))
+ || matchexec(acptr->name, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_UID))
+ || matchexec(acptr->user->username, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_SER))
+ || (!(acptr->user->server->flags & FLAGS_MAP)))
+ && ((!(matchsel & WHO_FIELD_HOS))
+ || matchexec(acptr->user->host, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_REN))
+ || matchexec(acptr->info, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_NIP))
+ || ((((acptr->ip.s_addr & imask.mask.s_addr) !=
+ imask.bits.s_addr)) || (imask.fall
+ && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
+ continue;
+ if (!SHOW_MORE(sptr, counter))
+ break;
+ do_who(sptr, acptr, chptr, fields, qrt);
+ }
+
+ /* Loop through all clients :-\, if we still have something to match to
+ and we can show more clients */
+ if ((!(counter < 1)) && matchsel)
+ for (acptr = me.prev; acptr; acptr = acptr->prev)
+ {
+ if (!(IsUser(acptr) && Process(acptr)))
+ continue;
+ if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
+ continue;
+ if (!(SEE_USER(sptr, acptr, bitsel)))
+ continue;
+ if ((mask) &&
+ ((!(matchsel & WHO_FIELD_NIC))
+ || matchexec(acptr->name, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_UID))
+ || matchexec(acptr->user->username, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_SER))
+ || (!(acptr->user->server->flags & FLAGS_MAP)))
+ && ((!(matchsel & WHO_FIELD_HOS))
+ || matchexec(acptr->user->host, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_REN))
+ || matchexec(acptr->info, mymask, minlen))
+ && ((!(matchsel & WHO_FIELD_NIP))
+ || ((((acptr->ip.s_addr & imask.mask.s_addr) != imask.bits.s_addr))
+ || (imask.fall
+ && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
+ continue;
+ if (!SHOW_MORE(sptr, counter))
+ break;
+ do_who(sptr, acptr, NULL, fields, qrt);
+ }
+ }
+
+ /* Make a clean mask suitable to be sent in the "end of" */
+ if (mask && (p = strchr(mask, ' ')))
+ *p = '\0';
+ sendto_one(sptr, rpl_str(RPL_ENDOFWHO),
+ me.name, parv[0], BadPtr(mask) ? "*" : mask);
+
+ /* Notify the user if we decided that his query was too long */
+ if (counter < 0)
+ sendto_one(sptr, err_str(ERR_QUERYTOOLONG), me.name, parv[0], "WHO");
+
+ return 0;
+}
+
+#define MAX_WHOIS_LINES 50
+
+/*
+ * m_whois
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nickname masklist
+ *
+ * or
+ *
+ * parv[1] = target server
+ * parv[2] = nickname masklist
+ */
+int m_whois(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ Reg2 Link *lp;
+ Reg3 anUser *user;
+ aClient *acptr, *a2cptr;
+ aChannel *chptr;
+ char *nick, *tmp, *name;
+ char *p = NULL;
+ int found, len, mlen, total;
+ static char buf[512];
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+ return 0;
+ }
+
+ if (parc > 2)
+ {
+ aClient *acptr;
+ /* For convenience: Accept a nickname as first parameter, by replacing
+ it with the correct servername - as is needed by hunt_server() */
+ if (MyUser(sptr) && (acptr = FindUser(parv[1])))
+ parv[1] = acptr->user->server->name;
+ if (hunt_server(0, cptr, sptr, ":%s WHOIS %s :%s", 1, parc, parv) !=
+ HUNTED_ISME)
+ return 0;
+ parv[1] = parv[2];
+ }
+
+ total = 0;
+ for (tmp = parv[1]; (nick = strtoken(&p, tmp, ",")); tmp = NULL)
+ {
+ int invis, showperson, member, wilds;
+
+ found = 0;
+ collapse(nick);
+ wilds = (strchr(nick, '?') || strchr(nick, '*'));
+ /* Do a hash lookup if the nick does not contain wilds */
+ if (wilds)
+ {
+ /*
+ * We're no longer allowing remote users to generate requests with wildcards.
+ */
+ if (!MyConnect(sptr))
+ continue;
+ for (acptr = client; (acptr = next_client(acptr, nick));
+ acptr = acptr->next)
+ {
+ if (!IsRegistered(acptr) || IsServer(acptr) || IsPing(acptr))
+ continue;
+ /*
+ * I'm always last :-) and acptr->next == NULL!!
+ */
+ if (IsMe(acptr))
+ break;
+ /*
+ * 'Rules' established for sending a WHOIS reply:
+ *
+ * - if wildcards are being used dont send a reply if
+ * the querier isnt any common channels and the
+ * client in question is invisible and wildcards are
+ * in use (allow exact matches only);
+ *
+ * - only send replies about common or public channels
+ * the target user(s) are on;
+ */
+ user = acptr->user;
+ name = (!*acptr->name) ? "?" : acptr->name;
+
+ invis = acptr != sptr && IsInvisible(acptr);
+ member = (user && user->channel) ? 1 : 0;
+ showperson = (wilds && !invis && !member) || !wilds;
+ if (user)
+ for (lp = user->channel; lp; lp = lp->next)
+ {
+ chptr = lp->value.chptr;
+ member = IsMember(sptr, chptr) ? 1 : 0;
+ if (invis && !member)
+ continue;
+ if (is_zombie(acptr, chptr))
+ continue;
+ if (member || (!invis && PubChannel(chptr)))
+ {
+ showperson = 1;
+ break;
+ }
+ if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
+ showperson = 1;
+ }
+ if (!showperson)
+ continue;
+
+ if (user)
+ {
+ a2cptr = user->server;
+ sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
+ parv[0], name, user->username, user->host, acptr->info);
+ }
+ else
+ {
+ a2cptr = &me;
+ sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
+ parv[0], name, "<unknown>", "<unknown>", "<unknown>");
+ }
+
+ found = 1;
+
+ exact_match:
+ if (user && !IsChannelService(acptr))
+ {
+ mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
+ for (len = 0, *buf = '\0', lp = user->channel; lp; lp = lp->next)
+ {
+ chptr = lp->value.chptr;
+ if (ShowChannel(sptr, chptr) &&
+ (acptr == sptr || !is_zombie(acptr, chptr)))
+ {
+ if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
+ {
+ sendto_one(sptr, ":%s %d %s %s :%s",
+ me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
+ *buf = '\0';
+ len = 0;
+ }
+ if (IsDeaf(acptr))
+ *(buf + len++) = '-';
+ if (is_chan_op(acptr, chptr))
+ *(buf + len++) = '@';
+ else if (has_voice(acptr, chptr))
+ *(buf + len++) = '+';
+ else if (is_zombie(acptr, chptr))
+ *(buf + len++) = '!';
+ if (len)
+ *(buf + len) = '\0';
+ strcpy(buf + len, chptr->chname);
+ len += strlen(chptr->chname);
+ strcat(buf + len, " ");
+ len++;
+ }
+ }
+ if (buf[0] != '\0')
+ sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS),
+ me.name, parv[0], name, buf);
+ }
+
+ sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name,
+ parv[0], name, a2cptr->name, a2cptr->info);
+
+ if (user)
+ {
+ if (user->away)
+ sendto_one(sptr, rpl_str(RPL_AWAY), me.name,
+ parv[0], name, user->away);
+
+ if (IsAnOper(acptr))
+ sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR),
+ me.name, parv[0], name);
+
+ if (MyConnect(acptr))
+ sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name,
+ parv[0], name, now - user->last, acptr->firsttime);
+ }
+ if (found == 2 || total++ >= MAX_WHOIS_LINES)
+ break;
+ }
+ }
+ else
+ {
+ /* No wildcards */
+ if ((acptr = FindUser(nick)))
+ {
+ found = 2; /* Make sure we exit the loop after passing it once */
+ user = acptr->user;
+ name = (!*acptr->name) ? "?" : acptr->name;
+ a2cptr = user->server;
+ sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
+ parv[0], name, user->username, user->host, acptr->info);
+ goto exact_match;
+ }
+ }
+ if (!found)
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
+ if (p)
+ p[-1] = ',';
+ if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
+ break;
+ }
+ sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
+
+ return 0;
+}
--- /dev/null
+
+/*
+ * IRC - Internet Relay Chat, ircd/whowas.c
+ * Copyright (C) 1990 Markku Savela
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * --- avalon --- 6th April 1992
+ * rewritten to scrap linked lists and use a table of structures which
+ * is referenced like a circular loop. Should be faster and more efficient.
+ */
+
+/*
+ * --- comstud --- 25th March 1997
+ * Everything rewritten from scratch. Avalon's code was bad. My version
+ * is faster and more efficient. No more hangs on /squits and you can
+ * safely raise NICKNAMEHISTORYLENGTH to a higher value without hurting
+ * performance.
+ */
+
+/*
+ * --- comstud --- 5th August 1997
+ * Fixed for Undernet..
+ */
+
+/*
+ * --- Run --- 27th August 1997
+ * Speeded up the code, added comments.
+ */
+
+#include "sys.h"
+#include <stdlib.h>
+#include "common.h"
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_misc.h"
+#include "s_err.h"
+#include "whowas.h"
+#include "ircd.h"
+#include "list.h"
+#include "s_user.h"
+#include "support.h"
+
+RCSTAG_CC("$Id$");
+
+static aWhowas whowas[NICKNAMEHISTORYLENGTH];
+static aWhowas *whowashash[WW_MAX];
+static aWhowas *whowas_next = whowas;
+
+static unsigned int hash_whowas_name(register const char *name);
+
+extern char *canonize(char *);
+
+/*
+ * Since the introduction of numeric nicks (at least for upstream messages,
+ * like MODE +o <nick>, KICK #chan <nick>, KILL <nick> etc), there is no
+ * real important reason for a nick history anymore.
+ * Nevertheless, there are two reason why we might want to keep it:
+ * 1) The /WHOWAS command, which is often usefull to catch harrashing
+ * users or abusers in general.
+ * 2) Clients still use the normal nicks in the client-server protocol,
+ * and it might be considered a nice feature that here we still have
+ * nick chasing.
+ * Note however that BOTH reasons make it redundant to keep a whowas history
+ * for users that split off.
+ *
+ * The rewrite of comstud was many to safe cpu during net.breaks and therefore
+ * a bit redundant imho (Run).
+ *
+ * But - it was written anyway. So lets look at the structure of the
+ * whowas history now:
+ *
+ * We still have a static table of 'aWhowas' structures in which we add
+ * new nicks (plus info) as in a rotating buffer. We keep a global pointer
+ * `whowas_next' that points to the next entry to be overwritten - or to
+ * the oldest entry in the table (which is the same).
+ *
+ * Each entry keeps pointers for two doubly linked lists (thus four pointers):
+ * A list of the entries that have the same hash value ('hashv list'), and
+ * a list of the entries that have the same online pointer (`online list').
+ * Note that the last list (pointers) is only updated as long as online points
+ * to the corresponding client: As soon as the client signs off, this list
+ * is not anymore maintained (and hopefully not used anymore either ;).
+ *
+ * So now we have two ways of accessing this database:
+ * 1) Given a <nick> we can calculate a hashv and then whowashash[hashv] will
+ * point to the start of the 'hash list': all entries with the same hashv.
+ * We'll have to search this list to find the entry with the correct <nick>.
+ * Once we found the correct whowas entry, we have a pointer to the
+ * corresponding client - if still online - for nich chasing purposes.
+ * Note that the same nick can occur multiple times in the whowas history,
+ * each of these having the same hash value of course. While a /WHOWAS on
+ * just a nick will return all entries, nick chasing will only find the
+ * first in the list. Because new entries are added at the start of the
+ * 'hash list' we will always find the youngest entry, which is what we want.
+ * 2) Given an online client we have a pointer to the first whowas entry
+ * of the linked list of whowas entries that all belong to this client.
+ * We ONLY need this to reset all `online' pointers when this client
+ * signs off.
+ *
+ * 27/8/79:
+ *
+ * Note that following:
+ *
+ * a) We *only* (need to) change the 'hash list' and the 'online' list
+ * in add_history().
+ * b) There we always ADD an entry to the BEGINNING of the 'hash list'
+ * and the 'online list': *new* entries are at the start of the lists.
+ * The oldest entries are at the end of the lists.
+ * c) We always REMOVE the oldest entry we have (whowas_next), this means
+ * that this is always an entry that is at the *end* of the 'hash list'
+ * and 'online list' that it is a part of: the next pointer will
+ * always be NULL.
+ * d) The previous pointer is *only* used to update the next pointer of the
+ * previous entry, therefore we could better use a pointer to this
+ * next pointer: That is faster - saves us a 'if' test (it will never be
+ * NULL because the last added entry will point to the pointer that
+ * points to the start of the list) and we won't need special code to
+ * update the list start pointers.
+ *
+ * I incorporated these considerations into the code below.
+ *
+ * --Run
+ */
+
+typedef union {
+ aWhowas *newww;
+ aWhowas *oldww;
+} Current;
+
+#define WHOWAS_UNUSED ((unsigned int)-1)
+
+/*
+ * add_history
+ *
+ * Add a client (cptr) that just changed nick (still_on == true), or
+ * just signed off (still_on == false) to the `whowas' table.
+ *
+ * If the entry used was already in use, then this entry is
+ * freed (lost).
+ */
+void add_history(aClient *cptr, int still_on)
+{
+ register Current ww;
+ ww.newww = whowas_next;
+
+ /* If this entry has already been used, remove it from the lists */
+ if (ww.newww->hashv != WHOWAS_UNUSED)
+ {
+ if (ww.oldww->online) /* No need to update cnext/cprev when offline! */
+ {
+ /* Remove ww.oldww from the linked list with the same `online' pointers */
+ *ww.oldww->cprevnextp = ww.oldww->cnext;
+
+ if (ww.oldww->cnext)
+ MyCoreDump;
+#if 0
+ if (ww.oldww->cnext) /* Never true, we always catch the
+ oldwwest nick of this client first */
+ ww.oldww->cnext->cprevnextp = ww.oldww->cprevnextp;
+#endif
+
+ }
+ /* Remove ww.oldww from the linked list with the same `hashv' */
+ *ww.oldww->hprevnextp = ww.oldww->hnext;
+
+ if (ww.oldww->hnext)
+ MyCoreDump;
+#if 0
+ if (ww.oldww->hnext)
+ ww.oldww->hnext->hprevnextp = ww.oldww->hprevnextp;
+#endif
+
+ if (ww.oldww->name)
+ RunFree(ww.oldww->name);
+ if (ww.oldww->username)
+ RunFree(ww.oldww->username);
+ if (ww.oldww->hostname)
+ RunFree(ww.oldww->hostname);
+ if (ww.oldww->servername)
+ RunFree(ww.oldww->servername);
+ if (ww.oldww->realname)
+ RunFree(ww.oldww->realname);
+ if (ww.oldww->away)
+ RunFree(ww.oldww->away);
+ }
+
+ /* Initialize aWhoWas struct `newww' */
+ ww.newww->hashv = hash_whowas_name(cptr->name);
+ ww.newww->logoff = now;
+ DupString(ww.newww->name, cptr->name);
+ DupString(ww.newww->username, cptr->user->username);
+ DupString(ww.newww->hostname, cptr->user->host);
+ /* Should be changed to server numeric */
+ DupString(ww.newww->servername, cptr->user->server->name);
+ DupString(ww.newww->realname, cptr->info);
+ if (cptr->user->away)
+ DupString(ww.newww->away, cptr->user->away);
+ else
+ ww.newww->away = NULL;
+
+ /* Update/initialize online/cnext/cprev: */
+ if (still_on) /* User just changed nicknames */
+ {
+ ww.newww->online = cptr;
+ /* Add aWhowas struct `newww' to start of 'online list': */
+ if ((ww.newww->cnext = cptr->whowas))
+ ww.newww->cnext->cprevnextp = &ww.newww->cnext;
+ ww.newww->cprevnextp = &cptr->whowas;
+ cptr->whowas = ww.newww;
+ }
+ else /* User quitting */
+ ww.newww->online = NULL;
+
+ /* Add aWhowas struct `newww' to start of 'hashv list': */
+ if ((ww.newww->hnext = whowashash[ww.newww->hashv]))
+ ww.newww->hnext->hprevnextp = &ww.newww->hnext;
+ ww.newww->hprevnextp = &whowashash[ww.newww->hashv];
+ whowashash[ww.newww->hashv] = ww.newww;
+
+ /* Advance `whowas_next' to next entry in the `whowas' table: */
+ if (++whowas_next == &whowas[NICKNAMEHISTORYLENGTH])
+ whowas_next = whowas;
+}
+
+/*
+ * off_history
+ *
+ * Client `cptr' signed off: Set all `online' pointers
+ * corresponding to this client to NULL.
+ */
+void off_history(const aClient *cptr)
+{
+ aWhowas *temp;
+
+ for (temp = cptr->whowas; temp; temp = temp->cnext)
+ temp->online = NULL;
+}
+
+/*
+ * get_history
+ *
+ * Return a pointer to a client that had nick `nick' not more then
+ * `timelimit' seconds ago, if still on line. Otherwise return NULL.
+ *
+ * This function is used for "nick chasing"; since the use of numeric
+ * nicks for "upstream" messages in ircu2.10, this is only used for
+ * looking up non-existing nicks in client->server messages.
+ */
+aClient *get_history(const char *nick, time_t timelimit)
+{
+ aWhowas *temp = whowashash[hash_whowas_name(nick)];
+ timelimit = now - timelimit;
+
+ for (; temp; temp = temp->hnext)
+ if (!strCasediff(nick, temp->name) && temp->logoff > timelimit)
+ return temp->online;
+
+ return NULL;
+}
+
+void count_whowas_memory(int *wwu, size_t *wwum, int *wwa, size_t *wwam)
+{
+ register aWhowas *tmp;
+ register int i;
+ int u = 0, a = 0;
+ size_t um = 0, am = 0;
+
+ for (i = 0, tmp = whowas; i < NICKNAMEHISTORYLENGTH; i++, tmp++)
+ if (tmp->hashv != WHOWAS_UNUSED)
+ {
+ u++;
+ um += (strlen(tmp->name) + 1);
+ um += (strlen(tmp->username) + 1);
+ um += (strlen(tmp->hostname) + 1);
+ um += (strlen(tmp->servername) + 1);
+ if (tmp->away)
+ {
+ a++;
+ am += (strlen(tmp->away) + 1);
+ }
+ }
+
+ *wwu = u;
+ *wwum = um;
+ *wwa = a;
+ *wwam = am;
+}
+
+/*
+ * m_whowas
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nickname queried
+ * parv[2] = maximum returned items (optional, default is unlimitted)
+ * parv[3] = remote server target (Opers only, max returned items 20)
+ */
+int m_whowas(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+ register aWhowas *temp;
+ register int cur = 0;
+ int max = -1, found = 0;
+ char *p, *nick, *s;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+ return 0;
+ }
+ if (parc > 2)
+ max = atoi(parv[2]);
+ if (parc > 3)
+ if (hunt_server(1, cptr, sptr, ":%s WHOWAS %s %s :%s", 3, parc, parv))
+ return 0;
+
+ parv[1] = canonize(parv[1]);
+ if (!MyConnect(sptr) && (max > 20))
+ max = 20; /* Set max replies at 20 */
+ for (s = parv[1]; (nick = strtoken(&p, s, ",")); s = NULL)
+ {
+ /* Search through bucket, finding all nicknames that match */
+ found = 0;
+ for (temp = whowashash[hash_whowas_name(nick)]; temp; temp = temp->hnext)
+ {
+ if (!strCasediff(nick, temp->name))
+ {
+ sendto_one(sptr, rpl_str(RPL_WHOWASUSER),
+ me.name, parv[0], temp->name, temp->username,
+ temp->hostname, temp->realname);
+ sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name, parv[0],
+ temp->name, temp->servername, myctime(temp->logoff));
+ if (temp->away)
+ sendto_one(sptr, rpl_str(RPL_AWAY),
+ me.name, parv[0], temp->name, temp->away);
+ cur++;
+ found++;
+ }
+ if (max >= 0 && cur >= max)
+ break;
+ }
+ if (!found)
+ sendto_one(sptr, err_str(ERR_WASNOSUCHNICK), me.name, parv[0], nick);
+ /* To keep parv[1] intact for ENDOFWHOWAS */
+ if (p)
+ p[-1] = ',';
+ }
+ sendto_one(sptr, rpl_str(RPL_ENDOFWHOWAS), me.name, parv[0], parv[1]);
+ return 0;
+}
+
+void initwhowas(void)
+{
+ register int i;
+
+ for (i = 0; i < NICKNAMEHISTORYLENGTH; i++)
+ whowas[i].hashv = WHOWAS_UNUSED;
+}
+
+static unsigned int hash_whowas_name(register const char *name)
+{
+ register unsigned int hash = 0;
+ register unsigned int hash2 = 0;
+ register char lower;
+
+ do
+ {
+ lower = toLower(*name);
+ hash = (hash << 1) + lower;
+ hash2 = (hash2 >> 1) + lower;
+ }
+ while (*++name);
+
+ return ((hash & WW_MAX_INITIAL_MASK) << BITS_PER_COL) +
+ (hash2 & BITS_PER_COL_MASK);
+}