aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorGravatar Jonas Gunz <himself@jonasgunz.de> 2020-05-25 20:09:04 +0200
committerGravatar Jonas Gunz <himself@jonasgunz.de> 2020-05-25 20:09:04 +0200
commit4440a86cfa359b8e40a484a2cd46d33db5455d8a (patch)
treef5c0c59aebf0058ae97e7ef8b5fb8017f459a05a /contrib
downloadircd-4440a86cfa359b8e40a484a2cd46d33db5455d8a.tar.gz
Initial
Diffstat (limited to 'contrib')
-rw-r--r--contrib/README21
-rw-r--r--contrib/RULES24
-rw-r--r--contrib/antispoof.README56
-rw-r--r--contrib/antispoof.diff484
-rw-r--r--contrib/ircdwatch/README56
-rw-r--r--contrib/ircdwatch/ircdwatch.850
-rw-r--r--contrib/ircdwatch/ircdwatch.c499
-rw-r--r--contrib/mkpasswd/README64
-rwxr-xr-xcontrib/mkpasswd/crypter55
-rw-r--r--contrib/mkpasswd/mkpasswd.c44
-rw-r--r--contrib/mod_passwd/README30
-rw-r--r--contrib/mod_passwd/mod_passwd.c171
-rw-r--r--contrib/tkserv/CHANGES37
-rw-r--r--contrib/tkserv/CREDITS60
-rw-r--r--contrib/tkserv/INSTALL20
-rw-r--r--contrib/tkserv/README290
-rw-r--r--contrib/tkserv/proto.h24
-rw-r--r--contrib/tkserv/tkserv.access.example10
-rw-r--r--contrib/tkserv/tkserv.c1022
19 files changed, 3017 insertions, 0 deletions
diff --git a/contrib/README b/contrib/README
new file mode 100644
index 0000000..7d62989
--- /dev/null
+++ b/contrib/README
@@ -0,0 +1,21 @@
+$Id: README,v 1.6 1999/03/13 23:06:15 kalt Exp $
+
+This directory contains ircd related contributions. Most of them fall
+ in one of the following categories:
+ * small side utility
+ * code sample
+ * service
+
+Each contribution lives in its own directory under contrib/ and has a
+README file. For those which need to be compiled, this can be done in
+the same directory as you compiled irc and/or ircd.
+
+If you want to submit a package for this directory, see the RULES file.
+
+---
+
+mkpasswd utility to crypt a password.
+ircdwatch utility to keep ircd running (eventually restarting it).
+tkserv stupid toy to manage "temporary" klines.
+mod_passwd example of DSM module for iauth
+antispoof.diff diff file to add extra code to ircd to prevent TCP spoofing
diff --git a/contrib/RULES b/contrib/RULES
new file mode 100644
index 0000000..f3de2a1
--- /dev/null
+++ b/contrib/RULES
@@ -0,0 +1,24 @@
+$Id: RULES,v 1.2 1998/11/09 20:07:12 kalt Exp $
+
+This file describes how to prepare a package for inclusion
+in the contrib/ directory. The goal is to have a consistent
+setup for all contributions, making it easy to install any
+of them.
+
+* each package must have a README file (at least) explaining
+ what it is about.
+* a man page would be nice ;-)
+
+* packages are stored in contrib/<package name>
+
+* C programs:
+ * If possible, use the same coding style as in the
+ rest of the package.
+ * They need to be portable
+ * They should use "setup.h" or "os.h"
+ * If needed, they may have a *.h file generated by configure
+ * They are to be compiled from within the same
+ directory as irc/ircd, using the same Makefile
+
+
+* questions, submissions should be sent to ircd-dev@irc.org
diff --git a/contrib/antispoof.README b/contrib/antispoof.README
new file mode 100644
index 0000000..fd1d5a7
--- /dev/null
+++ b/contrib/antispoof.README
@@ -0,0 +1,56 @@
+The nospoof patch was adapted from the nospoof5 patch in use on Undernet
+servers. It should NOT be needed unless you're running ircd on a really
+old OS which doesn't have a patch against TCP spoofing. Note that this
+patch should be applied BEFORE running ./configure -- this is very
+important, and the usual cause of any problems encountered.
+
+When a client connects to the server, they are sent a PING with a random
+number (please do not confuse this with a CTCP PING -- they're very
+different things). Until the client responds with a PONG and the correct
+random number, it is not registered with the server and cannot do
+anything.
+
+Please note that this does break the RFC. However, it has been tested
+with most popular clients and is in common use on large IRC networks
+currently. The only reported client to have problems is Homer (for
+the Macintosh).
+
+To cater for possibly broken clients, a message is also sent to clients
+on connect of the form:
+
+*** If your client freezes here, type /QUOTE PONG 12345678 or /PONG 12345678
+
+Because of this, it is a good idea to increase the allowed timeout on
+connections since the user might have to manually PONG the server with
+the ugly number (although hopefully they only need to cut'n'paste).
+
+If the client PONGs with the wrong number, another message is sent to
+the client directing the user what to type. In addition, if the
+connection does end up timing out due to no PONG, a message is sent to
+the user explaining the client may not be compatible, and lists where
+compatible clients for all the major platforms can be found.
+
+The random number sequence is based on an md5 series. I didn't write it.
+Someone else did. It's included because many have a dud random() in their
+libc (this applies to more people than you think). It is seeded on a
+#define value in config.h - YOU MUST CHANGE THIS FROM THE DEFAULT OR YOU
+CAN STILL BE SPOOFED. If you still find that you get spoofed, try
+changing this value again and recompiling.
+
+KNOWN BUGS:
+
+ - There is a known bug whereby the host provided by the client's
+ USER sequence is not checked for validity until after the PONG
+ reply (and registration takes place). This allows a form of
+ "spoofing" to take place, with the client showing under /whois
+ lookups with a fake hostname. At this stage the client can't
+ actually issue commands to the server though, and the hostmask
+ is corrected to what it should be upon the PONG being received
+ (and the client properly registered).
+
+ - The random number generator isn't 64-bit clean. On 64-bit
+ machines, a 64-bit random number is generated, but I'm not
+ convinced all 64-bits are random. At least 32 bits are
+ however, so this isn't a problem.
+
+ - Andrew (earthpig@yoyo.cc.monash.edu.au)
diff --git a/contrib/antispoof.diff b/contrib/antispoof.diff
new file mode 100644
index 0000000..b02832f
--- /dev/null
+++ b/contrib/antispoof.diff
@@ -0,0 +1,484 @@
+diff -urN irc-cvs/common/numeric_def.h irc-cvs-nospoof/common/numeric_def.h
+--- irc-cvs/common/numeric_def.h Wed Jan 20 12:28:46 1999
++++ irc-cvs-nospoof/common/numeric_def.h Tue Mar 16 12:45:56 1999
+@@ -198,6 +198,10 @@
+ #define ERR_UMODEUNKNOWNFLAG 501
+ #define ERR_USERSDONTMATCH 502
+
++#if defined(NOSPOOF)
++#define ERR_BADPING 513
++#endif
++
+ /*
+ * Numberic replies from server commands.
+ * These are currently in the range 200-399.
+diff -urN irc-cvs/common/parse.c irc-cvs-nospoof/common/parse.c
+--- irc-cvs/common/parse.c Tue Dec 29 02:44:57 1998
++++ irc-cvs-nospoof/common/parse.c Tue Mar 16 12:45:56 1999
+@@ -56,7 +56,11 @@
+ { MSG_INVITE, m_invite, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
+ { MSG_WALLOPS, m_wallops, MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
+ { MSG_PING, m_ping, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
++#if defined(NOSPOOF)
++ { MSG_PONG, m_pong, MAXPARA, MSG_LAG, 0, 0, 0L},
++#else
+ { MSG_PONG, m_pong, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
++#endif
+ { MSG_ERROR, m_error, MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
+ #ifdef OPER_KILL
+ { MSG_KILL, m_kill, MAXPARA, MSG_LAG|MSG_REG|MSG_OP|MSG_LOP, 0,0, 0L},
+diff -urN irc-cvs/common/send.c irc-cvs-nospoof/common/send.c
+--- irc-cvs/common/send.c Fri Feb 5 10:26:35 1999
++++ irc-cvs-nospoof/common/send.c Tue Mar 16 12:45:56 1999
+@@ -392,6 +392,9 @@
+ NULL,
+ # endif
+ 0, {0, 0, NULL }, {0, 0, NULL },
++#if defined(NOSPOOF)
++ -1,
++#endif
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, 0, NULL, 0
+ # if defined(__STDC__) /* hack around union{} initialization -Vesa */
+ ,{0}, NULL, "", ""
+diff -urN irc-cvs/common/struct_def.h irc-cvs-nospoof/common/struct_def.h
+--- irc-cvs/common/struct_def.h Sun Mar 14 11:59:54 1999
++++ irc-cvs-nospoof/common/struct_def.h Tue Mar 16 12:45:56 1999
+@@ -432,6 +432,9 @@
+ short lastsq; /* # of 2k blocks when sendqueued called last*/
+ dbuf sendQ; /* Outgoing message queue--if socket full */
+ dbuf recvQ; /* Hold for data incoming yet to be parsed */
++#if defined(NOSPOOF)
++ u_long cookie; /* Random cookie local clients must PONG */
++#endif
+ long sendM; /* Statistics: protocol messages send */
+ long sendK; /* Statistics: total k-bytes send */
+ long receiveM; /* Statistics: protocol messages received */
+diff -urN irc-cvs/ircd/ircd.c irc-cvs-nospoof/ircd/ircd.c
+--- irc-cvs/ircd/ircd.c Sun Mar 14 11:59:58 1999
++++ irc-cvs-nospoof/ircd/ircd.c Tue Mar 16 12:45:56 1999
+@@ -493,6 +493,22 @@
+ else
+ {
+ cptr->exitc = EXITC_PING;
++#if defined(NOSPOOF)
++ if((!IsRegistered(cptr)) && (cptr->name) &&
++ (cptr->username))
++ {
++ sendto_one(cptr,
++ ":%s %d %s :Your client may not be compatible with this server.",
++ me.name, ERR_BADPING, cptr->name);
++ /* Someone suggest a better
++ * place? :) - Earthpig
++ */
++ sendto_one(cptr,
++ ":%s %d %s :Compatible clients are available at "
++ "ftp://yoyo.cc.monash.edu.au/pub/irc/clients/",
++ me.name, ERR_BADPING, cptr->name);
++ }
++#endif
+ (void)exit_client(cptr, cptr, &me,
+ "Ping timeout");
+ }
+diff -urN irc-cvs/ircd/list.c irc-cvs-nospoof/ircd/list.c
+--- irc-cvs/ircd/list.c Tue Dec 29 02:44:57 1998
++++ irc-cvs-nospoof/ircd/list.c Tue Mar 16 12:45:56 1999
+@@ -146,6 +146,9 @@
+ if (size == CLIENT_LOCAL_SIZE)
+ {
+ cptr->since = cptr->lasttime = cptr->firsttime = timeofday;
++#if defined(NOSPOOF)
++ cptr->cookie = 0;
++#endif
+ cptr->confs = NULL;
+ cptr->sockhost[0] = '\0';
+ cptr->buffer[0] = '\0';
+diff -urN irc-cvs/ircd/random.c irc-cvs-nospoof/ircd/random.c
+--- irc-cvs/ircd/random.c Thu Jan 1 10:00:00 1970
++++ irc-cvs-nospoof/ircd/random.c Tue Mar 16 12:45:56 1999
+@@ -0,0 +1,162 @@
++/************************************************************************
++ * 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 <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/time.h>
++#include "os.h"
++#include "s_defines.h"
++
++#ifdef NOSPOOF
++
++char localkey[8] = RANDOM_SEED;
++
++/*
++ * MD5 transform algorithm, taken from code written by Colin Plumb,
++ * and put into the public domain
++ *
++ * Kev: Taken from Ted T'so's /dev/random random.c code and modified to
++ * be slightly simpler. That code is released under a BSD-style copyright
++ * OR under the terms of the GNU Public License, which should be included
++ * at the top of this source file.
++ *
++ * record: Cleaned up to work with ircd. RANDOM_TOKEN is defined in
++ * setup.h by the make script; if people start to "guess" your cookies,
++ * consider recompiling your server with a different random token.
++ */
++
++/* The four core functions - F1 is optimized somewhat */
++
++#define F1(x, y, z) (z ^ (x & (y ^ z)))
++#define F2(x, y, z) F1(z, x, y)
++#define F3(x, y, z) (x ^ y ^ z)
++#define F4(x, y, z) (y ^ (x | ~z))
++
++/* This is the central step in the MD5 algorithm. */
++#define MD5STEP(f, w, x, y, z, data, s) \
++ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
++
++/*
++ * The core of the MD5 algorithm, this alters an existing MD5 hash to
++ * reflect the addition of 16 longwords of new data. MD5Update blocks
++ * the data and converts bytes into longwords for this routine.
++ *
++ * original comment left in; this used to be called MD5Transform and took
++ * two arguments; I've internalized those arguments, creating the character
++ * array "localkey," which should contain 8 bytes of data. The function also
++ * originally returned nothing; now it returns an unsigned long that is the
++ * random number. It appears to be reallyrandom, so... -Kev
++ *
++ * I don't really know what this does. I tried to figure it out and got
++ * a headache. If you know what's good for you, you'll leave this stuff
++ * for the smart people and do something else. -record
++ */
++unsigned long ircrandom(void)
++{
++ unsigned long a, b, c, d;
++ unsigned char in[16];
++ struct timeval tv;
++
++ (void)gettimeofday(&tv, NULL);
++
++ (void)memcpy((void *)in, (void *)localkey, 8);
++ (void)memcpy((void *)(in+8), (void *)&tv.tv_sec, 4);
++ (void)memcpy((void *)(in+12), (void *)&tv.tv_usec, 4);
++
++ a = 0x67452301;
++ b = 0xefcdab89;
++ c = 0x98badcfe;
++ d = 0x10325476;
++
++ MD5STEP(F1, a, b, c, d, (long)in[ 0]+0xd76aa478, 7);
++ MD5STEP(F1, d, a, b, c, (long)in[ 1]+0xe8c7b756, 12);
++ MD5STEP(F1, c, d, a, b, (long)in[ 2]+0x242070db, 17);
++ MD5STEP(F1, b, c, d, a, (long)in[ 3]+0xc1bdceee, 22);
++ MD5STEP(F1, a, b, c, d, (long)in[ 4]+0xf57c0faf, 7);
++ MD5STEP(F1, d, a, b, c, (long)in[ 5]+0x4787c62a, 12);
++ MD5STEP(F1, c, d, a, b, (long)in[ 6]+0xa8304613, 17);
++ MD5STEP(F1, b, c, d, a, (long)in[ 7]+0xfd469501, 22);
++ MD5STEP(F1, a, b, c, d, (long)in[ 8]+0x698098d8, 7);
++ MD5STEP(F1, d, a, b, c, (long)in[ 9]+0x8b44f7af, 12);
++ MD5STEP(F1, c, d, a, b, (long)in[10]+0xffff5bb1, 17);
++ MD5STEP(F1, b, c, d, a, (long)in[11]+0x895cd7be, 22);
++ MD5STEP(F1, a, b, c, d, (long)in[12]+0x6b901122, 7);
++ MD5STEP(F1, d, a, b, c, (long)in[13]+0xfd987193, 12);
++ MD5STEP(F1, c, d, a, b, (long)in[14]+0xa679438e, 17);
++ MD5STEP(F1, b, c, d, a, (long)in[15]+0x49b40821, 22);
++
++ MD5STEP(F2, a, b, c, d, (long)in[ 1]+0xf61e2562, 5);
++ MD5STEP(F2, d, a, b, c, (long)in[ 6]+0xc040b340, 9);
++ MD5STEP(F2, c, d, a, b, (long)in[11]+0x265e5a51, 14);
++ MD5STEP(F2, b, c, d, a, (long)in[ 0]+0xe9b6c7aa, 20);
++ MD5STEP(F2, a, b, c, d, (long)in[ 5]+0xd62f105d, 5);
++ MD5STEP(F2, d, a, b, c, (long)in[10]+0x02441453, 9);
++ MD5STEP(F2, c, d, a, b, (long)in[15]+0xd8a1e681, 14);
++ MD5STEP(F2, b, c, d, a, (long)in[ 4]+0xe7d3fbc8, 20);
++ MD5STEP(F2, a, b, c, d, (long)in[ 9]+0x21e1cde6, 5);
++ MD5STEP(F2, d, a, b, c, (long)in[14]+0xc33707d6, 9);
++ MD5STEP(F2, c, d, a, b, (long)in[ 3]+0xf4d50d87, 14);
++ MD5STEP(F2, b, c, d, a, (long)in[ 8]+0x455a14ed, 20);
++ MD5STEP(F2, a, b, c, d, (long)in[13]+0xa9e3e905, 5);
++ MD5STEP(F2, d, a, b, c, (long)in[ 2]+0xfcefa3f8, 9);
++ MD5STEP(F2, c, d, a, b, (long)in[ 7]+0x676f02d9, 14);
++ MD5STEP(F2, b, c, d, a, (long)in[12]+0x8d2a4c8a, 20);
++
++ MD5STEP(F3, a, b, c, d, (long)in[ 5]+0xfffa3942, 4);
++ MD5STEP(F3, d, a, b, c, (long)in[ 8]+0x8771f681, 11);
++ MD5STEP(F3, c, d, a, b, (long)in[11]+0x6d9d6122, 16);
++ MD5STEP(F3, b, c, d, a, (long)in[14]+0xfde5380c, 23);
++ MD5STEP(F3, a, b, c, d, (long)in[ 1]+0xa4beea44, 4);
++ MD5STEP(F3, d, a, b, c, (long)in[ 4]+0x4bdecfa9, 11);
++ MD5STEP(F3, c, d, a, b, (long)in[ 7]+0xf6bb4b60, 16);
++ MD5STEP(F3, b, c, d, a, (long)in[10]+0xbebfbc70, 23);
++ MD5STEP(F3, a, b, c, d, (long)in[13]+0x289b7ec6, 4);
++ MD5STEP(F3, d, a, b, c, (long)in[ 0]+0xeaa127fa, 11);
++ MD5STEP(F3, c, d, a, b, (long)in[ 3]+0xd4ef3085, 16);
++ MD5STEP(F3, b, c, d, a, (long)in[ 6]+0x04881d05, 23);
++ MD5STEP(F3, a, b, c, d, (long)in[ 9]+0xd9d4d039, 4);
++ MD5STEP(F3, d, a, b, c, (long)in[12]+0xe6db99e5, 11);
++ MD5STEP(F3, c, d, a, b, (long)in[15]+0x1fa27cf8, 16);
++ MD5STEP(F3, b, c, d, a, (long)in[ 2]+0xc4ac5665, 23);
++
++ MD5STEP(F4, a, b, c, d, (long)in[ 0]+0xf4292244, 6);
++ MD5STEP(F4, d, a, b, c, (long)in[ 7]+0x432aff97, 10);
++ MD5STEP(F4, c, d, a, b, (long)in[14]+0xab9423a7, 15);
++ MD5STEP(F4, b, c, d, a, (long)in[ 5]+0xfc93a039, 21);
++ MD5STEP(F4, a, b, c, d, (long)in[12]+0x655b59c3, 6);
++ MD5STEP(F4, d, a, b, c, (long)in[ 3]+0x8f0ccc92, 10);
++ MD5STEP(F4, c, d, a, b, (long)in[10]+0xffeff47d, 15);
++ MD5STEP(F4, b, c, d, a, (long)in[ 1]+0x85845dd1, 21);
++ MD5STEP(F4, a, b, c, d, (long)in[ 8]+0x6fa87e4f, 6);
++ MD5STEP(F4, d, a, b, c, (long)in[15]+0xfe2ce6e0, 10);
++ MD5STEP(F4, c, d, a, b, (long)in[ 6]+0xa3014314, 15);
++ MD5STEP(F4, b, c, d, a, (long)in[13]+0x4e0811a1, 21);
++ MD5STEP(F4, a, b, c, d, (long)in[ 4]+0xf7537e82, 6);
++ MD5STEP(F4, d, a, b, c, (long)in[11]+0xbd3af235, 10);
++ MD5STEP(F4, c, d, a, b, (long)in[ 2]+0x2ad7d2bb, 15);
++ MD5STEP(F4, b, c, d, a, (long)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);
++}
++
++#endif
+diff -urN irc-cvs/ircd/s_debug.c irc-cvs-nospoof/ircd/s_debug.c
+--- irc-cvs/ircd/s_debug.c Fri Feb 5 10:44:16 1999
++++ irc-cvs-nospoof/ircd/s_debug.c Tue Mar 16 12:45:56 1999
+@@ -148,6 +148,9 @@
+ #ifdef INET6
+ '6',
+ #endif
++#if defined(NOSPOOF)
++'*',
++#endif
+ '\0'};
+
+ #ifdef DEBUGMODE
+@@ -344,7 +347,7 @@
+ sendto_one(cptr, ":%s %d %s :BS:%d MXR:%d MXB:%d MXBL:%d PY:%d",
+ ME, RPL_STATSDEFINE, nick, BUFSIZE, MAXRECIPIENTS, MAXBANS,
+ MAXBANLENGTH, MAXPENALTY);
+- sendto_one(cptr, ":%s %d %s :ZL:%d CM:%d CP:%d",
++ sendto_one(cptr, ":%s %d %s :ZL:%d CM:%d CP:%d NS:%s",
+ ME, RPL_STATSDEFINE, nick,
+ #ifdef ZIP_LINKS
+ ZIP_LEVEL,
+@@ -352,9 +355,14 @@
+ -1,
+ #endif
+ #ifdef CLONE_CHECK
+- CLONE_MAX, CLONE_PERIOD
++ CLONE_MAX, CLONE_PERIOD,
++#else
++ -1, -1,
++#endif
++#if defined(NOSPOOF)
++ "yes"
+ #else
+- -1, -1
++ "no"
+ #endif
+ );
+ }
+diff -urN irc-cvs/ircd/s_err.c irc-cvs-nospoof/ircd/s_err.c
+--- irc-cvs/ircd/s_err.c Sun Feb 28 02:45:29 1999
++++ irc-cvs-nospoof/ircd/s_err.c Tue Mar 16 12:45:56 1999
+@@ -162,6 +162,19 @@
+ { 0, (char *)NULL },
+ /* 501 */ { ERR_UMODEUNKNOWNFLAG, ":Unknown MODE flag" },
+ /* 502 */ { ERR_USERSDONTMATCH, ":Can't change mode for other users" },
++#if defined(NOSPOOF)
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++ { 0, (char *)NULL },
++/* 513 */ { ERR_BADPING, (char *)NULL },
++#endif
+ { 0, (char *)NULL }
+ };
+
+diff -urN irc-cvs/ircd/s_user.c irc-cvs-nospoof/ircd/s_user.c
+--- irc-cvs/ircd/s_user.c Tue Mar 16 12:32:51 1999
++++ irc-cvs-nospoof/ircd/s_user.c Tue Mar 16 12:51:41 1999
+@@ -335,9 +335,9 @@
+
+ /*
+ ** 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.
++** This function is called when both NICK and USER [and maybe PONG]
++** 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
+@@ -1007,9 +1007,30 @@
+
+ /* This had to be copied here to avoid problems.. */
+ (void)strcpy(sptr->name, nick);
++#if defined(NOSPOOF)
++ /* If the client hasn't gotten a cookie-ping yet,
++ choose a cookie and send it. -record!jegelhof@cloud9.net */
++
++ if (!sptr->cookie)
++ {
++ while((!sptr->cookie) || (sptr->cookie==-1))
++ sptr->cookie=(ircrandom());
++ sendto_one(cptr, "PING :%lu", sptr->cookie);
++ sendto_one(sptr,
++ ":%s %d %s :If your client freezes here, type /QUOTE PONG %lu "
++ "or /PONG %lu ",
++ me.name, ERR_BADPING, sptr->name,
++ sptr->cookie, sptr->cookie);
++ }
++
++ if ((sptr->user) && (sptr->cookie==-1))
++ {
++#else
+ if (sptr->user)
++#endif
+ /*
+- ** USER already received, now we have NICK.
++ ** USER [and possibly PONG] already received,
++ ** now we have NICK.
+ ** *NOTE* For servers "NICK" *must* precede the
+ ** user message (giving USER before NICK is possible
+ ** only for local client connection!). register_user
+@@ -1020,6 +1041,9 @@
+ sptr->user->username)
+ == FLUSH_BUFFER)
+ return FLUSH_BUFFER;
++#if defined(NOSPOOF)
++ }
++#endif
+ }
+ /*
+ ** Finally set new nick name.
+@@ -1849,7 +1873,12 @@
+ if (strlen(realname) > REALLEN)
+ realname[REALLEN] = '\0';
+ sptr->info = mystrdup(realname);
+- if (sptr->name[0]) /* NICK already received, now we have USER... */
++ if ((sptr->name[0])
++#if defined(NOSPOOF)
++ && (!MyConnect(sptr) || (sptr->cookie==-1))
++#endif
++ )
++ /* NICK already received, now we have USER... */
+ {
+ if ((parc == 6) && IsServer(cptr)) /* internal m_user() */
+ {
+@@ -2222,7 +2251,32 @@
+ aClient *acptr;
+ char *origin, *destination;
+
+- if (parc < 2 || *parv[1] == '\0')
++#if defined(NOSPOOF)
++ /* 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!=-1) && (parc>1))
++ {
++ if(strtoul(parv[parc-1],NULL,10)==sptr->cookie)
++ {
++ sptr->cookie=-1;
++ if((sptr->user) && (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 %lu or /PONG %lu",
++ me.name, ERR_BADPING, sptr->name,
++ sptr->cookie, sptr->cookie);
++
++ return 1;
++ }
++#endif
++
++ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOORIGIN, parv[0]));
+ return 1;
+diff -urN irc-cvs/support/Makefile.in irc-cvs-nospoof/support/Makefile.in
+--- irc-cvs/support/Makefile.in Fri Mar 12 20:06:00 1999
++++ irc-cvs-nospoof/support/Makefile.in Tue Mar 16 12:47:37 1999
+@@ -153,7 +153,7 @@
+ SERVER_OBJS = channel.o class.o hash.o ircd.o list.o res.o s_auth.o \
+ s_bsd.o s_conf.o s_debug.o s_err.o s_id.o s_misc.o s_numeric.o \
+ s_serv.o s_service.o s_user.o s_zip.o whowas.o \
+- res_init.o res_comp.o res_mkquery.o
++ res_init.o res_comp.o res_mkquery.o random.o
+
+ IAUTH_COMMON_OBJS = clsupport.o clmatch.o # This is a little evil
+ IAUTH_OBJS = iauth.o a_conf.o a_io.o a_log.o \
+@@ -431,6 +431,9 @@
+
+ res_mkquery.o: ../ircd/res_mkquery.c setup.h config.h
+ $(CC) $(S_CFLAGS) -c -o $@ ../ircd/res_mkquery.c
++
++random.o: ../ircd/random.c setup.h config.h
++ $(CC) $(S_CFLAGS) -c -o $@ ../ircd/random.c
+
+ iauth.o: ../iauth/iauth.c config.h setup.h
+ $(CC) $(A_CFLAGS) -c -o $@ ../iauth/iauth.c
+diff -urN irc-cvs/support/config.h.dist irc-cvs-nospoof/support/config.h.dist
+--- irc-cvs/support/config.h.dist Mon Feb 22 10:41:32 1999
++++ irc-cvs-nospoof/support/config.h.dist Tue Mar 16 12:45:57 1999
+@@ -374,6 +374,30 @@
+ */
+ #undef CLONE_CHECK
+
++/* Define this to turn on code that enables a PING/PONG cookies sequence
++ * whenever a client connects. The purpose of this is to prevent IP
++ * spoofing (normally based on predicting TCP/IP sequence numbers).
++ * This breaks the RFC slightly, but so far only one (old) client has
++ * been encountered that breaks. There's a small overhead if you define
++ * it, but it's essential for older BSD-based (or any other) TCP/IP
++ * stacks with predictable sequence numbers.
++ */
++
++#undef NOSPOOF
++
++/* Random number generator seed -- used for cookies if NOSPOOF is
++ * defined.
++ *
++ * Set this to an 8 character random text string.
++ * Do _NOT_ use the default text.
++ * If people are able to defeat the IP-spoofing protection on your
++ * server, please consider changing this value and recompiling.
++ */
++
++#if defined(NOSPOOF)
++#define RANDOM_SEED "ChangeMe"
++#endif
++
+ /* STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP */
+ /* STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP */
+ /* STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP */
diff --git a/contrib/ircdwatch/README b/contrib/ircdwatch/README
new file mode 100644
index 0000000..80653bd
--- /dev/null
+++ b/contrib/ircdwatch/README
@@ -0,0 +1,56 @@
+$Id: README,v 1.5 1998/11/09 22:56:34 borud Exp $
+
+DESCRIPTION
+===========
+ ircdwatch is a daemon that regularly checks to see if ircd is running.
+ if ircd should die or somehow stop running, ircdwatch will respawn the
+ ircd daemon. if desirable, ircdwatch can be configured to check if
+ the ircd configuration file is changed and send a SIGHUP to the ircd
+ daemon which then causes the daemon to reload the configuration
+ file.
+
+ the ircdwatch program itself is also used as sort of a remote
+ control depending on what command line arguments you feed it. you
+ can use it to start ircd and stop ircd and ircdwatch.
+
+OPTIONS
+=======
+ if you invoke ircdwatch without any command line arguments it will
+ either daemonize itself and become a daemon, or it will exit if it
+ detects that an ircdwatch daemon is already running.
+
+ for using the ircdwatch program as a remote control the following
+ command line options are available:
+
+ --kill
+ stop both ircdwatch and ircd
+
+ --rest
+ stop ircdwatch but leave ircd running
+
+ --help
+ list command line arguments
+
+
+COMPILE TIME CONFIGURATION
+==========================
+ you configure ircdwatch by editing config.h in the directory where
+ you build ircd. the configuration parameters available for
+ ircdwatch are those starting with the prefix "IRCDWATCH_". please
+ refer to the config.h file for more information on the parameter
+ settings. most of the defaults should be okay if you want to use
+ ircdwatch though.
+
+
+ADMINISTRATIVIA
+===============
+ ircdwatch was written by Bjorn Borud <borud@guardian.no> and is
+ Copyright (C) 1998 Bjorn Borud <borud@guardian.no>
+
+ the program is released under the GNU General Public License
+
+ the current maintainer can be reached at: <borud@guardian.no>
+
+ please report bugs to <ircd-users@stealth.net>
+ improvements are welcome.
+
diff --git a/contrib/ircdwatch/ircdwatch.8 b/contrib/ircdwatch/ircdwatch.8
new file mode 100644
index 0000000..cc944b1
--- /dev/null
+++ b/contrib/ircdwatch/ircdwatch.8
@@ -0,0 +1,50 @@
+.\" @(#)$Id: ircdwatch.8,v 1.1 1998/06/12 22:57:40 kalt Exp $
+.TH IRCDWATCH 8 "$Date: 1998/06/12 22:57:40 $"
+.SH NAME
+ircdwatch \- Daemon for monitoring ircd and ircd.conf
+.SH SYNOPSIS
+.hy 0
+.IP \fBircdwatch\fP
+[
+.BI \-\-kill
+] [
+.BI \-\-rest
+] [
+.BI \-\-help
+]
+.SH DESCRIPTION
+.LP
+\fIircdwatch\fP is a daemon that periodically checks that the
+\fIircd\fP daemon is running, restarting it of necessary. This
+daemon can also be configured (at compile time) to check for
+changes to the \fIircd\fP configuration file and make \fIircd\fP
+reload its configuration file by sending it a SIGHUP signal.
+.LP
+Given command line arguments \fIircdwatch\fP will serve as a remote
+control for stopping both \fIircdwatch\fP and \fIircd\fP or just
+\fIircdwatch\fP.
+
+.SH OPTIONS
+.TP
+.B \-\-kill
+Stop both \fIircd\fP and \fIircdwatch\fP
+.TP
+.B \-\-rest
+Stop \fIircdwatch\fP but leave \fIircd\fP running
+.TP
+.B \-\-help
+List command line arguments
+.SH COPYRIGHT
+Copyright (C) 1998 Bjorn Borud, <borud@guardian.no>
+.LP
+.RE
+.SH FILES
+ "ircd.conf"
+ "ircd.pid"
+ "ircdwatch.pid"
+.SH "SEE ALSO"
+ircd(8)
+.SH BUGS
+There may be some portability issues. Report bugs to <ircd-users@stealth.net>
+.SH AUTHOR
+Bjorn Borud, <borud@guardian.no>
diff --git a/contrib/ircdwatch/ircdwatch.c b/contrib/ircdwatch/ircdwatch.c
new file mode 100644
index 0000000..c384578
--- /dev/null
+++ b/contrib/ircdwatch/ircdwatch.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 1998 Bjorn Borud <borud@guardian.no>
+ *
+ * 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 lint
+static char rcsid[] = "@(#)$Id: ircdwatch.c,v 1.3 1999/02/21 00:33:44 kalt Exp $";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h> /* atol() */
+#include <unistd.h> /* fork() exec() */
+#include <sys/types.h>
+#include <sys/stat.h> /* stat() */
+#include <signal.h>
+#include <syslog.h>
+#include <string.h> /* strncmp() */
+#include <time.h>
+
+#include "os.h"
+#include "config.h"
+
+/*
+ * Try and find the correct name to use with getrlimit() for setting
+ * the max. number of files allowed to be open by this process.
+ * (borrowed from ircd/s_bsd.c)
+ */
+#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
+
+#define PID_LEN 7 /* overkill, but hey */
+#define MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS 256
+
+static int want_to_quit = 0;
+
+void finalize(int i)
+{
+#ifdef IRCDWATCH_USE_SYSLOG
+ syslog(LOG_NOTICE, "ircdwatch daemon exiting");
+ closelog();
+#endif
+ exit(i);
+}
+
+int daemonize(void)
+{
+ pid_t pid;
+ int i;
+ int open_max;
+
+#ifdef RLIMIT_FD_MAX
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_FD_MAX, &rlim) != 0) {
+ perror("getrlimit");
+ finalize(1);
+ }
+
+ /* if we get a lame answer we just take a wild guess */
+ if (rlim.rlim_max == 0) {
+ open_max = MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS;
+ }
+
+ open_max = rlim.rlim_max;
+
+#else
+
+ /* no RLIMIT_FD_MAX? take a wild guess */
+ open_max = MAX_OPEN_FILEDESCRIPTORS_WILD_GUESS;
+
+#endif
+
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ exit(1);
+ }
+
+ /* parent process dies */
+ if (pid != 0) {
+ exit(0);
+ }
+
+ setsid();
+ umask(0);
+
+ /* close file descriptors */
+ for (i=0; i < open_max; i++) {
+ close(i);
+ }
+
+ return(0);
+}
+
+static void sig_handler (int signo)
+{
+ if (signo == SIGHUP) {
+ want_to_quit = 1;
+ return;
+ }
+
+ if (signo == SIGALRM) {
+
+#ifndef POSIX_SIGNALS
+ (void)signal(SIGALRM, &sig_handler);
+#endif;
+
+ return;
+ }
+}
+
+
+void set_up_signals(void)
+{
+#ifdef POSIX_SIGNALS
+ struct sigaction act;
+
+ act.sa_handler = sig_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ if (sigaction(SIGHUP, &act, NULL) < 0) {
+ perror("sigaction");
+ }
+
+ if (sigaction(SIGALRM, &act, NULL) < 0) {
+ perror("sigaction");
+ }
+#else
+ (void)signal(SIGHUP, &sig_handler);
+ (void)signal(SIGALRM, &sig_handler);
+#endif
+
+ /* ignore it if child processes die */
+ signal(SIGCHLD, SIG_IGN);
+}
+
+int write_my_pid(void)
+{
+ FILE *f;
+
+ f = fopen(IRCDWATCH_PID_FILENAME, "w");
+ if (f == NULL) {
+ return(-1);
+ }
+
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+
+ return(0);
+}
+
+
+int file_modified(char *s)
+{
+ struct stat st;
+
+ if (stat(s, &st) < 0) {
+ return(-1);
+ }
+ return(st.st_ctime);
+}
+
+
+int spawn (char *cmd)
+{
+ pid_t pid;
+
+ pid = fork();
+
+ if (pid == -1) {
+#ifdef IRCDWATCH_USE_SYSLOG
+ syslog(LOG_ERR, "spawn() unable to fork, errno=%d", errno);
+#endif
+ return(-1);
+ }
+
+ if (pid == 0) {
+ execl("/bin/sh", "sh", "-c", cmd, (char *) NULL);
+ _exit(127);
+ }
+ return(0);
+}
+
+int read_pid(char *pid_filename)
+{
+ FILE *f;
+ char pidbuf[PID_LEN];
+ pid_t pid;
+
+ f = fopen(pid_filename, "r");
+ if (f == NULL) {
+#ifdef IRCDWATCH_USE_SYSLOG
+ syslog(LOG_ERR, "unable to fopen() %s: %s", pid_filename, strerror(errno));
+#endif
+ return(-1);
+ }
+
+ if (fgets(pidbuf, PID_LEN, f) != NULL) {
+ pid = atol(pidbuf);
+ } else {
+#ifdef IRCDWATCH_USE_SYSLOG
+ syslog(LOG_ERR, "fgets() %s: %s", pid_filename, strerror(errno));
+#endif
+ fclose(f);
+ return(-1);
+ }
+
+ fclose(f);
+ return(pid);
+}
+
+int file_exists (char *s)
+{
+ struct stat st;
+ if ((stat(s, &st) < 0) && (errno == ENOENT)) {
+ return(0);
+ }
+ return(1);
+}
+
+/* yeah, I'll get around to these in some later version */
+
+int file_readable (char *s)
+{
+ return(access(s, R_OK) == 0);
+}
+
+int file_writable (char *s)
+{
+ return(access(s, W_OK) == 0);
+}
+
+int file_executable (char *s)
+{
+ int rc;
+ rc = (access(IRCD_PATH, X_OK) == 0);
+ return rc;
+}
+
+int verify_pid(int pid)
+{
+ int res;
+
+ res = kill(pid, 0);
+ if (res < 0 && errno == EPERM) {
+ fprintf(stderr, "Not process owner\n");
+ exit(1);
+ }
+
+ return(res == 0);
+}
+
+int ircdwatch_running () {
+ int pid;
+
+ if (file_exists(IRCDWATCH_PID_FILENAME)) {
+ pid = read_pid(IRCDWATCH_PID_FILENAME);
+ if (pid > 0) {
+ return(verify_pid(pid) == 1);
+ } else {
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+int ircd_running () {
+ int pid;
+
+ if (file_exists(IRCDPID_PATH)) {
+ pid = read_pid(IRCDPID_PATH);
+ if (pid > 0) {
+ return(verify_pid(pid) == 1);
+ } else {
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+void hup_ircd ()
+{
+ int pid;
+ int res;
+
+ if (file_exists(IRCDPID_PATH)) {
+ pid = read_pid(IRCDPID_PATH);
+ if (pid > 0) {
+ res = kill(pid, SIGHUP);
+ if (res < 0 && errno == EPERM) {
+#ifdef IRCDWATCH_USE_SYSLOG
+ syslog(LOG_ERR, "not allowed to send SIGHUP to ircd");
+#endif
+ finalize(1);
+ }
+ }
+ }
+}
+
+
+void daemon_run ()
+{
+ int i;
+ int last_config_time = 0;
+
+ /* is ircdwatch already running? */
+ i = ircdwatch_running();
+ if (i == -1) {
+ /* unable to open pid file. wrong user? */
+ fprintf(stderr, "ircdwatch pid file exists but is unreadable\n");
+ exit(1);
+ } else if (i) {
+ fprintf(stderr, "ircdwatch is already running\n");
+ exit(0);
+ }
+
+ /* is ircd running? */
+ i = ircd_running();
+ if (i == -1) {
+ /* unable to open pid file. wrong user? */
+ fprintf(stderr, "ircdwatch pid file exists but is unreadable\n");
+ exit(1);
+ } else if (!i) {
+ fprintf(stderr, "ircd not running. attempting to start ircd...\n");
+ if (file_exists(IRCD_PATH)) {
+ if (file_executable(IRCD_PATH)) {
+ spawn(IRCD_PATH);
+ } else {
+ fprintf(stderr, "%s not executable\n", IRCD_PATH);
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, "%s does not exist\n", IRCD_PATH);
+ exit(1);
+ }
+ }
+
+ set_up_signals();
+ closelog();
+ /* daemonize(); */
+
+#ifdef IRCDWATCH_USE_SYSLOG
+ openlog(IRCDWATCH_SYSLOG_IDENT,
+ IRCDWATCH_SYSLOG_OPTIONS,
+ IRCDWATCH_SYSLOG_FACILITY);
+
+ syslog(LOG_NOTICE, "starting ircdwatch daemon");
+#endif
+
+ alarm(IRCDWATCH_POLLING_INTERVAL);
+ pause();
+
+ while (!want_to_quit) {
+ if (! ircd_running() ) {
+
+#ifdef IRCDWATCH_USE_SYSLOG
+ syslog(LOG_ERR, "spawning %s", IRCD_PATH);
+#endif
+
+ spawn(IRCD_PATH);
+ }
+
+#ifdef IRCDWATCH_HUP_ON_CONFIG_CHANGE
+ i = file_modified(IRCDCONF_PATH);
+ if (i != -1) {
+
+ if (last_config_time == 0) {
+ last_config_time = i;
+ }
+
+ else if (i > last_config_time) {
+ last_config_time = i;
+ hup_ircd();
+
+#ifdef IRCDWATCH_USE_SYSLOG
+ syslog(LOG_NOTICE, "config change, HUPing ircd");
+#endif
+
+ }
+ }
+#endif
+
+ alarm(IRCDWATCH_POLLING_INTERVAL);
+ pause();
+ }
+ return;
+}
+
+void kill_ircd ()
+{
+ int pid;
+ int res;
+
+ if (file_exists(IRCDPID_PATH)) {
+ pid = read_pid(IRCDPID_PATH);
+ if (pid > 0) {
+ res = kill(pid, SIGTERM);
+ if (res < 0) {
+ perror("ircd kill");
+ finalize(1);
+ } else {
+ fprintf(stderr, "killed ircd\n");
+ }
+ }
+ } else {
+ fprintf(stderr, "File %s does not exist\n", IRCDPID_PATH);
+ }
+}
+
+void kill_ircdwatch ()
+{
+ int pid;
+ int res;
+
+ if (file_exists(IRCDWATCH_PID_FILENAME)) {
+ pid = read_pid(IRCDWATCH_PID_FILENAME);
+ if (pid > 0) {
+ res = kill(pid, SIGHUP);
+ if (res < 0) {
+ perror("ircdwatch kill");
+ finalize(1);
+ } else {
+ fprintf(stderr, "Sent HUP to ircdwatch. it will die soon...\n");
+ }
+ }
+ } else {
+ fprintf(stderr, "File %s does not exist\n", IRCDWATCH_PID_FILENAME);
+ }
+}
+
+
+void usage (void)
+{
+ fprintf(stderr,"\n\
+Usage:\n\
+ ircdwatch [--kill | --rest | --help]\n\
+\n\
+ --kill, stop both ircdwatch and ircd\n\
+ --rest, stop ircdwatch but let ircd alone\n\
+ --help, display this text\n\
+\n\
+%s\n", rcsid);
+}
+
+int
+main (int argc, char **argv) {
+ int i;
+
+#ifdef IRCDWATCH_USE_SYSLOG
+ openlog(IRCDWATCH_SYSLOG_IDENT,
+ IRCDWATCH_SYSLOG_OPTIONS,
+ IRCDWATCH_SYSLOG_FACILITY);
+#endif
+
+ if (argc > 1) {
+ if (strncmp(argv[1], "--rest", 6) == 0) {
+ kill_ircdwatch();
+ exit(0);
+ }
+
+ if (strncmp(argv[1], "--kill", 6) == 0) {
+ kill_ircdwatch();
+ kill_ircd();
+ exit(0);
+ }
+
+ usage();
+ exit(0);
+ }
+
+ daemon_run();
+ finalize(0);
+}
diff --git a/contrib/mkpasswd/README b/contrib/mkpasswd/README
new file mode 100644
index 0000000..a2bb420
--- /dev/null
+++ b/contrib/mkpasswd/README
@@ -0,0 +1,64 @@
+/************************************************************************
+ * 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.
+ *
+ * $Id: README,v 1.1 1998/04/07 21:20:59 kalt Exp $
+ *
+ */
+
+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/contrib/mkpasswd/crypter b/contrib/mkpasswd/crypter
new file mode 100755
index 0000000..4851620
--- /dev/null
+++ b/contrib/mkpasswd/crypter
@@ -0,0 +1,55 @@
+#!/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.
+#*
+#* $Id: crypter,v 1.1 1998/04/07 21:21:00 kalt Exp $
+#*
+#*/
+
+#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/contrib/mkpasswd/mkpasswd.c b/contrib/mkpasswd/mkpasswd.c
new file mode 100644
index 0000000..8ea8413
--- /dev/null
+++ b/contrib/mkpasswd/mkpasswd.c
@@ -0,0 +1,44 @@
+/* simple password generator by Nelson Minar (minar@reed.edu)
+ * copyright 1991, all rights reserved.
+ * You can use this code as long as my name stays with it.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: mkpasswd.c,v 1.1 1998/04/07 21:21:00 kalt Exp $";
+#endif
+
+extern char *getpass();
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+ static char saltChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
+ char salt[3];
+ char * plaintext;
+ int i;
+
+ 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/contrib/mod_passwd/README b/contrib/mod_passwd/README
new file mode 100644
index 0000000..6aa2352
--- /dev/null
+++ b/contrib/mod_passwd/README
@@ -0,0 +1,30 @@
+$Id: README,v 1.1 1999/03/13 23:06:15 kalt Exp $
+
+This is an example module for the authentication program (iauth) used by
+the IRC server.
+
+* This module demonstrates how a module can access and use the PASS and
+ USER information provided by users. It is *NOT* a finished product. In
+ particular, the actual password validation is not implemented.
+
+* This module also demonstrates how a DSM module should be written. You'll
+ note that it is completely identical to ordinary modules, except for one
+ extra function: "passwd_load()"
+
+To be used, this module needs to be compiled from the normal compilation
+directory. It should be linked as a dynamic library. Methods vary
+depending on the compiler and platform.
+
+$ gcc -c -g -I../iauth -I../common -I. ../contrib/mod_passwd/mod_passwd.c
+$ ld -Bshareable mod_passwd.o -o mod_passwd.so
+$ ls -l mod_passwd.so
+-rwxr--r-- 1 kalt staff 26932 Mar 13 17:59 mod_passwd.so
+$
+
+To be used by iauth, add the following lines to the iauth.conf file:
+
+ extinfo
+ shared passwd /path/to/mod_passwd.so
+ module passwd
+
+See iauth.conf(5) for more information on configuring iauth.
diff --git a/contrib/mod_passwd/mod_passwd.c b/contrib/mod_passwd/mod_passwd.c
new file mode 100644
index 0000000..817a722
--- /dev/null
+++ b/contrib/mod_passwd/mod_passwd.c
@@ -0,0 +1,171 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, mod_passwd.c
+ * Copyright (C) 1999 Christophe Kalt
+ *
+ * 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 lint
+static char rcsid[] = "@(#)$Id: mod_passwd.c,v 1.1 1999/03/13 23:06:15 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "a_defines.h"
+#include "a_externs.h"
+
+/*
+ * passwd_init
+ *
+ * This procedure is called when a particular module is loaded.
+ * Returns NULL if everything went fine,
+ * an error message otherwise.
+ */
+char *
+passwd_init(self)
+AnInstance *self;
+{
+ return NULL;
+}
+
+/*
+ * passwd_release
+ *
+ * This procedure is called when a particular module is unloaded.
+ */
+void
+passwd_release(self)
+AnInstance *self;
+{
+}
+
+/*
+ * passwd_stats
+ *
+ * This procedure is called regularly to update statistics sent to ircd.
+ */
+void
+passwd_stats(self)
+AnInstance *self;
+{
+}
+
+/*
+ * passwd_start
+ *
+ * This procedure is called to start an authentication.
+ * Returns 0 if everything went fine,
+ * +1 if still waiting for some data (username, password)..
+ * -1 otherwise (nothing to be done, or failure)
+ *
+ * It is responsible for sending error messages where appropriate.
+ * In case of failure, it's responsible for cleaning up (e.g. passwd_clean
+ * will NOT be called)
+ */
+int
+passwd_start(cl)
+u_int cl;
+{
+ if (cldata[cl].authuser &&
+ cldata[cl].authfrom < cldata[cl].instance->in)
+ {
+ /*
+ ** another instance preceding this one in the configuration
+ ** has already authenticated the user, no need to bother
+ ** doing anything here then. (the other takes precedence)
+ */
+ return -1;
+ }
+ if ((cldata[cl].state & A_GOTU) == 0)
+ /* haven't received username/password pair from ircd yet */
+ return 1;
+ if ((cldata[cl].state & A_GOTP) == 0)
+ {
+ /* no password to check -> reject user! */
+ cldata[cl].state |= A_DENY;
+ sendto_ircd("K %d %s %u ", cl, cldata[cl].itsip,
+ cldata[cl].itsport);
+ return -1; /* done */
+ }
+ /*
+ **
+ **
+ ** INSERT FUNCTION TO CHECK PASSWORD VALIDITY
+ **
+ **
+ */
+ /* if failure, see above */
+ /* if success: */
+ cldata[cl].state |= A_UNIX;
+ if (cldata[cl].authuser)
+ free(cldata[cl].authuser);
+ cldata[cl].authuser = mystrdup(cldata[cl].user);
+ cldata[cl].authfrom = cldata[cl].instance->in;
+ sendto_ircd("U %d %s %u %s", cl, cldata[cl].itsip, cldata[cl].itsport,
+ cldata[cl].authuser);
+ return -1; /* done */
+}
+
+/*
+ * passwd_work
+ *
+ * This procedure is called whenever there's new data in the buffer.
+ * Returns 0 if everything went fine, and there is more work to be done,
+ * Returns -1 if the module has finished its work (and cleaned up).
+ *
+ * It is responsible for sending error messages where appropriate.
+ */
+int
+passwd_work(cl)
+u_int cl;
+{
+ return -1;
+}
+
+/*
+ * passwd_clean
+ *
+ * This procedure is called whenever the module should interrupt its work.
+ * It is responsible for cleaning up any allocated data, and in particular
+ * closing file descriptors.
+ */
+void
+passwd_clean(cl)
+u_int cl;
+{
+}
+
+/*
+ * passwd_timeout
+ *
+ * This procedure is called whenever the timeout set by the module is
+ * reached.
+ *
+ * Returns 0 if things are okay, -1 if authentication was aborted.
+ */
+int
+passwd_timeout(cl)
+u_int cl;
+{
+}
+
+static aModule Module_passwd =
+ { "passwd", passwd_init, passwd_release, passwd_stats,
+ passwd_start, passwd_work, passwd_timeout, passwd_clean };
+
+aModule *
+passwd_load()
+{
+ return &Module_passwd;
+}
diff --git a/contrib/tkserv/CHANGES b/contrib/tkserv/CHANGES
new file mode 100644
index 0000000..e983ded
--- /dev/null
+++ b/contrib/tkserv/CHANGES
@@ -0,0 +1,37 @@
+1.0.0b8 : Ported TkServ to C.
+1.0.0b8 : It is now possible to QUIT the service via SQUERY.
+1.0.0b9 : Added default expiration if no time value was specified (koopal)
+1.0.0b9 : It is now possible to remove existing tklines "manually" (koopal)
+1.0.0b10: the pwd of the service went to the cmd line (koopal)
+1.0.0 : Official release of TkServ
+1.0.1 : Applied some fixes to make TkServ work under BSDI (primetime)
+1.0.1 : no address of a local var is returned anymore :) (primetime)
+1.0.2 : Added forgotten tkserv.access.example to the package (primetime)
+1.0.2 : IP masks (x.x.x.*) are now also be recognized as IPs (primetime)
+1.0.2 : hostnames are now also possible in the restriction field (primetime)
+1.0.3 : DEBUGGING is now functional and displays all service<->server
+ traffic to the standard output
+1.0.3 : TkServ should now also compile under Irix (Szern)
+1.0.3 : IP restrictions are now also possible in the access file
+ - wildcards are allowed (Szern)
+1.0.3 : Wildcards do now also work in the oper u@h field of the access
+ file (Szern)
+1.0.3 : Replaced FNM_CASEFOLD by FNM_NOESCAPE
+1.0.4 : ':' is not allowed anymore in the TKLINE cmd line (Eumel)
+1.0.4 : Fixed lame bug which kept tklines active altho already expired (Eumel)
+1.0.5 : TkServ should now also compile under Redhat Linux. (Tero)
+1.0.5 : a few changes for ircd/contrib compliancy
+1.0.5 : the semantics for the access file have changed (check out README)
+1.0.5 : it is now possible to disallow tklining of specific hosts (tumble)
+1.0.6 : Added leading colons for the /squery commands (tumble)
+1.0.6 : Added a hint concerning the distribution to the README
+1.0.6 : it's now possible to decide whether a user needs to be opered or
+ not when accessing TkServ
+1.0.7 : TkServ won't crash anymore if a bad lifetime is given (Tero)
+1.0.8 : A few changes to make TkServ fit better into ircd/contrib.
+1.0.8 : TkServ now sends out a hint when it receives an unknown cmd
+ (Fusion & viha)
+1.0.9 : renamed the function logf due to a conflict in *BSD? (Virginie)
+1.0.9 : General rename of some tkserv functions
+1.2 : Password migration from cmd line to conf file (Earthpig)
+1.2 : TkServ now daemonizes itself. (Earthpig)
diff --git a/contrib/tkserv/CREDITS b/contrib/tkserv/CREDITS
new file mode 100644
index 0000000..617fb16
--- /dev/null
+++ b/contrib/tkserv/CREDITS
@@ -0,0 +1,60 @@
+Thanks to the following people, who have contributed in one way or another
+to the development of TkServ:
+
+- Andre 'koopal' Koopal
+ + beta testing
+ + default expiration
+ + removal of tklines before expiration (manual removal)
+ + password migration from config.h to cmd line
+
+- Asu 'primetime' Pala
+ + fix for BSDI
+ + IP recognition bug
+ + forgotten tkserv.access.example file
+ + hostnames in the restriction field of the access file
+
+- 'Szern'
+ + fix for Irix
+ + IPs in the restriction field
+ + wildcards in the u@h oper field of the access file
+
+- Michael 'Eumel' Neumayer
+ + colons are not allowed anymore outside the password parameter
+ + expired tklines do now get removed really automatically
+
+- Tero 'Tero' Saari
+ + RedHat Linux cludge
+ + no crashes anymore when a bad lifetime is given
+
+- Andrew ´Earthpig´ Snare
+ + password back to conf file (more secure)
+ + automatic daemonization
+
+- 'tumble'
+ + possibility to explicitly disallow specific hosts from getting tklined
+ + leading colons for the /squery commands in the README
+
+- Kristian 'Fusion' Bergmann & 'viha'
+ + Sending out of a hint when an unknown command is received
+
+- 'Virginie'
+ + rename of the logf function because of conflict under *BSD?
+
+- 'jbn'
+ + expiration of tklines / default expiration
+
+- The EPIC Software Labs
+ + for their nice ircII-EPIC client from which i took some networking
+ stuff, see http://www.snafu.de/~kl/epic/index.html for more about
+ epic
+
+- Kai 'Oswald' Seidler
+ + for his NoteServ, on which TkServ is based and which has helped me a
+ lot in making TkServ, see http://oswald.pages.de for more
+
+- Christophe 'syrk' Kalt and Vesa 'Vesa' Ruokonen
+ + for giving me the impulse to this kind of service :)
+
+
+And finally thanks to all the Linux people out there for contributing
+to the world's best OS! :-)
diff --git a/contrib/tkserv/INSTALL b/contrib/tkserv/INSTALL
new file mode 100644
index 0000000..9bc69bd
--- /dev/null
+++ b/contrib/tkserv/INSTALL
@@ -0,0 +1,20 @@
+How to install TkServ
+---------------------
+
+Yes, there isn't any configure yet. That's why the compilation on some
+machines could be difficult because you may have to include one or two
+libraries by hand. I'll try to create a configure file in the future.
+
+Up to now, i've only compiled TkServ on Linux, Solaris and Irix. I hope you
+won't get into trouble when trying to compile on another OS. But everyone
+should be running Linux anyway. :-)
+
+So here we go:
+
+1. Unpack and untar the archive. (which you might already have done :)
+2. RTFM. :->
+3. run "./configure" (if you haven't already done so)
+3. Edit the tkconf.h file.
+4. "make tkserv"
+
+Good luck, Kasi
diff --git a/contrib/tkserv/README b/contrib/tkserv/README
new file mode 100644
index 0000000..a7b7c47
--- /dev/null
+++ b/contrib/tkserv/README
@@ -0,0 +1,290 @@
+Welcome to TkServ !
+=+=+=+=+=+=+=+=+=+=
+
+This program is released under the GNU General Public License.
+A copy of this license is included in the distribution.
+
+I) Introduction
+---------------
+
+TkServ is a so-called temporary k-line service.
+
+[If you don't know what a service is, consult the documentation which comes
+along with the irc2.10.x package (if you don't know services, you shouldn't
+be reading this anyway ;).]
+
+This is what TkServ does - roughly: On request, it adds a given k-line
+pattern to the server's k-line list and (sooner or later, see below) then
+removes it. The adding/removing is done via the ircd.conf file.
+
+The purpose/advantages of a temporary k-line service:
+
+ - it facilitates the process of k-lining users
+ - added k-lines disappear automatically (see below)
+ - it allows _also_ remote users (only if they are listed in the access
+ file of the service - see below) to temp k-line users on the respective
+ server
+ - it allows people to specify a duration for each tkline
+ - it is more effective than /kill but practically as easy to use
+ - it could therefore act as a replacement for the /kill command - which in
+ fact is more or less its longtime goal...
+
+
+II) Security concerns
+---------------------
+
+Of course, when allowing remote "access" to the ircd.conf file, the main
+concern of most admins is security. Therefore, here's a list of the
+procedures used by TkServ to ensure that only authorized users may add
+temporary k-lines to the server's conf file [origin == the person who is
+sending a request]:
+
+- the origin's user@host has to match one of the u@h's in the tkserv.access
+ file (case sensitive!)
+- the origin has always to specify a password which has to match the
+ password that belongs to the corresponding user@host in the access file
+ (case sensitive!)
+- the origin has to be opered, if you want to
+- it is possible to allow an authorized user to only tkline given hostnames
+ or TLDs
+
+Especially the third point equals to strong security, because: Could you
+imagine any cracker who has gained O: on some server, wasting his time on
+trying to get access to a remote server's temp k-line list instead of
+playing around with his O: ? See, neither could i. ;-)
+
+
+III) Installation
+-----------------
+
+A) Precondition
+
+ The only thing you need in order to run TkServ is an "ircd.conf" file,
+ an "ircd.pid" file and an IRC server which has been compiled with
+ USE_SERVICES #define'd in the config.h file (!).
+
+B) Editing the configuration file (config.h)
+
+ You have to change the following entries in the config file:
+
+ TKSERV_ADMIN_NAME (your real name)
+ TKSERV_ADMIN_CONTACT (mail address)
+ TKSERV_ADMIN_OTHER (your nick, for example)
+
+ TKSERV_NAME (the name of the service appearing on /SERVLIST)
+ TKSERV_DESC (a neato description :)
+ TKSERV_DIST (the distribution of the service)
+
+| A few words to the distribution of the service. Here are the pros &
+| contras of a global TkServ from my point of view:
+|
+| [contra] It makes the /servlist become unnecessarily big and less handy
+| [contra] You can detect also local services by /trace'ing a server
+| [contra] You can access a local TkServ by setting up a special client
+| on the concerned server (maybe with a special I: line)
+|
+| [pro] A global TkServ is more comfortable to use for remote users.
+| [pro] If you want to see which server is running one (for e.g. to
+| request access to it), you do only have to do a /servlist.
+
+ TKSERV_PATH (the abs. path to your ircd.conf, no trailing slash please!)
+ TKSERV_LOGFILE (the abs. path to your tkserv logfile, no trailing slash)
+ TKSERV_ACCESSFILE (the abs. path to your tkserv access file, ditto)
+
+ TKSERV_DEBUG (for debugging only, displayes traffic to standard output)
+
+C) Compiling the source
+
+TkServ will be compiled along with the irc server.
+
+D) Setting up the access file: tkserv.access
+
+ If anyone should be authorized to use TkServ on your server, he/she has
+ to figure in the access file. The format of it is:
+
+| [!]<user@host.domain.tld><SPACE><password><SPACE>[<FQDN || IP>]<RETURN>
+|
+| Which means:
+| [!]<user@host.domain.tld> <password> [<FQDN || IP>]
+|
+| A '!' before the u@h means that the specified user doesn't need to be
+| opered when accessing TkServ. Before the FQDN/IP, it means that the user
+| is not allowed to tkline that given FQDN/IP. See below.
+|
+| Examples:
+| (1) foo@bar.com foo-pass
+| (2) some@user.on.the.net some.passw1 *.net,207.128.*
+| another@user.foo.com Akfdsv.df *netcom.com,*.ca
+| you@get.the.picture.org ditto. *.org,dialup*.lamer.net,194.44.44.*
+| oper@dialup*.dialup.net pwd1.2 *dialup.net,145.44.*,*.fr
+| (3) !luser@doesnt.need.to.be.opered.org bleh !elite.org,*.org
+| (4) !~luser@no.ident.no.oper.com yo *.com
+|
+| (1) Any oper who is running identd and whose userhost equals to
+| foo@bar.com can tkline everyone if supplied password equals
+| foo-pass.
+| (2) Any oper with identd whose u@h equals to some@user.on.the.net
+| can tkline everyone whose TLD is ".net" or who is IRCing within
+| the IP subnet 207.128.*, if correct password is given.
+| (3) Any luser (no need to be opered) whose u@h/passwd equals can tkline
+| everyone whose TLD is ".org" except the host "elite.org"
+| ("*@elite.org").
+|
+| Generally, you should be aware of the fact that if you put something
+| in the FQDN/IP field, then you automatically restrict the access.
+| Therefore, you must then also indicate all the allowed FQDNs/IPs.
+|
+| The access routines are pretty flexible. So pay attention in what order
+| you allow/disallow what. :)
+|
+| Other examples:
+| !foo@bar.com foo-pass !*.net,*.com,!*.com
+| some@user.on.the.net some.passw1 *.net,207.128.*,!127.0.0.*
+| another@user.foo.com Akfdsv.df *netcom.com,!*trends.ca,*.ca
+|
+| Notice that if you allow "*.de" and after it you forbid "blah.de",
+| this won't work. The oper will still be able to tkline blah.de since
+| "*.de" appears before "!blah.de" in the access field.
+|
+| Are we confused, yet? ;-)
+
+ For some other examples, refer to the tkserv.access.example file, which
+ is included in the package.
+
+ The u@h field for the user can also contain wildcards, as you can see.
+
+| If you specify an FQDN (or several of them in a comma-separated
+| list), the concerned user will only be allowed to tkline users from
+| that FQDN(s). Everything is done via 'wildcard-matching' (if any).
+| I.e. that "home.blah.de" matches only "*@home.blah.de". If you want
+| to allow a whole TLD, you have to do this by putting "*.tld" in the
+| access field.
+
+! An empty FQDN/IP field means that the concerned user can tkline everyone. !
+ [Except *@* that is.]
+
+E) Setting up the S: line in your ircd.conf file
+
+ If you're not yet familiar with S: lines, consult the documentation of
+ the ircd package.
+
+ S:<host>:<password>:<name>:33554432:<class>
+
+ <host> is the FQDN from which the service will connect to the server.
+
+ The service type 33554432 is mandatory! Currently, TkServ will refuse
+ any other service type. [Actually, it won't, but it's good to think that
+ it will... ;-]
+
+ The service class should refer to an existing class (according to the
+ documentation :).
+
+F) Starting TkServ
+
+ The command line syntax of TkServ looks like this:
+
+ tkserv <server> <port> <password>
+ or
+ tkserv <Unix domain socket> <password>
+
+ Example:
+
+ tkserv localhost 6667 my-serv.pass
+
+ Where <server> is the address of your IRC server, <port> the port to
+ which TkServ will connect and <password> the password for the service.
+
+G) Adding temp k-klines (TKLINE)
+
+ Provided that TkServ recognizes you, you can add temporary k-lines via
+ the TKLINE command, which has three different variants:
+
+ (1) :TKLINE <password> <lifetime in hours> <user@host> <reason>
+ (2) :TKLINE <password> <user@host> <reason>
+ (3) :TKLINE <password> -1 <user@host>
+
+ (1) adds a tkline for <u@h> with an expire time of <lifetime> hours and
+ with the reason <reason>.
+ (2) adds a tkline for <u@h> with the default expire time (2 hours) and
+ with the reason <reason>.
+ (3) removes any existing tklines found for <user@host>.
+
+ Examples:
+
+ (1) :TKLINE my.pass 5 lamer@lamers.org dont flood
+ (2) :TKLINE blah. *@foo.com stop spamming
+ (3) :TKLINE my.pass -1 lamer@lamers.org
+
+ <lifetime> must be > 0 and < 168.
+
+| [If your client doesn't support SQUERY, the entire cmd line has to be:
+| "/quote squery <name of tkserv> :tkline ...". If it does support it,
+| then "/squery <name of tkserv> tkline ..." should do it.]
+|
+| [And yes, ircII-EPIC4 supports SQUERY and SERVLIST! ;-)]
+|
+| If a non-opered user tries to use TKLINE without having a matching entry
+| in the access file, he gets "Only IRC-Operators may use this command" as
+| an error message. This is not correct anymore, but i didn't bother to
+| change it (since it may prevent kids from playing around with TkServ :).
+
+H) Online help/info
+
+TkServ does also have a little online help which is accessible via
+"/squery <service name> help".
+
+I) Quitting the service
+
+To make TkServ quit IRC you have to send him the following SQUERY:
+
+QUIT <password>
+
+Where <password> corresponds to password that you indicated at startup.
+Be aware that anyone who knows that password can make your TkServ quit.
+
+J) Debugging
+
+If you #define TKSERV_DEBUG in the config.h file, everything which is sent
+to the server from the service and from the server to the service will be
+displayed to the standard output.
+
+
+IV) Misc, or what goes where
+
+TkServ will create the following permanent files in your ircd directory:
+
+ircd.conf.tkserv (backup of the ircd.conf file)
+tkserv.log (TkServ's log file)
+
+The latter will contain most of the error messages (in case something goes
+wrong - what we all don't hope ;) as well as logs of successful TKLINE
+requests.
+
+The tkserv.access file has to be in the directory specified in the config.h
+file. If no tkserv.access file is found, no one will be able to add temp
+k-lines.
+
+Now and then you should zero your TkServ logfile because this won't happen
+by itself. =)
+
+
+V) Credits
+
+See the CREDITS file.
+
+
+VI) Bugs, comments, suggestions...
+
+Send everything to kl@snafu.de or to Kasi@IRC.
+
+
+VII) The TkServ-mailinglist
+
+There's now a mailinglist for TkServ out there. It is used for general
+announcements (bugs, new releases, etc.) concerning TkServ. Since it's a
+read-only mailinglist, it doesn't have much traffic. You can subscribe to
+it by sending a mail to tkserv@kl.Snafu.DE with "subscribe" in the subject
+or in the body of the mail.
+
+
+Enjoy, -Kasi
diff --git a/contrib/tkserv/proto.h b/contrib/tkserv/proto.h
new file mode 100644
index 0000000..edf23b7
--- /dev/null
+++ b/contrib/tkserv/proto.h
@@ -0,0 +1,24 @@
+void sendto_server(char *buf, ...);
+void sendto_user(char *text, ...);
+void process_server_output(char *line);
+void parse_server_output(char *buffer);
+int server_output(int fd, char *buffer);
+
+void service_pong(void);
+void service_notice(char **args);
+void service_squery(char **args);
+int service_userhost(char *args);
+void squery_help(char **args);
+void squery_tkline(char **args);
+void squery_quit(char **args);
+
+void sendlog(char *text, ...);
+char *ts(void);
+
+int is_opered(void);
+int is_authorized(char *pwd, char *host);
+
+void exec_cmd(char *cmd, ...);
+int add_tkline(char *host, char *user, char *reason, int lifetime);
+int check_tklines(char *host, char *user, int lifetime);
+void rehash(int what);
diff --git a/contrib/tkserv/tkserv.access.example b/contrib/tkserv/tkserv.access.example
new file mode 100644
index 0000000..4269c93
--- /dev/null
+++ b/contrib/tkserv/tkserv.access.example
@@ -0,0 +1,10 @@
+foo@bar.com foo.pass *netcom.com,*.net
+someone@SomeOrg.org aldkf.23 eleet.org,165.55.45.*
+~blah@ppp*.blah.de igh.pass
+user@users.Online.net ukuk.ua1
+some@ip*.user.com blah 204.85.*,194.55.43.*
+you.get@the.picture.com pass ppp*.lame.com,*.org,dialin*.whitehouse.gov,*.net,*.uk
+disallow@something.com bleh !*.de,!*.fr,*.net,*.org,*.com,!195.144.45.*
+disallow@another.host.fr yo !hugo.fr,!admin.irc.fr,*.fr,*.ca,195.55.66.*
+!luser@who.doesnt.need.to.be.opered.de yeh *.de
+
diff --git a/contrib/tkserv/tkserv.c b/contrib/tkserv/tkserv.c
new file mode 100644
index 0000000..92f281d
--- /dev/null
+++ b/contrib/tkserv/tkserv.c
@@ -0,0 +1,1022 @@
+/*
+** Powered by Linux. :-)
+**
+** Copyright (c) 1998 Kaspar 'Kasi' Landsberg, <kl@berlin.Snafu.DE>
+**
+** File : tkserv.c v1.2
+** Author : Kaspar 'Kasi' Landsberg, <kl@snafu.de>
+** Desc. : Temporary K-line Service.
+** For further info see the README file.
+** Location : http://www.snafu.de/~kl/tkserv
+** Usage : tkserv <server> <port>
+** E.g. : tkserv localhost 6667
+**
+** This program is distributed under the GNU General Public License 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 details.
+**
+** Note: The C version of this service is based on parts of the
+** ircII-EPIC IRC client by the EPIC Software Labs and on
+** the NoteServ service by Kai 'Oswald' Seidler - see
+** http://oswald.pages.de for more about NoteServ.
+** Thanks to both. =)
+**
+** PS: Casting rules the world! (doh)
+*/
+
+#include "os.h"
+#undef strcasecmp
+#include "config.h"
+#include "tkconf.h"
+#include "proto.h"
+
+/* Max. kill reason length */
+#define TKS_MAXKILLREASON 128
+
+/* Used max. length for an absolute Unix path */
+#define TKS_MAXPATH 128
+
+/* Max. buffer length (don't change this?) */
+#define TKS_MAXBUFFER 8192
+
+/* don't change this either(?) */
+#define TKS_MAXARGS 250
+
+/* The version information */
+#define TKS_VERSION "Hello, i'm TkServ v1.2."
+
+static char *nuh;
+FILE *tks_logf;
+int fd = -1, tklined = 0;
+
+/*
+** Returns the current time in a formated way.
+** Yes, "ts" stands for "time stamp". I know,
+** this hurts, but i couldn't find any better
+** description. ;->
+*/
+char *tks_ts(void)
+{
+ static char tempus[256];
+ time_t now;
+ struct tm *loctime;
+
+ /* Get the current time */
+ now = time(NULL);
+
+ /* Convert it to local time representation */
+ loctime = localtime(&now);
+
+ strftime(tempus, 256, "@%H:%M %m/%d", loctime);
+
+ return(tempus);
+}
+
+/* logging routine, with timestamps */
+void tks_log(char *text, ...)
+{
+ char txt[TKS_MAXBUFFER];
+ va_list va;
+
+ tks_logf = fopen(TKSERV_LOGFILE, "a");
+ va_start(va, text);
+ vsprintf(txt, text, va);
+
+ if (tks_logf != NULL)
+ fprintf(tks_logf, "%s %s\n", txt, tks_ts());
+ else
+ {
+ perror(TKSERV_LOGFILE);
+ va_end(va);
+ return;
+ }
+
+ va_end(va);
+ fclose(tks_logf);
+}
+
+/* an optimized system() function */
+void exec_cmd(char *cmd, ...)
+{
+ char command[TKS_MAXBUFFER];
+ va_list va;
+
+ va_start(va, cmd);
+ vsprintf(command, cmd, va);
+ system(command);
+ va_end(va);
+}
+
+/* sends a string (<= TKS_MAXBUFFER) to the server */
+void sendto_server(char *buf, ...)
+{
+ char buffer[TKS_MAXBUFFER];
+ va_list va;
+
+ va_start(va, buf);
+ vsprintf(buffer, buf, va);
+ write(fd, buffer, strlen(buffer));
+ va_end(va);
+
+#ifdef TKSERV_DEBUG
+ printf("%s", buffer);
+#endif
+}
+
+/* sends a NOTICE to the SQUERY origin */
+void sendto_user(char *text, ...)
+{
+ char *nick, *ch;
+ char txt[TKS_MAXBUFFER];
+ va_list va;
+
+ nick = (char *) strdup(nuh);
+ ch = (char *) strchr(nick, '!');
+ *ch = '\0';
+
+ va_start(va, text);
+ vsprintf(txt, text, va);
+ sendto_server("NOTICE %s :%s\n", nick, txt);
+ va_end(va);
+}
+
+void process_server_output(char *line)
+{
+ char *ptr;
+ static char *args[TKS_MAXARGS];
+ int i, argc = 0;
+
+ while ((ptr = (char *) strchr(line, ' ')) && argc < TKS_MAXARGS)
+ {
+ args[argc] = line;
+ argc++;
+ *ptr = '\0';
+ line = ptr + 1;
+ }
+
+ args[argc] = line;
+
+ for (i = argc + 1; i < TKS_MAXARGS; i++)
+ args[i] = "";
+
+ /*
+ ** After successfull registering, backup the ircd.conf file
+ ** and set the perms of the log file -- the easy way :)
+ */
+ if ((*args[0] == ':') && (!strcmp(args[1], "SERVSET")))
+ {
+ chmod(TKSERV_ACCESSFILE, S_IRUSR | S_IWRITE);
+ chmod(TKSERV_LOGFILE, S_IRUSR | S_IWRITE);
+ exec_cmd("cp "CPATH" "TKSERV_IRCD_CONFIG_BAK);
+ tks_log("Registration successful.");
+ }
+
+ /* We do only react upon PINGs, SQUERYs and &NOTICES */
+ if (!strcmp(args[0], "PING"))
+ service_pong();
+
+ if ((*args[0] == ':') && (!strcmp(args[1], "SQUERY")))
+ service_squery(args);
+
+ if (!strcmp(args[0], "&NOTICES"))
+ service_notice(args);
+}
+
+/* reformats the server output */
+void parse_server_output(char *buffer)
+{
+ char *ch, buf[TKS_MAXBUFFER];
+ static char tmp[TKS_MAXBUFFER];
+
+ /* server sent an empty line, so just return */
+ if (!buffer && !*buffer)
+ return;
+
+ while ((ch = (char *) strchr(buffer, '\n')))
+ {
+ *ch = '\0';
+
+ if (*(ch - 1) == '\r')
+ *(ch - 1) == '\0';
+
+ sprintf(buf, "%s%s", tmp, buffer);
+ *tmp = '\0';
+ process_server_output(buf);
+ buffer = ch + 1;
+ }
+
+ if (*buffer)
+ strcpy(tmp, buffer);
+}
+
+/* reads and returns output from the server */
+int server_output(int fd, char *buffer)
+{
+ int n = read(fd, buffer, TKS_MAXBUFFER);
+ buffer[n] = '\0';
+
+#ifdef TKSERV_DEBUG
+ printf("%s", buffer);
+#endif
+
+ return(n);
+}
+
+/* is the origin of the /squery opered? */
+int is_opered(void)
+{
+ char *nick, *ch, *token, *u_num, *userh;
+ char buffer[TKS_MAXBUFFER];
+
+ nick = (char *) strdup(nuh);
+ ch = (char *) strchr(nick, '!');
+ *ch = '\0';
+ sendto_server("USERHOST %s\n", nick);
+
+ /* get the USERHOST reply (hopefully) */
+ server_output(fd, buffer);
+
+ token = (char *) strtok(buffer, " ");
+ token = (char *) strtok(NULL, " ");
+ u_num = (char *) strdup(token);
+ token = (char *) strtok(NULL, " ");
+ token = (char *) strtok(NULL, " ");
+ userh = (char *) strdup(token);
+
+ /* if we got the USERHOST reply, perform the check */
+ if (!strcmp(u_num, "302"))
+ {
+ char *ch;
+ ch = (char *) strchr(userh, '=') - 1;
+
+ /* is the origin opered? */
+ if (*ch == '*')
+ {
+ char *old_uh, *new_uh, *ch;
+
+ old_uh = (char *) (strchr(nuh, '!') + 1);
+ new_uh = (char *) (strchr(userh, '=') + 2);
+
+ if (ch = (char *) strchr(new_uh, '\r'))
+ *ch = '\0';
+
+ /* Does the u@h of the USERHOST reply correspond to the u@h of our origin? */
+ if (!strcmp(old_uh, new_uh))
+ return(1);
+ else
+ /*
+ ** race condition == we sent a USERHOST request and got the USERHHOST reply,
+ ** but this reply doesn't correspond to our origin of the SQUERY --
+ ** this should never happen (but never say never ;)
+ */
+ sendto_user("A race condition has occured -- please try again.");
+ }
+ }
+ else
+ /*
+ ** race condition == we sent a USERHOST request but the next message from
+ ** the server was not a USERHOST reply (usually due to lag)
+ */
+ sendto_user("A race condition has occured -- please try again (and ignore the following error message).");
+
+ return(0);
+}
+
+/*
+** Look for an entry in the access file and
+** see if the origin needs to be opered
+*/
+int must_be_opered()
+{
+ FILE *fp;
+
+ /* if the access file exists, check for auth */
+ if ((fp = fopen(TKSERV_ACCESSFILE, "r")) != NULL)
+ {
+ char buffer[TKS_MAXBUFFER];
+ char *access_uh, *token, *uh;
+
+ while (fgets(buffer, TKS_MAXBUFFER, fp))
+ {
+ uh = (char *) (strchr(nuh, '!') + 1);
+ token = (char *) strtok(buffer, " ");
+
+ if (token)
+ access_uh = (char *) strdup(token);
+
+ /* check for access file corruption */
+ if (!access_uh)
+ {
+ tks_log("Corrupt access file. RTFM. :-)");
+
+ return(0);
+ }
+
+ /* do we need an oper? */
+ if (*access_uh == '!')
+ {
+ if (!fnmatch((char *) (strchr(access_uh, '!') + 1), uh, 0))
+ return(0);
+ }
+ }
+ }
+ else
+ tks_log("%s not found.", TKSERV_ACCESSFILE);
+
+ return(1);
+}
+
+/* check whether origin is authorized to use the service */
+int is_authorized(char *pwd, char *host)
+{
+ FILE *fp;
+
+ /* if the access file exists, check for authorization */
+ if ((fp = fopen(TKSERV_ACCESSFILE, "r")) != NULL)
+ {
+ char buffer[TKS_MAXBUFFER];
+ char *access_uh, *access_pwd;
+ char *token, *uh, *ch, *tlds = NULL;
+
+ while (fgets(buffer, TKS_MAXBUFFER, fp))
+ {
+ uh = (char *) (strchr(nuh, '!') + 1);
+ token = (char *) strtok(buffer, " ");
+
+ if (token)
+ access_uh = (char *) strdup(token);
+
+ if (*access_uh == '!')
+ access_uh = (char *) (strchr(access_uh, '!') + 1);
+
+ token = (char *) strtok(NULL, " ");
+
+ if (token)
+ access_pwd = (char *) strdup(token);
+
+ token = (char *) strtok(NULL, " ");
+
+ if (token)
+ tlds = (char *) strdup(token);
+ else
+ if (ch = (char *) strchr(access_pwd, '\n'))
+ *ch = '\0';
+
+ /* check for access file corruption */
+ if (!access_uh || !access_pwd)
+ {
+ tks_log("Corrupt access file. RTFM. :-)");
+
+ return(0);
+ }
+
+ /* check uh, pass and TLD */
+ if (!fnmatch(access_uh, uh, 0))
+ if (!strcmp(pwd, access_pwd))
+ if (!tlds)
+ return(1);
+ else
+ {
+ char *token, *ch;
+
+ /* blah */
+ if (ch = (char *) strchr(tlds, '\n'))
+ *ch = '\0';
+
+ token = (char *) strtok(tlds, ",");
+
+ /* '!' negates the given host/domain -> not allowed to tkline */
+ if (*token == '!')
+ {
+ if (!fnmatch(((char *) strchr(token, '!') + 1), host, 0))
+ {
+ sendto_user("You are not allowed to tkline \"%s\",", host);
+ return(0);
+ }
+ }
+ else if (!fnmatch(token, host, 0))
+ return(1);
+
+ /* walk thru the list */
+ while (token = (char *) strtok(NULL, ","))
+ {
+ if (*token == '!')
+ {
+ if (!fnmatch((char *) (strchr(token, '!') + 1), host, 0))
+ {
+ sendto_user("You are not allowed to tkline \"%s\",", host);
+ return(0);
+ }
+ }
+ else if (!fnmatch(token, host, 0))
+ return(1);
+ }
+
+ sendto_user("You are not allowed to tkline \"%s\".", host);
+ }
+ }
+
+ }
+ else
+ tks_log("%s not found.", TKSERV_ACCESSFILE);
+
+ return(0);
+}
+
+/*************** ircd.conf section ****************/
+
+/* Appends new tklines to the ircd.conf file */
+int add_tkline(char *host, char *user, char *reason, int lifetime)
+{
+ FILE *iconf;
+
+ if (iconf = fopen(CPATH, "a"))
+ {
+ time_t now;
+
+ now = time(NULL);
+ fprintf(iconf, "K:%s:%s:%s:0 # %d %u tkserv\n",
+ host, reason, user, lifetime, now);
+ fclose(iconf);
+ rehash(1);
+ tks_log("K:%s:%s:%s:0 added for %d hour(s) by %s.",
+ host, reason, user, lifetime, nuh);
+
+ return(1);
+ }
+ else
+ tks_log("Couldn't write to "CPATH);
+
+ return(0);
+}
+
+/* Check for expired tklines in the ircd.conf file */
+int check_tklines(char *host, char *user, int lifetime)
+{
+ FILE *iconf, *iconf_tmp;
+
+ if ((iconf = fopen(CPATH, "r")) && (iconf_tmp = fopen(TKSERV_IRCD_CONFIG_TMP, "w")))
+ {
+ int count = 0, found = 0;
+ time_t now;
+ char buffer[TKS_MAXBUFFER];
+ char buf_tmp[TKS_MAXBUFFER];
+
+ /* just in case... */
+ chmod(TKSERV_IRCD_CONFIG_TMP, S_IRUSR | S_IWRITE);
+
+ now = time(NULL);
+
+ while (fgets(buffer, TKS_MAXBUFFER, iconf))
+ {
+ if ((*buffer != 'K') || (!strstr(buffer, "tkserv")))
+ fprintf(iconf_tmp, buffer);
+ else
+ {
+ /*
+ ** If lifetime has a value of -1, look for matching
+ ** tklines and remove them. Otherwise, perform
+ ** the expiration check.
+ */
+ if (lifetime == -1)
+ {
+ char *token;
+ char buf[TKS_MAXBUFFER];
+
+ strcpy(buf, buffer);
+ token = (char *) strtok(buf, ":");
+ token = (char *) strtok(NULL, ":");
+
+ if (!strcasecmp(token, host))
+ {
+ token = (char *) strtok(NULL, ":");
+ token = (char *) strtok(NULL, ":");
+
+ if (!strcasecmp(token, user))
+ {
+ count++;
+ found = 1;
+ }
+ else
+ fprintf(iconf_tmp, buffer);
+ }
+ else
+ fprintf(iconf_tmp, buffer);
+ }
+ else
+ {
+ char *ch, *token;
+ char buf[TKS_MAXBUFFER];
+ unsigned long int lifetime, then;
+
+ strcpy(buf, buffer);
+ ch = (char *) strrchr(buf, '#');
+ token = (char *) strtok(ch, " ");
+ token = (char *) strtok(NULL, " ");
+ lifetime = strtoul(token, NULL, 0);
+ token = (char *) strtok(NULL, " ");
+ then = strtoul(token, NULL, 0);
+
+ if (!(((now - then) / (60 * 60)) >= lifetime))
+ fprintf(iconf_tmp, buffer);
+ else
+ found = 1;
+ }
+ }
+ }
+
+ fclose(iconf);
+ fclose(iconf_tmp);
+ exec_cmd("cp %s %s", TKSERV_IRCD_CONFIG_TMP,CPATH);
+ unlink(TKSERV_IRCD_CONFIG_TMP);
+
+ if (found)
+ rehash(-1);
+
+ return(count);
+ }
+ else
+ tks_log("Error while checking for expired tklines...");
+}
+
+/* reloads the ircd.conf file -- the easy way */
+void rehash(int what)
+{
+ exec_cmd("kill -HUP `cat "PPATH"`");
+
+ if (what != -1)
+ tklined = what;
+}
+
+/*************** end of ircd.conf section **************/
+
+/*************** The service command section *************/
+
+/* On PING, send PONG and check for expired tklines */
+void service_pong(void)
+{
+ sendto_server("PONG %s\n", TKSERV_NAME);
+ check_tklines(NULL, NULL, 0);
+}
+
+/*
+** If we get a rehash, tell the origin that the tklines are active/removed
+** and check for expired tklines...
+*/
+void service_notice(char **args)
+{
+ if ((!strcmp(args[4], "reloading") && (!strcmp(args[5], TKSERV_IRCD_CONF))) ||
+ (!strcmp(args[3], "rehashing") && (!strcmp(args[4], "Server"))))
+ {
+ if (tklined)
+ {
+ sendto_user("TK-line%s.", (tklined > 1) ? "(s) removed" : " active");
+ tklined = 0;
+ }
+ }
+}
+
+/* parse the received SQUERY */
+void service_squery(char **args)
+{
+ char *cmd, *ch;
+
+ nuh = (char *) strdup(args[0] + 1);
+ cmd = (char *) strdup(args[3] + 1);
+
+ if (ch = (char *) strchr(cmd, '\r'))
+ *ch = '\0';
+
+ if (!strcasecmp(cmd, "admin"))
+ {
+ sendto_user(TKSERV_ADMIN_NAME);
+ sendto_user(TKSERV_ADMIN_CONTACT);
+ sendto_user(TKSERV_ADMIN_OTHER);
+ }
+
+ else if (!strcasecmp(cmd, "help"))
+ squery_help(args);
+
+ else if (!strcasecmp(cmd, "info"))
+ {
+ sendto_user("This service is featuring temporary k-lines.");
+ sendto_user("It's available at http://www.snafu.de/~kl/tkserv.");
+ }
+
+ else if (!strcasecmp(cmd, "quit"))
+ squery_quit(args);
+
+ else if (!strcasecmp(cmd, "tkline"))
+ squery_tkline(args);
+
+ else if (!strcasecmp(cmd, "version"))
+ sendto_user(TKS_VERSION);
+
+ else
+ sendto_user("Unknown command. Try HELP.");
+}
+
+/* SQUERY HELP */
+void squery_help(char **args)
+{
+ char *ch, *help_about;
+
+ help_about = args[4];
+
+ if (help_about && *help_about)
+ {
+ if (ch = (char *) strchr(help_about, '\r'))
+ *ch = '\0';
+
+ if (!strcasecmp(help_about, "admin"))
+ sendto_user("ADMIN shows you the administrative info for this service.");
+
+ if (!strcasecmp(help_about, "help"))
+ sendto_user("HELP <command> shows you the help text for <command>.");
+
+ if (!strcasecmp(help_about, "info"))
+ sendto_user("INFO shows you a short description about this service.");
+
+ if (!strcasecmp(help_about, "tkline"))
+ {
+ sendto_user("TKLINE adds a temporary entry to the server's k-line list.");
+ sendto_user("TKLINE is a privileged command.");
+ }
+
+ if (!strcasecmp(help_about, "version"))
+ sendto_user("VERSION shows you the version information of this service.");
+ }
+ else
+ {
+ sendto_user("Available commands:");
+ sendto_user("HELP, INFO, VERSION, ADMIN, TKLINE.");
+ sendto_user("Send HELP <command> for further help.");
+ }
+}
+
+/* SQUERY TKLINE */
+void squery_tkline(char **args)
+{
+ int lifetime, i;
+ char *passwd, *pattern, *host, *ch, *user = "*";
+ char reason[TKS_MAXKILLREASON];
+
+ /* Before we go thru all this, make sure we don't waste our time... */
+ if (must_be_opered())
+ {
+ if (!is_opered())
+ {
+ sendto_user("Only IRC-Operators may use this command.");
+ return;
+ }
+ }
+
+ i = 5;
+
+ while (args[i] && *args[i])
+ {
+ if (strchr(args[i], ':'))
+ {
+ sendto_user("Colons are only allowed in the password.");
+ return;
+ }
+
+ i++;
+ }
+
+ if (args[5] && *args[5])
+ {
+ if (isdigit(*args[5]) || (*args[5] == '-'))
+ lifetime = atoi(args[5]);
+ else
+ {
+ sendto_user("The lifetime may only contain digits.");
+ return;
+ }
+ }
+ else
+ {
+ sendto_user("Usage: TKLINE <password> [<lifetime>] <u@h> <reason>");
+ return;
+ }
+
+ /* TKLINE <pass> <lifetime> <u@h> <reason> */
+ if ((lifetime > 0) && !(args[7] && *args[7]))
+ {
+ sendto_user("Usage: TKLINE <password> <lifetime> <u@h> <reason>");
+ return;
+ }
+
+ /* TKLINE <pass> <u@h> <reason> (default expiration) */
+ if ((lifetime == 0) && !(args[6] && *args[6]))
+ {
+ sendto_user("Usage: TKLINE <password> <u@h> <reason>");
+ return;
+ }
+
+ /* TKLINE <pass> -1 <u@h> (removal of tklines) */
+ if ((lifetime == -1) && !(args[6] && *args[6]))
+ {
+ sendto_user("Usage: TKLINE <password> -1 <u@h>");
+ return;
+ }
+
+ if ((lifetime >= 768) || (lifetime < -1))
+ {
+ sendto_user("<lifetime> must be greater than 0 and less than 768.");
+ return;
+ }
+
+ /* I don't want to get confused, so all this may be a bit redundant */
+
+ if (lifetime > 0)
+ {
+ passwd = args[4];
+ pattern = args[6];
+ strcpy(reason, args[7]);
+ i = 8;
+
+ /* I know... */
+ while(args[i] && *args[i])
+ {
+ strncat(reason, " ", TKS_MAXKILLREASON - strlen(reason) - 1);
+ strncat(reason, args[i], TKS_MAXKILLREASON - strlen(reason) - 1);
+ i++;
+ }
+
+ if (ch = (char *) strchr(reason, '\r'))
+ *ch = '\0';
+ }
+
+ if (lifetime == 0)
+ {
+ if (!(strchr(args[5], '@') || strchr(args[5], '*') ||
+ strchr(args[5], '.')))
+ {
+ sendto_user("<lifetime> must be greater than 0.");
+ return;
+ }
+
+ passwd = args[4];
+ lifetime = 2; /* Default lifetime */
+ pattern = args[5];
+ strcpy(reason, args[6]);
+ i = 7;
+
+ while(args[i] && *args[i])
+ {
+ strncat(reason, " ", TKS_MAXKILLREASON - strlen(reason) - 1);
+ strncat(reason, args[i], TKS_MAXKILLREASON - strlen(reason) - 1);
+ i++;
+ }
+
+ if (ch = (char *) strchr(reason, '\r'))
+ *ch = '\0';
+ }
+
+ if (lifetime == -1)
+ {
+ passwd = args[4];
+ pattern = args[6];
+
+ if (ch = (char *) strchr(pattern, '\r'))
+ *ch = '\0';
+ }
+
+
+ /* Don't allow "*@*" and "*" in the pattern */
+ if (!strcmp(pattern, "*@*") || !strcmp(pattern, "*"))
+ {
+ sendto_user("The pattern \"%s\" is not allowed.", pattern);
+ tks_log("%s tried to tkline/untkline \"%s\".", nuh, pattern);
+ return;
+ }
+
+ /* Split the pattern up into username and hostname */
+ if (ch = (char *) strchr(pattern, '@'))
+ {
+ host = (char *) (strchr(pattern, '@') + 1);
+ user = pattern;
+ *ch = '\0';
+ }
+ else /* user defaults to "*" */
+ host = pattern;
+
+ /*
+ ** Make sure there's a dot in the hostname.
+ ** The reason for this being that i "need" a dot
+ ** later on and that i don't want to perform
+ ** extra checks whether it's there or not...
+ ** Call this lazyness, but it also makes the service faster. ;-)
+ */
+ if (!strchr(host, '.'))
+ {
+ sendto_user("The hostname must contain at least one dot.");
+ return;
+ }
+
+ if (!is_authorized(passwd, host))
+ {
+ sendto_user("Authorization failed.");
+ return;
+ }
+
+ if (lifetime == -1)
+ {
+ int i;
+
+ i = check_tklines(host, user, lifetime);
+ sendto_user("%d tkline%sfor \"%s@%s\" found.", i,
+ (i > 1) ? "s " : " ", user, host);
+
+ if (i > 0)
+ rehash(2);
+ }
+ else
+ if (!add_tkline(host, user, reason, lifetime))
+ sendto_user("Error while trying to edit the "CPATH" file.");
+}
+
+/* SQUERY QUIT
+** Each time we receive a QUIT via SQUERY we check whether
+** the supplied password matches the one in the conf file or not.
+** If not, an error is sent out. If yes, we close the connection to
+** the server.
+*/
+void squery_quit(char **args)
+{
+ char *ch;
+
+ if (ch = (char *) strchr(args[4], '\r'))
+ *ch = '\0';
+
+ if (!strcmp(args[4], TKSERV_PASSWORD))
+ {
+ tks_log("Got QUIT from %s. Terminating.", nuh);
+ sendto_server("QUIT :Linux makes the world go round. :)\n");
+ }
+ else
+ {
+ sendto_user("I refuse to QUIT. Have a look at the log to see why.");
+ tks_log("Got QUIT from %s with wrong password. Continuing.", nuh);
+ }
+}
+
+/**************** End of service command section ***************/
+
+int main(int argc, char *argv[])
+{
+
+ char *host, *port, buffer[TKS_MAXBUFFER], last_buf[TKS_MAXBUFFER];
+ char tmp[TKS_MAXPATH];
+
+ int is_unix = (argv[1] && *argv[1] == '/');
+ int sock_type = (is_unix) ? AF_UNIX : AF_INET;
+ int proto_type = SOCK_STREAM;
+ int eof = 0;
+
+ struct in_addr LocalHostAddr;
+ struct sockaddr_in server;
+ struct sockaddr_in localaddr;
+ struct hostent *hp;
+ struct timeval timeout;
+
+ fd_set read_set;
+ fd_set write_set;
+
+ if ((is_unix) && (argc != 2))
+ {
+ fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
+ fprintf(stderr, " %s <Unix domain socket>\n", argv[0]);
+ exit(1);
+ }
+ else if (argc != 3)
+ {
+ fprintf(stderr, "Usage: %s <server> <port>\n", argv[0]);
+ fprintf(stderr, " %s <Unix domain socket>\n", argv[0]);
+ exit(1);
+ }
+
+ if (!strcmp(TKSERV_DIST, "*"))
+ {
+ printf("Your service has a global distribution. Please make sure that\n");
+ printf("you read the part about the service distribution in the README.\n");
+ }
+
+ tks_log("Welcome to TkServ. Lean back and enjoy the show...");
+
+ if ((fd = socket(sock_type, proto_type, 0)) < 0)
+ {
+ perror("socket");
+ exit(1);
+ }
+
+ /* copy the args into something more documentable */
+ host = argv[1];
+
+ if (!is_unix)
+ port = argv[2];
+
+ /* Unix domain socket */
+ if (is_unix)
+ {
+ struct sockaddr_un name;
+ memset(&name, 0, sizeof(struct sockaddr_un));
+ name.sun_family = AF_UNIX;
+ strcpy(name.sun_path, host);
+
+ if (connect(fd, (struct sockaddr *) &name, strlen(name.sun_path) + 2) == -1)
+ {
+ perror("connect");
+ close(fd);
+ exit(1);
+ }
+ }
+
+ memset(&localaddr, 0, sizeof(struct sockaddr_in));
+ localaddr.sin_family = AF_INET;
+ localaddr.sin_addr = LocalHostAddr;
+ localaddr.sin_port = 0;
+
+ if (bind(fd, (struct sockaddr *) &localaddr, sizeof(localaddr)))
+ {
+ perror("bind");
+ close(fd);
+ exit(1);
+ }
+
+ memset(&server, 0, sizeof(struct sockaddr_in));
+ memset(&LocalHostAddr, 0, sizeof(LocalHostAddr));
+
+ if (!(hp = gethostbyname(host)))
+ {
+ perror("resolv");
+ close(fd);
+ exit(1);
+ }
+
+ memmove(&(server.sin_addr), hp->h_addr, hp->h_length);
+ memmove((void *) &LocalHostAddr, hp->h_addr, sizeof(LocalHostAddr));
+ server.sin_family = AF_INET;
+ server.sin_port = htons(atoi(port));
+
+ if (connect(fd, (struct sockaddr *) &server, sizeof(server)) == -1)
+ {
+ perror("connect");
+ exit(1);
+ }
+
+ /* register the service with SERVICE_WANT_NOTICE */
+ sendto_server("PASS %s\n", TKSERV_PASSWORD);
+ sendto_server("SERVICE %s localhost %s 33554432 0 :%s\n", TKSERV_NAME, TKSERV_DIST, TKSERV_DESC);
+ sendto_server("SERVSET 33619968\n");
+
+ timeout.tv_usec = 1000;
+ timeout.tv_sec = 10;
+
+ /* daemonization... i'm sure it's not complete */
+ switch (fork())
+ {
+ case -1:
+ perror("fork()");
+ exit(3);
+ case 0:
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ if (setsid() == -1)
+ exit(4);
+ break;
+ default:
+ return 0;
+ }
+
+ /* listen for server output and parse it */
+ while (!eof)
+ {
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+ FD_SET(fd, &read_set);
+
+ if (select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout) == -1)
+ {
+ perror("select");
+ }
+
+ if (!server_output(fd, buffer))
+ {
+ printf("Connection closed.\n");
+ printf("Last server output was: %s\n", last_buf);
+ eof = 1;
+ }
+
+ strcpy(last_buf, buffer);
+ parse_server_output(buffer);
+ }
+
+ close(fd);
+
+ exit(0);
+}
+/* eof */