From: Bleep Date: Tue, 16 Nov 1999 05:13:14 +0000 (+0000) Subject: This commit was generated by cvs2svn to compensate for changes in r2, X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=commitdiff_plain;h=b70944c4b84fc2b707d0853ddf03975569dac2bd This commit was generated by cvs2svn to compensate for changes in r2, which included commits to RCS files with non-trunk default branches. git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@3 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..e4125b6 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,2 @@ +makefile +Makefile diff --git a/.indent.pro b/.indent.pro new file mode 100644 index 0000000..84ea6ad --- /dev/null +++ b/.indent.pro @@ -0,0 +1,50 @@ +--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 diff --git a/.patches b/.patches new file mode 100644 index 0000000..5682e37 --- /dev/null +++ b/.patches @@ -0,0 +1 @@ +ircu2.10.07+ diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..80df2d9 --- /dev/null +++ b/BUGS @@ -0,0 +1,7 @@ +# +# $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)" +# diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..d295e4b --- /dev/null +++ b/ChangeLog @@ -0,0 +1,11 @@ +# +# 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 + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..2336cb8 --- /dev/null +++ b/INSTALL @@ -0,0 +1,104 @@ + INSTALL file by Run + + +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. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9a17037 --- /dev/null +++ b/LICENSE @@ -0,0 +1,249 @@ + + 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) 19yy + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..8a0bad9 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,124 @@ +# Makefile for the Undernet IRC Daemon. +# 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. #### + +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 diff --git a/config/.cvsignore b/config/.cvsignore new file mode 100644 index 0000000..8f1df63 --- /dev/null +++ b/config/.cvsignore @@ -0,0 +1,11 @@ +Makefile +config.cache +config.log +config.status +setup.h +stamp-h +config-sh +Configure +config.h +.config +.tmpconfig.h diff --git a/config/Configure.in b/config/Configure.in new file mode 100644 index 0000000..3217707 --- /dev/null +++ b/config/Configure.in @@ -0,0 +1,549 @@ +# @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: +# xconfig: +# **************************************** +# +# 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 _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 >$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_. Thus, if the user selects , +# the CPP symbol CONFIG_ will be defined and the +# shell variable CONFIG_ 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 diff --git a/config/Makefile.in b/config/Makefile.in new file mode 100644 index 0000000..3804b73 --- /dev/null +++ b/config/Makefile.in @@ -0,0 +1,104 @@ +# config/Makefile for the Undernet IRC Daemon. +# 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. #### + +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 diff --git a/config/README b/config/README new file mode 100644 index 0000000..7baeded --- /dev/null +++ b/config/README @@ -0,0 +1,94 @@ +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 diff --git a/config/acconfig.h b/config/acconfig.h new file mode 100644 index 0000000..af2787a --- /dev/null +++ b/config/acconfig.h @@ -0,0 +1,32 @@ +/* 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 , or */ +#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 diff --git a/config/acinclude.m4 b/config/acinclude.m4 new file mode 100644 index 0000000..2119861 --- /dev/null +++ b/config/acinclude.m4 @@ -0,0 +1,290 @@ +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 +#include +#include +#include +#include +#include +$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 +#include +#include +#include +#include +#include +$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 ], +[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 +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 +#include +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/^.* /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]) +]) diff --git a/config/aclocal.m4 b/config/aclocal.m4 new file mode 100644 index 0000000..8cd8680 --- /dev/null +++ b/config/aclocal.m4 @@ -0,0 +1,391 @@ +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 +#include +#include +#include +#include +#include +$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 +#include +#include +#include +#include +#include +$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 ], +[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 +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 +#include +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/^.* /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 +#include +#include +#include +/* 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 +]) + diff --git a/config/config-sh.in b/config/config-sh.in new file mode 100644 index 0000000..dee9749 --- /dev/null +++ b/config/config-sh.in @@ -0,0 +1,382 @@ +# @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 + diff --git a/config/configure b/config/configure new file mode 100644 index 0000000..62f9400 --- /dev/null +++ b/config/configure @@ -0,0 +1,3937 @@ +#! /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 <&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 < +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 < +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 < +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 <&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 +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 < +#include +#include +#include +/* 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 <&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 <&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 <&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 <&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 <&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 <&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 <&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 <&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 <&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 <&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 < +#include +#include +#include +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 +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 +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 < +#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 < +#include +#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 +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 <&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 <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 < +#include +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 < +#include +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 <&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 < +#if STDC_HEADERS +#include +#include +#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 < +#include +#include +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 < +#include +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 +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 < +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 <&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 < +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 <&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 < +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 <&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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +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 <&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 < +#include +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 < +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 < +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 <&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 < +/* 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 < +#include +#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 <&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 < +/* 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 < +/* 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 < +/* 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 <&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 < +/* 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 <&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 < +/* 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 <&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 < +/* 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 <&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 +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 <&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/^.* /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 < +#include +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 < +#include +#include +#include +#include +#include +$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 < +#include +#include +#include +#include +#include +$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 < +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 < +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 <&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 </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 < 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 <> $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 <> $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 <> $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 <> $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 + diff --git a/config/configure.in b/config/configure.in new file mode 100644 index 0000000..2459635 --- /dev/null +++ b/config/configure.in @@ -0,0 +1,121 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl Copyright (c) 1997, by Carlo Wood + +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;],) diff --git a/config/gen.doc.Makefile b/config/gen.doc.Makefile new file mode 100644 index 0000000..3a0a539 --- /dev/null +++ b/config/gen.doc.Makefile @@ -0,0 +1,7 @@ +. ./.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 diff --git a/config/gen.ircd.Makefile b/config/gen.ircd.Makefile new file mode 100644 index 0000000..f6db89c --- /dev/null +++ b/config/gen.ircd.Makefile @@ -0,0 +1,20 @@ +. ./.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 diff --git a/config/install-sh b/config/install-sh new file mode 100644 index 0000000..ebc6691 --- /dev/null +++ b/config/install-sh @@ -0,0 +1,250 @@ +#! /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 diff --git a/config/parse.none b/config/parse.none new file mode 100644 index 0000000..447f9f5 --- /dev/null +++ b/config/parse.none @@ -0,0 +1,9 @@ +if [ "$CFLAGS" = "none" ]; then + CFLAGS="" +fi +if [ "$LDFLAGS" = "none" ]; then + LDFLAGS="" +fi +if [ "$IRCDLIBS" = "none" ]; then + IRCDLIBS="" +fi diff --git a/config/setup.h.in b/config/setup.h.in new file mode 100644 index 0000000..84e336e --- /dev/null +++ b/config/setup.h.in @@ -0,0 +1,219 @@ +/* setup.h.in. Generated automatically from configure.in by autoheader. */ +/* 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. + */ + +/* 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 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 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 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 and . */ +#undef TIME_WITH_SYS_TIME + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define to `int' if 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 , or */ +#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 header file. */ +#undef HAVE_ERRNO_H + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the header file. */ +#undef HAVE_NET_ERRNO_H + +/* Define if you have the header file. */ +#undef HAVE_POLL_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_CDEFS_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_FILE_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_MALLOC_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the crypt library (-lcrypt). */ +#undef HAVE_LIBCRYPT diff --git a/config/setup.h.top b/config/setup.h.top new file mode 100644 index 0000000..27fa27c --- /dev/null +++ b/config/setup.h.top @@ -0,0 +1,17 @@ +/* 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. + */ diff --git a/config/stamp-h.in b/config/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/config/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/configure b/configure new file mode 100755 index 0000000..9fe9154 --- /dev/null +++ b/configure @@ -0,0 +1,9 @@ +#! /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 diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 0000000..0be7160 --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1,2 @@ +Makefile +stamp-m diff --git a/doc/Authors b/doc/Authors new file mode 100644 index 0000000..31076f5 --- /dev/null +++ b/doc/Authors @@ -0,0 +1,171 @@ +/************************************************************************ + * 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 . +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 : Emacs-like editing facility for the client + +Kimmo Suominen : HP-UX port + +Jeff Trim : enhancements and advice + +Vijay Subramaniam : advice and ruthless publicity + +Karl Kleinpaste : user's manual + +Greg Lindahl : AUTOMATON code, the Wumpus GM automaton, +myriad bug fixes + +Bill Wisner : numerous bug fixes and code +enhancements + +Tom Davis and Tim Russell : +VMS modifications + +Markku Savela : advice, support, and being the +incentive to do some of our *own* coding. :) + +Tom Hopkins : bug fixes, quarantine lines, +consolidation of various patches. + +Christopher Davis : EFnet/Anet gateway coding, +many automata ;), documentation fixing. + +Helen Rose : documentation updating, and fixing. + +Tom Hinds : emacs client updating. + +Tim Miller : various server and client-breaking +features. + +Darren Reed : various bug fixes and enhancements. +Introduced nickname and channelname hash tables into the server. + +The version 2.2 release was coordinated by Mike Bolotski +. + +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 +Chuck Kane +Matt Lyle +Vesa Ruokonen + +Markku Savela / 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 / 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 / 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 / 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 +Bo Adler +Michael Sandrof +Jon Solomon +Jan Peterson +Nathan Glasser +Helen Rose +Mike Pelletier +Basalat Ali Raja +Eric P. Scott +Dan Goodwin +Noah Friedman + + +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 +(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 +CapVideo +Chaos +Cym +Derrick +Ensor +flux +Ghostwolf +Jamey +Jarle +Kev +Nemesi +Niels +record +smg +SeKs +Simon- +Starfox +Trio +WildThang +Xorath diff --git a/doc/Configure.help b/doc/Configure.help new file mode 100644 index 0000000..508e208 --- /dev/null +++ b/doc/Configure.help @@ -0,0 +1,1076 @@ +# Format of this file: descriptionvariablehelptext. +# 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.", where 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 ', 'truss -p ' or + 'trace -p ' 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 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 " 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 . 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 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. diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 0000000..6e3a405 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,57 @@ +# doc/Makefile for the Undernet IRC Daemon. +# 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. #### + +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 diff --git a/doc/example.conf b/doc/example.conf new file mode 100644 index 0000000..c1893ee --- /dev/null +++ b/doc/example.conf @@ -0,0 +1,353 @@ +# ircd.conf configuration file for ircd version ircu2.9.mu and ircu2.10 +# +# Written by Niels , 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::*::: +# +# The is the port that other servers can connect to. +# Client ports need to be specified with a P: line, see below. +# +# Note that 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::: + +A:The University of London:Undernet IRC server:IRC Admins + +# +# 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::::: + +# 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::::: + +# 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 , +# 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 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 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 or 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:: + +# 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::"": +# +# It is possible to use a file as comment for the ban. +# K::!: +# +# 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 " 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::: + +# +# 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::::: +# N::::: +# +# 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::: +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:::: + +# +# For an advanced, real-time rule-based routing decision making system +# you can use Disallow lines. For more information, see doc/readme.crules. +# D::: +# d::: +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::::: +# o::::: + +O:*@*.cs.vu.nl:VRKLKuGKn0jLs:Niels::10 + +# Note that the 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:::: + +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 +# or . +# 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 asking for information. +# Upgrades of the Undernet ircd can be found on http://coder-com.undernet.org/. +# +# For the rest: Good Luck! +# +# -- Niels. diff --git a/doc/history/2.4.notes b/doc/history/2.4.notes new file mode 100644 index 0000000..0321b35 --- /dev/null +++ b/doc/history/2.4.notes @@ -0,0 +1,478 @@ +/************************************************************************ + * 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) diff --git a/doc/history/2.7-New b/doc/history/2.7-New new file mode 100644 index 0000000..7980d77 --- /dev/null +++ b/doc/history/2.7-New @@ -0,0 +1,128 @@ + * 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 and MODE -b + 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 diff --git a/doc/history/Manual b/doc/history/Manual new file mode 100644 index 0000000..37a31a1 --- /dev/null +++ b/doc/history/Manual @@ -0,0 +1,316 @@ +/************************************************************************ + * 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 + +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 + +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 + ccc + Tttt + +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 [key] + +/join 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 + +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 <#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 + +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 +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 + +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 (you do not need to be a channel operator to do this). +For a list of channel bans, type /mode +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. diff --git a/doc/history/README-2.6 b/doc/history/README-2.6 new file mode 100644 index 0000000..6ba8ada --- /dev/null +++ b/doc/history/README-2.6 @@ -0,0 +1,71 @@ +/************************************************************************ + * 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 + + (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 + + 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 +o and /mode -o +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 kicks a user off channel. + +--Jarkko (jto@oulu.fi) diff --git a/doc/history/README.patches b/doc/history/README.patches new file mode 100644 index 0000000..cfc917b --- /dev/null +++ b/doc/history/README.patches @@ -0,0 +1,1901 @@ + +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 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 .) (Not yet implemented, prob. in pre8). + + + TSpre8 + + +From: Carlo Kid - Runaway +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 .) + + + + +From: Carlo Kid - Runaway +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 +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 + 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 [] + SILENCE [+|-] + + SILENCE allows you to TOTALLY ignore all private messages (PRIVMSG's) + and notices (NOTICE's) from any user whose nick!user@host matches + the 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 +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 [] +> SILENCE [+|-] +> + +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 [+|-] +Or : SILENCE + +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 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 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 +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 +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 +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 + +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: + NOTE USER [&passwd] [+-flags] [+-maxtime] +- or SEND|SPY|FIND|WAITFOR|NEWS +* or SEND|SPY|FIND|WAITFOR|WALL|WALLOPS|DENY|NEWS|KEY + NOTE LS|COUNT|RM|LOG [&pwd][+-flags][#ID] [date] + NOTE FLAG [&passwd] [+-flags] [#ID] <+-flags> +* NOTE SENT [NAME|COUNT|USERS] [RM] +- NOTE STATS [MSM|MSW|MUM|MUW|MST|MSF|USED] +- NOTE SENT [NAME|COUNT] +* NOTE STATS [MSM|MSW|MUM|MUW|MST|MSF|USED|RESET] [value] +* NOTE 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 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 (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 , 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 [] 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 [&] [+|-flags] [#] + 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 [&] [+|-] [+|-] +* +* 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 [&] [+|-] [+|-] + + 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 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 [&] [+|-] [#] + <+|-flags> + You can add or delete as many flags as you wish with +/-. + + 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 [&] [+|-] [+|-] +* 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 [&] [+|-] [#] + 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 [&] [+|-] [#] + [] + 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 [&] [+|-] [+|-] + + 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 <...> 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 [&] [+|-] [#] + 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 [&] [+|-] [+|-] + + 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] [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 +* Useful in robots. Note will take the requests as if from + + +Usage: NOTE SPY [&] [+|-] [+|-] + [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, 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 + SPY +10 foobar + SPY +aj &secret * + SPY +365 +e !user nick!*@* + 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 [&] [+|-] [+|-] + + 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 + 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 [&] [+|-] [+|-] + [] + 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 [&] [+|-] [+|-] +* +* 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 [&] [+|-] [+|-] +* +* WALLOPS is an alias for USER +BRW (default max 1 day) +* This command is same as WALL, except only opers could receive it. +============================================================================= diff --git a/doc/history/history.pre24 b/doc/history/history.pre24 new file mode 100644 index 0000000..cac4d30 --- /dev/null +++ b/doc/history/history.pre24 @@ -0,0 +1,51 @@ +/************************************************************************ + * 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. diff --git a/doc/history/overview.u2.9 b/doc/history/overview.u2.9 new file mode 100644 index 0000000..e677058 --- /dev/null +++ b/doc/history/overview.u2.9 @@ -0,0 +1,206 @@ +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 [] [] [] + +Sends (default 5, max 20) size 1024 bytes, from (remote) +server (default local 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 [] [] + +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 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 +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 + diff --git a/doc/irc.1 b/doc/irc.1 new file mode 100644 index 0000000..09b3ad3 --- /dev/null +++ b/doc/irc.1 @@ -0,0 +1,82 @@ +.\" @(#)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 +.nf +Manual page updated by Michel Fingerhut diff --git a/doc/ircd.8 b/doc/ircd.8 new file mode 100644 index 0000000..be36b88 --- /dev/null +++ b/doc/ircd.8 @@ -0,0 +1,147 @@ +.\" @(#)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. diff --git a/doc/readme.crules b/doc/readme.crules new file mode 100644 index 0000000..803f06f --- /dev/null +++ b/doc/readme.crules @@ -0,0 +1,126 @@ +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. diff --git a/doc/readme.indent b/doc/readme.indent new file mode 100644 index 0000000..6e02bc0 --- /dev/null +++ b/doc/readme.indent @@ -0,0 +1,9 @@ +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/ diff --git a/doc/readme.who b/doc/readme.who new file mode 100644 index 0000000..419abbe --- /dev/null +++ b/doc/readme.who @@ -0,0 +1,289 @@ +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 [ []] + + 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 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 o" is exactly the same as +"/who O". + +The "options2 part can be as follows: + + [][%[[,]]] + +in wich: + + : 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[,]]], + is as it has always been since the first who.patch, the 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 , 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 + diff --git a/doc/readme.www b/doc/readme.www new file mode 100644 index 0000000..f3c25e7 --- /dev/null +++ b/doc/readme.www @@ -0,0 +1,23 @@ +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/ diff --git a/include/IPcheck.h b/include/IPcheck.h new file mode 100644 index 0000000..f274996 --- /dev/null +++ b/include/IPcheck.h @@ -0,0 +1,16 @@ +#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 */ diff --git a/include/bsd.h b/include/bsd.h new file mode 100644 index 0000000..98ed790 --- /dev/null +++ b/include/bsd.h @@ -0,0 +1,14 @@ +#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 diff --git a/include/channel.h b/include/channel.h new file mode 100644 index 0000000..53feffe --- /dev/null +++ b/include/channel.h @@ -0,0 +1,172 @@ +/* + * 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 */ diff --git a/include/class.h b/include/class.h new file mode 100644 index 0000000..97e4832 --- /dev/null +++ b/include/class.h @@ -0,0 +1,82 @@ +/* + * 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 */ diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..beff780 --- /dev/null +++ b/include/common.h @@ -0,0 +1,170 @@ +/* + * 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 + +/*============================================================================= + * 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 */ diff --git a/include/crule.h b/include/crule.h new file mode 100644 index 0000000..d436378 --- /dev/null +++ b/include/crule.h @@ -0,0 +1,12 @@ +#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 */ diff --git a/include/dbuf.h b/include/dbuf.h new file mode 100644 index 0000000..03b45d1 --- /dev/null +++ b/include/dbuf.h @@ -0,0 +1,62 @@ +/* + * 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 /* 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 */ diff --git a/include/fileio.h b/include/fileio.h new file mode 100644 index 0000000..ad7ad7a --- /dev/null +++ b/include/fileio.h @@ -0,0 +1,53 @@ +#ifndef INCLUDED_fileio_h +#define INCLUDED_fileio_h + +#ifndef INCLUDED_sys_types_h +#include /* size_t */ +#define INCLUDED_sys_types_h +#endif +#ifndef INCLUDED_sys_stat_h +#include /* 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 */ diff --git a/include/h.h b/include/h.h new file mode 100644 index 0000000..c8d9f95 --- /dev/null +++ b/include/h.h @@ -0,0 +1,46 @@ +/* + * 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 */ diff --git a/include/hash.h b/include/hash.h new file mode 100644 index 0000000..47149bb --- /dev/null +++ b/include/hash.h @@ -0,0 +1,77 @@ +/* + * 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 ! */ +#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 */ diff --git a/include/ircd.h b/include/ircd.h new file mode 100644 index 0000000..e62ea9d --- /dev/null +++ b/include/ircd.h @@ -0,0 +1,55 @@ +#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 */ diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..ef61f3a --- /dev/null +++ b/include/list.h @@ -0,0 +1,70 @@ +#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 */ diff --git a/include/map.h b/include/map.h new file mode 100644 index 0000000..5d539d7 --- /dev/null +++ b/include/map.h @@ -0,0 +1,10 @@ +#ifndef MAP_H +#define MAP_H + +/*============================================================================= + * Proto types + */ + +extern int m_map(aClient *cptr, aClient *sptr, int parc, char *parv[]); + +#endif /* MAP_H */ diff --git a/include/match.h b/include/match.h new file mode 100644 index 0000000..7a328a7 --- /dev/null +++ b/include/match.h @@ -0,0 +1,35 @@ +#ifndef MATCH_H +#define MATCH_H + +/*============================================================================= + * System headers used by this header file + */ +#include +#include +#include + +/*============================================================================= + * 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 */ diff --git a/include/msg.h b/include/msg.h new file mode 100644 index 0000000..b6bf59c --- /dev/null +++ b/include/msg.h @@ -0,0 +1,346 @@ +/* + * 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 */ diff --git a/include/numeric.h b/include/numeric.h new file mode 100644 index 0000000..405067c --- /dev/null +++ b/include/numeric.h @@ -0,0 +1,266 @@ +/* + * 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 */ diff --git a/include/numnicks.h b/include/numnicks.h new file mode 100644 index 0000000..5a435a4 --- /dev/null +++ b/include/numnicks.h @@ -0,0 +1,85 @@ +/* + * 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 +#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 */ diff --git a/include/opercmds.h b/include/opercmds.h new file mode 100644 index 0000000..f16e549 --- /dev/null +++ b/include/opercmds.h @@ -0,0 +1,67 @@ +#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 */ diff --git a/include/packet.h b/include/packet.h new file mode 100644 index 0000000..9c2c639 --- /dev/null +++ b/include/packet.h @@ -0,0 +1,11 @@ +#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 */ diff --git a/include/parse.h b/include/parse.h new file mode 100644 index 0000000..f6ac2c2 --- /dev/null +++ b/include/parse.h @@ -0,0 +1,12 @@ +#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 */ diff --git a/include/patchlevel.h b/include/patchlevel.h new file mode 100644 index 0000000..700d74f --- /dev/null +++ b/include/patchlevel.h @@ -0,0 +1,370 @@ +/* + * 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 diff --git a/include/querycmds.h b/include/querycmds.h new file mode 100644 index 0000000..22a9c83 --- /dev/null +++ b/include/querycmds.h @@ -0,0 +1,75 @@ +#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 */ diff --git a/include/random.h b/include/random.h new file mode 100644 index 0000000..af18a28 --- /dev/null +++ b/include/random.h @@ -0,0 +1,10 @@ +#ifndef RANDOM_H +#define RANDOM_H + +/*============================================================================= + * Proto types + */ + +extern unsigned int ircrandom(void); + +#endif /* RANDOM_H */ diff --git a/include/res.h b/include/res.h new file mode 100644 index 0000000..b808652 --- /dev/null +++ b/include/res.h @@ -0,0 +1,37 @@ +#ifndef RES_H +#define RES_H + +#include +#include +#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 */ diff --git a/include/runmalloc.h b/include/runmalloc.h new file mode 100644 index 0000000..5b39361 --- /dev/null +++ b/include/runmalloc.h @@ -0,0 +1,66 @@ +/* + * 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 + +#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 */ diff --git a/include/s_auth.h b/include/s_auth.h new file mode 100644 index 0000000..c325432 --- /dev/null +++ b/include/s_auth.h @@ -0,0 +1,12 @@ +#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 */ diff --git a/include/s_bsd.h b/include/s_bsd.h new file mode 100644 index 0000000..9fa24d1 --- /dev/null +++ b/include/s_bsd.h @@ -0,0 +1,184 @@ +#ifndef S_BSD_H +#define S_BSD_H + +#include +#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 */ diff --git a/include/s_conf.h b/include/s_conf.h new file mode 100644 index 0000000..e67aa4a --- /dev/null +++ b/include/s_conf.h @@ -0,0 +1,124 @@ +#ifndef S_CONF_H +#define S_CONF_H + +#include "list.h" +#include +#include + +/*============================================================================= + * 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 */ diff --git a/include/s_debug.h b/include/s_debug.h new file mode 100644 index 0000000..80872a1 --- /dev/null +++ b/include/s_debug.h @@ -0,0 +1,157 @@ +#ifndef S_DEBUG_H +#define S_DEBUG_H + +#include +#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 */ diff --git a/include/s_err.h b/include/s_err.h new file mode 100644 index 0000000..ee746ae --- /dev/null +++ b/include/s_err.h @@ -0,0 +1,11 @@ +#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 */ diff --git a/include/s_misc.h b/include/s_misc.h new file mode 100644 index 0000000..e53b7df --- /dev/null +++ b/include/s_misc.h @@ -0,0 +1,71 @@ +#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 */ diff --git a/include/s_numeric.h b/include/s_numeric.h new file mode 100644 index 0000000..0c148b3 --- /dev/null +++ b/include/s_numeric.h @@ -0,0 +1,11 @@ +#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 */ diff --git a/include/s_ping.h b/include/s_ping.h new file mode 100644 index 0000000..20ff1fd --- /dev/null +++ b/include/s_ping.h @@ -0,0 +1,16 @@ +#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 */ diff --git a/include/s_serv.h b/include/s_serv.h new file mode 100644 index 0000000..4c7fdd1 --- /dev/null +++ b/include/s_serv.h @@ -0,0 +1,98 @@ +#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 */ diff --git a/include/s_user.h b/include/s_user.h new file mode 100644 index 0000000..47fee6c --- /dev/null +++ b/include/s_user.h @@ -0,0 +1,72 @@ +#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 */ diff --git a/include/send.h b/include/send.h new file mode 100644 index 0000000..73be18c --- /dev/null +++ b/include/send.h @@ -0,0 +1,62 @@ +#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 */ diff --git a/include/sprintf_irc.h b/include/sprintf_irc.h new file mode 100644 index 0000000..1a8d19b --- /dev/null +++ b/include/sprintf_irc.h @@ -0,0 +1,17 @@ +#ifndef SPRINTF_IRC +#define SPRINTF_IRC + +#include + +/*============================================================================= + * 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 */ diff --git a/include/struct.h b/include/struct.h new file mode 100644 index 0000000..1a18590 --- /dev/null +++ b/include/struct.h @@ -0,0 +1,166 @@ +/* + * 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 /* 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 */ diff --git a/include/support.h b/include/support.h new file mode 100644 index 0000000..5629669 --- /dev/null +++ b/include/support.h @@ -0,0 +1,22 @@ +#ifndef SUPPORT_H +#define SUPPORT_H + +#include + +/*============================================================================= + * 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 */ diff --git a/include/sys.h b/include/sys.h new file mode 100644 index 0000000..47814b5 --- /dev/null +++ b/include/sys.h @@ -0,0 +1,320 @@ +/* + * 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 +#include +#include + +#ifdef __osf__ +#undef _OSF_SOURCE +/* Buggy header */ +#include +#define _OSF_SOURCE +#endif + +#if HAVE_ERRNO_H +# include +#else +# if HAVE_NET_ERRNO_H +# include +# 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 +#else +# if HAVE_STRING_H +# include +# 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 +# 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 +#endif + +/* See AC_HEADER_TIME in 'info autoconf' */ +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# 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 +#include +#else /* !STDC_HEADERS */ +#ifdef HAVE_MALLOC_H +#include +#else +#ifdef HAVE_SYS_MALLOC_H +#include +#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 +#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__ */ diff --git a/include/userload.h b/include/userload.h new file mode 100644 index 0000000..50adf2c --- /dev/null +++ b/include/userload.h @@ -0,0 +1,47 @@ +/* + * Userload module by Michael L. VanLoon (mlv) + * 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 */ diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..e9e3c74 --- /dev/null +++ b/include/version.h @@ -0,0 +1,9 @@ +#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 */ diff --git a/include/whocmds.h b/include/whocmds.h new file mode 100644 index 0000000..f471538 --- /dev/null +++ b/include/whocmds.h @@ -0,0 +1,15 @@ +#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 */ diff --git a/include/whowas.h b/include/whowas.h new file mode 100644 index 0000000..e7def4e --- /dev/null +++ b/include/whowas.h @@ -0,0 +1,66 @@ +/* + * 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 */ diff --git a/ircd/.cvsignore b/ircd/.cvsignore new file mode 100644 index 0000000..54bccd8 --- /dev/null +++ b/ircd/.cvsignore @@ -0,0 +1,5 @@ +Makefile +stamp-m +version.c +ircd +chkconf diff --git a/ircd/IPcheck.c b/ircd/IPcheck.c new file mode 100644 index 0000000..df668b6 --- /dev/null +++ b/ircd/IPcheck.c @@ -0,0 +1,477 @@ +/* + * 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 +#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); +} diff --git a/ircd/Makefile.in b/ircd/Makefile.in new file mode 100644 index 0000000..4bf1c44 --- /dev/null +++ b/ircd/Makefile.in @@ -0,0 +1,460 @@ +# 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 /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 diff --git a/ircd/bsd.c b/ircd/bsd.c new file mode 100644 index 0000000..89467d0 --- /dev/null +++ b/ircd/bsd.c @@ -0,0 +1,181 @@ +/* + * 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 +#include /* 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); +} diff --git a/ircd/channel.c b/ircd/channel.c new file mode 100644 index 0000000..c917cdc --- /dev/null +++ b/ircd/channel.c @@ -0,0 +1,4506 @@ +/* + * 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 +#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: " BURST " */ + 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: " BURST [ [ ]]" */ + 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+' '+' '++'\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 part of /join 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 + */ +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] = + * parv[n+1] = (optional) + * parv[n+2] = (optional) + * If parv[n] starts with a '%', then n will be parc-1: + * parv[n] = % ... + * If parv[n] starts with another character: + * parv[n] = [:],[:],... + * where 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 + * numeric that is joining (and possibly opped) here. + * Therefore we consider the following situations: + * - The numeric exists and is from the direction of cptr: ok + * - The numeric does not exist: + * Apparently this previous numeric was killed (upstream) + * or it collided with an existing name. + * - The numeric exists but is from another direction: + * Apparently this previous 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; + } + } + } +} diff --git a/ircd/chkconf.c b/ircd/chkconf.c new file mode 100644 index 0000000..97eb441 --- /dev/null +++ b/ircd/chkconf.c @@ -0,0 +1,623 @@ +/* + * 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 +#endif +#include +#include +#include +#include +#if HAVE_FCNTL_H +#include +#endif +#ifdef HPUX +#include +#endif /* HPUX */ +#ifdef R_LINES +#include +#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 '-'; +} diff --git a/ircd/class.c b/ircd/class.c new file mode 100644 index 0000000..257f359 --- /dev/null +++ b/ircd/class.c @@ -0,0 +1,223 @@ +/* + * 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; +} diff --git a/ircd/common.c b/ircd/common.c new file mode 100644 index 0000000..bd12fe7 --- /dev/null +++ b/ircd/common.c @@ -0,0 +1,612 @@ +/* + * 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 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 +#include +#include + +#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 */ diff --git a/ircd/crule.c b/ircd/crule.c new file mode 100644 index 0000000..ccacadf --- /dev/null +++ b/ircd/crule.c @@ -0,0 +1,788 @@ +/* + * SmartRoute phase 1 + * connection rule patch + * by Tony Vencill (Tonto on IRC) + * + * 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 +#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 diff --git a/ircd/crypt/Makefile b/ircd/crypt/Makefile new file mode 100644 index 0000000..45cad8a --- /dev/null +++ b/ircd/crypt/Makefile @@ -0,0 +1,37 @@ +#************************************************************************ +#* 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 diff --git a/ircd/crypt/README b/ircd/crypt/README new file mode 100644 index 0000000..9d5f79c --- /dev/null +++ b/ircd/crypt/README @@ -0,0 +1,61 @@ +/************************************************************************ + * 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) diff --git a/ircd/crypt/crypter b/ircd/crypt/crypter new file mode 100644 index 0000000..1bf5ebb --- /dev/null +++ b/ircd/crypt/crypter @@ -0,0 +1,52 @@ +#!/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); diff --git a/ircd/crypt/mkpasswd.c b/ircd/crypt/mkpasswd.c new file mode 100644 index 0000000..ee65d9b --- /dev/null +++ b/ircd/crypt/mkpasswd.c @@ -0,0 +1,49 @@ +/* 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 +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include "../../config/setup.h" +#if STDC_HEADERS +# include +#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; +} + diff --git a/ircd/crypt/sums b/ircd/crypt/sums new file mode 100644 index 0000000..4cdd29a --- /dev/null +++ b/ircd/crypt/sums @@ -0,0 +1,60 @@ +#! /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 + diff --git a/ircd/dbuf.c b/ircd/dbuf.c new file mode 100644 index 0000000..95aedd9 --- /dev/null +++ b/ircd/dbuf.c @@ -0,0 +1,384 @@ +/* + * 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 +#include + +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); + * + * 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; +} diff --git a/ircd/fileio.c b/ircd/fileio.c new file mode 100644 index 0000000..44f5090 --- /dev/null +++ b/ircd/fileio.c @@ -0,0 +1,198 @@ +/* + * IRC - Internet Relay Chat, ircd/fileio.c + * Copyright (C) 1998 Thomas Helvey + * 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 /* BUFSIZ, EOF */ +#include /* O_RDONLY, O_WRONLY, ... */ +#include /* read, write, open, close */ +#include /* 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); +} diff --git a/ircd/hash.c b/ircd/hash.c new file mode 100644 index 0000000..700880b --- /dev/null +++ b/ircd/hash.c @@ -0,0 +1,552 @@ +/* + * 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 +#include +#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< +#endif +#include +#include +#include +#if HAVE_FCNTL_H +#include +#endif +#ifdef HPUX +#define _KERNEL +#endif +#include +#ifdef HPUX +#undef _KERNEL +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef USE_SYSLOG +#include +#endif +#ifdef CHROOTDIR +#include +#include +#include +#endif +#ifdef VIRTUAL_HOST +#include /* 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 \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(); + } +} diff --git a/ircd/list.c b/ircd/list.c new file mode 100644 index 0000000..36d4030 --- /dev/null +++ b/ircd/list.c @@ -0,0 +1,496 @@ +/* + * 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 : "", + 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 : "", + 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 diff --git a/ircd/map.c b/ircd/map.c new file mode 100644 index 0000000..3be7411 --- /dev/null +++ b/ircd/map.c @@ -0,0 +1,94 @@ +/* + * 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; +} diff --git a/ircd/match.c b/ircd/match.c new file mode 100644 index 0000000..f66f2b4 --- /dev/null +++ b/ircd/match.c @@ -0,0 +1,1014 @@ +/* + * 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 + * 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.. can't match head2.. */ + return ((*w) || (*r)) ? 1 : 0; /* and head matches only head */ + if (!*++w) + return 0; /* headZ matches head */ + + /* 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 fails on head2tail */ + return (*w) ? 1 : 0; /* but headZliat matches headtail */ + }; + + /* 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 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 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); + +} diff --git a/ircd/numnicks.c b/ircd/numnicks.c new file mode 100644 index 0000000..09b904c --- /dev/null +++ b/ircd/numnicks.c @@ -0,0 +1,501 @@ +/* + * 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 + +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 */ diff --git a/ircd/opercmds.c b/ircd/opercmds.c new file mode 100644 index 0000000..53098fd --- /dev/null +++ b/ircd/opercmds.c @@ -0,0 +1,1843 @@ +/* + * 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 +#if HAVE_FCNTL_H +#include +#endif +#include +#if HAVE_UNISTD_H +#include +#endif +#ifdef USE_SYSLOG +#include +#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[] = ""; + 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++] = ""; + } + + 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 :" */ + 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 : ""); +#else /* GODMODE */ + version, debugmode, tname, acptr ? acptr->from->name : "", + (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 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- + */ + + 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 : ""); + 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], "", *(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] = [+|-] + * parv[3] = Expiration offset + * parv[4] = Comment + * + * From client: + * + * parv[1] = [+|-] + * 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; +} diff --git a/ircd/packet.c b/ircd/packet.c new file mode 100644 index 0000000..e4ef0dc --- /dev/null +++ b/ircd/packet.c @@ -0,0 +1,153 @@ +/* + * 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 + +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; +} diff --git a/ircd/parse.c b/ircd/parse.c new file mode 100644 index 0000000..50639a4 --- /dev/null +++ b/ircd/parse.c @@ -0,0 +1,779 @@ +/* + * 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); +} diff --git a/ircd/querycmds.c b/ircd/querycmds.c new file mode 100644 index 0000000..c31d0d0 --- /dev/null +++ b/ircd/querycmds.c @@ -0,0 +1,423 @@ +/* + * 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 +#if HAVE_FCNTL_H +#include +#endif +#include +#if HAVE_UNISTD_H +#include +#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], + "Type /MOTD to read the AUP before continuing using this service."); + 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; +} diff --git a/ircd/random.c b/ircd/random.c new file mode 100644 index 0000000..5905c80 --- /dev/null +++ b/ircd/random.c @@ -0,0 +1,158 @@ +/* + * 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 +#include +#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<>(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); +} diff --git a/ircd/res.c b/ircd/res.c new file mode 100644 index 0000000..806bbec --- /dev/null +++ b/ircd/res.c @@ -0,0 +1,1678 @@ +/* + * 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 +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +/* 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; +} diff --git a/ircd/runmalloc.c b/ircd/runmalloc.c new file mode 100644 index 0000000..7baf8bc --- /dev/null +++ b/ircd/runmalloc.c @@ -0,0 +1,456 @@ +/* + * 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 + * 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 +#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 */ diff --git a/ircd/s_auth.c b/ircd/s_auth.c new file mode 100644 index 0000000..7299327 --- /dev/null +++ b/ircd/s_auth.c @@ -0,0 +1,270 @@ +/* + * 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 +#if HAVE_SYS_FILE_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#ifdef UNIXPORT +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#ifdef HPUX +#include +#endif /* HPUX */ +#if HAVE_FCNTL_H +#include +#endif +#ifdef USE_SYSLOG +#include +#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; +} diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c new file mode 100644 index 0000000..e1606c2 --- /dev/null +++ b/ircd/s_bsd.c @@ -0,0 +1,2615 @@ +/* + * 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 +#include +#if HAVE_SYS_FILE_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#ifdef SOL2 +#include +#endif +#ifdef UNIXPORT +#include +#endif +#include +#ifdef USE_POLL +#ifndef HAVE_POLL_H +#undef USE_POLL +#else /* HAVE_POLL_H */ +#ifdef HAVE_STROPTS_H +#include +#endif +#include +#endif /* HAVE_POLL_H */ +#endif /* USE_POLL */ +#include +#if HAVE_FCNTL_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#ifdef USE_SYSLOG +#include +#endif +#include +#include +#include +#include +#include +#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; + } +} diff --git a/ircd/s_conf.c b/ircd/s_conf.c new file mode 100644 index 0000000..4c5e3f2 --- /dev/null +++ b/ircd/s_conf.c @@ -0,0 +1,1541 @@ +/* + * 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 +#if HAVE_FCNTL_H +#include +#endif + +#if HAVE_SYS_WAIT_H +# include +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +#include +#ifdef R_LINES +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#ifdef USE_SYSLOG +#include +#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 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; +} diff --git a/ircd/s_debug.c b/ircd/s_debug.c new file mode 100644 index 0000000..39940ae --- /dev/null +++ b/ircd/s_debug.c @@ -0,0 +1,606 @@ +/* + * 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 +#endif +#if defined(HPUX) && HAVE_FCNTL_H +#include +#endif +#ifdef HPUX +#include +#define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b) +#endif +#if HAVE_GETRUSAGE +#include +#else +#if HAVE_TIMES +#include +#endif +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#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 */ diff --git a/ircd/s_err.c b/ircd/s_err.c new file mode 100644 index 0000000..ec40bd0 --- /dev/null +++ b/ircd/s_err.c @@ -0,0 +1,749 @@ +/* + * 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, " 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; +} diff --git a/ircd/s_misc.c b/ircd/s_misc.c new file mode 100644 index 0000000..fa9b078 --- /dev/null +++ b/ircd/s_misc.c @@ -0,0 +1,699 @@ +/* + * 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 +#if HAVE_FCNTL_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#ifdef USE_SYSLOG +#include +#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 + +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); +} diff --git a/ircd/s_numeric.c b/ircd/s_numeric.c new file mode 100644 index 0000000..7cf0fef --- /dev/null +++ b/ircd/s_numeric.c @@ -0,0 +1,103 @@ +/* + * 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; +} diff --git a/ircd/s_ping.c b/ircd/s_ping.c new file mode 100644 index 0000000..aa10dd9 --- /dev/null +++ b/ircd/s_ping.c @@ -0,0 +1,606 @@ +/* + * 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 +#if HAVE_SYS_FILE_H +#include +#endif +#if HAVE_SYS_IOCTL_H +#include +#endif +#ifdef UNIXPORT +#include +#endif +#if HAVE_FCNTL_H +#include +#endif +#include +#if HAVE_UNISTD_H +#include +#endif +#ifdef USE_SYSLOG +#include +#endif +#include +#include +#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); +} diff --git a/ircd/s_serv.c b/ircd/s_serv.c new file mode 100644 index 0000000..ab5ff17 --- /dev/null +++ b/ircd/s_serv.c @@ -0,0 +1,1117 @@ +/* + * 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 +#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 + * 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; +} diff --git a/ircd/s_user.c b/ircd/s_user.c new file mode 100644 index 0000000..cbfb599 --- /dev/null +++ b/ircd/s_user.c @@ -0,0 +1,3009 @@ +/* + * 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 +#include +#if HAVE_FCNTL_H +#include +#endif +#include +#if HAVE_UNISTD_H +#include +#endif +#ifdef USE_SYSLOG +#include +#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 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 and then + * check if it's hostname matches 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])) ? "" : parv[1]; + host = (parc < 3 || BadPtr(parv[2])) ? "" : parv[2]; + server = (parc < 4 || BadPtr(parv[3])) ? "" : parv[3]; + realname = (parc < 5 || BadPtr(parv[4])) ? "" : 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; +} diff --git a/ircd/send.c b/ircd/send.c new file mode 100644 index 0000000..08a70e7 --- /dev/null +++ b/ircd/send.c @@ -0,0 +1,894 @@ +/* + * 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 +#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); +} diff --git a/ircd/sprintf_irc.c b/ircd/sprintf_irc.c new file mode 100644 index 0000000..ab85492 --- /dev/null +++ b/ircd/sprintf_irc.c @@ -0,0 +1,417 @@ +/* + * IRC - Internet Relay Chat, ircd/s_ping.c + * + * (C) Copyright 1997 + * + * Author: + * + * 1024/624ACAD5 1997/01/26 Carlo Wood, Run on IRC + * 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 +#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; +} diff --git a/ircd/support.c b/ircd/support.c new file mode 100644 index 0000000..ebc1795 --- /dev/null +++ b/ircd/support.c @@ -0,0 +1,254 @@ +/* + * 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 +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#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); + } +} diff --git a/ircd/userload.c b/ircd/userload.c new file mode 100644 index 0000000..4b0c8b9 --- /dev/null +++ b/ircd/userload.c @@ -0,0 +1,275 @@ +/* + * Userload module by Michael L. VanLoon (mlv) + * Written 2/93. Originally grafted into irc2.7.2g 4/93. + * + * Rewritten 9/97 by Carlo Wood (Run) + * 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 +#include +#include +#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 */ +} diff --git a/ircd/version.c.SH b/ircd/version.c.SH new file mode 100644 index 0000000..c0b52a9 --- /dev/null +++ b/ircd/version.c.SH @@ -0,0 +1,111 @@ +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 <", + "", + "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 ", + "msa Markku Savela ", + "Wumpus Greg Lindahl ", + "", + "The main developer of version u2.9 and u2.10 is:", + "", + "Run Carlo Wood ", + "", + "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! diff --git a/ircd/whocmds.c b/ircd/whocmds.c new file mode 100644 index 0000000..58b8f31 --- /dev/null +++ b/ircd/whocmds.c @@ -0,0 +1,811 @@ + +/* + * 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 +#include +#if HAVE_FCNTL_H +#include +#endif +#include +#if HAVE_UNISTD_H +#include +#endif +#ifdef USE_SYSLOG +#include +#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, "", "", ""); + } + + 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; +} diff --git a/ircd/whowas.c b/ircd/whowas.c new file mode 100644 index 0000000..a893490 --- /dev/null +++ b/ircd/whowas.c @@ -0,0 +1,391 @@ + +/* + * 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 +#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 , KICK #chan , KILL 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 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 . + * 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); +}