From b70944c4b84fc2b707d0853ddf03975569dac2bd Mon Sep 17 00:00:00 2001 From: Bleep Date: Tue, 16 Nov 1999 05:13:14 +0000 Subject: [PATCH] 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 --- .cvsignore | 2 + .indent.pro | 50 + .patches | 1 + BUGS | 7 + ChangeLog | 11 + INSTALL | 104 + LICENSE | 249 ++ Makefile.in | 124 + config/.cvsignore | 11 + config/Configure.in | 549 +++++ config/Makefile.in | 104 + config/README | 94 + config/acconfig.h | 32 + config/acinclude.m4 | 290 +++ config/aclocal.m4 | 391 ++++ config/config-sh.in | 382 +++ config/configure | 3937 +++++++++++++++++++++++++++++++ config/configure.in | 121 + config/gen.doc.Makefile | 7 + config/gen.ircd.Makefile | 20 + config/install-sh | 250 ++ config/parse.none | 9 + config/setup.h.in | 219 ++ config/setup.h.top | 17 + config/stamp-h.in | 1 + configure | 9 + doc/.cvsignore | 2 + doc/Authors | 171 ++ doc/Configure.help | 1076 +++++++++ doc/Makefile.in | 57 + doc/example.conf | 353 +++ doc/history/2.4.notes | 478 ++++ doc/history/2.7-New | 128 + doc/history/Manual | 316 +++ doc/history/README-2.6 | 71 + doc/history/README.patches | 1901 +++++++++++++++ doc/history/history.pre24 | 51 + doc/history/overview.u2.9 | 206 ++ doc/irc.1 | 82 + doc/ircd.8 | 147 ++ doc/readme.crules | 126 + doc/readme.indent | 9 + doc/readme.who | 289 +++ doc/readme.www | 23 + include/IPcheck.h | 16 + include/bsd.h | 14 + include/channel.h | 172 ++ include/class.h | 82 + include/common.h | 170 ++ include/crule.h | 12 + include/dbuf.h | 62 + include/fileio.h | 53 + include/h.h | 46 + include/hash.h | 77 + include/ircd.h | 55 + include/list.h | 70 + include/map.h | 10 + include/match.h | 35 + include/msg.h | 346 +++ include/numeric.h | 266 +++ include/numnicks.h | 85 + include/opercmds.h | 67 + include/packet.h | 11 + include/parse.h | 12 + include/patchlevel.h | 370 +++ include/querycmds.h | 75 + include/random.h | 10 + include/res.h | 37 + include/runmalloc.h | 66 + include/s_auth.h | 12 + include/s_bsd.h | 184 ++ include/s_conf.h | 124 + include/s_debug.h | 157 ++ include/s_err.h | 11 + include/s_misc.h | 71 + include/s_numeric.h | 11 + include/s_ping.h | 16 + include/s_serv.h | 98 + include/s_user.h | 72 + include/send.h | 62 + include/sprintf_irc.h | 17 + include/struct.h | 166 ++ include/support.h | 22 + include/sys.h | 320 +++ include/userload.h | 47 + include/version.h | 9 + include/whocmds.h | 15 + include/whowas.h | 66 + ircd/.cvsignore | 5 + ircd/IPcheck.c | 477 ++++ ircd/Makefile.in | 460 ++++ ircd/bsd.c | 181 ++ ircd/channel.c | 4506 ++++++++++++++++++++++++++++++++++++ ircd/chkconf.c | 623 +++++ ircd/class.c | 223 ++ ircd/common.c | 612 +++++ ircd/crule.c | 788 +++++++ ircd/crypt/Makefile | 37 + ircd/crypt/README | 61 + ircd/crypt/crypter | 52 + ircd/crypt/mkpasswd.c | 49 + ircd/crypt/sums | 60 + ircd/dbuf.c | 384 +++ ircd/fileio.c | 198 ++ ircd/hash.c | 552 +++++ ircd/ircd.c | 972 ++++++++ ircd/list.c | 496 ++++ ircd/map.c | 94 + ircd/match.c | 1014 ++++++++ ircd/numnicks.c | 501 ++++ ircd/opercmds.c | 1843 +++++++++++++++ ircd/packet.c | 153 ++ ircd/parse.c | 779 +++++++ ircd/querycmds.c | 423 ++++ ircd/random.c | 158 ++ ircd/res.c | 1678 ++++++++++++++ ircd/runmalloc.c | 456 ++++ ircd/s_auth.c | 270 +++ ircd/s_bsd.c | 2615 +++++++++++++++++++++ ircd/s_conf.c | 1541 ++++++++++++ ircd/s_debug.c | 606 +++++ ircd/s_err.c | 749 ++++++ ircd/s_misc.c | 699 ++++++ ircd/s_numeric.c | 103 + ircd/s_ping.c | 606 +++++ ircd/s_serv.c | 1117 +++++++++ ircd/s_user.c | 3009 ++++++++++++++++++++++++ ircd/send.c | 894 +++++++ ircd/sprintf_irc.c | 417 ++++ ircd/support.c | 254 ++ ircd/userload.c | 275 +++ ircd/version.c.SH | 111 + ircd/whocmds.c | 811 +++++++ ircd/whowas.c | 391 ++++ 134 files changed, 48479 insertions(+) create mode 100644 .cvsignore create mode 100644 .indent.pro create mode 100644 .patches create mode 100644 BUGS create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 LICENSE create mode 100644 Makefile.in create mode 100644 config/.cvsignore create mode 100644 config/Configure.in create mode 100644 config/Makefile.in create mode 100644 config/README create mode 100644 config/acconfig.h create mode 100644 config/acinclude.m4 create mode 100644 config/aclocal.m4 create mode 100644 config/config-sh.in create mode 100644 config/configure create mode 100644 config/configure.in create mode 100644 config/gen.doc.Makefile create mode 100644 config/gen.ircd.Makefile create mode 100644 config/install-sh create mode 100644 config/parse.none create mode 100644 config/setup.h.in create mode 100644 config/setup.h.top create mode 100644 config/stamp-h.in create mode 100755 configure create mode 100644 doc/.cvsignore create mode 100644 doc/Authors create mode 100644 doc/Configure.help create mode 100644 doc/Makefile.in create mode 100644 doc/example.conf create mode 100644 doc/history/2.4.notes create mode 100644 doc/history/2.7-New create mode 100644 doc/history/Manual create mode 100644 doc/history/README-2.6 create mode 100644 doc/history/README.patches create mode 100644 doc/history/history.pre24 create mode 100644 doc/history/overview.u2.9 create mode 100644 doc/irc.1 create mode 100644 doc/ircd.8 create mode 100644 doc/readme.crules create mode 100644 doc/readme.indent create mode 100644 doc/readme.who create mode 100644 doc/readme.www create mode 100644 include/IPcheck.h create mode 100644 include/bsd.h create mode 100644 include/channel.h create mode 100644 include/class.h create mode 100644 include/common.h create mode 100644 include/crule.h create mode 100644 include/dbuf.h create mode 100644 include/fileio.h create mode 100644 include/h.h create mode 100644 include/hash.h create mode 100644 include/ircd.h create mode 100644 include/list.h create mode 100644 include/map.h create mode 100644 include/match.h create mode 100644 include/msg.h create mode 100644 include/numeric.h create mode 100644 include/numnicks.h create mode 100644 include/opercmds.h create mode 100644 include/packet.h create mode 100644 include/parse.h create mode 100644 include/patchlevel.h create mode 100644 include/querycmds.h create mode 100644 include/random.h create mode 100644 include/res.h create mode 100644 include/runmalloc.h create mode 100644 include/s_auth.h create mode 100644 include/s_bsd.h create mode 100644 include/s_conf.h create mode 100644 include/s_debug.h create mode 100644 include/s_err.h create mode 100644 include/s_misc.h create mode 100644 include/s_numeric.h create mode 100644 include/s_ping.h create mode 100644 include/s_serv.h create mode 100644 include/s_user.h create mode 100644 include/send.h create mode 100644 include/sprintf_irc.h create mode 100644 include/struct.h create mode 100644 include/support.h create mode 100644 include/sys.h create mode 100644 include/userload.h create mode 100644 include/version.h create mode 100644 include/whocmds.h create mode 100644 include/whowas.h create mode 100644 ircd/.cvsignore create mode 100644 ircd/IPcheck.c create mode 100644 ircd/Makefile.in create mode 100644 ircd/bsd.c create mode 100644 ircd/channel.c create mode 100644 ircd/chkconf.c create mode 100644 ircd/class.c create mode 100644 ircd/common.c create mode 100644 ircd/crule.c create mode 100644 ircd/crypt/Makefile create mode 100644 ircd/crypt/README create mode 100644 ircd/crypt/crypter create mode 100644 ircd/crypt/mkpasswd.c create mode 100644 ircd/crypt/sums create mode 100644 ircd/dbuf.c create mode 100644 ircd/fileio.c create mode 100644 ircd/hash.c create mode 100644 ircd/ircd.c create mode 100644 ircd/list.c create mode 100644 ircd/map.c create mode 100644 ircd/match.c create mode 100644 ircd/numnicks.c create mode 100644 ircd/opercmds.c create mode 100644 ircd/packet.c create mode 100644 ircd/parse.c create mode 100644 ircd/querycmds.c create mode 100644 ircd/random.c create mode 100644 ircd/res.c create mode 100644 ircd/runmalloc.c create mode 100644 ircd/s_auth.c create mode 100644 ircd/s_bsd.c create mode 100644 ircd/s_conf.c create mode 100644 ircd/s_debug.c create mode 100644 ircd/s_err.c create mode 100644 ircd/s_misc.c create mode 100644 ircd/s_numeric.c create mode 100644 ircd/s_ping.c create mode 100644 ircd/s_serv.c create mode 100644 ircd/s_user.c create mode 100644 ircd/send.c create mode 100644 ircd/sprintf_irc.c create mode 100644 ircd/support.c create mode 100644 ircd/userload.c create mode 100644 ircd/version.c.SH create mode 100644 ircd/whocmds.c create mode 100644 ircd/whowas.c 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); +} -- 2.20.1