aboutsummaryrefslogtreecommitdiff
path: root/ircd
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 /ircd
downloadircd-4440a86cfa359b8e40a484a2cd46d33db5455d8a.tar.gz
Initial
Diffstat (limited to 'ircd')
-rwxr-xr-xircd/buildm498
-rw-r--r--ircd/channel.c3396
-rw-r--r--ircd/channel_def.h29
-rw-r--r--ircd/channel_ext.h64
-rw-r--r--ircd/chkconf.c745
-rw-r--r--ircd/class.c256
-rw-r--r--ircd/class_ext.h49
-rw-r--r--ircd/hash.c940
-rw-r--r--ircd/hash_def.h32
-rw-r--r--ircd/hash_ext.h52
-rw-r--r--ircd/ircd.c1287
-rw-r--r--ircd/ircd_ext.h62
-rw-r--r--ircd/list.c685
-rw-r--r--ircd/list2.c515
-rw-r--r--ircd/list_ext.h66
-rw-r--r--ircd/nameser_def.h330
-rw-r--r--ircd/res.c1697
-rw-r--r--ircd/res_comp.c929
-rw-r--r--ircd/res_comp_ext.h44
-rw-r--r--ircd/res_def.h59
-rw-r--r--ircd/res_ext.h41
-rw-r--r--ircd/res_init.c642
-rw-r--r--ircd/res_init_ext.h39
-rw-r--r--ircd/res_mkquery.c178
-rw-r--r--ircd/res_mkquery_ext.h35
-rw-r--r--ircd/resolv_def.h220
-rw-r--r--ircd/s_auth.c804
-rw-r--r--ircd/s_auth_ext.h52
-rw-r--r--ircd/s_bsd.c3243
-rw-r--r--ircd/s_bsd_ext.h77
-rw-r--r--ircd/s_conf.c1684
-rw-r--r--ircd/s_conf_ext.h66
-rw-r--r--ircd/s_debug.c668
-rw-r--r--ircd/s_debug_ext.h45
-rw-r--r--ircd/s_defines.h41
-rw-r--r--ircd/s_err.c436
-rw-r--r--ircd/s_err_ext.h33
-rw-r--r--ircd/s_externs.h53
-rw-r--r--ircd/s_id.c208
-rw-r--r--ircd/s_id_ext.h43
-rw-r--r--ircd/s_misc.c998
-rw-r--r--ircd/s_misc_ext.h58
-rw-r--r--ircd/s_numeric.c127
-rw-r--r--ircd/s_numeric_ext.h33
-rw-r--r--ircd/s_serv.c2489
-rw-r--r--ircd/s_serv_ext.h72
-rw-r--r--ircd/s_service.c708
-rw-r--r--ircd/s_service_ext.h58
-rw-r--r--ircd/s_user.c2869
-rw-r--r--ircd/s_user_ext.h60
-rw-r--r--ircd/s_zip.c261
-rw-r--r--ircd/s_zip_ext.h38
-rw-r--r--ircd/service_def.h54
-rw-r--r--ircd/sys_def.h31
-rw-r--r--ircd/version.c.SH.in113
-rw-r--r--ircd/version_ext.h32
-rw-r--r--ircd/whowas.c464
-rw-r--r--ircd/whowas_def.h34
-rw-r--r--ircd/whowas_ext.h47
59 files changed, 28489 insertions, 0 deletions
diff --git a/ircd/buildm4 b/ircd/buildm4
new file mode 100755
index 0000000..bba769c
--- /dev/null
+++ b/ircd/buildm4
@@ -0,0 +1,98 @@
+#! /bin/sh
+# IRC - Internet Relay Chat, ircd/buildm4
+# Copyright (C) 1993, 1994 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.
+#
+# $Id: buildm4,v 1.11 1999/02/21 00:33:45 kalt Exp $
+#
+
+#
+# If only this was a perl script...*sigh*
+#
+INCLUDE=`../support/config.guess`
+# Installation with absolute path now (Kratz)
+M4=$1
+/bin/rm -f $M4
+egrep "^#def[^P]*PATCHLEVEL" ../common/patchlevel.h | \
+sed -e 's/[^\"]*\"\([^\"]*\)\"/define(VERSION,\1)/' >>$M4
+DEBUG=`egrep "^#define[ ]*DEBUGMODE" config.h`
+if [ -n "$DEBUG" ] ; then
+ echo "define(DEBUGMODE,1)" >>$M4
+else
+ echo "undefine(DEBUGMODE)" >>$M4
+fi
+HOST="`hostname | sed -e 's/\([a-zA-Z0-9\-]*\).*/\1/'`"
+echo "define(HOSTNAME,$HOST)" >> $M4
+
+echo "define(USER,$USER)" >>$M4
+
+PORT=`egrep '^#define[ ]*PORT[ ]*[0-9]*' ../$INCLUDE/config.h | \
+ sed -e 's/[^0-9]*\([0-9]*\).*/\1/'`
+echo "define(PORT,$PORT)" >> $M4
+
+PING=`egrep '^#define[ ]*PINGFREQUENCY[ ]*[0-9]*' ../$INCLUDE/config.h\
+ | sed -e 's/[^0-9]*\([0-9]*\).*/\1/'`
+echo "define(PFREQ,$PING)" >> $M4
+
+CONT=`egrep '^#define[ ]*CONNECTFREQUENCY[ ]*[0-9]*' ../$INCLUDE/config.h\
+ | sed -e 's/[^0-9]*\([0-9]*\).*/\1/'`
+echo "define(CFREQ,$CONT)" >> $M4
+
+MAXL=`egrep '^#define[ ]*MAXIMUM_LINKS[ ]*[0-9]* | head -1' \
+ ../$INCLUDE/config.h | sed -e 's/[^0-9]*\([0-9]*\).*/\1/'`
+echo "define(MAXLINKS,$MAXL)" >> $M4
+
+DOM=`egrep '^domain' /etc/resolv.conf | \
+ sed -e 's/^domain[ ]*\([^ ]*\).*/\1/'`
+echo "define(DOMAIN,$DOM)" >> $M4
+
+cat >>$M4 <<_EOF_
+define(CL,\`ifelse(len(\$1),0,0,\$1)')
+define(MAXSENDQ,0)
+define(HOST,\$1)
+define(HOSTM,\$1)
+define(ID,*@\$1)
+define(PASS,\$1)
+define(PING,\`ifelse(len(\$1),0,PFREQ,\$1)')
+define(APORT,\`ifelse(len(\$1),0,PORT,\$1)')
+define(FREQ,\`ifelse(len(\$1),0,CFREQ,\$1)')
+define(SENDQ,\`ifelse(len(\$1),0,MAXSENDQ,\$1)')
+define(MAX,\`ifelse(len(\$1),0,MAXLINKS,\$1)')
+define(UID,\`ifelse(len(\$1),0,unknown,\$1)')
+define(CPORT,\$1)
+define(SERV,\$1)
+define(ADMIN,A:\$1:\$2:\$3:\$4:\$5)
+define(ALLOW,N:\`HOST(\$1)':\`PASS(\$2)':\`SERV(\$3)':\`HOSTM(\$4)':\`CL(\$5)')
+define(BAN,K:\$1:\$2:\$3:\$4:)
+define(BANIDENT,k:\$1:\$2:\`UID(\$3)':\$4:)
+define(CLASS,Y:\$1:\`PING(\$2)':\$3:\`MAX(\$4)':\`SENDQ(\$5)':\$6:\$7)
+define(CLIENT,I:\`HOST(\$1)':\`PASS(\$2)':\`ifelse(len(HOST(\$3)),0,\$1,\$3)':\
+\`APORT(\$4)':\`CL(\$5)')
+define(RESTRICTED,I:\`HOST(\$1)':\`PASS(\$2)':\
+\`ifelse(len(HOST(\$3)),0,\$1,\$3)':\`APORT(\$4)':\`CL(\$5)')
+define(BOUNCE,B:\$1::\$2:\$3:)
+define(CONNECT,C:\`HOST(\$1)':\`PASS(\$2)':\`SERV(\$3)':\
+\`CPORT(\$4)':\`CL(\$5)')
+define(EXCLUDEVERSION,V:\$1:\$2:\`ifelse(len(\$3),0,*,\$3)'::)
+define(ME,M:\$1:\$2:\$3:\$4:\$5
+P:*:*:*:\$4)
+define(HUB,H:\`ifelse(len(\$2),0,*,\$2)':*:\$1)
+define(LEAF,L:\`ifelse(len(\$2),0,*,\$2)':*:\$1:1)
+define(SERVER,\`
+CONNECT(\$1,\$2,\$3,\$5,\$6)
+ALLOW(\$1,\$2,\$3,\$4,\$6)
+')
+_EOF_
diff --git a/ircd/channel.c b/ircd/channel.c
new file mode 100644
index 0000000..752f9ee
--- /dev/null
+++ b/ircd/channel.c
@@ -0,0 +1,3396 @@
+/************************************************************************
+ * 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.
+ */
+
+/* -- Jto -- 09 Jul 1990
+ * Bug fix
+ */
+
+/* -- Jto -- 03 Jun 1990
+ * Moved m_channel() and related functions from s_msg.c to here
+ * Many changes to start changing into string channels...
+ */
+
+/* -- Jto -- 24 May 1990
+ * Moved is_full() from list.c
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: channel.c,v 1.109 1999/08/13 17:30:16 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define CHANNEL_C
+#include "s_externs.h"
+#undef CHANNEL_C
+
+aChannel *channel = NullChn;
+
+static void add_invite __P((aClient *, aChannel *));
+static int can_join __P((aClient *, aChannel *, char *));
+void channel_modes __P((aClient *, char *, char *, aChannel *));
+static int check_channelmask __P((aClient *, aClient *, char *));
+static aChannel *get_channel __P((aClient *, char *, int));
+static int set_mode __P((aClient *, aClient *, aChannel *, int *, int,\
+ char **, char *,char *));
+static void free_channel __P((aChannel *));
+
+static int add_modeid __P((int, aClient *, aChannel *, char *));
+static int del_modeid __P((int, aChannel *, char *));
+static Link *match_modeid __P((int, aClient *, aChannel *));
+
+static char *PartFmt = ":%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];
+
+/*
+ * return the length (>=0) of a chain of links.
+ */
+static int list_length(lp)
+Reg Link *lp;
+{
+ Reg 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(sptr, user, chasing)
+aClient *sptr;
+char *user;
+Reg int *chasing;
+{
+ Reg aClient *who = find_client(user, (aClient *)NULL);
+
+ if (chasing)
+ *chasing = 0;
+ if (who)
+ return who;
+ if (!(who = get_history(user, (long)KILLCHASETIMELIMIT)))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK, sptr->name), user);
+ return NULL;
+ }
+ if (chasing)
+ *chasing = 1;
+ return who;
+}
+
+/*
+ * Fixes a string so that the first white space found becomes an end of
+ * string marker (`\-`). returns the 'fixed' string or "*" if the string
+ * was NULL length or a NULL pointer.
+ */
+static char *check_string(s)
+Reg char *s;
+{
+ static char star[2] = "*";
+ char *str = s;
+
+ if (BadPtr(s))
+ return star;
+
+ for ( ;*s; s++)
+ if (isspace(*s))
+ {
+ *s = '\0';
+ break;
+ }
+
+ return (BadPtr(str)) ? star : str;
+}
+
+/*
+ * 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(nick, name, host)
+Reg char *nick, *name, *host;
+{
+ static char namebuf[NICKLEN+USERLEN+HOSTLEN+6];
+ Reg char *s = namebuf;
+
+ bzero(namebuf, sizeof(namebuf));
+ nick = check_string(nick);
+ strncpyzt(namebuf, nick, NICKLEN + 1);
+ s += strlen(s);
+ *s++ = '!';
+ name = check_string(name);
+ strncpyzt(s, name, USERLEN + 1);
+ s += strlen(s);
+ *s++ = '@';
+ host = check_string(host);
+ strncpyzt(s, host, HOSTLEN + 1);
+ s += strlen(s);
+ *s = '\0';
+ return (namebuf);
+}
+
+/*
+ * Ban functions to work with mode +b/+e/+I
+ */
+/* add_modeid - add an id to the list of modes "type" for chptr
+ * (belongs to cptr)
+ */
+
+static int add_modeid(type, cptr, chptr, modeid)
+int type;
+aClient *cptr;
+aChannel *chptr;
+char *modeid;
+{
+ Reg Link *mode;
+ Reg int cnt = 0, len = 0;
+
+ if (MyClient(cptr))
+ (void) collapse(modeid);
+ for (mode = chptr->mlist; mode; mode = mode->next)
+ {
+ len += strlen(mode->value.cp);
+ if (MyClient(cptr))
+ {
+ if ((len > MAXBANLENGTH) || (++cnt >= MAXBANS))
+ {
+ sendto_one(cptr, err_str(ERR_BANLISTFULL,
+ cptr->name),
+ chptr->chname, modeid);
+ return -1;
+ }
+ if (type == mode->flags &&
+ (!match(mode->value.cp, modeid) ||
+ !match(modeid, mode->value.cp)))
+ {
+ int rpl;
+
+ if (type == CHFL_BAN)
+ rpl = RPL_BANLIST;
+ else if (type == CHFL_EXCEPTION)
+ rpl = RPL_EXCEPTLIST;
+ else
+ rpl = RPL_INVITELIST;
+
+ sendto_one(cptr, rpl_str(rpl, cptr->name),
+ chptr->chname, mode->value.cp);
+ return -1;
+ }
+ }
+ else if (type == mode->flags && !mycmp(mode->value.cp, modeid))
+ return -1;
+
+ }
+ mode = make_link();
+ istat.is_bans++;
+ bzero((char *)mode, sizeof(Link));
+ mode->flags = type;
+ mode->next = chptr->mlist;
+ mode->value.cp = (char *)MyMalloc(len = strlen(modeid)+1);
+ istat.is_banmem += len;
+ (void)strcpy(mode->value.cp, modeid);
+ chptr->mlist = mode;
+ return 0;
+}
+
+/*
+ * del_modeid - delete an id belonging to chptr
+ * if modeid is null, delete all ids belonging to chptr.
+ */
+static int del_modeid(type, chptr, modeid)
+int type;
+aChannel *chptr;
+char *modeid;
+{
+ Reg Link **mode;
+ Reg Link *tmp;
+
+ if (modeid == NULL)
+ {
+ for (mode = &(chptr->mlist); *mode; mode = &((*mode)->next))
+ if (type == (*mode)->flags)
+ {
+ tmp = *mode;
+ *mode = tmp->next;
+ istat.is_banmem -= (strlen(tmp->value.cp) + 1);
+ istat.is_bans--;
+ MyFree(tmp->value.cp);
+ free_link(tmp);
+ break;
+ }
+ }
+ else for (mode = &(chptr->mlist); *mode; mode = &((*mode)->next))
+ if (type == (*mode)->flags &&
+ mycmp(modeid, (*mode)->value.cp)==0)
+ {
+ tmp = *mode;
+ *mode = tmp->next;
+ istat.is_banmem -= (strlen(modeid) + 1);
+ istat.is_bans--;
+ MyFree(tmp->value.cp);
+ free_link(tmp);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * match_modeid - returns a pointer to the mode structure if matching else NULL
+ */
+static Link *match_modeid(type, cptr, chptr)
+int type;
+aClient *cptr;
+aChannel *chptr;
+{
+ Reg Link *tmp;
+ char *s;
+
+ if (!IsPerson(cptr))
+ return NULL;
+
+ s = make_nick_user_host(cptr->name, cptr->user->username,
+ cptr->user->host);
+
+ for (tmp = chptr->mlist; tmp; tmp = tmp->next)
+ if (tmp->flags == type && match(tmp->value.cp, s) == 0)
+ break;
+
+ if (!tmp && MyConnect(cptr))
+ {
+ char *ip = NULL;
+
+#ifdef INET6
+ ip = (char *) inetntop(AF_INET6, (char *)&cptr->ip,
+ mydummy, MYDUMMY_SIZE);
+#else
+ ip = (char *) inetntoa((char *)&cptr->ip);
+#endif
+
+ if (strcmp(ip, cptr->user->host))
+ {
+ s = make_nick_user_host(cptr->name,
+ cptr->user->username, ip);
+
+ for (tmp = chptr->mlist; tmp; tmp = tmp->next)
+ if (tmp->flags == type &&
+ match(tmp->value.cp, s) == 0)
+ break;
+ }
+ }
+
+ return (tmp);
+}
+
+/*
+ * adds a user to a channel by adding another link to the channels member
+ * chain.
+ */
+static void add_user_to_channel(chptr, who, flags)
+aChannel *chptr;
+aClient *who;
+int flags;
+{
+ Reg Link *ptr;
+ Reg int sz = sizeof(aChannel) + strlen(chptr->chname);
+
+ if (who->user)
+ {
+ ptr = make_link();
+ ptr->flags = flags;
+ ptr->value.cptr = who;
+ ptr->next = chptr->members;
+ chptr->members = ptr;
+ istat.is_chanusers++;
+ if (chptr->users++ == 0)
+ {
+ istat.is_chan++;
+ istat.is_chanmem += sz;
+ }
+ if (chptr->users == 1 && chptr->history)
+ {
+ /* Locked channel */
+ istat.is_hchan--;
+ istat.is_hchanmem -= sz;
+ /*
+ ** The modes had been kept, but now someone is joining,
+ ** they should be reset to avoid desynchs
+ ** (you wouldn't want to join a +i channel, either)
+ **
+ ** This can be wrong in some cases such as a netjoin
+ ** which will not complete, or on a mixed net (with
+ ** servers that don't do channel delay) - kalt
+ */
+ if (*chptr->chname != '!')
+ bzero((char *)&chptr->mode, sizeof(Mode));
+ }
+
+#ifdef USE_SERVICES
+ if (chptr->users == 1)
+ check_services_butone(SERVICE_WANT_CHANNEL|
+ SERVICE_WANT_VCHANNEL,
+ NULL, &me, "CHANNEL %s %d",
+ chptr->chname, chptr->users);
+ else
+ check_services_butone(SERVICE_WANT_VCHANNEL,
+ NULL, &me, "CHANNEL %s %d",
+ chptr->chname, chptr->users);
+#endif
+ ptr = make_link();
+ ptr->flags = flags;
+ ptr->value.chptr = chptr;
+ ptr->next = who->user->channel;
+ who->user->channel = ptr;
+ if (!IsQuiet(chptr))
+ {
+ who->user->joined++;
+ istat.is_userc++;
+ }
+
+ if (!(ptr = find_user_link(chptr->clist, who->from)))
+ {
+ ptr = make_link();
+ ptr->value.cptr = who->from;
+ ptr->next = chptr->clist;
+ chptr->clist = ptr;
+ }
+ ptr->flags++;
+ }
+}
+
+void remove_user_from_channel(sptr, chptr)
+aClient *sptr;
+aChannel *chptr;
+{
+ Reg Link **curr;
+ Reg Link *tmp, *tmp2;
+
+ for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
+ if (tmp->value.cptr == sptr)
+ {
+ /*
+ * if a chanop leaves (no matter how), record
+ * the time to be able to later massreop if
+ * necessary.
+ */
+ if (*chptr->chname == '!' &&
+ (tmp->flags & CHFL_CHANOP))
+ chptr->reop = timeofday + LDELAYCHASETIMELIMIT;
+
+ *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;
+ }
+ if (sptr->from)
+ tmp2 = find_user_link(chptr->clist, sptr->from);
+ else
+ tmp2 = find_user_link(chptr->clist, sptr);
+ if (tmp2 && !--tmp2->flags)
+ for (curr = &chptr->clist; (tmp = *curr); curr = &tmp->next)
+ if (tmp2 == tmp)
+ {
+ *curr = tmp->next;
+ free_link(tmp);
+ break;
+ }
+ if (!IsQuiet(chptr))
+ {
+ sptr->user->joined--;
+ istat.is_userc--;
+ }
+#ifdef USE_SERVICES
+ if (chptr->users == 1)
+ check_services_butone(SERVICE_WANT_CHANNEL|
+ SERVICE_WANT_VCHANNEL, NULL, &me,
+ "CHANNEL %s %d", chptr->chname,
+ chptr->users-1);
+ else
+ check_services_butone(SERVICE_WANT_VCHANNEL, NULL, &me,
+ "CHANNEL %s %d", chptr->chname,
+ chptr->users-1);
+#endif
+ if (--chptr->users <= 0)
+ {
+ u_int sz = sizeof(aChannel) + strlen(chptr->chname);
+
+ istat.is_chan--;
+ istat.is_chanmem -= sz;
+ istat.is_hchan++;
+ istat.is_hchanmem += sz;
+ free_channel(chptr);
+ }
+ istat.is_chanusers--;
+}
+
+static void change_chan_flag(lp, chptr)
+Link *lp;
+aChannel *chptr;
+{
+ Reg Link *tmp;
+ aClient *cptr = lp->value.cptr;
+
+ /*
+ * Set the channel members flags...
+ */
+ tmp = find_user_link(chptr->members, cptr);
+ if (lp->flags & MODE_ADD)
+ tmp->flags |= lp->flags & MODE_FLAGS;
+ else
+ {
+ tmp->flags &= ~lp->flags & MODE_FLAGS;
+ if (lp->flags & CHFL_CHANOP)
+ tmp->flags &= ~CHFL_UNIQOP;
+ }
+ /*
+ * and make sure client membership mirrors channel
+ */
+ tmp = find_user_link(cptr->user->channel, (aClient *)chptr);
+ if (lp->flags & MODE_ADD)
+ tmp->flags |= lp->flags & MODE_FLAGS;
+ else
+ {
+ tmp->flags &= ~lp->flags & MODE_FLAGS;
+ if (lp->flags & CHFL_CHANOP)
+ tmp->flags &= ~CHFL_UNIQOP;
+ }
+}
+
+int is_chan_op(cptr, chptr)
+aClient *cptr;
+aChannel *chptr;
+{
+ Reg Link *lp;
+ int chanop = 0;
+
+ if (MyConnect(cptr) && IsPerson(cptr) && IsRestricted(cptr) &&
+ *chptr->chname != '&')
+ return 0;
+ if (chptr)
+ if ((lp = find_user_link(chptr->members, cptr)))
+ chanop = (lp->flags & (CHFL_CHANOP|CHFL_UNIQOP));
+ if (chanop)
+ chptr->reop = 0;
+ return chanop;
+}
+
+int has_voice(cptr, chptr)
+aClient *cptr;
+aChannel *chptr;
+{
+ Reg Link *lp;
+
+ if (chptr)
+ if ((lp = find_user_link(chptr->members, cptr)))
+ return (lp->flags & CHFL_VOICE);
+
+ return 0;
+}
+
+int can_send(cptr, chptr)
+aClient *cptr;
+aChannel *chptr;
+{
+ Reg Link *lp;
+ Reg int member;
+
+ member = IsMember(cptr, chptr);
+ lp = find_user_link(chptr->members, cptr);
+
+ if ((!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE))) &&
+ !match_modeid(CHFL_EXCEPTION, cptr, chptr) &&
+ match_modeid(CHFL_BAN, cptr, chptr))
+ return (MODE_BAN);
+
+ if (chptr->mode.mode & MODE_MODERATED &&
+ (!lp || !(lp->flags & (CHFL_CHANOP|CHFL_VOICE))))
+ return (MODE_MODERATED);
+
+ if (chptr->mode.mode & MODE_NOPRIVMSGS && !member)
+ return (MODE_NOPRIVMSGS);
+
+ return 0;
+}
+
+aChannel *find_channel(chname, chptr)
+Reg char *chname;
+Reg aChannel *chptr;
+{
+ aChannel *achptr = chptr;
+
+ if (chname && *chname)
+ achptr = hash_find_channel(chname, chptr);
+ return achptr;
+}
+
+void setup_server_channels(mp)
+aClient *mp;
+{
+ aChannel *chptr;
+ int smode;
+
+ smode = MODE_MODERATED|MODE_TOPICLIMIT|MODE_NOPRIVMSGS|MODE_ANONYMOUS|
+ MODE_QUIET;
+
+ chptr = get_channel(mp, "&ERRORS", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: server errors");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&NOTICES", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: warnings and notices");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&KILLS", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: operator and server kills");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&CHANNEL", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: fake modes");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&NUMERICS", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: numerics received");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&SERVERS", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: servers joining and leaving");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&HASH", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: hash tables growth");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&LOCAL", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: notices about local connections");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+ chptr = get_channel(mp, "&SERVICES", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: services joining and leaving");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+#if defined(USE_IAUTH)
+ chptr = get_channel(mp, "&AUTH", CREATE);
+ strcpy(chptr->topic,
+ "SERVER MESSAGES: messages from the authentication slave");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode;
+#endif
+ chptr = get_channel(mp, "&DEBUG", CREATE);
+ strcpy(chptr->topic, "SERVER MESSAGES: debug messages [you shouldn't be here! ;)]");
+ add_user_to_channel(chptr, mp, CHFL_CHANOP);
+ chptr->mode.mode = smode|MODE_SECRET;
+
+ setup_svchans();
+}
+
+/*
+ * write the "simple" list of channel modes for channel chptr onto buffer mbuf
+ * with the parameters in pbuf.
+ */
+void channel_modes(cptr, mbuf, pbuf, chptr)
+aClient *cptr;
+Reg char *mbuf, *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.mode & MODE_ANONYMOUS)
+ *mbuf++ = 'a';
+ if (chptr->mode.mode & MODE_QUIET)
+ *mbuf++ = 'q';
+ if (chptr->mode.mode & MODE_REOP)
+ *mbuf++ = 'r';
+ if (chptr->mode.limit)
+ {
+ *mbuf++ = 'l';
+ if (IsMember(cptr, chptr) || IsServer(cptr))
+ SPRINTF(pbuf, "%d ", chptr->mode.limit);
+ }
+ if (*chptr->mode.key)
+ {
+ *mbuf++ = 'k';
+ if (IsMember(cptr, chptr) || IsServer(cptr))
+ (void)strcat(pbuf, chptr->mode.key);
+ }
+ *mbuf++ = '\0';
+ return;
+}
+
+static void send_mode_list(cptr, chname, top, mask, flag)
+aClient *cptr;
+Link *top;
+int mask;
+char flag, *chname;
+{
+ Reg Link *lp;
+ Reg char *cp, *name;
+ int count = 0, send = 0;
+
+ cp = modebuf + strlen(modebuf);
+ if (*parabuf) /* mode +l or +k xx */
+ count = strlen(modebuf)-1;
+ for (lp = top; lp; lp = lp->next)
+ {
+ if (!(lp->flags & mask))
+ continue;
+ if (mask == CHFL_BAN || mask == CHFL_EXCEPTION ||
+ mask == CHFL_INVITE)
+ name = lp->value.cp;
+ else
+ name = lp->value.cptr->name;
+ if (strlen(parabuf) + strlen(name) + 10 < (size_t) MODEBUFLEN)
+ {
+ (void)strcat(parabuf, " ");
+ (void)strcat(parabuf, name);
+ count++;
+ *cp++ = flag;
+ *cp = '\0';
+ }
+ else if (*parabuf)
+ send = 1;
+ if (count == 3)
+ send = 1;
+ if (send)
+ {
+ sendto_one(cptr, ":%s MODE %s %s %s",
+ ME, chname, modebuf, parabuf);
+ send = 0;
+ *parabuf = '\0';
+ cp = modebuf;
+ *cp++ = '+';
+ if (count != 3)
+ {
+ (void)strcpy(parabuf, name);
+ *cp++ = flag;
+ }
+ count = 0;
+ *cp = '\0';
+ }
+ }
+}
+
+/*
+ * send "cptr" a full list of the modes for channel chptr.
+ */
+void send_channel_modes(cptr, chptr)
+aClient *cptr;
+aChannel *chptr;
+{
+#if 0
+this is probably going to be very annoying, but leaving the following code
+uncommented may just lead to desynchs..
+ if ((*chptr->chname != '#' && *chptr->chname != '!')
+ || chptr->users == 0) /* channel is empty (locked), thus no mode */
+ return;
+#endif
+
+ if (check_channelmask(&me, cptr, chptr->chname))
+ return;
+
+ *modebuf = *parabuf = '\0';
+ channel_modes(cptr, modebuf, parabuf, chptr);
+
+ if (modebuf[1] || *parabuf)
+ sendto_one(cptr, ":%s MODE %s %s %s",
+ ME, chptr->chname, modebuf, parabuf);
+
+ *parabuf = '\0';
+ *modebuf = '+';
+ modebuf[1] = '\0';
+ send_mode_list(cptr, chptr->chname, chptr->mlist, CHFL_BAN, 'b');
+ if (cptr->serv->version & SV_NMODE)
+ {
+ if (modebuf[1] || *parabuf)
+ {
+ /* only needed to help compatibility */
+ sendto_one(cptr, ":%s MODE %s %s %s",
+ ME, chptr->chname, modebuf, parabuf);
+ *parabuf = '\0';
+ *modebuf = '+';
+ modebuf[1] = '\0';
+ }
+ send_mode_list(cptr, chptr->chname, chptr->mlist,
+ CHFL_EXCEPTION, 'e');
+ send_mode_list(cptr, chptr->chname, chptr->mlist,
+ CHFL_INVITE, 'I');
+ }
+ if (modebuf[1] || *parabuf)
+ sendto_one(cptr, ":%s MODE %s %s %s",
+ ME, chptr->chname, modebuf, parabuf);
+}
+
+/*
+ * send "cptr" a full list of the channel "chptr" members and their
+ * +ov status, using NJOIN
+ */
+void send_channel_members(cptr, chptr)
+aClient *cptr;
+aChannel *chptr;
+{
+ Reg Link *lp;
+ Reg aClient *c2ptr;
+ Reg int cnt = 0, len = 0, nlen;
+
+ if (check_channelmask(&me, cptr, chptr->chname) == -1)
+ return;
+ if (*chptr->chname == '!' && !(cptr->serv->version & SV_NCHAN))
+ return;
+
+ sprintf(buf, ":%s NJOIN %s :", ME, chptr->chname);
+ len = strlen(buf);
+
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ c2ptr = lp->value.cptr;
+ nlen = strlen(c2ptr->name);
+ if ((len + nlen) > (size_t) (BUFSIZE - 9)) /* ,@+ \r\n\0 */
+ {
+ sendto_one(cptr, "%s", buf);
+ sprintf(buf, ":%s NJOIN %s :", ME, chptr->chname);
+ len = strlen(buf);
+ cnt = 0;
+ }
+ if (cnt)
+ {
+ buf[len++] = ',';
+ buf[len] = '\0';
+ }
+ if (lp->flags & (CHFL_UNIQOP|CHFL_CHANOP|CHFL_VOICE))
+ {
+ if (lp->flags & CHFL_UNIQOP)
+ {
+ buf[len++] = '@';
+ buf[len++] = '@';
+ }
+ else
+ {
+ if (lp->flags & CHFL_CHANOP)
+ buf[len++] = '@';
+ }
+ if (lp->flags & CHFL_VOICE)
+ buf[len++] = '+';
+ buf[len] = '\0';
+ }
+ (void)strcpy(buf + len, c2ptr->name);
+ len += nlen;
+ cnt++;
+ }
+ if (*buf && cnt)
+ sendto_one(cptr, "%s", buf);
+
+ return;
+}
+
+/*
+ * m_mode
+ * parv[0] - sender
+ * parv[1] - target; channels and/or user
+ * parv[2] - optional modes
+ * parv[n] - optional parameters
+ */
+
+int m_mode(cptr, sptr, parc, parv)
+aClient *cptr;
+aClient *sptr;
+int parc;
+char *parv[];
+{
+ int mcount = 0, chanop;
+ int penalty = 0;
+ aChannel *chptr;
+ char *name, *p = NULL;
+
+ if (parc < 1)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "MODE");
+ return 1;
+ }
+
+ parv[1] = canonize(parv[1]);
+
+ for (name = strtoken(&p, parv[1], ","); name;
+ name = strtoken(&p, NULL, ","))
+ {
+ clean_channelname(name);
+ chptr = find_channel(name, NullChn);
+ if (chptr == NullChn)
+ {
+ parv[1] = name;
+ penalty += m_umode(cptr, sptr, parc, parv);
+ continue;
+ }
+ if (check_channelmask(sptr, cptr, name))
+ {
+ penalty += 1;
+ continue;
+ }
+ if (!UseModes(name))
+ {
+ sendto_one(sptr, err_str(ERR_NOCHANMODES, parv[0]),
+ name);
+ penalty += 1;
+ continue;
+ }
+ chanop = is_chan_op(sptr, chptr) || IsServer(sptr);
+
+ if (parc < 3) /* Only a query */
+ {
+ *modebuf = *parabuf = '\0';
+ modebuf[1] = '\0';
+ channel_modes(sptr, modebuf, parabuf, chptr);
+ sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS, parv[0]),
+ name, modebuf, parabuf);
+ penalty += 1;
+ }
+ else /* Check parameters for the channel */
+ {
+ if(!(mcount = set_mode(cptr, sptr, chptr, &penalty,
+ parc - 2, parv + 2,
+ modebuf, parabuf)))
+ continue; /* no valid mode change */
+ if ((mcount < 0) && MyConnect(sptr) && !IsServer(sptr))
+ { /* rejected mode change */
+ int num = ERR_CHANOPRIVSNEEDED;
+
+ if (IsClient(sptr) && IsRestricted(sptr))
+ num = ERR_RESTRICTED;
+ sendto_one(sptr, err_str(num, parv[0]), name);
+ continue;
+ }
+ if (strlen(modebuf) > (size_t)1)
+ { /* got new mode to pass on */
+ if (modebuf[1] == 'e' || modebuf[1] == 'I')
+ /* 2.9.x compatibility */
+ sendto_match_servs_v(chptr, cptr,
+ SV_NMODE,
+ ":%s MODE %s %s %s",
+ parv[0], name,
+ modebuf, parabuf);
+ else
+ sendto_match_servs(chptr, cptr,
+ ":%s MODE %s %s %s",
+ parv[0], name,
+ modebuf, parabuf);
+ if ((IsServer(cptr) && !IsServer(sptr) &&
+ !chanop) || mcount < 0)
+ {
+ sendto_flag(SCH_CHAN,
+ "Fake: %s MODE %s %s %s",
+ parv[0], name, modebuf,
+ parabuf);
+ ircstp->is_fake++;
+ }
+ else
+ {
+ sendto_channel_butserv(chptr, sptr,
+ ":%s MODE %s %s %s",
+ parv[0], name,
+ modebuf, parabuf);
+#ifdef USE_SERVICES
+ *modebuf = *parabuf = '\0';
+ modebuf[1] = '\0';
+ channel_modes(&me, modebuf, parabuf,
+ chptr);
+ check_services_butone(SERVICE_WANT_MODE,
+ NULL, sptr,
+ "MODE %s %s",
+ name, modebuf);
+#endif
+ }
+ } /* if(modebuf) */
+ } /* else(parc>2) */
+ } /* for (parv1) */
+ return penalty;
+}
+
+/*
+ * Check and try to apply the channel modes passed in the parv array for
+ * the client cptr to channel chptr. The resultant changes are printed
+ * into mbuf and pbuf (if any) and applied to the channel.
+ */
+static int set_mode(cptr, sptr, chptr, penalty, parc, parv, mbuf, pbuf)
+Reg aClient *cptr, *sptr;
+aChannel *chptr;
+int parc, *penalty;
+char *parv[], *mbuf, *pbuf;
+{
+ static Link chops[MAXMODEPARAMS+3];
+ static int flags[] = {
+ MODE_PRIVATE, 'p', MODE_SECRET, 's',
+ MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n',
+ MODE_TOPICLIMIT, 't', MODE_INVITEONLY, 'i',
+ MODE_ANONYMOUS, 'a', MODE_REOP, 'r',
+ 0x0, 0x0 };
+
+ Reg Link *lp = NULL;
+ Reg char *curr = parv[0], *cp = NULL;
+ Reg int *ip;
+ u_int whatt = MODE_ADD;
+ int limitset = 0, count = 0, chasing = 0;
+ int nusers = 0, ischop, new, len, keychange = 0, opcnt = 0;
+ aClient *who;
+ Mode *mode, oldm;
+ Link *plp = NULL;
+ int compat = -1; /* to prevent mixing old/new modes */
+
+ *mbuf = *pbuf = '\0';
+ if (parc < 1)
+ return 0;
+
+ mode = &(chptr->mode);
+ bcopy((char *)mode, (char *)&oldm, sizeof(Mode));
+ ischop = IsServer(sptr) || is_chan_op(sptr, chptr);
+ new = mode->mode;
+
+ while (curr && *curr && count >= 0)
+ {
+ if (compat == -1 && *curr != '-' && *curr != '+')
+ if (*curr == 'e' || *curr == 'I')
+ compat = 1;
+ else
+ compat = 0;
+ switch (*curr)
+ {
+ case '+':
+ whatt = MODE_ADD;
+ break;
+ case '-':
+ whatt = MODE_DEL;
+ break;
+ case 'O':
+ if (parc > 0)
+ {
+ if (*chptr->chname == '!')
+ {
+ if (IsMember(sptr, chptr))
+ {
+ *penalty += 1;
+ parc--;
+ /* Feature: no other modes after this query */
+ *(curr+1) = '\0';
+ for (lp = chptr->members; lp; lp = lp->next)
+ if (lp->flags & CHFL_UNIQOP)
+ {
+ sendto_one(sptr,
+ rpl_str(RPL_UNIQOPIS,
+ sptr->name),
+ chptr->chname,
+ lp->value.cptr->name);
+ break;
+ }
+ if (!lp)
+ sendto_one(sptr,
+ err_str(ERR_NOSUCHNICK,
+ sptr->name),
+ chptr->chname);
+ break;
+ }
+ else /* not IsMember() */
+ {
+ if (!IsServer(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL, sptr->name),
+ chptr->chname);
+ *(curr+1) = '\0';
+ break;
+ }
+ }
+ }
+ else /* *chptr->chname != '!' */
+ sendto_one(cptr, err_str(ERR_UNKNOWNMODE,
+ sptr->name), *curr, chptr->chname);
+ *(curr+1) = '\0';
+ break;
+ }
+ /*
+ * is this really ever used ?
+ * or do ^G & NJOIN do the trick?
+ */
+ if (*chptr->chname != '!' || whatt == MODE_DEL ||
+ !IsServer(sptr))
+ {
+ *penalty += 1;
+ --parc;
+ parv++;
+ break;
+ }
+ case 'o' :
+ case 'v' :
+ *penalty += 1;
+ if (--parc <= 0)
+ break;
+ parv++;
+ *parv = check_string(*parv);
+ if (opcnt >= MAXMODEPARAMS)
+#ifndef V29PlusOnly
+ if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1)
+#endif
+ break;
+ if (!IsServer(sptr) && !IsMember(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL,
+ sptr->name),
+ chptr->chname);
+ break;
+ }
+ /*
+ * Check for nickname changes and try to follow these
+ * to make sure the right client is affected by the
+ * mode change.
+ */
+ if (!(who = find_chasing(sptr, parv[0], &chasing)))
+ break;
+ if (!IsMember(who, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL,
+ sptr->name),
+ parv[0], chptr->chname);
+ break;
+ }
+ if (who == cptr && whatt == MODE_ADD && *curr == 'o')
+ break;
+
+ if (whatt == MODE_ADD)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cptr = who;
+ lp->flags = (*curr == 'O') ? MODE_UNIQOP:
+ (*curr == 'o') ? MODE_CHANOP:
+ MODE_VOICE;
+ lp->flags |= MODE_ADD;
+ }
+ else if (whatt == MODE_DEL)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cptr = who;
+ lp->flags = (*curr == 'o') ? MODE_CHANOP:
+ MODE_VOICE;
+ lp->flags |= MODE_DEL;
+ }
+ if (plp && plp->flags == lp->flags &&
+ plp->value.cptr == lp->value.cptr)
+ {
+ opcnt--;
+ break;
+ }
+ plp = lp;
+ /*
+ ** If this server noticed the nick change, the
+ ** information must be propagated back upstream.
+ ** This is a bit early, but at most this will generate
+ ** just some extra messages if nick appeared more than
+ ** once in the MODE message... --msa
+ */
+/* nobody can figure this part of the code anymore.. -kalt
+ if (chasing && ischop)
+ sendto_one(cptr, ":%s MODE %s %c%c %s",
+ ME, chptr->chname,
+ whatt == MODE_ADD ? '+' : '-',
+ *curr, who->name);
+*/
+ count++;
+ *penalty += 2;
+ break;
+ case 'k':
+ *penalty += 1;
+ if (--parc <= 0)
+ break;
+ parv++;
+ /* check now so we eat the parameter if present */
+ if (keychange)
+ break;
+ {
+ Reg u_char *s;
+
+ for (s = (u_char *)*parv; *s; )
+ if (*s > 0x7f)
+ if (*s > 0xa0)
+ *s++ &= 0x7f;
+ else
+ *s = '\0';
+ else
+ s++;
+ }
+
+ if (!**parv)
+ break;
+ *parv = check_string(*parv);
+ if (opcnt >= MAXMODEPARAMS)
+#ifndef V29PlusOnly
+ if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1)
+#endif
+ break;
+ if (whatt == MODE_ADD)
+ {
+ if (*mode->key && !IsServer(cptr))
+ sendto_one(cptr, err_str(ERR_KEYSET,
+ cptr->name), chptr->chname);
+ else if (ischop &&
+ (!*mode->key || IsServer(cptr)))
+ {
+ if (**parv == ':')
+ /* this won't propagate right*/
+ break;
+ 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 (ischop && (mycmp(mode->key, *parv) == 0 ||
+ IsServer(cptr)))
+ {
+ lp = &chops[opcnt++];
+ lp->value.cp = mode->key;
+ lp->flags = MODE_KEY|MODE_DEL;
+ keychange = 1;
+ }
+ }
+ count++;
+ *penalty += 2;
+ break;
+ case 'b':
+ *penalty += 1;
+ if (--parc <= 0) /* ban list query */
+ {
+ /* Feature: no other modes after ban query */
+ *(curr+1) = '\0'; /* Stop MODE # bb.. */
+ for (lp = chptr->mlist; lp; lp = lp->next)
+ if (lp->flags == CHFL_BAN)
+ sendto_one(cptr,
+ rpl_str(RPL_BANLIST,
+ cptr->name),
+ chptr->chname,
+ lp->value.cp);
+ sendto_one(cptr, rpl_str(RPL_ENDOFBANLIST,
+ cptr->name), chptr->chname);
+ break;
+ }
+ parv++;
+ if (BadPtr(*parv))
+ break;
+ if (opcnt >= MAXMODEPARAMS)
+#ifndef V29PlusOnly
+ if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1)
+#endif
+ break;
+ if (whatt == MODE_ADD)
+ {
+ if (**parv == ':')
+ /* this won't propagate right */
+ break;
+ 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;
+ }
+ count++;
+ *penalty += 2;
+ break;
+ case 'e':
+ *penalty += 1;
+ if (--parc <= 0) /* exception list query */
+ {
+ /* Feature: no other modes after query */
+ *(curr+1) = '\0'; /* Stop MODE # bb.. */
+ for (lp = chptr->mlist; lp; lp = lp->next)
+ if (lp->flags == CHFL_EXCEPTION)
+ sendto_one(cptr,
+ rpl_str(RPL_EXCEPTLIST,
+ cptr->name),
+ chptr->chname,
+ lp->value.cp);
+ sendto_one(cptr, rpl_str(RPL_ENDOFEXCEPTLIST,
+ cptr->name), chptr->chname);
+ break;
+ }
+ parv++;
+ if (BadPtr(*parv))
+ break;
+ if (opcnt >= MAXMODEPARAMS)
+#ifndef V29PlusOnly
+ if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1)
+#endif
+ break;
+ if (whatt == MODE_ADD)
+ {
+ if (**parv == ':')
+ /* this won't propagate right */
+ break;
+ lp = &chops[opcnt++];
+ lp->value.cp = *parv;
+ lp->flags = MODE_ADD|MODE_EXCEPTION;
+ }
+ else if (whatt == MODE_DEL)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cp = *parv;
+ lp->flags = MODE_DEL|MODE_EXCEPTION;
+ }
+ count++;
+ *penalty += 2;
+ break;
+ case 'I':
+ *penalty += 1;
+ if (--parc <= 0) /* invite list query */
+ {
+ /* Feature: no other modes after query */
+ *(curr+1) = '\0'; /* Stop MODE # bb.. */
+ for (lp = chptr->mlist; lp; lp = lp->next)
+ if (lp->flags == CHFL_INVITE)
+ sendto_one(cptr,
+ rpl_str(RPL_INVITELIST,
+ cptr->name),
+ chptr->chname,
+ lp->value.cp);
+ sendto_one(cptr, rpl_str(RPL_ENDOFINVITELIST,
+ cptr->name), chptr->chname);
+ break;
+ }
+ parv++;
+ if (BadPtr(*parv))
+ break;
+ if (opcnt >= MAXMODEPARAMS)
+#ifndef V29PlusOnly
+ if (MyClient(sptr) || opcnt >= MAXMODEPARAMS + 1)
+#endif
+ break;
+ if (whatt == MODE_ADD)
+ {
+ if (**parv == ':')
+ /* this won't propagate right */
+ break;
+ lp = &chops[opcnt++];
+ lp->value.cp = *parv;
+ lp->flags = MODE_ADD|MODE_INVITE;
+ }
+ else if (whatt == MODE_DEL)
+ {
+ lp = &chops[opcnt++];
+ lp->value.cp = *parv;
+ lp->flags = MODE_DEL|MODE_INVITE;
+ }
+ count++;
+ *penalty += 2;
+ break;
+ case 'l':
+ *penalty += 1;
+ /*
+ * limit 'l' to only *1* change per mode command but
+ * eat up others.
+ */
+ if (limitset || !ischop)
+ {
+ if (whatt == MODE_ADD && --parc > 0)
+ parv++;
+ break;
+ }
+ if (whatt == MODE_DEL)
+ {
+ limitset = 1;
+ nusers = 0;
+ count++;
+ break;
+ }
+ if (--parc > 0)
+ {
+ if (BadPtr(*parv))
+ break;
+ if (opcnt >= MAXMODEPARAMS)
+#ifndef V29PlusOnly
+ if (MyClient(sptr) ||
+ opcnt >= MAXMODEPARAMS + 1)
+#endif
+ break;
+ if (!(nusers = atoi(*++parv)))
+ break;
+ lp = &chops[opcnt++];
+ lp->flags = MODE_ADD|MODE_LIMIT;
+ limitset = 1;
+ count++;
+ *penalty += 2;
+ break;
+ }
+ sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS,
+ 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:
+ *penalty += 1;
+ for (ip = flags; *ip; ip += 2)
+ if (*(ip+1) == *curr)
+ break;
+
+ if (*ip)
+ {
+ if (*ip == MODE_ANONYMOUS &&
+ whatt == MODE_DEL && *chptr->chname == '!')
+ sendto_one(sptr,
+ err_str(ERR_UNIQOPRIVSNEEDED,
+ sptr->name), chptr->chname);
+ else if (((*ip == MODE_ANONYMOUS &&
+ whatt == MODE_ADD &&
+ *chptr->chname == '#') ||
+ (*ip == MODE_REOP &&
+ *chptr->chname != '!')) &&
+ !IsServer(sptr))
+ sendto_one(cptr,
+ err_str(ERR_UNKNOWNMODE,
+ sptr->name), *curr,
+ chptr->chname);
+ else if ((*ip == MODE_REOP ||
+ *ip == MODE_ANONYMOUS) &&
+ !IsServer(sptr) &&
+ !(is_chan_op(sptr,chptr) &CHFL_UNIQOP)
+ && *chptr->chname == '!')
+ /* 2 modes restricted to UNIQOP */
+ sendto_one(sptr,
+ err_str(ERR_UNIQOPRIVSNEEDED,
+ sptr->name), chptr->chname);
+ else
+ {
+ /*
+ ** If the channel is +s, ignore +p
+ ** modes coming from a server.
+ ** (Otherwise, it's desynch'ed) -kalt
+ */
+ if (whatt == MODE_ADD &&
+ *ip == MODE_PRIVATE &&
+ IsServer(sptr) &&
+ (new & MODE_SECRET))
+ break;
+ if (whatt == MODE_ADD)
+ {
+ if (*ip == MODE_PRIVATE)
+ new &= ~MODE_SECRET;
+ else if (*ip == MODE_SECRET)
+ new &= ~MODE_PRIVATE;
+ new |= *ip;
+ }
+ else
+ new &= ~*ip;
+ count++;
+ *penalty += 2;
+ }
+ }
+ else if (!IsServer(cptr))
+ sendto_one(cptr, err_str(ERR_UNKNOWNMODE,
+ cptr->name), *curr, chptr->chname);
+ break;
+ }
+ curr++;
+ /*
+ * Make sure modes strings such as "+m +t +p +i" are parsed
+ * fully.
+ */
+ if (!*curr && parc > 0)
+ {
+ curr = *++parv;
+ parc--;
+ }
+ /*
+ * Make sure old and new (+e/+I) modes won't get mixed
+ * together on the same line
+ */
+ if (MyClient(sptr) && curr && *curr != '-' && *curr != '+')
+ if (*curr == 'e' || *curr == 'I')
+ {
+ if (compat == 0)
+ *curr = '\0';
+ }
+ else if (compat == 1)
+ *curr = '\0';
+ } /* end of while loop for MODE processing */
+
+ whatt = 0;
+
+ for (ip = flags; *ip; ip += 2)
+ if ((*ip & new) && !(*ip & oldm.mode))
+ {
+ if (whatt == 0)
+ {
+ *mbuf++ = '+';
+ whatt = 1;
+ }
+ if (ischop)
+ {
+ mode->mode |= *ip;
+ if (*ip == MODE_ANONYMOUS && MyPerson(sptr))
+ {
+ sendto_channel_butone(NULL, &me, chptr, ":%s NOTICE %s :The anonymous flag is being set on channel %s.", ME, chptr->chname, chptr->chname);
+ sendto_channel_butone(NULL, &me, chptr, ":%s NOTICE %s :Be aware that anonymity on IRC is NOT securely enforced!", ME, chptr->chname);
+ }
+ }
+ *mbuf++ = *(ip+1);
+ }
+
+ for (ip = flags; *ip; ip += 2)
+ if ((*ip & oldm.mode) && !(*ip & new))
+ {
+ if (whatt != -1)
+ {
+ *mbuf++ = '-';
+ whatt = -1;
+ }
+ if (ischop)
+ mode->mode &= ~*ip;
+ *mbuf++ = *(ip+1);
+ }
+
+ if (limitset && !nusers && mode->limit)
+ {
+ if (whatt != -1)
+ {
+ *mbuf++ = '-';
+ whatt = -1;
+ }
+ mode->mode &= ~MODE_LIMIT;
+ mode->limit = 0;
+ *mbuf++ = 'l';
+ }
+
+ /*
+ * Reconstruct "+beIkOov" chain.
+ */
+ if (opcnt)
+ {
+ Reg int i = 0;
+ Reg char c = '\0';
+ char *user, *host, numeric[16];
+
+/* if (opcnt > MAXMODEPARAMS)
+ opcnt = MAXMODEPARAMS;
+*/
+ 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++ = '+';
+ whatt = MODE_ADD;
+ }
+ else
+ {
+ *mbuf++ = '-';
+ whatt = MODE_DEL;
+ }
+ len = strlen(pbuf);
+ /*
+ * get c as the mode char and tmp as a pointer to
+ * the paramter for this mode change.
+ */
+ switch(lp->flags & MODE_WPARAS)
+ {
+ case MODE_CHANOP :
+ c = 'o';
+ cp = lp->value.cptr->name;
+ break;
+ case MODE_UNIQOP :
+ c = 'O';
+ cp = lp->value.cptr->name;
+ break;
+ case MODE_VOICE :
+ c = 'v';
+ cp = lp->value.cptr->name;
+ break;
+ case MODE_BAN :
+ c = 'b';
+ cp = lp->value.cp;
+ if ((user = index(cp, '!')))
+ *user++ = '\0';
+ if ((host = rindex(user ? user : cp, '@')))
+ *host++ = '\0';
+ cp = make_nick_user_host(cp, user, host);
+ if (user)
+ *(--user) = '!';
+ if (host)
+ *(--host) = '@';
+ break;
+ case MODE_EXCEPTION :
+ c = 'e';
+ cp = lp->value.cp;
+ if ((user = index(cp, '!')))
+ *user++ = '\0';
+ if ((host = rindex(user ? user : cp, '@')))
+ *host++ = '\0';
+ cp = make_nick_user_host(cp, user, host);
+ if (user)
+ *(--user) = '!';
+ if (host)
+ *(--host) = '@';
+ break;
+ case MODE_INVITE :
+ c = 'I';
+ cp = lp->value.cp;
+ if ((user = index(cp, '!')))
+ *user++ = '\0';
+ if ((host = rindex(user ? user : cp, '@')))
+ *host++ = '\0';
+ cp = make_nick_user_host(cp, user, host);
+ if (user)
+ *(--user) = '!';
+ if (host)
+ *(--host) = '@';
+ break;
+ case MODE_KEY :
+ c = 'k';
+ cp = lp->value.cp;
+ break;
+ case MODE_LIMIT :
+ c = 'l';
+ (void)sprintf(numeric, "%-15d", nusers);
+ if ((cp = index(numeric, ' ')))
+ *cp = '\0';
+ cp = numeric;
+ break;
+ }
+
+ if (len + strlen(cp) + 2 > (size_t) MODEBUFLEN)
+ break;
+ /*
+ * pass on +/-o/v regardless of whether they are
+ * redundant or effective but check +b's to see if
+ * it existed before we created it.
+ */
+ switch(lp->flags & MODE_WPARAS)
+ {
+ case MODE_KEY :
+ *mbuf++ = c;
+ (void)strcat(pbuf, cp);
+ len += strlen(cp);
+ (void)strcat(pbuf, " ");
+ len++;
+ if (!ischop)
+ break;
+ if (strlen(cp) > (size_t) KEYLEN)
+ *(cp+KEYLEN) = '\0';
+ if (whatt == MODE_ADD)
+ strncpyzt(mode->key, cp,
+ sizeof(mode->key));
+ else
+ *mode->key = '\0';
+ break;
+ case MODE_LIMIT :
+ *mbuf++ = c;
+ (void)strcat(pbuf, cp);
+ len += strlen(cp);
+ (void)strcat(pbuf, " ");
+ len++;
+ if (!ischop)
+ break;
+ mode->limit = nusers;
+ break;
+ case MODE_CHANOP : /* fall through case */
+ if (ischop && lp->value.cptr == sptr &&
+ lp->flags == MODE_CHANOP|MODE_DEL)
+ chptr->reop = timeofday +
+ LDELAYCHASETIMELIMIT;
+ case MODE_UNIQOP :
+ case MODE_VOICE :
+ *mbuf++ = c;
+ (void)strcat(pbuf, cp);
+ len += strlen(cp);
+ (void)strcat(pbuf, " ");
+ len++;
+ if (ischop)
+ change_chan_flag(lp, chptr);
+ break;
+ case MODE_BAN :
+ if (ischop &&
+ (((whatt & MODE_ADD) &&
+ !add_modeid(CHFL_BAN, sptr, chptr, cp))||
+ ((whatt & MODE_DEL) &&
+ !del_modeid(CHFL_BAN, chptr, cp))))
+ {
+ *mbuf++ = c;
+ (void)strcat(pbuf, cp);
+ len += strlen(cp);
+ (void)strcat(pbuf, " ");
+ len++;
+ }
+ break;
+ case MODE_EXCEPTION :
+ if (ischop &&
+ (((whatt & MODE_ADD) &&
+ !add_modeid(CHFL_EXCEPTION, sptr, chptr, cp))||
+ ((whatt & MODE_DEL) &&
+ !del_modeid(CHFL_EXCEPTION, chptr, cp))))
+ {
+ *mbuf++ = c;
+ (void)strcat(pbuf, cp);
+ len += strlen(cp);
+ (void)strcat(pbuf, " ");
+ len++;
+ }
+ break;
+ case MODE_INVITE :
+ if (ischop &&
+ (((whatt & MODE_ADD) &&
+ !add_modeid(CHFL_INVITE, sptr, chptr, cp))||
+ ((whatt & MODE_DEL) &&
+ !del_modeid(CHFL_INVITE, chptr, cp))))
+ {
+ *mbuf++ = c;
+ (void)strcat(pbuf, cp);
+ len += strlen(cp);
+ (void)strcat(pbuf, " ");
+ len++;
+ }
+ break;
+ }
+ } /* for (; i < opcnt; i++) */
+ } /* if (opcnt) */
+
+ *mbuf++ = '\0';
+
+ return ischop ? count : -count;
+}
+
+static int can_join(sptr, chptr, key)
+aClient *sptr;
+Reg aChannel *chptr;
+char *key;
+{
+ Link *lp = NULL, *banned;
+
+ if (chptr->users == 0 && (bootopt & BOOT_PROT) &&
+ chptr->history != 0 && *chptr->chname != '!')
+ return (timeofday > chptr->history) ? 0 : ERR_UNAVAILRESOURCE;
+
+ for (lp = sptr->user->invited; lp; lp = lp->next)
+ if (lp->value.chptr == chptr)
+ break;
+
+ if (banned = match_modeid(CHFL_BAN, sptr, chptr))
+ if (match_modeid(CHFL_EXCEPTION, sptr, chptr))
+ banned = NULL;
+ else if (lp == NULL)
+ return (ERR_BANNEDFROMCHAN);
+
+ if ((chptr->mode.mode & MODE_INVITEONLY)
+ && !match_modeid(CHFL_INVITE, sptr, chptr)
+ && (lp == NULL))
+ return (ERR_INVITEONLYCHAN);
+
+ if (*chptr->mode.key && (BadPtr(key) || mycmp(chptr->mode.key, key)))
+ return (ERR_BADCHANNELKEY);
+
+ if (chptr->mode.limit && (chptr->users >= chptr->mode.limit) &&
+ (lp == NULL))
+ return (ERR_CHANNELISFULL);
+
+ if (banned)
+ sendto_channel_butone(&me, &me, chptr,
+ ":%s NOTICE %s :%s carries an invitation (overriding ban on %s).",
+ ME, chptr->chname, sptr->name,
+ banned->value.cp);
+ return 0;
+}
+
+/*
+** Remove bells and commas from channel name
+*/
+
+void clean_channelname(cn)
+Reg char *cn;
+{
+ for (; *cn; cn++)
+ if (*cn == '\007' || *cn == ' ' || *cn == ',')
+ {
+ *cn = '\0';
+ return;
+ }
+}
+
+/*
+** Return -1 if mask is present and doesnt match our server name.
+*/
+static int check_channelmask(sptr, cptr, chname)
+aClient *sptr, *cptr;
+char *chname;
+{
+ Reg char *s, *t;
+
+ if (*chname == '&' && IsServer(cptr))
+ return -1;
+ s = rindex(chname, ':');
+ if (!s)
+ return 0;
+ if ((t = index(s, '\007')))
+ *t = '\0';
+
+ s++;
+ if (match(s, ME) || (IsServer(cptr) && match(s, cptr->name)))
+ {
+ if (MyClient(sptr))
+ sendto_one(sptr, err_str(ERR_BADCHANMASK, sptr->name),
+ chname);
+ if (t)
+ *t = '\007';
+ return -1;
+ }
+ if (t)
+ *t = '\007';
+ return 0;
+}
+
+/*
+** Get Channel block for i (and allocate a new channel
+** block, if it didn't exists before).
+*/
+static aChannel *get_channel(cptr, chname, flag)
+aClient *cptr;
+char *chname;
+int flag;
+ {
+ Reg aChannel *chptr;
+ int len;
+
+ if (BadPtr(chname))
+ return NULL;
+
+ len = strlen(chname);
+ if (MyClient(cptr) && len > CHANNELLEN)
+ {
+ len = CHANNELLEN;
+ *(chname+CHANNELLEN) = '\0';
+ }
+ if ((chptr = find_channel(chname, (aChannel *)NULL)))
+ return (chptr);
+ if (flag == CREATE)
+ {
+ chptr = (aChannel *)MyMalloc(sizeof(aChannel) + len);
+ bzero((char *)chptr, sizeof(aChannel));
+ strncpyzt(chptr->chname, chname, len+1);
+ if (channel)
+ channel->prevch = chptr;
+ chptr->prevch = NULL;
+ chptr->nextch = channel;
+ chptr->history = 0;
+ channel = chptr;
+ (void)add_to_channel_hash_table(chname, chptr);
+ }
+ return chptr;
+ }
+
+static void add_invite(cptr, chptr)
+aClient *cptr;
+aChannel *chptr;
+{
+ Reg 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)
+ {
+/* This forgets the channel side of invitation -Vesa
+ inv = cptr->user->invited;
+ cptr->user->invited = inv->next;
+ free_link(inv);
+*/
+ 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;
+ istat.is_useri++;
+ /*
+ * 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;
+ istat.is_invite++;
+}
+
+/*
+ * Delete Invite block from channel invite list and client invite list
+ */
+void del_invite(cptr, chptr)
+aClient *cptr;
+aChannel *chptr;
+{
+ Reg Link **inv, *tmp;
+
+ for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
+ if (tmp->value.cptr == cptr)
+ {
+ *inv = tmp->next;
+ free_link(tmp);
+ istat.is_invite--;
+ break;
+ }
+
+ for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
+ if (tmp->value.chptr == chptr)
+ {
+ *inv = tmp->next;
+ free_link(tmp);
+ istat.is_useri--;
+ break;
+ }
+}
+
+/*
+** The last user has left the channel, free data in the channel block,
+** and eventually the channel block itself.
+*/
+static void free_channel(chptr)
+aChannel *chptr;
+{
+ Reg Link *tmp;
+ Link *obtmp;
+ int len = sizeof(aChannel) + strlen(chptr->chname), now = 0;
+
+ if (chptr->history == 0 || timeofday >= chptr->history)
+ /* no lock, nor expired lock, channel is no more, free it */
+ now = 1;
+
+ if (*chptr->chname != '!' || now)
+ {
+ while ((tmp = chptr->invites))
+ del_invite(tmp->value.cptr, chptr);
+
+ tmp = chptr->mlist;
+ while (tmp)
+ {
+ obtmp = tmp;
+ tmp = tmp->next;
+ istat.is_banmem -= (strlen(obtmp->value.cp) + 1);
+ istat.is_bans--;
+ MyFree(obtmp->value.cp);
+ free_link(obtmp);
+ }
+ chptr->mlist = NULL;
+ }
+
+ if (now)
+ {
+ istat.is_hchan--;
+ istat.is_hchanmem -= len;
+ if (chptr->prevch)
+ chptr->prevch->nextch = chptr->nextch;
+ else
+ channel = chptr->nextch;
+ if (chptr->nextch)
+ chptr->nextch->prevch = chptr->prevch;
+ del_from_channel_hash_table(chptr->chname, chptr);
+
+ if (*chptr->chname == '!' && close_chid(chptr->chname+1))
+ cache_chid(chptr);
+ else
+ MyFree((char *)chptr);
+ }
+}
+
+/*
+** m_join
+** parv[0] = sender prefix
+** parv[1] = channel
+** parv[2] = channel password (key)
+*/
+int m_join(cptr, sptr, parc, parv)
+Reg aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ static char jbuf[BUFSIZE], cbuf[BUFSIZE];
+ Reg Link *lp;
+ Reg aChannel *chptr;
+ Reg char *name, *key = NULL;
+ int i, flags = 0;
+ char *p = NULL, *p2 = NULL, *s, chop[5];
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "JOIN");
+ return 1;
+ }
+
+ *jbuf = '\0';
+ *cbuf = '\0';
+ /*
+ ** Rebuild list of channels joined to be the actual result of the
+ ** JOIN. Note that "JOIN 0" is the destructive problem.
+ ** Also note that this can easily trash the correspondance between
+ ** parv[1] and parv[2] lists.
+ */
+ for (i = 0, name = strtoken(&p, parv[1], ","); name;
+ name = strtoken(&p, NULL, ","))
+ {
+ if (check_channelmask(sptr, cptr, name)==-1)
+ continue;
+ if (*name == '&' && !MyConnect(sptr))
+ continue;
+ if (*name == '0' && !atoi(name))
+ {
+ (void)strcpy(jbuf, "0");
+ continue;
+ }
+ if (*name == '!')
+ {
+ chptr = NULL;
+ /*
+ ** !channels are special:
+ ** !!channel is supposed to be a new channel,
+ ** and requires a unique name to be built.
+ ** ( !#channel is obsolete )
+ ** !channel cannot be created, and must already
+ ** exist.
+ */
+ if (*(name+1) == '\0' ||
+ (*(name+1) == '#' && *(name+2) == '\0') ||
+ (*(name+1) == '!' && *(name+2) == '\0'))
+ {
+ if (MyClient(sptr))
+ sendto_one(sptr,
+ err_str(ERR_NOSUCHCHANNEL,
+ parv[0]), name);
+ continue;
+ }
+ if (*name == '!' && (*(name+1) == '#' ||
+ *(name+1) == '!'))
+ {
+ if (!MyClient(sptr))
+ {
+ sendto_flag(SCH_NOTICE,
+ "Invalid !%c channel from %s for %s",
+ *(name+1),
+ get_client_name(cptr,TRUE),
+ sptr->name);
+ continue;
+ }
+#if 0
+ /*
+ ** Note: creating !!!foo, e.g. !<ID>!foo is
+ ** a stupid thing to do because /join !!foo
+ ** will not join !<ID>!foo but create !<ID>foo
+ ** Some logic here could be reversed, but only
+ ** to find that !<ID>foo would be impossible to
+ ** create if !<ID>!foo exists.
+ ** which is better? it's hard to say -kalt
+ */
+ if (*(name+3) == '!')
+ {
+ sendto_one(sptr,
+ err_str(ERR_NOSUCHCHANNEL,
+ parv[0]), name);
+ continue;
+ }
+#endif
+ chptr = hash_find_channels(name+2, NULL);
+ if (chptr)
+ {
+ sendto_one(sptr,
+ err_str(ERR_TOOMANYTARGETS,
+ parv[0]),
+ "Duplicate", name,
+ "Join aborted.");
+ continue;
+ }
+ if (check_chid(name+2))
+ {
+ /*
+ * This is a bit wrong: if a channel
+ * rightfully ceases to exist, it
+ * can still be *locked* for up to
+ * 2*CHIDNB^3 seconds (~24h)
+ * Is it a reasonnable price to pay to
+ * ensure shortname uniqueness? -kalt
+ */
+ sendto_one(sptr,
+ err_str(ERR_UNAVAILRESOURCE,
+ parv[0]), name);
+ continue;
+ }
+ sprintf(buf, "!%.*s%s", CHIDLEN, get_chid(),
+ name+2);
+ name = buf;
+ }
+ else if (!find_channel(name, NullChn) &&
+ !(*name == '!' && *name != 0 &&
+ (chptr = hash_find_channels(name+1, NULL))))
+ {
+ if (MyClient(sptr))
+ sendto_one(sptr,
+ err_str(ERR_NOSUCHCHANNEL,
+ parv[0]), name);
+ if (!IsServer(cptr))
+ continue;
+ /* from a server, it is legitimate */
+ }
+ else if (chptr)
+ {
+ /* joining a !channel using the short name */
+ if (MyConnect(sptr) &&
+ hash_find_channels(name+1, chptr))
+ {
+ sendto_one(sptr,
+ err_str(ERR_TOOMANYTARGETS,
+ parv[0]),
+ "Duplicate", name,
+ "Join aborted.");
+ continue;
+ }
+ name = chptr->chname;
+ }
+ }
+ if (!IsChannelName(name) ||
+ (*name == '!' && IsChannelName(name+1)))
+ {
+ if (MyClient(sptr))
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL,
+ parv[0]), name);
+ continue;
+ }
+ if (*jbuf)
+ (void)strcat(jbuf, ",");
+ (void)strncat(jbuf, name, sizeof(jbuf) - i - 1);
+ i += strlen(name)+1;
+ }
+
+ p = NULL;
+ if (parv[2])
+ key = strtoken(&p2, parv[2], ",");
+ parv[2] = NULL; /* for m_names call later, parv[parc] must == NULL */
+ for (name = strtoken(&p, jbuf, ","); name;
+ key = (key) ? strtoken(&p2, NULL, ",") : NULL,
+ name = strtoken(&p, NULL, ","))
+ {
+ /*
+ ** JOIN 0 sends out a part for all channels a user
+ ** has joined.
+ */
+ if (*name == '0' && !atoi(name))
+ {
+ if (sptr->user->channel == NULL)
+ continue;
+ while ((lp = sptr->user->channel))
+ {
+ chptr = lp->value.chptr;
+ sendto_channel_butserv(chptr, sptr,
+ PartFmt,
+ parv[0], chptr->chname,
+ IsAnonymous(chptr) ? "None" :
+ (key ? key : parv[0]));
+ remove_user_from_channel(sptr, chptr);
+ }
+ sendto_match_servs(NULL, cptr, ":%s JOIN 0 :%s",
+ parv[0], key ? key : parv[0]);
+ continue;
+ }
+
+ if (cptr->serv && (s = index(name, '\007')))
+ *s++ = '\0';
+ else
+ clean_channelname(name), s = NULL;
+
+ if (MyConnect(sptr) &&
+ sptr->user->joined >= MAXCHANNELSPERUSER) {
+ /* Feature: Cannot join &flagchannels either
+ if already joined MAXCHANNELSPERUSER times. */
+ sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS,
+ parv[0]), name);
+ /* can't return, need to send the info everywhere */
+ continue;
+ }
+
+ chptr = get_channel(sptr, name, CREATE);
+
+ if (IsMember(sptr, chptr))
+ continue;
+ if (!chptr ||
+ (MyConnect(sptr) && (i = can_join(sptr, chptr, key))))
+ {
+ sendto_one(sptr, err_str(i, parv[0]), name);
+ continue;
+ }
+
+ /*
+ ** local client is first to enter previously nonexistant
+ ** channel so make them (rightfully) the Channel
+ ** Operator.
+ */
+ flags = 0;
+ chop[0] = '\0';
+ if (MyConnect(sptr) && UseModes(name) &&
+ (!IsRestricted(sptr) || (*name == '&')) && !chptr->users &&
+ !(chptr->history && *chptr->chname == '!'))
+ {
+ if (*name == '!')
+ strcpy(chop, "\007O");
+ else
+ strcpy(chop, "\007o");
+ s = chop+1; /* tricky */
+ }
+ /*
+ ** Complete user entry to the new channel (if any)
+ */
+ if (s && UseModes(name))
+ {
+ if (*s == 'O')
+ /*
+ * there can never be another mode here,
+ * because we use NJOIN for netjoins.
+ * here, it *must* be a channel creation. -kalt
+ */
+ flags |= CHFL_UNIQOP|CHFL_CHANOP;
+ else if (*s == 'o')
+ {
+ flags |= CHFL_CHANOP;
+ if (*(s+1) == 'v')
+ flags |= CHFL_VOICE;
+ }
+ else if (*s == 'v')
+ flags |= CHFL_VOICE;
+ }
+ add_user_to_channel(chptr, sptr, flags);
+ /*
+ ** notify all users on the channel
+ */
+ sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s",
+ parv[0], name);
+ if (s && UseModes(name))
+ {
+ /* no need if user is creating the channel */
+ if (chptr->users != 1)
+ sendto_channel_butserv(chptr, sptr,
+ ":%s MODE %s +%s %s %s",
+ cptr->name, name, s,
+ parv[0],
+ *(s+1)=='v'?parv[0]:"");
+ *--s = '\007';
+ }
+ /*
+ ** If s wasn't set to chop+1 above, name is now #chname^Gov
+ ** again (if coming from a server, and user is +o and/or +v
+ ** of course ;-)
+ ** This explains the weird use of name and chop..
+ ** Is this insane or subtle? -krys
+ */
+ if (MyClient(sptr))
+ {
+ del_invite(sptr, chptr);
+ if (chptr->topic[0] != '\0')
+ sendto_one(sptr, rpl_str(RPL_TOPIC, parv[0]),
+ name, chptr->topic);
+ parv[1] = name;
+ (void)m_names(cptr, sptr, 2, parv);
+ if (IsAnonymous(chptr) && !IsQuiet(chptr))
+ {
+ sendto_one(sptr, ":%s NOTICE %s :Channel %s has the anonymous flag set.", ME, chptr->chname, chptr->chname);
+ sendto_one(sptr, ":%s NOTICE %s :Be aware that anonymity on IRC is NOT securely enforced!", ME, chptr->chname);
+ }
+ }
+ /*
+ ** notify other servers
+ */
+ if (index(name, ':') || *chptr->chname == '!') /* compat */
+ sendto_match_servs(chptr, cptr, ":%s JOIN :%s%s",
+ parv[0], name, chop);
+ else if (*chptr->chname != '&')
+ {
+ if (*cbuf)
+ strcat(cbuf, ",");
+ strcat(cbuf, name);
+ if (chop)
+ strcat(cbuf, chop);
+ }
+ }
+ if (*cbuf)
+ sendto_serv_butone(cptr, ":%s JOIN :%s", parv[0], cbuf);
+ return 2;
+}
+
+/*
+** m_njoin
+** parv[0] = sender prefix
+** parv[1] = channel
+** parv[2] = channel members and modes
+*/
+int m_njoin(cptr, sptr, parc, parv)
+Reg aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ char nbuf[BUFSIZE], *q, *name, *target, *p, mbuf[4];
+ int chop, cnt = 0, nj = 0;
+ aChannel *chptr = NULL;
+ aClient *acptr;
+
+ if (parc < 3 || *parv[2] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),"NJOIN");
+ return 1;
+ }
+ *nbuf = '\0'; q = nbuf;
+ for (target = strtoken(&p, parv[2], ","); target;
+ target = strtoken(&p, NULL, ","))
+ {
+ /* check for modes */
+ chop = 0;
+ mbuf[0] = '\0';
+ if (*target == '@')
+ {
+ if (*(target+1) == '@')
+ {
+ /* actually never sends in a JOIN ^G */
+ if (*(target+2) == '+')
+ {
+ strcpy(mbuf, "\007Ov");
+ chop = CHFL_UNIQOP|CHFL_CHANOP| \
+ CHFL_VOICE;
+ name = target + 3;
+ }
+ else
+ {
+ strcpy(mbuf, "\007O");
+ chop = CHFL_UNIQOP|CHFL_CHANOP;
+ name = target + 2;
+ }
+ }
+ else
+ {
+ if (*(target+1) == '+')
+ {
+ strcpy(mbuf, "\007ov");
+ chop = CHFL_CHANOP|CHFL_VOICE;
+ name = target+2;
+ }
+ else
+ {
+ strcpy(mbuf, "\007o");
+ chop = CHFL_CHANOP;
+ name = target+1;
+ }
+ }
+ }
+ else if (*target == '+')
+ {
+ strcpy(mbuf, "\007v");
+ chop = CHFL_VOICE;
+ name = target+1;
+ }
+ else
+ name = target;
+ /* find user */
+ if (!(acptr = find_person(name, (aClient *)NULL)))
+ continue;
+ /* is user who we think? */
+ if (acptr->from != cptr)
+ continue;
+ /* get channel pointer */
+ if (!chptr)
+ {
+ if (check_channelmask(sptr, cptr, parv[1]) == -1)
+ {
+ sendto_flag(SCH_DEBUG,
+ "received NJOIN for %s from %s",
+ parv[1],
+ get_client_name(cptr, TRUE));
+ return 0;
+ }
+ chptr = get_channel(acptr, parv[1], CREATE);
+ if (!IsChannelName(parv[1]) || chptr == NULL)
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL,
+ parv[0]), parv[1]);
+ return 0;
+ }
+ }
+ /* make sure user isn't already on channel */
+ if (IsMember(acptr, chptr))
+ {
+ sendto_flag(SCH_ERROR, "NJOIN protocol error from %s",
+ get_client_name(cptr, TRUE));
+ sendto_one(cptr, "ERROR :NJOIN protocol error");
+ continue;
+ }
+ /* add user to channel */
+ add_user_to_channel(chptr, acptr, UseModes(parv[1]) ? chop :0);
+ /* build buffer for NJOIN capable servers */
+ if (q != nbuf)
+ *q++ = ',';
+ while (*target)
+ *q++ = *target++;
+ /* send 2.9 style join to other servers */
+ if (*chptr->chname != '!')
+ nj = sendto_match_servs_notv(chptr, cptr, SV_NJOIN,
+ ":%s JOIN %s%s", name,
+ parv[1],
+ UseModes(parv[1]) ? mbuf :
+ "");
+ /* send join to local users on channel */
+ sendto_channel_butserv(chptr, acptr, ":%s JOIN %s", name,
+ parv[1]);
+ /* build MODE for local users on channel, eventually send it */
+ if (*mbuf)
+ {
+ if (!UseModes(parv[1]))
+ {
+ sendto_one(cptr, err_str(ERR_NOCHANMODES,
+ parv[0]), parv[1]);
+ continue;
+ }
+ switch (cnt)
+ {
+ case 0:
+ *parabuf = '\0'; *modebuf = '\0';
+ /* fall through */
+ case 1:
+ strcat(modebuf, mbuf+1);
+ cnt += strlen(mbuf+1);
+ if (*parabuf)
+ {
+ strcat(parabuf, " ");
+ }
+ strcat(parabuf, name);
+ if (mbuf[2])
+ {
+ strcat(parabuf, " ");
+ strcat(parabuf, name);
+ }
+ break;
+ case 2:
+ sendto_channel_butserv(chptr, &me,
+ ":%s MODE %s +%s%c %s %s",
+ sptr->name, parv[1],
+ modebuf, mbuf[1],
+ parabuf, name);
+ if (mbuf[2])
+ {
+ strcpy(modebuf, mbuf+2);
+ strcpy(parabuf, name);
+ cnt = 1;
+ }
+ else
+ cnt = 0;
+ break;
+ }
+ if (cnt == 3)
+ {
+ sendto_channel_butserv(chptr, &me,
+ ":%s MODE %s +%s %s",
+ sptr->name, parv[1],
+ modebuf, parabuf);
+ cnt = 0;
+ }
+ }
+ }
+ /* send eventual MODE leftover */
+ if (cnt)
+ sendto_channel_butserv(chptr, &me, ":%s MODE %s +%s %s",
+ sptr->name, parv[1], modebuf, parabuf);
+
+ /* send NJOIN to capable servers */
+ *q = '\0';
+ if (nbuf[0])
+ sendto_match_servs_v(chptr, cptr, SV_NJOIN, ":%s NJOIN %s :%s",
+ parv[0], parv[1], nbuf);
+ return 0;
+}
+
+/*
+** m_part
+** parv[0] = sender prefix
+** parv[1] = channel
+*/
+int m_part(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ Reg aChannel *chptr;
+ char *p = NULL, *name, *comment = "";
+
+ if (parc < 2 || parv[1][0] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "PART");
+ return 1;
+ }
+
+ *buf = '\0';
+
+ for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ chptr = get_channel(sptr, name, 0);
+ if (!chptr)
+ {
+ if (MyPerson(sptr))
+ sendto_one(sptr,
+ err_str(ERR_NOSUCHCHANNEL, parv[0]),
+ name);
+ continue;
+ }
+ if (check_channelmask(sptr, cptr, name))
+ continue;
+ if (!IsMember(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL, parv[0]),
+ name);
+ continue;
+ }
+ comment = (BadPtr(parv[2])) ? parv[0] : parv[2];
+ if (IsAnonymous(chptr) && (comment == parv[0]))
+ comment = "None";
+ if (strlen(comment) > (size_t) TOPICLEN)
+ comment[TOPICLEN] = '\0';
+
+ /*
+ ** Remove user from the old channel (if any)
+ */
+ if (!index(name, ':') && (*chptr->chname != '!'))
+ { /* channel:*.mask */
+ if (*name != '&')
+ {
+ if (*buf)
+ (void)strcat(buf, ",");
+ (void)strcat(buf, name);
+ }
+ }
+ else
+ sendto_match_servs(chptr, cptr, PartFmt,
+ parv[0], name, comment);
+ sendto_channel_butserv(chptr, sptr, PartFmt,
+ parv[0], name, comment);
+ remove_user_from_channel(sptr, chptr);
+ }
+ if (*buf)
+ sendto_serv_butone(cptr, PartFmt, parv[0], buf, comment);
+ return 4;
+ }
+
+/*
+** m_kick
+** parv[0] = sender prefix
+** parv[1] = channel
+** parv[2] = client to kick
+** parv[3] = kick comment
+*/
+int m_kick(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *who;
+ aChannel *chptr;
+ int chasing = 0, penalty = 0;
+ char *comment, *name, *p = NULL, *user, *p2 = NULL;
+ int mlen, len = 0, nlen;
+
+ if (parc < 3 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "KICK");
+ return 1;
+ }
+ if (IsServer(sptr))
+ sendto_flag(SCH_NOTICE, "KICK from %s for %s %s",
+ parv[0], parv[1], parv[2]);
+ comment = (BadPtr(parv[3])) ? parv[0] : parv[3];
+ if (strlen(comment) > (size_t) TOPICLEN)
+ comment[TOPICLEN] = '\0';
+
+ *nickbuf = *buf = '\0';
+ mlen = 7 + strlen(parv[0]);
+
+ for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ if (penalty++ >= MAXPENALTY && MyPerson(sptr))
+ break;
+ chptr = get_channel(sptr, name, !CREATE);
+ if (!chptr)
+ {
+ if (MyPerson(sptr))
+ sendto_one(sptr,
+ err_str(ERR_NOSUCHCHANNEL, parv[0]),
+ name);
+ continue;
+ }
+ if (check_channelmask(sptr, cptr, name))
+ continue;
+ if (!UseModes(name))
+ {
+ sendto_one(sptr, err_str(ERR_NOCHANMODES, parv[0]),
+ name);
+ continue;
+ }
+ if (!IsServer(sptr) && !is_chan_op(sptr, chptr))
+ {
+ if (!IsMember(sptr, chptr))
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL,
+ parv[0]), chptr->chname);
+ else
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED,
+ parv[0]), chptr->chname);
+ continue;
+ }
+ if (len + mlen + strlen(name) < (size_t) BUFSIZE / 2)
+ {
+ if (*buf)
+ (void)strcat(buf, ",");
+ (void)strcat(buf, name);
+ len += strlen(name) + 1;
+ }
+ else
+ continue;
+ nlen = 0;
+
+ for (; (user = strtoken(&p2, parv[2], ",")); parv[2] = NULL)
+ {
+ penalty++;
+ if (!(who = find_chasing(sptr, user, &chasing)))
+ continue; /* No such user left! */
+ if (nlen + mlen + strlen(who->name) >
+ (size_t) BUFSIZE - NICKLEN)
+ continue;
+ if (IsMember(who, chptr))
+ {
+ sendto_channel_butserv(chptr, sptr,
+ ":%s KICK %s %s :%s", parv[0],
+ name, who->name, comment);
+ /* Don't send &local &kicks out */
+ if (*chptr->chname != '&' &&
+ *chptr->chname != '!' &&
+ index(chptr->chname, ':') == NULL) {
+ if (*nickbuf)
+ (void)strcat(nickbuf, ",");
+ (void)strcat(nickbuf, who->name);
+ nlen += strlen(who->name);
+ }
+ else
+ sendto_match_servs(chptr, cptr,
+ ":%s KICK %s %s :%s",
+ parv[0], name,
+ who->name, comment);
+ remove_user_from_channel(who,chptr);
+ penalty += 2;
+ if (penalty > MAXPENALTY && MyPerson(sptr))
+ break;
+ }
+ else
+ sendto_one(sptr,
+ err_str(ERR_USERNOTINCHANNEL,
+ parv[0]), user, name);
+ } /* loop on parv[2] */
+ } /* loop on parv[1] */
+
+ if (*buf && *nickbuf)
+ sendto_serv_butone(cptr, ":%s KICK %s %s :%s",
+ parv[0], buf, nickbuf, comment);
+ return penalty;
+}
+
+int count_channels(sptr)
+aClient *sptr;
+{
+Reg aChannel *chptr;
+ Reg int count = 0;
+
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ {
+ if (chptr->users) /* don't count channels in history */
+#ifdef SHOW_INVISIBLE_LUSERS
+ if (SecretChannel(chptr))
+ {
+ if (IsAnOper(sptr))
+ count++;
+ }
+ else
+#endif
+ count++;
+ }
+ return (count);
+}
+
+/*
+** m_topic
+** parv[0] = sender prefix
+** parv[1] = topic text
+*/
+int m_topic(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ aChannel *chptr = NullChn;
+ char *topic = NULL, *name, *p = NULL;
+ int penalty = 1;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
+ "TOPIC");
+ return 1;
+ }
+
+ parv[1] = canonize(parv[1]);
+
+ for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ if (!UseModes(name))
+ {
+ sendto_one(sptr, err_str(ERR_NOCHANMODES, parv[0]),
+ name);
+ continue;
+ }
+ if (parc > 1 && IsChannelName(name))
+ {
+ chptr = find_channel(name, NullChn);
+ if (!chptr || !IsMember(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL,
+ parv[0]), name);
+ continue;
+ }
+ if (parc > 2)
+ topic = parv[2];
+ }
+
+ if (!chptr)
+ {
+ sendto_one(sptr, rpl_str(RPL_NOTOPIC, parv[0]), name);
+ return penalty;
+ }
+
+ if (check_channelmask(sptr, cptr, name))
+ continue;
+
+ if (!topic) /* only asking for topic */
+ {
+ if (chptr->topic[0] == '\0')
+ sendto_one(sptr, rpl_str(RPL_NOTOPIC, parv[0]),
+ chptr->chname);
+ else
+ sendto_one(sptr, rpl_str(RPL_TOPIC, parv[0]),
+ chptr->chname, chptr->topic);
+ }
+ else if ((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
+ is_chan_op(sptr, chptr))
+ { /* setting a topic */
+ strncpyzt(chptr->topic, topic, sizeof(chptr->topic));
+ sendto_match_servs(chptr, 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);
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_TOPIC,
+ NULL, sptr, ":%s TOPIC %s :%s",
+ parv[0], chptr->chname,
+ chptr->topic);
+#endif
+ penalty += 2;
+ }
+ else
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED, parv[0]),
+ chptr->chname);
+ }
+ return penalty;
+ }
+
+/*
+** m_invite
+** parv[0] - sender prefix
+** parv[1] - user to invite
+** parv[2] - channel number
+*/
+int m_invite(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+ aChannel *chptr;
+
+ if (parc < 3 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
+ "INVITE");
+ return 1;
+ }
+
+ if (!(acptr = find_person(parv[1], (aClient *)NULL)))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK, parv[0]), parv[1]);
+ return 1;
+ }
+ clean_channelname(parv[2]);
+ if (check_channelmask(sptr, acptr->user->servp->bcptr, parv[2]))
+ return 1;
+ if (*parv[2] == '&' && !MyClient(acptr))
+ return 1;
+ chptr = find_channel(parv[2], NullChn);
+
+ if (!chptr)
+ {
+ sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s",
+ parv[0], parv[1], parv[2]);
+ if (MyConnect(sptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_INVITING, parv[0]),
+ acptr->name, parv[2]);
+ if (acptr->user->flags & FLAGS_AWAY)
+ sendto_one(sptr, rpl_str(RPL_AWAY, parv[0]),
+ acptr->name, (acptr->user->away) ?
+ acptr->user->away : "Gone");
+ }
+ return 3;
+ }
+
+ if (!IsMember(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTONCHANNEL, parv[0]), parv[2]);
+ return 1;
+ }
+
+ if (IsMember(acptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_USERONCHANNEL, parv[0]),
+ parv[1], parv[2]);
+ return 1;
+ }
+
+ if ((chptr->mode.mode & MODE_INVITEONLY) && !is_chan_op(sptr, chptr))
+ {
+ sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED,
+ parv[0]), chptr->chname);
+ return 1;
+ }
+
+ if (MyConnect(sptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_INVITING, parv[0]),
+ acptr->name, ((chptr) ? (chptr->chname) : parv[2]));
+ if (acptr->user->flags & FLAGS_AWAY)
+ sendto_one(sptr, rpl_str(RPL_AWAY, parv[0]),
+ acptr->name,
+ (acptr->user->away) ? acptr->user->away :
+ "Gone");
+ }
+
+ if (MyConnect(acptr))
+ if (chptr && /* (chptr->mode.mode & MODE_INVITEONLY) && */
+ sptr->user && is_chan_op(sptr, chptr))
+ add_invite(acptr, chptr);
+
+ sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s",parv[0],
+ acptr->name, ((chptr) ? (chptr->chname) : parv[2]));
+ return 2;
+}
+
+
+/*
+** m_list
+** parv[0] = sender prefix
+** parv[1] = channel
+*/
+int m_list(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ aChannel *chptr;
+ char *name, *p = NULL;
+ int rlen = 0;
+
+ if (parc > 1 &&
+ hunt_server(cptr, sptr, ":%s LIST %s %s", 2, parc, parv))
+ return 10;
+ if (BadPtr(parv[1]))
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ {
+ if (!sptr->user ||
+ !chptr->users || /* empty locked channel */
+ (SecretChannel(chptr) && !IsMember(sptr, chptr)))
+ continue;
+ name = ShowChannel(sptr, chptr) ? chptr->chname : NULL;
+ rlen += sendto_one(sptr, rpl_str(RPL_LIST, parv[0]),
+ name ? name : "*", chptr->users,
+ name ? chptr->topic : "");
+ if (!MyConnect(sptr) && rlen > CHREPLLEN)
+ break;
+ }
+ else {
+ parv[1] = canonize(parv[1]);
+ for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ chptr = find_channel(name, NullChn);
+ if (chptr && ShowChannel(sptr, chptr) && sptr->user)
+ {
+ rlen += sendto_one(sptr, rpl_str(RPL_LIST,
+ parv[0]), name,
+ chptr->users, chptr->topic);
+ if (!MyConnect(sptr) && rlen > CHREPLLEN)
+ break;
+ }
+ if (*name == '!')
+ {
+ chptr = NULL;
+ while (chptr=hash_find_channels(name+1, chptr))
+ {
+ int scr = SecretChannel(chptr) &&
+ !IsMember(sptr, chptr);
+ rlen += sendto_one(sptr,
+ rpl_str(RPL_LIST,
+ parv[0]),
+ chptr->chname,
+ (scr) ? -1 :
+ chptr->users,
+ (scr) ? "" :
+ chptr->topic);
+ if (!MyConnect(sptr) &&
+ rlen > CHREPLLEN)
+ break;
+ }
+ }
+ }
+ }
+ if (!MyConnect(sptr) && rlen > CHREPLLEN)
+ sendto_one(sptr, err_str(ERR_TOOMANYMATCHES, parv[0]),
+ !BadPtr(parv[1]) ? parv[1] : "*");
+ sendto_one(sptr, rpl_str(RPL_LISTEND, parv[0]));
+ return 2;
+ }
+
+
+/************************************************************************
+ * m_names() - Added by Jto 27 Apr 1989
+ ************************************************************************/
+
+/*
+** m_names
+** parv[0] = sender prefix
+** parv[1] = channel
+*/
+int m_names(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aChannel *chptr;
+ Reg aClient *c2ptr;
+ Reg Link *lp;
+ aChannel *ch2ptr = NULL;
+ int idx, flag, len, mlen, rlen = 0;
+ char *s, *para = parc > 1 ? parv[1] : NULL;
+
+ if (parc > 1 &&
+ hunt_server(cptr, sptr, ":%s NAMES %s %s", 2, parc, parv))
+ return 10;
+
+ mlen = strlen(ME) + 10; /* server names + : : + spaces + "353" */
+ mlen += strlen(parv[0]);
+ if (!BadPtr(para))
+ {
+ s = index(para, ',');
+ if (s && MyConnect(sptr) && s != para)
+ {
+ parv[1] = ++s;
+ (void)m_names(cptr, sptr, parc, parv);
+ }
+ clean_channelname(para);
+ ch2ptr = find_channel(para, (aChannel *)NULL);
+ }
+
+ *buf = '\0';
+
+ /*
+ * First, do all visible channels (public and the one user self is)
+ */
+
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ {
+ if (!chptr->users || /* locked empty channel */
+ ((chptr != ch2ptr) && !BadPtr(para))) /* 'wrong' channel */
+ continue;
+ if (!MyConnect(sptr) && (BadPtr(para) || (rlen > CHREPLLEN)))
+ break;
+ if ((BadPtr(para) || !HiddenChannel(chptr)) &&
+ !ShowChannel(sptr, chptr))
+ continue; /* -- users on this are not listed */
+
+ /* Find users on same channel (defined by chptr) */
+
+ (void)strcpy(buf, "* ");
+ len = strlen(chptr->chname);
+ (void)strcpy(buf + 2, chptr->chname);
+ (void)strcpy(buf + 2 + len, " :");
+
+ if (PubChannel(chptr))
+ *buf = '=';
+ else if (SecretChannel(chptr))
+ *buf = '@';
+
+ if (IsAnonymous(chptr))
+ {
+ if ((lp = find_user_link(chptr->members, sptr)))
+ {
+ if (lp->flags & CHFL_CHANOP)
+ (void)strcat(buf, "@");
+ else if (lp->flags & CHFL_VOICE)
+ (void)strcat(buf, "+");
+ (void)strcat(buf, parv[0]);
+ }
+ rlen += strlen(buf);
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY, parv[0]), buf);
+ continue;
+ }
+ idx = len + 4; /* channel name + [@=] + 2?? */
+ flag = 1;
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ c2ptr = lp->value.cptr;
+ if (IsInvisible(c2ptr) && !IsMember(sptr,chptr))
+ continue;
+ if (lp->flags & CHFL_CHANOP)
+ {
+ (void)strcat(buf, "@");
+ idx++;
+ }
+ else if (lp->flags & CHFL_VOICE)
+ {
+ (void)strcat(buf, "+");
+ idx++;
+ }
+ (void)strncat(buf, c2ptr->name, NICKLEN);
+ idx += strlen(c2ptr->name) + 1;
+ flag = 1;
+ (void)strcat(buf," ");
+ if (mlen + idx + NICKLEN + 1 > BUFSIZE - 2)
+ {
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY,
+ parv[0]), buf);
+ (void)strncpy(buf, "* ", 3);
+ (void)strncpy(buf+2, chptr->chname,
+ len + 1);
+ (void)strcat(buf, " :");
+ if (PubChannel(chptr))
+ *buf = '=';
+ else if (SecretChannel(chptr))
+ *buf = '@';
+ idx = len + 4;
+ flag = 0;
+ }
+ }
+ if (flag)
+ {
+ rlen += strlen(buf);
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY, parv[0]), buf);
+ }
+ } /* for(channels) */
+ if (!BadPtr(para))
+ {
+ if (!MyConnect(sptr) && (rlen > CHREPLLEN))
+ sendto_one(sptr, err_str(ERR_TOOMANYMATCHES, parv[0]),
+ para);
+ sendto_one(sptr, rpl_str(RPL_ENDOFNAMES, parv[0]), para);
+ return(1);
+ }
+
+ /* Second, do all non-public, non-secret channels in one big sweep */
+
+ (void)strncpy(buf, "* * :", 6);
+ idx = 5;
+ flag = 0;
+ for (c2ptr = client; c2ptr; c2ptr = c2ptr->next)
+ {
+ aChannel *ch3ptr;
+ int showflag = 0, secret = 0;
+
+ if (!IsPerson(c2ptr) || IsInvisible(c2ptr))
+ continue;
+ if (!MyConnect(sptr) && (BadPtr(para) || (rlen > CHREPLLEN)))
+ break;
+ lp = c2ptr->user->channel;
+ /*
+ * don't show a client if they are on a secret channel or
+ * they are on a channel sptr is on since they have already
+ * been show earlier. -avalon
+ */
+ while (lp)
+ {
+ ch3ptr = lp->value.chptr;
+ if (PubChannel(ch3ptr) || IsMember(sptr, ch3ptr))
+ showflag = 1;
+ if (SecretChannel(ch3ptr))
+ secret = 1;
+ lp = lp->next;
+ }
+ if (showflag) /* have we already shown them ? */
+ continue;
+ if (secret) /* on any secret channels ? */
+ continue;
+ (void)strncat(buf, c2ptr->name, NICKLEN);
+ idx += strlen(c2ptr->name) + 1;
+ (void)strcat(buf," ");
+ flag = 1;
+ if (mlen + idx + NICKLEN > BUFSIZE - 2)
+ {
+ rlen += strlen(buf);
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY, parv[0]), buf);
+ (void)strncpy(buf, "* * :", 6);
+ idx = 5;
+ flag = 0;
+ }
+ }
+ if (flag)
+ {
+ rlen += strlen(buf);
+ sendto_one(sptr, rpl_str(RPL_NAMREPLY, parv[0]), buf);
+ }
+ if (!MyConnect(sptr) && rlen > CHREPLLEN)
+ sendto_one(sptr, err_str(ERR_TOOMANYMATCHES, parv[0]),
+ para ? para : "*");
+ /* This is broken.. remove the recursion? */
+ sendto_one(sptr, rpl_str(RPL_ENDOFNAMES, parv[0]), "*");
+ return 2;
+}
+
+void send_user_joins(cptr, user)
+aClient *cptr, *user;
+{
+ Reg Link *lp;
+ Reg aChannel *chptr;
+ Reg int cnt = 0, len = 0, clen;
+ char *mask;
+
+ *buf = ':';
+ (void)strcpy(buf+1, user->name);
+ (void)strcat(buf, " JOIN ");
+ len = strlen(user->name) + 7;
+
+ for (lp = user->user->channel; lp; lp = lp->next)
+ {
+ chptr = lp->value.chptr;
+ if (*chptr->chname == '&')
+ continue;
+ if (*chptr->chname == '!' && !(cptr->serv->version & SV_NCHAN))
+ /* in reality, testing SV_NCHAN here is pointless */
+ continue;
+ if ((mask = index(chptr->chname, ':')))
+ if (match(++mask, cptr->name))
+ continue;
+ clen = strlen(chptr->chname);
+ if ((clen + len) > (size_t) BUFSIZE - 7)
+ {
+ if (cnt)
+ sendto_one(cptr, "%s", buf);
+ *buf = ':';
+ (void)strcpy(buf+1, user->name);
+ (void)strcat(buf, " JOIN ");
+ len = strlen(user->name) + 7;
+ cnt = 0;
+ }
+ if (cnt)
+ {
+ len++;
+ (void)strcat(buf, ",");
+ }
+ (void)strcpy(buf + len, chptr->chname);
+ len += clen;
+ if (lp->flags & (CHFL_UNIQOP|CHFL_CHANOP|CHFL_VOICE))
+ {
+ buf[len++] = '\007';
+ if (lp->flags & CHFL_UNIQOP) /*this should be useless*/
+ buf[len++] = 'O';
+ if (lp->flags & CHFL_CHANOP)
+ buf[len++] = 'o';
+ if (lp->flags & CHFL_VOICE)
+ buf[len++] = 'v';
+ buf[len] = '\0';
+ }
+ cnt++;
+ }
+ if (*buf && cnt)
+ sendto_one(cptr, "%s", buf);
+
+ return;
+}
+
+#define CHECKFREQ 300
+/* consider reoping an opless !channel */
+static int
+reop_channel(now, chptr)
+time_t now;
+aChannel *chptr;
+{
+ Link *lp, op;
+
+ op.value.chptr = NULL;
+ if (chptr->users <= 5 && (now - chptr->history > DELAYCHASETIMELIMIT))
+ {
+ /* few users, no recent split: this is really a small channel */
+ char mbuf[4], nbuf[3*(NICKLEN+1)+1];
+ int cnt;
+
+ lp = chptr->members;
+ while (lp)
+ {
+ if (lp->flags & CHFL_CHANOP)
+ {
+ chptr->reop = 0;
+ return 0;
+ }
+ if (MyConnect(lp->value.cptr))
+ op.value.cptr = lp->value.cptr;
+ lp = lp->next;
+ }
+ if (op.value.cptr == NULL &&
+ ((now - chptr->reop) < LDELAYCHASETIMELIMIT))
+ /*
+ ** do nothing if no local users,
+ ** unless the reop is really overdue.
+ */
+ return 0;
+ sendto_channel_butone(&me, &me, chptr,
+ ":%s NOTICE %s :Enforcing channel mode +r (%d)",
+ ME, chptr->chname, now - chptr->reop);
+ op.flags = MODE_ADD|MODE_CHANOP;
+ lp = chptr->members;
+ cnt = 3;
+ while (lp)
+ {
+ if (cnt == 3)
+ {
+ mbuf[cnt] = '\0';
+ if (lp != chptr->members)
+ {
+ sendto_match_servs_v(chptr, NULL, SV_NCHAN,
+ ":%s MODE %s +%s %s",
+ ME, chptr->chname,
+ mbuf, nbuf);
+ sendto_channel_butserv(chptr, &me,
+ ":%s MODE %s +%s %s",
+ ME, chptr->chname,
+ mbuf, nbuf);
+ }
+ cnt = 0;
+ mbuf[0] = nbuf[0] = '\0';
+ }
+ op.value.cptr = lp->value.cptr;
+ change_chan_flag(&op, chptr);
+ mbuf[cnt++] = 'o';
+ strcat(nbuf, lp->value.cptr->name);
+ strcat(nbuf, " ");
+ lp = lp->next;
+ }
+ if (cnt)
+ {
+ mbuf[cnt] = '\0';
+ sendto_match_servs_v(chptr, NULL, SV_NCHAN,
+ ":%s MODE %s +%s %s",
+ ME, chptr->chname, mbuf, nbuf);
+ sendto_channel_butserv(chptr, &me, ":%s MODE %s +%s %s",
+ ME, chptr->chname, mbuf, nbuf);
+ }
+ }
+ else
+ {
+ time_t idlelimit = now -
+ MIN((LDELAYCHASETIMELIMIT/2), (2*CHECKFREQ));
+
+ lp = chptr->members;
+ while (lp)
+ {
+ if (lp->flags & CHFL_CHANOP)
+ {
+ chptr->reop = 0;
+ return 0;
+ }
+ if (MyConnect(lp->value.cptr) &&
+ lp->value.cptr->user->last > idlelimit &&
+ (op.value.cptr == NULL ||
+ lp->value.cptr->user->last>op.value.cptr->user->last))
+ op.value.cptr = lp->value.cptr;
+ lp = lp->next;
+ }
+ if (op.value.cptr == NULL)
+ return 0;
+ sendto_channel_butone(&me, &me, chptr,
+ ":%s NOTICE %s :Enforcing channel mode +r (%d)", ME,
+ chptr->chname, now - chptr->reop);
+ op.flags = MODE_ADD|MODE_CHANOP;
+ change_chan_flag(&op, chptr);
+ sendto_match_servs_v(chptr, NULL, SV_NCHAN, ":%s MODE %s +o %s",
+ ME, chptr->chname, op.value.cptr->name);
+ sendto_channel_butserv(chptr, &me, ":%s MODE %s +o %s",
+ ME, chptr->chname, op.value.cptr->name);
+ }
+ chptr->reop = 0;
+ return 1;
+}
+
+/*
+ * Cleanup locked channels, run frequently.
+ *
+ * A channel life is defined by its users and the history stamp.
+ * It is alive if one of the following is true:
+ * chptr->users > 0 (normal state)
+ * chptr->history >= time(NULL) (eventually locked)
+ * It is locked if empty but alive.
+ *
+ * The history stamp is set when a remote user with channel op exits.
+ */
+time_t collect_channel_garbage(now)
+time_t now;
+{
+ static u_int max_nb = 0; /* maximum of live channels */
+ static u_char split = 0;
+ Reg aChannel *chptr = channel;
+ Reg u_int cur_nb = 1, curh_nb = 0, r_cnt = 0;
+ aChannel *del_ch;
+#ifdef DEBUGMODE
+ u_int del = istat.is_hchan;
+#endif
+#define SPLITBONUS (CHECKFREQ - 50)
+
+ collect_chid();
+
+ while (chptr)
+ {
+ if (chptr->users == 0)
+ curh_nb++;
+ else
+ {
+ cur_nb++;
+ if (*chptr->chname == '!' &&
+ (chptr->mode.mode & MODE_REOP) &&
+ chptr->reop && chptr->reop <= now)
+ r_cnt += reop_channel(now, chptr);
+ }
+ chptr = chptr->nextch;
+ }
+ if (cur_nb > max_nb)
+ max_nb = cur_nb;
+
+ if (r_cnt)
+ sendto_flag(SCH_CHAN, "Re-opped %u channel(s).", r_cnt);
+
+ /*
+ ** check for huge netsplits, if so, garbage collection is not really
+ ** done but make sure there aren't too many channels kept for
+ ** history - krys
+ */
+ if ((2*curh_nb > cur_nb) && curh_nb < max_nb)
+ split = 1;
+ else
+ {
+ split = 0;
+ /* no empty channel? let's skip the while! */
+ if (curh_nb == 0)
+ {
+#ifdef DEBUGMODE
+ sendto_flag(SCH_LOCAL,
+ "Channel garbage: live %u (max %u), hist %u (extended)",
+ cur_nb - 1, max_nb - 1, curh_nb);
+#endif
+ /* Check again after CHECKFREQ seconds */
+ return (time_t) (now + CHECKFREQ);
+ }
+ }
+
+ chptr = channel;
+ while (chptr)
+ {
+ /*
+ ** In case we are likely to be split, extend channel locking.
+ ** most splits should be short, but reality seems to prove some
+ ** aren't.
+ */
+ if (!chptr->history)
+ {
+ chptr = chptr->nextch;
+ continue;
+ }
+ if (split) /* net splitted recently and we have a lock */
+ chptr->history += SPLITBONUS; /* extend lock */
+
+ if ((chptr->users == 0) && (chptr->history <= now))
+ {
+ del_ch = chptr;
+
+ chptr = del_ch->nextch;
+ free_channel(del_ch);
+ }
+ else
+ chptr = chptr->nextch;
+ }
+
+#ifdef DEBUGMODE
+ sendto_flag(SCH_LOCAL,
+ "Channel garbage: live %u (max %u), hist %u (removed %u)%s",
+ cur_nb - 1, max_nb - 1, curh_nb, del - istat.is_hchan,
+ (split) ? " split detected" : "");
+#endif
+ /* Check again after CHECKFREQ seconds */
+ return (time_t) (now + CHECKFREQ);
+}
diff --git a/ircd/channel_def.h b/ircd/channel_def.h
new file mode 100644
index 0000000..7413f16
--- /dev/null
+++ b/ircd/channel_def.h
@@ -0,0 +1,29 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/channel_def.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.
+ */
+
+#define CREATE 1 /* whether a channel should be
+ created or just tested for existance */
+
+#define MODEBUFLEN 200
+
+#define NullChn ((aChannel *)0)
+
+#define ChannelExists(n) (find_channel(n, NullChn) != NullChn)
+
+#define MAXMODEPARAMS 3
diff --git a/ircd/channel_ext.h b/ircd/channel_ext.h
new file mode 100644
index 0000000..361d387
--- /dev/null
+++ b/ircd/channel_ext.h
@@ -0,0 +1,64 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/channel_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/channel.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef CHANNEL_C
+extern aChannel *channel;
+#endif /* CHANNEL_C */
+
+/* External definitions for global functions.
+ */
+#ifndef CHANNEL_C
+#define EXTERN extern
+#else /* CHANNEL_C */
+#define EXTERN
+#endif /* CHANNEL_C */
+EXTERN void remove_user_from_channel __P((aClient *sptr, aChannel *chptr));
+EXTERN int is_chan_op __P((aClient *cptr, aChannel *chptr));
+EXTERN int has_voice __P((aClient *cptr, aChannel *chptr));
+EXTERN int can_send __P((aClient *cptr, aChannel *chptr));
+EXTERN aChannel *find_channel __P((Reg char *chname, Reg aChannel *chptr));
+EXTERN void setup_server_channels __P((aClient *mp));
+EXTERN void channel_modes __P((aClient *cptr, Reg char *mbuf, Reg char *pbuf,
+ aChannel *chptr));
+EXTERN void send_channel_modes __P((aClient *cptr, aChannel *chptr));
+EXTERN void send_channel_members __P((aClient *cptr, aChannel *chptr));
+EXTERN int m_mode __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN void clean_channelname __P((Reg char *cn));
+EXTERN void del_invite __P((aClient *cptr, aChannel *chptr));
+EXTERN int m_join __P((Reg aClient *cptr, Reg aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_njoin __P((Reg aClient *cptr, Reg aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_part __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_kick __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int count_channels __P((aClient *sptr));
+EXTERN int m_topic __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_invite __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_list __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_names __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN void send_user_joins __P((aClient *cptr, aClient *user));
+EXTERN time_t collect_channel_garbage __P((time_t now));
+#undef EXTERN
diff --git a/ircd/chkconf.c b/ircd/chkconf.c
new file mode 100644
index 0000000..7c0032d
--- /dev/null
+++ b/ircd/chkconf.c
@@ -0,0 +1,745 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: chkconf.c,v 1.13 1999/03/11 23:40:12 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define CHKCONF_C
+#include "match_ext.h"
+#undef CHKCONF_C
+
+#define MyMalloc(x) malloc(x)
+/*#define MyFree(x) free(x)*/
+
+static void new_class();
+static char *getfield(), confchar ();
+static int openconf(), validate __P((aConfItem *));
+static int dgets __P((int, char *, int));
+static aClass *get_class();
+static aConfItem *initconf();
+
+static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0;
+static char *configfile = IRCDCONF_PATH;
+static char nullfield[] = "";
+static char maxsendq[12];
+
+#define SHOWSTR(x) ((x) ? (x) : "*")
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+ if (argc > 1 && !strncmp(argv[1], "-h", 2)) {
+ (void)fprintf(stderr, "Usage: %s [-h] [-d[#]] [%s]\n",
+ argv[0], IRCDCONF_PATH);
+ exit(0);
+ }
+ new_class(0);
+
+ if (argc > 1 && !strncmp(argv[1], "-d", 2))
+ {
+ debugflag = 1;
+ if (argv[1][2])
+ debugflag = atoi(argv[1]+2);
+ argc--, argv++;
+ }
+ if (argc > 1)
+ configfile = argv[1];
+ return validate(initconf());
+}
+
+/*
+ * openconf
+ *
+ * returns -1 on any error or else the fd opened from which to read the
+ * configuration file from. This may either be th4 file direct or one end
+ * of a pipe from m4.
+ */
+static int openconf()
+{
+#ifdef M4_PREPROC
+ int pi[2];
+
+ /* ircd.m4 with full path now! Kratz */
+ if (access(IRCDM4_PATH, R_OK) == -1)
+ {
+ (void)fprintf(stderr, "%s missing.\n", IRCDM4_PATH);
+ return -1;
+ }
+ if (pipe(pi) == -1)
+ return -1;
+ switch(fork())
+ {
+ case -1 :
+ return -1;
+ case 0 :
+ (void)close(pi[0]);
+ if (pi[1] != 1)
+ {
+ (void)dup2(pi[1], 1);
+ (void)close(pi[1]);
+ }
+ (void)dup2(1,2);
+ /*
+ * m4 maybe anywhere, use execvp to find it. Any error
+ * goes out with report_error. Could be dangerous,
+ * two servers running with the same fd's >:-) -avalon
+ */
+ (void)execlp("m4", "m4", IRCDM4_PATH, configfile, 0);
+ perror("m4");
+ exit(-1);
+ default :
+ (void)close(pi[1]);
+ return pi[0];
+ }
+#else
+ return open(configfile, O_RDONLY);
+#endif
+}
+
+/*
+** initconf()
+** Read configuration file.
+**
+** returns -1, if file cannot be opened
+** 0, if file opened
+*/
+
+static aConfItem *initconf(opt)
+int opt;
+{
+ int fd;
+ char line[512], *tmp, c[80], *s;
+ int ccount = 0, ncount = 0, dh, flags = 0;
+ aConfItem *aconf = NULL, *ctop = NULL;
+
+ (void)fprintf(stderr, "initconf(): ircd.conf = %s\n", configfile);
+ if ((fd = openconf()) == -1)
+ {
+#ifdef M4_PREPROC
+ (void)wait(0);
+#endif
+ return NULL;
+ }
+
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ while ((dh = dgets(fd, line, sizeof(line) - 1)) > 0)
+ {
+ if (aconf)
+ {
+ if (aconf->host)
+ (void)free(aconf->host);
+ if (aconf->passwd)
+ (void)free(aconf->passwd);
+ if (aconf->name)
+ (void)free(aconf->name);
+ }
+ else
+ aconf = (aConfItem *)malloc(sizeof(*aconf));
+ aconf->host = (char *)NULL;
+ aconf->passwd = (char *)NULL;
+ aconf->name = (char *)NULL;
+ aconf->class = (aClass *)NULL;
+ if ((tmp = (char *)index(line, '\n')))
+ *tmp = 0;
+ else while(dgets(fd, c, sizeof(c) - 1))
+ if ((tmp = (char *)index(c, '\n')))
+ {
+ *tmp = 0;
+ break;
+ }
+ /*
+ * 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+1)); s++)
+ ;
+ tmp++;
+ }
+ else if (*tmp == '#')
+ *tmp = '\0';
+ }
+ if (!*line || *line == '#' || *line == '\n' ||
+ *line == ' ' || *line == '\t')
+ continue;
+
+ if (line[1] != IRCDCONF_DELIMITER)
+ {
+ (void)fprintf(stderr, "ERROR: Bad config line (%s)\n",
+ line);
+ if (IRCDCONF_DELIMITER != ':')
+ (void)fprintf(stderr,
+ "\tWrong delimiter? (should be %c)\n",
+ IRCDCONF_DELIMITER);
+ continue;
+ }
+
+ if (debugflag)
+ (void)printf("\n%s\n",line);
+ (void)fflush(stdout);
+
+ tmp = getfield(line);
+ if (!tmp)
+ {
+ (void)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 'B': /* bounce line */
+ case 'b':
+ aconf->status = CONF_BOUNCE;
+ break;
+ case 'C': /* Server where I should try to connect */
+ case 'c': /* in case of lp failures */
+ ccount++;
+ aconf->status = CONF_CONNECT_SERVER;
+ break;
+ case 'D': /* auto connect restrictions */
+ case 'd':
+ aconf->status = CONF_DENY;
+ 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 */
+ case 'k':
+ aconf->status = CONF_KILL;
+ 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;
+ case 'Q': /* a server that you don't want in your */
+ case 'q': /* network. USE WITH CAUTION! */
+ aconf->status = CONF_QUARANTINED_SERVER;
+ 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 'S': /* Service. Same semantics as */
+ case 's': /* CONF_OPERATOR */
+ aconf->status = CONF_SERVICE;
+ break;
+ case 'U': /* Uphost, ie. host where client reading */
+ case 'u': /* this should connect. */
+ /* This is for client only, I must ignore this */
+ /* ...U-line should be removed... --msa */
+ break;
+ case 'V':
+ aconf->status = CONF_VER;
+ break;
+ case 'Y':
+ case 'y':
+ aconf->status = CONF_CLASS;
+ break;
+ default:
+ (void)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))
+ aconf->class = get_class(atoi(tmp));
+ break;
+ }
+ if (!aconf->class && (aconf->status & (CONF_CONNECT_SERVER|
+ CONF_NOCONNECT_SERVER|CONF_OPS|CONF_CLIENT)))
+ {
+ (void)fprintf(stderr,
+ "\tWARNING: No class. Default 0\n");
+ aconf->class = get_class(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)
+ {
+ (void)fprintf(stderr,"\tERROR: no class #\n");
+ continue;
+ }
+ if (!tmp)
+ {
+ (void)fprintf(stderr,
+ "\tWARNING: missing sendq field\n");
+ (void)fprintf(stderr, "\t\t default: %d\n",
+ QUEUELEN);
+ (void)sprintf(maxsendq, "%d", QUEUELEN);
+ }
+ else
+ (void)sprintf(maxsendq, "%d", atoi(tmp));
+ new_class(atoi(aconf->host));
+ aconf->class = get_class(atoi(aconf->host));
+ goto print_confline;
+ }
+
+ if (aconf->status & CONF_LISTEN_PORT)
+ {
+#ifdef UNIXPORT
+ struct stat sb;
+
+ if (!aconf->host)
+ (void)fprintf(stderr, "\tERROR: %s\n",
+ "null host field in P-line");
+ else if (index(aconf->host, '/'))
+ {
+ if (stat(aconf->host, &sb) == -1)
+ {
+ (void)fprintf(stderr, "\tERROR: (%s) ",
+ aconf->host);
+ perror("stat");
+ }
+ else if ((sb.st_mode & S_IFMT) != S_IFDIR)
+ (void)fprintf(stderr,
+ "\tERROR: %s not directory\n",
+ aconf->host);
+ }
+#else
+ if (!aconf->host)
+ (void)fprintf(stderr, "\tERROR: %s\n",
+ "null host field in P-line");
+ else if (index(aconf->host, '/'))
+ (void)fprintf(stderr, "\t%s %s\n",
+ "WARNING: / present in P-line",
+ "for non-UNIXPORT configuration");
+#endif
+ aconf->class = get_class(0);
+ goto print_confline;
+ }
+
+ if (aconf->status & CONF_SERVER_MASK &&
+ (!aconf->host || index(aconf->host, '*') ||
+ index(aconf->host, '?')))
+ {
+ (void)fprintf(stderr, "\tERROR: bad host field\n");
+ continue;
+ }
+
+ if (aconf->status & CONF_SERVER_MASK && BadPtr(aconf->passwd))
+ {
+ (void)fprintf(stderr,
+ "\tERROR: empty/no password field\n");
+ continue;
+ }
+
+ if (aconf->status & CONF_SERVER_MASK && !aconf->name)
+ {
+ (void)fprintf(stderr, "\tERROR: bad name field\n");
+ continue;
+ }
+
+ if (aconf->status & (CONF_SERVER_MASK|CONF_OPS))
+ if (!index(aconf->host, '@'))
+ {
+ char *newhost;
+ int len = 3; /* *@\0 = 3 */
+
+ len += strlen(aconf->host);
+ newhost = (char *)MyMalloc(len);
+ (void)sprintf(newhost, "*@%s", aconf->host);
+ (void)free(aconf->host);
+ aconf->host = newhost;
+ }
+
+ if (!aconf->class)
+ aconf->class = get_class(0);
+ (void)sprintf(maxsendq, "%d", aconf->class->class);
+
+ if (!aconf->name)
+ aconf->name = nullfield;
+ if (!aconf->passwd)
+ aconf->passwd = nullfield;
+ if (!aconf->host)
+ aconf->host = nullfield;
+ if (aconf->status & (CONF_ME|CONF_ADMIN))
+ {
+ if (flags & aconf->status)
+ (void)fprintf(stderr,
+ "ERROR: multiple %c-lines\n",
+ toupper(confchar(aconf->status)));
+ else
+ flags |= aconf->status;
+ }
+
+ if (aconf->status & CONF_VER)
+ {
+ if (*aconf->host && !index(aconf->host, '/'))
+ (void)fprintf(stderr,
+ "\tWARNING: No / in V line.");
+ else if (*aconf->passwd && !index(aconf->passwd, '/'))
+ (void)fprintf(stderr,
+ "\tWARNING: No / in V line.");
+ }
+print_confline:
+ if (debugflag > 8)
+ (void)printf("(%d) (%s) (%s) (%s) (%d) (%s)\n",
+ aconf->status, aconf->host, aconf->passwd,
+ aconf->name, aconf->port, maxsendq);
+ (void)fflush(stdout);
+ if (aconf->status & (CONF_SERVER_MASK|CONF_HUB|CONF_LEAF))
+ {
+ aconf->next = ctop;
+ ctop = aconf;
+ aconf = NULL;
+ }
+ }
+ (void)close(fd);
+#ifdef M4_PREPROC
+ (void)wait(0);
+#endif
+ return ctop;
+}
+
+static aClass *get_class(cn)
+int cn;
+{
+ static aClass cls;
+ int i = numclasses - 1;
+
+ cls.class = -1;
+ for (; i >= 0; i--)
+ if (classarr[i] == cn)
+ {
+ cls.class = cn;
+ break;
+ }
+ if (i == -1)
+ (void)fprintf(stderr,"\tWARNING: class %d not found\n", cn);
+ return &cls;
+}
+
+static void new_class(cn)
+int cn;
+{
+ numclasses++;
+ if (classarr)
+ classarr = (int *)realloc(classarr, sizeof(int) * numclasses);
+ else
+ classarr = (int *)malloc(sizeof(int));
+ classarr[numclasses-1] = cn;
+}
+
+/*
+ * field breakup for ircd.conf file.
+ */
+static char *getfield(irc_newline)
+char *irc_newline;
+{
+ static char *line = NULL;
+ char *end, *field;
+
+ if (irc_newline)
+ line = irc_newline;
+ if (line == NULL)
+ return(NULL);
+
+ field = line;
+ if ((end = (char *)index(line, IRCDCONF_DELIMITER)) == NULL)
+ {
+ line = NULL;
+ if ((end = (char *)index(field,'\n')) == NULL)
+ end = field + strlen(field);
+ }
+ else
+ line = end + 1;
+ *end = '\0';
+ return(field);
+}
+
+
+/*
+** read a string terminated by \r or \n in from a fd
+**
+** Created: Sat Dec 12 06:29:58 EST 1992 by avalon
+** Returns:
+** 0 - EOF
+** -1 - error on read
+** >0 - number of bytes returned (<=num)
+** After opening a fd, it is necessary to init dgets() by calling it as
+** dgets(x,y,0);
+** to mark the buffer as being empty.
+*/
+static int dgets(fd, buf, num)
+int fd, num;
+char *buf;
+{
+ static char dgbuf[8192];
+ static char *head = dgbuf, *tail = dgbuf;
+ register char *s, *t;
+ register int n, nr;
+
+ /*
+ ** Sanity checks.
+ */
+ if (head == tail)
+ *head = '\0';
+ if (!num)
+ {
+ head = tail = dgbuf;
+ *head = '\0';
+ return 0;
+ }
+ if (num > sizeof(dgbuf) - 1)
+ num = sizeof(dgbuf) - 1;
+dgetsagain:
+ if (head > dgbuf)
+ {
+ for (nr = tail - head, s = head, t = dgbuf; nr > 0; nr--)
+ *t++ = *s++;
+ tail = t;
+ head = dgbuf;
+ }
+ /*
+ ** check input buffer for EOL and if present return string.
+ */
+ if (head < tail &&
+ ((s = index(head, '\n')) || (s = index(head, '\r'))) && s < tail)
+ {
+ n = MIN(s - head + 1, num); /* at least 1 byte */
+dgetsreturnbuf:
+ bcopy(head, buf, n);
+ head += n;
+ if (head == tail)
+ head = tail = dgbuf;
+ return n;
+ }
+
+ if (tail - head >= num) /* dgets buf is big enough */
+ {
+ n = num;
+ goto dgetsreturnbuf;
+ }
+
+ n = sizeof(dgbuf) - (tail - dgbuf) - 1;
+ nr = read(fd, tail, n);
+ if (nr == -1)
+ {
+ head = tail = dgbuf;
+ return -1;
+ }
+ if (!nr)
+ {
+ if (head < tail)
+ {
+ n = MIN(head - tail, num);
+ goto dgetsreturnbuf;
+ }
+ head = tail = dgbuf;
+ return 0;
+ }
+ tail += nr;
+ *tail = '\0';
+ for (t = head; (s = index(t, '\n')); )
+ {
+ if ((s > head) && (s > dgbuf))
+ {
+ t = s-1;
+ for (nr = 0; *t == '\\'; nr++)
+ t--;
+ if (nr & 1)
+ {
+ t = s+1;
+ s--;
+ nr = tail - t;
+ while (nr--)
+ *s++ = *t++;
+ tail -= 2;
+ *tail = '\0';
+ }
+ else
+ s++;
+ }
+ else
+ s++;
+ t = s;
+ }
+ *tail = '\0';
+ goto dgetsagain;
+}
+
+
+static int validate(top)
+aConfItem *top;
+{
+ Reg aConfItem *aconf, *bconf;
+ u_int otype = 0, 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;
+
+ for (bconf = top; bconf; bconf = bconf->next)
+ {
+ if (bconf == aconf || !(bconf->status & otype))
+ continue;
+ if (bconf->class == aconf->class &&
+ !mycmp(bconf->name, aconf->name) &&
+ !mycmp(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 (!mycmp(bconf->name, aconf->name))
+ {
+ aconf->status |= CONF_MATCH;
+ break;
+ }
+ }
+ }
+
+ (void) fprintf(stderr, "\n");
+ for (aconf = top; aconf; aconf = aconf->next)
+ if (aconf->status & CONF_MATCH)
+ valid++;
+ else
+ (void)fprintf(stderr, "Unmatched %c:%s:%s:%s\n",
+ confchar(aconf->status), aconf->host,
+ SHOWSTR(aconf->passwd), aconf->name);
+ return valid ? 0 : -1;
+}
+
+static char confchar(status)
+u_int status;
+{
+ static char letrs[] = "QIiCcNoOMKARYSLPHV";
+ char *s = letrs;
+
+ status &= ~(CONF_MATCH|CONF_ILLEGAL);
+
+ for (; *s; s++, status >>= 1)
+ if (status & 1)
+ return *s;
+ return '-';
+}
+
+void outofmemory()
+{
+ (void)write(2, "Out of memory\n", 14);
+ exit(-1);
+}
diff --git a/ircd/class.c b/ircd/class.c
new file mode 100644
index 0000000..e3457c8
--- /dev/null
+++ b/ircd/class.c
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: class.c,v 1.6 1997/12/19 13:35:57 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define CLASS_C
+#include "s_externs.h"
+#undef CLASS_C
+
+#define BAD_CONF_CLASS -1
+#define BAD_PING -2
+#define BAD_CLIENT_CLASS -3
+
+aClass *classes;
+
+int get_conf_class(aconf)
+aConfItem *aconf;
+{
+ if ((aconf) && Class(aconf))
+ return (ConfClass(aconf));
+
+ Debug((DEBUG_DEBUG,"No Class For %s",
+ (aconf) ? aconf->name : "*No Conf*"));
+
+ return (BAD_CONF_CLASS);
+
+}
+
+static int get_conf_ping(aconf)
+aConfItem *aconf;
+{
+ if ((aconf) && Class(aconf))
+ return (ConfPingFreq(aconf));
+
+ Debug((DEBUG_DEBUG,"No Ping For %s",
+ (aconf) ? aconf->name : "*No Conf*"));
+
+ return (BAD_PING);
+}
+
+
+
+int get_client_class(acptr)
+aClient *acptr;
+{
+ Reg Link *tmp;
+ Reg aClass *cl;
+ int retc = BAD_CLIENT_CLASS;
+
+ if (acptr && !IsMe(acptr) && (acptr->confs))
+ for (tmp = acptr->confs; tmp; tmp = tmp->next)
+ {
+ if (!tmp->value.aconf ||
+ !(cl = tmp->value.aconf->class))
+ continue;
+ if (Class(cl) > retc)
+ retc = Class(cl);
+ }
+
+ Debug((DEBUG_DEBUG,"Returning Class %d For %s",retc,acptr->name));
+
+ return (retc);
+}
+
+int get_client_ping(acptr)
+aClient *acptr;
+{
+ 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|
+ CONF_ZCONNECT_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);
+}
+
+int get_con_freq(clptr)
+aClass *clptr;
+{
+ if (clptr)
+ return (MAX(60, 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(class, ping, confreq, maxli, sendq, hlocal, uhlocal,
+ hglobal, uhglobal)
+int class, ping, confreq, maxli, hlocal, uhlocal, hglobal, uhglobal;
+long sendq;
+{
+ aClass *t, *p;
+
+ t = find_class(class);
+ if ((t == classes) && (class != 0))
+ {
+ p = (aClass *)make_class();
+ NextClass(p) = NextClass(t);
+ NextClass(t) = p;
+ MaxSendq(p) = QUEUELEN;
+ istat.is_class++;
+ }
+ else
+ p = t;
+ Debug((DEBUG_DEBUG,
+"Add Class %d: p %x t %x - cf: %d pf: %d ml: %d sq: %l ml: %d.%d mg: %d.%d",
+ class, p, t, confreq, ping, maxli, QUEUELEN, hlocal, uhlocal,
+ hglobal, uhglobal));
+ Class(p) = class;
+ ConFreq(p) = confreq;
+ PingFreq(p) = ping;
+ MaxLinks(p) = maxli;
+ if (sendq)
+ MaxSendq(p) = sendq;
+ MaxHLocal(p) = hlocal;
+ MaxUHLocal(p) = uhlocal;
+ MaxHGlobal(p) = hglobal;
+ MaxUHGlobal(p) = uhglobal;
+ if (p != t)
+ Links(p) = 0;
+}
+
+aClass *find_class(cclass)
+int cclass;
+{
+ aClass *cltmp;
+
+ for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp))
+ if (Class(cltmp) == cclass)
+ return cltmp;
+ return classes;
+}
+
+void check_class()
+{
+ Reg aClass *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: %ld",
+ Class(cltmp), ConFreq(cltmp), PingFreq(cltmp),
+ MaxLinks(cltmp), Links(cltmp), MaxSendq(cltmp)));
+ if (MaxLinks(cltmp) < 0)
+ {
+ NextClass(cltmp2) = NextClass(cltmp);
+ if (Links(cltmp) <= 0)
+ {
+ free_class(cltmp);
+ istat.is_class--;
+ }
+ }
+ else
+ cltmp2 = cltmp;
+ }
+}
+
+void initclass()
+{
+ classes = (aClass *)make_class();
+ istat.is_class++;
+
+ Class(FirstClass()) = 0;
+ ConFreq(FirstClass()) = CONNECTFREQUENCY;
+ PingFreq(FirstClass()) = PINGFREQUENCY;
+ MaxLinks(FirstClass()) = MAXIMUM_LINKS;
+ MaxSendq(FirstClass()) = QUEUELEN;
+ Links(FirstClass()) = 0;
+ NextClass(FirstClass()) = NULL;
+}
+
+void report_classes(sptr, to)
+aClient *sptr;
+char *to;
+{
+ Reg aClass *cltmp;
+
+ for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp))
+ sendto_one(sptr, rpl_str(RPL_STATSYLINE, to), 'Y',
+ Class(cltmp), PingFreq(cltmp), ConFreq(cltmp),
+ MaxLinks(cltmp), MaxSendq(cltmp),
+ MaxHLocal(cltmp), MaxUHLocal(cltmp),
+ MaxHGlobal(cltmp), MaxUHGlobal(cltmp));
+}
+
+long get_sendq(cptr)
+aClient *cptr;
+{
+ Reg int sendq = QUEUELEN, retc = BAD_CLIENT_CLASS;
+ Reg Link *tmp;
+ Reg aClass *cl;
+
+ if (cptr->serv)
+ sendq = MaxSendq(cptr->serv->nline->class);
+ else if (cptr && !IsMe(cptr) && (cptr->confs))
+ for (tmp = cptr->confs; tmp; tmp = tmp->next)
+ {
+ if (!tmp->value.aconf ||
+ !(cl = tmp->value.aconf->class))
+ continue;
+ if (Class(cl) > retc)
+ sendq = MaxSendq(cl);
+ }
+ return sendq;
+}
diff --git a/ircd/class_ext.h b/ircd/class_ext.h
new file mode 100644
index 0000000..6d9fadb
--- /dev/null
+++ b/ircd/class_ext.h
@@ -0,0 +1,49 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/class_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/class.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef CLASS_C
+extern aClass *classes;
+#endif /* CLASS_C */
+
+/* External definitions for global functions.
+ */
+#ifndef CLASS_C
+#define EXTERN extern
+#else /* CLASS_C */
+#define EXTERN
+#endif /* CLASS_C */
+EXTERN int get_conf_class __P((aConfItem *aconf));
+EXTERN int get_client_class __P((aClient *acptr));
+EXTERN int get_client_ping __P((aClient *acptr));
+EXTERN int get_con_freq __P((aClass *clptr));
+EXTERN void add_class __P((int class, int ping, int confreq, int maxli,
+ long sendq, int hlocal, int uhlocal,
+ int hglobal, int uhglobal));
+EXTERN aClass *find_class __P((int cclass));
+EXTERN void check_class();
+EXTERN void initclass();
+EXTERN void report_classes __P((aClient *sptr, char *to));
+EXTERN long get_sendq __P((aClient *cptr));
+#undef EXTERN
diff --git a/ircd/hash.c b/ircd/hash.c
new file mode 100644
index 0000000..9d31f47
--- /dev/null
+++ b/ircd/hash.c
@@ -0,0 +1,940 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/hash.c
+ * 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.
+ */
+#ifndef lint
+static char rcsid[] = "@(#)$Id: hash.c,v 1.15 1999/06/25 21:50:01 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define HASH_C
+#include "s_externs.h"
+#undef HASH_C
+
+static aHashEntry *clientTable = NULL;
+static aHashEntry *channelTable = NULL;
+static aHashEntry *serverTable = NULL;
+static unsigned int *hashtab = NULL;
+static int clhits = 0, clmiss = 0, clsize = 0;
+static int chhits = 0, chmiss = 0, chsize = 0;
+static int svsize = 0;
+int _HASHSIZE = 0;
+int _CHANNELHASHSIZE = 0;
+int _SERVERSIZE = 0;
+
+/*
+ * Hashing.
+ *
+ * The server uses a chained hash table to provide quick and efficient
+ * hash table mantainence (providing the hash function works evenly over
+ * the input range). The hash table is thus not susceptible to problems
+ * of filling all the buckets or the need to rehash.
+ * It is expected that the hash table would look somehting like this
+ * during use:
+ * +-----+ +-----+ +-----+ +-----+
+ * ---| 224 |----| 225 |----| 226 |---| 227 |---
+ * +-----+ +-----+ +-----+ +-----+
+ * | | |
+ * +-----+ +-----+ +-----+
+ * | A | | C | | D |
+ * +-----+ +-----+ +-----+
+ * |
+ * +-----+
+ * | B |
+ * +-----+
+ *
+ * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
+ *
+ * The order shown above is just one instant of the server. Each time a
+ * lookup is made on an entry in the hash table and it is found, the entry
+ * is moved to the top of the chain.
+ */
+
+/*
+ * hash_nick_name
+ *
+ * this function must be *quick*. Thus there should be no multiplication
+ * or division or modulus in the inner loop. subtraction and other bit
+ * operations allowed.
+ */
+static u_int hash_nick_name(nname, store)
+char *nname;
+int *store;
+{
+ Reg u_char *name = (u_char *)nname;
+ Reg u_char ch;
+ Reg u_int hash = 1;
+
+ for (; (ch = *name); name++)
+ {
+ hash <<= 1;
+ hash += hashtab[(int)ch];
+ }
+ /*
+ if (hash < 0)
+ hash = -hash;
+ */
+ *store = hash;
+ hash %= _HASHSIZE;
+ return (hash);
+}
+
+/*
+ * hash_channel_name
+ *
+ * calculate a hash value on at most the first 30 characters of the channel
+ * name. Most names are short than this or dissimilar in this range. There
+ * is little or no point hashing on a full channel name which maybe 255 chars
+ * long.
+ */
+static u_int hash_channel_name(hname, store, shortname)
+char *hname, shortname;
+int *store;
+{
+ Reg u_char *name = (u_char *)hname;
+ Reg u_char ch;
+ Reg int i = 30;
+ Reg u_int hash = 5;
+
+ if (*name == '!' && shortname == 0)
+ name += 1 + CHIDLEN;
+ for (; (ch = *name) && --i; name++)
+ {
+ hash <<= 1;
+ hash += hashtab[(u_int)ch] + (i << 1);
+ }
+ *store = hash;
+ hash %= _CHANNELHASHSIZE;
+ return (hash);
+}
+
+/* bigger prime
+ *
+ * given a positive integer, return a prime number that's larger
+ *
+ * 13feb94 gbl
+ */
+static int bigger_prime(size)
+int size;
+{
+ int trial, failure, sq;
+
+ if (size < 0)
+ return -1;
+
+ if (size < 4)
+ return size;
+
+ if (size % 2 == 0) /* Make sure it's odd because... */
+ size++;
+
+ for ( ; ; size += 2) /* ...no point checking even numbers - Core */
+ {
+ failure = 0;
+ sq = (int)sqrt((double)size);
+ for (trial = 2; trial <= sq ; trial++)
+ {
+ if ((size % trial) == 0)
+ {
+ failure = 1;
+ break;
+ }
+ }
+ if (!failure)
+ return size;
+ }
+ /* return -1; */ /* Never reached */
+}
+
+/*
+ * clear_*_hash_table
+ *
+ * Nullify the hashtable and its contents so it is completely empty.
+ */
+static void clear_client_hash_table(size)
+int size;
+{
+ _HASHSIZE = bigger_prime(size);
+ clhits = 0;
+ clmiss = 0;
+ if (!clientTable)
+ clientTable = (aHashEntry *)MyMalloc(_HASHSIZE *
+ sizeof(aHashEntry));
+ bzero((char *)clientTable, sizeof(aHashEntry) * _HASHSIZE);
+ Debug((DEBUG_DEBUG, "Client Hash Table Init: %d (%d)",
+ _HASHSIZE, size));
+}
+
+static void clear_channel_hash_table(size)
+int size;
+{
+ _CHANNELHASHSIZE = bigger_prime(size);
+ chmiss = 0;
+ chhits = 0;
+ if (!channelTable)
+ channelTable = (aHashEntry *)MyMalloc(_CHANNELHASHSIZE *
+ sizeof(aHashEntry));
+ bzero((char *)channelTable, sizeof(aHashEntry) * _CHANNELHASHSIZE);
+ Debug((DEBUG_DEBUG, "Channel Hash Table Init: %d (%d)",
+ _CHANNELHASHSIZE, size));
+}
+
+static void clear_server_hash_table(size)
+int size;
+{
+ _SERVERSIZE = bigger_prime(size);
+ if (!serverTable)
+ serverTable = (aHashEntry *)MyMalloc(_SERVERSIZE *
+ sizeof(aHashEntry));
+ bzero((char *)serverTable, sizeof(aHashEntry) * _SERVERSIZE);
+ Debug((DEBUG_DEBUG, "Server Hash Table Init: %d (%d)",
+ _SERVERSIZE, size));
+}
+
+void inithashtables()
+{
+ Reg int i;
+
+ clear_client_hash_table((_HASHSIZE) ? _HASHSIZE : HASHSIZE);
+ clear_channel_hash_table((_CHANNELHASHSIZE) ? _CHANNELHASHSIZE
+ : CHANNELHASHSIZE);
+ clear_server_hash_table((_SERVERSIZE) ? _SERVERSIZE : SERVERSIZE);
+
+ /*
+ * Moved multiplication out from the hashfunctions and into
+ * a pre-generated lookup table. Should save some CPU usage
+ * even on machines with a fast mathprocessor. -- Core
+ */
+ hashtab = (u_int *) MyMalloc(256 * sizeof(u_int));
+ for (i = 0; i < 256; i++)
+ hashtab[i] = tolower((char)i) * 109;
+}
+
+static void bigger_hash_table(size, table, new)
+int *size;
+aHashEntry *table;
+int new;
+{
+ Reg aClient *cptr;
+ Reg aChannel *chptr;
+ Reg aServer *sptr;
+ aHashEntry *otab = table;
+ int osize = *size;
+
+ while (!new || new <= osize)
+ if (!new)
+ {
+ new = osize;
+ new = bigger_prime(1 + (int)((float)new * 1.30));
+ }
+ else
+ new = bigger_prime(1 + new);
+
+ Debug((DEBUG_NOTICE, "bigger_h_table(*%#x = %d,%#x,%d)",
+ size, osize, table, new));
+
+ *size = new;
+ MyFree((char *)table);
+ table = (aHashEntry *)MyMalloc(sizeof(*table) * new);
+ bzero((char *)table, sizeof(*table) * new);
+
+ if (otab == channelTable)
+ {
+ Debug((DEBUG_ERROR, "Channel Hash Table from %d to %d (%d)",
+ osize, new, chsize));
+ chmiss = 0;
+ chhits = 0;
+ chsize = 0;
+ channelTable = table;
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ chptr->hnextch = NULL;
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ (void)add_to_channel_hash_table(chptr->chname, chptr);
+ sendto_flag(SCH_HASH, "Channel Hash Table from %d to %d (%d)",
+ osize, new, chsize);
+ }
+ else if (otab == clientTable)
+ {
+ Debug((DEBUG_ERROR, "Client Hash Table from %d to %d (%d)",
+ osize, new, clsize));
+ sendto_flag(SCH_HASH, "Client Hash Table from %d to %d (%d)",
+ osize, new, clsize);
+ clmiss = 0;
+ clhits = 0;
+ clsize = 0;
+ clientTable = table;
+ for (cptr = client; cptr; cptr = cptr->next)
+ cptr->hnext = NULL;
+ for (cptr = client; cptr; cptr = cptr->next)
+ (void)add_to_client_hash_table(cptr->name, cptr);
+ }
+ else if (otab == serverTable)
+ {
+ Debug((DEBUG_ERROR, "Server Hash Table from %d to %d (%d)",
+ osize, new, svsize));
+ sendto_flag(SCH_HASH, "Server Hash Table from %d to %d (%d)",
+ osize, new, svsize);
+ svsize = 0;
+ serverTable = table;
+ for (sptr = svrtop; sptr; sptr = sptr->nexts)
+ sptr->shnext = NULL;
+ for (sptr = svrtop; sptr; sptr = sptr->nexts)
+ (void)add_to_server_hash_table(sptr, sptr->bcptr);
+ }
+ ircd_writetune(tunefile);
+ return;
+}
+
+
+/*
+ * add_to_client_hash_table
+ */
+int add_to_client_hash_table(name, cptr)
+char *name;
+aClient *cptr;
+{
+ Reg u_int hashv;
+
+ hashv = hash_nick_name(name, &cptr->hashv);
+ cptr->hnext = (aClient *)clientTable[hashv].list;
+ clientTable[hashv].list = (void *)cptr;
+ clientTable[hashv].links++;
+ clientTable[hashv].hits++;
+ clsize++;
+ if (clsize > _HASHSIZE)
+ bigger_hash_table(&_HASHSIZE, clientTable, 0);
+ return 0;
+}
+
+/*
+ * add_to_channel_hash_table
+ */
+int add_to_channel_hash_table(name, chptr)
+char *name;
+aChannel *chptr;
+{
+ Reg u_int hashv;
+
+ hashv = hash_channel_name(name, &chptr->hashv, 0);
+ chptr->hnextch = (aChannel *)channelTable[hashv].list;
+ channelTable[hashv].list = (void *)chptr;
+ channelTable[hashv].links++;
+ channelTable[hashv].hits++;
+ chsize++;
+ if (chsize > _CHANNELHASHSIZE)
+ bigger_hash_table(&_CHANNELHASHSIZE, channelTable, 0);
+ return 0;
+}
+
+/*
+ * add_to_server_hash_table
+ */
+int add_to_server_hash_table(sptr, cptr)
+aServer *sptr;
+aClient *cptr;
+{
+ Reg u_int hashv;
+
+ Debug((DEBUG_DEBUG, "Add %s token %d/%d/%s cptr %#x to server table",
+ sptr->bcptr->name, sptr->stok, sptr->ltok, sptr->tok, cptr));
+ hashv = sptr->stok * 15053;
+ hashv %= _SERVERSIZE;
+ sptr->shnext = (aServer *)serverTable[hashv].list;
+ serverTable[hashv].list = (void *)sptr;
+ serverTable[hashv].links++;
+ serverTable[hashv].hits++;
+ svsize++;
+ if (svsize > _SERVERSIZE)
+ bigger_hash_table(&_SERVERSIZE, serverTable, 0);
+ return 0;
+}
+
+/*
+ * del_from_client_hash_table
+ */
+int del_from_client_hash_table(name, cptr)
+char *name;
+aClient *cptr;
+{
+ Reg aClient *tmp, *prev = NULL;
+ Reg u_int hashv;
+
+ hashv = cptr->hashv;
+ hashv %= _HASHSIZE;
+ for (tmp = (aClient *)clientTable[hashv].list; tmp; tmp = tmp->hnext)
+ {
+ if (tmp == cptr)
+ {
+ if (prev)
+ prev->hnext = tmp->hnext;
+ else
+ clientTable[hashv].list = (void *)tmp->hnext;
+ tmp->hnext = NULL;
+ if (clientTable[hashv].links > 0)
+ {
+ clientTable[hashv].links--;
+ clsize--;
+ return 1;
+ }
+ else
+ {
+ sendto_flag(SCH_ERROR, "cl-hash table failure");
+ Debug((DEBUG_ERROR, "cl-hash table failure"));
+ /*
+ * Should never actually return from here and
+ * if we do it is an error/inconsistency in the
+ * hash table.
+ */
+ return -1;
+ }
+ }
+ prev = tmp;
+ }
+ return 0;
+}
+
+/*
+ * del_from_channel_hash_table
+ */
+int del_from_channel_hash_table(name, chptr)
+char *name;
+aChannel *chptr;
+{
+ Reg aChannel *tmp, *prev = NULL;
+ Reg u_int hashv;
+
+ hashv = chptr->hashv;
+ hashv %= _CHANNELHASHSIZE;
+ for (tmp = (aChannel *)channelTable[hashv].list; tmp;
+ tmp = tmp->hnextch)
+ {
+ if (tmp == chptr)
+ {
+ if (prev)
+ prev->hnextch = tmp->hnextch;
+ else
+ channelTable[hashv].list=(void *)tmp->hnextch;
+ tmp->hnextch = NULL;
+ if (channelTable[hashv].links > 0)
+ {
+ channelTable[hashv].links--;
+ chsize--;
+ return 1;
+ }
+ else
+ {
+ sendto_flag(SCH_ERROR, "ch-hash table failure");
+ return -1;
+ }
+ }
+ prev = tmp;
+ }
+ return 0;
+}
+
+
+/*
+ * del_from_server_hash_table
+ */
+int del_from_server_hash_table(sptr, cptr)
+aServer *sptr;
+aClient *cptr;
+{
+ Reg aServer *tmp, *prev = NULL;
+ Reg u_int hashv;
+
+ hashv = sptr->stok * 15053;
+ hashv %= _SERVERSIZE;
+ for (tmp = (aServer *)serverTable[hashv].list; tmp; tmp = tmp->shnext)
+ {
+ if (tmp == sptr)
+ {
+ if (prev)
+ prev->shnext = tmp->shnext;
+ else
+ serverTable[hashv].list = (void *)tmp->shnext;
+ tmp->shnext = NULL;
+ if (serverTable[hashv].links > 0)
+ {
+ serverTable[hashv].links--;
+ svsize--;
+ return 1;
+ }
+ else
+ {
+ sendto_flag(SCH_ERROR, "se-hash table failure");
+ return -1;
+ }
+ }
+ prev = tmp;
+ }
+ return 0;
+}
+
+
+/*
+ * hash_find_client
+ */
+aClient *hash_find_client(name, cptr)
+char *name;
+aClient *cptr;
+{
+ Reg aClient *tmp;
+ Reg aClient *prv = NULL;
+ Reg aHashEntry *tmp3;
+ u_int hashv, hv;
+
+ hashv = hash_nick_name(name, &hv);
+ tmp3 = &clientTable[hashv];
+
+ /*
+ * Got the bucket, now search the chain.
+ */
+ for (tmp = (aClient *)tmp3->list; tmp; prv = tmp, tmp = tmp->hnext)
+ if (hv == tmp->hashv && mycmp(name, tmp->name) == 0)
+ {
+ clhits++;
+ /*
+ * If the member of the hashtable we found isnt at
+ * the top of its chain, put it there. This builds
+ * a most-frequently used order into the chains of
+ * the hash table, giving speadier lookups on those
+ * nicks which are being used currently. This same
+ * block of code is also used for channels and
+ * servers for the same performance reasons.
+ */
+ if (prv)
+ {
+ aClient *tmp2;
+
+ tmp2 = (aClient *)tmp3->list;
+ tmp3->list = (void *)tmp;
+ prv->hnext = tmp->hnext;
+ tmp->hnext = tmp2;
+ }
+ return (tmp);
+ }
+ clmiss++;
+ return (cptr);
+}
+
+/*
+ * hash_find_server
+ */
+aClient *hash_find_server(server, cptr)
+char *server;
+aClient *cptr;
+{
+ Reg aClient *tmp, *prv = NULL;
+ Reg char *t;
+ Reg char ch;
+ aHashEntry *tmp3;
+ u_int hashv, hv;
+
+ hashv = hash_nick_name(server, &hv);
+ tmp3 = &clientTable[hashv];
+
+ for (tmp = (aClient *)tmp3->list; tmp; prv = tmp, tmp = tmp->hnext)
+ {
+ if (!IsServer(tmp) && !IsMe(tmp))
+ continue;
+ if (hv == tmp->hashv && mycmp(server, tmp->name) == 0)
+ {
+ clhits++;
+ if (prv)
+ {
+ aClient *tmp2;
+
+ tmp2 = (aClient *)tmp3->list;
+ tmp3->list = (void *)tmp;
+ prv->hnext = tmp->hnext;
+ tmp->hnext = tmp2;
+ }
+ return (tmp);
+ }
+ }
+ t = ((char *)server + strlen(server));
+ /*
+ * Whats happening in this next loop ? Well, it takes a name like
+ * foo.bar.edu and proceeds to search for *.edu and then *.bar.edu.
+ * This is for checking full server names against masks although
+ * it isn't often done this way in lieu of using match().
+ */
+ for (;;)
+ {
+ t--;
+ for (; t >= server; t--)
+ if (*(t+1) == '.')
+ break;
+ if (t < server || *t == '*')
+ break;
+ ch = *t;
+ *t = '*';
+ /*
+ * Don't need to check IsServer() here since nicknames can't
+ * have *'s in them anyway.
+ */
+ if (((tmp = hash_find_client(t, cptr))) != cptr)
+ {
+ *t = ch;
+ return (tmp);
+ }
+ *t = ch;
+ }
+ clmiss++;
+ return (cptr);
+}
+
+/*
+ * hash_find_channel
+ */
+aChannel *hash_find_channel(name, chptr)
+char *name;
+aChannel *chptr;
+{
+ Reg aChannel *tmp, *prv = NULL;
+ Reg aHashEntry *tmp3;
+ u_int hashv, hv;
+
+ hashv = hash_channel_name(name, &hv, 0);
+ tmp3 = &channelTable[hashv];
+
+ for (tmp = (aChannel *)tmp3->list; tmp; prv = tmp, tmp = tmp->hnextch)
+ if (hv == tmp->hashv && mycmp(name, tmp->chname) == 0)
+ {
+ chhits++;
+ if (prv)
+ {
+ register aChannel *tmp2;
+
+ tmp2 = (aChannel *)tmp3->list;
+ tmp3->list = (void *)tmp;
+ prv->hnextch = tmp->hnextch;
+ tmp->hnextch = tmp2;
+ }
+ return (tmp);
+ }
+ chmiss++;
+ return chptr;
+}
+
+/*
+ * hash_find_channels
+ *
+ * look up matches for !?????name instead of a real match.
+ */
+aChannel *hash_find_channels(name, chptr)
+char *name;
+aChannel *chptr;
+{
+ aChannel *tmp;
+ u_int hashv, hv;
+
+ if (chptr == NULL)
+ {
+ aHashEntry *tmp3;
+
+ hashv = hash_channel_name(name, &hv, 1);
+ tmp3 = &channelTable[hashv];
+ chptr = (aChannel *) tmp3->list;
+ }
+ else
+ {
+ hv = chptr->hashv;
+ chptr = chptr->hnextch;
+ }
+
+ if (chptr == NULL)
+ return NULL;
+ for (tmp = chptr; tmp; tmp = tmp->hnextch)
+ if (hv == tmp->hashv && *tmp->chname == '!' &&
+ mycmp(name, tmp->chname + CHIDLEN + 1) == 0)
+ {
+ chhits++;
+ return (tmp);
+ }
+ chmiss++;
+ return NULL;
+}
+
+/*
+ * hash_find_stoken
+ */
+aServer *hash_find_stoken(tok, cptr, dummy)
+int tok;
+aClient *cptr;
+void *dummy;
+{
+ Reg aServer *tmp, *prv = NULL;
+ Reg aHashEntry *tmp3;
+ u_int hashv, hv;
+
+ hv = hashv = tok * 15053;
+ hashv %= _SERVERSIZE;
+ tmp3 = &serverTable[hashv];
+
+ for (tmp = (aServer *)tmp3->list; tmp; prv = tmp, tmp = tmp->shnext)
+ if (tmp->stok == tok && tmp->bcptr->from == cptr)
+ {
+ if (prv)
+ {
+ Reg aServer *tmp2;
+
+ tmp2 = (aServer *)tmp3->list;
+ tmp3->list = (void *)tmp;
+ prv->shnext = tmp->shnext;
+ tmp->shnext = tmp2;
+ }
+ return (tmp);
+ }
+ return (aServer *)dummy;
+}
+
+/*
+ * NOTE: this command is not supposed to be an offical part of the ircd
+ * protocol. It is simply here to help debug and to monitor the
+ * performance of the hash functions and table, enabling a better
+ * algorithm to be sought if this one becomes troublesome.
+ * -avalon
+ */
+
+int m_hash(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+#ifdef DEBUGMODE
+ register int l, i;
+ register aHashEntry *tab;
+ int deepest = 0, deeplink = 0, showlist = 0, tothits = 0;
+ int mosthit = 0, mosthits = 0, used = 0, used_now = 0, totlink = 0;
+ int link_pop[10], size = _HASHSIZE;
+ char ch;
+ aHashEntry *table;
+
+ if (parc > 1) {
+ ch = *parv[1];
+ if (islower(ch))
+ table = clientTable;
+ else {
+ table = channelTable;
+ size = _CHANNELHASHSIZE;
+ }
+ if (ch == 'L' || ch == 'l')
+ showlist = 1;
+ } else {
+ ch = '\0';
+ table = clientTable;
+ }
+
+ for (i = 0; i < 10; i++)
+ link_pop[i] = 0;
+ for (i = 0; i < size; i++) {
+ tab = &table[i];
+ l = tab->links;
+ if (showlist)
+ sendto_one(sptr,
+ "NOTICE %s :Hash Entry:%6d Hits:%7d Links:%6d",
+ parv[0], i, tab->hits, l);
+ if (l > 0) {
+ if (l < 10)
+ link_pop[l]++;
+ else
+ link_pop[9]++;
+ used_now++;
+ totlink += l;
+ if (l > deepest) {
+ deepest = l;
+ deeplink = i;
+ }
+ }
+ else
+ link_pop[0]++;
+ l = tab->hits;
+ if (l) {
+ used++;
+ tothits += l;
+ if (l > mosthits) {
+ mosthits = l;
+ mosthit = i;
+ }
+ }
+ }
+ switch((int)ch)
+ {
+ case 'V' : case 'v' :
+ {
+ register aClient *acptr;
+ int bad = 0, listlength = 0;
+
+ for (acptr = client; acptr; acptr = acptr->next) {
+ if (hash_find_client(acptr->name,acptr) != acptr) {
+ if (ch == 'V')
+ sendto_one(sptr, "NOTICE %s :Bad hash for %s",
+ parv[0], acptr->name);
+ bad++;
+ }
+ listlength++;
+ }
+ sendto_one(sptr,"NOTICE %s :List Length: %d Bad Hashes: %d",
+ parv[0], listlength, bad);
+ }
+ case 'P' : case 'p' :
+ for (i = 0; i < 10; i++)
+ sendto_one(sptr,"NOTICE %s :Entires with %d links : %d",
+ parv[0], i, link_pop[i]);
+ return (2);
+ case 'r' :
+ {
+ Reg aClient *acptr;
+
+ sendto_one(sptr,"NOTICE %s :Rehashing Client List.", parv[0]);
+ clear_client_hash_table(_HASHSIZE);
+ for (acptr = client; acptr; acptr = acptr->next)
+ (void)add_to_client_hash_table(acptr->name, acptr);
+ break;
+ }
+ case 'R' :
+ {
+ Reg aChannel *acptr;
+
+ sendto_one(sptr,"NOTICE %s :Rehashing Channel List.", parv[0]);
+ clear_channel_hash_table(_CHANNELHASHSIZE);
+ for (acptr = channel; acptr; acptr = acptr->nextch)
+ (void)add_to_channel_hash_table(acptr->chname, acptr);
+ break;
+ }
+ case 'H' :
+ if (parc > 2)
+ sendto_one(sptr,"NOTICE %s :%s hash to entry %d",
+ parv[0], parv[2],
+ hash_channel_name(parv[2], NULL, 0));
+ return (2);
+ case 'h' :
+ if (parc > 2)
+ sendto_one(sptr,"NOTICE %s :%s hash to entry %d",
+ parv[0], parv[2],
+ hash_nick_name(parv[2], NULL));
+ return (2);
+ case 'n' :
+ {
+ aClient *tmp;
+ int max;
+
+ if (parc <= 2 || !IsAnOper(sptr))
+ return (1);
+ l = atoi(parv[2]) % _HASHSIZE;
+ if (parc > 3)
+ max = atoi(parv[3]) % _HASHSIZE;
+ else
+ max = l;
+ for (;l <= max; l++)
+ for (i = 0, tmp = (aClient *)clientTable[l].list; tmp;
+ i++, tmp = tmp->hnext)
+ {
+ if (parv[1][2] == '1' && tmp != tmp->from)
+ continue;
+ sendto_one(sptr,"NOTICE %s :Node: %d #%d %s",
+ parv[0], l, i, tmp->name);
+ }
+ return (2);
+ }
+ case 'N' :
+ {
+ aChannel *tmp;
+ int max;
+
+ if (parc <= 2 || !IsAnOper(sptr))
+ return (1);
+ l = atoi(parv[2]) % _CHANNELHASHSIZE;
+ if (parc > 3)
+ max = atoi(parv[3]) % _CHANNELHASHSIZE;
+ else
+ max = l;
+ for (;l <= max; l++)
+ for (i = 0, tmp = (aChannel *)channelTable[l].list; tmp;
+ i++, tmp = tmp->hnextch)
+ sendto_one(sptr,"NOTICE %s :Node: %d #%d %s",
+ parv[0], l, i, tmp->chname);
+ return (2);
+ }
+ case 'S' :
+#else
+ if (parc>1&&!strcmp(parv[1],"sums")){
+#endif
+ sendto_one(sptr, "NOTICE %s :[SBSDC] [SUSER]", parv[0]);
+ sendto_one(sptr, "NOTICE %s :[SSERV] [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]);
+#ifndef DEBUGMODE
+ }
+#endif
+ return 2;
+#ifdef DEBUGMODE
+ case 'z' :
+ {
+ if (parc <= 2 || !IsAnOper(sptr))
+ return 1;
+ l = atoi(parv[2]);
+ if (l < 256)
+ return 1;
+ bigger_hash_table(&_HASHSIZE, clientTable, l);
+ sendto_one(sptr, "NOTICE %s :HASHSIZE now %d", parv[0], l);
+ break;
+ }
+ case 'Z' :
+ {
+ if (parc <= 2 || !IsAnOper(sptr))
+ return 1;
+ l = atoi(parv[2]);
+ if (l < 256)
+ return 1;
+ bigger_hash_table(&_CHANNELHASHSIZE, channelTable, l);
+ sendto_one(sptr, "NOTICE %s :CHANNELHASHSIZE now %d",
+ parv[0], l);
+ break;
+ }
+ default :
+ break;
+ }
+ sendto_one(sptr,"NOTICE %s :Entries Hashed: %d NonEmpty: %d of %d",
+ parv[0], totlink, used_now, size);
+ if (!used_now)
+ used_now = 1;
+ sendto_one(sptr,"NOTICE %s :Hash Ratio (av. depth): %f %Full: %f",
+ parv[0], (float)((1.0 * totlink) / (1.0 * used_now)),
+ (float)((1.0 * used_now) / (1.0 * size)));
+ sendto_one(sptr,"NOTICE %s :Deepest Link: %d Links: %d",
+ parv[0], deeplink, deepest);
+ if (!used)
+ used = 1;
+ sendto_one(sptr,"NOTICE %s :Total Hits: %d Unhit: %d Av Hits: %f",
+ parv[0], tothits, size-used,
+ (float)((1.0 * (float)tothits) / (1.0 * (float)used)));
+ sendto_one(sptr,"NOTICE %s :Entry Most Hit: %d Hits: %d",
+ parv[0], mosthit, mosthits);
+ sendto_one(sptr,"NOTICE %s :Client hits %d miss %d",
+ parv[0], clhits, clmiss);
+ sendto_one(sptr,"NOTICE %s :Channel hits %d miss %d",
+ parv[0], chhits, chmiss);
+ return 2;
+#endif
+}
+
+
diff --git a/ircd/hash_def.h b/ircd/hash_def.h
new file mode 100644
index 0000000..e19c3ce
--- /dev/null
+++ b/ircd/hash_def.h
@@ -0,0 +1,32 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/hash_def.h
+ * 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.
+ */
+
+typedef struct hashentry {
+ int hits;
+ int links;
+ void *list;
+} aHashEntry;
+
+/*
+ * it is not important for these to be "big" as ircd will make them grow
+ * as required.
+ */
+#define HASHSIZE ((int)((float)MAXCONNECTIONS*1.75))
+#define CHANNELHASHSIZE ((int)(((float)MAXCONNECTIONS*1.75)/2.0))
+#define SERVERSIZE (MAXCONNECTIONS/10)
diff --git a/ircd/hash_ext.h b/ircd/hash_ext.h
new file mode 100644
index 0000000..8ee1d88
--- /dev/null
+++ b/ircd/hash_ext.h
@@ -0,0 +1,52 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/hash_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/hash.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef HASH_C
+extern int _HASHSIZE;
+extern int _CHANNELHASHSIZE;
+extern int _SERVERSIZE;
+#endif /* HASH_C */
+
+/* External definitions for global functions.
+ */
+#ifndef HASH_C
+#define EXTERN extern
+#else /* HASH_C */
+#define EXTERN
+#endif /* HASH_C */
+EXTERN void inithashtables();
+EXTERN int add_to_client_hash_table __P((char *name, aClient *cptr));
+EXTERN int add_to_channel_hash_table __P((char *name, aChannel *chptr));
+EXTERN int add_to_server_hash_table __P((aServer *sptr, aClient *cptr));
+EXTERN int del_from_client_hash_table __P((char *name, aClient *cptr));
+EXTERN int del_from_channel_hash_table __P((char *name, aChannel *chptr));
+EXTERN int del_from_server_hash_table __P((aServer *sptr, aClient *cptr));
+EXTERN aClient *hash_find_client __P((char *name, aClient *cptr));
+EXTERN aClient *hash_find_server __P((char *server, aClient *cptr));
+EXTERN aChannel *hash_find_channel __P((char *name, aChannel *chptr));
+EXTERN aChannel *hash_find_channels __P((char *name, aChannel *chptr));
+EXTERN aServer *hash_find_stoken __P((int tok, aClient *cptr, void *dummy));
+EXTERN int m_hash __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+#undef EXTERN
diff --git a/ircd/ircd.c b/ircd/ircd.c
new file mode 100644
index 0000000..72bbf95
--- /dev/null
+++ b/ircd/ircd.c
@@ -0,0 +1,1287 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/ircd.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: ircd.c,v 1.62 1999/08/13 17:17:42 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define IRCD_C
+#include "s_externs.h"
+#undef IRCD_C
+
+aClient me; /* That's me */
+aClient *client = &me; /* Pointer to beginning of Client list */
+
+static void open_debugfile(), setup_signals(), io_loop();
+
+istat_t istat;
+char **myargv;
+int rehashed = 0;
+int portnum = -1; /* Server port number, listening this */
+char *configfile = IRCDCONF_PATH; /* Server configuration file */
+int debuglevel = -1; /* Server debug level */
+int bootopt = BOOT_PROT|BOOT_STRICTPROT; /* Server boot option flags */
+char *debugmode = ""; /* -"- -"- -"- -"- */
+char *sbrk0; /* initial sbrk(0) */
+char *tunefile = IRCDTUNE_PATH;
+static int dorehash = 0,
+ dorestart = 0,
+ restart_iauth = 0;
+
+time_t nextconnect = 1; /* time for next try_connections call */
+time_t nextgarbage = 1; /* time for next collect_channel_garbage 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 nextiarestart = 1; /* next time to check if iauth is alive */
+
+#ifdef PROFIL
+extern etext();
+
+RETSIGTYPE s_monitor(s)
+int s;
+{
+ static int mon = 0;
+#if POSIX_SIGNALS
+ struct sigaction act;
+#endif
+
+ (void)moncontrol(mon);
+ mon = 1 - mon;
+#if POSIX_SIGNALS
+ act.sa_handler = s_rehash;
+ act.sa_flags = 0;
+ (void)sigemptyset(&act.sa_mask);
+ (void)sigaddset(&act.sa_mask, SIGUSR1);
+ (void)sigaction(SIGUSR1, &act, NULL);
+#else
+ (void)signal(SIGUSR1, s_monitor);
+#endif
+}
+#endif
+
+RETSIGTYPE s_die(s)
+int s;
+{
+#ifdef USE_SYSLOG
+ (void)syslog(LOG_CRIT, "Server Killed By SIGTERM");
+#endif
+ ircd_writetune(tunefile);
+ flush_connections(me.fd);
+ exit(-1);
+}
+
+#if defined(USE_IAUTH)
+RETSIGTYPE s_slave(s)
+int s;
+{
+# if POSIX_SIGNALS
+ struct sigaction act;
+
+ act.sa_handler = s_slave;
+ act.sa_flags = 0;
+ (void)sigemptyset(&act.sa_mask);
+ (void)sigaddset(&act.sa_mask, SIGUSR1);
+ (void)sigaction(SIGUSR1, &act, NULL);
+# else
+ (void)signal(SIGUSR1, s_slave);
+# endif
+ restart_iauth = 1;
+}
+#endif
+
+static RETSIGTYPE s_rehash(s)
+int s;
+{
+#if POSIX_SIGNALS
+ struct sigaction act;
+
+ act.sa_handler = s_rehash;
+ act.sa_flags = 0;
+ (void)sigemptyset(&act.sa_mask);
+ (void)sigaddset(&act.sa_mask, SIGHUP);
+ (void)sigaction(SIGHUP, &act, NULL);
+#else
+ (void)signal(SIGHUP, s_rehash); /* sysV -argv */
+#endif
+ dorehash = 1;
+}
+
+void restart(mesg)
+char *mesg;
+{
+#ifdef USE_SYSLOG
+ (void)syslog(LOG_WARNING, "Restarting Server because: %s (%u)", mesg,
+ (u_int)((char *)sbrk((size_t)0)-sbrk0));
+#endif
+ sendto_flag(SCH_NOTICE, "Restarting server because: %s (%u)", mesg,
+ (u_int)((char *)sbrk((size_t)0)-sbrk0));
+ server_reboot();
+}
+
+RETSIGTYPE s_restart(s)
+int s;
+{
+#if POSIX_SIGNALS
+ struct sigaction act;
+
+ act.sa_handler = s_restart;
+ act.sa_flags = 0;
+ (void)sigemptyset(&act.sa_mask);
+ (void)sigaddset(&act.sa_mask, SIGINT);
+ (void)sigaction(SIGINT, &act, NULL);
+#else
+ (void)signal(SIGHUP, SIG_DFL); /* sysV -argv */
+#endif
+ dorestart = 1;
+}
+
+void server_reboot()
+{
+ Reg int i;
+
+ sendto_flag(SCH_NOTICE, "Aieeeee!!! Restarting server... (%u)",
+ (u_int)((char *)sbrk((size_t)0)-sbrk0));
+
+ 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
+ (void)closelog();
+#endif
+ for (i = 3; i < MAXCONNECTIONS; i++)
+ (void)close(i);
+ if (!(bootopt & (BOOT_TTY|BOOT_DEBUG)))
+ (void)close(2);
+ (void)close(1);
+ if ((bootopt & BOOT_CONSOLE) || isatty(0))
+ (void)close(0);
+ ircd_writetune(tunefile);
+ if (!(bootopt & (BOOT_INETD|BOOT_OPER)))
+ {
+ (void)execv(IRCD_PATH, 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", IRCD_PATH,
+ myargv[0]);
+ closelog();
+#endif
+ Debug((DEBUG_FATAL,"Couldn't restart server: %s",
+ 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(currenttime)
+time_t currenttime;
+{
+ static time_t lastsort = 0;
+ Reg aConfItem *aconf;
+ Reg aClient *cptr;
+ aConfItem **pconf;
+ int confrq;
+ time_t next = 0;
+ aClass *cltmp;
+ aConfItem *con_conf = NULL;
+ double f, f2;
+ aCPing *cp;
+
+ Debug((DEBUG_NOTICE,"Connection check at : %s",
+ myctime(currenttime)));
+ for (aconf = conf; aconf; aconf = aconf->next )
+ {
+ /* Also when already connecting! (update holdtimes) --SRB */
+ if (!(aconf->status & (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER)))
+ continue;
+ /*
+ ** 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 > currenttime))
+ {
+ if ((next > aconf->hold) || (next == 0))
+ next = aconf->hold;
+ continue;
+ }
+ send_ping(aconf);
+ if (aconf->port <= 0)
+ continue;
+
+ cltmp = Class(aconf);
+ confrq = get_con_freq(cltmp);
+ aconf->hold = currenttime + confrq;
+ /*
+ ** Found a CONNECT config with port specified, scan clients
+ ** and see if this server is already connected?
+ */
+ cptr = find_name(aconf->name, (aClient *)NULL);
+ if (!cptr)
+ cptr = find_mask(aconf->name, (aClient *)NULL);
+ /*
+ ** It is not connected, scan clients and see if any matches
+ ** a D(eny) line.
+ */
+ if (find_denied(aconf->name, Class(cltmp)))
+ continue;
+ /* We have a candidate, let's see if it could be the best. */
+ if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
+ (!con_conf ||
+ (con_conf->pref > aconf->pref && aconf->pref >= 0) ||
+ (con_conf->pref == -1 &&
+ Class(cltmp) > ConfClass(con_conf))))
+ con_conf = aconf;
+ if ((next > aconf->hold) || (next == 0))
+ next = aconf->hold;
+ }
+ if (con_conf)
+ {
+ if (con_conf->next) /* are we already last? */
+ {
+ for (pconf = &conf; (aconf = *pconf);
+ pconf = &(aconf->next))
+ /* put the current one at the end and
+ * make sure we try all connections
+ */
+ if (aconf == con_conf)
+ *pconf = aconf->next;
+ (*pconf = con_conf)->next = 0;
+ }
+ if (connect_server(con_conf, (aClient *)NULL,
+ (struct hostent *)NULL) == 0)
+ sendto_flag(SCH_NOTICE,
+ "Connection to %s[%s] activated.",
+ con_conf->name, con_conf->host);
+ }
+ Debug((DEBUG_NOTICE,"Next connection check : %s", myctime(next)));
+ /*
+ * calculate preference value based on accumulated stats.
+ */
+ if (!lastsort || lastsort < currenttime)
+ {
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (!(cp = aconf->ping) || !cp->seq || !cp->recvd)
+ aconf->pref = -1;
+ else
+ {
+ f = (double)cp->recvd / (double)cp->seq;
+ f2 = pow(f, (double)20.0);
+ if (f2 < (double)0.001)
+ f = (double)0.001;
+ else
+ f = f2;
+ f2 = (double)cp->ping / (double)cp->recvd;
+ f = f2 / f;
+ if (f > 100000.0)
+ f = 100000.0;
+ aconf->pref = (u_int) (f * (double)100.0);
+ }
+ lastsort = currenttime + 60;
+ }
+ return (next);
+}
+
+
+static void close_held(cptr)
+aClient *cptr;
+{
+ Reg aClient *acptr;
+ int i;
+
+ for (i = highest_fd; i >= 0; i--)
+ if ((acptr = local[i]) && (cptr->port == acptr->port) &&
+ (acptr != cptr) && IsHeld(acptr) &&
+ !bcmp((char *)&cptr->ip, (char *)&acptr->ip,
+ sizeof(acptr->ip)))
+ {
+ (void) exit_client(acptr, acptr, &me,
+ "Reconnect Timeout");
+ return;
+ }
+}
+
+
+static time_t check_pings(currenttime)
+time_t currenttime;
+{
+ static time_t lkill = 0;
+ Reg aClient *cptr;
+ Reg int kflag = 0;
+ int ping = 0, i, rflag = 0;
+ time_t oldest = 0, timeout;
+ char *reason;
+
+ for (i = highest_fd; i >= 0; i--)
+ {
+ if (!(cptr = local[i]) || IsListening(cptr) || IsLog(cptr) ||
+ IsHeld(cptr))
+ continue;
+
+ /*
+ * K and R lines once per minute, max. This is the max.
+ * granularity in K-lines anyway (with time field).
+ */
+ if ((currenttime - lkill > 60) || rehashed)
+ {
+ if (IsPerson(cptr))
+ {
+ kflag = find_kill(cptr, rehashed, &reason);
+#ifdef R_LINES_OFTEN
+ rflag = find_restrict(cptr);
+#endif
+ }
+ else
+ {
+ kflag = rflag = 0;
+ reason = NULL;
+ }
+ }
+ ping = IsRegistered(cptr) ? get_client_ping(cptr) :
+ ACCEPTTIMEOUT;
+ Debug((DEBUG_DEBUG, "c(%s) %d p %d k %d r %d a %d",
+ cptr->name, cptr->status, ping, kflag, rflag,
+ currenttime - 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 (!kflag && !rflag && IsRegistered(cptr) &&
+ (ping >= currenttime - 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 (kflag || rflag ||
+ ((currenttime - cptr->lasttime) >= (2 * ping) &&
+ (cptr->flags & FLAGS_PINGSENT)) ||
+ (!IsRegistered(cptr) &&
+ (currenttime - cptr->firsttime) >= ping))
+ {
+ if (IsReconnect(cptr))
+ {
+ sendto_flag(SCH_ERROR,
+ "Reconnect timeout to %s",
+ get_client_name(cptr, TRUE));
+ close_held(cptr);
+ (void)exit_client(cptr, cptr, &me,
+ "Ping timeout");
+ }
+ if (!IsRegistered(cptr) &&
+ (DoingDNS(cptr) || DoingAuth(cptr) ||
+ DoingXAuth(cptr)))
+ {
+ if (cptr->authfd >= 0)
+ {
+ (void)close(cptr->authfd);
+ cptr->authfd = -1;
+ cptr->count = 0;
+ *cptr->buffer = '\0';
+ }
+ Debug((DEBUG_NOTICE, "%s/%c%s timeout %s",
+ (DoingDNS(cptr)) ? "DNS" : "dns",
+ (DoingXAuth(cptr)) ? "X" : "x",
+ (DoingAuth(cptr)) ? "AUTH" : "auth",
+ get_client_name(cptr,TRUE)));
+ del_queries((char *)cptr);
+ ClearAuth(cptr);
+#if defined(USE_IAUTH)
+ if (DoingDNS(cptr) || DoingXAuth(cptr))
+ {
+ if (DoingDNS(cptr) &&
+ (iauth_options & XOPT_EXTWAIT))
+ {
+ /* iauth wants more time */
+ sendto_iauth("%d d", cptr->fd);
+ ClearDNS(cptr);
+ cptr->lasttime = currenttime;
+ continue;
+ }
+ if (DoingXAuth(cptr) &&
+ (iauth_options & XOPT_NOTIMEOUT))
+ {
+ cptr->exitc = EXITC_AUTHTOUT;
+ sendto_iauth("%d T", cptr->fd);
+ ereject_user(cptr, " Timeout ",
+ "Authentication Timeout");
+ continue;
+ }
+ sendto_iauth("%d T", cptr->fd);
+ SetDoneXAuth(cptr);
+ }
+#endif
+ ClearDNS(cptr);
+ ClearXAuth(cptr);
+ ClearWXAuth(cptr);
+ cptr->firsttime = currenttime;
+ cptr->lasttime = currenttime;
+ continue;
+ }
+ if (IsServer(cptr) || IsConnecting(cptr) ||
+ IsHandshake(cptr))
+ sendto_flag(SCH_NOTICE,
+ "No response from %s closing link",
+ get_client_name(cptr, FALSE));
+ /*
+ * this is used for KILL lines with time restrictions
+ * on them - send a messgae to the user being killed
+ * first.
+ */
+ if (kflag && IsPerson(cptr))
+ {
+ char buf[100];
+
+ sendto_flag(SCH_NOTICE,
+ "Kill line active for %s",
+ get_client_name(cptr, FALSE));
+ cptr->exitc = EXITC_KLINE;
+ if (!BadPtr(reason))
+ sprintf(buf, "Kill line active: %.80s",
+ reason);
+ (void)exit_client(cptr, cptr, &me, (reason) ?
+ buf : "Kill line active");
+ }
+
+#if defined(R_LINES) && defined(R_LINES_OFTEN)
+ else if (IsPerson(cptr) && rflag)
+ {
+ sendto_flag(SCH_NOTICE,
+ "Restricting %s, closing link.",
+ get_client_name(cptr,FALSE));
+ cptr->exitc = EXITC_RLINE;
+ (void)exit_client(cptr, cptr, &me,
+ "Restricting");
+ }
+#endif
+ else
+ {
+ cptr->exitc = EXITC_PING;
+ (void)exit_client(cptr, cptr, &me,
+ "Ping timeout");
+ }
+ 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 = currenttime - ping;
+ sendto_one(cptr, "PING :%s", me.name);
+ }
+ping_timeout:
+ timeout = cptr->lasttime + ping;
+ while (timeout <= currenttime)
+ timeout += ping;
+ if (timeout < oldest || !oldest)
+ oldest = timeout;
+ }
+ if (currenttime - lkill > 60)
+ lkill = currenttime;
+ if (!oldest || oldest < currenttime)
+ oldest = currenttime + PINGFREQUENCY;
+ if (oldest < currenttime + 2)
+ oldest += 2;
+ Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d",
+ myctime(oldest), ping, oldest, currenttime));
+ return (oldest);
+}
+
+
+static void setup_me(mp)
+aClient *mp;
+{
+ struct passwd *p;
+
+ p = getpwuid(getuid());
+ strncpyzt(mp->username, (p) ? p->pw_name : "unknown",
+ sizeof(mp->username));
+ (void)get_my_name(mp, mp->sockhost, sizeof(mp->sockhost)-1);
+
+ /* Setup hostp - fake record to resolve localhost. -Toor */
+ mp->hostp = (struct hostent *)MyMalloc(sizeof(struct hostent));
+ mp->hostp->h_name = MyMalloc(strlen(me.sockhost)+1);
+ strcpy(mp->hostp->h_name, mp->sockhost);
+ mp->hostp->h_aliases = (char **)MyMalloc(sizeof(char *));
+ *mp->hostp->h_aliases = NULL;
+ mp->hostp->h_addrtype = AFINET;
+ mp->hostp->h_length =
+#ifdef INET6
+ IN6ADDRSZ;
+#else
+ sizeof(long);
+#endif
+ mp->hostp->h_addr_list = (char **)MyMalloc(2*sizeof(char *));
+#ifdef INET6
+ mp->hostp->h_addr_list[0] = (char *)&in6addr_loopback;
+#else
+ mp->hostp->h_addr_list[0] = (void *)MyMalloc(mp->hostp->h_length);
+ *(long *)(mp->hostp->h_addr_list[0]) = IN_LOOPBACKNET;
+#endif
+ mp->hostp->h_addr_list[1] = NULL ;
+
+ if (mp->name[0] == '\0')
+ strncpyzt(mp->name, mp->sockhost, sizeof(mp->name));
+ if (me.info == DefInfo)
+ me.info = mystrdup("IRCers United");
+ mp->lasttime = mp->since = mp->firsttime = time(NULL);
+ mp->hopcount = 0;
+ mp->authfd = -1;
+ mp->auth = mp->username;
+ mp->confs = NULL;
+ mp->flags = 0;
+ mp->acpt = mp->from = mp;
+ mp->next = NULL;
+ mp->user = NULL;
+ mp->fd = -1;
+ SetMe(mp);
+ (void) make_server(mp);
+ mp->serv->snum = find_server_num (ME);
+ (void) make_user(mp);
+ istat.is_users++; /* here, cptr->next is NULL, see make_user() */
+ mp->user->flags |= FLAGS_OPER;
+ mp->serv->up = mp->name;
+ mp->user->server = find_server_string(mp->serv->snum);
+ strncpyzt(mp->user->username, (p) ? p->pw_name : "unknown",
+ sizeof(mp->user->username));
+ (void) strcpy(mp->user->host, mp->name);
+
+ (void)add_to_client_hash_table(mp->name, mp);
+
+ setup_server_channels(mp);
+}
+
+/*
+** 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 [-a] [-b] [-c]%s [-h servername] [-q] [-o] [-i] [-T tunefile] [-p (strict|on|off)] [-s] [-v] %s\n",
+#ifdef CMDLINE_CONFIG
+ " [-f config]",
+#else
+ "",
+#endif
+#ifdef DEBUGMODE
+ " [-x loglevel] [-t]"
+#else
+ ""
+#endif
+ );
+ (void)printf("Server not started\n\n");
+ exit(-1);
+}
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+ uid_t uid, euid;
+
+ (void) myctime(time(NULL)); /* Don't ask, just *don't* ask */
+ sbrk0 = (char *)sbrk((size_t)0);
+ uid = getuid();
+ euid = geteuid();
+#ifdef PROFIL
+ (void)monstartup(0, etext);
+ (void)moncontrol(1);
+ (void)signal(SIGUSR1, s_monitor);
+#endif
+
+#ifdef CHROOTDIR
+ ircd_res_init();
+ if (chroot(ROOT_PATH))
+ {
+ perror("chroot");
+ (void)fprintf(stderr,"%s: Cannot chroot: %s.\n", IRCD_PATH,
+ ROOT_PATH);
+ exit(5);
+ }
+#endif /*CHROOTDIR*/
+
+#ifdef ZIP_LINKS
+ if (zlib_version[0] == '0')
+ {
+ fprintf(stderr, "zlib version 1.0 or higher required\n");
+ exit(1);
+ }
+ if (zlib_version[0] != ZLIB_VERSION[0])
+ {
+ fprintf(stderr, "incompatible zlib version\n");
+ exit(1);
+ }
+ if (strcmp(zlib_version, ZLIB_VERSION) != 0)
+ {
+ fprintf(stderr, "warning: different zlib version\n");
+ }
+#endif
+
+ myargv = argv;
+ (void)umask(077); /* better safe than sorry --SRB */
+ bzero((char *)&me, sizeof(me));
+
+ version = make_version(); /* Generate readable version string */
+
+ /*
+ ** 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 'b':
+ bootopt |= BOOT_BADTUNE;
+ break;
+ case 'c':
+ bootopt |= BOOT_CONSOLE;
+ break;
+ case 'q':
+ bootopt |= BOOT_QUICK;
+ break;
+ case 'o': /* Per user local daemon... */
+ (void)setuid((uid_t)uid);
+ bootopt |= BOOT_OPER;
+ break;
+#ifdef CMDLINE_CONFIG
+ case 'f':
+ (void)setuid((uid_t)uid);
+ configfile = p;
+ break;
+#endif
+ case 'h':
+ if (*p == '\0')
+ bad_command();
+ strncpyzt(me.name, p, sizeof(me.name));
+ break;
+ case 'i':
+ bootopt |= BOOT_INETD|BOOT_AUTODIE;
+ break;
+ case 'p':
+ if (!strcmp(p, "strict"))
+ bootopt |= BOOT_PROT|BOOT_STRICTPROT;
+ else if (!strcmp(p, "on"))
+ bootopt |= BOOT_PROT;
+ else if (!strcmp(p, "off"))
+ bootopt &= ~(BOOT_PROT|BOOT_STRICTPROT);
+ else
+ bad_command();
+ break;
+ case 's':
+ bootopt |= BOOT_NOIAUTH;
+ break;
+ case 't':
+ (void)setuid((uid_t)uid);
+ bootopt |= BOOT_TTY;
+ break;
+ case 'T':
+ if (*p == '\0')
+ bad_command();
+ tunefile = p;
+ break;
+ case 'v':
+ (void)printf("ircd %s %s\n\tzlib %s\n\t%s #%s\n",
+ version, serveropts,
+#ifndef ZIP_LINKS
+ "not used",
+#else
+ zlib_version,
+#endif
+ creation, generation);
+ exit(0);
+ case 'x':
+#ifdef DEBUGMODE
+ (void)setuid((uid_t)uid);
+ debuglevel = atoi(p);
+ debugmode = *p ? p : "0";
+ bootopt |= BOOT_DEBUG;
+ break;
+#else
+ (void)fprintf(stderr,
+ "%s: DEBUGMODE must be defined for -x y\n",
+ myargv[0]);
+ exit(0);
+#endif
+ default:
+ bad_command();
+ }
+ }
+
+ if (argc > 0)
+ bad_command(); /* This exits out */
+
+#if defined(USE_IAUTH) && defined(__CYGWIN32__)
+ if ((bootopt & BOOT_NOIAUTH) == 0)
+ {
+ bootopt |= BOOT_NOIAUTH;
+ (void)fprintf(stderr, "WARNING: Assuming -s option.\n");
+ }
+#endif
+
+#ifndef IRC_UID
+ if ((uid != euid) && !euid)
+ {
+ (void)fprintf(stderr,
+ "ERROR: do not run ircd setuid root. Make it setuid a\
+ normal user.\n");
+ exit(-1);
+ }
+#endif
+
+#if !defined(CHROOTDIR)
+ (void)setuid((uid_t)euid);
+# if defined(IRC_UID) && defined(IRC_GID)
+ if ((int)getuid() == 0)
+ {
+ /* run as a specified user */
+ (void)fprintf(stderr,"WARNING: running ircd with uid = %d\n",
+ IRC_UID);
+ (void)fprintf(stderr," changing to gid %d.\n",IRC_GID);
+ (void)setgid(IRC_GID);
+ (void)setuid(IRC_UID);
+ }
+# endif
+#endif /*CHROOTDIR/UID/GID*/
+
+#if defined(USE_IAUTH)
+ if ((bootopt & BOOT_NOIAUTH) == 0)
+ switch (vfork())
+ {
+ case -1:
+ fprintf(stderr, "%s: Unable to fork!", myargv[0]);
+ exit(-1);
+ case 0:
+ close(0); close(1); close(3);
+ if (execl(IAUTH_PATH, IAUTH, "-X", NULL) < 0)
+ _exit(-1);
+ default:
+ {
+ int rc;
+
+ (void)wait(&rc);
+ if (rc != 0)
+ {
+ fprintf(stderr,
+ "%s: error: unable to find \"%s\".\n",
+ myargv[0], IAUTH_PATH);
+ exit(-1);
+ }
+ }
+ }
+#endif
+
+ setup_signals();
+
+ /* didn't set debuglevel */
+ /* but asked for debugging output to tty */
+ if ((debuglevel < 0) && (bootopt & BOOT_TTY))
+ {
+ (void)fprintf(stderr,
+ "you specified -t without -x. use -x <n>\n");
+ exit(-1);
+ }
+
+ initstats();
+ ircd_readtune(tunefile);
+ timeofday = time(NULL);
+#ifdef CACHED_MOTD
+ motd = NULL;
+ read_motd(IRCDMOTD_PATH);
+#endif
+ inithashtables();
+ initlists();
+ initclass();
+ initwhowas();
+ timeofday = time(NULL);
+ open_debugfile();
+ timeofday = time(NULL);
+ (void)init_sys();
+
+#ifdef USE_SYSLOG
+ openlog(myargv[0], LOG_PID|LOG_NDELAY, LOG_FACILITY);
+#endif
+ timeofday = time(NULL);
+ if (initconf(bootopt) == -1)
+ {
+ Debug((DEBUG_FATAL, "Failed in reading configuration file %s",
+ configfile));
+ /* no can do.
+ (void)printf("Couldn't open configuration file %s\n",
+ configfile);
+ */
+ exit(-1);
+ }
+ else
+ {
+ aClient *acptr = NULL;
+ int i;
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = local[i]))
+ continue;
+ if (IsListening(acptr))
+ break;
+ acptr = NULL;
+ }
+ /* exit if there is nothing to listen to */
+ if (acptr == NULL)
+ exit(-1);
+ }
+
+ dbuf_init();
+ setup_me(&me);
+ check_class();
+ ircd_writetune(tunefile);
+ if (bootopt & BOOT_INETD)
+ {
+ aClient *tmp;
+ aConfItem *aconf;
+
+ tmp = make_client(NULL);
+
+ tmp->fd = 0;
+ tmp->flags = FLAGS_LISTEN;
+ tmp->acpt = tmp;
+ tmp->from = tmp;
+ tmp->firsttime = time(NULL);
+
+ SetMe(tmp);
+
+ (void)strcpy(tmp->name, "*");
+
+ if (inetport(tmp, 0, "0.0.0.0", 0))
+ tmp->fd = -1;
+ if (tmp->fd == 0)
+ {
+ aconf = make_conf();
+ aconf->status = CONF_LISTEN_PORT;
+ aconf->clients++;
+ aconf->next = conf;
+ conf = aconf;
+
+ tmp->confs = make_link();
+ tmp->confs->next = NULL;
+ tmp->confs->value.aconf = aconf;
+ add_fd(tmp->fd, &fdas);
+ add_fd(tmp->fd, &fdall);
+ set_non_blocking(tmp->fd, tmp);
+ }
+ else
+ exit(5);
+ }
+ if (bootopt & BOOT_OPER)
+ {
+ aClient *tmp = add_connection(&me, 0);
+
+ if (!tmp)
+ exit(1);
+ SetMaster(tmp);
+ local[0] = tmp;
+ }
+ else
+ write_pidfile();
+
+ Debug((DEBUG_NOTICE,"Server ready..."));
+#ifdef USE_SYSLOG
+ syslog(LOG_NOTICE, "Server Ready: v%s (%s #%s)", version, creation,
+ generation);
+#endif
+ timeofday = time(NULL);
+ while (1)
+ io_loop();
+}
+
+
+void io_loop()
+{
+ static time_t delay = 0;
+ int maxs = 4;
+
+ /*
+ ** 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 && timeofday >= nextconnect)
+ nextconnect = try_connections(timeofday);
+ /*
+ ** Every once in a while, hunt channel structures that
+ ** can be freed.
+ */
+ if (timeofday >= nextgarbage)
+ nextgarbage = collect_channel_garbage(timeofday);
+ /*
+ ** DNS checks. One to timeout queries, one for cache expiries.
+ */
+ if (timeofday >= nextdnscheck)
+ nextdnscheck = timeout_query_list(timeofday);
+ if (timeofday >= nextexpire)
+ nextexpire = expire_cache(timeofday);
+ /*
+ ** 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 -= timeofday;
+ /*
+ ** 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);
+
+ /*
+ ** First, try to drain traffic from servers (this includes listening
+ ** ports). Give up, either if there's no traffic, or too many
+ ** iterations.
+ */
+ while (maxs--)
+ if (read_message(0, &fdas, 0))
+ flush_fdary(&fdas);
+ else
+ break;
+
+ Debug((DEBUG_DEBUG, "delay for %d", delay));
+ /*
+ ** Second, deal with _all_ clients but only try to empty sendQ's for
+ ** servers. Other clients are dealt with below..
+ */
+ if (read_message(1, &fdall, 1) == 0 && delay > 1)
+ {
+ /*
+ ** Timed out (e.g. *NO* traffic at all).
+ ** Try again but also check to empty sendQ's for all clients.
+ */
+ sendto_flag(SCH_DEBUG, "read_message(RO) -> 0 [%d]", delay);
+ (void)read_message(delay - 1, &fdall, 0);
+ }
+ timeofday = time(NULL);
+
+ 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 (timeofday >= nextping)
+ {
+ nextping = check_pings(timeofday);
+ rehashed = 0;
+ }
+
+ if (dorestart)
+ restart("Caught SIGINT");
+ if (dorehash)
+ { /* Only on signal, not on oper /rehash */
+ ircd_writetune(tunefile);
+ (void)rehash(&me, &me, 1);
+ dorehash = 0;
+ }
+ if (restart_iauth || timeofday >= nextiarestart)
+ {
+ start_iauth(restart_iauth);
+ restart_iauth = 0;
+ nextiarestart = timeofday + 15;
+ }
+ /*
+ ** Flush output buffers on all connections now if they
+ ** have data in them (or at least try to flush)
+ ** -avalon
+ */
+ flush_connections(me.fd);
+
+#ifdef DEBUGMODE
+ checklists();
+#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 IRCDDBG_PATH.
+ * 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 don't waste the fd.
+ */
+static void open_debugfile()
+{
+#ifdef DEBUGMODE
+ int fd;
+ aClient *cptr;
+
+ if (debuglevel >= 0)
+ {
+ cptr = make_client(NULL);
+ cptr->fd = 2;
+ SetLog(cptr);
+ cptr->port = debuglevel;
+ cptr->flags = 0;
+ cptr->acpt = cptr;
+ local[2] = cptr;
+ (void)strcpy(cptr->sockhost, me.sockhost);
+
+ (void)printf("isatty = %d ttyname = %#x\n",
+ isatty(2), (u_int)ttyname(2));
+ if (!(bootopt & BOOT_TTY)) /* leave debugging output on fd 2 */
+ {
+ (void)truncate(IRCDDBG_PATH, 0);
+ if ((fd = open(IRCDDBG_PATH,O_WRONLY|O_CREAT,0600))<0)
+ if ((fd = open("/dev/null", O_WRONLY)) < 0)
+ exit(-1);
+ if (fd != 2)
+ {
+ (void)dup2(fd, 2);
+ (void)close(fd);
+ }
+ strncpyzt(cptr->name, IRCDDBG_PATH,sizeof(cptr->name));
+ }
+ else if (isatty(2) && ttyname(2))
+ strncpyzt(cptr->name, ttyname(2), sizeof(cptr->name));
+ else
+ (void)strcpy(cptr->name, "FD2-Pipe");
+ Debug((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s",
+ cptr->name, cptr->port, myctime(time(NULL))));
+ }
+ else
+ local[2] = NULL;
+#endif
+ return;
+}
+
+static void setup_signals()
+{
+#if POSIX_SIGNALS
+ struct sigaction act;
+
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ (void)sigemptyset(&act.sa_mask);
+ (void)sigaddset(&act.sa_mask, SIGPIPE);
+ (void)sigaddset(&act.sa_mask, SIGALRM);
+# ifdef SIGWINCH
+ (void)sigaddset(&act.sa_mask, SIGWINCH);
+ (void)sigaction(SIGWINCH, &act, NULL);
+# endif
+ (void)sigaction(SIGPIPE, &act, NULL);
+ act.sa_handler = dummy;
+ (void)sigaction(SIGALRM, &act, NULL);
+ act.sa_handler = s_rehash;
+ (void)sigemptyset(&act.sa_mask);
+ (void)sigaddset(&act.sa_mask, SIGHUP);
+ (void)sigaction(SIGHUP, &act, NULL);
+ act.sa_handler = s_restart;
+ (void)sigaddset(&act.sa_mask, SIGINT);
+ (void)sigaction(SIGINT, &act, NULL);
+ act.sa_handler = s_die;
+ (void)sigaddset(&act.sa_mask, SIGTERM);
+ (void)sigaction(SIGTERM, &act, NULL);
+# if defined(USE_IAUTH)
+ act.sa_handler = s_slave;
+ (void)sigaddset(&act.sa_mask, SIGUSR1);
+ (void)sigaction(SIGUSR1, &act, NULL);
+ act.sa_handler = SIG_IGN;
+# ifdef SA_NOCLDWAIT
+ act.sa_flags = SA_NOCLDWAIT;
+# else
+ act.sa_flags = 0;
+# endif
+ (void)sigaddset(&act.sa_mask, SIGCHLD);
+ (void)sigaction(SIGCHLD, &act, NULL);
+# endif
+
+#else /* POSIX_SIGNALS */
+
+# ifndef HAVE_RELIABLE_SIGNALS
+ (void)signal(SIGPIPE, dummy);
+# ifdef SIGWINCH
+ (void)signal(SIGWINCH, dummy);
+# endif
+# else /* HAVE_RELIABLE_SIGNALS */
+# ifdef SIGWINCH
+ (void)signal(SIGWINCH, SIG_IGN);
+# endif
+ (void)signal(SIGPIPE, SIG_IGN);
+# endif /* HAVE_RELIABLE_SIGNALS */
+ (void)signal(SIGALRM, dummy);
+ (void)signal(SIGHUP, s_rehash);
+ (void)signal(SIGTERM, s_die);
+ (void)signal(SIGINT, s_restart);
+# if defined(USE_IAUTH)
+ (void)signal(SIGUSR1, s_slave);
+ (void)signal(SIGCHLD, SIG_IGN);
+# endif
+#endif /* POSIX_SIGNAL */
+
+#ifdef RESTARTING_SYSTEMCALLS
+ /*
+ ** 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.
+ */
+ (void)siginterrupt(SIGALRM, 1);
+#endif
+}
+
+/*
+ * Called from bigger_hash_table(), s_die(), server_reboot(),
+ * main(after initializations), grow_history(), rehash(io_loop) signal.
+ */
+void ircd_writetune(filename)
+char *filename;
+{
+ int fd;
+ char buf[100];
+
+ (void)truncate(filename, 0);
+ if ((fd = open(filename, O_CREAT|O_WRONLY, 0600)) >= 0)
+ {
+ (void)sprintf(buf, "%d\n%d\n%d\n%d\n%d\n%d\n", ww_size,
+ lk_size, _HASHSIZE, _CHANNELHASHSIZE,
+ _SERVERSIZE, poolsize);
+ if (write(fd, buf, strlen(buf)) == -1)
+ sendto_flag(SCH_ERROR,
+ "Failed (%d) to write tune file: %s.",
+ errno, mybasename(filename));
+ else
+ sendto_flag(SCH_NOTICE, "Updated %s.",
+ mybasename(filename));
+ close(fd);
+ }
+ else
+ sendto_flag(SCH_ERROR, "Failed (%d) to open tune file: %s.",
+ errno, mybasename(filename));
+}
+
+/*
+ * Called only from main() at startup.
+ */
+void ircd_readtune(filename)
+char *filename;
+{
+ int fd, t_data[6];
+ char buf[100];
+
+ buf[0] = '\0';
+ if ((fd = open(filename, O_RDONLY)) != -1)
+ {
+ read(fd, buf, 100); /* no panic if this fails.. */
+ if (sscanf(buf, "%d\n%d\n%d\n%d\n%d\n%d\n", &t_data[0],
+ &t_data[1], &t_data[2], &t_data[3],
+ &t_data[4], &t_data[5]) != 6)
+ {
+ close(fd);
+ if (bootopt & BOOT_BADTUNE)
+ return;
+ else
+ {
+ fprintf(stderr,
+ "ircd tune file %s: bad format\n",
+ filename);
+ exit(1);
+ }
+ }
+
+ /*
+ ** Initiate the tune-values after successfully
+ ** reading the tune-file.
+ */
+ ww_size = t_data[0];
+ lk_size = t_data[1];
+ _HASHSIZE = t_data[2];
+ _CHANNELHASHSIZE = t_data[3];
+ _SERVERSIZE = t_data[4];
+ poolsize = t_data[5];
+
+ /*
+ ** the lock array only grows if the whowas array grows,
+ ** I don't think it should be initialized with a lower
+ ** size since it will never adjust unless whowas array does.
+ */
+ if (lk_size < ww_size)
+ lk_size = ww_size;
+ close(fd);
+ }
+}
diff --git a/ircd/ircd_ext.h b/ircd/ircd_ext.h
new file mode 100644
index 0000000..e47b148
--- /dev/null
+++ b/ircd/ircd_ext.h
@@ -0,0 +1,62 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/ircd_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/ircd.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef IRCD_C
+extern aClient me;
+extern aClient *client;
+extern istat_t istat;
+extern char **myargv;
+extern int rehashed;
+extern int portnum;
+extern char *configfile;
+extern int debuglevel;
+extern int bootopt;
+extern char *debugmode;
+extern char *sbrk0;
+extern char *tunefile;
+extern time_t nextconnect;
+extern time_t nextgarbage;
+extern time_t nextping;
+extern time_t nextdnscheck;
+extern time_t nextexpire;
+#endif /* IRCD_C */
+
+/* External definitions for global functions.
+ */
+#ifndef IRCD_C
+#define EXTERN extern
+#else /* IRCD_C */
+#define EXTERN
+#endif /* IRCD_C */
+#ifdef PROFIL
+EXTERN RETSIGTYPE s_monitor __P((int s));
+#endif /* PROFIL */
+EXTERN RETSIGTYPE s_die __P((int s));
+EXTERN void restart __P((char *mesg));
+EXTERN RETSIGTYPE s_restart __P((int s));
+EXTERN void server_reboot();
+EXTERN void ircd_writetune __P((char *filename));
+EXTERN void ircd_readtune __P((char *filename));
+#undef EXTERN
diff --git a/ircd/list.c b/ircd/list.c
new file mode 100644
index 0000000..52ba815
--- /dev/null
+++ b/ircd/list.c
@@ -0,0 +1,685 @@
+/************************************************************************
+ * 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.
+ */
+
+/* -- Jto -- 20 Jun 1990
+ * extern void free() fixed as suggested by
+ * gruner@informatik.tu-muenchen.de
+ */
+
+/* -- Jto -- 03 Jun 1990
+ * Added chname initialization...
+ */
+
+/* -- Jto -- 24 May 1990
+ * Moved is_full() to channel.c
+ */
+
+/* -- Jto -- 10 May 1990
+ * Added #include <sys.h>
+ * Changed memset(xx,0,yy) into bzero(xx,yy)
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: list.c,v 1.9 1999/07/02 16:49:37 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define LIST_C
+#include "s_externs.h"
+#undef LIST_C
+
+char *DefInfo = "*Not On This Net*"; /* constant */
+
+#ifdef DEBUGMODE
+static struct liststats {
+ int inuse;
+} cloc, crem, users, servs, links, classs, aconfs;
+
+#endif
+
+aServer *svrtop = NULL;
+
+int numclients = 0;
+
+void initlists()
+{
+#ifdef DEBUGMODE
+ bzero((char *)&cloc, sizeof(cloc));
+ bzero((char *)&crem, sizeof(crem));
+ bzero((char *)&users, sizeof(users));
+ bzero((char *)&servs, sizeof(servs));
+ bzero((char *)&links, sizeof(links));
+ bzero((char *)&classs, sizeof(classs));
+ bzero((char *)&aconfs, sizeof(aconfs));
+#endif
+}
+
+void outofmemory()
+{
+ Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
+ sendto_flag(SCH_NOTICE, "Ouch!!! Out of memory...");
+ restart("Out of Memory");
+}
+
+#ifdef DEBUGMODE
+void checklists()
+{
+ aServer *sp;
+ anUser *up;
+
+ for (sp = svrtop; sp; sp = sp->nexts)
+ if (sp->bcptr->serv != sp)
+ Debug((DEBUG_ERROR, "svrtop: %#x->%#x->%#x != %#x",
+ sp, sp->bcptr, sp->bcptr->serv, sp));
+}
+#endif
+
+/*
+** Create a new aClient structure and set it to initial state.
+**
+** from == NULL, create local client (a client connected
+** to a socket).
+**
+** from, create remote client (behind a socket
+** associated with the client defined by
+** 'from'). ('from' is a local client!!).
+*/
+aClient *make_client(from)
+aClient *from;
+{
+ Reg aClient *cptr = NULL;
+ Reg unsigned 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 *)MyMalloc(size)))
+ outofmemory();
+ bzero((char *)cptr, (int)size);
+
+#ifdef DEBUGMODE
+ if (size == CLIENT_LOCAL_SIZE)
+ cloc.inuse++;
+ else
+ crem.inuse++;
+#endif
+
+ /* Note: structure is zero (calloc) */
+ cptr->from = from ? from : cptr; /* 'from' of local client is self! */
+ cptr->next = NULL; /* For machines with NON-ZERO NULL pointers >;) */
+ cptr->prev = NULL;
+ cptr->hnext = NULL;
+ cptr->user = NULL;
+ cptr->serv = NULL;
+ cptr->status = STAT_UNKNOWN;
+ cptr->fd = -1;
+ (void)strcpy(cptr->username, "unknown");
+ cptr->info = DefInfo;
+ if (size == CLIENT_LOCAL_SIZE)
+ {
+ cptr->since = cptr->lasttime = cptr->firsttime = timeofday;
+ cptr->confs = NULL;
+ cptr->sockhost[0] = '\0';
+ cptr->buffer[0] = '\0';
+ cptr->authfd = -1;
+ cptr->auth = cptr->username;
+ cptr->exitc = EXITC_UNDEF;
+#ifdef ZIP_LINKS
+ cptr->zip = NULL;
+#endif
+ }
+ return (cptr);
+}
+
+void free_client(cptr)
+aClient *cptr;
+{
+ if (cptr->info != DefInfo)
+ MyFree(cptr->info);
+ MyFree((char *)cptr);
+}
+
+/*
+** 'make_user' add's an User information block to a client
+** if it was not previously allocated.
+*/
+anUser *make_user(cptr)
+aClient *cptr;
+{
+ Reg anUser *user;
+
+ user = cptr->user;
+ if (!user)
+ {
+ user = (anUser *)MyMalloc(sizeof(anUser));
+#ifdef DEBUGMODE
+ users.inuse++;
+#endif
+ user->away = NULL;
+ user->refcnt = 1;
+ user->joined = 0;
+ user->flags = 0;
+ user->channel = NULL;
+ user->invited = NULL;
+ user->uwas = NULL;
+ cptr->user = user;
+ user->servp = NULL;
+ user->bcptr = cptr;
+ if (cptr->next) /* the only cptr->next == NULL is me */
+ istat.is_users++;
+ }
+ return user;
+}
+
+aServer *make_server(cptr)
+aClient *cptr;
+{
+ Reg aServer *serv = cptr->serv, *sp, *spp = NULL;
+
+ if (!serv)
+ {
+ serv = (aServer *)MyMalloc(sizeof(aServer));
+#ifdef DEBUGMODE
+ servs.inuse++;
+#endif
+ serv->user = NULL;
+ serv->snum = -1;
+ *serv->by = '\0';
+ *serv->tok = '\0';
+ serv->stok = 0;
+ serv->up = NULL;
+ serv->refcnt = 1;
+ serv->nexts = NULL;
+ cptr->serv = serv;
+
+ for (sp = svrtop; sp; spp = sp, sp = sp->nexts)
+ if (spp && ((spp->ltok) + 1 < sp->ltok))
+ break;
+ serv->prevs = spp;
+ if (spp)
+ {
+ serv->ltok = spp->ltok + 1;
+ spp->nexts = serv;
+ }
+ else
+ { /* Me, myself and I alone */
+ svrtop = serv;
+ serv->ltok = 1;
+ }
+
+ if (sp)
+ {
+ serv->nexts = sp;
+ sp->prevs = serv;
+ }
+ serv->bcptr = cptr;
+ SPRINTF(serv->tok, "%d", serv->ltok);
+ serv->lastload = 0;
+ }
+ return cptr->serv;
+}
+
+/*
+** free_user
+** Decrease user reference count by one and realease block,
+** if count reaches 0
+*/
+void free_user(user, cptr)
+Reg anUser *user;
+aClient *cptr;
+{
+ aServer *serv;
+
+ if (--user->refcnt <= 0)
+ {
+ if ((serv = user->servp))
+ {
+ user->servp = NULL; /* to avoid some impossible loop */
+ free_server(serv, cptr);
+ }
+ if (user->away)
+ {
+ istat.is_away--;
+ istat.is_awaymem -= (strlen(user->away) + 1);
+ MyFree((char *)user->away);
+ }
+ /*
+ * sanity check
+ */
+ if (user->joined || user->refcnt < 0 ||
+ user->invited || user->channel || user->uwas ||
+ user->bcptr)
+ {
+ char buf[512];
+ /*too many arguments for dumpcore() and sendto_flag()*/
+ SPRINTF(buf, "%#x %#x %#x %#x %d %d %#x (%s)",
+ user, user->invited, user->channel, user->uwas,
+ user->joined, user->refcnt,
+ user->bcptr,
+ (user->bcptr) ? user->bcptr->name :"none");
+#ifdef DEBUGMODE
+ dumpcore("%#x user (%s!%s@%s) %s",
+ cptr, cptr ? cptr->name : "<noname>",
+ user->username, user->host, buf);
+#else
+ sendto_flag(SCH_ERROR,
+ "* %#x user (%s!%s@%s) %s *",
+ cptr, cptr ? cptr->name : "<noname>",
+ user->username, user->host, buf);
+#endif
+ }
+ MyFree((char *)user);
+#ifdef DEBUGMODE
+ users.inuse--;
+#endif
+ }
+}
+
+void free_server(serv, cptr)
+aServer *serv;
+aClient *cptr;
+{
+ if (--serv->refcnt <= 0)
+ {
+ if (serv->refcnt < 0 || serv->prevs || serv->nexts ||
+ serv->bcptr || serv->user)
+ {
+ char buf[512];
+ SPRINTF(buf, "%d %#x %#x %#x %#x (%s)",
+ serv->refcnt, serv->prevs, serv->nexts,
+ serv->user, serv->bcptr,
+ (serv->bcptr) ? serv->bcptr->name : "none");
+#ifdef DEBUGMODE
+ dumpcore("%#x server %s %s",
+ cptr, cptr ? cptr->name : "<noname>", buf);
+ servs.inuse--;
+#else
+ sendto_flag(SCH_ERROR, "* %#x server %s %s *",
+ cptr, cptr ? cptr->name : "<noname>", buf);
+#endif
+ }
+ MyFree((char *)serv);
+ }
+}
+
+/*
+ * taken the code from ExitOneClient() for this and placed it here.
+ * - avalon
+ * remove client **AND** _related structures_ from lists,
+ * *free* them too. -krys
+ */
+void remove_client_from_list(cptr)
+Reg aClient *cptr;
+{
+ checklist();
+ if (cptr->hopcount == 0) /* is there another way, at this point? */
+ istat.is_localc--;
+ else
+ istat.is_remc--;
+ if (cptr->prev)
+ cptr->prev->next = cptr->next;
+ else
+ {
+ client = cptr->next;
+ client->prev = NULL;
+ }
+ if (cptr->next)
+ cptr->next->prev = cptr->prev;
+
+ if (cptr->user)
+ {
+ istat.is_users--;
+ /* decrement reference counter, and eventually free it */
+ cptr->user->bcptr = NULL;
+ (void)free_user(cptr->user, cptr);
+ }
+
+ if (cptr->serv)
+ {
+ /* has to be removed from the list of aServer structures */
+ if (cptr->serv->nexts)
+ cptr->serv->nexts->prevs = cptr->serv->prevs;
+ if (cptr->serv->prevs)
+ cptr->serv->prevs->nexts = cptr->serv->nexts;
+ if (svrtop == cptr->serv)
+ svrtop = cptr->serv->nexts;
+ cptr->serv->prevs = NULL;
+ cptr->serv->nexts = NULL;
+
+ if (cptr->serv->user)
+ {
+ free_user(cptr->serv->user, cptr);
+ cptr->serv->user = NULL;
+ }
+
+ /* decrement reference counter, and eventually free it */
+ cptr->serv->bcptr = NULL;
+ free_server(cptr->serv, cptr);
+ }
+
+ if (cptr->service)
+ /*
+ ** has to be removed from the list of aService structures,
+ ** no reference counter for services, thus this part of the
+ ** code can safely be included in free_service()
+ */
+ free_service(cptr);
+
+#ifdef DEBUGMODE
+ if (cptr->fd == -2)
+ cloc.inuse--;
+ else
+ crem.inuse--;
+#endif
+
+ (void)free_client(cptr);
+ numclients--;
+ return;
+}
+
+/*
+ * move the client aClient struct before its server's
+ */
+void reorder_client_in_list(cptr)
+aClient *cptr;
+{
+ if (cptr->user == NULL && cptr->service == NULL)
+ return;
+
+ /* update neighbours */
+ if (cptr->next)
+ cptr->next->prev = cptr->prev;
+ if (cptr->prev)
+ cptr->prev->next = cptr->next;
+ else
+ client = cptr->next;
+
+ /* re-insert */
+ if (cptr->user)
+ {
+ cptr->next = cptr->user->servp->bcptr;
+ cptr->prev = cptr->user->servp->bcptr->prev;
+#ifdef DEBUGMODE
+ sendto_flag(SCH_DEBUG, "%p [%s] moved before server: %p [%s]",
+ cptr, cptr->name, cptr->user->servp->bcptr,
+ cptr->user->servp->bcptr->name);
+#endif
+ }
+ else if (cptr->service)
+ {
+ cptr->next = cptr->service->servp->bcptr;
+ cptr->prev = cptr->service->servp->bcptr->prev;
+ }
+
+ /* update new neighbours */
+ if (cptr->prev)
+ cptr->prev->next = cptr;
+ else
+ client = cptr;
+ cptr->next->prev = cptr;
+}
+
+/*
+ * 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, isnt it ?
+ * -avalon
+ */
+void add_client_to_list(cptr)
+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.
+ */
+ if (cptr->from == cptr)
+ istat.is_localc++;
+ else
+ istat.is_remc++;
+ if (cptr->user)
+ istat.is_users++;
+
+ cptr->next = client;
+ client = cptr;
+
+ if (cptr->next)
+ cptr->next->prev = cptr;
+
+ numclients++;
+ return;
+}
+
+/*
+ * Look for ptr in the linked listed pointed to by link.
+ */
+Link *find_user_link(lp, ptr)
+Reg Link *lp;
+Reg aClient *ptr;
+{
+ if (ptr)
+ for (; lp; lp = lp->next)
+ if (lp->value.cptr == ptr)
+ return (lp);
+ return NULL;
+}
+
+Link *find_channel_link(lp, ptr)
+Reg Link *lp;
+Reg aChannel *ptr;
+{
+ if (ptr)
+ for (; lp; lp = lp->next)
+ if (lp->value.chptr == ptr)
+ return (lp);
+ return NULL;
+}
+
+Link *make_link()
+{
+ Reg Link *lp;
+
+ lp = (Link *)MyMalloc(sizeof(Link));
+#ifdef DEBUGMODE
+ links.inuse++;
+#endif
+ lp->flags = 0;
+ return lp;
+}
+
+void free_link(lp)
+Reg Link *lp;
+{
+ MyFree((char *)lp);
+#ifdef DEBUGMODE
+ links.inuse--;
+#endif
+}
+
+
+aClass *make_class()
+{
+ Reg aClass *tmp;
+
+ tmp = (aClass *)MyMalloc(sizeof(aClass));
+#ifdef DEBUGMODE
+ classs.inuse++;
+#endif
+ return tmp;
+}
+
+void free_class(tmp)
+Reg aClass *tmp;
+{
+ MyFree((char *)tmp);
+#ifdef DEBUGMODE
+ classs.inuse--;
+#endif
+}
+
+aConfItem *make_conf()
+{
+ Reg aConfItem *aconf;
+
+ aconf = (struct ConfItem *)MyMalloc(sizeof(aConfItem));
+
+#ifdef DEBUGMODE
+ aconfs.inuse++;
+#endif
+ istat.is_conf++;
+ istat.is_confmem += sizeof(aConfItem);
+
+ bzero((char *)&aconf->ipnum, sizeof(struct in_addr));
+ aconf->clients = aconf->port = 0;
+ aconf->next = NULL;
+ aconf->host = aconf->passwd = aconf->name = NULL;
+ aconf->ping = NULL;
+ aconf->status = CONF_ILLEGAL;
+ aconf->pref = -1;
+ aconf->hold = time(NULL);
+ Class(aconf) = NULL;
+ return (aconf);
+}
+
+void delist_conf(aconf)
+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(aconf)
+aConfItem *aconf;
+{
+ del_queries((char *)aconf);
+
+ istat.is_conf--;
+ istat.is_confmem -= aconf->host ? strlen(aconf->host)+1 : 0;
+ istat.is_confmem -= aconf->passwd ? strlen(aconf->passwd)+1 : 0;
+ istat.is_confmem -= aconf->name ? strlen(aconf->name)+1 : 0;
+ istat.is_confmem -= aconf->ping ? sizeof(*aconf->ping) : 0;
+ istat.is_confmem -= sizeof(aConfItem);
+
+ MyFree(aconf->host);
+ if (aconf->passwd)
+ bzero(aconf->passwd, strlen(aconf->passwd));
+ if (aconf->ping)
+ MyFree((char *)aconf->ping);
+ MyFree(aconf->passwd);
+ MyFree(aconf->name);
+ MyFree((char *)aconf);
+#ifdef DEBUGMODE
+ aconfs.inuse--;
+#endif
+ return;
+}
+
+#ifdef DEBUGMODE
+void send_listinfo(cptr, name)
+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(aClass));
+ 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
+
+
+void add_fd(fd, ary)
+int fd;
+FdAry *ary;
+{
+ Debug((DEBUG_DEBUG,"add_fd(%d,%#x)", fd, ary));
+ if (fd >= 0)
+ ary->fd[++(ary->highest)] = fd;
+}
+
+
+int del_fd(fd, ary)
+int fd;
+FdAry *ary;
+{
+ int i;
+
+ Debug((DEBUG_DEBUG,"del_fd(%d,%#x)", fd, ary));
+ if ((ary->highest == -1) || (fd < 0))
+ return -1;
+ for (i = 0; i <= ary->highest; i++)
+ if (ary->fd[i] == fd)
+ break;
+ if (i < ary->highest)
+ {
+ ary->fd[i] = ary->fd[ary->highest--];
+ return 0;
+ }
+ else if (i > ary->highest)
+ return -1;
+ ary->highest--;
+ return 0;
+}
diff --git a/ircd/list2.c b/ircd/list2.c
new file mode 100644
index 0000000..17f5ac0
--- /dev/null
+++ b/ircd/list2.c
@@ -0,0 +1,515 @@
+/************************************************************************
+ * 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.
+ */
+
+/* -- Jto -- 20 Jun 1990
+ * extern void free() fixed as suggested by
+ * gruner@informatik.tu-muenchen.de
+ */
+
+/* -- Jto -- 03 Jun 1990
+ * Added chname initialization...
+ */
+
+/* -- Jto -- 24 May 1990
+ * Moved is_full() to channel.c
+ */
+
+/* -- Jto -- 10 May 1990
+ * Added #include <sys.h>
+ * Changed memset(xx,0,yy) into bzero(xx,yy)
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)list2.c 1.1 1/22/95 (C) 1988 University of Oulu, \
+Computing Center and Jarkko Oikarinen";
+#endif
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#ifdef DBMALLOC
+#include "malloc.h"
+#endif
+void free_link __P((Link *));
+Link *make_link __P(());
+
+static struct liststats {
+ int inuse;
+ int free;
+} listc[8];
+
+#define LC_CLOC 0
+#define LC_CREM 1
+#define LC_SERV 2
+#define LC_LINK 3
+#define LC_USER 4
+#define LC_CONF 5
+#define LC_CLAS 6
+#define LC_DBUF 7
+
+void outofmemory();
+
+static aClient *clofree = NULL;
+static aClient *crefree = NULL;
+static aClass *clfree = NULL;
+static aConfItem *cofree = NULL;
+static anUser *ufree = NULL;
+static Link *lfree = NULL;
+static aServer *sfree = NULL;
+
+int numclients = 0;
+
+void initlists()
+{
+ bzero(listc, sizeof(struct liststats)* 7);
+}
+
+void outofmemory()
+{
+ 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, create remote client (behind a socket
+** associated with the client defined by
+** 'from'). ('from' is a local client!!).
+*/
+aClient *make_client(from)
+aClient *from;
+{
+ Reg aClient *cptr = NULL;
+ Reg unsigned 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 = clofree))
+ {
+ clofree = cptr->next;
+ listc[LC_CLOC].free--;
+ Debug((DEBUG_LIST, "make_client(%#x) = %#x",
+ from, cptr));
+ }
+ }
+ else if ((cptr = crefree))
+ {
+ crefree = cptr->next;
+ listc[LC_CREM].free--;
+ Debug((DEBUG_LIST, "make_client(%#x) = %#x",
+ from, cptr));
+ }
+
+ if (!cptr)
+ {
+ if (!(cptr = (aClient *)MyMalloc(size)))
+ outofmemory();
+ else
+ {
+ if (size == CLIENT_LOCAL_SIZE)
+ listc[LC_CLOC].inuse++;
+ else
+ listc[LC_CREM].inuse++;
+ }
+ }
+
+ bzero((char *)cptr, (int)size);
+
+ /* Note: structure is zero (calloc) */
+ cptr->from = from ? from : cptr; /* 'from' of local client is self! */
+ cptr->next = NULL; /* For machines with NON-ZERO NULL pointers >;) */
+ cptr->prev = NULL;
+ cptr->hnext = NULL;
+ cptr->user = NULL;
+ cptr->serv = NULL;
+ cptr->status = STAT_UNKNOWN;
+ cptr->fd = -1;
+ (void)strcpy(cptr->username, "unknown");
+ if (size == CLIENT_LOCAL_SIZE)
+ {
+ cptr->since = cptr->lasttime = cptr->firsttime = time(NULL);
+ cptr->confs = NULL;
+ cptr->sockhost[0] = '\0';
+ cptr->buffer[0] = '\0';
+ cptr->authfd = -1;
+ }
+ return (cptr);
+}
+
+
+checksanity()
+{
+ register aClient *c;
+ register anUser *u;
+ register aServer *s;
+
+ for (c = client; c; c = c->next)
+#ifdef LIST_DEBUG
+ if ((u = c->user) && (u->bcptr != c))
+ dumpcore("c %#x u %#x b %#x", c, u, u->bcptr);
+ else if ((s = c->serv) && s->bcptr != c)
+ dumpcore("c %#x s %#x b %#x", c, s, s->bcptr);
+ else
+#endif
+ if (u && u->refcnt <= 0)
+ dumpcore("c %#x u %#x r %d", c, u, u->refcnt);
+}
+
+
+void free_client(cptr)
+aClient *cptr;
+{
+ Debug((DEBUG_LIST, "free_client(%#x) %d", cptr, cptr->fd));
+ if (cptr->fd != -1)
+ {
+ bzero((char *)cptr, CLIENT_LOCAL_SIZE);
+ listc[LC_CLOC].free++;
+ cptr->next = clofree;
+ clofree = cptr;
+ }
+ else
+ {
+ bzero((char *)cptr, CLIENT_REMOTE_SIZE);
+ listc[LC_CREM].free++;
+ cptr->next = crefree;
+ crefree = cptr;
+ }
+}
+
+/*
+** 'make_user' add's an User information block to a client
+** if it was not previously allocated.
+*/
+anUser *make_user(cptr)
+aClient *cptr;
+{
+ Reg anUser *user;
+ char c;
+
+ user = cptr->user;
+ if (!user)
+ if ((user = ufree))
+ {
+ ufree = user->nextu;
+ listc[LC_USER].free--;
+ c = '-';
+ }
+ if (!user)
+ {
+ user = (anUser *)MyMalloc(sizeof(anUser));
+ listc[LC_USER].inuse++;
+ c = '=';
+ }
+ cptr->user = user;
+ user->nextu = NULL;
+ user->away = NULL;
+ user->refcnt = 1;
+ user->joined = 0;
+ user->channel = NULL;
+ user->invited = NULL;
+ Debug((DEBUG_LIST, "make_user(%#x) %c %#x %d",
+ cptr, c, user, user->refcnt));
+ user->bcptr = cptr;
+ return user;
+}
+
+aServer *make_server(cptr)
+aClient *cptr;
+{
+ Reg aServer *serv = cptr->serv;
+ char c;
+
+ if (!serv)
+ if ((serv = sfree))
+ {
+ sfree = serv->nexts;
+ listc[LC_SERV].free--;
+ c = '-';
+ }
+ if (!serv)
+ {
+ serv = (aServer *)MyMalloc(sizeof(aServer));
+ listc[LC_SERV].inuse++;
+ c = '=';
+ }
+ serv->user = NULL;
+ serv->nexts = NULL;
+ *serv->by = '\0';
+ *serv->up = '\0';
+ cptr->serv = serv;
+#ifdef LIST_DEBUG
+ serv->bcptr = cptr;
+#endif
+ Debug((DEBUG_LIST, "make_server(%#x) %c %#x",
+ cptr, c, serv));
+ return cptr->serv;
+}
+
+/*
+** free_user
+** Decrease user reference count by one and realease block,
+** if count reaches 0
+*/
+void free_user(user, cptr)
+Reg anUser *user;
+aClient *cptr;
+{
+ if (cptr && user->bcptr && (user->bcptr != cptr))
+ {
+ dumpcore("user %#x bcptr %#x cptr %#x",
+ user, user->bcptr, cptr);
+ exit(0);
+ }
+ user->bcptr = cptr;
+ user->refcnt--;
+ Debug((DEBUG_LIST, "free_user(%#x,%#x) %d",
+ user, cptr, user->refcnt));
+ if (user->refcnt <= 0)
+ {
+ if (user->away)
+ (void)MyFree((char *)user->away);
+ bzero((char *)user, sizeof(*user));
+ user->nextu = ufree;
+ ufree = user;
+ listc[LC_USER].free++;
+ }
+}
+
+/*
+ * taken the code from ExitOneClient() for this and placed it here.
+ * - avalon
+ */
+void remove_client_from_list(cptr)
+Reg 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 (cptr->user)
+ {
+ add_history(cptr);
+ off_history(cptr);
+ (void)free_user(cptr->user, cptr);
+ }
+ if (cptr->serv)
+ {
+ if (cptr->serv->user)
+ free_user(cptr->serv->user, cptr);
+ listc[LC_SERV].free++;
+ cptr->serv->nexts = sfree;
+ cptr->serv->bcptr = NULL;
+ sfree = cptr->serv;
+ }
+ 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, isnt it ?
+ * -avalon
+ */
+void add_client_to_list(cptr)
+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(lp, ptr)
+Reg Link *lp;
+Reg aClient *ptr;
+{
+ while (lp && ptr)
+ {
+ if (lp->value.cptr == ptr)
+ return (lp);
+ lp = lp->next;
+ }
+ return NULL;
+}
+
+Link *make_link()
+{
+ Reg Link *lp;
+ char c;
+
+ if ((lp = lfree))
+ {
+ lfree = lp->next;
+ listc[LC_LINK].free--;
+ c = '-';
+ }
+ else
+ {
+ lp = (Link *)MyMalloc(sizeof(Link)*3);
+ bzero((char *)lp+1, sizeof(Link)*2);
+ lp->next = lp+1;
+ lp->next->next = lp+2;
+ lp->next->next->next = lfree;
+ lfree = lp->next;
+ listc[LC_LINK].inuse += 3;
+ listc[LC_LINK].free += 2;
+ c = '=';
+ }
+ Debug((DEBUG_LIST, "make_link() %c %#x", c, lp));
+ return lp;
+}
+
+void free_link(lp)
+Reg Link *lp;
+{
+ bzero((char *)lp, sizeof(*lp));
+ lp->next = lfree;
+ lfree = lp;
+ listc[LC_LINK].free++;
+ Debug((DEBUG_LIST, "free_link(%#x)", lp));
+}
+
+
+aClass *make_class()
+{
+ Reg aClass *tmp;
+
+ if ((tmp = clfree))
+ {
+ listc[LC_CLAS].free--;
+ clfree = tmp->next;
+ Debug((DEBUG_LIST, "make_class() - %#x", tmp));
+ }
+ else
+ {
+ tmp = (aClass *)MyMalloc(sizeof(aClass));
+ listc[LC_CLAS].inuse++;
+ Debug((DEBUG_LIST, "make_class() = %#x", tmp));
+ }
+ return tmp;
+}
+
+void free_class(tmp)
+Reg aClass *tmp;
+{
+ bzero((char *)tmp, sizeof(*tmp));
+ tmp->next = clfree;
+ clfree = tmp;
+ listc[LC_CLAS].free++;
+ Debug((DEBUG_LIST, "free_class(%#x)", tmp));
+}
+
+aConfItem *make_conf()
+{
+ Reg aConfItem *aconf;
+ char c;
+
+ if ((aconf = cofree))
+ {
+ cofree = aconf->next;
+ listc[LC_CONF].free--;
+ c = '-';
+ }
+ else
+ {
+ aconf = (struct ConfItem *)MyMalloc(sizeof(aConfItem));
+ listc[LC_CONF].inuse++;
+ c = '=';
+ bzero((char *)aconf, sizeof(*aconf));
+ }
+ aconf->next = NULL;
+ aconf->host = aconf->passwd = aconf->name = NULL;
+ aconf->status = CONF_ILLEGAL;
+ Class(aconf) = 0;
+ Debug((DEBUG_LIST, "make_conf() %c %#x",c , aconf));
+ return (aconf);
+}
+
+void free_conf(aconf)
+aConfItem *aconf;
+{
+ MyFree(aconf->host);
+ if (aconf->passwd)
+ bzero(aconf->passwd, strlen(aconf->passwd));
+ MyFree(aconf->passwd);
+ MyFree(aconf->name);
+ bzero((char *)aconf, sizeof(*aconf));
+ aconf->next = cofree;
+ cofree = aconf;
+ Debug((DEBUG_LIST, "free_conf(%#x)", aconf));
+ listc[LC_CONF].free++;
+ return;
+}
+
+void send_listinfo(cptr, name)
+aClient *cptr;
+char *name;
+{
+ static char *labels[] = { "Local", "Remote", "Servs", "Links",
+ "Users", "Confs", "Classes", "dbufs" };
+ static int sizes[] = { CLIENT_LOCAL_SIZE, CLIENT_REMOTE_SIZE,
+ sizeof(aServer), sizeof(Link),
+ sizeof(anUser), sizeof(aConfItem),
+ sizeof(aClass), sizeof(dbufbuf)};
+
+ struct liststats *ls = listc;
+ int inuse = 0, mem = 0, tmp = 0, i;
+
+ listc[LC_DBUF].inuse = dbufblocks;
+ listc[LC_DBUF].free = dbufblocks - dbufalloc;
+ for (i = 0; i < 8; i++, ls++)
+ {
+ tmp = sizes[i] * ls->inuse;
+ sendto_one(cptr, ":%s NOTICE %s :%s: inuse: %d(%d) free: %d",
+ me.name, cptr->name,
+ labels[i], ls->inuse, tmp, ls->free);
+ inuse += ls->inuse;
+ mem += tmp;
+ }
+
+ sendto_one(cptr, ":%s NOTICE %s :Totals: inuse %d %d",
+ me.name, name, inuse, mem);
+}
diff --git a/ircd/list_ext.h b/ircd/list_ext.h
new file mode 100644
index 0000000..db3f245
--- /dev/null
+++ b/ircd/list_ext.h
@@ -0,0 +1,66 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/list_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/list.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef LIST_C
+extern anUser *usrtop;
+extern aServer *svrtop;
+extern int numclients;
+extern const char *DefInfo;
+#endif /* LIST_C */
+
+/* External definitions for global functions.
+ */
+#ifndef LIST_C
+#define EXTERN extern
+#else /* LIST_C */
+#define EXTERN
+#endif /* LIST_C */
+EXTERN void initlists();
+EXTERN void outofmemory();
+#ifdef DEBUGMODE
+EXTERN void checklists();
+EXTERN void send_listinfo __P((aClient *cptr, char *name));
+#endif /* DEBUGMOE */
+EXTERN aClient *make_client __P((aClient *from));
+EXTERN void free_client __P((aClient *cptr));
+EXTERN anUser *make_user __P((aClient *cptr));
+EXTERN aServer *make_server __P((aClient *cptr));
+EXTERN void free_user __P((Reg anUser *user, aClient *cptr));
+EXTERN void free_server __P((aServer *serv, aClient *cptr));
+EXTERN void remove_client_from_list __P((Reg aClient *cptr));
+EXTERN void reorder_client_in_list __P((aClient *cptr));
+EXTERN void add_client_to_list __P((aClient *cptr));
+EXTERN Link *find_user_link __P((Reg Link *lp, Reg aClient *ptr));
+EXTERN Link *find_channel_link __P((Reg Link *lp, Reg aChannel *ptr));
+EXTERN Link *make_link();
+EXTERN void free_link __P((Reg Link *lp));
+EXTERN aClass *make_class();
+EXTERN void free_class __P((Reg aClass *tmp));
+EXTERN aConfItem *make_conf();
+EXTERN void delist_conf __P((aConfItem *aconf));
+EXTERN void free_conf __P((aConfItem *aconf));
+EXTERN void add_fd __P((int fd, FdAry *ary));
+EXTERN int del_fd __P((int fd, FdAry *ary));
+#undef EXTERN
diff --git a/ircd/nameser_def.h b/ircd/nameser_def.h
new file mode 100644
index 0000000..39c2ce8
--- /dev/null
+++ b/ircd/nameser_def.h
@@ -0,0 +1,330 @@
+/*
+ * ++Copyright++ 1983, 1989, 1993
+ * -
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * --Copyright--
+ */
+
+/*
+ * @(#)nameser.h 8.1 (Berkeley) 6/2/93
+ * $Id: nameser_def.h,v 1.3 1998/12/13 00:19:03 kalt Exp $
+ */
+
+/*
+ * revision information. this is the release date in YYYYMMDD format.
+ * it can change every day so the right thing to do with it is use it
+ * in preprocessor commands such as "#if (__BIND > 19931104)". do not
+ * compare for equality; rather, use it to determine whether your resolver
+ * is new enough to contain a certain feature.
+ */
+
+#define __BIND 19960801 /* interface version stamp */
+
+/*
+ * Define constants based on rfc883
+ */
+#define PACKETSZ 512 /* maximum packet size */
+#define MAXDNAME 1025 /* maximum presentation domain name */
+#define MAXCDNAME 255 /* maximum compressed domain name */
+#define MAXLABEL 63 /* maximum length of domain label */
+#define HFIXEDSZ 12 /* #/bytes of fixed data in header */
+#define QFIXEDSZ 4 /* #/bytes of fixed data in query */
+#define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
+#define INT32SZ 4 /* for systems without 32-bit ints */
+#define INT16SZ 2 /* for systems without 16-bit ints */
+#define INADDRSZ 4 /* IPv4 T_A */
+#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
+
+/*
+ * Internet nameserver port number
+ */
+#define NAMESERVER_PORT 53
+
+/*
+ * Currently defined opcodes
+ */
+#define QUERY 0x0 /* standard query */
+#define IQUERY 0x1 /* inverse query */
+#define STATUS 0x2 /* nameserver status query */
+/*#define xxx 0x3*/ /* 0x3 reserved */
+#define NS_NOTIFY_OP 0x4 /* notify secondary of SOA change */
+/*
+ * Currently defined response codes
+ */
+#define NOERROR 0 /* no error */
+#define FORMERR 1 /* format error */
+#define SERVFAIL 2 /* server failure */
+#define NXDOMAIN 3 /* non existent domain */
+#define NOTIMP 4 /* not implemented */
+#define REFUSED 5 /* query refused */
+
+/*
+ * Type values for resources and queries
+ */
+#define T_A 1 /* host address */
+#define T_NS 2 /* authoritative server */
+#define T_MD 3 /* mail destination */
+#define T_MF 4 /* mail forwarder */
+#define T_CNAME 5 /* canonical name */
+#define T_SOA 6 /* start of authority zone */
+#define T_MB 7 /* mailbox domain name */
+#define T_MG 8 /* mail group member */
+#define T_MR 9 /* mail rename name */
+#define T_NULL 10 /* null resource record */
+#define T_WKS 11 /* well known service */
+#define T_PTR 12 /* domain name pointer */
+#define T_HINFO 13 /* host information */
+#define T_MINFO 14 /* mailbox information */
+#define T_MX 15 /* mail routing information */
+#define T_TXT 16 /* text strings */
+#define T_RP 17 /* responsible person */
+#define T_AFSDB 18 /* AFS cell database */
+#define T_X25 19 /* X_25 calling address */
+#define T_ISDN 20 /* ISDN calling address */
+#define T_RT 21 /* router */
+#define T_NSAP 22 /* NSAP address */
+#define T_NSAP_PTR 23 /* reverse NSAP lookup (deprecated) */
+#define T_SIG 24 /* security signature */
+#define T_KEY 25 /* security key */
+#define T_PX 26 /* X.400 mail mapping */
+#define T_GPOS 27 /* geographical position (withdrawn) */
+#define T_AAAA 28 /* IP6 Address */
+#define T_LOC 29 /* Location Information */
+#define T_NXT 30 /* Next Valid Name in Zone */
+#define T_EID 31 /* Endpoint identifier */
+#define T_NIMLOC 32 /* Nimrod locator */
+#define T_SRV 33 /* Server selection */
+#define T_ATMA 34 /* ATM Address */
+#define T_NAPTR 35 /* Naming Authority PoinTeR */
+ /* non standard */
+#define T_UINFO 100 /* user (finger) information */
+#define T_UID 101 /* user ID */
+#define T_GID 102 /* group ID */
+#define T_UNSPEC 103 /* Unspecified format (binary data) */
+ /* Query type values which do not appear in resource records */
+#define T_IXFR 251 /* incremental zone transfer */
+#define T_AXFR 252 /* transfer zone of authority */
+#define T_MAILB 253 /* transfer mailbox records */
+#define T_MAILA 254 /* transfer mail agent records */
+#define T_ANY 255 /* wildcard match */
+
+/*
+ * Values for class field
+ */
+
+#define C_IN 1 /* the arpa internet */
+#define C_CHAOS 3 /* for chaos net (MIT) */
+#define C_HS 4 /* for Hesiod name server (MIT) (XXX) */
+ /* Query class values which do not appear in resource records */
+#define C_ANY 255 /* wildcard match */
+
+/*
+ * Flags field of the KEY RR rdata
+ */
+#define KEYFLAG_TYPEMASK 0xC000 /* Mask for "type" bits */
+#define KEYFLAG_TYPE_AUTH_CONF 0x0000 /* Key usable for both */
+#define KEYFLAG_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */
+#define KEYFLAG_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */
+#define KEYFLAG_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */
+/* The type bits can also be interpreted independently, as single bits: */
+#define KEYFLAG_NO_AUTH 0x8000 /* Key not usable for authentication */
+#define KEYFLAG_NO_CONF 0x4000 /* Key not usable for confidentiality */
+
+#define KEYFLAG_EXPERIMENTAL 0x2000 /* Security is *mandatory* if bit=0 */
+#define KEYFLAG_RESERVED3 0x1000 /* reserved - must be zero */
+#define KEYFLAG_RESERVED4 0x0800 /* reserved - must be zero */
+#define KEYFLAG_USERACCOUNT 0x0400 /* key is assoc. with a user acct */
+#define KEYFLAG_ENTITY 0x0200 /* key is assoc. with entity eg host */
+#define KEYFLAG_ZONEKEY 0x0100 /* key is zone key for the zone named */
+#define KEYFLAG_IPSEC 0x0080 /* key is for IPSEC use (host or user)*/
+#define KEYFLAG_EMAIL 0x0040 /* key is for email (MIME security) */
+#define KEYFLAG_RESERVED10 0x0020 /* reserved - must be zero */
+#define KEYFLAG_RESERVED11 0x0010 /* reserved - must be zero */
+#define KEYFLAG_SIGNATORYMASK 0x000F /* key can sign DNS RR's of same name */
+
+#define KEYFLAG_RESERVED_BITMASK ( KEYFLAG_RESERVED3 | \
+ KEYFLAG_RESERVED4 | \
+ KEYFLAG_RESERVED10| KEYFLAG_RESERVED11)
+
+/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */
+#define ALGORITHM_MD5RSA 1 /* MD5 with RSA */
+#define ALGORITHM_EXPIRE_ONLY 253 /* No alg, no security */
+#define ALGORITHM_PRIVATE_OID 254 /* Key begins with OID indicating alg */
+
+/* Signatures */
+ /* Size of a mod or exp in bits */
+#define MIN_MD5RSA_KEY_PART_BITS 512
+#define MAX_MD5RSA_KEY_PART_BITS 2552
+ /* Total of binary mod and exp, bytes */
+#define MAX_MD5RSA_KEY_BYTES ((MAX_MD5RSA_KEY_PART_BITS+7/8)*2+3)
+ /* Max length of text sig block */
+#define MAX_KEY_BASE64 (((MAX_MD5RSA_KEY_BYTES+2)/3)*4)
+
+/*
+ * Status return codes for T_UNSPEC conversion routines
+ */
+#define CONV_SUCCESS 0
+#define CONV_OVERFLOW (-1)
+#define CONV_BADFMT (-2)
+#define CONV_BADCKSUM (-3)
+#define CONV_BADBUFLEN (-4)
+
+/*
+ * Structure for query header. The order of the fields is machine- and
+ * compiler-dependent, depending on the byte/bit order and the layout
+ * of bit fields. We use bit fields only in int variables, as this
+ * is all ANSI requires. This requires a somewhat confusing rearrangement.
+ */
+
+typedef struct {
+ unsigned id :16; /* query identification number */
+#if WORDS_BIGENDIAN
+ /* fields in third byte */
+ unsigned qr: 1; /* response flag */
+ unsigned opcode: 4; /* purpose of message */
+ unsigned aa: 1; /* authoritive answer */
+ unsigned tc: 1; /* truncated message */
+ unsigned rd: 1; /* recursion desired */
+ /* fields in fourth byte */
+ unsigned ra: 1; /* recursion available */
+ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */
+ unsigned ad: 1; /* authentic data from named */
+ unsigned cd: 1; /* checking disabled by resolver */
+ unsigned rcode :4; /* response code */
+#else /* WORDS_BIGENDIAN */
+ /* fields in third byte */
+ unsigned rd :1; /* recursion desired */
+ unsigned tc :1; /* truncated message */
+ unsigned aa :1; /* authoritive answer */
+ unsigned opcode :4; /* purpose of message */
+ unsigned qr :1; /* response flag */
+ /* fields in fourth byte */
+ unsigned rcode :4; /* response code */
+ unsigned cd: 1; /* checking disabled by resolver */
+ unsigned ad: 1; /* authentic data from named */
+ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */
+ unsigned ra :1; /* recursion available */
+#endif /* WORDS_BIGENDIAN */
+ /* remaining bytes */
+ unsigned qdcount :16; /* number of question entries */
+ unsigned ancount :16; /* number of answer entries */
+ unsigned nscount :16; /* number of authority entries */
+ unsigned arcount :16; /* number of resource entries */
+} HEADER;
+
+/*
+ * Defines for handling compressed domain names
+ */
+#define INDIR_MASK 0xc0
+
+/*
+ * Inline versions of get/put short/long. Pointer is advanced.
+ *
+ * These macros demonstrate the property of C whereby it can be
+ * portable or it can be elegant but rarely both.
+ */
+#define GETSHORT(s, cp) { \
+ register u_char *t_cp = (u_char *)(cp); \
+ (s) = ((u_int16_t)t_cp[0] << 8) \
+ | ((u_int16_t)t_cp[1]) \
+ ; \
+ (cp) += INT16SZ; \
+}
+
+#define GETLONG(l, cp) { \
+ register u_char *t_cp = (u_char *)(cp); \
+ (l) = ((u_int32_t)t_cp[0] << 24) \
+ | ((u_int32_t)t_cp[1] << 16) \
+ | ((u_int32_t)t_cp[2] << 8) \
+ | ((u_int32_t)t_cp[3]) \
+ ; \
+ (cp) += INT32SZ; \
+}
+
+#define PUTSHORT(s, cp) { \
+ register u_int16_t t_s = (u_int16_t)(s); \
+ register u_char *t_cp = (u_char *)(cp); \
+ *t_cp++ = t_s >> 8; \
+ *t_cp = t_s; \
+ (cp) += INT16SZ; \
+}
+
+#define PUTLONG(l, cp) { \
+ register u_int32_t t_l = (u_int32_t)(l); \
+ register u_char *t_cp = (u_char *)(cp); \
+ *t_cp++ = t_l >> 24; \
+ *t_cp++ = t_l >> 16; \
+ *t_cp++ = t_l >> 8; \
+ *t_cp = t_l; \
+ (cp) += INT32SZ; \
+}
diff --git a/ircd/res.c b/ircd/res.c
new file mode 100644
index 0000000..1be09c7
--- /dev/null
+++ b/ircd/res.c
@@ -0,0 +1,1697 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/res.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 "os.h"
+#include "s_defines.h"
+#define RES_C
+#include "s_externs.h"
+#undef RES_C
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: res.c,v 1.21 1999/07/02 17:31:17 kalt Exp $";
+#endif
+
+/* #undef DEBUG /* because there is a lot of debug code in here :-) */
+
+static char hostbuf[HOSTLEN+1+100]; /* +100 for INET6 */
+static char dot[] = ".";
+static int incache = 0;
+static CacheTable hashtable[ARES_CACSIZE];
+static aCache *cachetop = NULL;
+static ResRQ *last, *first;
+
+static void rem_cache __P((aCache *));
+static void rem_request __P((ResRQ *));
+static int do_query_name __P((Link *, char *, ResRQ *));
+static int do_query_number __P((Link *, struct IN_ADDR *, ResRQ *));
+static void resend_query __P((ResRQ *));
+static int proc_answer __P((ResRQ *, HEADER *, char *, char *));
+static int query_name __P((char *, int, int, ResRQ *));
+static aCache *make_cache __P((ResRQ *)), *rem_list __P((aCache *));
+static aCache *find_cache_name __P((char *));
+static aCache *find_cache_number __P((ResRQ *, char *));
+static int add_request __P((ResRQ *));
+static ResRQ *make_request __P((Link *));
+static int send_res_msg __P((char *, int, int));
+static ResRQ *find_id __P((int));
+static int hash_number __P((unsigned char *));
+static void update_list __P((ResRQ *, aCache *));
+static int hash_name __P((char *));
+static int bad_hostname __P((char *, int));
+
+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(op)
+int op;
+{
+ int ret = 0;
+
+#ifdef LRAND48
+ srand48(time(NULL));
+#endif
+ if (op & RES_INITLIST)
+ {
+ bzero((char *)&reinfo, sizeof(reinfo));
+ first = last = NULL;
+ }
+ if (op & RES_CALLINIT)
+ {
+ ret = ircd_res_init();
+ if (!ircd_res.nscount)
+ {
+ ircd_res.nscount = 1;
+#ifdef INET6
+ /* still IPv4 */
+ ircd_res.nsaddr_list[0].sin_addr.s_addr =
+ inet_pton(AF_INET, "127.0.0.1",
+ &ircd_res.nsaddr_list[0].sin_addr.s_addr);
+#else
+ ircd_res.nsaddr_list[0].sin_addr.s_addr =
+ inetaddr("127.0.0.1");
+#endif
+ }
+ }
+
+ if (op & RES_INITSOCK)
+ {
+ int on = 0;
+
+#ifdef INET6
+ /* still IPv4 */
+ ret = resfd = socket(AF_INET, SOCK_DGRAM, 0);
+#else
+ ret = resfd = socket(AF_INET, SOCK_DGRAM, 0);
+#endif
+ (void) SETSOCKOPT(ret, SOL_SOCKET, SO_BROADCAST, &on, on);
+ }
+#ifdef DEBUG
+ if (op & RES_INITDEBG);
+ ircd_res.options |= RES_DEBUG;
+#endif
+ if (op & RES_INITCACH)
+ {
+ bzero((char *)&cainfo, sizeof(cainfo));
+ bzero((char *)hashtable, sizeof(hashtable));
+ }
+ if (op == 0)
+ ret = resfd;
+ return ret;
+}
+
+static int add_request(new)
+ResRQ *new;
+{
+ if (!new)
+ return -1;
+ if (!first)
+ first = last = new;
+ else
+ {
+ last->next = new;
+ last = new;
+ }
+ new->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(old)
+ResRQ *old;
+{
+ Reg ResRQ **rptr, *r2ptr = NULL;
+ Reg int i;
+ Reg char *s;
+
+ if (!old)
+ return;
+ for (rptr = &first; *rptr; r2ptr = *rptr, rptr = &(*rptr)->next)
+ if (*rptr == old)
+ {
+ *rptr = old->next;
+ if (last == old)
+ last = r2ptr;
+ break;
+ }
+#ifdef DEBUG
+ Debug((DEBUG_INFO,"rem_request:Remove %#x at %#x %#x",
+ old, *rptr, r2ptr));
+#endif
+ r2ptr = old;
+ if (r2ptr->he.h_name)
+ MyFree((char *)r2ptr->he.h_name);
+ for (i = 0; i < MAXALIASES; i++)
+ if ((s = r2ptr->he.h_aliases[i]))
+ MyFree(s);
+ if (r2ptr->name)
+ MyFree(r2ptr->name);
+ MyFree((char *)r2ptr);
+
+ return;
+}
+
+/*
+ * Create a DNS request record for the server.
+ */
+static ResRQ *make_request(lp)
+Link *lp;
+{
+ Reg ResRQ *nreq;
+
+ nreq = (ResRQ *)MyMalloc(sizeof(ResRQ));
+ bzero((char *)nreq, sizeof(ResRQ));
+ nreq->next = NULL; /* where NULL is non-zero ;) */
+ nreq->sentat = timeofday;
+ nreq->retries = 3;
+ nreq->resend = 1;
+ nreq->srch = -1;
+ if (lp)
+ bcopy((char *)lp, (char *)&nreq->cinfo, sizeof(Link));
+ else
+ bzero((char *)&nreq->cinfo, sizeof(Link));
+ nreq->timeout = 4; /* start at 4 and exponential inc. */
+ nreq->he.h_addrtype = AFINET;
+ nreq->he.h_name = NULL;
+ nreq->he.h_aliases[0] = NULL;
+ (void)add_request(nreq);
+ return nreq;
+}
+
+/*
+ * Remove queries from the list which have been there too long without
+ * being resolved.
+ */
+time_t timeout_query_list(now)
+time_t now;
+{
+ Reg ResRQ *rptr, *r2ptr;
+ Reg time_t next = 0, tout;
+ 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)
+ {
+#ifdef DEBUG
+ Debug((DEBUG_ERROR,"timeout %x now %d cptr %x",
+ rptr, now, rptr->cinfo.value.cptr));
+#endif
+ reinfo.re_timeouts++;
+ cptr = rptr->cinfo.value.cptr;
+ switch (rptr->cinfo.flags)
+ {
+ case ASYNC_CLIENT :
+#if defined(USE_IAUTH)
+ sendto_iauth("%d d", cptr->fd);
+#endif
+ ClearDNS(cptr);
+ break;
+ case ASYNC_CONNECT :
+ sendto_flag(SCH_ERROR,
+ "Host %s unknown",
+ rptr->name);
+ break;
+ }
+ rem_request(rptr);
+ continue;
+ }
+ else
+ {
+ rptr->sentat = now;
+ rptr->timeout += rptr->timeout;
+ resend_query(rptr);
+ tout = now + rptr->timeout;
+#ifdef DEBUG
+ Debug((DEBUG_INFO,"r %x now %d retry %d c %x",
+ rptr, now, rptr->retries,
+ rptr->cinfo.value.cptr));
+#endif
+ }
+ if (!next || tout < next)
+ next = tout;
+ }
+ 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(cp)
+char *cp;
+{
+ Reg ResRQ *rptr, *r2ptr;
+
+ for (rptr = first; rptr; rptr = r2ptr)
+ {
+ r2ptr = rptr->next;
+ if (cp == rptr->cinfo.value.cp)
+ rem_request(rptr);
+ }
+}
+
+/*
+ * sends msg to all nameservers found in the "ircd_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(msg, len, rcount)
+char *msg;
+int len, rcount;
+{
+ Reg int i;
+ int sent = 0, max;
+
+ if (!msg)
+ return -1;
+
+ max = MIN(ircd_res.nscount, rcount);
+ if (ircd_res.options & RES_PRIMARY)
+ max = 1;
+ if (!max)
+ max = 1;
+
+ for (i = 0; i < max; i++)
+ {
+#ifdef INET6
+ /* still IPv4 */
+ ircd_res.nsaddr_list[i].sin_family = AF_INET;
+#else
+ ircd_res.nsaddr_list[i].sin_family = AF_INET;
+#endif
+#ifdef INET6
+ if (sendto(resfd, msg, len, 0,
+ (struct sockaddr *)&(ircd_res.nsaddr_list[i]),
+ sizeof(struct sockaddr)) == len)
+#else
+ if (sendto(resfd, msg, len, 0,
+ (struct sockaddr *)&(ircd_res.nsaddr_list[i]),
+ sizeof(struct sockaddr)) == len)
+#endif
+
+ {
+ reinfo.re_sent++;
+ sent++;
+ }
+ else
+ Debug((DEBUG_ERROR,"s_r_m:sendto: %d on %d",
+ errno, resfd));
+ }
+
+ return (sent) ? sent : -1;
+}
+
+
+/*
+ * find a dns request id (id is determined by dn_mkquery)
+ */
+static ResRQ *find_id(id)
+int id;
+{
+ Reg ResRQ *rptr;
+
+ for (rptr = first; rptr; rptr = rptr->next)
+ if (rptr->id == id)
+ return rptr;
+ return NULL;
+}
+
+struct hostent *gethost_byname(name, lp)
+char *name;
+Link *lp;
+{
+ Reg aCache *cp;
+
+ reinfo.re_na_look++;
+ if ((cp = find_cache_name(name)))
+ return (struct hostent *)&(cp->he);
+ if (!lp)
+ return NULL;
+ (void)do_query_name(lp, name, NULL);
+ return NULL;
+}
+
+struct hostent *gethost_byaddr(addr, lp)
+char *addr;
+Link *lp;
+{
+ aCache *cp;
+
+ reinfo.re_nu_look++;
+ if ((cp = find_cache_number(NULL, addr)))
+ return (struct hostent *)&(cp->he);
+ if (!lp)
+ return NULL;
+ (void)do_query_number(lp, (struct IN_ADDR *)addr, NULL);
+ return NULL;
+}
+
+static int do_query_name(lp, name, rptr)
+Link *lp;
+char *name;
+Reg ResRQ *rptr;
+{
+ char hname[HOSTLEN+1];
+ int len;
+
+ strncpyzt(hname, name, sizeof(hname));
+ len = strlen(hname);
+
+ if (rptr && !index(hname, '.') && ircd_res.options & RES_DEFNAMES)
+ {
+ (void)strncat(hname, dot, sizeof(hname) - len - 1);
+ len++;
+ (void)strncat(hname, ircd_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)
+ {
+ rptr = make_request(lp);
+#ifdef INET6
+ rptr->type = T_AAAA;
+#else
+ rptr->type = T_A;
+#endif
+ rptr->name = (char *)MyMalloc(strlen(name) + 1);
+ (void)strcpy(rptr->name, name);
+ }
+ Debug((DEBUG_DNS,"do_query_name(): %s ", hname));
+#ifdef INET6
+ return (query_name(hname, C_IN, T_AAAA, rptr));
+#else
+ return (query_name(hname, C_IN, T_A, rptr));
+#endif
+}
+
+/*
+ * Use this to do reverse IP# lookups.
+ */
+static int do_query_number(lp, numb, rptr)
+Link *lp;
+struct IN_ADDR *numb;
+Reg ResRQ *rptr;
+{
+ char ipbuf[128];
+ Reg u_char *cp;
+
+#ifdef INET6
+ cp = (u_char *)numb->s6_addr;
+ if (cp[0]==0 && cp[1]==0 && cp[2]==0 && cp[3]==0 && cp[4]==0 &&
+ cp[5]==0 && cp[6]==0 && cp[7]==0 && cp[8]==0 && cp[9]==0 &&
+ ((cp[10]==0 && cp[11]==0) || (cp[10]==0xff && cp[11]==0xff)))
+ {
+ (void)sprintf(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
+ (u_int)(cp[15]), (u_int)(cp[14]),
+ (u_int)(cp[13]), (u_int)(cp[12]));
+ }
+ else
+ {
+ (void)sprintf(ipbuf, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.int.",
+ (u_int)(cp[15]&0xf), (u_int)(cp[15]>>4),
+ (u_int)(cp[14]&0xf), (u_int)(cp[14]>>4),
+ (u_int)(cp[13]&0xf), (u_int)(cp[13]>>4),
+ (u_int)(cp[12]&0xf), (u_int)(cp[12]>>4),
+ (u_int)(cp[11]&0xf), (u_int)(cp[11]>>4),
+ (u_int)(cp[10]&0xf), (u_int)(cp[10]>>4),
+ (u_int)(cp[9]&0xf), (u_int)(cp[9]>>4),
+ (u_int)(cp[8]&0xf), (u_int)(cp[8]>>4),
+ (u_int)(cp[7]&0xf), (u_int)(cp[7]>>4),
+ (u_int)(cp[6]&0xf), (u_int)(cp[6]>>4),
+ (u_int)(cp[5]&0xf), (u_int)(cp[5]>>4),
+ (u_int)(cp[4]&0xf), (u_int)(cp[4]>>4),
+ (u_int)(cp[3]&0xf), (u_int)(cp[3]>>4),
+ (u_int)(cp[2]&0xf), (u_int)(cp[2]>>4),
+ (u_int)(cp[1]&0xf), (u_int)(cp[1]>>4),
+ (u_int)(cp[0]&0xf), (u_int)(cp[0]>>4));
+ }
+#else
+ cp = (u_char *)&numb->s_addr;
+ (void)sprintf(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
+ (u_int)(cp[3]), (u_int)(cp[2]),
+ (u_int)(cp[1]), (u_int)(cp[0]));
+#endif
+
+ if (!rptr)
+ {
+ rptr = make_request(lp);
+ rptr->type = T_PTR;
+#ifdef INET6
+ bcopy(numb->s6_addr, rptr->addr.s6_addr, IN6ADDRSZ);
+ bcopy((char *)numb->s6_addr,
+ (char *)&rptr->he.h_addr, sizeof(struct in6_addr));
+#else
+ rptr->addr.s_addr = numb->s_addr;
+ bcopy((char *)&numb->s_addr,
+ (char *)&rptr->he.h_addr, sizeof(struct in_addr));
+#endif
+ rptr->he.h_length = sizeof(struct IN_ADDR);
+ }
+ return (query_name(ipbuf, C_IN, T_PTR, rptr));
+}
+
+/*
+ * generate a query based on class, type and name.
+ */
+static int query_name(name, class, type, rptr)
+char *name;
+int class, type;
+ResRQ *rptr;
+{
+ struct timeval tv;
+ char buf[MAXPACKET];
+ int r,s,k = 0;
+ HEADER *hptr;
+
+ bzero(buf, sizeof(buf));
+ r = ircd_res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
+ (u_char *)buf, sizeof(buf));
+ if (r <= 0)
+ {
+ h_errno = NO_RECOVERY;
+ return r;
+ }
+ hptr = (HEADER *)buf;
+#ifdef LRAND48
+ do {
+ hptr->id = htons(ntohs(hptr->id) + k + lrand48() & 0xffff);
+#else
+ (void) gettimeofday(&tv, NULL);
+ do {
+ /* htons/ntohs can be assembler macros, which cannot
+ be nested. Thus two lines. -Vesa */
+ u_short nstmp = ntohs(hptr->id) + k +
+ (u_short)(tv.tv_usec & 0xffff);
+ hptr->id = htons(nstmp);
+#endif /* LRAND48 */
+ 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(rptr)
+ResRQ *rptr;
+{
+ if (rptr->resend == 0)
+ return;
+ reinfo.re_resends++;
+ switch(rptr->type)
+ {
+ case T_PTR:
+ (void)do_query_number(NULL, &rptr->addr, rptr);
+ break;
+#ifdef INET6
+ case T_AAAA:
+#endif
+ case T_A:
+ (void)do_query_name(NULL, rptr->name, rptr);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/*
+ * process name server reply.
+ */
+static int proc_answer(rptr, hptr, buf, eob)
+ResRQ *rptr;
+char *buf, *eob;
+HEADER *hptr;
+{
+ Reg char *cp, **alias;
+ Reg struct hent *hp;
+ int class, type, dlen, len, ans = 0, n;
+ struct IN_ADDR dr, *adr;
+
+ cp = buf + sizeof(HEADER);
+ hp = (struct hent *)&(rptr->he);
+ adr = &hp->h_addr;
+#ifdef INET6
+ while (adr->s6_laddr[0] | adr->s6_laddr[1] | adr->s6_laddr[2] |
+ adr->s6_laddr[3])
+#else
+ while (adr->s_addr)
+#endif
+ adr++;
+ alias = hp->h_aliases;
+ while (*alias)
+ alias++;
+#if SOLARIS_2 && !defined(__GNUC__) /* brain damaged compiler it seems */
+ for (; hptr->qdcount > 0; hptr->qdcount--)
+#else
+ while (hptr->qdcount-- > 0)
+#endif
+ if ((n = __ircd_dn_skipname((u_char *)cp, (u_char *)eob)) == -1)
+ break;
+ else
+ cp += (n + QFIXEDSZ);
+ /*
+ * proccess each answer sent to us blech.
+ */
+ while (hptr->ancount-- > 0 && cp && cp < eob) {
+ n = ircd_dn_expand((u_char *)buf, (u_char *)eob, (u_char *)cp,
+ hostbuf, sizeof(hostbuf));
+ if (n <= 0)
+ break;
+
+ cp += n;
+ type = (int)ircd_getshort((u_char *)cp);
+ cp += 2; /* INT16SZ */
+ class = (int)ircd_getshort((u_char *)cp);
+ cp += 2; /* INT16SZ */
+ rptr->ttl = ircd_getlong((u_char *)cp);
+ cp += 4; /* INT32SZ */
+ dlen = (int)ircd_getshort((u_char *)cp);
+ cp += 2; /* INT16SZ */
+ rptr->type = type;
+
+ len = strlen(hostbuf);
+ /* name server never returns with trailing '.' */
+ if (!index(hostbuf,'.') && (ircd_res.options & RES_DEFNAMES))
+ {
+ (void)strcat(hostbuf, dot);
+ len++;
+ (void)strncat(hostbuf, ircd_res.defdname,
+ sizeof(hostbuf) - 1 - len);
+ len = MIN(len + strlen(ircd_res.defdname),
+ sizeof(hostbuf) - 1);
+ }
+
+ switch(type)
+ {
+#ifdef INET6
+ case T_AAAA :
+#endif
+ case T_A :
+#ifdef INET6
+ if (dlen != ((type==T_AAAA) ? sizeof(dr) :
+ sizeof(struct in_addr)))
+#else
+ if (dlen != sizeof(dr))
+#endif
+ {
+ sendto_flag(SCH_ERROR,
+ "Bad IP length (%d) returned for %s", dlen,
+ hostbuf);
+ Debug((DEBUG_DNS,
+ "Bad IP length (%d) returned for %s",
+ dlen, hostbuf));
+ return -2;
+ }
+ hp->h_length = dlen;
+ if (ans == 1)
+ hp->h_addrtype = (class == C_IN) ?
+ AFINET: AF_UNSPEC;
+#ifdef INET6
+ if (type == T_AAAA)
+ bcopy(cp, (char *)&dr, dlen);
+ else {
+ dr.s6_laddr[0]=dr.s6_laddr[1]=0;
+ dr.s6_laddr[2]=htonl(0xffff);
+ bcopy(cp, &dr.s6_laddr[3], INADDRSZ);
+ }
+ bcopy(dr.s6_addr, adr->s6_addr, IN6ADDRSZ);
+#else
+ bcopy(cp, (char *)&dr, dlen);
+ adr->s_addr = dr.s_addr;
+#endif
+#ifdef INET6
+ Debug((DEBUG_INFO,"got ip # %s for %s",
+ inet_ntop(AF_INET6, (char *)adr, mydummy,
+ MYDUMMY_SIZE),
+ hostbuf));
+#else
+ Debug((DEBUG_INFO,"got ip # %s for %s",
+ inetntoa((char *)adr),
+ hostbuf));
+#endif
+ if (!hp->h_name)
+ {
+ hp->h_name =(char *)MyMalloc(len+1);
+ (void)strcpy(hp->h_name, hostbuf);
+ }
+ ans++;
+ adr++;
+ cp += dlen;
+ break;
+ case T_PTR :
+ if((n = ircd_dn_expand((u_char *)buf, (u_char *)eob,
+ (u_char *)cp, hostbuf,
+ sizeof(hostbuf) )) < 0)
+ {
+ cp = NULL;
+ break;
+ }
+ cp += n;
+ len = strlen(hostbuf);
+ Debug((DEBUG_INFO, "got host %s (%d vs %d)",
+ hostbuf, len, strlen(hostbuf)));
+ if (bad_hostname(hostbuf, len))
+ return -1;
+ /*
+ * copy the returned hostname into the host name
+ * or alias field if there is a known hostname
+ * already.
+ */
+ if (hp->h_name)
+ {
+ Debug((DEBUG_INFO, "duplicate PTR ignored"));
+ }
+ else
+ {
+ hp->h_name = (char *)MyMalloc(len + 1);
+ (void)strcpy(hp->h_name, hostbuf);
+ }
+ ans++;
+ break;
+ case T_CNAME :
+ cp += dlen;
+ Debug((DEBUG_INFO,"got cname %s",hostbuf));
+ if (bad_hostname(hostbuf, len))
+ return -1; /* a break would be enough here */
+ if (alias >= &(hp->h_aliases[MAXALIASES-1]))
+ break;
+ *alias = (char *)MyMalloc(len + 1);
+ (void)strcpy(*alias++, hostbuf);
+ *alias = NULL;
+ ans++;
+ break;
+ default :
+#ifdef DEBUG
+ Debug((DEBUG_INFO,"proc_answer: type:%d for:%s",
+ type,hostbuf));
+#endif
+ break;
+ }
+ }
+ return ans;
+}
+
+/*
+ * read a dns reply from the nameserver and process it.
+ */
+struct hostent *get_res(lp)
+char *lp;
+{
+ static char buf[sizeof(HEADER) + MAXPACKET];
+ Reg HEADER *hptr;
+ Reg ResRQ *rptr = NULL;
+ aCache *cp = NULL;
+#ifdef INET6
+ struct sockaddr_in sin;
+#else
+ struct sockaddr_in sin;
+#endif
+ int rc, a, max;
+ SOCK_LEN_TYPE len = sizeof(sin);
+
+ (void)alarm((unsigned)4);
+#ifdef INET6
+ rc = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len);
+#else
+ rc = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len);
+#endif
+
+ (void)alarm((unsigned)0);
+ if (rc <= sizeof(HEADER))
+ goto getres_err;
+ /*
+ * 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.
+ */
+ rptr = find_id(hptr->id);
+ if (!rptr)
+ goto getres_err;
+ /*
+ * check against possibly fake replies
+ */
+ max = MIN(ircd_res.nscount, rptr->sends);
+ if (!max)
+ max = 1;
+
+ for (a = 0; a < max; a++)
+#ifdef INET6
+ if (!ircd_res.nsaddr_list[a].sin_addr.s_addr ||
+ !bcmp((char *)&sin.sin_addr,
+ (char *)&ircd_res.nsaddr_list[a].sin_addr,
+ sizeof(struct in_addr)))
+#else
+ if (!ircd_res.nsaddr_list[a].sin_addr.s_addr ||
+ !bcmp((char *)&sin.sin_addr,
+ (char *)&ircd_res.nsaddr_list[a].sin_addr,
+ sizeof(struct in_addr)))
+#endif
+ break;
+ if (a == max)
+ {
+ reinfo.re_unkrep++;
+ 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;
+ }
+ a = proc_answer(rptr, hptr, buf, buf+rc);
+ if (a == -1) {
+ sendto_flag(SCH_ERROR, "Bad hostname returned from %s for %s",
+#ifdef INET6
+ inetntop(AF_INET, &sin.sin_addr, mydummy2,
+ MYDUMMY_SIZE),
+ inetntop(AF_INET6, rptr->he.h_addr.s6_addr,
+ mydummy, MYDUMMY_SIZE));
+#else
+ inetntoa((char *)&sin.sin_addr),
+ inetntoa((char *)&rptr->he.h_addr));
+#endif
+#ifdef INET6
+ Debug((DEBUG_DNS, "Bad hostname returned from %s for %s",
+ inet_ntop(AF_INET, &sin.sin_addr,mydummy2,MYDUMMY_SIZE),
+ inet_ntop(AF_INET6, rptr->he.h_addr.s6_addr, mydummy,
+ MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_DNS, "Bad hostname returned from %s for %s",
+ inetntoa((char *)&sin.sin_addr),
+ inetntoa((char *)&rptr->he.h_addr)));
+#endif
+ }
+#ifdef DEBUG
+ Debug((DEBUG_INFO,"get_res:Proc answer = %d",a));
+#endif
+ if (a > 0 && rptr->type == T_PTR)
+ {
+ struct hostent *hp2 = NULL;
+
+ if (BadPtr(rptr->he.h_name)) /* Kludge! 960907/Vesa */
+ goto getres_err;
+
+#ifdef INET6
+ Debug((DEBUG_DNS, "relookup %s <-> %s",
+ rptr->he.h_name, inet_ntop(AF_INET6,
+ (char *)&rptr->he.h_addr,
+ mydummy, MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_DNS, "relookup %s <-> %s",
+ rptr->he.h_name, inetntoa((char *)&rptr->he.h_addr)));
+#endif
+ /*
+ * 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(rptr->he.h_name, &rptr->cinfo)))
+ if (lp)
+ bcopy((char *)&rptr->cinfo, lp, 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
+ */
+ if (!hp2 && rptr->he.h_aliases[0])
+ for (a = 0; rptr->he.h_aliases[a]; a++)
+ {
+ Debug((DEBUG_DNS, "Copied CNAME %s for %s",
+ rptr->he.h_aliases[a],
+ rptr->he.h_name));
+ last->he.h_aliases[a] = rptr->he.h_aliases[a];
+ rptr->he.h_aliases[a] = NULL;
+ }
+
+ rem_request(rptr);
+ return hp2;
+ }
+
+ if (a > 0)
+ {
+ if (lp)
+ bcopy((char *)&rptr->cinfo, lp, sizeof(Link));
+ cp = make_cache(rptr);
+#ifdef DEBUG
+ Debug((DEBUG_INFO,"get_res:cp=%#x rptr=%#x (made)",cp,rptr));
+#endif
+
+ rem_request(rptr);
+ }
+ else
+ if (!rptr->sent)
+ rem_request(rptr);
+ return cp ? (struct hostent *)&cp->he : 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 (ircd_res.options & RES_DEFNAMES && ++rptr->srch == 0)
+ {
+ rptr->retries = ircd_res.retry;
+ rptr->sends = 0;
+ rptr->resend = 1;
+#ifdef INET6
+/* Comment out this ifdef to get names like ::ffff:a.b.c.d */
+ if(rptr->type == T_AAAA)
+ query_name(rptr->name, C_IN, T_A, rptr);
+ Debug((DEBUG_DNS,"getres_err: didn't work with T_AAAA, now also trying with T_A for %s",rptr->name));
+#endif
+ resend_query(rptr);
+ }
+ else
+ {
+#ifdef INET6
+/* Comment out this ifdef to get names like ::ffff:a.b.c.d */
+ if(rptr->type == T_AAAA)
+ query_name(rptr->name, C_IN, T_A, rptr);
+ Debug((DEBUG_DNS,"getres_err: didn't work with T_AAAA, now also trying with T_A for %s",rptr->name));
+#endif
+ resend_query(rptr);
+ }
+ }
+ else if (lp)
+ bcopy((char *)&rptr->cinfo, lp, sizeof(Link));
+ }
+ return (struct hostent *)NULL;
+}
+
+static int hash_number(ip)
+Reg u_char *ip;
+{
+ Reg u_int hashv = 0;
+
+ /* could use loop but slower */
+ hashv += (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+#ifdef INET6
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+ hashv += hashv + (int)*ip++;
+#endif
+ hashv += hashv + (int)*ip;
+ hashv %= ARES_CACSIZE;
+ return (hashv);
+}
+
+static int hash_name(name)
+register char *name;
+{
+ Reg u_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(ocp)
+Reg aCache *ocp;
+{
+ Reg aCache *cp = NULL;
+ Reg int hashv;
+
+#ifdef DEBUG
+ Debug((DEBUG_INFO,
+ "add_to_cache:ocp %#x he %#x name %#x addrl %#x 0 %#x",
+ ocp, &ocp->he, ocp->he.h_name, ocp->he.h_addr_list,
+ ocp->he.h_addr_list[0]));
+#endif
+ ocp->list_next = cachetop;
+ cachetop = ocp;
+
+ hashv = hash_name(ocp->he.h_name);
+ ocp->hname_next = hashtable[hashv].name_list;
+ hashtable[hashv].name_list = ocp;
+
+ hashv = hash_number((u_char *)ocp->he.h_addr);
+ ocp->hnum_next = hashtable[hashv].num_list;
+ hashtable[hashv].num_list = ocp;
+
+#ifdef DEBUG
+#ifdef INET6
+ Debug((DEBUG_INFO,"add_to_cache:added %s[%08x%08x%08x%08x] cache %#x.",
+ ocp->he.h_name,
+ ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[0],
+ ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[1],
+ ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[2],
+ ((struct in6_addr *)ocp->he.h_addr_list)->s6_laddr[3], ocp));
+#else
+ Debug((DEBUG_INFO, "add_to_cache:added %s[%08x] cache %#x.",
+ ocp->he.h_name, ocp->he.h_addr_list[0], ocp));
+#endif
+ Debug((DEBUG_INFO,
+ "add_to_cache:h1 %d h2 %x lnext %#x namnext %#x numnext %#x",
+ hash_name(ocp->he.h_name), hashv, ocp->list_next,
+ ocp->hname_next, ocp->hnum_next));
+#endif
+
+ /*
+ * 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(rptr, cachep)
+ResRQ *rptr;
+aCache *cachep;
+{
+ Reg aCache **cpp, *cp = cachep;
+ Reg char *s, *t, **base;
+ Reg int i, j;
+ int addrcount;
+
+ /*
+ ** 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;
+
+#ifdef DEBUG
+ Debug((DEBUG_DEBUG,"u_l:cp %#x na %#x al %#x ad %#x",
+ cp,cp->he.h_name,cp->he.h_aliases,cp->he.h_addr));
+ Debug((DEBUG_DEBUG,"u_l:rptr %#x h_n %#x", rptr, rptr->he.h_name));
+#endif
+ /*
+ * Compare the cache entry against the new record. Add any
+ * previously missing names for this entry.
+ */
+ for (i = 0; cp->he.h_aliases[i]; i++)
+ ;
+ addrcount = i;
+ for (i = 0, s = rptr->he.h_name; s && i < MAXALIASES;
+ s = rptr->he.h_aliases[i++])
+ {
+ for (j = 0, t = cp->he.h_name; t && j < MAXALIASES;
+ t = cp->he.h_aliases[j++])
+ if (!mycmp(t, s))
+ break;
+ if (!t && j < MAXALIASES-1)
+ {
+ base = cp->he.h_aliases;
+
+ addrcount++;
+ base = (char **)MyRealloc((char *)base,
+ sizeof(char *) * (addrcount + 1));
+ cp->he.h_aliases = base;
+#ifdef DEBUG
+ Debug((DEBUG_DNS,"u_l:add name %s hal %x ac %d",
+ s, cp->he.h_aliases, addrcount));
+#endif
+ base[addrcount-1] = mystrdup(s);
+ base[addrcount] = NULL;
+ }
+ }
+#ifdef INET6
+ for (i = 0; cp->he.h_addr_list[i]; i++)
+#else
+ for (i = 0; cp->he.h_addr_list[i]; i++)
+#endif
+ ;
+ addrcount = i;
+
+ /*
+ * Do the same again for IP#'s.
+ */
+#ifdef INET6
+ for (s = (char *)rptr->he.h_addr.S_ADDR;
+ ((struct IN_ADDR *)s)->S_ADDR; s += sizeof(struct IN_ADDR))
+#else
+ for (s = (char *)&rptr->he.h_addr.S_ADDR;
+ ((struct IN_ADDR *)s)->S_ADDR; s += sizeof(struct IN_ADDR))
+#endif
+ {
+#ifdef INET6
+ for (i = 0; (t = cp->he.h_addr_list[i]); i++)
+#else
+ for (i = 0; (t = cp->he.h_addr_list[i]); i++)
+#endif
+ if (!bcmp(s, t, sizeof(struct IN_ADDR)))
+ break;
+ if (i >= MAXADDRS || addrcount >= MAXADDRS)
+ break;
+ /*
+ * Oh man this is bad...I *HATE* it. -avalon
+ *
+ * Whats it do ? Reallocate two arrays, one of pointers
+ * to "char *" and the other of IP addresses. Contents of
+ * the IP array *MUST* be preserved and the pointers into
+ * it recalculated.
+ */
+ if (!t)
+ {
+ struct IN_ADDR **ab;
+
+ ab = (struct IN_ADDR **)cp->he.h_addr_list;
+ addrcount++;
+ t = (char *)MyRealloc((char *)*ab,
+ addrcount * sizeof(struct IN_ADDR));
+ base = (char **)MyRealloc((char *)ab,
+ (addrcount + 1) * sizeof(*ab));
+ cp->he.h_addr_list = base;
+#ifdef DEBUG
+ Debug((DEBUG_DNS,"u_l:add IP %x hal %x ac %d",
+ ntohl(((struct IN_ADDR *)s)->S_ADDR),
+ cp->he.h_addr_list,
+ addrcount));
+#endif
+ for (; addrcount; addrcount--)
+ {
+ *ab++ = (struct IN_ADDR *)t;
+ t += sizeof(struct IN_ADDR);
+ }
+ *ab = NULL;
+ bcopy(s, (char *)*--ab, sizeof(struct IN_ADDR));
+ }
+ }
+ return;
+}
+
+static aCache *find_cache_name(name)
+char *name;
+{
+ Reg aCache *cp;
+ Reg char *s;
+ Reg int hashv, i;
+
+ hashv = hash_name(name);
+
+ cp = hashtable[hashv].name_list;
+#ifdef DEBUG
+ Debug((DEBUG_DNS,"find_cache_name:find %s : hashv = %d",name,hashv));
+#endif
+
+ for (; cp; cp = cp->hname_next)
+ for (i = 0, s = cp->he.h_name; s; s = cp->he.h_aliases[i++])
+ if (mycmp(s, name) == 0)
+ {
+ cainfo.ca_na_hits++;
+ update_list(NULL, 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_aliases)
+ continue;
+ if (hashv == hash_name(cp->he.h_name))
+ continue;
+ for (i = 0, s = cp->he.h_aliases[i]; s && i < MAXALIASES; i++)
+ if (!mycmp(name, s)) {
+ cainfo.ca_na_hits++;
+ update_list(NULL, cp);
+ return cp;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * find a cache entry by ip# and update its expire time
+ */
+static aCache *find_cache_number(rptr, numb)
+ResRQ *rptr;
+char *numb;
+{
+ Reg aCache *cp;
+ Reg int hashv,i;
+#ifdef DEBUG
+ struct IN_ADDR *ip = (struct IN_ADDR *)numb;
+#endif
+
+ hashv = hash_number((u_char *)numb);
+
+ cp = hashtable[hashv].num_list;
+#ifdef DEBUG
+#ifdef INET6
+ Debug((DEBUG_DNS,
+ "find_cache_number:find %s[%08x%08x%08x%08x]: hashv = %d",
+ inet_ntop(AF_INET6, numb,mydummy,MYDUMMY_SIZE), ip->s6_laddr[0],
+ ip->s6_laddr[1], ip->s6_laddr[2], ip->s6_laddr[3], hashv));
+#else
+ Debug((DEBUG_DNS,"find_cache_number:find %s[%08x]: hashv = %d",
+ inetntoa(numb), ntohl(ip->s_addr), hashv));
+#endif
+#endif
+ for (; cp; cp = cp->hnum_next)
+ {
+#ifdef INET6
+ for (i = 0; cp->he.h_addr_list[i]; i++)
+#else
+ for (i = 0; cp->he.h_addr_list[i]; i++)
+#endif
+ {
+ if (!bcmp(cp->he.h_addr_list[i], numb,
+ sizeof(struct IN_ADDR)))
+ {
+ cainfo.ca_nu_hits++;
+ update_list(rptr, cp);
+ return cp;
+ }
+ }
+ }
+ for (cp = cachetop; cp; cp = cp->list_next)
+ {
+ if (!cp->he.h_addr_list && !cp->he.h_aliases)
+ {
+ cp = rem_list(cp);
+ continue;
+ }
+ /*
+ * single address entry...would have been done by hashed
+ * search above...
+ */
+#ifdef INET6
+ if (!cp->he.h_addr_list[1])
+#else
+ if (!cp->he.h_addr_list[1])
+#endif
+ continue;
+ /*
+ * if the first IP# has the same hashnumber as the IP# we
+ * are looking for, its been done already.
+ */
+ if (hashv == hash_number((u_char *)cp->he.h_addr_list[0]))
+ continue;
+#ifdef INET6
+ for (i = 1; cp->he.h_addr_list[i]; i++)
+#else
+ for (i = 1; cp->he.h_addr_list[i]; i++)
+#endif
+ if (!bcmp(cp->he.h_addr_list[i], numb,
+ sizeof(struct IN_ADDR)))
+ {
+ cainfo.ca_nu_hits++;
+ update_list(rptr, cp);
+ return cp;
+ }
+ }
+ return NULL;
+}
+
+static aCache *make_cache(rptr)
+ResRQ *rptr;
+{
+ Reg aCache *cp;
+ Reg int i, n;
+ Reg struct hostent *hp;
+ Reg char *s, **t;
+
+ /*
+ ** shouldn't happen but it just might...
+ */
+ if (!rptr->he.h_name || !WHOSTENTP(rptr->he.h_addr.S_ADDR))
+ 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; WHOSTENTP(rptr->he.h_addr_list[i].S_ADDR); i++)
+ if ((cp = find_cache_number(rptr,
+#ifdef INET6
+ (char *)(rptr->he.h_addr_list[i].S_ADDR))))
+#else
+ (char *)&(rptr->he.h_addr_list[i].S_ADDR))))
+#endif
+ return cp;
+
+ /*
+ ** a matching entry wasnt found in the cache so go and make one up.
+ */
+ cp = (aCache *)MyMalloc(sizeof(aCache));
+ bzero((char *)cp, sizeof(aCache));
+ hp = &cp->he;
+ for (i = 0; i < MAXADDRS - 1; i++)
+ if (!WHOSTENTP(rptr->he.h_addr_list[i].S_ADDR))
+ break;
+
+ /*
+ ** build two arrays, one for IP#'s, another of pointers to them.
+ */
+ t = hp->h_addr_list = (char **)MyMalloc(sizeof(char *) * (i+1));
+ bzero((char *)t, sizeof(char *) * (i+1));
+
+ s = (char *)MyMalloc(sizeof(struct IN_ADDR) * i);
+ bzero(s, sizeof(struct IN_ADDR) * i);
+
+ for (n = 0; n < i; n++, s += sizeof(struct IN_ADDR))
+ {
+ *t++ = s;
+ bcopy((char *)&rptr->he.h_addr_list[n], s,
+ sizeof(struct IN_ADDR));
+ }
+ *t = (char *)NULL;
+
+ /*
+ ** an array of pointers to CNAMEs.
+ */
+ for (i = 0; i < MAXALIASES - 1; i++)
+ if (!rptr->he.h_aliases[i])
+ break;
+ i++;
+ t = hp->h_aliases = (char **)MyMalloc(sizeof(char *) * i);
+ for (n = 0; n < i; n++, t++)
+ {
+ *t = rptr->he.h_aliases[n];
+ rptr->he.h_aliases[n] = NULL;
+ }
+
+ hp->h_addrtype = rptr->he.h_addrtype;
+ hp->h_length = rptr->he.h_length;
+ hp->h_name = rptr->he.h_name;
+ if (rptr->ttl < 600)
+ {
+ reinfo.re_shortttl++;
+ cp->ttl = 600;
+ }
+ else
+ cp->ttl = rptr->ttl;
+ cp->expireat = timeofday + cp->ttl;
+ rptr->he.h_name = NULL;
+#ifdef DEBUG
+ Debug((DEBUG_INFO,"make_cache:made cache %#x", cp));
+#endif
+ return add_to_cache(cp);
+}
+
+/*
+ * rem_list
+ */
+static aCache *rem_list(cp)
+aCache *cp;
+{
+ aCache **cpp, *cr = cp->list_next;
+
+ /*
+ * remove cache entry from linked list
+ */
+ for (cpp = &cachetop; *cpp; cpp = &((*cpp)->list_next))
+ if (*cpp == cp)
+ {
+ *cpp = cp->list_next;
+ MyFree((char *)cp);
+ break;
+ }
+ return cr;
+}
+
+
+/*
+ * 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(ocp)
+aCache *ocp;
+{
+ Reg aCache **cp;
+ Reg struct hostent *hp = &ocp->he;
+ Reg int hashv;
+ Reg aClient *cptr;
+
+#ifdef DEBUG
+ Debug((DEBUG_DNS, "rem_cache: ocp %#x hp %#x l_n %#x aliases %#x",
+ ocp, hp, ocp->list_next, hp->h_aliases));
+#endif
+ /*
+ ** Cleanup any references to this structure by destroying the
+ ** pointer.
+ */
+ for (hashv = highest_fd; hashv >= 0; hashv--)
+ if ((cptr = local[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);
+#ifdef DEBUG
+ Debug((DEBUG_DEBUG,"rem_cache: h_name %s hashv %d next %#x first %#x",
+ hp->h_name, hashv, ocp->hname_next,
+ hashtable[hashv].name_list));
+#endif
+ 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((u_char *)hp->h_addr);
+#ifdef DEBUG
+# ifdef INET6
+ Debug((DEBUG_DEBUG,"rem_cache: h_addr %s hashv %d next %#x first %#x",
+ inet_ntop(AF_INET6, hp->h_addr, mydummy, MYDUMMY_SIZE),
+ hashv, ocp->hnum_next, hashtable[hashv].num_list));
+# else
+ Debug((DEBUG_DEBUG,"rem_cache: h_addr %s hashv %d next %#x first %#x",
+ inetntoa(hp->h_addr),
+ hashv, ocp->hnum_next, hashtable[hashv].num_list));
+# endif
+#endif
+ for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next))
+ if (*cp == ocp)
+ {
+ *cp = ocp->hnum_next;
+ break;
+ }
+
+ /*
+ * free memory used to hold the various host names and the array
+ * of alias pointers.
+ */
+ if (hp->h_name)
+ MyFree(hp->h_name);
+ if (hp->h_aliases)
+ {
+ for (hashv = 0; hp->h_aliases[hashv]; hashv++)
+ MyFree(hp->h_aliases[hashv]);
+ MyFree((char *)hp->h_aliases);
+ }
+
+ /*
+ * free memory used to hold ip numbers and the array of them.
+ */
+ if (hp->h_addr_list)
+ {
+ if (*hp->h_addr_list)
+ MyFree((char *)*hp->h_addr_list);
+ MyFree((char *)hp->h_addr_list);
+ }
+
+ MyFree((char *)ocp);
+
+ incache--;
+ cainfo.ca_dels++;
+
+ return;
+}
+
+/*
+ * 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(now)
+time_t now;
+{
+ Reg aCache *cp, *cp2;
+ Reg 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()
+{
+ Reg aCache *cp;
+
+ while ((cp = cachetop))
+ rem_cache(cp);
+}
+
+int m_dns(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aCache *cp;
+ Reg int i;
+
+ if (parv[1] && *parv[1] == 'l') {
+ for(cp = cachetop; cp; cp = cp->list_next)
+ {
+ sendto_one(sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)",
+ parv[0], cp->expireat - timeofday, cp->ttl,
+#ifdef INET6
+ cp->he.h_name, inetntop(AF_INET6,
+ cp->he.h_addr,
+ mydummy,
+ MYDUMMY_SIZE));
+#else
+ cp->he.h_name, inetntoa(cp->he.h_addr));
+#endif
+ for (i = 0; cp->he.h_aliases[i]; i++)
+ sendto_one(sptr,"NOTICE %s : %s = %s (CN)",
+ parv[0], cp->he.h_name,
+ cp->he.h_aliases[i]);
+#ifdef INET6
+ for (i = 1; cp->he.h_addr_list[i]; i++) {
+#else
+ for (i = 1; cp->he.h_addr_list[i]; i++) {
+#endif
+ sendto_one(sptr,"NOTICE %s : %s = %s (IP)",
+ parv[0], cp->he.h_name,
+#ifdef INET6
+ inetntop(AF_INET6,
+ cp->he.h_addr_list[i],
+ mydummy, MYDUMMY_SIZE));
+#else
+ inetntoa(cp->he.h_addr_list[i]));
+#endif
+ }
+ }
+ return 2;
+ }
+ 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 2;
+}
+
+u_long cres_mem(sptr, nick)
+aClient *sptr;
+char *nick;
+{
+ register aCache *c = cachetop;
+ register struct hostent *h;
+ register int i;
+ u_long nm = 0, im = 0, sm = 0, ts = 0;
+
+ for ( ;c ; c = c->list_next)
+ {
+ sm += sizeof(*c);
+ h = &c->he;
+#ifdef INET6
+ for (i = 0; h->h_addr_list[i]; i++)
+#else
+ for (i = 0; h->h_addr_list[i]; i++)
+#endif
+ {
+ 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 %d",
+ me.name, RPL_STATSDEBUG, nick, ts);
+ sendto_one(sptr, ":%s %d %s :Structs %d IP storage %d Name storage %d",
+ me.name, RPL_STATSDEBUG, nick, sm, im, nm);
+ return ts + sm + im + nm;
+}
+
+
+static int bad_hostname(name, len)
+char *name;
+int len;
+{
+ char *s, c;
+
+ for (s = name; (c = *s) && len; s++, len--)
+ if (isspace(c) || (c == 0x7) || (c == ':') ||
+ (c == '*') || (c == '?'))
+ return -1;
+ return 0;
+}
diff --git a/ircd/res_comp.c b/ircd/res_comp.c
new file mode 100644
index 0000000..74e517f
--- /dev/null
+++ b/ircd/res_comp.c
@@ -0,0 +1,929 @@
+/*
+ * ++Copyright++ 1985, 1993
+ * -
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)res_comp.c 8.1 (Berkeley) 6/4/93";
+static char rcsid[] = "$Id: res_comp.c,v 1.6 1999/03/08 20:51:58 kalt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include "os.h"
+#include "s_defines.h"
+#define RES_COMP_C
+#include "s_externs.h"
+#undef RES_COMP_C
+
+static int ns_name_ntop __P((const u_char *, char *, size_t));
+static int ns_name_pton __P((const char *, u_char *, size_t));
+static int ns_name_unpack __P((const u_char *, const u_char *,
+ const u_char *, u_char *, size_t));
+static int ns_name_pack __P((const u_char *, u_char *, int,
+ const u_char **, const u_char **));
+static int ns_name_uncompress __P((const u_char *, const u_char *,
+ const u_char *, char *, size_t));
+static int ns_name_compress __P((const char *, u_char *, size_t,
+ const u_char **, const u_char **));
+static int ns_name_skip __P((const u_char **, const u_char *));
+
+/*
+ * Expand compressed domain name 'comp_dn' to full domain name.
+ * 'msg' is a pointer to the begining of the message,
+ * 'eomorig' points to the first location after the message,
+ * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
+ * Return size of compressed name or -1 if there was an error.
+ */
+int
+ircd_dn_expand(msg, eom, src, dst, dstsiz)
+ const u_char *msg;
+ const u_char *eom;
+ const u_char *src;
+ char *dst;
+ int dstsiz;
+{
+ int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz);
+
+ if (n > 0 && dst[0] == '.')
+ dst[0] = '\0';
+ return (n);
+}
+
+/*
+ * Pack domain name 'exp_dn' in presentation form into 'comp_dn'.
+ * Return the size of the compressed name or -1.
+ * 'length' is the size of the array pointed to by 'comp_dn'.
+ */
+int
+ircd_dn_comp(src, dst, dstsiz, dnptrs, lastdnptr)
+ const char *src;
+ u_char *dst;
+ int dstsiz;
+ u_char **dnptrs;
+ u_char **lastdnptr;
+{
+ return (ns_name_compress(src, dst, (size_t)dstsiz,
+ (const u_char **)dnptrs,
+ (const u_char **)lastdnptr));
+}
+
+/*
+ * Skip over a compressed domain name. Return the size or -1.
+ */
+int
+__ircd_dn_skipname(ptr, eom)
+ const u_char *ptr;
+ const u_char *eom;
+{
+ const u_char *saveptr = ptr;
+
+ if (ns_name_skip(&ptr, eom) == -1)
+ return (-1);
+ return (ptr - saveptr);
+}
+
+/*
+ * Verify that a domain name uses an acceptable character set.
+ */
+
+/*
+ * Note the conspicuous absence of ctype macros in these definitions. On
+ * non-ASCII hosts, we can't depend on string literals or ctype macros to
+ * tell us anything about network-format data. The rest of the BIND system
+ * is not careful about this, but for some reason, we're doing it right here.
+ */
+#define PERIOD 0x2e
+#define hyphenchar(c) ((c) == 0x2d)
+#define bslashchar(c) ((c) == 0x5c)
+#define periodchar(c) ((c) == PERIOD)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \
+ || ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#define middlechar(c) (borderchar(c) || hyphenchar(c))
+#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+#if 0
+/* it seems that we don't need these -krys */
+
+int
+res_hnok(dn)
+ const char *dn;
+{
+ int ppch = '\0', pch = PERIOD, ch = *dn++;
+
+ while (ch != '\0') {
+ int nch = *dn++;
+
+ if (periodchar(ch)) {
+ NULL;
+ } else if (periodchar(pch)) {
+ if (!borderchar(ch))
+ return (0);
+ } else if (periodchar(nch) || nch == '\0') {
+ if (!borderchar(ch))
+ return (0);
+ } else {
+ if (!middlechar(ch))
+ return (0);
+ }
+ ppch = pch, pch = ch, ch = nch;
+ }
+ return (1);
+}
+
+/*
+ * hostname-like (A, MX, WKS) owners can have "*" as their first label
+ * but must otherwise be as a host name.
+ */
+int
+res_ownok(dn)
+ const char *dn;
+{
+ if (asterchar(dn[0])) {
+ if (periodchar(dn[1]))
+ return (res_hnok(dn+2));
+ if (dn[1] == '\0')
+ return (1);
+ }
+ return (res_hnok(dn));
+}
+
+/*
+ * SOA RNAMEs and RP RNAMEs can have any printable character in their first
+ * label, but the rest of the name has to look like a host name.
+ */
+int
+res_mailok(dn)
+ const char *dn;
+{
+ int ch, escaped = 0;
+
+ /* "." is a valid missing representation */
+ if (*dn == '\0')
+ return(1);
+
+ /* otherwise <label>.<hostname> */
+ while ((ch = *dn++) != '\0') {
+ if (!domainchar(ch))
+ return (0);
+ if (!escaped && periodchar(ch))
+ break;
+ if (escaped)
+ escaped = 0;
+ else if (bslashchar(ch))
+ escaped = 1;
+ }
+ if (periodchar(ch))
+ return (res_hnok(dn));
+ return(0);
+}
+
+/*
+ * This function is quite liberal, since RFC 1034's character sets are only
+ * recommendations.
+ */
+int
+res_dnok(dn)
+ const char *dn;
+{
+ int ch;
+
+ while ((ch = *dn++) != '\0')
+ if (!domainchar(ch))
+ return (0);
+ return (1);
+}
+#endif
+
+/*
+ * Routines to insert/extract short/long's.
+ */
+
+u_int16_t
+ircd_getshort(msgp)
+ register const u_char *msgp;
+{
+ register u_int16_t u;
+
+ GETSHORT(u, msgp);
+ return (u);
+}
+
+u_int32_t
+ircd_getlong(msgp)
+ register const u_char *msgp;
+{
+ register u_int32_t u;
+
+ GETLONG(u, msgp);
+ return (u);
+}
+
+void
+#if __STDC__
+ircd__putshort(register u_int16_t s, register u_char *msgp) /* must match proto */
+#else
+ircd__putshort(s, msgp)
+ register u_int16_t s;
+ register u_char *msgp;
+#endif
+{
+ PUTSHORT(s, msgp);
+}
+
+void
+ircd__putlong(l, msgp)
+ register u_int32_t l;
+ register u_char *msgp;
+{
+ PUTLONG(l, msgp);
+}
+
+/* ++ From BIND 8.1.1. ++ */
+/*
+ * Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*"Id: ns_name.c,v 1.1 1997/12/13 02:41:13 vixie Exp vixie"*/
+
+/*#include "port_before.h"*/
+
+/*#include <sys/types.h>*/
+
+/*#include <netinet/in.h>*/
+/*#include <arpa/nameser.h>*/
+
+/*#include <errno.h>*/
+/*#include <resolv.h>*/
+/*#include <string.h>*/
+
+/*#include "port_after.h"*/
+
+#define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */
+#define NS_MAXCDNAME 255 /* maximum compressed domain name */
+
+/* Data. */
+
+static char digits[] = "0123456789";
+
+/* Forward. */
+
+static int special __P((int));
+static int printable __P((int));
+static int dn_find __P((const u_char *, const u_char *,
+ const u_char * const *,
+ const u_char * const *));
+
+/* Public. */
+
+/*
+ * ns_name_ntop(src, dst, dstsiz)
+ * Convert an encoded domain name to printable ascii as per RFC1035.
+ * return:
+ * Number of bytes written to buffer, or -1 (with errno set)
+ * notes:
+ * The root is returned as "."
+ * All other domains are returned in non absolute form
+ */
+static int
+ns_name_ntop(src, dst, dstsiz)
+ const u_char *src;
+ char *dst;
+ size_t dstsiz;
+{
+ const u_char *cp;
+ char *dn, *eom;
+ u_char c;
+ u_int n;
+
+ cp = src;
+ dn = dst;
+ eom = dst + dstsiz;
+
+ while ((n = *cp++) != 0) {
+ if ((n & NS_CMPRSFLGS) != 0) {
+ /* Some kind of compression pointer. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dn != dst) {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '.';
+ }
+ if (dn + n >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ for ((void)NULL; n > 0; n--) {
+ c = *cp++;
+ if (special(c)) {
+ if (dn + 1 >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\\';
+ *dn++ = (char)c;
+ } else if (!printable(c)) {
+ if (dn + 3 >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\\';
+ *dn++ = digits[c / 100];
+ *dn++ = digits[(c % 100) / 10];
+ *dn++ = digits[c % 10];
+ } else {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = (char)c;
+ }
+ }
+ }
+ if (dn == dst) {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '.';
+ }
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\0';
+ return (dn - dst);
+}
+
+/*
+ * ns_name_pton(src, dst, dstsiz)
+ * Convert a ascii string into an encoded domain name as per RFC1035.
+ * return:
+ * -1 if it fails
+ * 1 if string was fully qualified
+ * 0 is string was not fully qualified
+ * notes:
+ * Enforces label and domain length limits.
+ */
+
+static int
+ns_name_pton(src, dst, dstsiz)
+ const char *src;
+ u_char *dst;
+ size_t dstsiz;
+{
+ u_char *label, *bp, *eom;
+ int c, n, escaped;
+ char *cp;
+
+ escaped = 0;
+ bp = dst;
+ eom = dst + dstsiz;
+ label = bp++;
+
+ while ((c = *src++) != 0) {
+ if (escaped) {
+ if ((cp = strchr(digits, c)) != NULL) {
+ n = (cp - digits) * 100;
+ if ((c = *src++) == 0 ||
+ (cp = strchr(digits, c)) == NULL) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ n += (cp - digits) * 10;
+ if ((c = *src++) == 0 ||
+ (cp = strchr(digits, c)) == NULL) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ n += (cp - digits);
+ if (n > 255) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ c = n;
+ }
+ escaped = 0;
+ } else if (c == '\\') {
+ escaped = 1;
+ continue;
+ } else if (c == '.') {
+ c = (bp - label - 1);
+ if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (label >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *label = c;
+ /* Fully qualified ? */
+ if (*src == '\0') {
+ if (c != 0) {
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = '\0';
+ }
+ if ((bp - dst) > MAXCDNAME) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (1);
+ }
+ if (c == 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ label = bp++;
+ continue;
+ }
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = (u_char)c;
+ }
+ c = (bp - label - 1);
+ if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (label >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *label = c;
+ if (c != 0) {
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = 0;
+ }
+ if ((bp - dst) > MAXCDNAME) { /* src too big */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * ns_name_unpack(msg, eom, src, dst, dstsiz)
+ * Unpack a domain name from a message, source may be compressed.
+ * return:
+ * -1 if it fails, or consumed octets if it succeeds.
+ */
+static int
+ns_name_unpack(msg, eom, src, dst, dstsiz)
+ const u_char *msg;
+ const u_char *eom;
+ const u_char *src;
+ u_char *dst;
+ size_t dstsiz;
+{
+ const u_char *srcp, *dstlim;
+ u_char *dstp;
+ int n, c, len, checked;
+
+ len = -1;
+ checked = 0;
+ dstp = dst;
+ srcp = src;
+ dstlim = dst + dstsiz;
+ if (srcp < msg || srcp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ /* Fetch next label in domain name. */
+ while ((n = *srcp++) != 0) {
+ /* Check for indirection. */
+ switch (n & NS_CMPRSFLGS) {
+ case 0:
+ /* Limit checks. */
+ if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ checked += n + 1;
+ *dstp++ = n;
+ memcpy(dstp, srcp, n);
+ dstp += n;
+ srcp += n;
+ break;
+
+ case NS_CMPRSFLGS:
+ if (srcp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (len < 0)
+ len = srcp - src + 1;
+ srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
+ if (srcp < msg || srcp >= eom) { /* Out of range. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ checked += 2;
+ /*
+ * Check for loops in the compressed name;
+ * if we've looked at the whole message,
+ * there must be a loop.
+ */
+ if (checked >= eom - msg) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ break;
+
+ default:
+ errno = EMSGSIZE;
+ return (-1); /* flag error */
+ }
+ }
+ *dstp = '\0';
+ if (len < 0)
+ len = srcp - src;
+ return (len);
+}
+
+/*
+ * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
+ * Pack domain name 'domain' into 'comp_dn'.
+ * return:
+ * Size of the compressed name, or -1.
+ * notes:
+ * 'dnptrs' is an array of pointers to previous compressed names.
+ * dnptrs[0] is a pointer to the beginning of the message. The array
+ * ends with NULL.
+ * 'lastdnptr' is a pointer to the end of the array pointed to
+ * by 'dnptrs'.
+ * Side effects:
+ * The list of pointers in dnptrs is updated for labels inserted into
+ * the message as we compress the name. If 'dnptr' is NULL, we don't
+ * try to compress names. If 'lastdnptr' is NULL, we don't update the
+ * list.
+ */
+static int
+ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
+ const u_char *src;
+ u_char *dst;
+ int dstsiz;
+ const u_char **dnptrs;
+ const u_char **lastdnptr;
+{
+ u_char *dstp;
+ const u_char **cpp, **lpp, *eob, *msg;
+ const u_char *srcp;
+ int n, l;
+
+ srcp = src;
+ dstp = dst;
+ eob = dstp + dstsiz;
+ lpp = cpp = NULL;
+ if (dnptrs != NULL) {
+ if ((msg = *dnptrs++) != NULL) {
+ for (cpp = dnptrs; *cpp != NULL; cpp++)
+ (void)NULL;
+ lpp = cpp; /* end of list to search */
+ }
+ } else
+ msg = NULL;
+
+ /* make sure the domain we are about to add is legal */
+ l = 0;
+ do {
+ n = *srcp;
+ if ((n & NS_CMPRSFLGS) != 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ l += n + 1;
+ if (l > MAXCDNAME) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ srcp += n + 1;
+ } while (n != 0);
+
+ srcp = src;
+ do {
+ /* Look to see if we can use pointers. */
+ n = *srcp;
+ if (n != 0 && msg != NULL) {
+ l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
+ (const u_char * const *)lpp);
+ if (l >= 0) {
+ if (dstp + 1 >= eob) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dstp++ = (l >> 8) | NS_CMPRSFLGS;
+ *dstp++ = l % 256;
+ return (dstp - dst);
+ }
+ /* Not found, save it. */
+ if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
+ (dstp - msg) < 0x4000) {
+ *cpp++ = dstp;
+ *cpp = NULL;
+ }
+ }
+ /* copy label to buffer */
+ if (n & NS_CMPRSFLGS) { /* Should not happen. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dstp + 1 + n >= eob) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ memcpy(dstp, srcp, n + 1);
+ srcp += n + 1;
+ dstp += n + 1;
+ } while (n != 0);
+
+ if (dstp > eob) {
+ if (msg != NULL)
+ *lpp = NULL;
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (dstp - dst);
+}
+
+/*
+ * ns_name_uncompress(msg, eom, src, dst, dstsiz)
+ * Expand compressed domain name to presentation format.
+ * return:
+ * Number of bytes read out of `src', or -1 (with errno set).
+ * note:
+ * Root domain returns as "." not "".
+ */
+static int
+ns_name_uncompress(msg, eom, src, dst, dstsiz)
+ const u_char *msg;
+ const u_char *eom;
+ const u_char *src;
+ char *dst;
+ size_t dstsiz;
+{
+ u_char tmp[NS_MAXCDNAME];
+ int n;
+
+ if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
+ return (-1);
+ if (ns_name_ntop(tmp, dst, dstsiz) == -1)
+ return (-1);
+ return (n);
+}
+
+/*
+ * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
+ * Compress a domain name into wire format, using compression pointers.
+ * return:
+ * Number of bytes consumed in `dst' or -1 (with errno set).
+ * notes:
+ * 'dnptrs' is an array of pointers to previous compressed names.
+ * dnptrs[0] is a pointer to the beginning of the message.
+ * The list ends with NULL. 'lastdnptr' is a pointer to the end of the
+ * array pointed to by 'dnptrs'. Side effect is to update the list of
+ * pointers for labels inserted into the message as we compress the name.
+ * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
+ * is NULL, we don't update the list.
+ */
+static int
+ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
+ const char *src;
+ u_char *dst;
+ size_t dstsiz;
+ const u_char **dnptrs;
+ const u_char **lastdnptr;
+{
+ u_char tmp[NS_MAXCDNAME];
+
+ if (ns_name_pton(src, tmp, sizeof tmp) == -1)
+ return (-1);
+ return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
+}
+
+/*
+ * ns_name_skip(ptrptr, eom)
+ * Advance *ptrptr to skip over the compressed name it points at.
+ * return:
+ * 0 on success, -1 (with errno set) on failure.
+ */
+static int
+ns_name_skip(ptrptr, eom)
+ const u_char **ptrptr;
+ const u_char *eom;
+{
+ const u_char *cp;
+ u_int n;
+
+ cp = *ptrptr;
+ while (cp < eom && (n = *cp++) != 0) {
+ /* Check for indirection. */
+ switch (n & NS_CMPRSFLGS) {
+ case 0: /* normal case, n == len */
+ cp += n;
+ continue;
+ case NS_CMPRSFLGS: /* indirection */
+ cp++;
+ break;
+ default: /* illegal type */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ break;
+ }
+ if (cp > eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *ptrptr = cp;
+ return (0);
+}
+
+/* Private. */
+
+/*
+ * special(ch)
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * is this characted special ("in need of quoting") ?
+ * return:
+ * boolean.
+ */
+static int
+special(ch)
+ int ch;
+{
+ switch (ch) {
+ case 0x22: /* '"' */
+ case 0x2E: /* '.' */
+ case 0x3B: /* ';' */
+ case 0x5C: /* '\\' */
+ /* Special modifiers in zone files. */
+ case 0x40: /* '@' */
+ case 0x24: /* '$' */
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * printable(ch)
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * is this character visible and not a space when printed ?
+ * return:
+ * boolean.
+ */
+static int
+printable(ch)
+ int ch;
+{
+ return (ch > 0x20 && ch < 0x7f);
+}
+
+/*
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * convert this character to lower case if it's upper case.
+ */
+static int
+mklower(ch)
+ int ch;
+{
+ if (ch >= 0x41 && ch <= 0x5A)
+ return (ch + 0x20);
+ return (ch);
+}
+
+/*
+ * dn_find(domain, msg, dnptrs, lastdnptr)
+ * Search for the counted-label name in an array of compressed names.
+ * return:
+ * offset from msg if found, or -1.
+ * notes:
+ * dnptrs is the pointer to the first name on the list,
+ * not the pointer to the start of the message.
+ */
+static int
+dn_find(domain, msg, dnptrs, lastdnptr)
+ const u_char *domain;
+ const u_char *msg;
+ const u_char * const *dnptrs;
+ const u_char * const *lastdnptr;
+{
+ const u_char *dn, *cp, *sp;
+ const u_char * const *cpp;
+ u_int n;
+
+ for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
+ dn = domain;
+ sp = cp = *cpp;
+ while ((n = *cp++) != 0) {
+ /*
+ * check for indirection
+ */
+ switch (n & NS_CMPRSFLGS) {
+ case 0: /* normal case, n == len */
+ if (n != *dn++)
+ goto next;
+ for ((void)NULL; n > 0; n--)
+ if (mklower(*dn++) != mklower(*cp++))
+ goto next;
+ /* Is next root for both ? */
+ if (*dn == '\0' && *cp == '\0')
+ return (sp - msg);
+ if (*dn)
+ continue;
+ goto next;
+
+ case NS_CMPRSFLGS: /* indirection */
+ cp = msg + (((n & 0x3f) << 8) | *cp);
+ break;
+
+ default: /* illegal type */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ }
+ next: ;
+ }
+ errno = ENOENT;
+ return (-1);
+}
+
+/* -- From BIND 8.1.1. -- */
diff --git a/ircd/res_comp_ext.h b/ircd/res_comp_ext.h
new file mode 100644
index 0000000..273bf05
--- /dev/null
+++ b/ircd/res_comp_ext.h
@@ -0,0 +1,44 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/res_comp_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/res_comp.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef RES_COMP_C
+#define EXTERN extern
+#else /* RES_COMP_C */
+#define EXTERN
+#endif /* RES_COMP_C */
+EXTERN int ircd_dn_expand __P((const u_char *msg, const u_char *eomorig,
+ const u_char *comp_dn, char *exp_dn,
+ int length));
+EXTERN int ircd_dn_comp __P((const char *exp_dn, u_char *comp_dn, int length,
+ u_char **dnptrs, u_char **lastdnptr));
+EXTERN int __ircd_dn_skipname __P((const u_char *comp_dn, const u_char *eom));
+EXTERN u_int16_t ircd_getshort __P((register const u_char *msgp));
+EXTERN u_int32_t ircd_getlong __P((register const u_char *msgp));
+EXTERN void ircd__putshort __P((register u_int16_t s, register u_char *msgp));
+EXTERN void ircd__putlong __P((register u_int32_t l, register u_char *msgp));
+#ifdef NEXT
+EXTERN u_int16_t res_getshort __P((register const u_char *msgp));
+#endif /* NEXT */
+#undef EXTERN
diff --git a/ircd/res_def.h b/ircd/res_def.h
new file mode 100644
index 0000000..689b36f
--- /dev/null
+++ b/ircd/res_def.h
@@ -0,0 +1,59 @@
+/*
+ * ircd/res_def.h (C)opyright 1992 Darren Reed.
+ */
+
+#define RES_INITLIST 1
+#define RES_CALLINIT 2
+#define RES_INITSOCK 4
+#define RES_INITDEBG 8
+#define RES_INITCACH 16
+
+#define MAXPACKET 1024
+#define MAXALIASES 35
+#define MAXADDRS 35
+
+#define AR_TTL 600 /* TTL in seconds for dns cache entries */
+
+struct hent {
+ char *h_name; /* official name of host */
+ char *h_aliases[MAXALIASES]; /* alias list */
+ int h_addrtype; /* host address type */
+ int h_length; /* length of address */
+ /* list of addresses from name server */
+ struct IN_ADDR h_addr_list[MAXADDRS];
+#define h_addr h_addr_list[0] /* address, for backward compatiblity */
+};
+
+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;
+ struct hent he;
+ } ResRQ;
+
+typedef struct cache {
+ time_t expireat;
+ time_t ttl;
+ struct hostent he;
+ struct cache *hname_next, *hnum_next, *list_next;
+ } aCache;
+
+typedef struct cachetable {
+ aCache *num_list;
+ aCache *name_list;
+ } CacheTable;
+
+#define ARES_CACSIZE 101
+
+#define MAXCACHED 81
diff --git a/ircd/res_ext.h b/ircd/res_ext.h
new file mode 100644
index 0000000..9e4c8eb
--- /dev/null
+++ b/ircd/res_ext.h
@@ -0,0 +1,41 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/res_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/res.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef RES_C
+#define EXTERN extern
+#else /* RES_C */
+#define EXTERN
+#endif /* RES_C */
+extern int init_resolver __P((int op));
+EXTERN time_t timeout_query_list __P((time_t now));
+EXTERN void del_queries __P((char *cp));
+EXTERN struct hostent *gethost_byname __P((char *name, Link *lp));
+EXTERN struct hostent *gethost_byaddr __P((char *addr, Link *lp));
+EXTERN struct hostent *get_res __P((char *lp));
+EXTERN time_t expire_cache __P((time_t now));
+EXTERN void flush_cache();
+EXTERN int m_dns __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN u_long cres_mem __P((aClient *sptr, char *nick));
+#undef EXTERN
diff --git a/ircd/res_init.c b/ircd/res_init.c
new file mode 100644
index 0000000..126144a
--- /dev/null
+++ b/ircd/res_init.c
@@ -0,0 +1,642 @@
+/*
+ * ++Copyright++ 1985, 1989, 1993
+ * -
+ * Copyright (c) 1985, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)res_init.c 8.1 (Berkeley) 6/7/93";
+static char rcsid[] = "$Id: res_init.c,v 1.10 1999/01/20 01:33:08 kalt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include "os.h"
+#include "s_defines.h"
+#define RES_INIT_C
+#include "s_externs.h"
+#undef RES_INIT_C
+
+/*-------------------------------------- info about "sortlist" --------------
+ * Marc Majka 1994/04/16
+ * Allan Nathanson 1994/10/29 (BIND 4.9.3.x)
+ *
+ * NetInfo resolver configuration directory support.
+ *
+ * Allow a NetInfo directory to be created in the hierarchy which
+ * contains the same information as the resolver configuration file.
+ *
+ * - The local domain name is stored as the value of the "domain" property.
+ * - The Internet address(es) of the name server(s) are stored as values
+ * of the "nameserver" property.
+ * - The name server addresses are stored as values of the "nameserver"
+ * property.
+ * - The search list for host-name lookup is stored as values of the
+ * "search" property.
+ * - The sortlist comprised of IP address netmask pairs are stored as
+ * values of the "sortlist" property. The IP address and optional netmask
+ * should be seperated by a slash (/) or ampersand (&) character.
+ * - Internal resolver variables can be set from the value of the "options"
+ * property.
+ */
+#if defined(NEXT)
+# define NI_PATH_RESCONF "/locations/resolver"
+# define NI_TIMEOUT 10
+static int ircd_netinfo_res_init __P((int *haveenv, int *havesearch));
+#endif
+
+static void ircd_res_setoptions __P((char *, char *));
+
+#ifdef RESOLVSORT
+static const char sort_mask[] = "/&";
+#define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL)
+#ifdef INET6
+static u_int32_t ircd_net_mask __P((struct in_addr));
+#else
+static u_int32_t ircd_net_mask __P((struct in_addr));
+#endif
+#endif
+
+#if !defined(isascii) /* XXX - could be a function */
+# define isascii(c) (!(c & 0200))
+#endif
+
+/*
+ * Resolver state default settings.
+ */
+
+struct __res_state ircd_res
+# if defined(__BIND_RES_TEXT)
+ = { RES_TIMEOUT, } /* Motorola, et al. */
+# endif
+ ;
+
+/*
+ * Set up default settings. If the configuration file exist, the values
+ * there will have precedence. Otherwise, the server address is set to
+ * INADDR_ANY and the default domain name comes from the gethostname().
+ *
+ * An interrim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1
+ * rather than INADDR_ANY ("0.0.0.0") as the default name server address
+ * since it was noted that INADDR_ANY actually meant ``the first interface
+ * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface,
+ * it had to be "up" in order for you to reach your own name server. It
+ * was later decided that since the recommended practice is to always
+ * install local static routes through 127.0.0.1 for all your network
+ * interfaces, that we could solve this problem without a code change.
+ *
+ * The configuration file should always be used, since it is the only way
+ * to specify a default domain. If you are running a server on your local
+ * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1"
+ * in the configuration file.
+ *
+ * Return 0 if completes successfully, -1 on error
+ */
+int
+ircd_res_init()
+{
+ register FILE *fp;
+ register char *cp, **pp;
+ register int n;
+ char buf[MAXDNAME];
+ int nserv = 0; /* number of nameserver records read from file */
+ int haveenv = 0;
+ int havesearch = 0;
+#ifdef RESOLVSORT
+ int nsort = 0;
+ char *net;
+#endif
+#ifndef RFC1535
+ int dots;
+#endif
+
+ /*
+ * These three fields used to be statically initialized. This made
+ * it hard to use this code in a shared library. It is necessary,
+ * now that we're doing dynamic initialization here, that we preserve
+ * the old semantics: if an application modifies one of these three
+ * fields of _res before res_init() is called, res_init() will not
+ * alter them. Of course, if an application is setting them to
+ * _zero_ before calling res_init(), hoping to override what used
+ * to be the static default, we can't detect it and unexpected results
+ * will follow. Zero for any of these fields would make no sense,
+ * so one can safely assume that the applications were already getting
+ * unexpected results.
+ *
+ * _res.options is tricky since some apps were known to diddle the bits
+ * before res_init() was first called. We can't replicate that semantic
+ * with dynamic initialization (they may have turned bits off that are
+ * set in RES_DEFAULT). Our solution is to declare such applications
+ * "broken". They could fool us by setting RES_INIT but none do (yet).
+ */
+ if (!ircd_res.retrans)
+ ircd_res.retrans = RES_TIMEOUT;
+ if (!ircd_res.retry)
+ ircd_res.retry = 4;
+ if (!(ircd_res.options & RES_INIT))
+ ircd_res.options = RES_DEFAULT;
+
+ /*
+ * This one used to initialize implicitly to zero, so unless the app
+ * has set it to something in particular, we can randomize it now.
+ */
+ if (!ircd_res.id)
+ ircd_res.id = ircd_res_randomid();
+
+/*ifdef INET6 not, because of IPv4 DNS serving */
+#ifdef USELOOPBACK
+ ircd_res.nsaddr.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1);
+#else
+ ircd_res.nsaddr.sin_addr.s_addr = INADDR_ANY;
+#endif
+ ircd_res.nsaddr.sin_family = AF_INET;
+ ircd_res.nsaddr.sin_port = htons(NAMESERVER_PORT);
+ ircd_res.nscount = 1;
+ ircd_res.ndots = 1;
+ ircd_res.pfcode = 0;
+
+ /* Allow user to override the local domain definition */
+ if ((cp = getenv("LOCALDOMAIN")) != NULL) {
+ strncpyzt(ircd_res.defdname, cp, sizeof(ircd_res.defdname));
+ haveenv++;
+
+ /*
+ * Set search list to be blank-separated strings
+ * from rest of env value. Permits users of LOCALDOMAIN
+ * to still have a search list, and anyone to set the
+ * one that they want to use as an individual (even more
+ * important now that the rfc1535 stuff restricts searches)
+ */
+ cp = ircd_res.defdname;
+ pp = ircd_res.dnsrch;
+ *pp++ = cp;
+ for (n = 0; *cp && pp < ircd_res.dnsrch + MAXDNSRCH; cp++) {
+ if (*cp == '\n') /* silly backwards compat */
+ break;
+ else if (*cp == ' ' || *cp == '\t') {
+ *cp = 0;
+ n = 1;
+ } else if (n) {
+ *pp++ = cp;
+ n = 0;
+ havesearch = 1;
+ }
+ }
+ /* null terminate last domain if there are excess */
+ while (*cp != '\0' && *cp != ' ' && *cp != '\t' && *cp != '\n')
+ cp++;
+ *cp = '\0';
+ *pp++ = 0;
+ }
+
+#define MATCH(line, name) \
+ (!strncmp(line, name, sizeof(name) - 1) && \
+ (line[sizeof(name) - 1] == ' ' || \
+ line[sizeof(name) - 1] == '\t'))
+
+#ifdef NEXT
+ if (ircd_netinfo_res_init(&haveenv, &havesearch) == 0)
+#endif
+ if ((fp = fopen(IRC_RESCONF, "r")) != NULL) {
+ /* read the config file */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ /* skip comments */
+ if (*buf == ';' || *buf == '#')
+ continue;
+ /* read default domain name */
+ if (MATCH(buf, "domain")) {
+ if (haveenv) /* skip if have from environ */
+ continue;
+ cp = buf + sizeof("domain") - 1;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if ((*cp == '\0') || (*cp == '\n'))
+ continue;
+ strncpyzt(ircd_res.defdname, cp, sizeof(ircd_res.defdname));
+ if ((cp = strpbrk(ircd_res.defdname, " \t\n")) != NULL)
+ *cp = '\0';
+ havesearch = 0;
+ continue;
+ }
+ /* set search list */
+ if (MATCH(buf, "search")) {
+ if (haveenv) /* skip if have from environ */
+ continue;
+ cp = buf + sizeof("search") - 1;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if ((*cp == '\0') || (*cp == '\n'))
+ continue;
+ strncpyzt(ircd_res.defdname, cp, sizeof(ircd_res.defdname));
+ if ((cp = strchr(ircd_res.defdname, '\n')) != NULL)
+ *cp = '\0';
+ /*
+ * Set search list to be blank-separated strings
+ * on rest of line.
+ */
+ cp = ircd_res.defdname;
+ pp = ircd_res.dnsrch;
+ *pp++ = cp;
+ for (n = 0; *cp && pp < ircd_res.dnsrch + MAXDNSRCH; cp++) {
+ if (*cp == ' ' || *cp == '\t') {
+ *cp = 0;
+ n = 1;
+ } else if (n) {
+ *pp++ = cp;
+ n = 0;
+ }
+ }
+ /* null terminate last domain if there are excess */
+ while (*cp != '\0' && *cp != ' ' && *cp != '\t')
+ cp++;
+ *cp = '\0';
+ *pp++ = 0;
+ havesearch = 1;
+ continue;
+ }
+ /* read nameservers to query */
+ if (MATCH(buf, "nameserver") && nserv < MAXNS) {
+ struct in_addr a;
+
+ cp = buf + sizeof("nameserver") - 1;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if ((*cp != '\0') && (*cp != '\n') && inetaton(cp, &a)) {
+ ircd_res.nsaddr_list[nserv].sin_addr = a;
+ ircd_res.nsaddr_list[nserv].sin_family = AF_INET;
+ ircd_res.nsaddr_list[nserv].sin_port =
+ htons(NAMESERVER_PORT);
+ nserv++;
+ }
+ continue;
+ }
+#ifdef RESOLVSORT
+ if (MATCH(buf, "sortlist")) {
+ struct in_addr a;
+
+ cp = buf + sizeof("sortlist") - 1;
+ while (nsort < MAXRESOLVSORT) {
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0' || *cp == '\n' || *cp == ';')
+ break;
+ net = cp;
+ while (*cp && !ISSORTMASK(*cp) && *cp != ';' &&
+ isascii(*cp) && !isspace(*cp))
+ cp++;
+ n = *cp;
+ *cp = 0;
+ if (inetaton(net, &a)) {
+ ircd_res.sort_list[nsort].addr = a;
+ if (ISSORTMASK(n)) {
+ *cp++ = n;
+ net = cp;
+ while (*cp && *cp != ';' &&
+ isascii(*cp) && !isspace(*cp))
+ cp++;
+ n = *cp;
+ *cp = 0;
+ if (inetaton(net, &a)) {
+ ircd_res.sort_list[nsort].mask = a.s_addr;
+ } else {
+ ircd_res.sort_list[nsort].mask =
+ ircd_net_mask(ircd_res.sort_list[nsort].addr);
+ }
+ } else {
+ ircd_res.sort_list[nsort].mask =
+ ircd_net_mask(ircd_res.sort_list[nsort].addr);
+ }
+ nsort++;
+ }
+ *cp = n;
+ }
+ continue;
+ }
+#endif
+ if (MATCH(buf, "options")) {
+ ircd_res_setoptions(buf + sizeof("options") - 1, "conf");
+ continue;
+ }
+ }
+ if (nserv > 1)
+ ircd_res.nscount = nserv;
+#ifdef RESOLVSORT
+ ircd_res.nsort = nsort;
+#endif
+ (void) fclose(fp);
+ }
+ if (ircd_res.defdname[0] == 0 &&
+ gethostname(buf, sizeof(ircd_res.defdname) - 1) == 0 &&
+ (cp = strchr(buf, '.')) != NULL)
+ strcpy(ircd_res.defdname, cp + 1);
+
+ /* find components of local domain that might be searched */
+ if (havesearch == 0) {
+ pp = ircd_res.dnsrch;
+ *pp++ = ircd_res.defdname;
+ *pp = NULL;
+
+#ifndef RFC1535
+ dots = 0;
+ for (cp = ircd_res.defdname; *cp; cp++)
+ dots += (*cp == '.');
+
+ cp = ircd_res.defdname;
+ while (pp < ircd_res.dnsrch + MAXDFLSRCH) {
+ if (dots < LOCALDOMAINPARTS)
+ break;
+ cp = strchr(cp, '.') + 1; /* we know there is one */
+ *pp++ = cp;
+ dots--;
+ }
+ *pp = NULL;
+#ifdef DEBUG
+ if (ircd_res.options & RES_DEBUG) {
+ printf(";; res_init()... default dnsrch list:\n");
+ for (pp = ircd_res.dnsrch; *pp; pp++)
+ printf(";;\t%s\n", *pp);
+ printf(";;\t..END..\n");
+ }
+#endif /* DEBUG */
+#endif /* !RFC1535 */
+ }
+
+ if ((cp = getenv("RES_OPTIONS")) != NULL)
+ ircd_res_setoptions(cp, "env");
+ ircd_res.options |= RES_INIT;
+ return (0);
+}
+
+static void
+ircd_res_setoptions(options, source)
+ char *options, *source;
+{
+ char *cp = options;
+ int i;
+
+#ifdef DEBUG
+ if (ircd_res.options & RES_DEBUG)
+ printf(";; ircd_res_setoptions(\"%s\", \"%s\")...\n",
+ options, source);
+#endif
+ while (*cp) {
+ /* skip leading and inner runs of spaces */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ /* search for and process individual options */
+ if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) {
+ i = atoi(cp + sizeof("ndots:") - 1);
+ if (i <= RES_MAXNDOTS)
+ ircd_res.ndots = i;
+ else
+ ircd_res.ndots = RES_MAXNDOTS;
+#ifdef DEBUG
+ if (ircd_res.options & RES_DEBUG)
+ printf(";;\tndots=%d\n", ircd_res.ndots);
+#endif
+ } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) {
+#ifdef DEBUG
+ if (!(ircd_res.options & RES_DEBUG)) {
+ printf(";; ircd_res_setoptions(\"%s\", \"%s\")..\n",
+ options, source);
+ ircd_res.options |= RES_DEBUG;
+ }
+ printf(";;\tdebug\n");
+#endif
+ } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) {
+ ircd_res.options |= RES_USE_INET6;
+ } else {
+ /* XXX - print a warning here? */
+ }
+ /* skip to next run of spaces */
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ }
+}
+
+#ifdef RESOLVSORT
+/* XXX - should really support CIDR which means explicit masks always. */
+static u_int32_t
+ircd_net_mask(in) /* XXX - should really use system's version of this */
+ struct in_addr in;
+{
+ register u_int32_t i = ntohl(in.s_addr);
+
+ if (IN_CLASSA(i))
+ return (htonl(IN_CLASSA_NET));
+ else if (IN_CLASSB(i))
+ return (htonl(IN_CLASSB_NET));
+ return (htonl(IN_CLASSC_NET));
+}
+#endif
+
+#ifdef NEXT
+static int
+ircd_netinfo_res_init(haveenv, havesearch)
+ int *haveenv;
+ int *havesearch;
+{
+ register int n;
+ void *domain, *parent;
+ ni_id dir;
+ ni_status status;
+ ni_namelist nl;
+ int nserv = 0;
+#ifdef RESOLVSORT
+ int nsort = 0;
+#endif
+
+ status = ni_open(NULL, ".", &domain);
+ if (status == NI_OK) {
+ ni_setreadtimeout(domain, NI_TIMEOUT);
+ ni_setabort(domain, 1);
+
+ /* climb the NetInfo hierarchy to find a resolver directory */
+ while (status == NI_OK) {
+ status = ni_pathsearch(domain, &dir, NI_PATH_RESCONF);
+ if (status == NI_OK) {
+ /* found a resolver directory */
+
+ if (*haveenv == 0) {
+ /* get the default domain name */
+ status = ni_lookupprop(domain, &dir, "domain", &nl);
+ if (status == NI_OK && nl.ni_namelist_len > 0) {
+ (void)strncpy(ircd_res.defdname,
+ nl.ni_namelist_val[0],
+ sizeof(ircd_res.defdname) - 1);
+ ircd_res.defdname[sizeof(ircd_res.defdname) - 1] = '\0';
+ ni_namelist_free(&nl);
+ *havesearch = 0;
+ }
+
+ /* get search list */
+ status = ni_lookupprop(domain, &dir, "search", &nl);
+ if (status == NI_OK && nl.ni_namelist_len > 0) {
+ (void)strncpy(ircd_res.defdname,
+ nl.ni_namelist_val[0],
+ sizeof(ircd_res.defdname) - 1);
+ ircd_res.defdname[sizeof(ircd_res.defdname) - 1] = '\0';
+ /* copy */
+ for (n = 0;
+ n < nl.ni_namelist_len && n < MAXDNSRCH;
+ n++) {
+ /* duplicate up to MAXDNSRCH servers */
+ char *cp = nl.ni_namelist_val[n];
+ ircd_res.dnsrch[n] =
+ strcpy((char *)malloc(strlen(cp) + 1), cp);
+ }
+ ni_namelist_free(&nl);
+ *havesearch = 1;
+ }
+ }
+
+ /* get list of nameservers */
+ status = ni_lookupprop(domain, &dir, "nameserver", &nl);
+ if (status == NI_OK && nl.ni_namelist_len > 0) {
+ /* copy up to MAXNS servers */
+ for (n = 0;
+ n < nl.ni_namelist_len && nserv < MAXNS;
+ n++) {
+ struct in_addr a;
+
+ if (inetaton(nl.ni_namelist_val[n], &a)) {
+ ircd_res.nsaddr_list[nserv].sin_addr = a;
+ ircd_res.nsaddr_list[nserv].sin_family = AF_INET;
+ ircd_res.nsaddr_list[nserv].sin_port =
+ htons(NAMESERVER_PORT);
+ nserv++;
+ }
+ }
+ ni_namelist_free(&nl);
+ }
+
+ if (nserv > 1)
+ ircd_res.nscount = nserv;
+
+#ifdef RESOLVSORT
+ /* get sort order */
+ status = ni_lookupprop(domain, &dir, "sortlist", &nl);
+ if (status == NI_OK && nl.ni_namelist_len > 0) {
+
+ /* copy up to MAXRESOLVSORT address/netmask pairs */
+ for (n = 0;
+ n < nl.ni_namelist_len && nsort < MAXRESOLVSORT;
+ n++) {
+ char ch;
+ char *cp;
+ const char *sp;
+ struct in_addr a;
+
+ cp = NULL;
+ for (sp = sort_mask; *sp; sp++) {
+ char *cp1;
+ cp1 = strchr(nl.ni_namelist_val[n], *sp);
+ if (cp && cp1)
+ cp = (cp < cp1)? cp : cp1;
+ else if (cp1)
+ cp = cp1;
+ }
+ if (cp != NULL) {
+ ch = *cp;
+ *cp = '\0';
+ break;
+ }
+ if (inetaton(nl.ni_namelist_val[n], &a)) {
+ ircd_res.sort_list[nsort].addr = a;
+ if (*cp && ISSORTMASK(ch)) {
+ *cp++ = ch;
+ if (inetaton(cp, &a)) {
+ ircd_res.sort_list[nsort].mask = a.s_addr;
+ } else {
+ ircd_res.sort_list[nsort].mask =
+ ircd_net_mask(ircd_res.sort_list[nsort].addr);
+ }
+ } else {
+ ircd_res.sort_list[nsort].mask =
+ ircd_net_mask(ircd_res.sort_list[nsort].addr);
+ }
+ nsort++;
+ }
+ }
+ ni_namelist_free(&nl);
+ }
+
+ ircd_res.nsort = nsort;
+#endif
+
+ /* get resolver options */
+ status = ni_lookupprop(domain, &dir, "options", &nl);
+ if (status == NI_OK && nl.ni_namelist_len > 0) {
+ ircd_res_setoptions(nl.ni_namelist_val[0], "conf");
+ ni_namelist_free(&nl);
+ }
+
+ ni_free(domain);
+ return(1); /* using DNS configuration from NetInfo */
+ }
+
+ status = ni_open(domain, "..", &parent);
+ ni_free(domain);
+ if (status == NI_OK)
+ domain = parent;
+ }
+ }
+ return(0); /* if not using DNS configuration from NetInfo */
+}
+#endif /* NEXT */
+
+u_int
+ircd_res_randomid()
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ return (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid()));
+}
diff --git a/ircd/res_init_ext.h b/ircd/res_init_ext.h
new file mode 100644
index 0000000..8537269
--- /dev/null
+++ b/ircd/res_init_ext.h
@@ -0,0 +1,39 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/res_init_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/res_init.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef RES_INIT_C
+extern struct __res_state ircd_res;
+#endif /* RES_INIT_C */
+
+/* External definitions for global functions.
+ */
+#ifndef RES_INIT_C
+#define EXTERN extern
+#else /* RES_INIT_C */
+#define EXTERN
+#endif /* RES_INIT_C */
+EXTERN int ircd_res_init();
+EXTERN u_int ircd_res_randomid();
+#undef EXTERN
diff --git a/ircd/res_mkquery.c b/ircd/res_mkquery.c
new file mode 100644
index 0000000..a0fb2fc
--- /dev/null
+++ b/ircd/res_mkquery.c
@@ -0,0 +1,178 @@
+/*
+ * ++Copyright++ 1985, 1993
+ * -
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)res_mkquery.c 8.1 (Berkeley) 6/4/93";
+static char rcsid[] = "$Id: res_mkquery.c,v 1.4 1997/09/03 17:45:55 kalt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include "os.h"
+#include "s_defines.h"
+#define RES_MKQUERY_C
+#include "s_externs.h"
+#undef RES_MKQUERY_C
+
+/*
+ * Form all types of queries.
+ * Returns the size of the result or -1.
+ */
+int
+ircd_res_mkquery(op, dname, class, type, data, datalen, newrr_in, buf, buflen)
+ int op; /* opcode of query */
+ const char *dname; /* domain name */
+ int class, type; /* class and type of query */
+ const u_char *data; /* resource record data */
+ int datalen; /* length of data */
+ const u_char *newrr_in; /* new rr for modify or append */
+ u_char *buf; /* buffer to put query */
+ int buflen; /* size of buffer */
+{
+ register HEADER *hp;
+ register u_char *cp;
+ register int n;
+ u_char *dnptrs[20], **dpp, **lastdnptr;
+
+ if ((ircd_res.options & RES_INIT) == 0 && ircd_res_init() == -1) {
+ h_errno = NETDB_INTERNAL;
+ return (-1);
+ }
+#ifdef DEBUG
+ if (ircd_res.options & RES_DEBUG)
+ printf(";; res_mkquery(%d, %s, %d, %d)\n",
+ op, dname, class, type);
+#endif
+ /*
+ * Initialize header fields.
+ */
+ if ((buf == NULL) || (buflen < HFIXEDSZ))
+ return (-1);
+ bzero(buf, HFIXEDSZ);
+ hp = (HEADER *) buf;
+ hp->id = htons(++ircd_res.id);
+ hp->opcode = op;
+ hp->rd = (ircd_res.options & RES_RECURSE) != 0;
+ hp->rcode = NOERROR;
+ cp = buf + HFIXEDSZ;
+ buflen -= HFIXEDSZ;
+ dpp = dnptrs;
+ *dpp++ = buf;
+ *dpp++ = NULL;
+ lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+ /*
+ * perform opcode specific processing
+ */
+ switch (op) {
+ case QUERY: /*FALLTHROUGH*/
+ case NS_NOTIFY_OP:
+ if ((buflen -= QFIXEDSZ) < 0)
+ return (-1);
+ if ((n = ircd_dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+ return (-1);
+ cp += n;
+ buflen -= n;
+ ircd__putshort(type, cp);
+ cp += INT16SZ;
+ ircd__putshort(class, cp);
+ cp += INT16SZ;
+ hp->qdcount = htons(1);
+ if (op == QUERY || data == NULL)
+ break;
+ /*
+ * Make an additional record for completion domain.
+ */
+ buflen -= RRFIXEDSZ;
+ n = ircd_dn_comp((char *)data, cp, buflen, dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ buflen -= n;
+ ircd__putshort(T_NULL, cp);
+ cp += INT16SZ;
+ ircd__putshort(class, cp);
+ cp += INT16SZ;
+ ircd__putlong(0, cp);
+ cp += INT32SZ;
+ ircd__putshort(0, cp);
+ cp += INT16SZ;
+ hp->arcount = htons(1);
+ break;
+
+ case IQUERY:
+ /*
+ * Initialize answer section
+ */
+ if (buflen < 1 + RRFIXEDSZ + datalen)
+ return (-1);
+ *cp++ = '\0'; /* no domain name */
+ ircd__putshort(type, cp);
+ cp += INT16SZ;
+ ircd__putshort(class, cp);
+ cp += INT16SZ;
+ ircd__putlong(0, cp);
+ cp += INT32SZ;
+ ircd__putshort(datalen, cp);
+ cp += INT16SZ;
+ if (datalen) {
+ bcopy(data, cp, datalen);
+ cp += datalen;
+ }
+ hp->ancount = htons(1);
+ break;
+
+ default:
+ return (-1);
+ }
+ return (cp - buf);
+}
diff --git a/ircd/res_mkquery_ext.h b/ircd/res_mkquery_ext.h
new file mode 100644
index 0000000..7441ead
--- /dev/null
+++ b/ircd/res_mkquery_ext.h
@@ -0,0 +1,35 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/res_mkquery_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/res_mkquery.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef RES_MKQUERY_C
+#define EXTERN extern
+#else /* RES_MKQUERY_C */
+#define EXTERN
+#endif /* RES_MKQUERY_C */
+EXTERN int ircd_res_mkquery __P((int op, const char *dname, int class,
+ int type, const u_char *data, int datalen,
+ const u_char *newrr_in, u_char *buf,
+ int buflen));
+#undef EXTERN
diff --git a/ircd/resolv_def.h b/ircd/resolv_def.h
new file mode 100644
index 0000000..3cc74eb
--- /dev/null
+++ b/ircd/resolv_def.h
@@ -0,0 +1,220 @@
+/*
+ * ++Copyright++ 1983, 1987, 1989, 1993
+ * -
+ * Copyright (c) 1983, 1987, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+/*
+ * @(#)resolv.h 8.1 (Berkeley) 6/2/93
+ * $Id: resolv_def.h,v 1.5 1999/01/20 01:33:08 kalt Exp $
+ */
+
+/*
+ * Revision information. This is the release date in YYYYMMDD format.
+ * It can change every day so the right thing to do with it is use it
+ * in preprocessor commands such as "#if (__RES > 19931104)". Do not
+ * compare for equality; rather, use it to determine whether your resolver
+ * is new enough to contain a certain feature.
+ */
+
+#define __RES 19960801
+
+/*
+ * Global defines and variables for resolver stub.
+ */
+#define MAXNS 3 /* max # name servers we'll track */
+#define MAXDFLSRCH 3 /* # default domain levels to try */
+#define MAXDNSRCH 6 /* max # domains in search path */
+#define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */
+
+#define RES_TIMEOUT 5 /* min. seconds between retries */
+#define MAXRESOLVSORT 10 /* number of net to sort on */
+#define RES_MAXNDOTS 15 /* should reflect bit field size */
+
+struct __res_state {
+ int retrans; /* retransmition time interval */
+ int retry; /* number of times to retransmit */
+ u_long options; /* option flags - see below. */
+ int nscount; /* number of name servers */
+ struct sockaddr_in
+ nsaddr_list[MAXNS]; /* address of name server */
+#define nsaddr nsaddr_list[0] /* for backward compatibility */
+ u_short id; /* current message id */
+ char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */
+ char defdname[256]; /* default domain (deprecated) */
+ u_long pfcode; /* RES_PRF_ flags - see below. */
+ unsigned ndots:4; /* threshold for initial abs. query */
+ unsigned nsort:4; /* number of elements in sort_list[] */
+ char unused[3];
+ struct {
+ struct in_addr addr;
+ u_int32_t mask;
+ } sort_list[MAXRESOLVSORT];
+ char pad[72]; /* on an i386 this means 512b total */
+};
+
+/*
+ * Resolver options (keep these in synch with res_debug.c, please)
+ */
+#define RES_INIT 0x00000001 /* address initialized */
+#define RES_DEBUG 0x00000002 /* print debug messages */
+#define RES_AAONLY 0x00000004 /* authoritative answers only (!IMPL)*/
+#define RES_USEVC 0x00000008 /* use virtual circuit */
+#define RES_PRIMARY 0x00000010 /* query primary server only (!IMPL) */
+#define RES_IGNTC 0x00000020 /* ignore trucation errors */
+#define RES_RECURSE 0x00000040 /* recursion desired */
+#define RES_DEFNAMES 0x00000080 /* use default domain name */
+#define RES_STAYOPEN 0x00000100 /* Keep TCP socket open */
+#define RES_DNSRCH 0x00000200 /* search up local domain tree */
+#define RES_INSECURE1 0x00000400 /* type 1 security disabled */
+#define RES_INSECURE2 0x00000800 /* type 2 security disabled */
+#define RES_NOALIASES 0x00001000 /* shuts off HOSTALIASES feature */
+#define RES_USE_INET6 0x00002000 /* use/map IPv6 in gethostbyname() */
+
+#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH)
+
+/*
+ * Resolver "pfcode" values. Used by dig.
+ */
+#define RES_PRF_STATS 0x00000001
+/* 0x00000002 */
+#define RES_PRF_CLASS 0x00000004
+#define RES_PRF_CMD 0x00000008
+#define RES_PRF_QUES 0x00000010
+#define RES_PRF_ANS 0x00000020
+#define RES_PRF_AUTH 0x00000040
+#define RES_PRF_ADD 0x00000080
+#define RES_PRF_HEAD1 0x00000100
+#define RES_PRF_HEAD2 0x00000200
+#define RES_PRF_TTLID 0x00000400
+#define RES_PRF_HEADX 0x00000800
+#define RES_PRF_QUERY 0x00001000
+#define RES_PRF_REPLY 0x00002000
+#define RES_PRF_INIT 0x00004000
+/* 0x00008000 */
+
+/* hooks are still experimental as of 4.9.2 */
+#if defined(INET6) && defined(__GNUC__)
+
+#else
+
+typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error }
+ res_sendhookact;
+
+typedef res_sendhookact (*res_send_qhook)__P((struct SOCKADDR_IN * const *ns,
+ const u_char **query,
+ int *querylen,
+ u_char *ans,
+ int anssiz,
+ int *resplen));
+
+typedef res_sendhookact (*res_send_rhook)__P((const struct SOCKADDR_IN *ns,
+ const u_char *query,
+ int querylen,
+ u_char *ans,
+ int anssiz,
+ int *resplen));
+#endif
+
+struct res_sym {
+ int number; /* Identifying number, like T_MX */
+ char * name; /* Its symbolic name, like "MX" */
+ char * humanname; /* Its fun name, like "mail exchanger" */
+};
+
+/* Private routines shared between libc/net, named, nslookup and others. */
+#define res_hnok __res_hnok
+#define res_ownok __res_ownok
+#define res_mailok __res_mailok
+#define res_dnok __res_dnok
+#define sym_ston __sym_ston
+#define sym_ntos __sym_ntos
+#define sym_ntop __sym_ntop
+#define b64_ntop __b64_ntop
+#define b64_pton __b64_pton
+#define loc_ntoa __loc_ntoa
+#define loc_aton __loc_aton
+#define dn_skipname __dn_skipname
+#define fp_resstat __fp_resstat
+#define fp_query __fp_query
+#define fp_nquery __fp_nquery
+#define hostalias __hostalias
+#define putlong __putlong
+#define putshort __putshort
+#define p_class __p_class
+#define p_time __p_time
+#define p_type __p_type
+#define p_query __p_query
+#define p_cdnname __p_cdnname
+#define p_cdname __p_cdname
+#define p_fqnname __p_fqnname
+#define p_fqname __p_fqname
+#define p_rr __p_rr
+#define p_option __p_option
+#define p_secstodate __p_secstodate
+#define dn_count_labels __dn_count_labels
+#define dn_comp __dn_comp
+#define res_randomid __res_randomid
+#define res_isourserver __res_isourserver
+#define res_nameinquery __res_nameinquery
+#define res_queriesmatch __res_queriesmatch
+#define res_close __res_close
+
+#ifdef BIND_RES_POSIX3
+#define dn_expand __dn_expand
+#define res_init __res_init
+#define res_query __res_query
+#define res_search __res_search
+#define res_querydomain __res_querydomain
+#define res_mkquery __res_mkquery
+#define res_send __res_send
+#endif
diff --git a/ircd/s_auth.c b/ircd/s_auth.c
new file mode 100644
index 0000000..89d19ed
--- /dev/null
+++ b/ircd/s_auth.c
@@ -0,0 +1,804 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_auth.c,v 1.43 1999/07/02 16:38:21 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_AUTH_C
+#include "s_externs.h"
+#undef S_AUTH_C
+
+/*
+ * set_clean_username
+ *
+ * As non OTHER type of usernames retrieved via ident lookup are forced as
+ * usernames for the user, one has to be careful not to allow potentially
+ * damaging characters in the username.
+ * This procedure fills up cptr->username based on cptr->auth
+ * Characters disallowed:
+ * leading : for obvious reasons
+ * spaces for obvious reasons
+ * @ because of how get_sockhost() is implemented,
+ * and because it's used from attached_Iline()
+ * [ /trace parsing is impossible
+ */
+static void
+set_clean_username(cptr)
+aClient *cptr;
+{
+ int i = 0, dirty = 0;
+ char *s;
+
+ if (cptr->auth == NULL)
+ return;
+ s = cptr->auth;
+ if (index(cptr->auth, '[') || index(cptr->auth, '@') ||
+ strlen(cptr->auth) > USERLEN)
+ dirty = 1;
+ else if (cptr->auth[0] == ':')
+ {
+ dirty = 1;
+ s += 1;
+ }
+ else
+ {
+ char *r = cptr->auth;
+
+ while (*r)
+ if (isspace(*(r++)))
+ break;
+ if (*r)
+ dirty = 1;
+ }
+ if (dirty)
+ cptr->username[i++] = '-';
+ while (i < USERLEN && *s)
+ {
+ if (*s != '@' && *s != '[' && !isspace(*s))
+ cptr->username[i++] = *s;
+ s += 1;
+ }
+ cptr->username[i] = '\0';
+ if (!strcmp(cptr->username, cptr->auth))
+ {
+ MyFree(cptr->auth);
+ cptr->auth = cptr->username;
+ }
+ else
+ {
+ istat.is_authmem += sizeof(cptr->auth);
+ istat.is_auth += 1;
+ }
+}
+
+#if defined(USE_IAUTH)
+
+u_char iauth_options = 0;
+u_int iauth_spawn = 0;
+char *iauth_version = NULL;
+
+static aExtCf *iauth_conf = NULL;
+static aExtData *iauth_stats = NULL;
+
+/*
+ * sendto_iauth
+ *
+ * Send the buffer to the authentication slave process.
+ * Return 0 if everything went well, -1 otherwise.
+ */
+#if ! USE_STDARG
+int
+sendto_iauth(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
+char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10;
+#else
+int
+vsendto_iauth(char *pattern, va_list va)
+#endif
+{
+ static char abuf[BUFSIZ];
+
+#if ! USE_STDARG
+ sprintf(abuf, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
+#else
+ vsprintf(abuf, pattern, va);
+#endif
+ strcat(abuf, "\n");
+
+ if (adfd < 0)
+ return -1;
+ if (write(adfd, abuf, strlen(abuf)) != strlen(abuf))
+ {
+ sendto_flag(SCH_AUTH, "Aiiie! lost slave authentication process");
+ close(adfd);
+ adfd = -1;
+ start_iauth(0);
+ return -1;
+ }
+ return 0;
+}
+
+# if USE_STDARG
+int
+sendto_iauth(char *pattern, ...)
+{
+ int i;
+
+ va_list va;
+ va_start(va, pattern);
+ i = vsendto_iauth(pattern, va);
+ va_end(va);
+ return i;
+}
+# endif
+
+/*
+ * read_iauth
+ *
+ * read and process data from the authentication slave process.
+ */
+void
+read_iauth()
+{
+ static char obuf[READBUF_SIZE+1], last = '?';
+ static int olen = 0, ia_dbg = 0;
+ char buf[READBUF_SIZE+1], *start, *end, tbuf[BUFSIZ];
+ aClient *cptr;
+ int i;
+
+ if (adfd == -1)
+ {
+ olen = 0;
+ return;
+ }
+ while (1)
+ {
+ if (olen)
+ bcopy(obuf, buf, olen);
+ if ((i = recv(adfd, buf+olen, READBUF_SIZE-olen, 0)) <= 0)
+ {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ {
+ sendto_flag(SCH_AUTH, "Aiiie! lost slave authentication process (errno = %d)", errno);
+ close(adfd);
+ adfd = -1;
+ start_iauth(0);
+ }
+ break;
+ }
+ olen += i;
+ buf[olen] = '\0';
+ start = buf;
+ while (end = index(start, '\n'))
+ {
+ *end++ = '\0';
+ last = *start;
+ if (*start == '>')
+ {
+ sendto_flag(SCH_AUTH, "%s", start+1);
+ start = end;
+ continue;
+ }
+ if (*start == 'G')
+ {
+ ia_dbg = atoi(start+2);
+ if (ia_dbg)
+ sendto_flag(SCH_AUTH,"ia_dbg = %d",ia_dbg);
+ start = end;
+ continue;
+ }
+ if (*start == 'O') /* options */
+ {
+ iauth_options = 0;
+ if (strchr(start+2, 'A'))
+ iauth_options |= XOPT_EARLYPARSE;
+ if (strchr(start+2, 'R'))
+ iauth_options |= XOPT_REQUIRED;
+ if (strchr(start+2, 'T'))
+ iauth_options |= XOPT_NOTIMEOUT;
+ if (strchr(start+2, 'W'))
+ iauth_options |= XOPT_EXTWAIT;
+ if (iauth_options)
+ sendto_flag(SCH_AUTH, "iauth options: %x",
+ iauth_options);
+ start = end;
+ continue;
+ }
+ if (*start == 'V') /* version */
+ {
+ if (iauth_version)
+ MyFree(iauth_version);
+ iauth_version = mystrdup(start+2);
+ sendto_flag(SCH_AUTH, "iauth version %s running.",
+ iauth_version);
+ start = end;
+ continue;
+ }
+ if (*start == 'a')
+ {
+ aExtCf *ectmp;
+
+ while (ectmp = iauth_conf)
+ {
+ iauth_conf = iauth_conf->next;
+ MyFree(ectmp->line);
+ MyFree(ectmp);
+ }
+ /* little lie.. ;) */
+ sendto_flag(SCH_AUTH, "New iauth configuration.");
+ start = end;
+ continue;
+ }
+ if (*start == 'A')
+ {
+ aExtCf **ectmp = &iauth_conf;
+
+ while (*ectmp)
+ ectmp = &((*ectmp)->next);
+ *ectmp = (aExtCf *) MyMalloc(sizeof(aExtCf));
+ (*ectmp)->line = mystrdup(start+2);
+ (*ectmp)->next = NULL;
+ start = end;
+ continue;
+ }
+ if (*start == 's')
+ {
+ aExtData *ectmp;
+
+ while (ectmp = iauth_stats)
+ {
+ iauth_stats = iauth_stats->next;
+ MyFree(ectmp->line);
+ MyFree(ectmp);
+ }
+ iauth_stats = (aExtData *)
+ MyMalloc(sizeof(aExtData));
+ iauth_stats->line = MyMalloc(60);
+ sprintf(iauth_stats->line,
+ "iauth modules statistics (%s)",
+ myctime(timeofday));
+ iauth_stats->next = (aExtData *)
+ MyMalloc(sizeof(aExtData));
+ iauth_stats->next->line = MyMalloc(60);
+ sprintf(iauth_stats->next->line,
+ "spawned: %d, current options: %X (%.11s)",
+ iauth_spawn, iauth_options,
+ (iauth_version) ? iauth_version : "???");
+ iauth_stats->next->next = NULL;
+ start = end;
+ continue;
+ }
+ if (*start == 'S')
+ {
+ aExtData **ectmp = &iauth_stats;
+
+ while (*ectmp)
+ ectmp = &((*ectmp)->next);
+ *ectmp = (aExtData *) MyMalloc(sizeof(aExtData));
+ (*ectmp)->line = mystrdup(start+2);
+ (*ectmp)->next = NULL;
+ start = end;
+ continue;
+ }
+ if (*start != 'U' && *start != 'u' && *start != 'o' &&
+ *start != 'K' && *start != 'k' &&
+ *start != 'D')
+ {
+ sendto_flag(SCH_AUTH, "Garbage from iauth [%s]",
+ start);
+ sendto_iauth("-1 E Garbage [%s]", start);
+ /*
+ ** The above should never happen, but i've seen it
+ ** occasionnally, so let's try to get more info
+ ** about it! -kalt
+ */
+ sendto_flag(SCH_AUTH,
+ "last=%u start=%x end=%x buf=%x olen=%d i=%d",
+ last, start, end, buf, olen, i);
+ sendto_iauth(
+ "-1 E last=%u start=%x end=%x buf=%x olen=%d i=%d",
+ last, start, end, buf, olen, i);
+ start = end;
+ continue;
+ }
+ if ((cptr = local[i = atoi(start+2)]) == NULL)
+ {
+ /* this is fairly common and can be ignored */
+ if (ia_dbg)
+ {
+ sendto_flag(SCH_AUTH, "Client %d is gone.",
+ i);
+ sendto_iauth("%d E Gone [%s]", i, start);
+ }
+ start = end;
+ continue;
+ }
+ sprintf(tbuf, "%c %d %s %u ", start[0], i,
+ inetntoa((char *)&cptr->ip), cptr->port);
+ if (strncmp(tbuf, start, strlen(tbuf)))
+ {
+ /* this is fairly common and can be ignored */
+ if (ia_dbg)
+ {
+ sendto_flag(SCH_AUTH,
+ "Client mismatch: %d [%s] != [%s]",
+ i, start, tbuf);
+ sendto_iauth("%d E Mismatch [%s] != [%s]",
+ i, start, tbuf);
+ }
+ start = end;
+ continue;
+ }
+ if (start[0] == 'U')
+ {
+ if (*(start+strlen(tbuf)) == '\0')
+ {
+ sendto_flag(SCH_AUTH,
+ "Null U message! %d [%s]",
+ i, start);
+ sendto_iauth("%d E Null U [%s]", i, start);
+ start = end;
+ continue;
+ }
+ if (cptr->auth != cptr->username)
+ {
+ istat.is_authmem -= sizeof(cptr->auth);
+ istat.is_auth -= 1;
+ MyFree(cptr->auth);
+ }
+ cptr->auth = mystrdup(start+strlen(tbuf));
+ set_clean_username(cptr);
+ cptr->flags |= FLAGS_GOTID;
+ }
+ else if (start[0] == 'u')
+ {
+ if (*(start+strlen(tbuf)) == '\0')
+ {
+ sendto_flag(SCH_AUTH,
+ "Null u message! %d [%s]",
+ i, start);
+ sendto_iauth("%d E Null u [%s]", i, start);
+ start = end;
+ continue;
+ }
+ if (cptr->auth != cptr->username)
+ {
+ istat.is_authmem -= sizeof(cptr->auth);
+ istat.is_auth -= 1;
+ MyFree(cptr->auth);
+ }
+ cptr->auth = MyMalloc(strlen(start+strlen(tbuf))
+ + 2);
+ *cptr->auth = '-';
+ strcpy(cptr->auth+1, start+strlen(tbuf));
+ set_clean_username(cptr);
+ cptr->flags |= FLAGS_GOTID;
+ }
+ else if (start[0] == 'o')
+ {
+ if (!WaitingXAuth(cptr))
+ {
+ sendto_flag(SCH_AUTH,
+ "Early o message discarded!");
+ sendto_iauth("%d E Early o [%s]", i,start);
+ start = end;
+ continue;
+ }
+ if (cptr->user == NULL)
+ {
+ /* just to be safe */
+ sendto_flag(SCH_AUTH,
+ "Ack! cptr->user is NULL");
+ start = end;
+ continue;
+ }
+ strncpyzt(cptr->user->username, tbuf, USERLEN+1);
+ }
+ else if (start[0] == 'D')
+ {
+ /*authentication finished*/
+ ClearXAuth(cptr);
+ SetDoneXAuth(cptr);
+ if (WaitingXAuth(cptr))
+ {
+ ClearWXAuth(cptr);
+ register_user(cptr, cptr, cptr->name,
+ cptr->user->username);
+ }
+ else
+ ClearWXAuth(cptr);
+ }
+ else
+ {
+ /*
+ ** mark for kill, because it cannot be killed
+ ** yet: we don't even know if this is a server
+ ** or a user connection!
+ */
+ if (start[0] == 'K')
+ cptr->exitc = EXITC_AREF;
+ else
+ cptr->exitc = EXITC_AREFQ;
+ /* should also check to make sure it's still
+ an unregistered client.. */
+ /* should be extended to work after registration */
+ }
+ start = end;
+ }
+ if (start != buf+olen)
+ bcopy(start, obuf, olen = (buf+olen)-start+1);
+ else
+ olen = 0;
+ }
+}
+
+/*
+ * report_iauth_conf
+ *
+ * called from m_stats(), this is the reply to /stats a
+ */
+void
+report_iauth_conf(sptr, to)
+aClient *sptr;
+char *to;
+{
+ aExtCf *ectmp = iauth_conf;
+
+ if (adfd < 0)
+ return;
+ while (ectmp)
+ {
+ sendto_one(sptr, ":%s %d %s :%s",
+ ME, RPL_STATSIAUTH, to, ectmp->line);
+ ectmp = ectmp->next;
+ }
+}
+
+/*
+ * report_iauth_stats
+ *
+ * called from m_stats(), this is part of the reply to /stats t
+ */
+void
+report_iauth_stats(sptr, to)
+aClient *sptr;
+char *to;
+{
+ aExtData *ectmp = iauth_stats;
+
+ while (ectmp)
+ {
+ sendto_one(sptr, ":%s %d %s :%s",
+ ME, RPL_STATSDEBUG, to, ectmp->line);
+ ectmp = ectmp->next;
+ }
+}
+#endif
+
+/*
+ * 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(cptr)
+Reg aClient *cptr;
+{
+#ifndef NO_IDENT
+ struct SOCKADDR_IN us, them;
+
+ SOCK_LEN_TYPE ulen, tlen;
+
+# if defined(USE_IAUTH)
+ if ((iauth_options & XOPT_REQUIRED) && adfd < 0)
+ return;
+# endif
+ Debug((DEBUG_NOTICE,"start_auth(%x) fd %d status %d",
+ cptr, cptr->fd, cptr->status));
+ if ((cptr->authfd = socket(AFINET, SOCK_STREAM, 0)) == -1)
+ {
+# 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))));
+ ircstp->is_abad++;
+ return;
+ }
+ if (cptr->authfd >= (MAXCONNECTIONS - 2))
+ {
+ sendto_flag(SCH_ERROR, "Can't allocate fd for auth on %s",
+ get_client_name(cptr, TRUE));
+ (void)close(cptr->authfd);
+ return;
+ }
+
+ set_non_blocking(cptr->authfd, cptr);
+
+ /* get remote host peer - so that we get right interface -- jrg */
+ tlen = ulen = sizeof(us);
+ if (getpeername(cptr->fd, (struct sockaddr *)&them, &tlen) < 0)
+ {
+ /* we probably don't need this error message -kalt */
+ report_error("getpeername for auth request %s:%s", cptr);
+ close(cptr->authfd);
+ cptr->authfd = -1;
+ return;
+ }
+ them.SIN_FAMILY = AFINET;
+
+ /* We must bind the local end to the interface that they connected
+ to: The local system might have more than one network address,
+ and RFC931 check only sends port numbers: server takes IP addresses
+ from query socket -- jrg */
+ (void)getsockname(cptr->fd, (struct sockaddr *)&us, &ulen);
+ us.SIN_FAMILY = AFINET;
+# if defined(USE_IAUTH)
+ if (adfd >= 0)
+ {
+ char abuf[BUFSIZ];
+# ifdef INET6
+ sprintf(abuf, "%d C %s %u ", cptr->fd,
+ inetntop(AF_INET6, (char *)&them.sin6_addr, mydummy,
+ MYDUMMY_SIZE), ntohs(them.SIN_PORT));
+ sprintf(abuf+strlen(abuf), "%s %u",
+ inetntop(AF_INET6, (char *)&us.sin6_addr, mydummy,
+ MYDUMMY_SIZE), ntohs(us.SIN_PORT));
+# else
+ sprintf(abuf, "%d C %s %u ", cptr->fd,
+ inetntoa((char *)&them.sin_addr),ntohs(them.SIN_PORT));
+ sprintf(abuf+strlen(abuf), "%s %u",
+ inetntoa((char *)&us.sin_addr), ntohs(us.SIN_PORT));
+# endif
+ if (sendto_iauth(abuf) == 0)
+ {
+ close(cptr->authfd);
+ cptr->authfd = -1;
+ cptr->flags |= FLAGS_XAUTH;
+ return;
+ }
+ }
+# endif
+# ifdef INET6
+ Debug((DEBUG_NOTICE,"auth(%x) from %s %x %x",
+ cptr, inet_ntop(AF_INET6, (char *)&us.sin6_addr, mydummy,
+ MYDUMMY_SIZE), us.sin6_addr.s6_addr[14],
+ us.sin6_addr.s6_addr[15]));
+# else
+ Debug((DEBUG_NOTICE,"auth(%x) from %s",
+ cptr, inetntoa((char *)&us.sin_addr)));
+# endif
+ them.SIN_PORT = htons(113);
+ us.SIN_PORT = htons(0); /* bind assigns us a port */
+ if (bind(cptr->authfd, (struct SOCKADDR *)&us, ulen) >= 0)
+ {
+ (void)getsockname(cptr->fd, (struct SOCKADDR *)&us, &ulen);
+# ifdef INET6
+ Debug((DEBUG_NOTICE,"auth(%x) to %s",
+ cptr, inet_ntop(AF_INET6, (char *)&them.sin6_addr,
+ mydummy, MYDUMMY_SIZE)));
+# else
+ Debug((DEBUG_NOTICE,"auth(%x) to %s",
+ cptr, inetntoa((char *)&them.sin_addr)));
+# endif
+ (void)alarm((unsigned)4);
+ if (connect(cptr->authfd, (struct SOCKADDR *)&them,
+ tlen) == -1 && errno != EINPROGRESS)
+ {
+# ifdef INET6
+ Debug((DEBUG_ERROR,
+ "auth(%x) connect failed to %s - %d", cptr,
+ inet_ntop(AF_INET6, (char *)&them.sin6_addr,
+ mydummy, MYDUMMY_SIZE), errno));
+# else
+ Debug((DEBUG_ERROR,
+ "auth(%x) connect failed to %s - %d", cptr,
+ inetntoa((char *)&them.sin_addr), errno));
+# endif
+ ircstp->is_abad++;
+ /*
+ * No error report from this...
+ */
+ (void)alarm((unsigned)0);
+ (void)close(cptr->authfd);
+ cptr->authfd = -1;
+ return;
+ }
+ (void)alarm((unsigned)0);
+ }
+ else
+ {
+ report_error("binding stream socket for auth request %s:%s",
+ cptr);
+# ifdef INET6
+ Debug((DEBUG_ERROR,"auth(%x) bind failed on %s port %d - %d",
+ cptr, inet_ntop(AF_INET6, (char *)&us.sin6_addr,
+ mydummy, MYDUMMY_SIZE),
+ ntohs(us.SIN_PORT), errno));
+# else
+ Debug((DEBUG_ERROR,"auth(%x) bind failed on %s port %d - %d",
+ cptr, inetntoa((char *)&us.sin_addr),
+ ntohs(us.SIN_PORT), errno));
+# endif
+ }
+
+ cptr->flags |= (FLAGS_WRAUTH|FLAGS_AUTH);
+ if (cptr->authfd > highest_fd)
+ highest_fd = cptr->authfd;
+#endif
+ 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(cptr)
+aClient *cptr;
+{
+ struct SOCKADDR_IN us, them;
+
+ char authbuf[32];
+ SOCK_LEN_TYPE ulen, tlen;
+
+ Debug((DEBUG_NOTICE,"write_authports(%x) 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(authbuf, "%u , %u\r\n",
+ (unsigned int)ntohs(them.SIN_PORT),
+ (unsigned int)ntohs(us.SIN_PORT));
+
+#ifdef INET6
+ Debug((DEBUG_SEND, "sending [%s] to auth port %s.113",
+ authbuf, inet_ntop,(AF_INET6, (char *)&them.sin6_addr,
+ mydummy, MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_SEND, "sending [%s] to auth port %s.113",
+ authbuf, inetntoa((char *)&them.sin_addr)));
+#endif
+ if (write(cptr->authfd, authbuf, strlen(authbuf)) != strlen(authbuf))
+ {
+authsenderr:
+ ircstp->is_abad++;
+ (void)close(cptr->authfd);
+ if (cptr->authfd == highest_fd)
+ while (!local[highest_fd])
+ highest_fd--;
+ cptr->authfd = -1;
+ cptr->flags &= ~(FLAGS_AUTH|FLAGS_WRAUTH);
+ return;
+ }
+ 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(cptr)
+Reg aClient *cptr;
+{
+ Reg char *s, *t;
+ Reg int len;
+ char ruser[513], system[8];
+ u_short remp = 0, locp = 0;
+
+ *system = *ruser = '\0';
+ Debug((DEBUG_NOTICE,"read_authports(%x) fd %d authfd %d stat %d",
+ cptr, cptr->fd, cptr->authfd, cptr->status));
+ /*
+ * Nasty. Can't 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';
+ }
+
+ if ((len > 0) && (cptr->count != (sizeof(cptr->buffer) - 1)) &&
+ (sscanf(cptr->buffer, "%hd , %hd : USERID : %*[^:]: %512s",
+ &remp, &locp, ruser) == 3))
+ {
+ s = rindex(cptr->buffer, ':');
+ *s++ = '\0';
+ for (t = (rindex(cptr->buffer, ':') + 1); *t; t++)
+ if (!isspace(*t))
+ break;
+ strncpyzt(system, t, sizeof(system));
+ for (t = ruser; *s && (t < ruser + sizeof(ruser)); s++)
+ if (!isspace(*s) && *s != ':')
+ *t++ = *s;
+ *t = '\0';
+ Debug((DEBUG_INFO,"auth reply ok [%s] [%s]", system, ruser));
+ }
+ else if (len != 0)
+ {
+ if (!index(cptr->buffer, '\n') && !index(cptr->buffer, '\r'))
+ return;
+ Debug((DEBUG_ERROR,"local %d remote %d s %x",
+ locp, remp, ruser));
+ Debug((DEBUG_ERROR,"bad auth reply in [%s]", cptr->buffer));
+ *ruser = '\0';
+ }
+ (void)close(cptr->authfd);
+ if (cptr->authfd == highest_fd)
+ while (!local[highest_fd])
+ highest_fd--;
+ cptr->count = 0;
+ cptr->authfd = -1;
+ ClearAuth(cptr);
+ if (len > 0)
+ Debug((DEBUG_INFO,"ident reply: [%s]", cptr->buffer));
+
+ if (!locp || !remp || !*ruser)
+ {
+ ircstp->is_abad++;
+ return;
+ }
+ ircstp->is_asuc++;
+ if (cptr->auth != cptr->username)/*impossible, but...*/
+ {
+ istat.is_authmem -= sizeof(cptr->auth);
+ istat.is_auth -= 1;
+ MyFree(cptr->auth);
+ }
+ if (!strncmp(system, "OTHER", 5))
+ { /* OTHER type of identifier */
+ cptr->auth = MyMalloc(strlen(ruser) + 2);
+ *cptr->auth = '-';
+ strcpy(cptr->auth+1, ruser);
+ }
+ else
+ cptr->auth = mystrdup(ruser);
+ set_clean_username(cptr);
+ cptr->flags |= FLAGS_GOTID;
+ Debug((DEBUG_INFO, "got username [%s]", ruser));
+ return;
+}
diff --git a/ircd/s_auth_ext.h b/ircd/s_auth_ext.h
new file mode 100644
index 0000000..4a6a3d1
--- /dev/null
+++ b/ircd/s_auth_ext.h
@@ -0,0 +1,52 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_auth_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_auth.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef S_AUTH_C
+# if defined(USE_IAUTH)
+extern u_char iauth_options;
+extern u_int iauth_spawn;
+# endif
+#
+# define EXTERN extern
+#else /* S_AUTH_C */
+# define EXTERN
+#endif /* S_AUTH_C */
+
+#if defined(USE_IAUTH)
+# if ! USE_STDARG
+EXTERN int sendto_iauth();
+# else /* USE_STDARG */
+EXTERN int vsendto_iauth (char *pattern, va_list va);
+EXTERN int sendto_iauth (char *pattern, ...);
+# endif
+EXTERN void read_iauth();
+EXTERN void report_iauth_conf __P((aClient *, char *));
+EXTERN void report_iauth_stats __P((aClient *, char *));
+#endif
+EXTERN void start_auth __P((Reg aClient *cptr));
+EXTERN void send_authports __P((aClient *cptr));
+EXTERN void read_authports __P((Reg aClient *cptr));
+
+#undef EXTERN
diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c
new file mode 100644
index 0000000..f10e459
--- /dev/null
+++ b/ircd/s_bsd.c
@@ -0,0 +1,3243 @@
+/************************************************************************
+ * 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.
+ */
+
+/* -- Jto -- 07 Jul 1990
+ * Added jlp@hamblin.byu.edu's debugtty fix
+ */
+
+/* -- Armin -- Jun 18 1990
+ * Added setdtablesize() for more socket connections
+ * (sequent OS Dynix only) -- maybe select()-call must be changed ...
+ */
+
+/* -- Jto -- 13 May 1990
+ * Added several fixes from msa:
+ * Better error messages
+ * Changes in check_access
+ * Added SO_REUSEADDR fix from zessel@informatik.uni-kl.de
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_bsd.c,v 1.73 1999/07/23 17:15:14 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_BSD_C
+#include "s_externs.h"
+#undef S_BSD_C
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET 0x7f
+#endif
+
+aClient *local[MAXCONNECTIONS];
+FdAry fdas, fdall;
+int highest_fd = 0, readcalls = 0, udpfd = -1, resfd = -1, adfd = -1;
+time_t timeofday;
+static struct SOCKADDR_IN mysk;
+static void polludp();
+
+static struct SOCKADDR *connect_inet __P((aConfItem *, aClient *, int *));
+static int completed_connection __P((aClient *));
+static int check_init __P((aClient *, char *));
+static int check_ping __P((char *, int));
+static void do_dns_async __P(());
+static int set_sock_opts __P((int, aClient *));
+#ifdef UNIXPORT
+static struct SOCKADDR *connect_unix __P((aConfItem *, aClient *, int *));
+static void add_unixconnection __P((aClient *, int));
+static char unixpath[256];
+#endif
+static char readbuf[READBUF_SIZE];
+
+#define CFLAG (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER)
+#define NFLAG CONF_NOCONNECT_SERVER
+
+/*
+ * 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
+
+/*
+** add_local_domain()
+** Add the domain to hostname, if it is missing
+** (as suggested by eps@TOASTER.SFSU.EDU)
+*/
+
+void add_local_domain(hname, size)
+char *hname;
+int size;
+{
+#ifdef RES_INIT
+ /* try to fix up unqualified names */
+ if (!index(hname, '.'))
+ {
+ if (!(ircd_res.options & RES_INIT))
+ {
+ Debug((DEBUG_DNS,"ircd_res_init()"));
+ ircd_res_init();
+ }
+ if (ircd_res.defdname[0])
+ {
+ (void)strncat(hname, ".", size-1);
+ (void)strncat(hname, ircd_res.defdname, size-2);
+ }
+ }
+#endif
+ return;
+}
+
+/*
+** 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(text, cptr)
+char *text;
+aClient *cptr;
+{
+ Reg int errtmp = errno; /* debug may change 'errno' */
+ Reg char *host;
+ int err;
+ SOCK_LEN_TYPE len = sizeof(err);
+ extern char *strerror();
+
+ 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.
+ */
+#ifdef SO_ERROR
+ if (!IsMe(cptr) && cptr->fd >= 0)
+ if (!GETSOCKOPT(cptr->fd, SOL_SOCKET, SO_ERROR, &err, &len))
+ if (err)
+ errtmp = err;
+#endif
+ sendto_flag(SCH_ERROR, text, host, strerror(errtmp));
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, text, host, strerror(errtmp));
+#endif
+ return;
+}
+
+/*
+ * inetport
+ *
+ * Create a socket in the AF_INET domain, bind it to the port given in
+ * 'port' and listen to it. If 'ip' has a value, use it as vif to listen.
+ * Connections are accepted to this socket depending on the IP# mask given
+ * by 'ipmask'. Returns the fd of the socket created or -1 on error.
+ */
+int inetport(cptr, ip, ipmask, port)
+aClient *cptr;
+char *ipmask, *ip;
+int port;
+{
+ static struct SOCKADDR_IN server;
+ int ad[4];
+ SOCK_LEN_TYPE 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 :-)
+ */
+ (void)sscanf(ipmask, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
+ (void)sprintf(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
+
+ (void)sprintf(cptr->sockhost, "%-.42s.%u", ip ? ip : ME,
+ (unsigned int)port);
+ (void)strcpy(cptr->name, ME);
+ DupString(cptr->auth, ipname);
+ /*
+ * At first, open a new socket
+ */
+ if (cptr->fd == -1)
+ cptr->fd = socket(AFINET, SOCK_STREAM, 0);
+ if (cptr->fd < 0)
+ {
+ report_error("opening stream socket %s:%s", cptr);
+ return -1;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_ERROR,
+ "No more connections allowed (%s)", cptr->name);
+ (void)close(cptr->fd);
+ return -1;
+ }
+ (void)set_sock_opts(cptr->fd, cptr);
+ /*
+ * 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 = AFINET;
+#ifdef INET6
+ if (!ip || (!isxdigit(*ip) && *ip != ':'))
+ server.sin6_addr = in6addr_any;
+ else
+ if(!inet_pton(AF_INET6, ip, server.sin6_addr.s6_addr))
+ bcopy(minus_one, server.sin6_addr.s6_addr,
+ IN6ADDRSZ);
+#else
+ if (!ip || !isdigit(*ip))
+ server.sin_addr.s_addr = INADDR_ANY;
+ else
+ server.sin_addr.s_addr = inetaddr(ip);
+#endif
+ server.SIN_PORT = htons(port);
+ /*
+ * Try 10 times to bind the socket with an interval of 20
+ * seconds. Do this so we don't have to keep trying manually
+ * to bind. Why ? Because a port that has closed often lingers
+ * around for a short time.
+ * This used to be the case. Now it no longer is.
+ * Could cause the server to hang for too long - avalon
+ */
+ if (bind(cptr->fd, (SAP)&server, sizeof(server)) == -1)
+ {
+ report_error("binding stream socket %s:%s", cptr);
+ (void)close(cptr->fd);
+ return -1;
+ }
+ }
+
+ if (getsockname(cptr->fd, (struct SOCKADDR *)&server, &len))
+ {
+ report_error("getsockname failed for %s:%s",cptr);
+ (void)close(cptr->fd);
+ return -1;
+ }
+
+ if (cptr == &me) /* KLUDGE to get it work... */
+ {
+ char buf[1024];
+
+ (void)sprintf(buf, rpl_str(RPL_MYPORTIS, "*"),
+ ntohs(server.SIN_PORT));
+ (void)write(0, buf, strlen(buf));
+ }
+
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+#ifdef INET6
+ bcopy(server.sin6_addr.s6_addr, cptr->ip.s6_addr, IN6ADDRSZ);
+#else
+ cptr->ip.s_addr = server.sin_addr.s_addr; /* broken on linux at least*/
+#endif
+ cptr->port = port;
+ (void)listen(cptr->fd, LISTENQUEUE);
+ local[cptr->fd] = cptr;
+
+ return 0;
+}
+
+/*
+ * 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(aconf)
+aConfItem *aconf;
+{
+ aClient *cptr;
+
+ cptr = make_client(NULL);
+ cptr->flags = FLAGS_LISTEN;
+ cptr->acpt = cptr;
+ cptr->from = cptr;
+ cptr->firsttime = time(NULL);
+ SetMe(cptr);
+#ifdef UNIXPORT
+ if (*aconf->host == '/')
+ {
+ strncpyzt(cptr->name, aconf->host, sizeof(cptr->name));
+ if (unixport(cptr, aconf->host, aconf->port))
+ cptr->fd = -2;
+ }
+ else
+#endif
+ if (inetport(cptr, aconf->host, aconf->name, aconf->port))
+ cptr->fd = -2;
+
+ if (cptr->fd >= 0)
+ {
+ cptr->confs = make_link();
+ cptr->confs->next = NULL;
+ cptr->confs->value.aconf = aconf;
+ add_fd(cptr->fd, &fdas);
+ add_fd(cptr->fd, &fdall);
+ set_non_blocking(cptr->fd, cptr);
+ }
+ else
+ free_client(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(cptr, path, port)
+aClient *cptr;
+char *path;
+int port;
+{
+ struct sockaddr_un un;
+
+ if ((cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ report_error("error opening unix domain socket %s:%s", cptr);
+ return -1;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_ERROR,
+ "No more connections allowed (%s)", cptr->name);
+ (void)close(cptr->fd);
+ return -1;
+ }
+
+ un.sun_family = AF_UNIX;
+ (void)mkdir(path, 0755);
+ SPRINTF(unixpath, "%s/%d", path, port);
+ (void)unlink(unixpath);
+ strncpyzt(un.sun_path, unixpath, sizeof(un.sun_path));
+ (void)strcpy(cptr->name, ME);
+ errno = 0;
+ get_sockhost(cptr, unixpath);
+
+ if (bind(cptr->fd, (SAP)&un, strlen(unixpath)+2) == -1)
+ {
+ report_error("error binding unix socket %s:%s", cptr);
+ (void)close(cptr->fd);
+ return -1;
+ }
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+ (void)listen(cptr->fd, LISTENQUEUE);
+ (void)chmod(path, 0755);
+ (void)chmod(unixpath, 0777);
+ cptr->flags |= FLAGS_UNIX;
+ cptr->port = 0;
+ local[cptr->fd] = cptr;
+
+ return 0;
+}
+#endif
+
+/*
+ * 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()
+{
+ Reg aClient *cptr;
+ Reg int i;
+ Reg 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 = local[i]))
+ continue;
+ if (cptr == &me || !IsListening(cptr))
+ continue;
+ aconf = cptr->confs->value.aconf;
+
+ if (IsIllegal(aconf) && aconf->clients == 0)
+ {
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ SPRINTF(unixpath, "%s/%d",
+ aconf->host, aconf->port);
+ (void)unlink(unixpath);
+ }
+#endif
+ close_connection(cptr);
+ }
+ }
+}
+
+void
+start_iauth(rcvdsig)
+int rcvdsig;
+{
+#if defined(USE_IAUTH)
+ static time_t last = 0;
+ static char first = 1;
+ int sp[2], fd;
+
+ if ((bootopt & BOOT_NOIAUTH) != 0)
+ return;
+ if (adfd >= 0)
+ {
+ if (rcvdsig)
+ sendto_flag(SCH_AUTH,
+ "iauth is already running, restart canceled");
+ return;
+ }
+ if ((time(NULL) - last) > 90 || rcvdsig)
+ {
+ sendto_flag(SCH_AUTH, "Starting iauth...");
+ last = time(NULL);
+ read_iauth(); /* to reset olen */
+ iauth_spawn += 1;
+ }
+ else
+ return;
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
+ {
+ sendto_flag(SCH_ERROR, "socketpair() failed!");
+ sendto_flag(SCH_AUTH, "Failed to restart iauth!");
+ }
+ adfd = sp[0];
+ set_non_blocking(sp[0], NULL);
+ set_non_blocking(sp[1], NULL); /* less to worry about in iauth */
+ switch (vfork())
+ {
+ case -1:
+ sendto_flag(SCH_ERROR, "vfork() failed!");
+ sendto_flag(SCH_AUTH, "Failed to restart iauth!");
+ close(sp[0]); close(sp[1]);
+ adfd = -1;
+ return;
+ case 0:
+ for (fd = 0; fd < MAXCONNECTIONS; fd++)
+ if (fd != sp[1])
+ (void)close(fd);
+ if (sp[1] != 0)
+ {
+ (void)dup2(sp[1], 0);
+ close(sp[1]);
+ }
+ if (execl(IAUTH_PATH, IAUTH, NULL) < 0)
+ _exit(-1); /* should really not happen.. */
+ default:
+ close(sp[1]);
+ }
+
+ if (first)
+ first = 0;
+ else
+ {
+ int i;
+ aClient *cptr;
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(cptr = local[i]))
+ continue;
+ if (IsServer(cptr) || IsService(cptr))
+ continue;
+ sendto_iauth("%d O", i);
+ }
+ }
+#endif
+}
+
+/*
+ * init_sys
+ */
+void init_sys()
+{
+ Reg int fd;
+
+#ifdef RLIMIT_FD_MAX
+ struct rlimit limit;
+
+ if (!getrlimit(RLIMIT_FD_MAX, &limit))
+ {
+ if (limit.rlim_max < MAXCONNECTIONS)
+ {
+ (void)fprintf(stderr, "ircd fd table is too big\n");
+ (void)fprintf(stderr, "Hard Limit: %d IRC max: %d\n",
+ (int) limit.rlim_max, MAXCONNECTIONS);
+ (void)fprintf(stderr,
+ "Fix MAXCONNECTIONS and recompile.\n");
+ exit(-1);
+ }
+ limit.rlim_cur = limit.rlim_max; /* make soft limit the max */
+ if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
+ {
+ (void)fprintf(stderr, "error setting max fd's to %d\n",
+ (int) limit.rlim_cur);
+ exit(-1);
+ }
+ }
+#endif
+#if ! USE_POLL
+# ifdef sequent
+# ifndef DYNIXPTX
+ int fd_limit;
+
+ fd_limit = setdtablesize(MAXCONNECTIONS + 1);
+ if (fd_limit < MAXCONNECTIONS)
+ {
+ (void)fprintf(stderr,"ircd fd table too big\n");
+ (void)fprintf(stderr,"Hard Limit: %d IRC max: %d\n",
+ fd_limit, MAXCONNECTIONS);
+ (void)fprintf(stderr,"Fix MAXCONNECTIONS\n");
+ exit(-1);
+ }
+# endif
+# endif
+#endif /* USE_POLL */
+
+#if defined(PCS) || defined(DYNIXPTX) || defined(SVR3)
+ char logbuf[BUFSIZ];
+
+ (void)setvbuf(stderr,logbuf,_IOLBF,sizeof(logbuf));
+#else
+# if defined(HPUX)
+ (void)setvbuf(stderr, NULL, _IOLBF, 0);
+# else
+# if !defined(SVR4)
+ (void)setlinebuf(stderr);
+# endif
+# endif
+#endif
+
+ bzero((char *)&fdas, sizeof(fdas));
+ bzero((char *)&fdall, sizeof(fdall));
+ fdas.highest = fdall.highest = -1;
+
+ for (fd = 3; fd < MAXCONNECTIONS; fd++)
+ {
+ (void)close(fd);
+ local[fd] = NULL;
+ }
+ local[1] = NULL;
+ (void) fclose(stdout);
+ (void)close(1);
+
+ if (bootopt & BOOT_TTY) /* debugging is going to a tty */
+ goto init_dgram;
+ if (!(bootopt & BOOT_DEBUG))
+ (void)close(2);
+
+ if (((bootopt & BOOT_CONSOLE) || isatty(0)) &&
+ !(bootopt & (BOOT_INETD|BOOT_OPER)))
+ {
+#ifndef __CYGWIN32__
+ if (fork())
+ exit(0);
+#endif
+#ifdef TIOCNOTTY
+ if ((fd = open("/dev/tty", O_RDWR)) >= 0)
+ {
+ (void)ioctl(fd, TIOCNOTTY, (char *)NULL);
+ (void)close(fd);
+ }
+#endif
+#if defined(HPUX) || defined(SVR4) || defined(DYNIXPTX) || \
+ defined(_POSIX_SOURCE) || defined(SGI)
+ (void)setsid();
+#else
+ (void)setpgrp(0, (int)getpid());
+#endif
+ (void)close(0); /* fd 0 opened by inetd */
+ local[0] = NULL;
+ }
+init_dgram:
+ resfd = init_resolver(0x1f);
+
+ start_iauth(0);
+}
+
+void write_pidfile()
+{
+ int fd;
+ char buff[20];
+ (void)truncate(IRCDPID_PATH, 0);
+ if ((fd = open(IRCDPID_PATH, O_CREAT|O_WRONLY, 0600))>=0)
+ {
+ bzero(buff, sizeof(buff));
+ (void)sprintf(buff,"%5d\n", (int)getpid());
+ if (write(fd, buff, strlen(buff)) == -1)
+ Debug((DEBUG_NOTICE,"Error writing to pid file %s",
+ IRCDPID_PATH));
+ (void)close(fd);
+ return;
+ }
+# ifdef DEBUGMODE
+ else
+ Debug((DEBUG_NOTICE,"Error opening pid file %s",
+ IRCDPID_PATH));
+# 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(cptr, sockn)
+Reg aClient *cptr;
+Reg char *sockn;
+{
+ struct SOCKADDR_IN sk;
+ SOCK_LEN_TYPE len = sizeof(struct SOCKADDR_IN);
+
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ strncpyzt(sockn, cptr->acpt->sockhost, HOSTLEN+1);
+ get_sockhost(cptr, sockn);
+ return 0;
+ }
+#endif
+
+ /* If descriptor is a tty, special checking... */
+ if (isatty(cptr->fd))
+ {
+ strncpyzt(sockn, me.sockhost, HOSTLEN);
+ bzero((char *)&sk, sizeof(struct SOCKADDR_IN));
+ }
+ else if (getpeername(cptr->fd, (SAP)&sk, &len) == -1)
+ {
+ report_error("connect failure: %s %s", cptr);
+ return -1;
+ }
+#ifdef INET6
+ inetntop(AF_INET6, (char *)&sk.sin6_addr, sockn, MYDUMMY_SIZE);
+ Debug((DEBUG_DNS,"sockn %x",sockn));
+ Debug((DEBUG_DNS,"sockn %s",sockn));
+#else
+ (void)strcpy(sockn, (char *)inetntoa((char *)&sk.sin_addr));
+#endif
+#ifdef INET6
+ if (IN6_IS_ADDR_LOOPBACK(&sk.SIN_ADDR))
+#else
+ if (inetnetof(sk.SIN_ADDR) == IN_LOOPBACKNET)
+#endif
+ {
+ cptr->hostp = me.hostp;
+ }
+ bcopy((char *)&sk.SIN_ADDR, (char *)&cptr->ip, sizeof(struct IN_ADDR));
+ cptr->port = (int)(ntohs(sk.SIN_PORT));
+
+ return 0;
+}
+
+/*
+ * Ordinary client access check. Look for conf lines which have the same
+ * status as the flags passed.
+ * 0 = Success
+ * -1 = Bad socket.
+ * -2 = Access denied
+ */
+int check_client(cptr)
+Reg aClient *cptr;
+{
+ static char sockname[HOSTLEN+1];
+ Reg struct hostent *hp = NULL;
+ Reg int i;
+
+#ifdef INET6
+ Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+ cptr->name, inet_ntop(AF_INET6, (char *)&cptr->ip, mydummy,
+ MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+ cptr->name, inetntoa((char *)&cptr->ip)));
+#endif
+
+ if (check_init(cptr, sockname))
+ return -1;
+
+ 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.
+ * We shouldn't check it for localhost, because hp is fake in that
+ * case. -Toor
+ */
+ if (hp && (hp != me.hostp))
+ {
+ for (i = 0; hp->h_addr_list[i]; i++)
+ if (!bcmp(hp->h_addr_list[i], (char *)&cptr->ip,
+ sizeof(struct IN_ADDR)))
+ break;
+ if (!hp->h_addr_list[i])
+ {
+#ifdef INET6
+ sendto_flag(SCH_ERROR,
+ "IP# Mismatch: %s != %s[%08x%08x%08x%08x]",
+ inetntop(AF_INET6, (char *)&cptr->ip,
+ mydummy,MYDUMMY_SIZE),hp->h_name,
+ ((unsigned long *)hp->h_addr)[0],
+ ((unsigned long *)hp->h_addr)[1],
+ ((unsigned long *)hp->h_addr)[2],
+ ((unsigned long *)hp->h_addr)[3]);
+#else
+ sendto_flag(SCH_ERROR, "IP# Mismatch: %s != %s[%08x]",
+ inetntoa((char *)&cptr->ip), hp->h_name,
+ *((unsigned long *)hp->h_addr));
+#endif
+ hp = NULL;
+ }
+ }
+
+ if ((i = attach_Iline(cptr, hp, sockname)))
+ {
+ Debug((DEBUG_DNS,"ch_cl: access denied: %s[%s]",
+ cptr->name, sockname));
+ return i;
+ }
+
+ Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]",
+ cptr->name, sockname));
+
+#ifdef INET6
+ if (IN6_IS_ADDR_LOOPBACK(&cptr->ip) || IsUnixSocket(cptr) ||
+ (cptr->ip.s6_laddr[0]==mysk.sin6_addr.s6_laddr[0] &&
+ cptr->ip.s6_laddr[1]==mysk.sin6_addr.s6_laddr[1])
+/* ||
+ IN6_ARE_ADDR_SAMEPREFIX(&cptr->ip, &mysk.SIN_ADDR))
+ about the same, I think NOT */
+ )
+#else
+ if (inetnetof(cptr->ip) == IN_LOOPBACKNET || IsUnixSocket(cptr) ||
+ inetnetof(cptr->ip) == inetnetof(mysk.SIN_ADDR))
+#endif
+ {
+
+ ircstp->is_loc++;
+ cptr->flags |= FLAGS_LOCAL;
+ }
+ return 0;
+}
+
+/*
+ * check_server_init(), 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.
+ * 0 = Success
+ * -1 = Access denied
+ * -2 = Bad socket.
+ */
+int check_server_init(cptr)
+aClient *cptr;
+{
+ Reg char *name;
+ Reg aConfItem *c_conf = NULL, *n_conf = NULL;
+ struct hostent *hp = NULL;
+ Link *lp;
+
+ 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_flag(SCH_ERROR, "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)
+ {
+ Reg aConfItem *aconf;
+
+ aconf = count_cnlines(lp);
+ if (aconf)
+ {
+ Reg 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 can't interrupt that very
+ ** well.
+ */
+ lin.value.aconf = aconf;
+ lin.flags = ASYNC_CONF;
+ nextdnscheck = 1;
+ if ((s = index(aconf->host, '@')))
+ s++;
+ else
+ s = aconf->host;
+ Debug((DEBUG_DNS,"sv_ci:cache lookup (%s)",s));
+ hp = gethost_byname(s, &lin);
+ }
+ }
+ return check_server(cptr, hp, c_conf, n_conf, 0);
+}
+
+int check_server(cptr, hp, c_conf, n_conf, estab)
+aClient *cptr;
+Reg aConfItem *n_conf, *c_conf;
+Reg struct hostent *hp;
+int estab;
+{
+ Reg char *name;
+ char abuff[HOSTLEN+USERLEN+2];
+ char sockname[HOSTLEN+1], fullname[HOSTLEN+1];
+ Link *lp = cptr->confs;
+ int i;
+
+ if (check_init(cptr, sockname))
+ return -2;
+
+check_serverback:
+ if (hp)
+ {
+ for (i = 0; hp->h_addr_list[i]; i++)
+ if (!bcmp(hp->h_addr_list[i], (char *)&cptr->ip,
+ sizeof(struct IN_ADDR)))
+ break;
+ if (!hp->h_addr_list[i])
+ {
+#ifdef INET6
+ sendto_flag(SCH_ERROR,
+ "IP# Mismatch: %s != %s[%08x%08x%08x%08x]",
+ inetntop(AF_INET6, (char *)&cptr->ip,
+ mydummy,MYDUMMY_SIZE),hp->h_name,
+ ((unsigned long *)hp->h_addr)[0],
+ ((unsigned long *)hp->h_addr)[1],
+ ((unsigned long *)hp->h_addr)[2],
+ ((unsigned long *)hp->h_addr)[3]);
+#else
+ sendto_flag(SCH_ERROR, "IP# Mismatch: %s != %s[%08x]",
+ inetntoa((char *)&cptr->ip), hp->h_name,
+ *((unsigned long *)hp->h_addr));
+#endif
+ 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++])
+ {
+ strncpyzt(fullname, name, sizeof(fullname));
+ add_local_domain(fullname, HOSTLEN-strlen(fullname));
+ Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s",
+ sockname, fullname));
+ SPRINTF(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(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 %x n %x",
+ name, cptr->auth, cptr->sockhost,
+ c_conf, n_conf));
+ return -1;
+ }
+ /*
+ * attach the C and N lines to the client structure for later use.
+ */
+ (void)attach_conf(cptr, n_conf);
+ (void)attach_conf(cptr, c_conf);
+ (void)attach_confs(cptr, name, CONF_HUB|CONF_LEAF);
+
+#ifdef INET6
+ if ((AND16(c_conf->ipnum.s6_addr) == 255) && !IsUnixSocket(cptr))
+#else
+ if ((c_conf->ipnum.s_addr == -1) && !IsUnixSocket(cptr))
+#endif
+ bcopy((char *)&cptr->ip, (char *)&c_conf->ipnum,
+ 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));
+ if (estab)
+ return m_server_estab(cptr);
+ return 0;
+}
+
+/*
+** 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(cptr)
+aClient *cptr;
+{
+ aConfItem *aconf;
+
+ SetHandshake(cptr);
+
+ aconf = find_conf(cptr->confs, cptr->name, CFLAG);
+ if (!aconf)
+ {
+ sendto_flag(SCH_NOTICE,
+ "Lost C-Line for %s", get_client_name(cptr,FALSE));
+ return -1;
+ }
+ if (!BadPtr(aconf->passwd))
+#ifndef ZIP_LINKS
+ sendto_one(cptr, "PASS %s %s IRC|%s %s", aconf->passwd,
+ pass_version, serveropts,
+ (bootopt & BOOT_STRICTPROT) ? "P" : "");
+#else
+ sendto_one(cptr, "PASS %s %s IRC|%s %s%s", aconf->passwd,
+ pass_version, serveropts,
+ (bootopt & BOOT_STRICTPROT) ? "P" : "",
+ (aconf->status == CONF_ZCONNECT_SERVER) ? "Z" : "");
+#endif
+
+ aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
+ if (!aconf)
+ {
+ sendto_flag(SCH_NOTICE,
+ "Lost N-Line for %s", get_client_name(cptr,FALSE));
+ return -1;
+ }
+ sendto_one(cptr, "SERVER %s 1 :%s",
+ my_name_for_link(ME, aconf->port), me.info);
+ if (!IsDead(cptr))
+ {
+ start_auth(cptr);
+#if defined(USE_IAUTH)
+ /*
+ ** This could become a bug.. but I don't think iauth needs the
+ ** hostname/aliases in this case. -kalt
+ */
+ sendto_iauth("%d d", cptr->fd);
+#endif
+ }
+
+ return (IsDead(cptr)) ? -1 : 0;
+}
+
+int hold_server(cptr)
+aClient *cptr;
+{
+ return -1; /* needs to be fixed, don't forget virtual hosts */
+
+#if 0 /* code and variables declarations are removed, this
+ avoids compiler warnings */
+
+ struct SOCKADDR_IN sin;
+ aConfItem *aconf;
+ aClient *acptr;
+ int fd;
+
+#ifdef ZIP_LINKS
+ /*
+ * reconnecting will not work with compressed links,
+ * unless someones fixes reconnect and implements what's needed
+ * to have it work for compressed links. -krys
+ */
+ return -1;
+#else
+ if (!IsServer(cptr) ||
+ !(aconf = find_conf_name(cptr->name, CFLAG)))
+ return -1;
+
+ if (!aconf->port)
+ return -1;
+
+ fd = socket(AFINET, SOCK_STREAM, 0);
+
+ if (fd >= MAXCLIENTS)
+ {
+ (void)close(fd);
+ sendto_flag(SCH_ERROR,
+ "Can't reconnect - all connections in use");
+ return -1;
+ }
+
+ cptr->flags |= FLAGS_HELD;
+ (void)close(cptr->fd);
+ del_fd(cptr->fd, &fdall);
+ del_fd(cptr->fd, &fdas);
+ cptr->fd = -2;
+
+ acptr = make_client(NULL);
+ acptr->fd = fd;
+ acptr->port = aconf->port;
+ set_non_blocking(acptr->fd, acptr);
+ (void)set_sock_opts(acptr->fd, acptr);
+ bzero((char *)&sin, sizeof(sin));
+ sin.SIN_FAMILY = AFINET;
+ sin.SIN_PORT = htons(aconf->port);
+ bcopy((char *)&cptr->ip, (char *)&sin.SIN_ADDR, sizeof(cptr->ip));
+ bcopy((char *)&cptr->ip, (char *)&acptr->ip, sizeof(cptr->ip));
+
+ if (connect(acptr->fd, (SAP)&sin, sizeof(sin)) < 0 &&
+ errno != EINPROGRESS)
+ {
+ report_error("Connect to host %s failed: %s", acptr); /*buggy*/
+ (void)close(acptr->fd);
+ MyFree((char *)acptr);
+ return -1;
+ }
+
+ acptr->status = STAT_RECONNECT;
+ if (acptr->fd > highest_fd)
+ highest_fd = acptr->fd;
+ add_fd(acptr->fd, &fdall);
+ local[acptr->fd] = acptr;
+ acptr->acpt = &me;
+ add_client_to_list(acptr);
+ (void)strcpy(acptr->name, cptr->name);
+ /* broken syntax
+ sendto_one(acptr, "PASS %s %s", aconf->passwd, pass_version);
+ */
+ sendto_one(acptr, "RECONNECT %s %d", acptr->name, cptr->sendM);
+ sendto_flag(SCH_NOTICE, "Reconnecting to %s", acptr->name);
+ Debug((DEBUG_NOTICE, "Reconnect %s %#x via %#x %d", cptr->name, cptr,
+ acptr, acptr->fd));
+ return 0;
+#endif
+#endif
+}
+
+/*
+** close_connection
+** Close the physical connection. This function must make
+** MyConnect(cptr) == FALSE, and set cptr->from == NULL.
+*/
+void close_connection(cptr)
+aClient *cptr;
+{
+ Reg aConfItem *aconf;
+ Reg int i,j;
+#ifdef SO_LINGER
+ struct linger sockling;
+
+ sockling.l_onoff = 0;
+#endif
+
+ 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 += timeofday - 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 (IsClient(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 += timeofday - 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, CFLAG)))
+ {
+ /*
+ * 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 = timeofday;
+ aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ?
+ HANGONRETRYDELAY : ConfConFreq(aconf);
+ if (nextconnect > aconf->hold)
+ nextconnect = aconf->hold;
+ }
+
+ if (cptr->authfd >= 0)
+ {
+#ifdef SO_LINGER
+ if (cptr->exitc == EXITC_PING)
+ if (SETSOCKOPT(cptr->authfd, SOL_SOCKET, SO_LINGER,
+ &sockling, sockling))
+ report_error("setsockopt(SO_LINGER) %s:%s",
+ cptr);
+#endif
+ (void)close(cptr->authfd);
+ }
+
+ if ((i = cptr->fd) >= 0)
+ {
+#if defined(USE_IAUTH)
+ sendto_iauth("%d D", cptr->fd);
+#endif
+ flush_connections(i);
+ if (IsServer(cptr) || IsListening(cptr))
+ {
+ del_fd(i, &fdas);
+#ifdef ZIP_LINKS
+ /*
+ ** the connection might have zip data (even if
+ ** FLAGS_ZIP is not set)
+ */
+ zip_free(cptr);
+#endif
+ }
+ else if (IsClient(cptr))
+ {
+#ifdef SO_LINGER
+ if (cptr->exitc == EXITC_PING)
+ if (SETSOCKOPT(i, SOL_SOCKET, SO_LINGER,
+ &sockling, sockling))
+ report_error("setsockopt(SO_LINGER) %s:%s",
+ cptr);
+#endif
+ }
+ del_fd(i, &fdall);
+ local[i] = NULL;
+ (void)close(i);
+
+ /*
+ * fd remap to keep local[i] filled at the bottom.
+ * don't *ever* move descriptors for
+ * + log file
+ * + sockets bound to listen() ports
+ * --Yegg
+ */
+ if (i >= 0 && (j = highest_fd) > i)
+ {
+ while (!local[j])
+ j--;
+ if (j > i && local[j] &&
+ !(IsLog(local[j]) || IsMe(local[j])))
+ {
+ if (dup2(j,i) == -1)
+ return;
+ local[i] = local[j];
+ local[i]->fd = i;
+ local[j] = NULL;
+ (void)close(j);
+ del_fd(j, &fdall);
+ add_fd(i, &fdall);
+ if (IsServer(local[i]) || IsMe(local[i]))
+ {
+ del_fd(j, &fdas);
+ add_fd(i, &fdas);
+ }
+ while (!local[highest_fd])
+ highest_fd--;
+#if defined(USE_IAUTH)
+ sendto_iauth("%d R %d", j, i);
+#endif
+ }
+ }
+ cptr->fd = -2;
+ DBufClear(&cptr->sendQ);
+ DBufClear(&cptr->recvQ);
+ bzero(cptr->passwd, sizeof(cptr->passwd));
+ /*
+ * clean up extra sockets from P-lines which have been
+ * discarded.
+ */
+ if (cptr->acpt != &me)
+ {
+ aconf = cptr->acpt->confs->value.aconf;
+ if (aconf->clients > 0)
+ aconf->clients--;
+ if (!aconf->clients && IsIllegal(aconf))
+ close_connection(cptr->acpt);
+ }
+ }
+
+ det_confs_butmask(cptr, 0);
+ cptr->from = NULL; /* ...this should catch them! >:) --msa */
+ return;
+}
+
+/*
+** set_sock_opts
+*/
+static int set_sock_opts(fd, cptr)
+int fd;
+aClient *cptr;
+{
+ int opt, ret = 0;
+#ifdef SO_REUSEADDR
+ opt = 1;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_REUSEADDR, &opt, opt) < 0)
+ report_error("setsockopt(SO_REUSEADDR) %s:%s", cptr);
+#endif
+#if defined(SO_DEBUG) && defined(DEBUGMODE) && 0
+/* Solaris 2.x with SO_DEBUG writes to syslog by default */
+#if ! SOLARIS_2 || defined(USE_SYSLOG)
+ opt = 1;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_DEBUG, &opt, opt) < 0)
+ report_error("setsockopt(SO_DEBUG) %s:%s", cptr);
+#endif /* SOLARIS_2 */
+#endif
+#ifdef SO_USELOOPBACK
+ opt = 1;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, opt) < 0)
+ report_error("setsockopt(SO_USELOOPBACK) %s:%s", cptr);
+#endif
+#ifdef SO_RCVBUF
+ opt = 8192;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_RCVBUF, &opt, 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).
+ */
+# endif
+ opt = 8192;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_SNDBUF, &opt, opt) < 0)
+ report_error("setsockopt(SO_SNDBUF) %s:%s", cptr);
+# ifdef SO_SNDLOWAT
+ /*
+ * Setting the low water mark should improve performence by avoiding
+ * early returns from select()/poll(). It shouldn't delay sending
+ * data, provided that io_loop() combines read_message() and
+ * flush_fdary/connections() calls properly. -kalt
+ * This call isn't always implemented, even when defined.. so be quiet
+ * about errors. -kalt
+ */
+ opt = 8192;
+ SETSOCKOPT(fd, SOL_SOCKET, SO_SNDLOWAT, &opt, opt);
+# endif
+#endif
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP) && !defined(AIX) && \
+ !defined(SUN_GSO_BUG) && !defined(INET6)
+ /*
+ * Mainly to turn off and alert us to source routing, here.
+ * Method borrowed from Wietse Venema's TCP wrapper.
+ */
+ {
+ if (!IsUnixSocket(cptr) && !IsListening(cptr))
+ {
+ u_char opbuf[256], *t = opbuf;
+ char *s = readbuf;
+
+ opt = sizeof(opbuf);
+ if (GETSOCKOPT(fd, IPPROTO_IP, IP_OPTIONS, t, &opt) == -1)
+ report_error("getsockopt(IP_OPTIONS) %s:%s", cptr);
+ else if (opt > 0)
+ {
+ for (; opt > 0; opt--, s+= 3)
+ (void)sprintf(s, " %02x", *t++);
+ *s = '\0';
+ sendto_flag(SCH_NOTICE,
+ "Connection %s with IP opts%s",
+ get_client_name(cptr, TRUE), readbuf);
+ Debug((DEBUG_NOTICE,
+ "Connection %s with IP opts%s",
+ get_client_name(cptr, TRUE), readbuf));
+ ret = -1;
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+int get_sockerr(cptr)
+aClient *cptr;
+{
+ int errtmp = errno, err = 0;
+ SOCK_LEN_TYPE len = sizeof(err);
+
+#ifdef SO_ERROR
+ if (cptr->fd >= 0)
+ if (!GETSOCKOPT(cptr->fd, SOL_SOCKET, SO_ERROR, &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(fd, cptr)
+int fd;
+aClient *cptr;
+{
+ int res, nonb = 0;
+
+ /*
+ ** 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.
+ */
+#if NBLOCK_POSIX
+ nonb |= O_NONBLOCK;
+#endif
+#if NBLOCK_BSD
+ nonb |= O_NDELAY;
+#endif
+#if 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;
+}
+
+#ifdef CLONE_CHECK
+/*
+ * check_clones
+ * adapted by jecete 4 IRC Ptnet
+ */
+static int check_clones(cptr)
+aClient *cptr;
+{
+ struct abacklog {
+ struct IN_ADDR ip;
+ time_t PT;
+ struct abacklog *next;
+ };
+ static struct abacklog *backlog = NULL;
+ register struct abacklog **blscn = &backlog,
+ *blptr;
+ register int count = 0;
+
+ /* First, ditch old entries */
+ while (*blscn != NULL)
+ {
+ if ((*blscn)->PT+CLONE_PERIOD < timeofday)
+ {
+ blptr= *blscn;
+ *blscn=blptr->next;
+ MyFree((char *)blptr);
+ }
+ else
+ blscn = &(*blscn)->next;
+ }
+ /* Now add new item to the list */
+ blptr = (struct abacklog *) MyMalloc(sizeof(struct abacklog));
+#ifdef INET6
+ bcopy(cptr->ip.s6_addr, blptr->ip.s6_addr, IN6ADDRSZ);
+#else
+ blptr->ip.s_addr = cptr->ip.s_addr;
+#endif
+ blptr->PT = timeofday;
+ blptr->next = backlog;
+ backlog = blptr;
+
+ /* Count the number of entries from the same host */
+ blptr = backlog;
+ while (blptr != NULL)
+ {
+#ifdef INET6
+ if (bcmp(blptr->ip.s6_addr, cptr->ip.s6_addr, IN6ADDRSZ) == 0)
+#else
+ if (blptr->ip.s_addr == cptr->ip.s_addr)
+#endif
+ count++;
+ blptr = blptr->next;
+ }
+ return (count);
+}
+#endif
+
+/*
+ * 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 doesnt have a name.
+ */
+aClient *add_connection(cptr, fd)
+aClient *cptr;
+int fd;
+{
+ Link lin;
+ aClient *acptr;
+ aConfItem *aconf = NULL;
+ acptr = make_client(NULL);
+
+ 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 (isatty(fd)) /* If descriptor is a tty, special checking... */
+ get_sockhost(acptr, cptr->sockhost);
+ else
+ {
+ struct SOCKADDR_IN addr;
+ SOCK_LEN_TYPE len = sizeof(struct SOCKADDR_IN);
+
+ if (getpeername(fd, (SAP)&addr, &len) == -1)
+ {
+#if defined(linux)
+ if (errno != ENOTCONN)
+#endif
+ report_error("Failed in connecting to %s :%s",
+ cptr);
+add_con_refuse:
+ ircstp->is_ref++;
+ acptr->fd = -2;
+ free_client(acptr);
+ (void)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...
+ */
+#ifdef INET6
+ inetntop(AF_INET6, (char *)&addr.sin6_addr, mydummy,
+ MYDUMMY_SIZE);
+ get_sockhost(acptr, (char *)mydummy);
+#else
+ get_sockhost(acptr, (char *)inetntoa((char *)&addr.sin_addr));
+#endif
+ bcopy ((char *)&addr.SIN_ADDR, (char *)&acptr->ip,
+ sizeof(struct IN_ADDR));
+ acptr->port = ntohs(addr.SIN_PORT);
+
+ lin.flags = ASYNC_CLIENT;
+ lin.value.cptr = acptr;
+#ifdef INET6
+ Debug((DEBUG_DNS, "lookup %s",
+ inet_ntop(AF_INET6, (char *)&addr.sin6_addr,
+ mydummy, MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_DNS, "lookup %s",
+ inetntoa((char *)&addr.sin_addr)));
+#endif
+ acptr->hostp = gethost_byaddr((char *)&acptr->ip, &lin);
+ if (!acptr->hostp)
+ SetDNS(acptr);
+ nextdnscheck = 1;
+ }
+
+#ifdef CLONE_CHECK
+ if (check_clones(acptr) > CLONE_MAX)
+ {
+ sendto_flag(SCH_LOCAL, "Rejecting connection from %s[%s].",
+ (acptr->hostp) ? acptr->hostp->h_name : "",
+ acptr->sockhost);
+ sendto_flog(acptr, " ?Clone? ", 0, "<none>",
+ (acptr->hostp) ? acptr->hostp->h_name :
+ acptr->sockhost);
+ del_queries((char *)acptr);
+# ifdef INET6
+ (void)sendto(acptr->fd,
+ "ERROR :Too rapid connections from your host\r\n",
+ 46, 0, 0, 0);
+# else
+ (void)send(acptr->fd,
+ "ERROR :Too rapid connections from your host\r\n",
+ 46, 0);
+# endif
+ goto add_con_refuse;
+ }
+#endif
+ acptr->fd = fd;
+ set_non_blocking(acptr->fd, acptr);
+ if (set_sock_opts(acptr->fd, acptr) == -1)
+ goto add_con_refuse;
+ if (aconf)
+ aconf->clients++;
+ if (fd > highest_fd)
+ highest_fd = fd;
+ local[fd] = acptr;
+ add_fd(fd, &fdall);
+ acptr->acpt = cptr;
+ add_client_to_list(acptr);
+ start_auth(acptr);
+#if defined(USE_IAUTH)
+ if (!isatty(fd) && !DoingDNS(acptr))
+ {
+ int i = 0;
+
+ while (acptr->hostp->h_aliases[i])
+ sendto_iauth("%d A %s", acptr->fd,
+ acptr->hostp->h_aliases[i++]);
+ if (acptr->hostp->h_name)
+ sendto_iauth("%d N %s",acptr->fd,acptr->hostp->h_name);
+ else if (acptr->hostp->h_aliases[0])
+ sendto_iauth("%d n", acptr->fd);
+ }
+#endif
+ return acptr;
+}
+
+#ifdef UNIXPORT
+static void add_unixconnection(cptr, fd)
+aClient *cptr;
+int fd;
+{
+ aClient *acptr;
+ aConfItem *aconf = NULL;
+
+ acptr = make_client(NULL);
+
+ /* Copy ascii address to 'sockhost' just in case. Then we
+ * have something valid to put into error messages...
+ */
+ get_sockhost(acptr, me.sockhost);
+ aconf = cptr->confs->value.aconf;
+ if (aconf)
+ {
+ if (IsIllegal(aconf))
+ {
+ ircstp->is_ref++;
+ acptr->fd = -2;
+ free_client(acptr);
+ (void)close(fd);
+ return;
+ }
+ else
+ aconf->clients++;
+ }
+ acptr->fd = fd;
+ if (fd > highest_fd)
+ highest_fd = fd;
+ local[fd] = acptr;
+ add_fd(fd, &fdall);
+ acptr->acpt = cptr;
+ SetUnixSock(acptr);
+ bcopy((char *)&me.ip, (char *)&acptr->ip, sizeof(struct IN_ADDR));
+
+ add_client_to_list(acptr);
+ set_non_blocking(acptr->fd, acptr);
+ (void)set_sock_opts(acptr->fd, acptr);
+ return;
+}
+#endif
+
+/*
+** read_listener
+**
+** Accept incoming connections, extracted from read_message() 98/12 -kalt
+** Up to 10 connections will be accepted, unless SLOW_ACCEPT is defined.
+*/
+static void
+read_listener(cptr)
+aClient *cptr;
+{
+ int fdnew, max = 10;
+
+#if defined(SLOW_ACCEPT)
+ max = 1;
+#endif
+ while (max--)
+ {
+ /*
+ ** 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 ((fdnew = accept(cptr->fd, NULL, NULL)) < 0)
+ {
+ if (errno != EWOULDBLOCK)
+ report_error("Cannot accept connections %s:%s",
+ cptr);
+ break;
+ }
+ ircstp->is_ac++;
+ if (fdnew >= MAXCLIENTS)
+ {
+ ircstp->is_ref++;
+ sendto_flag(SCH_ERROR, "All connections in use. (%s)",
+ get_client_name(cptr, TRUE));
+ find_bounce(NULL, 0, fdnew);
+#ifdef INET6
+ (void)sendto(fdnew,
+ "ERROR :All connections in use\r\n",
+ 32, 0, 0, 0);
+#else
+ (void)send(fdnew, "ERROR :All connections in use\r\n",
+ 32, 0);
+#endif
+ (void)close(fdnew);
+ continue;
+ }
+ /*
+ * Use of add_connection (which never fails :) meLazy
+ * Never say never. MrMurphy visited here. -Vesa
+ */
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ add_unixconnection(cptr, fdnew);
+ else
+#endif
+ if (!add_connection(cptr, fdnew))
+ continue;
+ nextping = timeofday; /* isn't this abusive? -kalt */
+ istat.is_unknown++;
+ }
+}
+
+/*
+** client_packet
+**
+** Process data from receive buffer to client.
+** Extracted from read_packet() 960804/291p3/Vesa
+*/
+static int client_packet(cptr)
+Reg aClient *cptr;
+{
+ Reg int dolen = 0;
+
+ while (DBufLength(&cptr->recvQ) && !NoNewLine(cptr) &&
+ ((cptr->status < STAT_UNKNOWN) ||
+ (cptr->since - timeofday < MAXPENALTY)))
+ {
+ /*
+ ** If it has become registered as a Service or Server
+ ** then skip the per-message parsing below.
+ */
+ if (IsService(cptr) || IsServer(cptr))
+ {
+ dolen = dbuf_get(&cptr->recvQ, readbuf,
+ sizeof(readbuf));
+ if (dolen <= 0)
+ break;
+ dolen = dopacket(cptr, readbuf, dolen);
+ if (dolen == 2 && cptr->since == cptr->lasttime)
+ cptr->since += 5;
+ if (dolen)
+ return dolen;
+ break;
+ }
+ dolen = dbuf_getmsg(&cptr->recvQ, readbuf,
+ sizeof(readbuf));
+ /*
+ ** 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
+ */
+ while (dolen <= 0)
+ {
+ if (dolen < 0)
+ return exit_client(cptr, cptr, &me,
+ "dbuf_getmsg fail");
+ if (DBufLength(&cptr->recvQ) < 510)
+ { /* hmm? */
+ cptr->flags |= FLAGS_NONL;
+ break;
+ }
+ dolen = dbuf_get(&cptr->recvQ, readbuf, 511);
+ if (dolen > 0 && DBufLength(&cptr->recvQ))
+ DBufClear(&cptr->recvQ);
+ }
+
+ /* Is it okay not to test for other return values? -krys */
+ if (dolen > 0 &&
+ (dopacket(cptr, readbuf, dolen) == FLUSH_BUFFER))
+ return FLUSH_BUFFER;
+ }
+ return 1;
+}
+
+/*
+** 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(cptr, msg_ready)
+Reg aClient *cptr;
+int msg_ready;
+{
+ Reg int length = 0, done;
+
+ if (msg_ready &&
+ !(IsPerson(cptr) && DBufLength(&cptr->recvQ) > 6090))
+ {
+ errno = 0;
+#ifdef INET6
+ length = recvfrom(cptr->fd, readbuf, sizeof(readbuf), 0, 0, 0);
+#else
+ length = recv(cptr->fd, readbuf, sizeof(readbuf), 0);
+#endif
+#if defined(DEBUGMODE) && defined(DEBUG_READ)
+ if (length > 0)
+ Debug((DEBUG_READ,
+ "recv = %d bytes from %d[%s]:[%*.*s]\n",
+ length, cptr->fd, cptr->name, length, length,
+ readbuf));
+#endif
+
+ Debug((DEBUG_DEBUG, "Received %d(%d-%s) bytes from %d %s",
+ length, errno, strerror(errno),
+ cptr->fd, get_client_name(cptr, TRUE)));
+ cptr->lasttime = timeofday;
+ 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;
+ }
+ else if (msg_ready)
+ return exit_client(cptr, cptr, &me, "EOF From Client");
+
+ /*
+ ** 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) ||
+ IsService(cptr))
+ {
+ if (length > 0)
+ {
+ done = dopacket(cptr, readbuf, length);
+ if (done && done != 2)
+ 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 (length && dbuf_put(&cptr->recvQ, readbuf, length) < 0)
+ return exit_client(cptr, cptr, &me, "dbuf_put fail");
+
+ if (IsPerson(cptr) &&
+ DBufLength(&cptr->recvQ) > CLIENT_FLOOD)
+ {
+ cptr->exitc = EXITC_FLOOD;
+ return exit_client(cptr, cptr, &me, "Excess Flood");
+ }
+
+ return client_packet(cptr);
+ }
+ 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.
+ */
+int read_message(delay, fdp, ro)
+time_t delay; /* Don't ever use ZERO here, unless you mean to poll and then
+ * you have to have sleep/wait somewhere else in the code.--msa
+ * Actually, ZERO is NOT ZERO anymore.. see below -kalt
+ */
+FdAry *fdp;
+int ro;
+{
+#if ! USE_POLL
+# define SET_READ_EVENT( thisfd ) FD_SET( thisfd, &read_set)
+# define SET_WRITE_EVENT( thisfd ) FD_SET( thisfd, &write_set)
+# define CLR_READ_EVENT( thisfd ) FD_CLR( thisfd, &read_set)
+# define CLR_WRITE_EVENT( thisfd ) FD_CLR( thisfd, &write_set)
+# define TST_READ_EVENT( thisfd ) FD_ISSET( thisfd, &read_set)
+# define TST_WRITE_EVENT( thisfd ) FD_ISSET( thisfd, &write_set)
+
+ fd_set read_set, write_set;
+ int highfd = -1;
+#else
+/* most of the following use pfd */
+# define POLLSETREADFLAGS (POLLIN|POLLRDNORM)
+# define POLLREADFLAGS (POLLSETREADFLAGS|POLLHUP|POLLERR)
+# define POLLSETWRITEFLAGS (POLLOUT|POLLWRNORM)
+# define POLLWRITEFLAGS (POLLOUT|POLLWRNORM|POLLHUP|POLLERR)
+
+# define SET_READ_EVENT( thisfd ){ CHECK_PFD( thisfd );\
+ pfd->events |= POLLSETREADFLAGS;}
+# define SET_WRITE_EVENT( thisfd ){ CHECK_PFD( thisfd );\
+ pfd->events |= POLLSETWRITEFLAGS;}
+
+# define CLR_READ_EVENT( thisfd ) pfd->revents &= ~POLLSETREADFLAGS
+# define CLR_WRITE_EVENT( thisfd ) pfd->revents &= ~POLLSETWRITEFLAGS
+# define TST_READ_EVENT( thisfd ) pfd->revents & POLLREADFLAGS
+# define TST_WRITE_EVENT( thisfd ) pfd->revents & POLLWRITEFLAGS
+
+# define CHECK_PFD( thisfd ) \
+ if ( pfd->fd != thisfd ) { \
+ pfd = &poll_fdarray[nbr_pfds++];\
+ pfd->fd = thisfd; \
+ pfd->events = 0; \
+ }
+
+ struct pollfd poll_fdarray[MAXCONNECTIONS];
+ struct pollfd * pfd = poll_fdarray;
+ struct pollfd * res_pfd = NULL;
+ struct pollfd * udp_pfd = NULL;
+ struct pollfd * ad_pfd = NULL;
+ aClient * authclnts[MAXCONNECTIONS]; /* mapping of auth fds to client ptrs */
+ int nbr_pfds = 0;
+#endif
+
+ aClient *cptr;
+ int nfds, ret = 0;
+ struct timeval wait;
+ time_t delay2 = delay;
+ int res, length, fd, i;
+ int auth;
+
+ for (res = 0;;)
+ {
+#if ! USE_POLL
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+#else
+ /* set up such that CHECK_FD works */
+ nbr_pfds = 0;
+ pfd = poll_fdarray;
+ pfd->fd = -1;
+ res_pfd = NULL;
+ udp_pfd = NULL;
+ ad_pfd = NULL;
+#endif /* USE_POLL */
+ auth = 0;
+
+#if USE_POLL
+ if ( auth == 0 )
+ bzero((char *) authclnts, sizeof( authclnts ));
+#endif
+ for (i = fdp->highest; i >= 0; i--)
+ {
+ if (!(cptr = local[fd = fdp->fd[i]]) ||
+ IsLog(cptr) || IsHeld(cptr))
+ continue;
+ Debug((DEBUG_L11, "fd %d cptr %#x %d %#x %s",
+ fd, cptr, cptr->status, cptr->flags,
+ get_client_name(cptr,TRUE)));
+ /* authentication fd's */
+ if (DoingAuth(cptr))
+ {
+ auth++;
+ SET_READ_EVENT(cptr->authfd);
+ Debug((DEBUG_NOTICE,"auth on %x %d", cptr,
+ fd));
+ if (cptr->flags & FLAGS_WRAUTH)
+ SET_WRITE_EVENT(cptr->authfd);
+#if USE_POLL
+ authclnts[cptr->authfd] = cptr;
+#else
+ if (cptr->authfd > highfd)
+ highfd = cptr->authfd;
+#endif
+ }
+ /*
+ ** if any of these is true, data won't be parsed
+ ** so no need to check for anything!
+ */
+#if defined(USE_IAUTH)
+ if (DoingDNS(cptr) || DoingAuth(cptr) ||
+ WaitingXAuth(cptr) ||
+ (DoingXAuth(cptr) &&
+ !(iauth_options & XOPT_EARLYPARSE)))
+#else
+ if (DoingDNS(cptr) || DoingAuth(cptr))
+#endif
+ continue;
+#if ! USE_POLL
+ if (fd > highfd)
+ highfd = fd;
+#endif
+ /*
+ ** Checking for new connections is only done up to
+ ** once per second.
+ */
+ if (IsListening(cptr))
+ {
+ if (timeofday > cptr->lasttime + 1 && ro == 0)
+ {
+ SET_READ_EVENT( fd );
+ }
+ else if (delay2 > 1)
+ delay2 = 1;
+ continue;
+ }
+
+ /*
+ ** This is very approximate, it should take
+ ** cptr->since into account. -kalt
+ */
+ if (DBufLength(&cptr->recvQ) && delay2 > 2)
+ delay2 = 1;
+
+ if (IsRegisteredUser(cptr))
+ {
+ if (cptr->since - timeofday < MAXPENALTY+1)
+ SET_READ_EVENT( fd );
+ }
+ else if (DBufLength(&cptr->recvQ) < 4088)
+ SET_READ_EVENT( fd );
+
+ /*
+ ** If we have anything in the sendQ, check if there is
+ ** room to write data.
+ */
+ if (DBufLength(&cptr->sendQ) || IsConnecting(cptr) ||
+#ifdef ZIP_LINKS
+ ((cptr->flags & FLAGS_ZIP) &&
+ (cptr->zip->outcount > 0)) ||
+#endif
+ IsReconnect(cptr))
+ if (IsServer(cptr) || IsConnecting(cptr) ||
+ ro == 0)
+ SET_WRITE_EVENT( fd );
+ }
+
+ if (udpfd >= 0)
+ {
+ SET_READ_EVENT(udpfd);
+#if ! USE_POLL
+ if (udpfd > highfd)
+ highfd = udpfd;
+#else
+ udp_pfd = pfd;
+#endif
+ }
+ if (resfd >= 0)
+ {
+ SET_READ_EVENT(resfd);
+#if ! USE_POLL
+ if (resfd > highfd)
+ highfd = resfd;
+#else
+ res_pfd = pfd;
+#endif
+ }
+#if defined(USE_IAUTH)
+ if (adfd >= 0)
+ {
+ SET_READ_EVENT(adfd);
+# if ! USE_POLL
+ if (adfd > highfd)
+ highfd = adfd;
+# else
+ ad_pfd = pfd;
+# endif
+ }
+#endif
+ Debug((DEBUG_L11, "udpfd %d resfd %d adfd %d", udpfd, resfd,
+ adfd));
+#if ! USE_POLL
+ Debug((DEBUG_L11, "highfd %d", highfd));
+#endif
+
+ wait.tv_sec = MIN(delay2, delay);
+ wait.tv_usec = (delay == 0) ? 200000 : 0;
+#if ! USE_POLL
+ nfds = select(highfd + 1, (SELECT_FDSET_TYPE *)&read_set,
+ (SELECT_FDSET_TYPE *)&write_set, 0, &wait);
+#else
+ nfds = poll( poll_fdarray, nbr_pfds,
+ wait.tv_sec * 1000 + wait.tv_usec/1000 );
+#endif
+ ret = nfds;
+ if (nfds == -1 && errno == EINTR)
+ return -1;
+ else if (nfds >= 0)
+ break;
+#if ! USE_POLL
+ report_error("select %s:%s", &me);
+#else
+ report_error("poll %s:%s", &me);
+#endif
+ res++;
+ if (res > 5)
+ restart("too many select()/poll() errors");
+ sleep(10);
+ timeofday = time(NULL);
+ } /* for(res=0;;) */
+
+ timeofday = time(NULL);
+ if (nfds > 0 &&
+#if ! USE_POLL
+ resfd >= 0 &&
+#else
+ (pfd = res_pfd) &&
+#endif
+ TST_READ_EVENT(resfd))
+ {
+ CLR_READ_EVENT(resfd);
+ nfds--;
+ do_dns_async();
+ }
+ if (nfds > 0 &&
+#if ! USE_POLL
+ udpfd >= 0 &&
+#else
+ (pfd = udp_pfd) &&
+#endif
+ TST_READ_EVENT(udpfd))
+ {
+ CLR_READ_EVENT(udpfd);
+ nfds--;
+ polludp();
+ }
+#if defined(USE_IAUTH)
+ if (nfds > 0 &&
+# if ! USE_POLL
+ adfd >= 0 &&
+# else
+ (pfd = ad_pfd) &&
+# endif
+ TST_READ_EVENT(adfd))
+ {
+ CLR_READ_EVENT(adfd);
+ nfds--;
+ read_iauth();
+ }
+#endif
+
+#if ! USE_POLL
+ for (i = fdp->highest; i >= 0; i--)
+#else
+ for (pfd = poll_fdarray, i = 0; i < nbr_pfds; i++, pfd++ )
+#endif
+ {
+#if ! USE_POLL
+ if (!(cptr = local[fd = fdp->fd[i]]))
+ continue;
+#else
+ fd = pfd->fd;
+ if ((cptr = authclnts[fd]))
+ {
+#endif
+ /*
+ * check for the auth fd's
+ */
+ if (auth > 0 && nfds > 0
+#if ! USE_POLL
+ && cptr->authfd >= 0
+#endif
+ )
+ {
+ auth--;
+ if (TST_WRITE_EVENT(cptr->authfd))
+ {
+ nfds--;
+ send_authports(cptr);
+ }
+ else if (TST_READ_EVENT(cptr->authfd))
+ {
+ nfds--;
+ read_authports(cptr);
+ }
+ continue;
+ }
+#if USE_POLL
+ }
+ fd = pfd->fd;
+ if (!(cptr = local[fd]))
+ continue;
+#else
+ fd = cptr->fd;
+#endif
+ /*
+ * accept connections
+ */
+ if (TST_READ_EVENT(fd) && IsListening(cptr))
+ {
+ CLR_READ_EVENT(fd);
+ cptr->lasttime = timeofday;
+ read_listener(cptr);
+ continue;
+ }
+ if (IsMe(cptr))
+ continue;
+ if (TST_WRITE_EVENT(fd))
+ {
+ int write_err = 0;
+ /*
+ ** ...room for writing, empty some queue then...
+ */
+ if (IsConnecting(cptr))
+ write_err = completed_connection(cptr);
+ if (!write_err)
+ (void)send_queued(cptr);
+ if (IsDead(cptr) || write_err)
+ {
+deadsocket:
+ if (TST_READ_EVENT(fd))
+ CLR_READ_EVENT(fd);
+ cptr->exitc = EXITC_ERROR;
+ (void)exit_client(cptr, cptr, &me,
+ strerror(get_sockerr(cptr)));
+ continue;
+ }
+ }
+ length = 1; /* for fall through case */
+ if (!NoNewLine(cptr) || TST_READ_EVENT(fd))
+ {
+ if (!DoingAuth(cptr))
+ length = read_packet(cptr, TST_READ_EVENT(fd));
+ }
+ readcalls++;
+ if (length == FLUSH_BUFFER)
+ continue;
+ else if (length > 0)
+ flush_connections(cptr->fd);
+ if (IsDead(cptr))
+ goto deadsocket;
+ if (length > 0)
+ continue;
+
+ /* Ghost! Unknown users are tagged in parse() since 2.9.
+ * Let's not drop the uplink but just the ghost's message.
+ */
+ if (length == -3)
+ continue;
+
+ /*
+ ** NB: This following section has been modified to *expect*
+ ** cptr to be valid (ie if (length == FLUSH_BUFFER) is
+ ** above and stays there). - avalon 24/9/94
+ */
+ /*
+ ** ...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",
+ cptr->fd, errno, length));
+
+ if (IsServer(cptr) || IsHandshake(cptr))
+ {
+ int timeconnected = timeofday - cptr->firsttime;
+
+ if (length == 0)
+ sendto_flag(SCH_NOTICE,
+ "Server %s closed the connection (%d, %2d:%02d:%02d)",
+ get_client_name(cptr, FALSE),
+ timeconnected / 86400,
+ (timeconnected % 86400) / 3600,
+ (timeconnected % 3600)/60,
+ timeconnected % 60);
+ else /* this must be for -1 */
+ {
+ report_error("Lost connection to %s:%s",cptr);
+ sendto_flag(SCH_NOTICE,
+ "%s had been connected for %d, %2d:%02d:%02d",
+ get_client_name(cptr, FALSE),
+ timeconnected / 86400,
+ (timeconnected % 86400) / 3600,
+ (timeconnected % 3600)/60,
+ timeconnected % 60);
+ if (hold_server(cptr) == 0)
+ continue;
+ }
+ }
+ (void)exit_client(cptr, cptr, &me, length >= 0 ?
+ "EOF From client" :
+ strerror(get_sockerr(cptr)));
+ } /* for(i) */
+ return ret;
+}
+
+/*
+ * connect_server
+ */
+int connect_server(aconf, by, hp)
+aConfItem *aconf;
+aClient *by;
+struct hostent *hp;
+{
+ Reg struct SOCKADDR *svp;
+ Reg aClient *cptr, *c2ptr;
+ Reg char *s;
+ int i, len;
+
+#ifdef INET6
+ Debug((DEBUG_NOTICE,"Connect to %s[%s] @%s",
+ aconf->name, aconf->host,
+ inet_ntop(AF_INET6, (char *)&aconf->ipnum, mydummy,
+ MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_NOTICE,"Connect to %s[%s] @%s",
+ aconf->name, aconf->host,
+ inetntoa((char *)&aconf->ipnum)));
+#endif
+
+ if ((c2ptr = find_server(aconf->name, NULL)))
+ {
+ sendto_flag(SCH_NOTICE, "Server %s already present from %s",
+ aconf->name, get_client_name(c2ptr, TRUE));
+ if (by && IsPerson(by) && !MyClient(by))
+ sendto_one(by,
+ ":%s NOTICE %s :Server %s already present from %s",
+ ME, by->name, aconf->name,
+ get_client_name(c2ptr, TRUE));
+ return -1;
+ }
+
+ /*
+ * If we don't know the IP# for this host and it is a hostname and
+ * not a ip# string, then try and find the appropriate host record.
+ */
+ if (!aconf->ipnum.S_ADDR && *aconf->host != '/')
+ {
+ Link lin;
+
+ lin.flags = ASYNC_CONNECT;
+ lin.value.aconf = aconf;
+ nextdnscheck = 1;
+ s = (char *)index(aconf->host, '@');
+ s++; /* should NEVER be NULL */
+#ifdef INET6
+ if (!inet_pton(AF_INET6, s, aconf->ipnum.s6_addr))
+#else
+ if ((aconf->ipnum.s_addr = inetaddr(s)) == -1)
+#endif
+ {
+#ifdef INET6
+ bzero(aconf->ipnum.s6_addr, IN6ADDRSZ);
+#else
+ aconf->ipnum.s_addr = 0;
+#endif
+ hp = gethost_byname(s, &lin);
+ Debug((DEBUG_NOTICE, "co_sv: hp %x ac %x na %s ho %s",
+ hp, aconf, aconf->name, s));
+ if (!hp)
+ return 0;
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ }
+ }
+ cptr = make_client(NULL);
+ cptr->hostp = hp;
+ /*
+ * Copy these in so we have something for error detection.
+ */
+ strncpyzt(cptr->name, aconf->name, sizeof(cptr->name));
+ strncpyzt(cptr->sockhost, aconf->host, HOSTLEN+1);
+
+#ifdef UNIXPORT
+ if (*aconf->host == '/') /* (/ 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 != -1)
+ (void)close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ return -1;
+ }
+
+ set_non_blocking(cptr->fd, cptr);
+ (void)set_sock_opts(cptr->fd, cptr);
+ (void)signal(SIGALRM, dummy);
+ (void)alarm(4);
+ if (connect(cptr->fd, (SAP)svp, len) < 0 && errno != EINPROGRESS)
+ {
+ i = errno; /* other system calls may eat errno */
+ (void)alarm(0);
+ report_error("Connect to host %s failed: %s",cptr);
+ if (by && IsPerson(by) && !MyClient(by))
+ sendto_one(by,
+ ":%s NOTICE %s :Connect to host %s failed.",
+ ME, by->name, cptr);
+ (void)close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ errno = i;
+ if (errno == EINTR)
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ (void)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
+ */
+ (void)attach_confs_host(cptr, aconf->host, CFLAG|NFLAG);
+
+ if (!find_conf_host(cptr->confs, aconf->host, NFLAG) ||
+ !find_conf_host(cptr->confs, aconf->host, CFLAG))
+ {
+ sendto_flag(SCH_NOTICE,
+ "Host %s is not enabled for connecting:no C/N-line",
+ aconf->host);
+ if (by && IsPerson(by) && !MyClient(by))
+ sendto_one(by,
+ ":%s NOTICE %s :Connect to host %s failed.",
+ ME, by->name, cptr);
+ det_confs_butmask(cptr, 0);
+ (void)close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ return(-1);
+ }
+ /*
+ ** The socket has been connected or connect is in progress.
+ */
+ (void)make_server(cptr);
+ if (by && IsPerson(by))
+ {
+ (void)strcpy(cptr->serv->by, by->name);
+ cptr->serv->user = by->user;
+ by->user->refcnt++;
+ }
+ else
+ (void)strcpy(cptr->serv->by, "AutoConn.");
+ cptr->serv->up = ME;
+ cptr->serv->nline = aconf;
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+ add_fd(cptr->fd, &fdall);
+ local[cptr->fd] = cptr;
+ cptr->acpt = &me;
+ SetConnecting(cptr);
+
+ get_sockhost(cptr, aconf->host);
+ add_client_to_list(cptr);
+ nextping = timeofday;
+ istat.is_unknown++;
+
+ return 0;
+}
+
+static struct SOCKADDR *connect_inet(aconf, cptr, lenp)
+Reg aConfItem *aconf;
+Reg aClient *cptr;
+int *lenp;
+{
+ static struct SOCKADDR_IN server;
+ Reg struct hostent *hp;
+ aClient *acptr;
+ int i;
+
+ /*
+ * Might as well get sockhost from here, the connection is attempted
+ * with it so if it fails its useless.
+ */
+ cptr->fd = socket(AFINET, SOCK_STREAM, 0);
+ if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_NOTICE,
+ "No more connections allowed (%s)", cptr->name);
+ return NULL;
+ }
+ mysk.SIN_PORT = 0;
+ bzero((char *)&server, sizeof(server));
+ server.SIN_FAMILY = AFINET;
+ get_sockhost(cptr, aconf->host);
+
+ if (cptr->fd == -1)
+ {
+ report_error("opening stream socket to server %s:%s", cptr);
+ return NULL;
+ }
+ /*
+ ** 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#.
+ ** With VIFs, M:line defines outgoing IP# and initialises mysk.
+ */
+ if (bind(cptr->fd, (SAP)&mysk, sizeof(mysk)) == -1)
+ {
+ report_error("error binding to local port for %s:%s", cptr);
+ return NULL;
+ }
+ /*
+ * 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 don't know it, then the connect fails.
+ */
+#ifdef INET6
+ if (isdigit(*aconf->host) && (AND16(aconf->ipnum.s6_addr) == 255))
+ if (!inet_pton(AF_INET6, aconf->host,aconf->ipnum.s6_addr))
+ bcopy(minus_one, aconf->ipnum.s6_addr, IN6ADDRSZ);
+ if (AND16(aconf->ipnum.s6_addr) == 255)
+#else
+ if (isdigit(*aconf->host) && (aconf->ipnum.s_addr == -1))
+ aconf->ipnum.s_addr = inetaddr(aconf->host);
+ if (aconf->ipnum.s_addr == -1)
+#endif
+ {
+ hp = cptr->hostp;
+ if (!hp)
+ {
+ Debug((DEBUG_FATAL, "%s: unknown host", aconf->host));
+ return NULL;
+ }
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ }
+ bcopy((char *)&aconf->ipnum, (char *)&server.SIN_ADDR,
+ sizeof(struct IN_ADDR));
+ bcopy((char *)&aconf->ipnum, (char *)&cptr->ip,
+ sizeof(struct IN_ADDR));
+ cptr->port = (aconf->port > 0) ? aconf->port : portnum;
+ server.SIN_PORT = htons(cptr->port);
+ /*
+ * Look for a duplicate IP#,port pair among already open connections
+ * (This caters for unestablished connections).
+ */
+ for (i = highest_fd; i >= 0; i--)
+ if ((acptr = local[i]) &&
+ !bcmp((char *)&cptr->ip, (char *)&acptr->ip,
+ sizeof(cptr->ip)) && server.SIN_PORT == acptr->port)
+ return NULL;
+ *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(aconf, cptr, lenp)
+aConfItem *aconf;
+aClient *cptr;
+int *lenp;
+{
+ static struct sockaddr_un sock;
+
+ if ((cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ report_error("Unix domain connect to host %s failed: %s", cptr);
+ return NULL;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_NOTICE,
+ "No more connections allowed (%s)", cptr->name);
+ return NULL;
+ }
+
+ get_sockhost(cptr, aconf->host);
+ strncpyzt(sock.sun_path, aconf->host + 2, sizeof(sock.sun_path));
+ sock.sun_family = AF_UNIX;
+ *lenp = strlen(sock.sun_path) + 2;
+
+ SetUnixSock(cptr);
+ return (struct sockaddr *)&sock;
+}
+#endif
+
+/*
+ * The following section of code performs summoning of users to irc.
+ */
+#if defined(ENABLE_SUMMON) || defined(ENABLE_USERS)
+int utmp_open()
+{
+#ifdef O_NOCTTY
+ return (open(UTMP, O_RDONLY|O_NOCTTY));
+#else
+ return (open(UTMP, O_RDONLY));
+#endif
+}
+
+int utmp_read(fd, name, line, host, hlen)
+int fd, hlen;
+char *name, *line, *host;
+{
+ struct utmp ut;
+ while (read(fd, (char *)&ut, sizeof (struct utmp))
+ == sizeof (struct utmp))
+ {
+ strncpyzt(name, ut.ut_name, 9);
+ strncpyzt(line, ut.ut_line, 10);
+#ifdef USER_PROCESS
+# if defined(HPUX) || defined(AIX)
+ strncpyzt(host,(ut.ut_host[0]) ? (ut.ut_host) : ME, 16);
+# else
+ strncpyzt(host, ME, 9);
+# endif
+ if (ut.ut_type == USER_PROCESS)
+ return 0;
+#else
+ strncpyzt(host, (ut.ut_host[0]) ? (ut.ut_host) : ME,
+ hlen);
+ if (ut.ut_name[0])
+ return 0;
+#endif
+ }
+ return -1;
+}
+
+int utmp_close(fd)
+int fd;
+{
+ return(close(fd));
+}
+
+#ifdef ENABLE_SUMMON
+void summon(who, namebuf, linebuf, chname)
+aClient *who;
+char *namebuf, *linebuf, *chname;
+{
+ static char wrerr[] = "NOTICE %s :Write error. Couldn't summon.";
+ int fd;
+ char line[120];
+ struct tm *tp;
+
+ tp = localtime(&timeofday);
+ if (strlen(linebuf) > (size_t) 9)
+ {
+ sendto_one(who,"NOTICE %s :Serious fault in SUMMON.",
+ who->name);
+ sendto_one(who,
+ "NOTICE %s :linebuf too long. Inform Administrator",
+ who->name);
+ return;
+ }
+ /*
+ * Following line added to prevent cracking to e.g. /dev/kmem if
+ * UTMP is for some silly reason writable to everyone...
+ */
+ if ((linebuf[0] != 't' || linebuf[1] != 't' || linebuf[2] != 'y')
+ && (linebuf[0] != 'c' || linebuf[1] != 'o' || linebuf[2] != 'n')
+#ifdef HPUX
+ && (linebuf[0] != 'p' || linebuf[1] != 't' || linebuf[2] != 'y' ||
+ linebuf[3] != '/')
+#endif
+ )
+ {
+ sendto_one(who,
+ "NOTICE %s :Looks like mere mortal souls are trying to",
+ who->name);
+ sendto_one(who,"NOTICE %s :enter the twilight zone... ",
+ who->name);
+ Debug((0, "%s (%s@%s, nick %s, %s)",
+ "FATAL: major security hack. Notify Administrator !",
+ who->username, who->user->host,
+ who->name, who->info));
+ return;
+ }
+
+ SPRINTF(line,"/dev/%s", linebuf);
+ (void)alarm(5);
+#ifdef O_NOCTTY
+ if ((fd = open(line, O_WRONLY | O_NDELAY | O_NOCTTY)) == -1)
+#else
+ if ((fd = open(line, O_WRONLY | O_NDELAY)) == -1)
+#endif
+ {
+ (void)alarm(0);
+ sendto_one(who,
+ "NOTICE %s :%s seems to have disabled summoning...",
+ who->name, namebuf);
+ return;
+ }
+#if !defined(O_NOCTTY) && defined(TIOCNOTTY)
+ (void)ioctl(fd, TIOCNOTTY, NULL);
+#endif
+ (void)alarm(0);
+ (void)sprintf(line,"\n\r\007Message from IRC_Daemon@%s at %d:%02d\n\r",
+ ME, tp->tm_hour, tp->tm_min);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)alarm(0);
+ (void)strcpy(line, "ircd: You are being summoned to Internet Relay \
+Chat on\n\r");
+ (void)alarm(5);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)alarm(0);
+ SPRINTF(line, "ircd: Channel %s, by %s@%s (%s) %s\n\r", chname,
+ who->user->username, who->user->host, who->name, who->info);
+ (void)alarm(5);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)alarm(0);
+ (void)strcpy(line,"ircd: Respond with irc\n\r");
+ (void)alarm(5);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)close(fd);
+ (void)alarm(0);
+ sendto_one(who, rpl_str(RPL_SUMMONING, who->name), namebuf);
+ return;
+}
+# endif
+#endif /* ENABLE_SUMMON */
+
+/*
+** 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(cptr, name, len)
+aClient *cptr;
+char *name;
+int len;
+{
+ static char tmp[HOSTLEN+1];
+ struct hostent *hp;
+ char *cname = cptr->name;
+ aConfItem *aconf;
+
+ /*
+ ** Setup local socket structure to use for binding to.
+ */
+ bzero((char *)&mysk, sizeof(mysk));
+ mysk.SIN_FAMILY = AFINET;
+
+ if ((aconf = find_me())->passwd && isdigit(*aconf->passwd))
+#ifdef INET6
+ if(!inet_pton(AF_INET6, aconf->passwd, mysk.sin6_addr.s6_addr))
+ bcopy(minus_one, mysk.sin6_addr.s6_addr, IN6ADDRSZ);
+#else
+ mysk.sin_addr.s_addr = inetaddr(aconf->passwd);
+#endif
+
+ if (gethostname(name, len) == -1)
+ return;
+ name[len] = '\0';
+
+ /* assume that a name containing '.' is a FQDN */
+ if (!index(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 ((hp = gethostbyname(cname)) || (hp = gethostbyname(name)))
+ {
+ char *hname;
+ int i = 0;
+
+ for (hname = hp->h_name; hname; hname = hp->h_aliases[i++])
+ {
+ strncpyzt(tmp, hname, sizeof(tmp));
+ add_local_domain(tmp, sizeof(tmp) - 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 (!mycmp(ME, tmp))
+ break;
+ }
+ if (mycmp(ME, tmp))
+ strncpyzt(name, hp->h_name, len);
+ else
+ strncpyzt(name, tmp, len);
+ if (!aconf->passwd)
+ bcopy(hp->h_addr, (char *)&mysk.SIN_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(aconf)
+aConfItem *aconf;
+{
+ struct SOCKADDR_IN from;
+ int on = 1;
+
+ if (udpfd != -1)
+ return udpfd;
+ bzero((char *)&from, sizeof(from));
+ if (aconf->passwd && isdigit(*aconf->passwd))
+#ifdef INET6
+ if(!inet_pton(AF_INET6, aconf->passwd,from.sin6_addr.s6_addr))
+ bcopy(minus_one, from.sin6_addr.s6_addr, IN6ADDRSZ);
+#else
+ from.sin_addr.s_addr = inetaddr(aconf->passwd);
+#endif
+ else
+#ifdef INET6
+ from.SIN_ADDR = in6addr_any;
+#else
+ from.sin_addr.s_addr = htonl(INADDR_ANY); /* hmmpf */
+#endif
+ from.SIN_PORT = htons((u_short) aconf->port);
+ from.SIN_FAMILY = AFINET;
+
+ if ((udpfd = socket(AFINET, SOCK_DGRAM, 0)) == -1)
+ {
+ Debug((DEBUG_ERROR, "socket udp : %s", strerror(errno)));
+ return -1;
+ }
+ if (SETSOCKOPT(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, on) == -1)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "setsockopt udp fd %d : %m", udpfd);
+#endif
+ Debug((DEBUG_ERROR, "setsockopt so_reuseaddr : %s",
+ strerror(errno)));
+ (void)close(udpfd);
+ return udpfd = -1;
+ }
+ on = 0;
+ (void) SETSOCKOPT(udpfd, SOL_SOCKET, SO_BROADCAST, &on, on);
+ if (bind(udpfd, (SAP)&from, sizeof(from))==-1)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "bind udp.%d fd %d : %m",
+ ntohs(from.SIN_PORT), udpfd);
+#endif
+ Debug((DEBUG_ERROR, "bind : %s", strerror(errno)));
+ (void)close(udpfd);
+ return udpfd = -1;
+ }
+ if (fcntl(udpfd, F_SETFL, FNDELAY)==-1)
+ {
+ Debug((DEBUG_ERROR, "fcntl fndelay : %s", strerror(errno)));
+ (void)close(udpfd);
+ return udpfd = -1;
+ }
+ Debug((DEBUG_INFO, "udpfd = %d, port %d", udpfd,ntohs(from.SIN_PORT)));
+ return udpfd;
+}
+
+
+void send_ping(aconf)
+aConfItem *aconf;
+{
+ Ping pi;
+ struct SOCKADDR_IN sin;
+ aCPing *cp = aconf->ping;
+
+#ifdef INET6
+ if (!aconf->ipnum.s6_addr || AND16(aconf->ipnum.s6_addr) == 255 || !cp->port)
+#else
+ if (!aconf->ipnum.s_addr || aconf->ipnum.s_addr == -1 || !cp->port)
+#endif
+ return;
+ if (aconf->class->conFreq == 0) /* avoid flooding */
+ return;
+ pi.pi_cp = aconf;
+ pi.pi_id = htonl(PING_CPING);
+ pi.pi_seq = cp->lseq++;
+ cp->seq++;
+ /*
+ * Only recognise stats from the last 20 minutes as significant...
+ * Try and fake sliding along a "window" here.
+ */
+ if (cp->seq > 1 && cp->seq * aconf->class->conFreq > 1200)
+ {
+ if (cp->recvd)
+ {
+ cp->ping -= (cp->ping / cp->recvd);
+ if (cp->recvd == cp->seq)
+ cp->recvd--;
+ }
+ else
+ cp->ping = 0;
+ cp->seq--;
+ }
+
+ bzero((char *)&sin, sizeof(sin));
+#ifdef INET6
+ bcopy(aconf->ipnum.s6_addr, sin.sin6_addr.s6_addr, IN6ADDRSZ);
+#else
+ sin.sin_addr.s_addr = aconf->ipnum.s_addr;
+#endif
+ sin.SIN_PORT = htons(cp->port);
+ sin.SIN_FAMILY = AFINET;
+ (void)gettimeofday(&pi.pi_tv, NULL);
+#ifdef INET6
+ Debug((DEBUG_SEND,"Send ping to %s,%d fd %d, %d bytes",
+ inet_ntop(AF_INET6, (char *)&aconf->ipnum,mydummy,MYDUMMY_SIZE),
+ cp->port, udpfd, sizeof(pi)));
+#else
+ Debug((DEBUG_SEND,"Send ping to %s,%d fd %d, %d bytes",
+ inetntoa((char *)&aconf->ipnum),
+ cp->port, udpfd, sizeof(pi)));
+#endif
+ (void)sendto(udpfd, (char *)&pi, sizeof(pi), 0,(SAP)&sin,sizeof(sin));
+}
+
+static int check_ping(buf, len)
+char *buf;
+int len;
+{
+ Ping pi;
+ aConfItem *aconf;
+ struct timeval tv;
+ double d;
+ aCPing *cp = NULL;
+ u_long rtt;
+
+ (void)gettimeofday(&tv, NULL);
+
+ if (len < sizeof(pi) + 8)
+ return -1;
+
+ bcopy(buf, (char *)&pi, sizeof(pi)); /* ensure nice byte align. */
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (pi.pi_cp == aconf && (cp = aconf->ping))
+ break;
+ if (!aconf || match(aconf->name, buf + sizeof(pi)))
+ return -1;
+
+ cp->recvd++;
+ cp->lrecvd++;
+ rtt = ((tv.tv_sec - pi.pi_tv.tv_sec) * 1000 +
+ (tv.tv_usec - pi.pi_tv.tv_usec) / 1000);
+ cp->ping += rtt;
+ cp->rtt += rtt;
+ if (cp->rtt > 1000000)
+ {
+ cp->ping = (cp->rtt /= cp->lrecvd);
+ cp->recvd = cp->lrecvd = 1;
+ cp->seq = cp->lseq = 1;
+ }
+ d = (double)cp->recvd / (double)cp->seq;
+ d = pow(d, (double)20.0);
+ d = (double)cp->ping / (double)cp->recvd / d;
+ if (d > 10000.0)
+ d = 10000.0;
+ aconf->pref = (int) (d * 100.0);
+
+ return 0;
+}
+
+/*
+ * max # of pings set to 15/sec.
+ */
+static void polludp()
+{
+ static time_t last = 0;
+ static int cnt = 0, mlen = 0, lasterr = 0;
+ Reg char *s;
+ struct SOCKADDR_IN from;
+ Ping pi;
+ int n;
+ SOCK_LEN_TYPE fromlen = sizeof(from);
+
+ /*
+ * find max length of data area of packet.
+ */
+ if (!mlen)
+ {
+ mlen = sizeof(readbuf) - strlen(ME) - strlen(version);
+ mlen -= 6;
+ if (mlen < 0)
+ mlen = 0;
+ }
+ Debug((DEBUG_DEBUG,"udp poll"));
+
+ n = recvfrom(udpfd, readbuf, mlen, 0, (SAP)&from, &fromlen);
+ if (n == -1)
+ {
+ ircstp->is_udperr++;
+ if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
+ return;
+ else
+ {
+ report_error("udp port recvfrom (%s): %s", &me);
+ return;
+ }
+ }
+
+ if (timeofday == last)
+ {
+ if (++cnt > 14)
+ {
+ if (timeofday > lasterr + 30)
+ {
+ sendto_flag(SCH_NOTICE,
+ "udp packet dropped: %d bytes from %s.%d",
+#ifdef INET6
+ n, inetntop(AF_INET6,
+ (char *)&from.sin6_addr, mydummy,
+ MYDUMMY_SIZE),
+#else
+ n,inetntoa((char *)&from.sin_addr),
+#endif
+ ntohs(from.SIN_PORT));
+ lasterr = timeofday;
+ }
+ ircstp->is_udpdrop++;
+ return;
+ }
+ }
+ else
+ cnt = 0, last = timeofday;
+
+#ifdef INET6
+ Debug((DEBUG_NOTICE, "udp (%d) %d bytes from %s,%d", cnt, n,
+ inet_ntop(AF_INET6, (char *)&from.sin6_addr, mydummy,
+ MYDUMMY_SIZE),
+ ntohs(from.SIN_PORT)));
+#else
+ Debug((DEBUG_NOTICE, "udp (%d) %d bytes from %s,%d", cnt, n,
+ inetntoa((char *)&from.sin_addr),
+ ntohs(from.SIN_PORT)));
+#endif
+
+ readbuf[n] = '\0';
+ ircstp->is_udpok++;
+ if (n < 8)
+ return;
+
+ bcopy(s = readbuf, (char *)&pi, MIN(n, sizeof(pi)));
+ pi.pi_id = ntohl(pi.pi_id);
+ Debug((DEBUG_INFO, "\tpi_id %#x pi_seq %d pi_cp %#x",
+ pi.pi_id, pi.pi_seq, pi.pi_cp));
+
+ if ((pi.pi_id == (PING_CPING|PING_REPLY) ||
+ pi.pi_id == (PING_CPING|(PING_REPLY << 24))) && n >= sizeof(pi))
+ {
+ check_ping(s, n);
+ return;
+ }
+ else if (pi.pi_id & PING_REPLY)
+ return;
+ /*
+ * attach my name and version for the reply
+ */
+ pi.pi_id |= PING_REPLY;
+ pi.pi_id = htonl(pi.pi_id);
+ bcopy((char *)&pi, s, MIN(n, sizeof(pi)));
+ s += n;
+ (void)strcpy(s, ME);
+ s += strlen(s)+1;
+ (void)strcpy(s, version);
+ s += strlen(s);
+ (void)sendto(udpfd, readbuf, s-readbuf, 0, (SAP)&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()
+{
+ static Link ln;
+ aClient *cptr;
+ aConfItem *aconf;
+ struct hostent *hp;
+ int bytes, pkts;
+
+ pkts = 0;
+
+ do {
+ ln.flags = -1;
+ hp = get_res((char *)&ln);
+
+ Debug((DEBUG_DNS,"%#x = get_res(%d,%#x)", 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);
+ cptr->hostp = hp;
+#if defined(USE_IAUTH)
+ if (hp)
+ {
+ int i = 0;
+
+ while (hp->h_aliases[i])
+ sendto_iauth("%d A %s",
+ cptr->fd,
+ hp->h_aliases[i++]);
+ if (hp->h_name)
+ sendto_iauth("%d N %s",
+ cptr->fd, hp->h_name);
+ else if (hp->h_aliases[0])
+ sendto_iauth("%d n", cptr->fd);
+ }
+ else
+ sendto_iauth("%d d", cptr->fd);
+ if (iauth_options & XOPT_EXTWAIT)
+ cptr->lasttime = timeofday;
+#endif
+ }
+ break;
+ case ASYNC_CONNECT :
+ aconf = ln.value.aconf;
+ if (hp && aconf)
+ {
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ (void)connect_server(aconf, NULL, hp);
+ }
+ else
+ sendto_flag(SCH_ERROR,
+ "Connect to %s failed: host lookup",
+ (aconf) ? aconf->host : "unknown");
+ break;
+ case ASYNC_CONF :
+ aconf = ln.value.aconf;
+ if (hp && aconf)
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ break;
+ default :
+ break;
+ }
+ pkts++;
+ if (ioctl(resfd, FIONREAD, &bytes) == -1)
+ bytes = 0;
+ } while ((bytes > 0) && (pkts < 10));
+}
diff --git a/ircd/s_bsd_ext.h b/ircd/s_bsd_ext.h
new file mode 100644
index 0000000..6209e8e
--- /dev/null
+++ b/ircd/s_bsd_ext.h
@@ -0,0 +1,77 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_bsd_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_bsd.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef S_BSD_C
+extern aClient *local[];
+extern FdAry fdas, fdaa, fdall;
+extern int highest_fd, readcalls, udpfd, resfd, adfd;
+extern time_t timeofday;
+#endif /* S_BSD_C */
+
+/* External definitions for global functions.
+ */
+#ifndef S_BSD_C
+#define EXTERN extern
+#else /* S_BSD_C */
+#define EXTERN
+#endif /* S_BSD_C */
+EXTERN void add_local_domain __P((char *hname, int size));
+EXTERN void report_error __P((char *text, aClient *cptr));
+EXTERN int inetport __P((aClient *cptr, char *ip, char *ipmask, int port));
+EXTERN int add_listener __P((aConfItem *aconf));
+EXTERN void close_listeners();
+EXTERN void start_iauth __P((int));
+EXTERN void init_sys();
+EXTERN void write_pidfile();
+EXTERN int check_client __P((Reg aClient *cptr));
+EXTERN int check_server_init __P((aClient *cptr));
+EXTERN int check_server __P((aClient *cptr, Reg struct hostent *hp,
+ Reg aConfItem *c_conf, Reg aConfItem *n_conf,
+ int estab));
+EXTERN int hold_server __P((aClient *cptr));
+EXTERN void close_connection __P((aClient *cptr));
+EXTERN int get_sockerr __P((aClient *cptr));
+EXTERN void set_non_blocking __P((int fd, aClient *cptr));
+EXTERN aClient *add_connection __P((aClient *cptr, int fd));
+EXTERN int read_message __P((time_t delay, FdAry *fdp, int ro));
+EXTERN int connect_server __P((aConfItem *aconf, aClient *by,
+ struct hostent *hp));
+EXTERN void get_my_name __P((aClient *cptr, char *name, int len));
+EXTERN int setup_ping __P((aConfItem *aconf));
+EXTERN void send_ping __P((aConfItem *aconf));
+#if defined(ENABLE_SUMMON) || defined(ENABLE_USERS)
+EXTERN int utmp_open();
+EXTERN int utmp_read __P((int fd, char *name, char *line, char *host,
+ int hlen));
+EXTERN int utmp_close(int fd);
+#ifdef ENABLE_SUMMON
+EXTERN void summon __P((aClient *who, char *namebuf, char *linebuf,
+ char *chname));
+#endif /* ENABLE_SUMMON */
+#endif /* ENABLE_SUMMON || ENABLE_USERS */
+#ifdef UNIXPORT
+EXTERN int unixport __P((aClient *cptr, char *path, int port));
+#endif
+#undef EXTERN
diff --git a/ircd/s_conf.c b/ircd/s_conf.c
new file mode 100644
index 0000000..2abbbd3
--- /dev/null
+++ b/ircd/s_conf.c
@@ -0,0 +1,1684 @@
+/************************************************************************
+ * 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.
+ */
+
+/* -- avalon -- 20 Feb 1992
+ * Reversed the order of the params for attach_conf().
+ * detach_conf() and attach_conf() are now the same:
+ * function_conf(aClient *, aConfItem *)
+ */
+
+/* -- Jto -- 20 Jun 1990
+ * Added gruner's overnight fix..
+ */
+
+/* -- Jto -- 16 Jun 1990
+ * Moved matches to ../common/match.c
+ */
+
+/* -- Jto -- 03 Jun 1990
+ * Added Kill fixes from gruner@lan.informatik.tu-muenchen.de
+ * Added jarlek's msgbase fix (I still don't understand it... -- Jto)
+ */
+
+/* -- Jto -- 13 May 1990
+ * Added fixes from msa:
+ * Comments and return value to init_conf()
+ */
+
+/*
+ * -- Jto -- 12 May 1990
+ * Added close() into configuration file (was forgotten...)
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_conf.c,v 1.42 1999/05/01 21:29:13 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_CONF_C
+#include "s_externs.h"
+#undef S_CONF_C
+
+static int check_time_interval __P((char *, char *));
+static int lookup_confhost __P((aConfItem *));
+
+aConfItem *conf = NULL;
+aConfItem *kconf = NULL;
+
+/*
+ * remove all conf entries from the client except those which match
+ * the status field mask.
+ */
+void det_confs_butmask(cptr, mask)
+aClient *cptr;
+int mask;
+{
+ Reg Link *tmp, *tmp2;
+
+ for (tmp = cptr->confs; tmp; tmp = tmp2)
+ {
+ tmp2 = tmp->next;
+ if ((tmp->value.aconf->status & mask) == 0)
+ (void)detach_conf(cptr, tmp->value.aconf);
+ }
+}
+
+/*
+ * Match address by #IP bitmask (10.11.12.128/27)
+ */
+int match_ipmask(mask, cptr)
+char *mask;
+aClient *cptr;
+{
+ int i1, i2, i3, i4, m;
+ u_long lmask, baseip;
+ char *at;
+
+ if (at = index(mask, '@'))
+ mask = at + 1;
+ if (sscanf(mask, "%d.%d.%d.%d/%d", &i1, &i2, &i3, &i4, &m) != 5 ||
+ m < 1 || m > 31) {
+ sendto_flag(SCH_LOCAL, "Ignoring bad mask: %s", mask);
+ return -1;
+ }
+ lmask = htonl((u_long)0xffffffffL << (32 - m)); /* /24->0xffffff00ul */
+ baseip = htonl(i1 * 0x1000000 + i2 * 0x10000 + i3 * 0x100 + i4);
+#ifdef INET6
+ return 1;
+/* return ((cptr->ip.s6_addr & lmask) == baseip) ? 0 : 1;*/
+#else
+ return ((cptr->ip.s_addr & lmask) == baseip) ? 0 : 1;
+#endif
+}
+
+/*
+ * find the first (best) I line to attach.
+ */
+int attach_Iline(cptr, hp, sockhost)
+aClient *cptr;
+Reg struct hostent *hp;
+char *sockhost;
+{
+ Reg aConfItem *aconf;
+ Reg char *hname;
+ Reg 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) &&
+ (aconf->status != CONF_RCLIENT))
+ continue;
+ if (aconf->port && aconf->port != cptr->acpt->port)
+ continue;
+ if (!aconf->host || !aconf->name)
+ goto attach_iline;
+ if (hp)
+ for (i = 0, hname = hp->h_name; hname;
+ hname = hp->h_aliases[i++])
+ {
+ strncpyzt(fullname, hname,
+ sizeof(fullname));
+ add_local_domain(fullname,
+ HOSTLEN - strlen(fullname));
+ Debug((DEBUG_DNS, "a_il: %s->%s",
+ sockhost, fullname));
+ if (index(aconf->name, '@'))
+ {
+ (void)strcpy(uhost, cptr->username);
+ (void)strcat(uhost, "@");
+ }
+ else
+ *uhost = '\0';
+ (void)strncat(uhost, fullname,
+ sizeof(uhost) - strlen(uhost));
+ if (!match(aconf->name, uhost))
+ goto attach_iline;
+ }
+
+ if (index(aconf->host, '@'))
+ {
+ strncpyzt(uhost, cptr->username, sizeof(uhost));
+ (void)strcat(uhost, "@");
+ }
+ else
+ *uhost = '\0';
+ (void)strncat(uhost, sockhost, sizeof(uhost) - strlen(uhost));
+ if (strchr(aconf->host, '/')) /* 1.2.3.0/24 */
+ {
+ if (match_ipmask(aconf->host, cptr))
+ continue;
+ } else if (match(aconf->host, uhost)) /* 1.2.3.* */
+ continue;
+ if (*aconf->name == '\0' && hp)
+ {
+ strncpyzt(uhost, hp->h_name, sizeof(uhost));
+ add_local_domain(uhost, sizeof(uhost) - strlen(uhost));
+ }
+attach_iline:
+ if (aconf->status & CONF_RCLIENT)
+ SetRestricted(cptr);
+ get_sockhost(cptr, uhost);
+ if ((i = attach_conf(cptr, aconf)) < -1)
+ find_bounce(cptr, ConfClass(aconf), -1);
+ return i;
+ }
+ find_bounce(cptr, 0, -2);
+ return -2; /* used in register_user() */
+}
+
+/*
+ * Find the single N line and return pointer to it (from list).
+ * If more than one then return NULL pointer.
+ */
+aConfItem *count_cnlines(lp)
+Reg Link *lp;
+{
+ Reg 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 ||
+ aconf->status == CONF_ZCONNECT_SERVER) && !cline)
+ cline = aconf;
+ else if (aconf->status == CONF_NOCONNECT_SERVER && !nline)
+ nline = aconf;
+ }
+ return nline;
+}
+
+/*
+** detach_conf
+** Disassociate configuration from the client.
+** Also removes a class from the list if marked for deleting.
+*/
+int detach_conf(cptr, aconf)
+aClient *cptr;
+aConfItem *aconf;
+{
+ Reg Link **lp, *tmp;
+
+ lp = &(cptr->confs);
+
+ while (*lp)
+ {
+ if ((*lp)->value.aconf == aconf)
+ {
+ if ((aconf) && (Class(aconf)))
+ {
+ if (aconf->status & CONF_CLIENT_MASK)
+ if (ConfLinks(aconf) > 0)
+ --ConfLinks(aconf);
+ if (ConfMaxLinks(aconf) == -1 &&
+ ConfLinks(aconf) == 0)
+ {
+ free_class(Class(aconf));
+ Class(aconf) = NULL;
+ }
+ }
+ if (aconf && !--aconf->clients && IsIllegal(aconf))
+ free_conf(aconf);
+ tmp = *lp;
+ *lp = tmp->next;
+ free_link(tmp);
+ istat.is_conflink--;
+ return 0;
+ }
+ else
+ lp = &((*lp)->next);
+ }
+ return -1;
+}
+
+static int is_attached(aconf, cptr)
+aConfItem *aconf;
+aClient *cptr;
+{
+ Reg 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...
+*/
+int attach_conf(cptr, aconf)
+aConfItem *aconf;
+aClient *cptr;
+{
+ Reg Link *lp;
+
+ if (is_attached(aconf, cptr))
+ return 1;
+ if (IsIllegal(aconf))
+ return -1;
+ if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT |
+ CONF_RCLIENT)))
+ {
+ if (aconf->clients >= ConfMaxLinks(aconf) &&
+ ConfMaxLinks(aconf) > 0)
+ return -3; /* Use this for printing error message */
+ }
+ if ((aconf->status & (CONF_CLIENT | CONF_RCLIENT)))
+ {
+ int hcnt = 0, ucnt = 0;
+
+ /* check on local/global limits per host and per user@host */
+
+ /*
+ ** local limits first to save CPU if any is hit.
+ ** host check is done on the IP address.
+ ** user check is done on the IDENT reply.
+ */
+ if (ConfMaxHLocal(aconf) > 0 || ConfMaxUHLocal(aconf) > 0) {
+ Reg aClient *acptr;
+ Reg int i;
+
+ for (i = highest_fd; i >= 0; i--)
+ if ((acptr = local[i]) && (cptr != acptr) &&
+ !IsListening(acptr) &&
+ !bcmp((char *)&cptr->ip,(char *)&acptr->ip,
+ sizeof(cptr->ip)))
+ {
+ hcnt++;
+ if (!strncasecmp(acptr->auth,
+ cptr->auth, USERLEN))
+ ucnt++;
+ }
+ if (ConfMaxHLocal(aconf) > 0 &&
+ hcnt >= ConfMaxHLocal(aconf))
+ return -4; /* for error message */
+ if (ConfMaxUHLocal(aconf) > 0 &&
+ ucnt >= ConfMaxUHLocal(aconf))
+ return -5; /* for error message */
+ }
+ /*
+ ** Global limits
+ ** host check is done on the hostname (IP if unresolved)
+ ** user check is done on username
+ */
+ if (ConfMaxHGlobal(aconf) > 0 || ConfMaxUHGlobal(aconf) > 0)
+ {
+ Reg aClient *acptr;
+ Reg int ghcnt = hcnt, gucnt = ucnt;
+
+ for (acptr = client; acptr; acptr = acptr->next)
+ {
+ if (!IsPerson(acptr))
+ continue;
+ if (MyConnect(acptr) &&
+ (ConfMaxHLocal(aconf) > 0 ||
+ ConfMaxUHLocal(aconf) > 0))
+ continue;
+ if (!strcmp(cptr->sockhost, acptr->user->host))
+ {
+ if (ConfMaxHGlobal(aconf) > 0 &&
+ ++ghcnt >= ConfMaxHGlobal(aconf))
+ return -6;
+ if (ConfMaxUHGlobal(aconf) > 0 &&
+ !strcmp(cptr->user->username,
+ acptr->user->username) &&
+ (++gucnt >=ConfMaxUHGlobal(aconf)))
+ return -7;
+ }
+ }
+ }
+ }
+
+ lp = make_link();
+ istat.is_conflink++;
+ lp->next = cptr->confs;
+ lp->value.aconf = aconf;
+ cptr->confs = lp;
+ aconf->clients++;
+ if (aconf->status & CONF_CLIENT_MASK)
+ ConfLinks(aconf)++;
+ return 0;
+}
+
+
+aConfItem *find_admin()
+ {
+ Reg aConfItem *aconf;
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (aconf->status & CONF_ADMIN)
+ break;
+
+ return (aconf);
+ }
+
+aConfItem *find_me()
+ {
+ Reg 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(cptr, name, statmask)
+aClient *cptr;
+char *name;
+int statmask;
+{
+ Reg 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) && !first)
+ first = tmp;
+ }
+ else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+ (tmp->status & (CONF_SERVER_MASK|CONF_HUB)) &&
+ tmp->name && !mycmp(tmp->name, name))
+ {
+ if (!attach_conf(cptr, tmp) && !first)
+ first = tmp;
+ }
+ }
+ return (first);
+}
+
+/*
+ * Added for new access check meLazy
+ */
+aConfItem *attach_confs_host(cptr, host, statmask)
+aClient *cptr;
+char *host;
+int statmask;
+{
+ Reg 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) && !first)
+ first = tmp;
+ }
+ else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+ (tmp->status & CONF_SERVER_MASK) &&
+ (tmp->host && mycmp(tmp->host, host) == 0))
+ {
+ if (!attach_conf(cptr, tmp) && !first)
+ first = tmp;
+ }
+ }
+ return (first);
+}
+
+/*
+ * find a conf entry which matches the hostname and has the same name.
+ */
+aConfItem *find_conf_exact(name, user, host, statmask)
+char *name, *host, *user;
+int statmask;
+{
+ Reg aConfItem *tmp;
+ char userhost[USERLEN+HOSTLEN+3];
+
+ SPRINTF(userhost, "%s@%s", user, host);
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ {
+ if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
+ mycmp(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(Class(tmp)))
+ return tmp;
+ else
+ continue;
+ }
+ else
+ return tmp;
+ }
+ return NULL;
+}
+
+aConfItem *find_conf_name(name, statmask)
+char *name;
+int statmask;
+{
+ Reg aConfItem *tmp;
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ {
+ /*
+ ** Accept if the *real* hostname (usually sockecthost)
+ ** matches *either* host or name field of the configuration.
+ */
+ if ((tmp->status & statmask) &&
+ (!tmp->name || match(tmp->name, name) == 0))
+ return tmp;
+ }
+ return NULL;
+}
+
+aConfItem *find_conf(lp, name, statmask)
+char *name;
+Link *lp;
+int statmask;
+{
+ Reg 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 && !mycmp(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(lp, host, statmask)
+Reg Link *lp;
+char *host;
+Reg int statmask;
+{
+ Reg 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(lp, ip, user, statmask)
+char *ip, *user;
+Link *lp;
+int statmask;
+{
+ Reg aConfItem *tmp;
+ Reg char *s;
+
+ for (; lp; lp = lp->next)
+ {
+ tmp = lp->value.aconf;
+ if (!(tmp->status & statmask))
+ continue;
+ s = index(tmp->host, '@');
+ *s = '\0';
+ if (match(tmp->host, user))
+ {
+ *s = '@';
+ continue;
+ }
+ *s = '@';
+ if (!bcmp((char *)&tmp->ipnum, ip, sizeof(struct IN_ADDR)))
+ return tmp;
+ }
+ return NULL;
+}
+
+/*
+ * find_conf_entry
+ *
+ * - looks for a match on all given fields.
+ */
+aConfItem *find_conf_entry(aconf, mask)
+aConfItem *aconf;
+u_int mask;
+{
+ Reg 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) && mycmp(bconf->host, aconf->host))
+ continue;
+
+ if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) ||
+ (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd)))
+ continue;
+ if (!BadPtr(bconf->passwd) &&
+ mycmp(bconf->passwd, aconf->passwd))
+ continue;
+
+ if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) ||
+ (BadPtr(aconf->name) && !BadPtr(bconf->name)))
+ continue;
+ if (!BadPtr(bconf->name) && mycmp(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(cptr, sptr, sig)
+aClient *cptr, *sptr;
+int sig;
+{
+ Reg aConfItem **tmp = &conf, *tmp2 = NULL;
+ Reg aClass *cltmp;
+ Reg aClient *acptr;
+ Reg int i;
+ int ret = 0;
+
+ if (sig == 1)
+ {
+ sendto_flag(SCH_NOTICE,
+ "Got signal SIGHUP, reloading ircd.conf file");
+#ifdef ULTRIX
+ if (fork() > 0)
+ exit(0);
+ write_pidfile();
+#endif
+ }
+
+ for (i = 0; i <= highest_fd; i++)
+ if ((acptr = local[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;
+#if defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN)
+ if (find_restrict(acptr))
+ {
+ sendto_flag(SCH_NOTICE,
+ "Restricting %s, closing lp",
+ get_client_name(acptr,FALSE));
+ acptr->exitc = EXITC_RLINE;
+ if (exit_client(cptr,acptr,&me,"R-lined") ==
+ FLUSH_BUFFER)
+ ret = FLUSH_BUFFER;
+ }
+#endif
+ }
+
+ 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_conf(tmp2);
+ }
+
+ tmp = &kconf;
+ while ((tmp2 = *tmp))
+ {
+ *tmp = tmp2->next;
+ 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))
+ MaxLinks(cltmp) = -1;
+
+ if (sig != 2)
+ flush_cache();
+ (void) initconf(0);
+ 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);
+ }
+#ifdef CACHED_MOTD
+ read_motd(IRCDMOTD_PATH);
+#endif
+ rehashed = 1;
+ return ret;
+}
+
+/*
+ * openconf
+ *
+ * returns -1 on any error or else the fd opened from which to read the
+ * configuration file from. This may either be the file direct or one end
+ * of a pipe from m4.
+ */
+int openconf()
+{
+#ifdef M4_PREPROC
+ int pi[2], i;
+
+ if (pipe(pi) == -1)
+ return -1;
+ switch(vfork())
+ {
+ case -1 :
+ return -1;
+ case 0 :
+ (void)close(pi[0]);
+ if (pi[1] != 1)
+ {
+ (void)dup2(pi[1], 1);
+ (void)close(pi[1]);
+ }
+ (void)dup2(1,2);
+ for (i = 3; i < MAXCONNECTIONS; i++)
+ if (local[i])
+ (void) close(i);
+ /*
+ * m4 maybe anywhere, use execvp to find it. Any error
+ * goes out with report_error. Could be dangerous,
+ * two servers running with the same fd's >:-) -avalon
+ */
+ (void)execlp("m4", "m4", IRCDM4_PATH, configfile, 0);
+ report_error("Error executing m4 %s:%s", &me);
+ _exit(-1);
+ default :
+ (void)close(pi[1]);
+ return pi[0];
+ }
+#else
+ return open(configfile, O_RDONLY);
+#endif
+}
+
+/*
+** initconf()
+** Read configuration file.
+**
+** returns -1, if file cannot be opened
+** 0, if file opened
+*/
+
+#define MAXCONFLINKS 150
+
+int initconf(opt)
+int opt;
+{
+ static char quotes[9][2] = {{'b', '\b'}, {'f', '\f'}, {'n', '\n'},
+ {'r', '\r'}, {'t', '\t'}, {'v', '\v'},
+ {'\\', '\\'}, { 0, 0}};
+ Reg char *tmp, *s;
+ int fd, i;
+ char line[512], c[80], *tmp2 = NULL, *tmp3 = NULL, *tmp4 = NULL;
+ int ccount = 0, ncount = 0;
+ aConfItem *aconf = NULL;
+
+ Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
+ if ((fd = openconf()) == -1)
+ {
+#if defined(M4_PREPROC) && !defined(USE_IAUTH)
+ (void)wait(0);
+#endif
+ return -1;
+ }
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ while ((i = dgets(fd, line, sizeof(line) - 1)) > 0)
+ {
+ line[i] = '\0';
+ if ((tmp = (char *)index(line, '\n')))
+ *tmp = 0;
+ else while(dgets(fd, c, sizeof(c) - 1) > 0)
+ if ((tmp = (char *)index(c, '\n')))
+ {
+ *tmp = 0;
+ break;
+ }
+ /*
+ * 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';
+ break; /* Ignore the rest of the line */
+ }
+ }
+ 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] != IRCDCONF_DELIMITER)
+ {
+ Debug((DEBUG_ERROR, "Bad config line: %s", line));
+ continue;
+ }
+ if (aconf)
+ free_conf(aconf);
+ aconf = make_conf();
+
+ if (tmp2)
+ MyFree(tmp2);
+ tmp3 = tmp4 = NULL;
+ 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 'B': /* Name of alternate servers */
+ case 'b':
+ aconf->status = CONF_BOUNCE;
+ break;
+ case 'C': /* Server where I should try to connect */
+ /* in case of lp failures */
+ ccount++;
+ aconf->status = CONF_CONNECT_SERVER;
+ break;
+ case 'c':
+ ccount++;
+ aconf->status = CONF_ZCONNECT_SERVER;
+ break;
+ case 'D': /* auto connect restrictions */
+ case 'd':
+ aconf->status = CONF_DENY;
+ break;
+ case 'H': /* Hub server line */
+ case 'h':
+ aconf->status = CONF_HUB;
+ break;
+ case 'I': /* Just plain normal irc client trying */
+ /* to connect me */
+ aconf->status = CONF_CLIENT;
+ break;
+ case 'i' : /* Restricted client */
+ aconf->status = CONF_RCLIENT;
+ break;
+ case 'K': /* Kill user line on irc.conf */
+ aconf->status = CONF_KILL;
+ break;
+ case 'k':
+ aconf->status = CONF_OTHERKILL;
+ 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;
+ case 'Q': /* a server that you don't want in your */
+ case 'q': /* network. USE WITH CAUTION! */
+ aconf->status = CONF_QUARANTINED_SERVER;
+ 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 'S': /* Service. Same semantics as */
+ case 's': /* CONF_OPERATOR */
+ aconf->status = CONF_SERVICE;
+ break;
+#if 0
+ case 'U': /* Uphost, ie. host where client reading */
+ case 'u': /* this should connect. */
+ /* This is for client only, I must ignore this */
+ /* ...U-line should be removed... --msa */
+ break;
+#endif
+ case 'V': /* Server link version requirements */
+ aconf->status = CONF_VER;
+ 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)) == NULL)
+ break;
+ DupString(aconf->passwd, tmp);
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ DupString(aconf->name, tmp);
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ aconf->port = 0;
+ if (sscanf(tmp, "0x%x", &aconf->port) != 1 ||
+ aconf->port == 0)
+ aconf->port = atoi(tmp);
+ if (aconf->status == CONF_CONNECT_SERVER)
+ DupString(tmp2, tmp);
+ if (aconf->status == CONF_ZCONNECT_SERVER)
+ DupString(tmp2, tmp);
+ if ((tmp = getfield(NULL)) == NULL)
+ break;
+ Class(aconf) = find_class(atoi(tmp));
+ /* the following are only used for Y: */
+ if ((tmp3 = getfield(NULL)) == NULL)
+ break;
+ tmp4 = getfield(NULL);
+ break;
+ }
+ istat.is_confmem += aconf->host ? strlen(aconf->host)+1 : 0;
+ istat.is_confmem += aconf->passwd ? strlen(aconf->passwd)+1 :0;
+ istat.is_confmem += aconf->name ? strlen(aconf->name)+1 : 0;
+
+ /*
+ ** Bounce line fields are mandatory
+ */
+ if (aconf->status == CONF_BOUNCE && aconf->port == 0)
+ continue;
+ /*
+ ** 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 (atoi(aconf->host) >= 0)
+ add_class(atoi(aconf->host),
+ atoi(aconf->passwd),
+ atoi(aconf->name), aconf->port,
+ tmp ? atoi(tmp) : 0,
+/* tmp3 ? atoi(tmp3) : 0,
+** tmp3 && index(tmp3, '.') ?
+** atoi(index(tmp3, '.') + 1) : 0,
+** the next 3 lines should be replaced by the previous sometime in the
+** future. It is only kept for "backward" compatibility and not needed,
+** but I'm in good mood today -krys
+*/
+ tmp3 ? atoi(tmp3) : (atoi(aconf->name) > 0) ? atoi(aconf->name) : 0,
+ tmp3 && index(tmp3, '.') ?
+ atoi(index(tmp3, '.') + 1) : (atoi(aconf->name) < 0) ? -1 * atoi(aconf->name) : 0,
+/* end of backward compatibility insanity */
+ tmp4 ? atoi(tmp4) : 0,
+ tmp4 && index(tmp4, '.') ?
+ atoi(index(tmp4, '.') + 1) : 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 (Class(aconf) == 0)
+ Class(aconf) = find_class(0);
+ if (MaxLinks(Class(aconf)) < 0)
+ Class(aconf) = 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)
+ {
+ bconf->class->links -= bconf->clients;
+ bconf->class = aconf->class;
+ bconf->class->links += bconf->clients;
+ }
+ free_conf(aconf);
+ aconf = bconf;
+ }
+ else if (aconf->host &&
+ aconf->status == CONF_LISTEN_PORT)
+ (void)add_listener(aconf);
+ }
+ if (aconf->status & CONF_SERVICE)
+ aconf->port &= SERVICE_MASK_ALL;
+ if (aconf->status & (CONF_SERVER_MASK|CONF_SERVICE))
+ if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
+ !aconf->host || index(aconf->host, '*') ||
+ index(aconf->host,'?') || !aconf->name)
+ continue;
+
+ if (aconf->status &
+ (CONF_SERVER_MASK|CONF_LOCOP|CONF_OPERATOR|CONF_SERVICE))
+ if (!index(aconf->host, '@') && *aconf->host != '/')
+ {
+ char *newhost;
+ int len = 3; /* *@\0 = 3 */
+
+ len += strlen(aconf->host);
+ newhost = (char *)MyMalloc(len);
+ SPRINTF(newhost, "*@%s", aconf->host);
+ MyFree(aconf->host);
+ aconf->host = newhost;
+ istat.is_confmem += 2;
+ }
+ if (aconf->status & CONF_SERVER_MASK)
+ {
+ if (BadPtr(aconf->passwd))
+ continue;
+ else if (!(opt & BOOT_QUICK))
+ (void)lookup_confhost(aconf);
+ }
+ if (aconf->status & (CONF_CONNECT_SERVER | CONF_ZCONNECT_SERVER))
+ {
+ aconf->ping = (aCPing *)MyMalloc(sizeof(aCPing));
+ bzero((char *)aconf->ping, sizeof(*aconf->ping));
+ istat.is_confmem += sizeof(*aconf->ping);
+ if (tmp2 && index(tmp2, '.'))
+ aconf->ping->port = atoi(index(tmp2, '.') + 1);
+ else
+ aconf->ping->port = aconf->port;
+ if (tmp2)
+ {
+ MyFree(tmp2);
+ tmp2 = NULL;
+ }
+
+ }
+ /*
+ ** 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)
+ {
+ if (me.info != DefInfo)
+ MyFree(me.info);
+ me.info = MyMalloc(REALLEN+1);
+ strncpyzt(me.info, aconf->name, REALLEN+1);
+ if (ME[0] == '\0' && aconf->host[0])
+ strncpyzt(ME, aconf->host,
+ sizeof(ME));
+ if (aconf->port)
+ setup_ping(aconf);
+ }
+ (void)collapse(aconf->host);
+ (void)collapse(aconf->name);
+ Debug((DEBUG_NOTICE,
+ "Read Init: (%d) (%s) (%s) (%s) (%d) (%d)",
+ aconf->status, aconf->host, aconf->passwd,
+ aconf->name, aconf->port,
+ aconf->class ? ConfClass(aconf) : 0));
+
+ if (aconf->status & (CONF_KILL|CONF_OTHERKILL))
+ {
+ aconf->next = kconf;
+ kconf = aconf;
+ }
+ else
+ {
+ aconf->next = conf;
+ conf = aconf;
+ }
+ aconf = NULL;
+ }
+ if (aconf)
+ free_conf(aconf);
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ (void)close(fd);
+#if defined(M4_PREPROC) && !defined(USE_IAUTH)
+ (void)wait(0);
+#endif
+ check_class();
+ nextping = nextconnect = timeofday;
+ 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(aconf)
+Reg aConfItem *aconf;
+{
+ Reg char *s;
+ Reg struct hostent *hp;
+ Link ln;
+
+ if (BadPtr(aconf->host) || BadPtr(aconf->name))
+ goto badlookup;
+ if ((s = index(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))
+#ifdef INET6
+ if(!inet_pton(AF_INET6, s, aconf->ipnum.s6_addr))
+ bcopy(minus_one, aconf->ipnum.s6_addr, IN6ADDRSZ);
+#else
+ aconf->ipnum.s_addr = inetaddr(s);
+#endif
+ else if ((hp = gethost_byname(s, &ln)))
+ bcopy(hp->h_addr, (char *)&(aconf->ipnum),
+ sizeof(struct IN_ADDR));
+
+#ifdef INET6
+ if (AND16(aconf->ipnum.s6_addr) == 255)
+#else
+ if (aconf->ipnum.s_addr == -1)
+#endif
+ goto badlookup;
+ return 0;
+badlookup:
+#ifdef INET6
+ if (AND16(aconf->ipnum.s6_addr) == 255)
+#else
+ if (aconf->ipnum.s_addr == -1)
+#endif
+ bzero((char *)&aconf->ipnum, sizeof(struct IN_ADDR));
+ Debug((DEBUG_ERROR,"Host/server name error: (%s) (%s)",
+ aconf->host, aconf->name));
+ return -1;
+}
+
+int find_kill(cptr, doall, comment)
+aClient *cptr;
+int doall;
+char **comment;
+{
+ static char reply[256];
+ char *host, *ip, *name, *ident, *check;
+ aConfItem *tmp;
+ int now;
+
+ if (!cptr->user)
+ return 0;
+
+ host = cptr->sockhost;
+#ifdef INET6
+ ip = (char *) inetntop(AF_INET6, (char *)&cptr->ip, mydummy,
+ MYDUMMY_SIZE);
+#else
+ ip = (char *) inetntoa((char *)&cptr->ip);
+#endif
+ if (!strcmp(host, ip))
+ ip = NULL; /* we don't have a name for the ip# */
+ name = cptr->user->username;
+ ident = cptr->auth;
+
+ if (strlen(host) > (size_t) HOSTLEN ||
+ (name ? strlen(name) : 0) > (size_t) HOSTLEN)
+ return (0);
+
+ *reply = '\0';
+
+ for (tmp = kconf; tmp; tmp = tmp->next)
+ {
+ if (!doall && (BadPtr(tmp->passwd) || !isdigit(*tmp->passwd)))
+ continue;
+ if (!(tmp->status & (CONF_KILL | CONF_OTHERKILL)))
+ continue; /* should never happen with kconf */
+ if (!tmp->host || !tmp->name)
+ continue;
+ if (tmp->status == CONF_KILL)
+ check = name;
+ else
+ check = ident;
+ /* host & IP matching.. */
+ if (!ip) /* unresolved */
+ {
+ if (strchr(tmp->host, '/'))
+ {
+ if (match_ipmask((*tmp->host == '=') ?
+ tmp->host+1: tmp->host, cptr))
+ continue;
+ }
+ else
+ if (match((*tmp->host == '=') ? tmp->host+1 :
+ tmp->host, host))
+ continue;
+ }
+ else if (*tmp->host == '=') /* numeric only */
+ continue;
+ else /* resolved */
+ if (strchr(tmp->host, '/'))
+ {
+ if (match_ipmask(tmp->host, cptr))
+ continue;
+ }
+ else
+ if (match(tmp->host, ip) &&
+ match(tmp->host, host))
+ continue;
+
+ /* user & port matching */
+ if ((!check || match(tmp->name, check) == 0) &&
+ (!tmp->port || (tmp->port == cptr->acpt->port)))
+ {
+ now = 0;
+ if (!BadPtr(tmp->passwd) && isdigit(*tmp->passwd) &&
+ !(now = check_time_interval(tmp->passwd, reply)))
+ continue;
+ if (now == ERR_YOUWILLBEBANNED)
+ tmp = NULL;
+ break;
+ }
+ }
+
+ if (*reply)
+ sendto_one(cptr, reply, ME, now, cptr->name);
+ else if (tmp)
+ sendto_one(cptr, ":%s %d %s :%s%s", ME,
+ ERR_YOUREBANNEDCREEP, cptr->name,
+ BadPtr(tmp->passwd) ?
+ "You are not welcome to this server" :
+ "You are not welcome to this server: ",
+ BadPtr(tmp->passwd) ? "" : tmp->passwd);
+
+ if (tmp && !BadPtr(tmp->passwd))
+ *comment = tmp->passwd;
+ else
+ *comment = NULL;
+
+ return (tmp ? -1 : 0);
+}
+
+/*
+ * For type stat, check if both name and host masks match.
+ * Return -1 for match, 0 for no-match.
+ */
+int find_two_masks(name, host, stat)
+char *name, *host;
+int stat;
+{
+ aConfItem *tmp;
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ if ((tmp->status == stat) && tmp->host && tmp->name &&
+ (match(tmp->host, host) == 0) &&
+ (match(tmp->name, name) == 0))
+ break;
+ return (tmp ? -1 : 0);
+}
+
+/*
+ * For type stat, check if name matches and any char from key matches
+ * to chars in passwd field.
+ * Return -1 for match, 0 for no-match.
+ */
+int find_conf_flags(name, key, stat)
+char *name, *key;
+int stat;
+{
+ aConfItem *tmp;
+ int l;
+
+ if (index(key, '/') == NULL)
+ return 0;
+ l = ((char *)index(key, '/') - key) + 1;
+ for (tmp = conf; tmp; tmp = tmp->next)
+ if ((tmp->status == stat) && tmp->passwd && tmp->name &&
+ (strncasecmp(key, tmp->passwd, l) == 0) &&
+ (match(tmp->name, name) == 0) &&
+ (strpbrk(key + l, tmp->passwd + l)))
+ break;
+ return (tmp ? -1 : 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(cptr)
+aClient *cptr;
+{
+ aConfItem *tmp;
+ char reply[80], temprpl[80];
+ char *rplhold = reply, *host, *name, *s;
+ char rplchar = 'Y';
+ int pi[2], rc = 0, n;
+
+ 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))
+ continue;
+
+ if (pipe(pi) == -1)
+ {
+ report_error("Error creating pipe for R-line %s:%s",
+ &me);
+ return 0;
+ }
+ switch (rc = vfork())
+ {
+ case -1 :
+ report_error("Error forking for R-line %s:%s", &me);
+ return 0;
+ case 0 :
+ {
+ Reg int i;
+
+ (void)close(pi[0]);
+ for (i = 2; i < MAXCONNECTIONS; i++)
+ if (i != pi[1])
+ (void)close(i);
+ if (pi[1] != 2)
+ (void)dup2(pi[1], 2);
+ (void)dup2(2, 1);
+ if (pi[1] != 2 && pi[1] != 1)
+ (void)close(pi[1]);
+ (void)execlp(tmp->passwd, tmp->passwd, name, host,
+ cptr->username, 0);
+ _exit(-1);
+ }
+ default :
+ (void)close(pi[1]);
+ break;
+ }
+ *reply = '\0';
+ (void)dgets(-1, NULL, 0); /* make sure buffer marked empty */
+ while ((n = dgets(pi[0], temprpl, sizeof(temprpl)-1)) > 0)
+ {
+ temprpl[n] = '\0';
+ if ((s = (char *)index(temprpl, '\n')))
+ *s = '\0';
+ if (strlen(temprpl) + strlen(reply) < sizeof(reply)-2)
+ SPRINTF(rplhold,"%s %s", rplhold, temprpl);
+ else
+ {
+ sendto_flag(SCH_ERROR,
+ "R-line %s/%s: reply too long!",
+ name, host);
+ break;
+ }
+ }
+ (void)dgets(-1, NULL, 0); /* make sure buffer marked empty */
+ (void)close(pi[0]);
+ (void)kill(rc, SIGKILL); /* cleanup time */
+#if !defined(USE_IAUTH)
+ (void)wait(0);
+#endif
+
+ rc = 0;
+ while (*rplhold == ' ')
+ rplhold++;
+ rplchar = *rplhold; /* Pull out the yes or no */
+ while (*rplhold != ' ')
+ rplhold++;
+ while (*rplhold == ' ')
+ rplhold++;
+ (void)strcpy(reply,rplhold);
+ rplhold = reply;
+
+ if ((rc = (rplchar == 'n' || rplchar == 'N')))
+ break;
+ }
+ if (rc)
+ {
+ sendto_one(cptr, ":%s %d %s :Restriction: %s",
+ ME, ERR_YOUREBANNEDCREEP, cptr->name, reply);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+
+/*
+** check against a set of time intervals
+*/
+
+static int check_time_interval(interval, reply)
+char *interval, *reply;
+{
+ struct tm *tptr;
+ char *p;
+ int perm_min_hours, perm_min_minutes,
+ perm_max_hours, perm_max_minutes;
+ int now, perm_min, perm_max;
+
+ tptr = localtime(&timeofday);
+ now = tptr->tm_hour * 60 + tptr->tm_min;
+
+ while (interval)
+ {
+ p = (char *)index(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 <= now && now <= perm_max)
+ : (perm_min <= now || now <= perm_max))
+ {
+ (void)sprintf(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 <= now + 5 && now + 5 <= perm_max)
+ : (perm_min <= now + 5 || now + 5 <= perm_max))
+ {
+ (void)sprintf(reply, ":%%s %%d %%s :%d minute%s%s",
+ perm_min-now,(perm_min-now)>1?"s ":" ",
+ "and you will be denied for further access");
+ return(ERR_YOUWILLBEBANNED);
+ }
+ interval = p;
+ }
+ return(0);
+}
+
+/*
+** find_bounce
+** send a bounce numeric to a client.
+** fd is optional, and only makes sense if positive and when cptr is NULL
+** fd == -1 : not fd, class is a class number.
+** fd == -2 : not fd, class isn't a class number.
+*/
+void find_bounce(cptr, class, fd)
+aClient *cptr;
+int class, fd;
+ {
+ Reg aConfItem *aconf;
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ {
+ if (aconf->status != CONF_BOUNCE)
+ continue;
+
+ if (fd >= 0)
+ /*
+ ** early rejection,
+ ** connection class and hostname are unknown
+ */
+ if (*aconf->host == '\0')
+ {
+ char rpl[BUFSIZE];
+
+ SPRINTF(rpl, rpl_str(RPL_BOUNCE,"unknown"),
+ aconf->name, aconf->port);
+ strcat(rpl, "\r\n");
+#ifdef INET6
+ sendto(class, rpl, strlen(rpl), 0, 0, 0);
+#else
+ send(class, rpl, strlen(rpl), 0);
+#endif
+ return;
+ }
+ else
+ continue;
+
+ /* fd < 0 */
+ /*
+ ** "too many" type rejection, class is known.
+ ** check if B line is for a class #,
+ ** and if it is for a hostname.
+ */
+ if (fd != -2 &&
+ !strchr(aconf->host, '.') && isdigit(*aconf->host))
+ {
+ if (class != atoi(aconf->host))
+ continue;
+ }
+ else
+ if (strchr(aconf->host, '/'))
+ {
+ if (match_ipmask(aconf->host, cptr))
+ continue;
+ }
+ else if (match(aconf->host, cptr->sockhost))
+ continue;
+
+ sendto_one(cptr, rpl_str(RPL_BOUNCE, cptr->name), aconf->name,
+ aconf->port);
+ return;
+ }
+
+ }
+
+/*
+** find_denied
+** for a given server name, make sure no D line matches any of the
+** servers currently present on the net.
+*/
+aConfItem *
+find_denied(name, class)
+ char *name;
+ int class;
+{
+ aConfItem *aconf;
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ {
+ if (aconf->status != CONF_DENY)
+ continue;
+ if (!aconf->name)
+ continue;
+ if (match(aconf->name, name) && aconf->port != class)
+ continue;
+ if (isdigit(*aconf->passwd))
+ {
+ aConfItem *aconf2;
+ int ck = atoi(aconf->passwd);
+
+ for (aconf2 = conf; aconf2; aconf2 = aconf2->next)
+ {
+ if (aconf2->status != CONF_NOCONNECT_SERVER)
+ continue;
+ if (!aconf2->class || ConfClass(aconf2) != ck)
+ continue;
+ if (find_client(aconf2->host, NULL))
+ return aconf2;
+ }
+ }
+ if (aconf->host)
+ {
+ aServer *asptr;
+
+ for (asptr = svrtop; asptr; asptr = asptr->nexts)
+ if (aconf->host &&
+ !match(aconf->host, asptr->bcptr->name))
+ return aconf;
+ }
+ }
+ return NULL;
+}
diff --git a/ircd/s_conf_ext.h b/ircd/s_conf_ext.h
new file mode 100644
index 0000000..8257254
--- /dev/null
+++ b/ircd/s_conf_ext.h
@@ -0,0 +1,66 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_conf_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_conf.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef S_CONF_C
+extern aConfItem *conf, *kconf;
+#endif /* S_CONF_C */
+
+/* External definitions for global functions.
+ */
+#ifndef S_CONF_C
+#define EXTERN extern
+#else /* S_CONF_C */
+#define EXTERN
+#endif /* S_CONF_C */
+EXTERN void det_confs_butmask __P((aClient *cptr, int mask));
+EXTERN int attach_Iline __P((aClient *cptr, Reg struct hostent *hp,
+ char *sockhost));
+EXTERN aConfItem *count_cnlines __P((Reg Link *lp));
+EXTERN int detach_conf __P((aClient *cptr, aConfItem *aconf));
+EXTERN int attach_conf __P((aClient *cptr, aConfItem *aconf));
+EXTERN aConfItem *find_admin();
+EXTERN aConfItem *find_me();
+EXTERN aConfItem *attach_confs __P((aClient *cptr, char *name, int statmask));
+EXTERN aConfItem *attach_confs_host __P((aClient *cptr, char *host,
+ int statmask));
+EXTERN aConfItem *find_conf_exact __P((char *name, char *user, char *host,
+ int statmask));
+EXTERN aConfItem *find_conf_name __P((char *name, int statmask));
+EXTERN aConfItem *find_conf __P((Link *lp, char *name, int statmask));
+EXTERN aConfItem *find_conf_host __P((Reg Link *lp, char *host,
+ Reg int statmask));
+EXTERN aConfItem *find_conf_ip __P((Link *lp, char *ip, char *user,
+ int statmask));
+EXTERN aConfItem *find_conf_entry __P((aConfItem *aconf, u_int mask));
+EXTERN int rehash __P((aClient *cptr, aClient *sptr, int sig));
+EXTERN int openconf();
+EXTERN int initconf __P((int opt));
+EXTERN int find_kill __P((aClient *cptr, int doall, char **comment));
+EXTERN int find_two_masks __P((char *name, char *host, int stat));
+EXTERN int find_conf_flags __P((char *name, char *key, int stat));
+EXTERN int find_restrict __P((aClient *cptr));
+EXTERN void find_bounce __P((aClient *cptr, int class, int fd));
+EXTERN aConfItem *find_denied __P((char *name, int class));
+#undef EXTERN
diff --git a/ircd/s_debug.c b/ircd/s_debug.c
new file mode 100644
index 0000000..c3d9c4f
--- /dev/null
+++ b/ircd/s_debug.c
@@ -0,0 +1,668 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_debug.c,v 1.28 1999/07/11 22:11:17 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_DEBUG_C
+#include "s_externs.h"
+#undef S_DEBUG_C
+
+/*
+ * Option string. Must be before #ifdef DEBUGMODE.
+ * spaces are not allowed.
+ */
+char serveropts[] = {
+#ifndef NO_IDENT
+'a',
+#endif
+#ifdef CHROOTDIR
+'c',
+#endif
+#ifdef CMDLINE_CONFIG
+'C',
+#endif
+#ifdef DEBUGMODE
+'D',
+#endif
+#ifdef RANDOM_NDELAY
+'d',
+#endif
+#if defined(LOCOP_REHASH) && defined(OPER_REHASH)
+'e',
+#endif
+#ifdef OPER_REHASH
+'E',
+#endif
+#ifdef SLOW_ACCEPT
+'f',
+#endif
+#ifdef CLONE_CHECK
+'F',
+#endif
+#ifdef SUN_GSO_BUG
+'g',
+#endif
+#ifdef HUB
+'H',
+#endif
+#ifdef BETTER_CDELAY
+'h',
+#endif
+#ifdef SHOW_INVISIBLE_LUSERS
+'i',
+#endif
+#ifndef NO_DEFAULT_INVISIBLE
+'I',
+#endif
+#if defined(LOCOP_DIE) && defined(OPER_DIE)
+'j',
+#endif
+#ifdef OPER_DIE
+'J',
+#endif
+#ifdef OPER_KILL
+# ifdef LOCAL_KILL_ONLY
+'k',
+# else
+'K',
+# endif
+#endif
+#ifdef LEAST_IDLE
+'L',
+#endif
+#ifdef M4_PREPROC
+'m',
+#endif
+#ifdef IDLE_FROM_MSG
+'M',
+#endif
+#ifdef NPATH /* gone */
+'N',
+#endif
+#ifdef BETTER_NDELAY
+'n',
+#endif
+#ifdef CRYPT_OPER_PASSWORD
+'p',
+#endif
+#ifdef CRYPT_LINK_PASSWORD
+'P',
+#endif
+#if defined(LOCOP_RESTART) && defined(OPER_RESTART)
+'r',
+#endif
+#ifdef OPER_RESTART
+'R',
+#endif
+#ifdef USE_SERVICES
+'s',
+#endif
+#ifdef ENABLE_SUMMON
+'S',
+#endif
+#ifdef OPER_REMOTE
+'t',
+#endif
+#ifndef NO_PREFIX
+'u',
+#endif
+#ifdef ENABLE_USERS
+'U',
+#endif
+#ifdef VALLOC
+'V',
+#endif
+#ifdef NOWRITEALARM
+'w',
+#endif
+#ifdef UNIXPORT
+'X',
+#endif
+#ifdef USE_SYSLOG
+'Y',
+#endif
+#ifdef ZIP_LINKS
+'Z',
+#endif
+#ifdef INET6
+'6',
+#endif
+'\0'};
+
+#ifdef DEBUGMODE
+static char debugbuf[2*READBUF_SIZE]; /* needs to be big.. */
+
+#if ! USE_STDARG
+void debug(level, form, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
+int level;
+char *form, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10;
+#else
+void debug(int level, char *form, ...)
+#endif
+{
+ int err = errno;
+
+#ifdef USE_SYSLOG
+ if (level == DEBUG_ERROR)
+ {
+#if ! USE_STDARG
+ syslog(LOG_ERR, form, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
+#else
+# if HAVE_VSYSLOG
+ va_list va;
+ va_start(va, form);
+ vsyslog(LOG_ERR, form, va);
+ va_end(va);
+# else
+ va_list va;
+ va_start(va, form);
+ vsprintf(debugbuf, form, va);
+ va_end(va);
+ syslog(LOG_ERR, debugbuf);
+# endif
+#endif
+ }
+#endif
+ if ((debuglevel >= 0) && (level <= debuglevel))
+ {
+#if ! USE_STDARG
+ (void)sprintf(debugbuf, form,
+ p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
+#else
+ va_list va;
+ va_start(va, form);
+ (void)vsprintf(debugbuf, form, va);
+ va_end(va);
+#endif
+ if (local[2])
+ {
+ local[2]->sendM++;
+ local[2]->sendB += strlen(debugbuf);
+ }
+ (void)fprintf(stderr, "%s", debugbuf);
+ (void)fputc('\n', stderr);
+ }
+ errno = err;
+}
+#endif /* DEBUGMODE */
+
+/*
+ * 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(cptr, nick)
+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)
+ {
+ sendto_one(cptr,":%s NOTICE %s :Getruseage error: %s.",
+ me.name, nick, sys_errlist[errno]);
+ return;
+ }
+ secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
+ rup = timeofday - me.since;
+ if (secs == 0)
+ secs = 1;
+
+ sendto_one(cptr,
+ ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
+ 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);
+ if (rup && hzz)
+ sendto_one(cptr, ":%s %d %s :RSS %d ShMem %d Data %d Stack %d",
+ 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 %d Reclaims %d Faults %d",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_nswap,
+ rus.ru_minflt, rus.ru_majflt);
+ sendto_one(cptr, ":%s %d %s :Block in %d out %d",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_inblock,
+ rus.ru_oublock);
+ sendto_one(cptr, ":%s %d %s :Msg Rcv %d Send %d",
+ me.name, RPL_STATSDEBUG, nick, rus.ru_msgrcv, rus.ru_msgsnd);
+ sendto_one(cptr, ":%s %d %s :Signals %d Context Vol. %d Invol %d",
+ 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 :DBUF alloc %d blocks %d",
+ me.name, RPL_STATSDEBUG, nick, istat.is_dbufuse,
+ istat.is_dbufnow);
+#ifdef DEBUGMODE
+ sendto_one(cptr, ":%s %d %s :Reads %d Writes %d",
+ me.name, RPL_STATSDEBUG, nick, readcalls, writecalls);
+ 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]);
+#endif
+ return;
+}
+
+void send_defines(cptr, nick)
+aClient *cptr;
+char *nick;
+{
+ sendto_one(cptr, ":%s %d %s :HUB:%s MS:%d",
+ ME, RPL_STATSDEFINE, nick,
+#ifdef HUB
+ "yes",
+#else
+ "no",
+#endif
+ MAXSERVERS);
+ sendto_one(cptr,
+ ":%s %d %s :LQ:%d MXC:%d TS:%d HRD:%d HGL:%d WWD:%d ATO:%d",
+ ME, RPL_STATSDEFINE, nick, LISTENQUEUE, MAXCONNECTIONS,
+ TIMESEC, HANGONRETRYDELAY, HANGONGOODLINK, WRITEWAITDELAY,
+ ACCEPTTIMEOUT);
+ sendto_one(cptr, ":%s %d %s :KCTL:%d DCTL:%d LDCTL: %d CF:%d MCPU:%d",
+ ME, RPL_STATSDEFINE, nick, KILLCHASETIMELIMIT,
+ DELAYCHASETIMELIMIT, LDELAYCHASETIMELIMIT,
+ CLIENT_FLOOD, MAXCHANNELSPERUSER);
+ sendto_one(cptr, ":%s %d %s :H:%d N:%d U:%d R:%d T:%d C:%d P:%d K:%d",
+ ME, RPL_STATSDEFINE, nick, HOSTLEN, NICKLEN, USERLEN,
+ REALLEN, TOPICLEN, CHANNELLEN, PASSWDLEN, KEYLEN);
+ 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",
+ ME, RPL_STATSDEFINE, nick,
+#ifdef ZIP_LINKS
+ ZIP_LEVEL,
+#else
+ -1,
+#endif
+#ifdef CLONE_CHECK
+ CLONE_MAX, CLONE_PERIOD
+#else
+ -1, -1
+#endif
+ );
+}
+
+void count_memory(cptr, nick, debug)
+aClient *cptr;
+char *nick;
+int debug;
+{
+ extern aChannel *channel;
+ extern aClass *classes;
+ extern aConfItem *conf;
+ extern int _HASHSIZE, _CHANNELHASHSIZE;
+
+ Reg aClient *acptr;
+ Reg Link *link;
+ Reg aChannel *chptr;
+ Reg aConfItem *aconf;
+ Reg aClass *cltmp;
+
+ int lc = 0, d_lc = 0, /* local clients */
+ ch = 0, d_ch = 0, /* channels */
+ lcc = 0, d_lcc = 0, /* local client conf links */
+ rc = 0, d_rc = 0, /* remote clients */
+ us = 0, d_us = 0, /* user structs */
+ chu = 0, d_chu = 0, /* channel users */
+ chi = 0, d_chi = 0, /* channel invites */
+ chb = 0, d_chb = 0, /* channel bans */
+ chh = 0, d_chh = 0, /* channel in history */
+ wwu = 0, d_wwu = 0, /* whowas users */
+ cl = 0, d_cl = 0, /* classes */
+ co = 0, d_co = 0; /* conf lines */
+
+ int usi = 0, d_usi = 0, /* users invited */
+ usc = 0, d_usc = 0, /* users in channels */
+ aw = 0, d_aw = 0, /* aways set */
+ wwa = 0, d_wwa = 0, /* whowas aways */
+ wwuw = 0, d_wwuw = 0; /* whowas uwas */
+
+ u_long chm = 0, d_chm = 0, /* memory used by channels */
+ chhm = 0, d_chhm = 0, /* memory used by channel in history */
+ chbm = 0, d_chbm = 0, /* memory used by channel bans */
+ lcm = 0, d_lcm = 0, /* memory used by local clients */
+ rcm = 0, d_rcm = 0, /* memory used by remote clients */
+ awm = 0, d_awm = 0, /* memory used by aways */
+ wwam = 0, d_wwam = 0, /* whowas away memory used */
+ wwm = 0, d_wwm = 0, /* whowas array memory used */
+ dm = 0, d_dm = 0, /* delay array memory used */
+ com = 0, d_com = 0, /* memory used by conf lines */
+ db = 0, d_db = 0, /* memory used by dbufs */
+ rm = 0, d_rm = 0, /* res memory used */
+ totcl = 0, d_totcl = 0,
+ totch = 0, d_totch = 0,
+ totww = 0, d_totww = 0,
+ tot = 0, d_tot = 0;
+ time_t start = 0;
+
+ if (debug)
+ {
+ start = time(NULL);
+ count_whowas_memory(&d_wwu, &d_wwa, &d_wwam, &d_wwuw);
+ d_wwm = sizeof(aName) * ww_size;
+ d_dm = sizeof(aLock) * lk_size;
+ }
+ wwu = istat.is_wwusers;
+ wwa = istat.is_wwaways;
+ wwam = istat.is_wwawaysmem;
+ wwuw = istat.is_wwuwas;
+ wwm = sizeof(aName) * ww_size;
+ dm = sizeof(aLock) * lk_size;
+
+ /*lc = istat.is_unknown + istat.is_myclnt + istat.is_serv;*/
+ lc = istat.is_localc;
+ lcc = istat.is_conflink;
+ rc = istat.is_remc;
+ us = istat.is_users;
+ usi = istat.is_useri;
+ usc = istat.is_userc;
+ aw = istat.is_away;
+ awm = istat.is_awaymem;
+
+ if (debug)
+ for (acptr = client; acptr; acptr = acptr->next)
+ {
+ if (MyConnect(acptr))
+ {
+ d_lc++;
+ for (link =acptr->confs; link; link=link->next)
+ d_lcc++;
+ }
+ else
+ d_rc++;
+ if (acptr->user)
+ {
+ d_us++;
+ for (link = acptr->user->invited; link;
+ link = link->next)
+ d_usi++;
+ d_usc += acptr->user->joined;
+ if (acptr->user->away)
+ {
+ d_aw++;
+ d_awm += (strlen(acptr->user->away)+1);
+ }
+ }
+ }
+
+ lcm = lc * CLIENT_LOCAL_SIZE;
+ rcm = rc * CLIENT_REMOTE_SIZE;
+
+ d_lcm = d_lc * CLIENT_LOCAL_SIZE;
+ d_rcm = d_rc * CLIENT_REMOTE_SIZE;
+
+ ch = istat.is_chan;
+ chm = istat.is_chanmem;
+ chh = istat.is_hchan;
+ chhm = istat.is_hchanmem;
+ chi = istat.is_invite;
+ chb = istat.is_bans;
+ chbm = istat.is_banmem + chb * sizeof(Link);
+ chu = istat.is_chanusers;
+
+ if (debug)
+ {
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ {
+ if (chptr->users == 0)
+ {
+ d_chh++;
+ d_chhm+=strlen(chptr->chname)+sizeof(aChannel);
+ }
+ else
+ {
+ d_ch++;
+ d_chm += (strlen(chptr->chname) +
+ sizeof(aChannel));
+ }
+ for (link = chptr->members; link; link = link->next)
+ d_chu++;
+ for (link = chptr->invites; link; link = link->next)
+ d_chi++;
+ for (link = chptr->mlist; link; link = link->next)
+ {
+ d_chb++;
+ d_chbm += strlen(link->value.cp) + 1;
+ }
+ }
+ d_chbm += d_chb * sizeof(Link);
+ }
+
+ co = istat.is_conf;
+ com = istat.is_confmem;
+ cl = istat.is_class;
+
+ if (debug)
+ {
+ for (aconf = conf; aconf; aconf = aconf->next)
+ {
+ d_co++;
+ d_com += aconf->host ? strlen(aconf->host)+1 : 0;
+ d_com += aconf->passwd ? strlen(aconf->passwd)+1 : 0;
+ d_com += aconf->name ? strlen(aconf->name)+1 : 0;
+ d_com += aconf->ping ? sizeof(*aconf->ping) : 0;
+ d_com += sizeof(aConfItem);
+ }
+ for (cltmp = classes; cltmp; cltmp = cltmp->next)
+ d_cl++;
+ }
+
+ if (debug)
+ sendto_one(cptr, ":%s %d %s :Request processed in %u seconds",
+ me.name, RPL_STATSDEBUG, nick, time(NULL) - start);
+
+ sendto_one(cptr,
+ ":%s %d %s :Client Local %d(%d) Remote %d(%d) Auth %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, lc, lcm, rc, rcm,
+ istat.is_auth, istat.is_authmem);
+ if (debug
+ && (lc != d_lc || lcm != d_lcm || rc != d_rc || rcm != d_rcm))
+ sendto_one(cptr,
+ ":%s %d %s :Client Local %d(%d) Remote %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_lc, d_lcm, d_rc,
+ d_rcm);
+ sendto_one(cptr,
+ ":%s %d %s :Users %d in/visible %d/%d(%d) Invites %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, us, istat.is_user[1],
+ istat.is_user[0], us*sizeof(anUser), usi,
+ usi*sizeof(Link));
+ if (debug && (us != d_us || usi != d_usi))
+ sendto_one(cptr,
+ ":%s %d %s :Users %d(%d) Invites %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_us,
+ d_us*sizeof(anUser), d_usi, d_usi * sizeof(Link));
+ sendto_one(cptr, ":%s %d %s :User channels %d(%d) Aways %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, usc, usc*sizeof(Link),
+ aw, awm);
+ if (debug && (usc != d_usc || aw != d_aw || awm != d_awm))
+ sendto_one(cptr,
+ ":%s %d %s :User channels %d(%d) Aways %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_usc,
+ d_usc*sizeof(Link), d_aw, d_awm);
+ sendto_one(cptr, ":%s %d %s :Attached confs %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, lcc, lcc*sizeof(Link));
+ if (debug && lcc != d_lcc)
+ sendto_one(cptr, ":%s %d %s :Attached confs %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_lcc,
+ d_lcc*sizeof(Link));
+
+ totcl = lcm + rcm + us*sizeof(anUser) + usc*sizeof(Link) + awm;
+ totcl += lcc*sizeof(Link) + usi*sizeof(Link);
+ d_totcl = d_lcm + d_rcm + d_us*sizeof(anUser) + d_usc*sizeof(Link);
+ d_totcl += d_awm + d_lcc*sizeof(Link) + d_usi*sizeof(Link);
+
+ sendto_one(cptr, ":%s %d %s :Conflines %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, co, com);
+ if (debug && (co != d_co || com != d_com))
+ sendto_one(cptr, ":%s %d %s :Conflines %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_co, d_com);
+
+ sendto_one(cptr, ":%s %d %s :Classes %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, cl, cl*sizeof(aClass));
+ if (debug && cl != d_cl)
+ sendto_one(cptr, ":%s %d %s :Classes %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_cl,
+ d_cl*sizeof(aClass));
+
+ sendto_one(cptr,
+ ":%s %d %s :Channels %d(%d) Modes %d(%d) History %d(%d) Cache %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, ch, chm, chb, chbm, chh,
+ chhm, istat.is_cchan, istat.is_cchanmem);
+ if (debug && (ch != d_ch || chm != d_chm || chb != d_chb
+ || chbm != d_chbm || chh != d_chh || chhm != d_chhm))
+ sendto_one(cptr,
+ ":%s %d %s :Channels %d(%d) Modes %d(%d) History %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_ch, d_chm, d_chb,
+ d_chbm, d_chh, d_chhm);
+ sendto_one(cptr, ":%s %d %s :Channel members %d(%d) invite %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, chu, chu*sizeof(Link),
+ chi, chi*sizeof(Link));
+ if (debug && (chu != d_chu || chi != d_chi))
+ sendto_one(cptr,
+ ":%s %d %s :Channel members %d(%d) invite %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_chu, d_chu*sizeof(Link),
+ d_chi, d_chi*sizeof(Link));
+
+ totch = chm + chhm + chbm + chu*sizeof(Link) + chi*sizeof(Link);
+ d_totch = d_chm + d_chhm + d_chbm + d_chu*sizeof(Link)
+ + d_chi*sizeof(Link);
+
+ sendto_one(cptr,
+ ":%s %d %s :Whowas users %d(%d) away %d(%d) links %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, wwu, wwu*sizeof(anUser),
+ wwa, wwam, wwuw, wwuw*sizeof(Link));
+ if (debug && (wwu != d_wwu || wwa != d_wwa || wwam != d_wwam
+ || wwuw != d_wwuw))
+ sendto_one(cptr,
+ ":%s %d %s :Whowas users %d(%d) away %d(%d) links %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_wwu, d_wwu*sizeof(anUser),
+ d_wwa, d_wwam, d_wwuw, d_wwuw*sizeof(Link));
+ sendto_one(cptr, ":%s %d %s :Whowas array %d(%d) Delay array %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, ww_size, wwm, lk_size, dm);
+ if (debug && (wwm != d_wwm || dm != d_dm))
+ sendto_one(cptr,
+ ":%s %d %s :Whowas array %d(%d) Delay array %d(%d) [REAL]",
+ me.name, RPL_STATSDEBUG, nick, ww_size, d_wwm, lk_size,
+ d_dm);
+
+ totww = wwu*sizeof(anUser) + wwam + wwm;
+ d_totww = d_wwu*sizeof(anUser) + d_wwam + d_wwm;
+
+ sendto_one(cptr, ":%s %d %s :Hash: client %d(%d) chan %d(%d)",
+ me.name, RPL_STATSDEBUG, nick, _HASHSIZE,
+ sizeof(aHashEntry) * _HASHSIZE,
+ _CHANNELHASHSIZE, sizeof(aHashEntry) * _CHANNELHASHSIZE);
+ d_db = db = istat.is_dbufnow * sizeof(dbufbuf);
+ db = istat.is_dbufnow * sizeof(dbufbuf);
+ sendto_one(cptr,
+ ":%s %d %s :Dbuf blocks %u(%d) (> %u [%u]) (%u < %u) [%u]",
+ me.name, RPL_STATSDEBUG, nick, istat.is_dbufnow, db,
+ istat.is_dbuf,
+ (u_int) (((u_int)BUFFERPOOL) / ((u_int)sizeof(dbufbuf))),
+ istat.is_dbufuse, istat.is_dbufmax, istat.is_dbufmore);
+
+ d_rm = rm = cres_mem(cptr, nick);
+
+ tot = totww + totch + totcl + com + cl*sizeof(aClass) + db + rm;
+ tot += sizeof(aHashEntry) * _HASHSIZE;
+ tot += sizeof(aHashEntry) * _CHANNELHASHSIZE;
+ d_tot = d_totww + d_totch + d_totcl + d_com + d_cl*sizeof(aClass);
+ d_tot += d_db + d_rm;
+ d_tot += sizeof(aHashEntry) * _HASHSIZE;
+ d_tot += sizeof(aHashEntry) * _CHANNELHASHSIZE;
+
+ sendto_one(cptr, ":%s %d %s :Total: ww %d ch %d cl %d co %d db %d",
+ me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, com,db);
+ if (debug && tot != d_tot)
+ {
+ sendto_one(cptr,
+ ":%s %d %s :Total: ww %d ch %d cl %d co %d db %d [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_totww, d_totch, d_totcl,
+ d_com, d_db);
+ sendto_one(cptr, ":%s %d %s :TOTAL: %d [REAL]",
+ me.name, RPL_STATSDEBUG, nick, d_tot);
+ }
+ sendto_one(cptr, ":%s %d %s :TOTAL: %d sbrk(0)-etext: %u",
+ me.name, RPL_STATSDEBUG, nick, tot,
+ (u_long)sbrk((size_t)0)-(u_long)sbrk0);
+ return;
+}
diff --git a/ircd/s_debug_ext.h b/ircd/s_debug_ext.h
new file mode 100644
index 0000000..a2b2df5
--- /dev/null
+++ b/ircd/s_debug_ext.h
@@ -0,0 +1,45 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_debug_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_debug.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef S_DEBUG_C
+extern char serveropts[];
+#endif /* S_DEBUG_C */
+
+/* External definitions for global functions.
+ */
+#ifndef S_DEBUG_C
+#define EXTERN extern
+#else /* S_DEBUG_C */
+#define EXTERN
+#endif /* S_DEBUG_C */
+#if ! USE_STDARG
+EXTERN void debug();
+#else /* USE_STDARG */
+EXTERN void debug (int level, char *form, ...);
+#endif /* USE_STDARG */
+EXTERN void send_usage __P((aClient *cptr, char *nick));
+EXTERN void send_defines __P((aClient *cptr, char *nick));
+EXTERN void count_memory __P((aClient *cptr, char *nick, int debug));
+#undef EXTERN
diff --git a/ircd/s_defines.h b/ircd/s_defines.h
new file mode 100644
index 0000000..19f0d07
--- /dev/null
+++ b/ircd/s_defines.h
@@ -0,0 +1,41 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_defines.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file includes all files defining constants, macros and types
+ definitions used by the IRC server.
+ */
+
+#include "config.h"
+#include "patchlevel.h"
+
+#include "common_def.h"
+#include "dbuf_def.h"
+#include "class_def.h"
+#include "struct_def.h"
+#include "msg_def.h"
+#include "numeric_def.h"
+#include "support_def.h"
+#include "channel_def.h"
+#include "hash_def.h"
+#include "res_def.h"
+#include "whowas_def.h"
+#include "service_def.h"
+#include "sys_def.h"
+#include "resolv_def.h"
+#include "nameser_def.h"
diff --git a/ircd/s_err.c b/ircd/s_err.c
new file mode 100644
index 0000000..319e7b5
--- /dev/null
+++ b/ircd/s_err.c
@@ -0,0 +1,436 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_err.c,v 1.24 1999/02/22 21:38:33 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_ERR_C
+#include "s_externs.h"
+#undef S_ERR_C
+
+typedef struct {
+ int num_val;
+ char *num_form;
+} Numeric;
+
+static char *prepbuf __P((char *, char *, char *, int, char *));
+static char numbuff[512];
+
+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 aoOirw abeiIklmnoOpqrstv" },
+/* 005 */ { RPL_BOUNCE, ":Try server %s, port %d" },
+ { 0, (char *)NULL }
+};
+
+static Numeric numeric_errors[] = {
+/* 401 */ { ERR_NOSUCHNICK, "%s :No such nick/channel" },
+/* 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 :%s recipients. %s" },
+/* 408 */ { ERR_NOSUCHSERVICE, "%s :No such service" },
+/* 409 */ { ERR_NOORIGIN, ":No origin specified" },
+ { 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 */ { ERR_BADMASK, "%s :Bad Server/host mask" },
+/* 416 */ { ERR_TOOMANYMATCHES, "%s %s :Output too long (try locally)" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 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 */ { ERR_FILEERROR, ":File error doing %s on %s" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 431 */ { ERR_NONICKNAMEGIVEN, ":No nickname given" },
+/* 432 */ { ERR_ERRONEUSNICKNAME, "%s :Erroneous Nickname" },
+/* 433 */ { ERR_NICKNAMEINUSE, "%s :Nickname is already in use." },
+/* 434 */ { ERR_SERVICENAMEINUSE, (char *)NULL },
+/* 435 */ { ERR_SERVICECONFUSED, (char *)NULL },
+/* 436 */ { ERR_NICKCOLLISION, "%s :Nickname collision KILL from %s@%s" },
+/* 437 */ { ERR_UNAVAILRESOURCE,
+ "%s :Nick/channel is temporarily unavailable" },
+/* 438 */ { 0, (char *)NULL }, /* reserved for later use -krys */
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { ERR_USERNOTINCHANNEL, "%s %s :They aren't on that channel" },
+ { ERR_NOTONCHANNEL, "%s :You're not on that channel" },
+/* 443 */ { ERR_USERONCHANNEL, "%s %s :is already on channel" },
+/* 444 */ { ERR_NOLOGIN, "%s :User not logged in" },
+#ifndef ENABLE_SUMMON
+/* 445 */ { ERR_SUMMONDISABLED, ":SUMMON has been disabled" },
+#else
+ { 0, (char *)NULL },
+#endif
+#ifndef ENABLE_USERS
+/* 446 */ { ERR_USERSDISABLED, ":USERS has been disabled" },
+#else
+ { 0, (char *)NULL },
+#endif
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 451 */ { ERR_NOTREGISTERED, ":You have not registered" },
+ { 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 },
+/* 461 */ { ERR_NEEDMOREPARAMS, "%s :Not enough parameters" },
+/* 462 */ { ERR_ALREADYREGISTRED,
+ ":Unauthorized command (already registered)" },
+/* 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" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 471 */ { ERR_CHANNELISFULL, "%s :Cannot join channel (+l)" },
+/* 472 */ { ERR_UNKNOWNMODE , "%c :is unknown mode char to me for %s" },
+/* 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 */ { ERR_NOCHANMODES, "%s :Channel doesn't support modes" },
+/* 478 */ { ERR_BANLISTFULL, "%s %s :Channel list is full" },
+ { 0, (char *)NULL },
+ { 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, "%s :You can't kill a server!" },
+/* 484 */ { ERR_RESTRICTED, ":Your connection is restricted!" },
+/* 485 */ { ERR_UNIQOPRIVSNEEDED,
+ ":You're not the original channel operator" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 491 */ { ERR_NOOPERHOST, ":No O-lines for your host" },
+/* 492 */ { ERR_NOSERVICEHOST, (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 },
+/* 501 */ { ERR_UMODEUNKNOWNFLAG, ":Unknown MODE flag" },
+/* 502 */ { ERR_USERSDONTMATCH, ":Can't change mode for other users" },
+ { 0, (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" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 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 */ { RPL_WHOISCHANOP, (char *)NULL },
+/* 317 */ { RPL_WHOISIDLE, "%s %ld :seconds idle" },
+/* 318 */ { RPL_ENDOFWHOIS, "%s :End of WHOIS list." },
+/* 319 */ { RPL_WHOISCHANNELS, "%s :%s" },
+ { 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 */ { RPL_UNIQOPIS, "%s %s" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 331 */ { RPL_NOTOPIC, "%s :No topic is set." },
+/* 332 */ { RPL_TOPIC, "%s :%s" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 341 */ { RPL_INVITING, "%s %s" },
+/* 342 */ { RPL_SUMMONING, "%s :User summoned to irc" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 346 */ { RPL_INVITELIST, "%s %s" },
+/* 347 */ { RPL_ENDOFINVITELIST, "%s :End of Channel Invite List" },
+/* 348 */ { RPL_EXCEPTLIST, "%s %s" },
+/* 349 */ { RPL_ENDOFEXCEPTLIST, "%s :End of Channel Exception List" },
+ { 0, (char *)NULL },
+/* 351 */ { RPL_VERSION, "%s.%s %s :%s" },
+/* 352 */ { RPL_WHOREPLY, "%s %s %s %s %s %s :%d %s" },
+/* 353 */ { RPL_NAMREPLY, "%s" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 361 */ { RPL_KILLDONE, (char *)NULL },
+/* 362 */ { RPL_CLOSING, "%s :Closed. Status = %d" },
+/* 363 */ { RPL_CLOSEEND, "%d: Connections Closed" },
+/* 364 */ { RPL_LINKS, "%s %s :%d %s" },
+/* 365 */ { RPL_ENDOFLINKS, "%s :End of LINKS list." },
+/* 366 */ { RPL_ENDOFNAMES, "%s :End of NAMES list." },
+/* 367 */ { RPL_BANLIST, "%s %s" },
+/* 368 */ { RPL_ENDOFBANLIST, "%s :End of Channel Ban List" },
+/* 369 */ { RPL_ENDOFWHOWAS, "%s :End of WHOWAS" },
+ { 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." },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 381 */ { RPL_YOUREOPER, ":You are now an IRC Operator" },
+/* 382 */ { RPL_REHASHING, "%s :Rehashing" },
+/* 383 */ { RPL_YOURESERVICE, ":You are service %s" },
+/* 384 */ { RPL_MYPORTIS, "%d :Port to local server is\r\n" },
+/* 385 */ { RPL_NOTOPERANYMORE, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 391 */ { RPL_TIME, "%s :%s" },
+#ifdef ENABLE_USERS
+/* 392 */ { RPL_USERSSTART, ":UserID Terminal Host" },
+/* 393 */ { RPL_USERS, ":%-8s %-9s %-8s" },
+/* 394 */ { RPL_ENDOFUSERS, ":End of Users" },
+/* 395 */ { RPL_NOUSERS, ":Nobody logged in." },
+#else
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+#endif
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 200 */ { RPL_TRACELINK, "Link %s%s %s %s V%X%s %d %d %d" },
+/* 201 */ { RPL_TRACECONNECTING, "Try. %d %s" },
+/* 202 */ { RPL_TRACEHANDSHAKE, "H.S. %d %s" },
+/* 203 */ { RPL_TRACEUNKNOWN, "???? %d %s" },
+/* 204 */ { RPL_TRACEOPERATOR, "Oper %d %s" },
+/* 205 */ { RPL_TRACEUSER, "User %d %s" },
+/* 206 */ { RPL_TRACESERVER, "Serv %d %dS %dC %s %s!%s@%s V%X%s" },
+/* 207 */ { RPL_TRACESERVICE, "Service %d %s 0x%X 0x%X" },
+/* 208 */ { RPL_TRACENEWTYPE, "<newtype> 0 %s" },
+/* 209 */ { RPL_TRACECLASS, "Class %d %d" },
+/* 210 */ { RPL_TRACERECONNECT, "Retry. %d %s" },
+/* 211 */ { RPL_STATSLINKINFO, (char *)NULL },
+/* 212 */ { RPL_STATSCOMMANDS, "%s %u %u %u" },
+/* 213 */ { RPL_STATSCLINE, "%c %s %s %s %d %d" },
+/* 214 */ { RPL_STATSNLINE, "%c %s %s %s %d %d" },
+/* 215 */ { RPL_STATSILINE, "%c %s %s %s %d %d" },
+/* 216 */ { RPL_STATSKLINE, "%c %s %s %s %d %d" },
+/* 217 */ { RPL_STATSQLINE, "%c %s %s %s %d %d" },
+/* 218 */ { RPL_STATSYLINE, "%c %d %d %d %d %ld %d.%d %d.%d" },
+/* 219 */ { RPL_ENDOFSTATS, "%c :End of STATS report" },
+ { 0, (char *)NULL },
+/* 221 */ { RPL_UMODEIS, "%s" },
+ { 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 },
+/* 231 */ { RPL_SERVICEINFO, (char *)NULL },
+/* 232 */ { RPL_ENDOFSERVICES, (char *)NULL },
+/* 233 */ { RPL_SERVICE, (char *)NULL },
+/* 234 */ { RPL_SERVLIST, "%s %s %s 0x%X %d :%s" },
+/* 235 */ { RPL_SERVLISTEND, "%s %d :End of service listing" },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+ { 0, (char *)NULL },
+/* 240 */ { RPL_STATSVLINE, "%c %s %s %s %d %d" },
+/* 241 */ { RPL_STATSLLINE, "%c %s %s %s %d %d" },
+/* 242 */ { RPL_STATSUPTIME, ":Server Up %d days, %d:%02d:%02d" },
+/* 243 */ { RPL_STATSOLINE, "%c %s %s %s %d %d" },
+/* 244 */ { RPL_STATSHLINE, "%c %s %s %s %d %d" },
+/* 245 */ { RPL_STATSSLINE, "%c %s %s %s 0x%X %d" },
+/* 246 */ { RPL_STATSPING, "%s %d %d %d %d" },
+/* 247 */ { RPL_STATSBLINE, "%c %s %s %s %d %d" },
+ { 0, (char *)NULL }, /* RPL_STATSDEFINE */
+ { 0, (char *)NULL }, /* RPL_STATSDEBUG */
+/* 250 */ { RPL_STATSDLINE, "%c %s %s %s %d %d" },
+/* 251 */ { RPL_LUSERCLIENT,
+ ":There are %d users and %d services on %d servers" },
+/* 252 */ { RPL_LUSEROP, "%d :operators online" },
+/* 253 */ { RPL_LUSERUNKNOWN, "%d :unknown connections" },
+/* 254 */ { RPL_LUSERCHANNELS, "%d :channels formed" },
+/* 255 */ { RPL_LUSERME, ":I have %d clients, %d services and %d servers" },
+/* 256 */ { RPL_ADMINME, ":Administrative info about %s" },
+/* 257 */ { RPL_ADMINLOC1, ":%s" },
+/* 258 */ { RPL_ADMINLOC2, ":%s" },
+/* 259 */ { RPL_ADMINEMAIL, ":%s" },
+ { 0, (char *)NULL },
+/* 261 */ { RPL_TRACELOG, "File %s %d" },
+/* 262 */ { RPL_TRACEEND, "%s %s.%s :End of TRACE" },
+/* 263 */ { RPL_TRYAGAIN, "%s :Please wait a while and try again." },
+ { 0, (char *)NULL }
+};
+
+char *err_str(numeric, to)
+int numeric;
+char *to;
+{
+ Reg Numeric *nptr;
+ Reg int num = numeric;
+
+ if (BadPtr(to)) /* for unregistered clients */
+ to = "*";
+
+ num -= numeric_errors[0].num_val;
+ if (num < 0 || num > ERR_USERSDONTMATCH)
+ SPRINTF(numbuff,
+ ":%%s %d %%s :INTERNAL ERROR: BAD NUMERIC! %d",
+ numeric, num);
+ else
+ {
+ nptr = &numeric_errors[num];
+ Debug((DEBUG_NUM,
+ "err_str: to %s #%d num %d nptr %#x %d [%s]", to,
+ numeric, num, nptr, nptr->num_val, nptr->num_form));
+ if (!nptr->num_form || !nptr->num_val)
+ SPRINTF(numbuff,
+ ":%%s %d %%s :NO ERROR FOR NUMERIC ERROR %d",
+ numeric, num);
+ else
+ (void)prepbuf(numbuff, ME, to, nptr->num_val,
+ nptr->num_form);
+ }
+ return numbuff;
+}
+
+
+char *rpl_str(numeric, to)
+int numeric;
+char *to;
+{
+ Reg Numeric *nptr;
+ Reg int num = numeric;
+
+ if (num > 5)
+ num -= (num > 300) ? 300 : 100;
+
+ if (BadPtr(to)) /* for unregistered clients */
+ to = "*";
+
+ if (num < 0 || num > 200)
+ SPRINTF(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: to %s #%d num %d nptr %#x %d [%s]", to,
+ numeric, num, nptr, nptr->num_val, nptr->num_form));
+ if (!nptr->num_form || !nptr->num_val)
+ SPRINTF(numbuff,
+ ":%%s %d %%s :NO REPLY FOR NUMERIC ERROR %d",
+ numeric, num);
+ else
+ (void)prepbuf(numbuff, ME, to, nptr->num_val,
+ nptr->num_form);
+ }
+ return numbuff;
+}
+
+static char *prepbuf(buffer, from, to, num, tail)
+char *buffer;
+Reg int num;
+char *from, *to, *tail;
+{
+ Reg char *s = buffer;
+
+ *s++ = ':';
+ (void)strcpy(s, from);
+ (void)strcat(s, " ");
+ s += strlen(s);
+
+ *s++ = '0' + num/100;
+ num %= 100;
+ *s++ = '0' + num/10;
+ *s++ = '0' + num%10;
+ *s++ = ' ';
+ (void)strcpy(s, to);
+ s += strlen(s);
+ *s++ = ' ';
+ (void)strcpy(s, tail);
+ return buffer;
+}
diff --git a/ircd/s_err_ext.h b/ircd/s_err_ext.h
new file mode 100644
index 0000000..e5d0534
--- /dev/null
+++ b/ircd/s_err_ext.h
@@ -0,0 +1,33 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_err_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_err.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef S_ERR_C
+#define EXTERN extern
+#else /* S_ERR_C */
+#define EXTERN
+#endif /* S_ERR_C */
+EXTERN char *err_str __P((int numeric, char *to));
+EXTERN char *rpl_str __P((int numeric, char *to));
+#undef EXTERN
diff --git a/ircd/s_externs.h b/ircd/s_externs.h
new file mode 100644
index 0000000..d0ef238
--- /dev/null
+++ b/ircd/s_externs.h
@@ -0,0 +1,53 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_externs.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file includes all *_ext.h files containing external declarations
+ * for the IRC server.
+ */
+
+#include "bsd_ext.h"
+#include "channel_ext.h"
+#include "class_ext.h"
+#include "dbuf_ext.h"
+#include "hash_ext.h"
+#include "ircd_ext.h"
+#include "list_ext.h"
+#include "match_ext.h"
+#include "packet_ext.h"
+#include "parse_ext.h"
+#include "res_comp_ext.h"
+#include "res_ext.h"
+#include "res_init_ext.h"
+#include "res_mkquery_ext.h"
+#include "s_auth_ext.h"
+#include "s_bsd_ext.h"
+#include "s_conf_ext.h"
+#include "s_debug_ext.h"
+#include "s_err_ext.h"
+#include "s_misc_ext.h"
+#include "s_numeric_ext.h"
+#include "s_serv_ext.h"
+#include "s_service_ext.h"
+#include "s_user_ext.h"
+#include "s_zip_ext.h"
+#include "s_id_ext.h"
+#include "send_ext.h"
+#include "support_ext.h"
+#include "version_ext.h"
+#include "whowas_ext.h"
diff --git a/ircd/s_id.c b/ircd/s_id.c
new file mode 100644
index 0000000..5feb948
--- /dev/null
+++ b/ircd/s_id.c
@@ -0,0 +1,208 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_id.c
+ * Copyright (C) 1998 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: s_id.c,v 1.9 1999/07/27 11:26:37 chopin Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_ID_C
+#include "s_externs.h"
+#undef S_ID_C
+
+/*
+ * channel IDs
+ */
+#define CHIDNB 36
+
+static unsigned char id_alphabet[CHIDNB+1] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890A";
+
+static unsigned int alphabet_id[256] =
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ /* 0 */ 35, /* 1 */ 26, /* 2 */ 27, /* 3 */ 28, /* 4 */ 29,
+ /* 5 */ 30, /* 6 */ 31, /* 7 */ 32, /* 8 */ 33, /* 9 */ 34,
+ -1, -1, -1, -1, -1, -1, -1,
+ /* A */ 0, /* B */ 1, /* C */ 2, /* D */ 3, /* E */ 4, /* F */ 5,
+ /* G */ 6, /* H */ 7, /* I */ 8, /* J */ 9, /* K */ 10, /* L */ 11,
+ /* M */ 12, /* N */ 13, /* O */ 14, /* P */ 15, /* Q */ 16,
+ /* R */ 17, /* S */ 18, /* T */ 19, /* U */ 20, /* V */ 21,
+ /* W */ 22, /* X */ 23, /* Y */ 24, /* Z */ 25,
+ -1, -1, -1, -1, -1, -1,
+ /* a */ 0, /* b */ 1, /* c */ 2, /* d */ 3, /* e */ 4, /* f */ 5,
+ /* g */ 6, /* h */ 7, /* i */ 8, /* j */ 9, /* k */ 10, /* l */ 11,
+ /* m */ 12, /* n */ 13, /* o */ 14, /* p */ 15, /* q */ 16,
+ /* r */ 17, /* s */ 18, /* t */ 19, /* u */ 20, /* v */ 21,
+ /* w */ 22, /* x */ 23, /* y */ 24, /* z */ 25,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1 };
+
+/* ltoid: base 10 -> base 36 conversion */
+static char *
+ltoid(l)
+time_t l;
+{
+ static char idrpl[CHIDLEN+1];
+ char c = CHIDLEN-1;
+
+ idrpl[CHIDLEN] = '\0';
+ do
+ {
+ idrpl[c] = id_alphabet[1 + l % CHIDNB];
+ l /= CHIDNB;
+ }
+ while (c-- > 0);
+ return (char *) idrpl;
+}
+
+/* idtol: base 36 -> base 10 conversion */
+static unsigned long
+idtol(id)
+char *id;
+{
+ unsigned long l = 0;
+ char c = CHIDLEN-1;
+
+ l = alphabet_id[*id++];
+ while (c-- > 0)
+ l = l * CHIDNB + alphabet_id[*id++];
+ return l;
+}
+
+/* get_chid: give the current id */
+char *
+get_chid()
+{
+ return ltoid(time(NULL));
+}
+
+/* close_chid: is the ID in the close future? (written for CHIDLEN == 5) */
+int
+close_chid(id)
+char *id;
+{
+ static time_t last = 0;
+ static char current;
+ char *curid;
+
+ if (timeofday - last > 900 || id[0] == current)
+ {
+ last = timeofday;
+ curid = get_chid();
+ current = curid[0];
+ }
+ if (id_alphabet[1 + alphabet_id[current]] == id[1])
+ return 1;
+ if (id[0] == current &&
+ idtol(id) >= (timeofday % (u_int) pow(CHIDNB, CHIDLEN)))
+ return 1;
+ return 0;
+}
+
+aChannel *idcache = NULL;
+
+/* cache_chid: add a channel to the list of cached names */
+void
+cache_chid(chptr)
+aChannel *chptr;
+{
+ /*
+ ** caching should be limited to the minimum,
+ ** for memory reasons, but most importantly for
+ ** user friendly-ness.
+ ** Is the logic here right, tho?
+ */
+ if (chptr->history == 0 ||
+ (timeofday - chptr->history) >LDELAYCHASETIMELIMIT+DELAYCHASETIMELIMIT)
+ {
+ MyFree((char *)chptr);
+ return;
+ }
+
+ chptr->nextch = idcache;
+ idcache = chptr;
+ istat.is_cchan++;
+ istat.is_cchanmem -= sizeof(aChannel) + strlen(chptr->chname);
+}
+
+/* check_chid: checks if a (short) channel name is in the cache
+ * returns: 0 if not, 1 if yes
+ */
+int
+check_chid(name)
+char *name;
+{
+ aChannel *chptr = idcache;
+
+ while (chptr)
+ {
+ if (!strcasecmp(name, chptr->chname+1+CHIDLEN))
+ return 1;
+ chptr = chptr->nextch;
+ }
+ return 0;
+}
+
+/* collect_chid: remove expired entries from the cache */
+void
+collect_chid()
+{
+ aChannel **chptr = &idcache, *del;
+
+ while (*chptr)
+ {
+ if (close_chid((*chptr)->chname) == 0)
+ {
+ del = *chptr;
+ *chptr = del->nextch;
+ istat.is_cchan--;
+ istat.is_cchanmem -= sizeof(aChannel) +strlen(del->chname);
+ MyFree((char *)del);
+ }
+ else
+ chptr = &((*chptr)->nextch);
+ }
+}
+
+/* checks wether the ID is valid */
+int
+cid_ok(name)
+char *name;
+{
+ int l = 1;
+
+ while (l <= CHIDLEN)
+ {
+ if (alphabet_id[name[l]] == -1)
+ return 0;
+ l += 1;
+ }
+ if (l != CHIDLEN+1)
+ return 0;
+ return 1;
+}
diff --git a/ircd/s_id_ext.h b/ircd/s_id_ext.h
new file mode 100644
index 0000000..e55ca33
--- /dev/null
+++ b/ircd/s_id_ext.h
@@ -0,0 +1,43 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_id_ext.h
+ * Copyright (C) 1998 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.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_id.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef S_ID_C
+/* none */
+#endif /* S_ID_C */
+
+/* External definitions for global functions.
+ */
+#ifndef S_ID_C
+#define EXTERN extern
+#else /* S_ID_C */
+#define EXTERN
+#endif /* S_ID_C */
+EXTERN char *get_chid __P(());
+EXTERN int close_chid __P((char *));
+EXTERN void cache_chid __P((aChannel *));
+EXTERN int check_chid __P((char *));
+EXTERN void collect_chid __P(());
+EXTERN int cid_ok __P((char *));
+#undef EXTERN
diff --git a/ircd/s_misc.c b/ircd/s_misc.c
new file mode 100644
index 0000000..17c9946
--- /dev/null
+++ b/ircd/s_misc.c
@@ -0,0 +1,998 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_misc.c,v 1.30 1999/07/21 22:57:39 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_MISC_C
+#include "s_externs.h"
+#undef S_MISC_C
+
+static void exit_one_client __P((aClient *,aClient *,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(clock)
+time_t clock;
+{
+ static char buf[80], plus;
+ Reg struct tm *lt, *gm;
+ struct tm gmbuf;
+ int minswest;
+
+ if (!clock)
+ time(&clock);
+ gm = gmtime(&clock);
+ bcopy((char *)gm, (char *)&gmbuf, 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 = ((lt->tm_hour + 24) - gm->tm_hour) * 60;
+
+ plus = (minswest > 0) ? '-' : '+';
+ if (minswest < 0)
+ minswest = -minswest;
+
+ (void)sprintf(buf, "%s %s %d %d -- %02d:%02d %c%02d:%02d",
+ weekdays[lt->tm_wday], months[lt->tm_mon],lt->tm_mday,
+ lt->tm_year + 1900, lt->tm_hour, lt->tm_min,
+ plus, minswest/60, minswest%60);
+
+ return buf;
+}
+
+/*
+** check_registered_user is used to cancel message, if the
+** originator is a server or not registered yet. In other
+** words, passing this test, *MUST* guarantee that the
+** sptr->user exists (not checked after this--let there
+** be coredumps to catch bugs... this is intentional --msa ;)
+**
+** There is this nagging feeling... should this NOT_REGISTERED
+** error really be sent to remote users? This happening means
+** that remote servers have this user registered, althout this
+** one has it not... Not really users fault... Perhaps this
+** error message should be restricted to local clients and some
+** other thing generated for remotes...
+*/
+int check_registered_user(sptr)
+aClient *sptr;
+{
+ if (!IsRegisteredUser(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTREGISTERED, "*"));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+** check_registered user cancels message, if 'x' is not
+** registered (e.g. we don't know yet whether a server
+** or user)
+*/
+int check_registered(sptr)
+aClient *sptr;
+{
+ if (!IsRegistered(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTREGISTERED, "*"));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+** check_registered_service cancels message, if 'x' is not
+** a registered service.
+*/
+int check_registered_service(sptr)
+aClient *sptr;
+{
+ if (!IsService(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOTREGISTERED, "*"));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+** 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[username@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(sptr, showip)
+aClient *sptr;
+int showip;
+{
+ static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+ if (MyConnect(sptr))
+ {
+ if (IsUnixSocket(sptr))
+ {
+ if (showip)
+ SPRINTF(nbuf, "%s[%s]",
+ sptr->name, sptr->sockhost);
+ else
+ SPRINTF(nbuf, "%s[%s]",
+ sptr->name, me.sockhost);
+ }
+ else
+ {
+ if (showip)
+ (void)sprintf(nbuf, "%s[%.*s@%s]",
+ sptr->name, USERLEN,
+ (!(sptr->flags & FLAGS_GOTID)) ? "" :
+ sptr->auth,
+#ifdef INET6
+ inetntop(AF_INET6,
+ (char *)&sptr->ip,
+ mydummy, MYDUMMY_SIZE));
+#else
+ inetntoa((char *)&sptr->ip));
+#endif
+ else
+ {
+ if (mycmp(sptr->name, sptr->sockhost))
+ /* Show username for clients and
+ * ident for others.
+ */
+ SPRINTF(nbuf, "%s[%.*s@%s]",
+ sptr->name, USERLEN,
+ IsPerson(sptr) ?
+ sptr->user->username :
+ sptr->auth,
+ sptr->sockhost);
+ else
+ return sptr->name;
+ }
+ }
+ return nbuf;
+ }
+ return sptr->name;
+}
+
+char *get_client_host(cptr)
+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(nbuf, "%s[%s]", cptr->name, ME);
+ else
+ (void)sprintf(nbuf, "%s[%-.*s@%-.*s]",
+ cptr->name, USERLEN,
+ (!(cptr->flags & FLAGS_GOTID)) ? "" : cptr->auth,
+ 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(cptr, host)
+Reg aClient *cptr;
+Reg char *host;
+{
+ Reg char *s;
+ if ((s = (char *)index(host, '@')))
+ s++;
+ else
+ s = host;
+ strncpyzt(cptr->sockhost, s, sizeof(cptr->sockhost));
+ Debug((DEBUG_DNS,"get_sockhost %s",s));
+}
+
+/*
+ * Return wildcard name of my server name according to given config entry
+ * --Jto
+ */
+char *my_name_for_link(name, count)
+char *name;
+Reg int count;
+{
+ static char namebuf[HOSTLEN];
+ Reg char *start = name;
+
+ if (count <= 0 || count > 5)
+ return start;
+
+ while (count-- && name)
+ {
+ name++;
+ name = (char *)index(name, '.');
+ }
+ if (!name)
+ return start;
+
+ namebuf[0] = '*';
+ (void)strncpy(&namebuf[1], name, HOSTLEN - 1);
+ namebuf[HOSTLEN - 1] = '\0';
+
+ return namebuf;
+}
+
+/*
+ * Goes thru the list of locally connected servers (except cptr),
+ * check if my neighbours can see the server "name" (or if it is hidden
+ * by a hostmask)
+ * Returns the number of marked servers
+ */
+int
+mark_blind_servers (cptr, name)
+aClient *cptr;
+char *name;
+{
+ Reg int i, j = 0;
+ Reg aClient *acptr;
+ Reg aConfItem *aconf;
+
+ for (i = fdas.highest; i >= 0; i--)
+ {
+ if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr))
+ continue;
+ if (acptr == cptr || IsMe(acptr))
+ {
+ acptr->flags &= ~FLAGS_HIDDEN;
+ continue;
+ }
+ if ((aconf = acptr->serv->nline) &&
+ (match(my_name_for_link(ME, aconf->port), name) == 0))
+ {
+ acptr->flags |= FLAGS_HIDDEN;
+ j++;
+ }
+ }
+ return j;
+}
+
+/*
+** exit_client
+** This is old "m_bye". Name changed, because this is not a
+** protocol function, but a general server utility function.
+**
+** 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.
+**
+** 1) If the client is a local client, then this implicitly
+** exits all other clients depending on this connection (e.g.
+** remote clients having 'from'-field that points to this.
+**
+** 2) If the client is a remote client, then only this is exited.
+**
+** For convenience, this function returns a suitable value for
+** m_funtion return value:
+**
+** FLUSH_BUFFER if (cptr == sptr)
+** 0 if (cptr != sptr)
+*/
+int exit_client(cptr, sptr, from, comment)
+aClient *cptr; /*
+ ** The local client originating the exit or NULL, if this
+ ** exit is generated by this server for internal reasons.
+ ** This will not get any of the generated messages.
+ */
+aClient *sptr; /* Client exiting */
+aClient *from; /* Client firing off this Exit, never NULL! */
+char *comment; /* Reason for the exit */
+ {
+ Reg aClient *acptr;
+ Reg aClient *next;
+ Reg aServer *asptr;
+ Reg aService *asvptr;
+#if defined(FNAME_USERLOG) || defined(USE_SYSLOG) || defined(USE_SERVICES)
+ time_t on_for;
+#endif
+ char comment1[HOSTLEN + HOSTLEN + 2];
+ int flags = 0;
+
+ if (MyConnect(sptr) || (sptr->flags & FLAGS_HELD))
+ {
+ if (sptr->flags & FLAGS_KILLED)
+ {
+ sendto_flag(SCH_LOCAL, "Killed: %s.",
+ get_client_name(sptr, TRUE));
+ sptr->exitc = EXITC_KILL;
+ }
+
+ sptr->flags |= FLAGS_CLOSING;
+#if (defined(FNAME_USERLOG) || defined(FNAME_CONNLOG) \
+ || defined(USE_SERVICES)) \
+ || (defined(USE_SYSLOG) && (defined(SYSLOG_USERS) || defined(SYSLOG_CONN)))
+ if (IsPerson(sptr))
+ {
+ /* It's ugly, it's simple, it's not so important */
+ on_for = timeofday - sptr->firsttime + 1;
+# if defined(USE_SYSLOG) && defined(SYSLOG_USERS)
+ syslog(LOG_NOTICE,
+ "%s (%3d:%02d:%02d): %s@%s [%s] %c\n",
+ myctime(sptr->firsttime),
+ on_for / 3600, (on_for % 3600)/60,
+ on_for % 60,
+ sptr->user->username, sptr->user->host,
+ sptr->auth, sptr->exitc);
+# endif
+# if defined(FNAME_USERLOG) || defined(USE_SERVICES)
+ sendto_flog(sptr, NULL, on_for, sptr->user->username,
+ sptr->user->host);
+# endif
+ }
+ else if (sptr->exitc != EXITC_REF && sptr->exitc != EXITC_AREF)
+ {
+# if defined(USE_SYSLOG) && defined(SYSLOG_CONN)
+ syslog(LOG_NOTICE,
+ "%s ( Unknown ): <none>@%s [%s] %c\n",
+ myctime(sptr->firsttime),
+ (IsUnixSocket(sptr)) ? me.sockhost :
+ ((sptr->hostp) ? sptr->hostp->h_name :
+ sptr->sockhost), sptr->auth, sptr->exitc);
+# endif
+# if defined(FNAME_CONNLOG) || defined(USE_SERVICES)
+ sendto_flog(sptr, " Unknown ", 0, "<none>",
+ (IsUnixSocket(sptr)) ? me.sockhost :
+ ((sptr->hostp) ? sptr->hostp->h_name :
+ sptr->sockhost));
+# endif
+ }
+#endif
+ if (MyConnect(sptr))
+ {
+ if (IsPerson(sptr))
+ istat.is_myclnt--;
+ else if (IsServer(sptr))
+ istat.is_myserv--;
+ else if (IsService(sptr))
+ istat.is_myservice--;
+ else
+ istat.is_unknown--;
+
+ if (cptr != NULL && sptr != cptr)
+ sendto_one(sptr, "ERROR :Closing Link: %s %s (%s)",
+ get_client_name(sptr,FALSE),
+ cptr->name, comment);
+ else
+ sendto_one(sptr, "ERROR :Closing Link: %s (%s)",
+ get_client_name(sptr,FALSE), comment);
+
+ if (sptr->auth != sptr->username)
+ {
+ istat.is_authmem -= sizeof(sptr->auth);
+ istat.is_auth -= 1;
+ MyFree(sptr->auth);
+ sptr->auth = sptr->username;
+ }
+ }
+ /*
+ ** Currently only server connections can have
+ ** depending remote clients here, but it does no
+ ** harm to check for all local clients. In
+ ** future some other clients than servers might
+ ** have remotes too...
+ ** now, I think it harms big client servers... - krys
+ **
+ ** Close the Client connection first and mark it
+ ** so that no messages are attempted to send to it.
+ ** (The following *must* make MyConnect(sptr) == FALSE!).
+ ** It also makes sptr->from == NULL, thus it's unnecessary
+ ** to test whether "sptr != acptr" in the following loops.
+ */
+ close_connection(sptr);
+
+ if (IsServer(sptr))
+ {
+ /*
+ ** First QUIT all NON-servers which are behind this link
+ **
+ ** Note There is no danger of 'cptr' being exited in
+ ** the following loops. 'cptr' is a *local* client,
+ ** all dependants are *remote* clients.
+ */
+
+ /* This next bit is a a bit ugly but all it does is take the
+ ** name of us.. me.name and tack it together with the name of
+ ** the server sptr->name that just broke off and puts this
+ ** together into exit_one_client() to provide some useful
+ ** information about where the net is broken. Ian
+ */
+ (void)strcpy(comment1, ME);
+ (void)strcat(comment1," ");
+ (void)strcat(comment1, sptr->name);
+
+ /* This will quit all the *users*, without checking the
+ ** whole list of clients.
+ */
+ for (asptr = svrtop; asptr; asptr = (aServer *)next)
+ {
+ next = (aClient *)asptr->nexts;
+ if ((asptr->bcptr == NULL) ||
+ (asptr->bcptr->from != sptr
+ && asptr->bcptr != sptr))
+ continue;
+ /*
+ ** This version doesn't need QUITs to be
+ ** propagaged unless the remote server is
+ ** hidden (by a hostmask)
+ */
+ if (mark_blind_servers(NULL,
+ asptr->bcptr->name))
+ flags |= FLAGS_SPLIT | FLAGS_HIDDEN;
+ else
+ flags |= FLAGS_SPLIT;
+ while (GotDependantClient(asptr->bcptr))
+ {
+ acptr = asptr->bcptr->prev;
+ acptr->flags |= flags;
+ exit_one_client(NULL, acptr, &me,
+ comment1);
+ }
+ }
+ /*
+ ** Second SQUIT all servers behind this link
+ */
+ for (asptr = svrtop; asptr; asptr = (aServer *)next)
+ {
+ next = (aClient *)asptr->nexts;
+ if ((acptr = asptr->bcptr) &&
+ acptr->from == sptr)
+ {
+ sendto_flag(SCH_SERVER,
+ "Sending SQUIT %s (%s)",
+ acptr->name, comment);
+ exit_one_client(NULL, acptr, &me, ME);
+ }
+ }
+ } /* If (IsServer(sptr)) */
+ } /* if (MyConnect(sptr) || (sptr->flags & FLAGS_HELD)) */
+
+ if (IsServer(sptr) && GotDependantClient(sptr))
+ {
+ /*
+ ** generate QUITs locally when receiving a SQUIT
+ ** check for hostmasking.
+ */
+ if (mark_blind_servers(cptr, sptr->name))
+ flags = FLAGS_SPLIT | FLAGS_HIDDEN;
+ else
+ flags = FLAGS_SPLIT;
+
+ if (IsServer(from))
+ /* this is a guess */
+ (void)strcpy(comment1, from->name);
+ else
+ /* this is right */
+ (void)strcpy(comment1, sptr->serv->up);
+ (void)strcat(comment1, " ");
+ (void)strcat(comment1, sptr->name);
+
+ while (GotDependantClient(sptr))
+ {
+ acptr = sptr->prev;
+ acptr->flags |= flags;
+ exit_one_client(cptr, acptr, &me, comment1);
+ }
+ }
+
+ /*
+ ** Try to guess from comment if the client is exiting
+ ** normally (KILL or issued QUIT), or if it is splitting
+ ** It requires comment for splitting users to be
+ ** "server.some.where splitting.some.where"
+ */
+ comment1[0] = '\0';
+ if (!IsServer(sptr) && ((sptr->flags & FLAGS_KILLED) == 0))
+ {
+ char *c = comment;
+ int i = 0;
+ while (*c && *c != ' ')
+ if (*c++ == '.')
+ i++;
+ if (*c++ && i)
+ {
+ i = 0;
+ while (*c && *c != ' ')
+ if (*c++ == '.')
+ i++;
+ if (!i || *c)
+ sptr->flags |= FLAGS_QUIT;
+ }
+ else
+ sptr->flags |= FLAGS_QUIT;
+
+ if (sptr == cptr && !(sptr->flags & FLAGS_QUIT))
+ {
+ /*
+ ** This will avoid nick delay to be abused by
+ ** letting local users put a comment looking
+ ** like a server split.
+ */
+ strncpyzt(comment1, comment, HOSTLEN + HOSTLEN);
+ strcat(comment1, " ");
+ sptr->flags |= FLAGS_QUIT;
+ }
+ }
+
+ if (IsServer(sptr) && (cptr == sptr))
+ sendto_flag(SCH_SERVER, "Sending SQUIT %s (%s)",
+ cptr->name, comment);
+
+ exit_one_client(cptr, sptr, from, (*comment1) ? comment1 : comment);
+ return cptr == sptr ? FLUSH_BUFFER : 0;
+ }
+
+/*
+** Exit one client, local or remote. Assuming all dependants have
+** been already removed, and socket closed for local client.
+*/
+static void exit_one_client(cptr, sptr, from, comment)
+aClient *sptr;
+aClient *cptr;
+aClient *from;
+char *comment;
+{
+ Reg aClient *acptr;
+ Reg int i;
+ Reg Link *lp;
+
+ /*
+ ** For a server or user quitting, propagage the information to
+ ** other servers (except to the one where is came from (cptr))
+ */
+ if (IsMe(sptr))
+ {
+ sendto_flag(SCH_ERROR,
+ "ERROR: tried to exit me! : %s", comment);
+ return; /* ...must *never* exit self!! */
+ }
+ else if (IsServer(sptr)) {
+ /*
+ ** Old sendto_serv_but_one() call removed because we now
+ ** need to send different names to different servers
+ ** (domain name matching)
+ */
+ istat.is_serv--;
+ for (i = fdas.highest; i >= 0; i--)
+ {
+ Reg aConfItem *aconf;
+
+ if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) ||
+ acptr == cptr || IsMe(acptr))
+ continue;
+ if ((aconf = acptr->serv->nline) &&
+ (match(my_name_for_link(ME, aconf->port),
+ sptr->name) == 0))
+ continue;
+ sendto_one(acptr, ":%s SQUIT %s :%s",
+ from->name, sptr->name, comment);
+ }
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_SQUIT, sptr->name, sptr,
+ ":%s SQUIT %s :%s", from->name,
+ sptr->name, comment);
+#endif
+ (void) del_from_server_hash_table(sptr->serv, cptr ? cptr :
+ sptr->from);
+ } else if (!IsPerson(sptr) && !IsService(sptr))
+ /* ...this test is *dubious*, would need
+ ** some thougth.. but for now it plugs a
+ ** nasty hole in the server... --msa
+ */
+ ; /* Nothing */
+ else if (sptr->name[0] && !IsService(sptr)) /* clean with QUIT... */
+ {
+ /*
+ ** If this exit is generated from "m_kill", then there
+ ** is no sense in sending the QUIT--KILL's have been
+ ** sent instead.
+ */
+ if ((sptr->flags & FLAGS_KILLED) == 0)
+ {
+ if ((sptr->flags & FLAGS_SPLIT) == 0)
+ {
+ sendto_serv_butone(cptr, ":%s QUIT :%s",
+ sptr->name, comment);
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_QUIT|
+ SERVICE_WANT_RQUIT,
+ (sptr->user) ?
+ sptr->user->server
+ : NULL, cptr,
+ ":%s QUIT :%s",
+ sptr->name, comment);
+#endif
+ }
+ else
+ {
+ if (sptr->flags & FLAGS_HIDDEN)
+ /* joys of hostmasking */
+ for (i = fdas.highest; i >= 0; i--)
+ {
+ if (!(acptr =local[fdas.fd[i]])
+ || !IsServer(acptr)
+ || acptr == cptr
+ || IsMe(acptr))
+ continue;
+ if (acptr->flags &FLAGS_HIDDEN)
+ sendto_one(acptr,
+ ":%s QUIT :%s",
+ sptr->name,
+ comment);
+ }
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_QUIT,
+ (sptr->user) ? sptr->user->server
+ : NULL, cptr,
+ ":%s QUIT :%s",
+ sptr->name, comment);
+#endif
+ }
+ }
+ /*
+ ** 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*)
+ */
+ if (sptr->user)
+ {
+ if (IsInvisible(sptr))
+ istat.is_user[1]--;
+ else
+ istat.is_user[0]--;
+ if (IsAnOper(sptr))
+ istat.is_oper--;
+ sendto_common_channels(sptr, ":%s QUIT :%s",
+ sptr->name, comment);
+
+ if (!(acptr = cptr ? cptr : sptr->from))
+ acptr = sptr;
+ while ((lp = sptr->user->channel))
+ {
+ /*
+ ** Mark channels from where remote chop left,
+ ** this will eventually lock the channel.
+ ** close_connection() has already been called,
+ ** it makes MyConnect == False - krys
+ */
+ if (sptr != cptr)
+ if (*lp->value.chptr->chname == '!')
+ {
+ if (!(sptr->flags &FLAGS_QUIT))
+ lp->value.chptr->history = timeofday + LDELAYCHASETIMELIMIT;
+ }
+ else if (
+#ifndef BETTER_CDELAY
+ !(sptr->flags & FLAGS_QUIT) &&
+#endif
+ is_chan_op(sptr, lp->value.chptr))
+ lp->value.chptr->history = timeofday + DELAYCHASETIMELIMIT;
+ if (IsAnonymous(lp->value.chptr) &&
+ !IsQuiet(lp->value.chptr))
+ sendto_channel_butserv(lp->value.chptr, sptr, ":%s PART %s :None", sptr->name, lp->value.chptr->chname);
+ remove_user_from_channel(sptr,lp->value.chptr);
+ }
+
+ /* Clean up invitefield */
+ while ((lp = sptr->user->invited))
+ del_invite(sptr, lp->value.chptr);
+ /* again, this is all that is needed */
+
+ /* Add user to history */
+#ifndef BETTER_NDELAY
+ add_history(sptr, (sptr->flags & FLAGS_QUIT) ?
+ &me : NULL);
+#else
+ add_history(sptr, (sptr == cptr) ? &me : NULL);
+#endif
+ off_history(sptr);
+ }
+ }
+ else if (sptr->name[0] && IsService(sptr))
+ {
+ /*
+ ** If this exit is generated from "m_kill", then there
+ ** is no sense in sending the QUIT--KILL's have been
+ ** sent instead.
+ */
+ if ((sptr->flags & FLAGS_KILLED) == 0)
+ {
+ /*
+ ** A service quitting is annoying, It has to be sent
+ ** to connected servers depending on
+ ** sptr->service->dist
+ */
+ for (i = fdas.highest; i >= 0; i--)
+ {
+ if (!(acptr = local[fdas.fd[i]])
+ || !IsServer(acptr) || acptr == cptr
+ || IsMe(acptr))
+ continue;
+ if (match(sptr->service->dist, acptr->name))
+ continue;
+ sendto_one(acptr, ":%s QUIT :%s", sptr->name,
+ comment);
+ }
+ }
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_SERVICE, NULL, NULL,
+ ":%s QUIT :%s", sptr->name, comment);
+#endif
+ /* MyConnect(sptr) is always FALSE here */
+ if (cptr == sptr)
+ sendto_flag(SCH_NOTICE, "Service %s disconnected",
+ get_client_name(sptr, TRUE));
+ sendto_flag(SCH_SERVICE, "Received QUIT %s from %s (%s)",
+ sptr->name, from->name, comment);
+ istat.is_service--;
+ }
+
+ /* Remove sptr from the client list */
+ if (del_from_client_hash_table(sptr->name, sptr) != 1)
+ Debug((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x",
+ sptr, sptr->name,
+ sptr->from ? sptr->from->sockhost : "??host",
+ sptr->from, sptr->next, sptr->prev, sptr->fd,
+ sptr->status, sptr->user));
+ remove_client_from_list(sptr);
+ return;
+}
+
+void checklist()
+{
+ Reg aClient *acptr;
+ Reg int i,j;
+
+ if (!(bootopt & BOOT_AUTODIE))
+ return;
+ for (j = i = 0; i <= highest_fd; i++)
+ if (!(acptr = local[i]))
+ continue;
+ else if (IsClient(acptr))
+ j++;
+ if (!j)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING,"ircd exiting: autodie");
+#endif
+ exit(0);
+ }
+ return;
+}
+
+void initstats()
+{
+ bzero((char *)&istat, sizeof(istat));
+ istat.is_serv = 1;
+ istat.is_remc = 1; /* don't ask me why, I forgot. */
+ bzero((char *)&ircst, sizeof(ircst));
+}
+
+void tstats(cptr, name)
+aClient *cptr;
+char *name;
+{
+ Reg aClient *acptr;
+ Reg int i;
+ Reg struct stats *sp;
+ struct stats tmp;
+
+ sp = &tmp;
+ bcopy((char *)ircstp, (char *)sp, sizeof(*sp));
+ for (i = 0; i < MAXCONNECTIONS; i++)
+ {
+ if (!(acptr = local[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 += timeofday - 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 (IsClient(acptr))
+ {
+ sp->is_cbs += acptr->sendB;
+ sp->is_cbr += acptr->receiveB;
+ sp->is_cks += acptr->sendK;
+ sp->is_ckr += acptr->receiveK;
+ sp->is_cti += timeofday - 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, RPL_STATSDEBUG, name, sp->is_ac, sp->is_ref);
+ sendto_one(cptr, ":%s %d %s :unknown: commands %u prefixes %u",
+ ME, RPL_STATSDEBUG, name, sp->is_unco, sp->is_unpf);
+ sendto_one(cptr, ":%s %d %s :nick collisions %u unknown closes %u",
+ ME, RPL_STATSDEBUG, name, sp->is_kill, sp->is_ni);
+ sendto_one(cptr, ":%s %d %s :wrong direction %u empty %u",
+ ME, RPL_STATSDEBUG, name, sp->is_wrdi, sp->is_empt);
+ sendto_one(cptr, ":%s %d %s :users without servers %u ghosts N/A",
+ ME, RPL_STATSDEBUG, name, sp->is_nosrv);
+ sendto_one(cptr, ":%s %d %s :numerics seen %u mode fakes %u",
+ ME, RPL_STATSDEBUG, name, sp->is_num, sp->is_fake);
+ sendto_one(cptr, ":%s %d %s :auth: successes %u fails %u",
+ ME, RPL_STATSDEBUG, name, sp->is_asuc, sp->is_abad);
+ sendto_one(cptr,":%s %d %s :local connections %u udp packets %u",
+ ME, RPL_STATSDEBUG, name, sp->is_loc, sp->is_udpok);
+ sendto_one(cptr,":%s %d %s :udp errors %u udp dropped %u",
+ ME, RPL_STATSDEBUG, name, sp->is_udperr, sp->is_udpdrop);
+ sendto_one(cptr,
+ ":%s %d %s :link checks %u passed %u 15s/%u 30s dropped %uSq/%uYg/%uFl",
+ ME, RPL_STATSDEBUG, name, sp->is_ckl, sp->is_cklq,
+ sp->is_cklok, sp->is_cklQ, sp->is_ckly, sp->is_cklno);
+ if (sp->is_wwcnt)
+ sendto_one(cptr, ":%s %d %s :whowas turnover %u/%u/%u [%u]",
+ ME, RPL_STATSDEBUG, name, sp->is_wwmt,
+ (u_int) (sp->is_wwt / sp->is_wwcnt), sp->is_wwMt,
+ KILLCHASETIMELIMIT);
+ if (sp->is_lkcnt)
+ sendto_one(cptr, ":%s %d %s :ndelay turnover %u/%u/%u [%u]",
+ ME, RPL_STATSDEBUG, name, sp->is_lkmt,
+ (u_int) (sp->is_lkt / sp->is_lkcnt), sp->is_lkMt,
+ DELAYCHASETIMELIMIT);
+ sendto_one(cptr, ":%s %d %s :abuse protections %u strict %u", ME,
+ RPL_STATSDEBUG, name, (bootopt & BOOT_PROT) ? 1 : 0,
+ (bootopt & BOOT_STRICTPROT) ? 1 : 0);
+ sendto_one(cptr, ":%s %d %s :Client - Server",
+ ME, RPL_STATSDEBUG, name);
+ sendto_one(cptr, ":%s %d %s :connected %u %u",
+ ME, RPL_STATSDEBUG, name, sp->is_cl, sp->is_sv);
+ sendto_one(cptr, ":%s %d %s :bytes sent %u.%uK %u.%uK",
+ ME, 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, RPL_STATSDEBUG, name,
+ sp->is_ckr, sp->is_cbr, sp->is_skr, sp->is_sbr);
+ sendto_one(cptr, ":%s %d %s :time connected %u %u",
+ ME, RPL_STATSDEBUG, name, sp->is_cti, sp->is_sti);
+#if defined(USE_IAUTH)
+ report_iauth_stats(cptr, name);
+#endif
+}
+
+#ifdef CACHED_MOTD
+aMotd *motd = NULL;
+struct tm motd_tm;
+
+void read_motd(filename)
+char *filename;
+{
+ int fd;
+ register aMotd *temp, *last;
+ struct stat Sb;
+ char line[80];
+ register char *tmp;
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return;
+ if (fstat(fd, &Sb) == -1)
+ {
+ close(fd);
+ return;
+ }
+ for(;motd != NULL;motd=last)
+ {
+ last = motd->next;
+ MyFree(motd->line);
+ MyFree((char *)motd);
+ }
+ motd_tm = *localtime(&Sb.st_mtime);
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ last = NULL;
+ while (dgets(fd, line, sizeof(line)-1) > 0)
+ {
+ if ((tmp = strchr(line, '\n')) != NULL)
+ *tmp = (char) 0;
+ if ((tmp = strchr(line, '\r')) != NULL)
+ *tmp = (char) 0;
+ temp = (aMotd *)MyMalloc(sizeof(aMotd));
+ if (!temp)
+ outofmemory();
+ temp->line = mystrdup(line);
+ temp->next = NULL;
+ if (!motd)
+ motd = temp;
+ else
+ last->next = temp;
+ last = temp;
+ }
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ close(fd);
+}
+#endif
diff --git a/ircd/s_misc_ext.h b/ircd/s_misc_ext.h
new file mode 100644
index 0000000..3c4f572
--- /dev/null
+++ b/ircd/s_misc_ext.h
@@ -0,0 +1,58 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_misc_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_misc.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef S_MISC_C
+extern struct stats ircst, *ircstp;
+#ifdef CACHED_MOTD
+extern aMotd *motd;
+extern struct tm motd_tm;
+#endif /* CACHED_MOTD */
+#endif /* S_MISC_C */
+
+/* External definitions for global functions.
+ */
+#ifndef S_MISC_C
+#define EXTERN extern
+#else /* S_MISC_C */
+#define EXTERN
+#endif /* S_MISC_C */
+EXTERN char *date __P((time_t clock));
+EXTERN int check_registered_user __P((aClient *sptr));
+EXTERN int check_registered __P((aClient *sptr));
+EXTERN int check_registered_service __P((aClient *sptr));
+EXTERN char *get_client_name __P((aClient *sptr, int showip));
+EXTERN char *get_client_host __P((aClient *cptr));
+EXTERN void get_sockhost __P((Reg aClient *cptr, Reg char *host));
+EXTERN char *my_name_for_link __P((char *name, Reg int count));
+EXTERN int mark_blind_servers __P((aClient *cptr, char *name));
+EXTERN int exit_client __P((aClient *cptr, aClient *sptr, aClient *from,
+ char *comment));
+EXTERN void checklist();
+EXTERN void initstats();
+EXTERN void tstats __P((aClient *cptr, char *name));
+#ifdef CACHED_MOTD
+EXTERN void read_motd __P((char *filename));
+#endif /* CACHED_MOTD */
+#undef EXTERN
diff --git a/ircd/s_numeric.c b/ircd/s_numeric.c
new file mode 100644
index 0000000..bed99c1
--- /dev/null
+++ b/ircd/s_numeric.c
@@ -0,0 +1,127 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_numeric.c,v 1.4 1998/12/12 23:48:17 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_NUMERIC_C
+#include "s_externs.h"
+#undef S_NUMERIC_C
+
+static char buffer[1024];
+
+/*
+** DoNumeric (replacement for the old do_numeric)
+**
+** parc number of arguments ('sender' counted as one!)
+** parv[0] pointer to 'sender' (may point to empty string) (not used)
+** parv[1]..parv[parc-1]
+** pointers to additional parameters, this is a NULL
+** terminated list (parv[parc] == NULL).
+**
+** *WARNING*
+** Numerics are mostly error reports. If there is something
+** wrong with the message, just *DROP* it! Don't even think of
+** sending back a neat error message -- big danger of creating
+** a ping pong error message...
+*/
+int do_numeric(numeric, cptr, sptr, parc, parv)
+int numeric;
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+ aChannel *chptr;
+ char *nick, *p;
+ int i;
+
+ if (parc < 1 || !IsServer(sptr))
+ return 1;
+ /* Remap low number numerics. */
+ if (numeric < 100)
+ numeric += 100;
+ /*
+ ** Prepare the parameter portion of the message into 'buffer'.
+ ** (Because the buffer is twice as large as the message buffer
+ ** for the socket, no overflow can occur here... ...on current
+ ** assumptions--bets are off, if these are changed --msa)
+ ** Note: if buffer is non-empty, it will begin with SPACE.
+ */
+ buffer[0] = '\0';
+ if (parc > 1)
+ {
+ for (i = 2; i < (parc - 1); i++)
+ {
+ (void)strcat(buffer, " ");
+ (void)strcat(buffer, parv[i]);
+ }
+ (void)strcat(buffer, " :");
+ (void)strcat(buffer, parv[parc-1]);
+ }
+ for (; (nick = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+ {
+ if ((acptr = find_client(nick, (aClient *)NULL)))
+ {
+ /*
+ ** Drop to bit bucket if for me...
+ ** ...one might consider sendto_ops
+ ** here... --msa
+ ** And so it was done. -avalon
+ ** And regretted. Don't do it that way. Make sure
+ ** it goes only to non-servers. -avalon
+ ** Check added to make sure servers don't try to loop
+ ** with numerics which can happen with nick collisions.
+ ** - Avalon
+ */
+ if (IsMe(acptr) || acptr->from == cptr)
+ sendto_flag(SCH_NUM,
+ "From %s for %s: %s %d %s %s.",
+ get_client_name(cptr, TRUE),
+ acptr->name, sptr->name,
+ numeric, nick, buffer);
+ else if (IsPerson(acptr) || IsServer(acptr) ||
+ IsService(acptr))
+ sendto_prefix_one(acptr, sptr,":%s %d %s%s",
+ parv[0], numeric, nick, buffer);
+ }
+ /* any reason why no cptr == acptr->from checks here? -krys */
+/* because these are not used.. -Vesa
+ else if ((acptr = find_service(nick, (aClient *)NULL)))
+ sendto_prefix_one(acptr, sptr,":%s %d %s%s",
+ parv[0], numeric, nick, buffer);
+ else if ((acptr = find_server(nick, (aClient *)NULL)))
+ {
+ if (!IsMe(acptr) && acptr->from != cptr)
+ sendto_prefix_one(acptr, sptr,":%s %d %s%s",
+ parv[0], numeric, nick, buffer);
+ }
+..nuke them */
+ else if ((chptr = find_channel(nick, (aChannel *)NULL)))
+ sendto_channel_butone(cptr,sptr,chptr,":%s %d %s%s",
+ parv[0],
+ numeric, chptr->chname, buffer);
+ }
+ return 1;
+}
diff --git a/ircd/s_numeric_ext.h b/ircd/s_numeric_ext.h
new file mode 100644
index 0000000..c17cd94
--- /dev/null
+++ b/ircd/s_numeric_ext.h
@@ -0,0 +1,33 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_numeric_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_numeric.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef S_NUMERIC_C
+#define EXTERN extern
+#else /* S_NUMERIC_C */
+#define EXTERN
+#endif /* S_NUMERIC_C */
+EXTERN int do_numeric __P((int numeric, aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+#undef EXTERN
diff --git a/ircd/s_serv.c b/ircd/s_serv.c
new file mode 100644
index 0000000..67c857d
--- /dev/null
+++ b/ircd/s_serv.c
@@ -0,0 +1,2489 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_serv.c,v 1.65 1999/07/02 16:49:37 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_SERV_C
+#include "s_externs.h"
+#undef S_SERV_C
+
+static char buf[BUFSIZE];
+
+static int check_link __P((aClient *));
+
+/*
+** 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(cptr, sptr, parc, parv)
+aClient *sptr, *cptr;
+int parc;
+char *parv[];
+{
+ if (hunt_server(cptr,sptr,":%s VERSION :%s",1,parc,parv)==HUNTED_ISME)
+ sendto_one(sptr, rpl_str(RPL_VERSION, parv[0]),
+ version, debugmode, ME, serveropts);
+ return 2;
+}
+
+/*
+** m_squit
+** parv[0] = sender prefix
+** parv[1] = server name
+** parv[2] = comment
+*/
+int m_squit(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ Reg aConfItem *aconf;
+ char *server;
+ Reg aClient *acptr;
+ char *comment = (parc > 2 && parv[2]) ? parv[2] : cptr->name;
+
+ if (parc > 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.
+ */
+ while ((*server == '*') && IsServer(cptr))
+ {
+ aconf = cptr->serv->nline;
+ if (!aconf)
+ break;
+ if (!mycmp(server,
+ my_name_for_link(ME, aconf->port)))
+ server = cptr->name;
+ break; /* WARNING is normal here */
+ }
+ /*
+ ** 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 && IsMe(acptr))
+ {
+ acptr = cptr;
+ server = cptr->sockhost;
+ }
+ }
+ else
+ {
+ /*
+ ** This is actually protocol error. But, well, closing
+ ** the link is very proper answer to that...
+ */
+ server = cptr->name;
+ acptr = cptr;
+ }
+
+ /*
+ ** SQUIT semantics is tricky, be careful...
+ **
+ ** The old (irc2.2PL1 and earlier) code just cleans away the
+ ** server client from the links (because it is never true
+ ** "cptr == acptr".
+ **
+ ** This logic here works the same way until "SQUIT host" hits
+ ** the server having the target "host" as local link. Then it
+ ** will do a real cleanup spewing SQUIT's and QUIT's to all
+ ** directions, also to the link from which the orinal SQUIT
+ ** came, generating one unnecessary "SQUIT host" back to that
+ ** link.
+ **
+ ** One may think that this could be implemented like
+ ** "hunt_server" (e.g. just pass on "SQUIT" without doing
+ ** nothing until the server having the link as local is
+ ** reached). Unfortunately this wouldn't work in the real life,
+ ** because either target may be unreachable or may not comply
+ ** with the request. In either case it would leave target in
+ ** links--no command to clear it away. So, it's better just
+ ** clean out while going forward, just to be sure.
+ **
+ ** ...of course, even better cleanout would be to QUIT/SQUIT
+ ** dependant users/servers already on the way out, but
+ ** currently there is not enough information about remote
+ ** clients to do this... --msa
+ */
+ if (!acptr)
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]), server);
+ return 1;
+ }
+ if (MyConnect(sptr) && !MyConnect(acptr) && parc < 3)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS,parv[0]), "SQUIT");
+ return 0;
+ }
+ if (IsLocOp(sptr) && !MyConnect(acptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
+ return 1;
+ }
+ if (!MyConnect(acptr) && (cptr != acptr->from))
+ {
+ /*
+ ** The following is an awful kludge, but I don't see any other
+ ** way to change the pre 2.10.3 behaviour. I'm probably going
+ ** to regret it.. -kalt
+ */
+ if ((acptr->from->serv->version & SV_OLDSQUIT) == 0)
+ {
+ /* better server: just propagate upstream */
+ sendto_one(acptr->from, ":%s SQUIT %s :%s", parv[0],
+ acptr->name, comment);
+ sendto_flag(SCH_SERVER,
+ "Forwarding SQUIT %s from %s (%s)",
+ acptr->name, parv[0], comment);
+ sendto_flag(SCH_DEBUG,
+ "Forwarding SQUIT %s to %s from %s (%s)",
+ acptr->name, acptr->from->name,
+ parv[0], comment);
+ return 1;
+ }
+ /*
+ ** ack, bad server encountered!
+ ** must send back to other good servers which were trying to
+ ** do the right thing, and fake the yet to come SQUIT which
+ ** will never be received from the bad servers.
+ */
+ if (IsServer(cptr) &&
+ (cptr->serv->version & SV_OLDSQUIT) == 0)
+ {
+ sendto_one(cptr, ":%s SQUIT %s :%s (Bounced for %s)",
+ ME, acptr->name, comment, parv[0]);
+ sendto_flag(SCH_DEBUG, "Bouncing SQUIT %s back to %s",
+ acptr->name, acptr->from->name);
+ }
+ }
+ /*
+ ** Notify all opers, if my local link is remotely squitted
+ */
+ if (MyConnect(acptr) && !IsAnOper(cptr))
+ {
+ sendto_ops_butone(NULL, &me,
+ ":%s WALLOPS :Received SQUIT %s from %s (%s)",
+ ME, server, parv[0], comment);
+#if defined(USE_SYSLOG) && defined(SYSLOG_SQUIT)
+ syslog(LOG_DEBUG,"SQUIT From %s : %s (%s)",
+ parv[0], server, comment);
+#endif
+ }
+ if (MyConnect(acptr))
+ {
+ int timeconnected = timeofday - acptr->firsttime;
+ sendto_flag(SCH_NOTICE,
+ "Closing link to %s (%d, %2d:%02d:%02d)",
+ get_client_name(acptr, FALSE),
+ timeconnected / 86400,
+ (timeconnected % 86400) / 3600,
+ (timeconnected % 3600)/60,
+ timeconnected % 60);
+ }
+ sendto_flag(SCH_SERVER, "Received SQUIT %s from %s (%s)",
+ acptr->name, parv[0], comment);
+
+ if (MyConnect(acptr) &&
+ IsServer(cptr) && (cptr->serv->version & SV_OLDSQUIT) == 0)
+ {
+ sendto_one(cptr, ":%s SQUIT %s :%s", ME, acptr->name, comment);
+ sendto_flag(SCH_DEBUG, "Issuing additionnal SQUIT %s for %s",
+ acptr->name, acptr->from->name);
+ }
+ return exit_client(cptr, acptr, sptr, comment);
+ }
+
+/*
+** check_version
+** The PASS command delivers additional information about incoming
+** connection. The data is temporarily stored to info/name/username
+** in m_pass() and processed here before the fields are natively used.
+** Return: < 1: exit/error, > 0: no error
+*/
+int check_version(cptr)
+aClient *cptr;
+{
+ char *id, *misc = NULL, *link = NULL;
+
+ Debug((DEBUG_INFO,"check_version: %s", cptr->info));
+
+ if (cptr->info == DefInfo)
+ {
+ cptr->hopcount = SV_OLD;
+ return 1; /* no version checked (e.g. older than 2.9) */
+ }
+ if (id = index(cptr->info, ' '))
+ {
+ *id++ = '\0';
+ if (link = index(id, ' '))
+ *link++ = '\0';
+ if (misc = index(id, '|'))
+ *misc++ = '\0';
+ else
+ {
+ misc = id;
+ id = "";
+ }
+ }
+ else
+ id = "";
+
+ if (!strncmp(cptr->info, "021", 3))
+ cptr->hopcount = SV_29|SV_NJOIN|SV_NMODE|SV_NCHAN; /* SV_2_10*/
+ else if (!strncmp(cptr->info, "0209", 4))
+ cptr->hopcount = SV_29|SV_OLDSQUIT; /* 2.9+ protocol */
+ else
+ cptr->hopcount = SV_OLD; /* uhuh */
+
+ if (!strcmp("IRC", id) && !strncmp(cptr->info, "02100", 5) &&
+ atoi(cptr->info+5) < 20600)
+ /* before 2.10.3a6 ( 2.10.3a5 is just broken ) */
+ cptr->hopcount |= SV_OLDSQUIT;
+
+ /* Check version number/mask from conf */
+ sprintf(buf, "%s/%s", id, cptr->info);
+ if (find_two_masks(cptr->name, buf, CONF_VER))
+ {
+ sendto_flag(SCH_ERROR, "Bad version %s %s from %s", id,
+ cptr->info, get_client_name(cptr, TRUE));
+ return exit_client(cptr, cptr, &me, "Bad version");
+ }
+
+ if (misc)
+ {
+ sprintf(buf, "%s/%s", id, misc);
+ /* Check version flags from conf */
+ if (find_conf_flags(cptr->name, buf, CONF_VER))
+ {
+ sendto_flag(SCH_ERROR, "Bad flags %s (%s) from %s",
+ misc, id, get_client_name(cptr, TRUE));
+ return exit_client(cptr, cptr, &me, "Bad flags");
+ }
+ }
+
+ /* right now, I can't code anything good for this */
+ /* Stop whining, and do it! ;) */
+ if (link && strchr(link, 'Z')) /* Compression requested */
+ cptr->flags |= FLAGS_ZIPRQ;
+ /*
+ * If server was started with -p strict, be careful about the
+ * other server mode.
+ */
+ if (link && strncmp(cptr->info, "020", 3) &&
+ (bootopt & BOOT_STRICTPROT) && !strchr(link, 'P'))
+ return exit_client(cptr, cptr, &me, "Unsafe mode");
+
+ return 2;
+}
+
+/*
+** m_server
+** parv[0] = sender prefix
+** parv[1] = servername
+** parv[2] = serverinfo/hopcount
+** parv[3] = token/serverinfo (2.9)
+** parv[4] = serverinfo
+*/
+int m_server(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg char *ch;
+ Reg int i;
+ char info[REALLEN+1], *inpath, *host, *stok;
+ aClient *acptr, *bcptr;
+ aConfItem *aconf;
+ int hop = 0, token = 0;
+
+ if (sptr->user) /* in case NICK hasn't been received yet */
+ {
+ sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0]));
+ return 1;
+ }
+ info[0] = info[REALLEN] = '\0'; /* strncpy() doesn't guarantee NULL */
+ inpath = get_client_name(cptr, FALSE);
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(cptr,"ERROR :No servername");
+ return 1;
+ }
+ host = parv[1];
+ if (parc > 3 && (hop = atoi(parv[2])))
+ {
+ if (parc > 4 && (token = atoi(parv[3])))
+ (void)strncpy(info, parv[4], REALLEN);
+ else
+ (void)strncpy(info, parv[3], REALLEN);
+ }
+ else if (parc > 2)
+ {
+ (void)strncpy(info, parv[2], REALLEN);
+ i = strlen(info);
+ if (parc > 3 && ((i+2) < REALLEN))
+ {
+ (void)strncat(info, " ", REALLEN - i - 1);
+ (void)strncat(info, parv[3], REALLEN - i - 2);
+ }
+ }
+ /*
+ ** 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) > (size_t) HOSTLEN)
+ host[HOSTLEN] = '\0';
+ for (ch = host; *ch; ch++)
+ if (*ch <= ' ' || *ch > '~')
+ break;
+ if (*ch || !index(host, '.'))
+ {
+ sendto_one(sptr,"ERROR :Bogus server name (%s)", host);
+ sendto_flag(SCH_ERROR, "Bogus server name (%s) from %s", host,
+ get_client_name(cptr, TRUE));
+ return 2;
+ }
+
+ /* *WHEN* can it be that "cptr != sptr" ????? --msa */
+ /* When SERVER command (like now) has prefix. -avalon */
+
+ if (IsRegistered(cptr) && ((acptr = find_name(host, NULL))
+ || (acptr = find_mask(host, NULL))))
+ {
+ /*
+ ** This link is trying feed me a server that I already have
+ ** access through another path -- multiple paths not accepted
+ ** currently, kill this link immeatedly!!
+ **
+ ** Rather than KILL the link which introduced it, KILL the
+ ** youngest of the two links. -avalon
+ */
+ bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr :
+ acptr->from;
+ sendto_one(bcptr, "ERROR :Server %s already exists", host);
+ /* in both cases the bcptr (the youngest is killed) */
+ if (bcptr == cptr)
+ {
+ sendto_flag(SCH_ERROR,
+ "Link %s cancelled, server %s already exists",
+ get_client_name(bcptr, TRUE), host);
+ return exit_client(bcptr, bcptr, &me, "Server Exists");
+ }
+ else
+ {
+ /*
+ ** in this case, we are not dropping the link from
+ ** which we got the SERVER message. Thus we canNOT
+ ** `return' yet! -krys
+ */
+ strcpy(buf, get_client_name(bcptr, TRUE));
+ sendto_flag(SCH_ERROR,
+ "Link %s cancelled, server %s reintroduced by %s",
+ buf, host, get_client_name(cptr, TRUE));
+ (void) exit_client(bcptr, bcptr, &me, "Server Exists");
+ }
+ }
+ if ((acptr = find_person(host, NULL)) && (acptr != cptr))
+ {
+ /*
+ ** Server trying to use the same name as a person. Would
+ ** cause a fair bit of confusion. Enough to make it hellish
+ ** for a while and servers to send stuff to the wrong place.
+ */
+ sendto_one(cptr,"ERROR :Nickname %s already exists!", host);
+ sendto_flag(SCH_ERROR,
+ "Link %s cancelled: Server/nick collision on %s",
+ inpath, host);
+ sendto_serv_butone(NULL, /* all servers */
+ ":%s KILL %s :%s (%s <- %s)",
+ ME, acptr->name, ME,
+ acptr->from->name, host);
+ acptr->flags |= FLAGS_KILLED;
+ (void)exit_client(NULL, acptr, &me, "Nick collision");
+ return exit_client(cptr, cptr, &me, "Nick as Server");
+ }
+
+ if (IsServer(cptr))
+ {
+ /* A server can only be introduced by another server. */
+ if (!IsServer(sptr))
+ {
+ sendto_flag(SCH_LOCAL,
+ "Squitting %s brought by %s (introduced by %s)",
+ host, get_client_name(cptr, FALSE),
+ sptr->name);
+ sendto_one(cptr,
+ ":%s SQUIT %s :(Introduced by %s from %s)",
+ me.name, host, sptr->name,
+ get_client_name(cptr, FALSE));
+ return 1;
+ }
+ /*
+ ** 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...
+ */
+ if (parc == 1 || info[0] == '\0')
+ {
+ sendto_one(cptr,
+ "ERROR :No server info specified for %s",
+ host);
+ sendto_flag(SCH_ERROR, "No server info for %s from %s",
+ host, get_client_name(cptr, TRUE));
+ return 1;
+ }
+
+ /*
+ ** See if the newly found server is behind a guaranteed
+ ** leaf (L-line). If so, close the link.
+ */
+ if ((aconf = find_conf_host(cptr->confs, host, CONF_LEAF)) &&
+ (!aconf->port || (hop > aconf->port)))
+ {
+ sendto_flag(SCH_ERROR,
+ "Leaf-only link %s->%s - Closing",
+ get_client_name(cptr, TRUE),
+ aconf->host ? aconf->host : "*");
+ sendto_one(cptr, "ERROR :Leaf-only link, sorry.");
+ return exit_client(cptr, cptr, &me, "Leaf Only");
+ }
+ /*
+ **
+ */
+ if (!(aconf = find_conf_host(cptr->confs, host, CONF_HUB)) ||
+ (aconf->port && (hop > aconf->port)) )
+ {
+ sendto_flag(SCH_ERROR,
+ "Non-Hub link %s introduced %s(%s).",
+ get_client_name(cptr, TRUE), host,
+ aconf ? (aconf->host ? aconf->host : "*") :
+ "!");
+ return exit_client(cptr, cptr, &me,
+ "Too many servers");
+ }
+ /*
+ ** See if the newly found server has a Q line for it in
+ ** our conf. If it does, lose the link that brought it
+ ** into our network. Format:
+ **
+ ** Q:<unused>:<reason>:<servername>
+ **
+ ** Example: Q:*:for the hell of it:eris.Berkeley.EDU
+ */
+ if ((aconf = find_conf_name(host, CONF_QUARANTINED_SERVER)))
+ {
+ sendto_ops_butone(NULL, &me,
+ ":%s WALLOPS * :%s brought in %s, %s %s",
+ ME, get_client_name(cptr, TRUE),
+ host, "closing link because",
+ BadPtr(aconf->passwd) ? "reason unspecified" :
+ aconf->passwd);
+
+ sendto_one(cptr,
+ "ERROR :%s is not welcome: %s. %s",
+ host, BadPtr(aconf->passwd) ?
+ "reason unspecified" : aconf->passwd,
+ "Go away and get a life");
+
+ return exit_client(cptr, cptr, &me, "Q-Lined Server");
+ }
+
+ acptr = make_client(cptr);
+ (void)make_server(acptr);
+ acptr->hopcount = hop;
+ strncpyzt(acptr->name, host, sizeof(acptr->name));
+ if (acptr->info != DefInfo)
+ MyFree(acptr->info);
+ acptr->info = mystrdup(info);
+ acptr->serv->up = sptr->name;
+ acptr->serv->stok = token;
+ acptr->serv->snum = find_server_num(acptr->name);
+ SetServer(acptr);
+ istat.is_serv++;
+ add_client_to_list(acptr);
+ (void)add_to_client_hash_table(acptr->name, acptr);
+ (void)add_to_server_hash_table(acptr->serv, cptr);
+ /*
+ ** Old sendto_serv_but_one() call removed because we now
+ ** need to send different names to different servers
+ ** (domain name matching)
+ */
+ for (i = fdas.highest; i >= 0; i--)
+ {
+ if (!(bcptr = local[fdas.fd[i]]) || !IsServer(bcptr) ||
+ bcptr == cptr || IsMe(bcptr))
+ continue;
+ if (!(aconf = bcptr->serv->nline))
+ {
+ sendto_flag(SCH_NOTICE,
+ "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, aconf->port),
+ acptr->name) == 0)
+ continue;
+ stok = acptr->serv->tok;
+ sendto_one(bcptr, ":%s SERVER %s %d %s :%s", parv[0],
+ acptr->name, hop+1, stok, acptr->info);
+ }
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_SERVER, acptr->name, acptr,
+ ":%s SERVER %s %d %s :%s", parv[0],
+ acptr->name, hop+1, acptr->serv->tok,
+ acptr->info);
+#endif
+ sendto_flag(SCH_SERVER, "Received SERVER %s from %s (%d %s)",
+ acptr->name, parv[0], hop+1, acptr->info);
+ return 0;
+ }
+
+ if ((!IsUnknown(cptr) && !IsHandshake(cptr)) ||
+ (cptr->flags & FLAGS_UNKCMD))
+ return 1;
+ /*
+ ** A local link that is still in undefined state wants
+ ** to be a SERVER. Check if this is allowed and change
+ ** status accordingly...
+ */
+ strncpyzt(cptr->name, host, sizeof(cptr->name));
+ /* cptr->name has to exist before check_version(), and cptr->info
+ * may not be filled before check_version(). */
+ if ((hop = check_version(cptr)) < 1)
+ return hop; /* from exit_client() */
+ if (cptr->info != DefInfo)
+ MyFree(cptr->info);
+ cptr->info = mystrdup(info[0] ? info : ME);
+
+ switch (check_server_init(cptr))
+ {
+ case 0 :
+ return m_server_estab(cptr);
+ case 1 :
+ sendto_flag(SCH_NOTICE, "Checking access for %s",
+ get_client_name(cptr,TRUE));
+ return 1;
+ default :
+ ircstp->is_ref++;
+ sendto_flag(SCH_NOTICE, "Unauthorized server from %s.",
+ get_client_host(cptr));
+ return exit_client(cptr, cptr, &me, "No C/N conf lines");
+ }
+}
+
+int m_server_estab(cptr)
+Reg aClient *cptr;
+{
+ Reg aClient *acptr;
+ Reg aConfItem *aconf, *bconf;
+ char mlname[HOSTLEN+1], *inpath, *host, *s, *encr, *stok;
+ int split, i;
+
+ host = cptr->name;
+ inpath = get_client_name(cptr,TRUE); /* "refresh" inpath with host */
+ split = mycmp(cptr->name, cptr->sockhost);
+
+ if (!(aconf = find_conf(cptr->confs, host, CONF_NOCONNECT_SERVER)))
+ {
+ ircstp->is_ref++;
+ sendto_one(cptr,
+ "ERROR :Access denied. No N line for server %s",
+ inpath);
+ sendto_flag(SCH_ERROR,
+ "Access denied. No N line for server %s", inpath);
+ return exit_client(cptr, cptr, &me, "No N line for server");
+ }
+ if (!(bconf = find_conf(cptr->confs, host, CONF_CONNECT_SERVER|
+ CONF_ZCONNECT_SERVER)))
+ {
+ ircstp->is_ref++;
+ sendto_one(cptr, "ERROR :Only N (no C) field for server %s",
+ inpath);
+ sendto_flag(SCH_ERROR,
+ "Only N (no C) field for server %s",inpath);
+ return exit_client(cptr, cptr, &me, "No C line for server");
+ }
+
+ if (cptr->hopcount == SV_OLD) /* lame test, should be == 0 */
+ {
+ sendto_one(cptr, "ERROR :Server version is too old.");
+ sendto_flag(SCH_ERROR, "Old version for %s", inpath);
+ return exit_client(cptr, cptr, &me, "Old version");
+ }
+
+#ifdef CRYPT_LINK_PASSWORD
+ /* use first two chars of the password they send in as salt */
+
+ /* passwd may be NULL. Head it off at the pass... */
+ if (*cptr->passwd)
+ {
+ char salt[3];
+ extern char *crypt();
+
+ /* Determine if MD5 or DES */
+ if (strncmp(aconf->passwd, "$1$", 3))
+ {
+ salt[0] = aconf->passwd[0];
+ salt[1] = aconf->passwd[1];
+ }
+ else
+ {
+ salt[0] = aconf->passwd[3];
+ salt[1] = aconf->passwd[4];
+ }
+ salt[2] = '\0';
+ encr = crypt(cptr->passwd, salt);
+ }
+ else
+ encr = "";
+#else
+ encr = cptr->passwd;
+#endif /* CRYPT_LINK_PASSWORD */
+ if (*aconf->passwd && !StrEq(aconf->passwd, encr))
+ {
+ ircstp->is_ref++;
+ sendto_one(cptr, "ERROR :No Access (passwd mismatch) %s",
+ inpath);
+ sendto_flag(SCH_ERROR,
+ "Access denied (passwd mismatch) %s", inpath);
+ return exit_client(cptr, cptr, &me, "Bad Password");
+ }
+ bzero(cptr->passwd, sizeof(cptr->passwd));
+
+#ifndef HUB
+ for (i = 0; i <= highest_fd; i++)
+ if (local[i] && IsServer(local[i]))
+ {
+ ircstp->is_ref++;
+ sendto_flag(SCH_ERROR, "I'm a leaf, cannot link %s",
+ get_client_name(cptr, TRUE));
+ return exit_client(cptr, cptr, &me, "I'm a leaf");
+ }
+#endif
+ (void) strcpy(mlname, my_name_for_link(ME, aconf->port));
+ if (IsUnknown(cptr))
+ {
+ if (bconf->passwd[0])
+#ifndef ZIP_LINKS
+ sendto_one(cptr, "PASS %s %s IRC|%s %s", bconf->passwd,
+ pass_version, serveropts,
+ (bootopt & BOOT_STRICTPROT) ? "P" : "");
+#else
+ sendto_one(cptr, "PASS %s %s IRC|%s %s%s",
+ bconf->passwd, pass_version, serveropts,
+ (bconf->status == CONF_ZCONNECT_SERVER) ? "Z" : "",
+ (bootopt & BOOT_STRICTPROT) ? "P" : "");
+#endif
+ /*
+ ** Pass my info to the new server
+ */
+ sendto_one(cptr, "SERVER %s 1 :%s", mlname, me.info);
+
+ /*
+ ** If we get a connection which has been authorized to be
+ ** an already existing connection, remove the already
+ ** existing connection if it has a sendq else remove the
+ ** new and duplicate server. -avalon
+ ** Remove existing link only if it has been linked for longer
+ ** and has sendq higher than a threshold. -Vesa
+ */
+ if ((acptr = find_name(host, NULL))
+ || (acptr = find_mask(host, NULL)))
+ {
+ if (MyConnect(acptr) &&
+ DBufLength(&acptr->sendQ) > CHREPLLEN &&
+ timeofday - acptr->firsttime > TIMESEC)
+ (void) exit_client(acptr, acptr, &me,
+ "New Server");
+ else
+ return exit_client(cptr, cptr, &me,
+ "Server Exists");
+ }
+ }
+ else
+ {
+ s = (char *)index(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_flag(SCH_ERROR,
+ "Username mismatch [%s]v[%s] : %s",
+ aconf->host, cptr->username,
+ get_client_name(cptr, TRUE));
+ sendto_one(cptr, "ERROR :No Username Match");
+ return exit_client(cptr, cptr, &me, "Bad User");
+ }
+ *s = '@';
+ }
+
+#ifdef ZIP_LINKS
+ if ((cptr->flags & FLAGS_ZIPRQ) &&
+ (bconf->status == CONF_ZCONNECT_SERVER))
+ {
+ if (zip_init(cptr) == -1)
+ {
+ zip_free(cptr);
+ sendto_flag(SCH_ERROR,
+ "Unable to setup compressed link for %s",
+ get_client_name(cptr, TRUE));
+ return exit_client(cptr, cptr, &me,
+ "zip_init() failed");
+ }
+ cptr->flags |= FLAGS_ZIP|FLAGS_ZIPSTART;
+ }
+#endif
+
+ det_confs_butmask(cptr, CONF_LEAF|CONF_HUB|CONF_NOCONNECT_SERVER);
+ /*
+ ** *WARNING*
+ ** In the following code in place of plain server's
+ ** name we send what is returned by get_client_name
+ ** which may add the "sockhost" after the name. It's
+ ** *very* *important* that there is a SPACE between
+ ** the name and sockhost (if present). The receiving
+ ** server will start the information field from this
+ ** first blank and thus puts the sockhost into info.
+ ** ...a bit tricky, but you have been warned, besides
+ ** code is more neat this way... --msa
+ */
+ SetServer(cptr);
+ istat.is_unknown--;
+ istat.is_serv++;
+ istat.is_myserv++;
+ nextping = timeofday;
+ sendto_flag(SCH_NOTICE, "Link with %s established. (%X%s)", inpath,
+ cptr->hopcount, (cptr->flags & FLAGS_ZIP) ? "z" : "");
+ (void)add_to_client_hash_table(cptr->name, cptr);
+ /* doesnt duplicate cptr->serv if allocted this struct already */
+ (void)make_server(cptr);
+ cptr->serv->up = me.name;
+ cptr->serv->nline = aconf;
+ cptr->serv->version = cptr->hopcount; /* temporary location */
+ cptr->hopcount = 1; /* local server connection */
+ cptr->serv->snum = find_server_num(cptr->name);
+ cptr->serv->stok = 1;
+ cptr->flags |= FLAGS_CBURST;
+ (void) add_to_server_hash_table(cptr->serv, cptr);
+ Debug((DEBUG_NOTICE, "Server link established with %s V%X %d",
+ cptr->name, cptr->serv->version, cptr->serv->stok));
+ add_fd(cptr->fd, &fdas);
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_SERVER, cptr->name, cptr,
+ ":%s SERVER %s %d %s :%s", ME, cptr->name,
+ cptr->hopcount+1, cptr->serv->tok, cptr->info);
+#endif
+ sendto_flag(SCH_SERVER, "Sending SERVER %s (%d %s)", cptr->name,
+ 1, cptr->info);
+ /*
+ ** 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 = fdas.highest; i >= 0; i--)
+ {
+ if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) ||
+ acptr == cptr || IsMe(acptr))
+ continue;
+ if ((aconf = acptr->serv->nline) &&
+ !match(my_name_for_link(ME, aconf->port), cptr->name))
+ continue;
+ stok = cptr->serv->tok;
+ if (split)
+ sendto_one(acptr,":%s SERVER %s 2 %s :[%s] %s",
+ ME, cptr->name, stok,
+ cptr->sockhost, cptr->info);
+ else
+ sendto_one(acptr,":%s SERVER %s 2 %s :%s",
+ ME, cptr->name, stok, 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...
+ **
+ ** ALSO NOTE: using the get_client_name for server names--
+ ** see previous *WARNING*!!! (Also, original inpath
+ ** is destroyed...)
+ */
+ aconf = cptr->serv->nline;
+ for (acptr = &me; acptr; acptr = acptr->prev)
+ {
+ /* acptr->from == acptr for acptr == cptr */
+ if ((acptr->from == cptr) || !IsServer(acptr))
+ continue;
+ if (*mlname == '*' && match(mlname, acptr->name) == 0)
+ continue;
+ split = (MyConnect(acptr) &&
+ mycmp(acptr->name, acptr->sockhost));
+ stok = acptr->serv->tok;
+ if (split)
+ sendto_one(cptr, ":%s SERVER %s %d %s :[%s] %s",
+ acptr->serv->up,
+ acptr->name, acptr->hopcount+1, stok,
+ acptr->sockhost, acptr->info);
+ else
+ sendto_one(cptr, ":%s SERVER %s %d %s :%s",
+ acptr->serv->up, acptr->name,
+ acptr->hopcount+1, stok, acptr->info);
+ }
+
+ for (acptr = &me; acptr; acptr = acptr->prev)
+ {
+ /* acptr->from == acptr for acptr == cptr */
+ if (acptr->from == cptr)
+ continue;
+ if (IsPerson(acptr))
+ {
+ /*
+ ** IsPerson(x) is true only when IsClient(x) is true.
+ ** These are only true when *BOTH* NICK and USER have
+ ** been received. -avalon
+ */
+ if (*mlname == '*' &&
+ match(mlname, acptr->user->server) == 0)
+ stok = me.serv->tok;
+ else
+ stok = acptr->user->servp->tok;
+ send_umode(NULL, acptr, 0, SEND_UMODES, buf);
+ sendto_one(cptr,"NICK %s %d %s %s %s %s :%s",
+ acptr->name, acptr->hopcount + 1,
+ acptr->user->username,
+ acptr->user->host, stok,
+ (*buf) ? buf : "+", acptr->info);
+ if ((cptr->serv->version & SV_NJOIN) == 0)
+ send_user_joins(cptr, acptr);
+ }
+ else if (IsService(acptr) &&
+ match(acptr->service->dist, cptr->name) == 0)
+ {
+ if (*mlname == '*' &&
+ match(mlname, acptr->service->server) == 0)
+ stok = me.serv->tok;
+ else
+ stok = acptr->service->servp->tok;
+ sendto_one(cptr, "SERVICE %s %s %s %d %d :%s",
+ acptr->name, stok, acptr->service->dist,
+ acptr->service->type, acptr->hopcount + 1,
+ acptr->info);
+ }
+ /* the previous if does NOT catch all services.. ! */
+ }
+
+ flush_connections(cptr->fd);
+
+ /*
+ ** Last, pass all channels modes
+ ** only sending modes for LIVE channels.
+ */
+ {
+ Reg aChannel *chptr;
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ if (chptr->users)
+ {
+ if (cptr->serv->version & SV_NJOIN)
+ send_channel_members(cptr, chptr);
+ send_channel_modes(cptr, chptr);
+ }
+ }
+
+ cptr->flags &= ~FLAGS_CBURST;
+#ifdef ZIP_LINKS
+ /*
+ ** some stats about the connect burst,
+ ** they are slightly incorrect because of cptr->zip->outbuf.
+ */
+ if ((cptr->flags & FLAGS_ZIP) && cptr->zip->out->total_in)
+ sendto_flag(SCH_NOTICE,
+ "Connect burst to %s: %lu, compressed: %lu (%3.1f%%)",
+ get_client_name(cptr, TRUE),
+ cptr->zip->out->total_in,cptr->zip->out->total_out,
+ (float) 100*cptr->zip->out->total_out/cptr->zip->out->total_in);
+#endif
+ return 0;
+}
+
+int m_reconnect(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aConfItem *aconf;
+ aClient *acptr = NULL;
+ char *name;
+ int i;
+
+ if (IsRegistered(sptr))
+ return exit_client(cptr, sptr, &me, "Already registered");
+
+ if (parc < 3)
+ return 1;
+
+ name = parv[1];
+
+ for (i = highest_fd; i >= 0; i--)
+ {
+ if (!(acptr = local[i]) || !IsHeld(acptr) ||
+ bcmp((char *)&acptr->ip, (char *)&cptr->ip,
+ sizeof(acptr->ip)) || mycmp(acptr->name, name))
+ continue;
+ if (!(aconf = find_conf_name(name, CONF_CONNECT_SERVER|
+ CONF_ZCONNECT_SERVER)) ||
+ atoi(parv[2]) != acptr->receiveM)
+ break;
+ attach_confs(acptr, name, CONF_SERVER_MASK);
+ acptr->flags &= ~FLAGS_HELD;
+ acptr->fd = cptr->fd;
+ cptr->fd = -2;
+ SetUnknown(acptr);
+ if (check_server(acptr, NULL, NULL, NULL, TRUE) < 0)
+ break;
+ sendto_flag(SCH_NOTICE, "%s has reconnected",
+ get_client_name(acptr, TRUE));
+ return exit_client(cptr, sptr, &me, "Reconnected");
+ }
+ sendto_flag(SCH_NOTICE, "Reconnect from %s failed",
+ get_client_name(cptr, TRUE));
+ if (acptr)
+ (void) exit_client(cptr, acptr, &me, "Reconnect failed");
+ return exit_client(cptr, sptr, &me, "Reconnect failed");
+}
+
+/*
+** m_info
+** parv[0] = sender prefix
+** parv[1] = servername
+*/
+int m_info(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ char **text = infotext;
+
+ if (IsServer(cptr) && check_link(cptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]),
+ "INFO");
+ return 5;
+ }
+ if (hunt_server(cptr,sptr,":%s INFO :%s",1,parc,parv) == HUNTED_ISME)
+ {
+ while (*text)
+ sendto_one(sptr, rpl_str(RPL_INFO, parv[0]), *text++);
+
+ sendto_one(sptr, rpl_str(RPL_INFO, parv[0]), "");
+ sendto_one(sptr,
+ ":%s %d %s :Birth Date: %s, compile # %s",
+ ME, RPL_INFO, parv[0], creation, generation);
+ sendto_one(sptr, ":%s %d %s :On-line since %s",
+ ME, RPL_INFO, parv[0],
+ myctime(me.firsttime));
+ sendto_one(sptr, rpl_str(RPL_ENDOFINFO, parv[0]));
+ return 5;
+ }
+ else
+ return 10;
+}
+
+/*
+** 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(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aServer *asptr;
+ char *mask;
+ aClient *acptr;
+
+ if (parc > 2)
+ {
+ if (IsServer(cptr) && check_link(cptr) && !IsOper(sptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]),
+ "LINKS");
+ return 5;
+ }
+ if (hunt_server(cptr, sptr, ":%s LINKS %s :%s", 1, parc, parv)
+ != HUNTED_ISME)
+ return 5;
+ mask = parv[2];
+ }
+ else
+ mask = parc < 2 ? NULL : parv[1];
+
+ for (asptr = svrtop, (void)collapse(mask); asptr; asptr = asptr->nexts)
+ {
+ acptr = asptr->bcptr;
+ if (!BadPtr(mask) && match(mask, acptr->name))
+ continue;
+ sendto_one(sptr, rpl_str(RPL_LINKS, parv[0]),
+ acptr->name, acptr->serv->up,
+ acptr->hopcount, (acptr->info[0] ? acptr->info :
+ "(Unknown Location)"));
+ }
+
+ sendto_one(sptr, rpl_str(RPL_ENDOFLINKS, parv[0]),
+ BadPtr(mask) ? "*" : mask);
+ return 2;
+}
+
+/*
+** m_summon should be redefined to ":prefix SUMMON host user" so
+** that "hunt_server"-function could be used for this too!!! --msa
+** As of 2.7.1e, this was the case. -avalon
+**
+** parv[0] = sender prefix
+** parv[1] = user
+** parv[2] = server
+** parv[3] = channel (optional)
+*/
+int m_summon(cptr, sptr, parc, parv)
+aClient *sptr, *cptr;
+int parc;
+char *parv[];
+{
+ char *host, *user, *chname;
+#ifdef ENABLE_SUMMON
+ char hostbuf[17], namebuf[10], linebuf[10];
+# ifdef LEAST_IDLE
+ char linetmp[10], ttyname[15]; /* Ack */
+ struct stat stb;
+ time_t ltime = (time_t)0;
+# endif
+ int fd, flag = 0;
+#endif
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NORECIPIENT, parv[0]), "SUMMON");
+ return 1;
+ }
+ user = parv[1];
+ host = (parc < 3 || BadPtr(parv[2])) ? ME : parv[2];
+ chname = (parc > 3) ? parv[3] : "*";
+ /*
+ ** Summoning someone on remote server, find out which link to
+ ** use and pass the message there...
+ */
+ parv[1] = user;
+ parv[2] = host;
+ parv[3] = chname;
+ parv[4] = NULL;
+ if (hunt_server(cptr, sptr, ":%s SUMMON %s %s %s", 2, parc, parv) ==
+ HUNTED_ISME)
+ {
+#ifdef ENABLE_SUMMON
+ if ((fd = utmp_open()) == -1)
+ {
+ sendto_one(sptr, err_str(ERR_FILEERROR, parv[0]),
+ "open", UTMP);
+ return 1;
+ }
+# ifndef LEAST_IDLE
+ while ((flag = utmp_read(fd, namebuf, linebuf, hostbuf,
+ sizeof(hostbuf))) == 0)
+ if (StrEq(namebuf,user))
+ break;
+# else
+ /* use least-idle tty, not the first
+ * one we find in utmp. 10/9/90 Spike@world.std.com
+ * (loosely based on Jim Frost jimf@saber.com code)
+ */
+
+ while ((flag = utmp_read(fd, namebuf, linetmp, hostbuf,
+ sizeof(hostbuf))) == 0)
+ {
+ if (StrEq(namebuf,user))
+ {
+ SPRINTF(ttyname,"/dev/%s",linetmp);
+ if (stat(ttyname,&stb) == -1)
+ {
+ sendto_one(sptr,
+ err_str(ERR_FILEERROR,
+ sptr->name),
+ "stat", ttyname);
+ return 1;
+ }
+ if (!ltime)
+ {
+ ltime= stb.st_mtime;
+ (void)strcpy(linebuf,linetmp);
+ }
+ else if (stb.st_mtime > ltime) /* less idle */
+ {
+ ltime= stb.st_mtime;
+ (void)strcpy(linebuf,linetmp);
+ }
+ }
+ }
+# endif
+ (void)utmp_close(fd);
+# ifdef LEAST_IDLE
+ if (ltime == 0)
+# else
+ if (flag == -1)
+# endif
+ sendto_one(sptr, err_str(ERR_NOLOGIN, parv[0]), user);
+ else
+ summon(sptr, user, linebuf, chname);
+#else
+ sendto_one(sptr, err_str(ERR_SUMMONDISABLED, parv[0]));
+#endif /* ENABLE_SUMMON */
+ }
+ else
+ return 3;
+ return 2;
+}
+
+
+/*
+** m_stats
+** parv[0] = sender prefix
+** parv[1] = statistics selector (defaults to Message frequency)
+** parv[2] = server name (current server defaulted, if omitted)
+**
+** Currently supported are:
+** M = Message frequency (the old stat behaviour)
+** L = Local Link statistics
+** C = Report C and N configuration lines
+*/
+/*
+** 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 int report_array[17][3] = {
+ { CONF_ZCONNECT_SERVER, RPL_STATSCLINE, 'c'},
+ { CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
+ { CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
+ { CONF_CLIENT, RPL_STATSILINE, 'I'},
+ { CONF_RCLIENT, RPL_STATSILINE, 'i'},
+ { CONF_OTHERKILL, RPL_STATSKLINE, 'k'},
+ { CONF_KILL, RPL_STATSKLINE, 'K'},
+ { CONF_QUARANTINED_SERVER,RPL_STATSQLINE, 'Q'},
+ { CONF_LEAF, RPL_STATSLLINE, 'L'},
+ { CONF_OPERATOR, RPL_STATSOLINE, 'O'},
+ { CONF_HUB, RPL_STATSHLINE, 'H'},
+ { CONF_LOCOP, RPL_STATSOLINE, 'o'},
+ { CONF_SERVICE, RPL_STATSSLINE, 'S'},
+ { CONF_VER, RPL_STATSVLINE, 'V'},
+ { CONF_BOUNCE, RPL_STATSBLINE, 'B'},
+ { CONF_DENY, RPL_STATSDLINE, 'D'},
+ { 0, 0, 0}
+ };
+
+static void report_configured_links(sptr, to, mask)
+aClient *sptr;
+char *to;
+int mask;
+{
+ static char null[] = "<NULL>";
+ aConfItem *tmp;
+ int *p, port;
+ char c, *host, *pass, *name;
+
+ for (tmp = (mask & (CONF_KILL|CONF_OTHERKILL)) ? kconf : 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 = (int)tmp->port;
+ /*
+ * On K/V lines the passwd contents can be
+ * displayed on STATS reply. -Vesa
+ */
+ if (tmp->status == CONF_KILL
+ || tmp->status == CONF_OTHERKILL
+ || tmp->status == CONF_VER)
+ sendto_one(sptr, rpl_str(p[1], to), c, host,
+ (pass) ? pass : null,
+ name, port, get_conf_class(tmp));
+ else
+ sendto_one(sptr, rpl_str(p[1], to), c, host,
+ (pass) ? "*" : null,
+ name, port, get_conf_class(tmp));
+ }
+ return;
+}
+
+static void report_ping(sptr, to)
+aClient *sptr;
+char *to;
+{
+ aConfItem *tmp;
+ aCPing *cp;
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ if ((cp = tmp->ping) && cp->lseq)
+ {
+ if (mycmp(tmp->name, tmp->host))
+ SPRINTF(buf,"%s[%s]",tmp->name, tmp->host);
+ else
+ (void)strcpy(buf, tmp->name);
+ sendto_one(sptr, rpl_str(RPL_STATSPING, to),
+ buf, cp->lseq, cp->lrecvd,
+ cp->ping / (cp->recvd ? cp->recvd : 1),
+ tmp->pref);
+ sendto_flag(SCH_DEBUG, "%s: %d", buf, cp->seq);
+ }
+ return;
+}
+
+int m_stats(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :%u";
+ struct Message *mptr;
+ aClient *acptr;
+ char stat = parc > 1 ? parv[1][0] : '\0';
+ Reg int i;
+ int doall = 0, wilds = 0;
+ char *name = NULL, *cm = NULL;
+
+ if (IsServer(cptr) &&
+ (stat != 'd' && stat != 'p' && stat != 'q' && stat != 's' &&
+ stat != 'u' && stat != 'v') &&
+ !(stat == 'o' && IsOper(sptr)))
+ {
+ if (check_link(cptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]),
+ "STATS");
+ return 5;
+ }
+ }
+ if (parc == 3)
+ {
+ if (hunt_server(cptr, sptr, ":%s STATS %s %s",
+ 2, parc, parv) != HUNTED_ISME)
+ return 5;
+ }
+ else if (parc == 4)
+ {
+ if (hunt_server(cptr, sptr, ":%s STATS %s %s %s",
+ 2, parc, parv) != HUNTED_ISME)
+ return 5;
+ }
+
+ if (parc > 2)
+ {
+ name = parv[2];
+ if (!mycmp(name, ME))
+ doall = 2;
+ else if (match(name, ME) == 0)
+ doall = 1;
+ if (index(name, '*') || index(name, '?'))
+ wilds = 1;
+ if (parc > 3)
+ {
+ cm = parv[3];
+ if (!index(cm, '*') && !index(cm, '?'))
+ wilds = 0, doall = 0;
+ }
+ }
+ else
+ name = ME;
+
+ switch (stat)
+ {
+ case 'L' : case 'l' :
+ /*
+ * send info about connections which match, or all if the
+ * mask matches ME. Only restrictions are on those who
+ * are invisible not being visible to 'foreigners' who use
+ * a wild card based search to list it.
+ */
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = local[i]))
+ continue;
+#if 0
+ if (IsPerson(acptr) && IsInvisible(acptr) &&
+ (doall || wilds) && !(MyConnect(sptr) &&
+ IsLocal(sptr) && IsOper(sptr)) &&
+ !IsAnOper(acptr) && acptr != sptr)
+#endif
+ if (IsPerson(acptr) &&
+ (doall || wilds) &&
+ !(MyConnect(sptr) && IsAnOper(sptr)) &&
+ acptr != sptr)
+ continue;
+ if (!doall && wilds && match(name, acptr->name))
+ continue;
+ if (!(doall || wilds) &&
+ ((!cm && mycmp(name, acptr->name)) ||
+ (cm && match(cm, acptr->name))))
+ continue;
+ sendto_one(cptr, Lformat, ME,
+ RPL_STATSLINKINFO, parv[0],
+ get_client_name(acptr, isupper(stat)),
+ (int)DBufLength(&acptr->sendQ),
+ (int)acptr->sendM, (int)acptr->sendK,
+ (int)acptr->receiveM, (int)acptr->receiveK,
+ timeofday - acptr->firsttime);
+ }
+ break;
+#if defined(USE_IAUTH)
+ case 'a' : case 'A' : /* iauth configuration */
+ report_iauth_conf(sptr, parv[0]);
+ break;
+#endif
+ case 'B' : case 'b' : /* B conf lines */
+ report_configured_links(cptr, parv[0], CONF_BOUNCE);
+ break;
+ case 'c' : case 'C' : /* C and N conf lines */
+ report_configured_links(cptr, parv[0], CONF_CONNECT_SERVER|
+ CONF_ZCONNECT_SERVER|
+ CONF_NOCONNECT_SERVER);
+ break;
+ case 'd' : case 'D' : /* defines */
+ send_defines(cptr, parv[0]);
+ break;
+ case 'H' : case 'h' : /* H, L and D conf lines */
+ report_configured_links(cptr, parv[0],
+ CONF_HUB|CONF_LEAF|CONF_DENY);
+ break;
+ case 'I' : case 'i' : /* I (and i) conf lines */
+ report_configured_links(cptr, parv[0],
+ CONF_CLIENT|CONF_RCLIENT);
+ break;
+ case 'K' : case 'k' : /* K lines */
+ report_configured_links(cptr, parv[0],
+ (CONF_KILL|CONF_OTHERKILL));
+ break;
+ case 'M' : case 'm' : /* commands use/stats */
+ for (mptr = msgtab; mptr->cmd; mptr++)
+ if (mptr->count)
+ sendto_one(cptr, rpl_str(RPL_STATSCOMMANDS,
+ parv[0]), mptr->cmd,
+ mptr->count, mptr->bytes,
+ mptr->rcount);
+ break;
+ case 'o' : case 'O' : /* O (and o) lines */
+ report_configured_links(cptr, parv[0], CONF_OPS);
+ break;
+ case 'p' : case 'P' : /* ircd ping stats */
+ report_ping(sptr, parv[0]);
+ break;
+ case 'Q' : case 'q' : /* Q lines */
+ report_configured_links(cptr,parv[0],CONF_QUARANTINED_SERVER);
+ break;
+ case 'R' : case 'r' : /* usage */
+ send_usage(cptr, parv[0]);
+ break;
+ case 'S' : case 's' : /* S lines */
+ report_configured_links(cptr, parv[0], CONF_SERVICE);
+ break;
+ case 'T' : case 't' : /* various statistics */
+ tstats(cptr, parv[0]);
+ break;
+ case 'U' : case 'u' : /* uptime */
+ {
+ register time_t now;
+
+ now = timeofday - me.since;
+ sendto_one(sptr, rpl_str(RPL_STATSUPTIME, parv[0]),
+ now/86400, (now/3600)%24, (now/60)%60, now%60);
+ break;
+ }
+ case 'V' : case 'v' : /* V conf lines */
+ report_configured_links(cptr, parv[0], CONF_VER);
+ break;
+ case 'X' : case 'x' : /* lists */
+#ifdef DEBUGMODE
+ send_listinfo(cptr, parv[0]);
+#endif
+ break;
+ case 'Y' : case 'y' : /* Y lines */
+ report_classes(cptr, parv[0]);
+ break;
+ case 'Z' : /* memory use (OPER only) */
+ if (MyOper(sptr))
+ count_memory(cptr, parv[0], 1);
+ else
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
+ break;
+ case 'z' : /* memory use */
+ count_memory(cptr, parv[0], 0);
+ break;
+ default :
+ stat = '*';
+ break;
+ }
+ sendto_one(cptr, rpl_str(RPL_ENDOFSTATS, parv[0]), stat);
+ return 2;
+ }
+
+/*
+** m_users
+** parv[0] = sender prefix
+** parv[1] = servername
+*/
+int m_users(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+#ifdef ENABLE_USERS
+ char namebuf[10],linebuf[10],hostbuf[17];
+ int fd, flag = 0;
+#endif
+
+ if (hunt_server(cptr,sptr,":%s USERS :%s",1,parc,parv) == HUNTED_ISME)
+ {
+#ifdef ENABLE_USERS
+ if ((fd = utmp_open()) == -1)
+ {
+ sendto_one(sptr, err_str(ERR_FILEERROR, parv[0]),
+ "open", UTMP);
+ return 1;
+ }
+
+ sendto_one(sptr, rpl_str(RPL_USERSSTART, parv[0]));
+ while (utmp_read(fd, namebuf, linebuf,
+ hostbuf, sizeof(hostbuf)) == 0)
+ {
+ flag = 1;
+ sendto_one(sptr, rpl_str(RPL_USERS, parv[0]),
+ namebuf, linebuf, hostbuf);
+ }
+ if (flag == 0)
+ sendto_one(sptr, rpl_str(RPL_NOUSERS, parv[0]));
+
+ sendto_one(sptr, rpl_str(RPL_ENDOFUSERS, parv[0]));
+ (void)utmp_close(fd);
+#else
+ sendto_one(sptr, err_str(ERR_USERSDISABLED, parv[0]));
+#endif
+ }
+ else
+ return 3;
+ return 2;
+}
+
+/*
+** Note: At least at protocol level ERROR has only one parameter,
+** although this is called internally from other functions
+** --msa
+**
+** parv[0] = sender prefix
+** parv[*] = parameters
+*/
+int m_error(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ Reg char *para;
+
+ para = (parc > 1 && *parv[1] != '\0') ? parv[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 (IsPerson(cptr) || IsUnknown(cptr) || IsService(cptr))
+ return 2;
+ if (cptr == sptr)
+ sendto_flag(SCH_ERROR, "from %s -- %s",
+ get_client_name(cptr, FALSE), para);
+ else
+ sendto_flag(SCH_ERROR, "from %s via %s -- %s",
+ sptr->name, get_client_name(cptr,FALSE), para);
+ return 2;
+ }
+
+/*
+** m_help
+** parv[0] = sender prefix
+*/
+int m_help(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ int i;
+
+ for (i = 0; msgtab[i].cmd; i++)
+ sendto_one(sptr,":%s NOTICE %s :%s",
+ ME, parv[0], msgtab[i].cmd);
+ return 2;
+ }
+
+/*
+ * parv[0] = sender
+ * parv[1] = host/server mask.
+ * parv[2] = server to query
+ */
+int m_lusers(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ int s_count = 0, /* server */
+ c_count = 0, /* client (visible) */
+ u_count = 0, /* unknown */
+ i_count = 0, /* invisible client */
+ o_count = 0, /* oparator */
+ v_count = 0; /* service */
+ int m_client = 0, /* my clients */
+ m_server = 0, /* my server links */
+ m_service = 0; /* my services */
+ aClient *acptr;
+
+ if (parc > 2)
+ if (hunt_server(cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv)
+ != HUNTED_ISME)
+ return 3;
+
+ if (parc == 1 || !MyConnect(sptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_LUSERCLIENT, parv[0]),
+ istat.is_user[0] + istat.is_user[1],
+ istat.is_service, istat.is_serv);
+ if (istat.is_oper)
+ sendto_one(sptr, rpl_str(RPL_LUSEROP, parv[0]),
+ istat.is_oper);
+ if (istat.is_unknown > 0)
+ sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN, parv[0]),
+ istat.is_unknown);
+ if (istat.is_chan)
+ sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS, parv[0]),
+ istat.is_chan);
+ sendto_one(sptr, rpl_str(RPL_LUSERME, parv[0]),
+ istat.is_myclnt, istat.is_myservice,
+ istat.is_myserv);
+ return 2;
+ }
+ (void)collapse(parv[1]);
+ for (acptr = client; acptr; acptr = acptr->next)
+ {
+ if (!IsServer(acptr) && acptr->user)
+ {
+ if (match(parv[1], acptr->user->server))
+ continue;
+ }
+ else
+ if (match(parv[1], acptr->name))
+ continue;
+
+ switch (acptr->status)
+ {
+ case STAT_SERVER:
+ if (MyConnect(acptr))
+ m_server++;
+ /* flow thru */
+ case STAT_ME:
+ s_count++;
+ break;
+ case STAT_SERVICE:
+ if (MyConnect(acptr))
+ m_service++;
+ v_count++;
+ break;
+ case STAT_CLIENT:
+ if (IsOper(acptr))
+ o_count++;
+#ifdef SHOW_INVISIBLE_LUSERS
+ if (MyConnect(acptr))
+ m_client++;
+ if (!IsInvisible(acptr))
+ c_count++;
+ else
+ i_count++;
+#else
+ if (MyConnect(acptr))
+ {
+ if (IsInvisible(acptr))
+ {
+ if (IsAnOper(sptr))
+ m_client++;
+ }
+ else
+ m_client++;
+ }
+ if (!IsInvisible(acptr))
+ c_count++;
+ else
+ i_count++;
+#endif
+ break;
+ default:
+ u_count++;
+ break;
+ }
+ }
+#ifndef SHOW_INVISIBLE_LUSERS
+ if (IsAnOper(sptr) && i_count)
+#endif
+ sendto_one(sptr, rpl_str(RPL_LUSERCLIENT, parv[0]),
+ c_count + i_count, v_count, s_count);
+#ifndef SHOW_INVISIBLE_LUSERS
+ else
+ sendto_one(sptr, rpl_str(RPL_LUSERCLIENT, parv[0]),
+ c_count, v_count, s_count);
+#endif
+ if (o_count)
+ sendto_one(sptr, rpl_str(RPL_LUSEROP, parv[0]), o_count);
+ if (u_count > 0)
+ sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN, parv[0]), u_count);
+ if ((c_count = count_channels(sptr))>0)
+ sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS, parv[0]),
+ count_channels(sptr));
+ sendto_one(sptr, rpl_str(RPL_LUSERME, parv[0]), m_client, m_service,
+ m_server);
+ return 2;
+ }
+
+
+/***********************************************************************
+ * m_connect() - Added by Jto 11 Feb 1989
+ ***********************************************************************/
+
+/*
+** m_connect
+** parv[0] = sender prefix
+** parv[1] = servername
+** parv[2] = port number
+** parv[3] = remote server
+*/
+int m_connect(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ int port, tmpport, retval;
+ aConfItem *aconf;
+ aClient *acptr;
+
+ if (parc > 3 && IsLocOp(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
+ return 1;
+ }
+
+ if (hunt_server(cptr,sptr,":%s CONNECT %s %s :%s",
+ 3,parc,parv) != HUNTED_ISME)
+ return 1;
+
+ if (parc < 3 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
+ "CONNECT");
+ return 0;
+ }
+
+ if ((acptr = find_name(parv[1], NULL))
+ || (acptr = find_mask(parv[1], NULL)))
+ {
+ sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
+ ME, parv[0], parv[1], "already exists from",
+ acptr->from->name);
+ return 0;
+ }
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if ((aconf->status == CONF_CONNECT_SERVER ||
+ aconf->status == CONF_ZCONNECT_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 ||
+ aconf->status == CONF_ZCONNECT_SERVER) &&
+ (match(parv[1], aconf->host) == 0 ||
+ match(parv[1], index(aconf->host, '@')+1) == 0))
+ break;
+
+ if (!aconf)
+ {
+ sendto_one(sptr,
+ "NOTICE %s :Connect: Host %s not listed in irc.conf",
+ parv[0], parv[1]);
+ return 0;
+ }
+ /*
+ ** Get port number from user, if given. If not specified,
+ ** use the default form configuration structure. If missing
+ ** from there, then use the precompiled default.
+ */
+ tmpport = port = aconf->port;
+ if ((port = atoi(parv[2])) <= 0)
+ {
+ sendto_one(sptr, "NOTICE %s :Connect: Illegal port number",
+ parv[0]);
+ return 0;
+ }
+ else if (port <= 0)
+ {
+ sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
+ ME, parv[0]);
+ 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, 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:
+ sendto_one(sptr, ":%s NOTICE %s :*** Connecting to %s[%s].",
+ ME, parv[0], aconf->host, aconf->name);
+ sendto_flag(SCH_NOTICE, "Connecting to %s[%s] by %s",
+ aconf->host, aconf->name,
+ get_client_name(sptr, FALSE));
+ break;
+ case -1:
+ sendto_one(sptr, ":%s NOTICE %s :*** Couldn't connect to %s.",
+ ME, parv[0], aconf->host);
+ sendto_flag(SCH_NOTICE, "Couldn't connect to %s by %s",
+ aconf->host, get_client_name(sptr, FALSE));
+ break;
+ case -2:
+ sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
+ ME, parv[0], aconf->host);
+ sendto_flag(SCH_NOTICE, "Connect by %s to unknown host %s",
+ get_client_name(sptr, FALSE), aconf->host);
+ break;
+ default:
+ sendto_one(sptr,
+ ":%s NOTICE %s :*** Connection to %s failed: %s",
+ ME, parv[0], aconf->host, strerror(retval));
+ sendto_flag(SCH_NOTICE, "Connection to %s by %s failed: %s",
+ aconf->host, get_client_name(sptr, FALSE),
+ strerror(retval));
+ }
+ aconf->port = tmpport;
+ return 0;
+ }
+
+/*
+** m_wallops (write to *all* opers currently online)
+** parv[0] = sender prefix
+** parv[1] = message text
+*/
+int m_wallops(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ char *message, *pv[4];
+
+ message = parc > 1 ? parv[1] : NULL;
+
+ if (BadPtr(message))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
+ "WALLOPS");
+ return 1;
+ }
+
+ if (!IsServer(sptr))
+ {
+ pv[0] = parv[0];
+ pv[1] = "+wallops";
+ pv[2] = message;
+ pv[3] = NULL;
+ return m_private(cptr, sptr, 3, pv);
+ }
+ sendto_ops_butone(IsServer(cptr) ? cptr : NULL, sptr,
+ ":%s WALLOPS :%s", parv[0], message);
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_WALLOP, NULL, sptr,
+ ":%s WALLOP :%s", parv[0], message);
+#endif
+ return 2;
+ }
+
+/*
+** m_time
+** parv[0] = sender prefix
+** parv[1] = servername
+*/
+int m_time(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ if (hunt_server(cptr,sptr,":%s TIME :%s",1,parc,parv) == HUNTED_ISME)
+ sendto_one(sptr, rpl_str(RPL_TIME, parv[0]), ME, date((long)0));
+ return 2;
+}
+
+
+/*
+** m_admin
+** parv[0] = sender prefix
+** parv[1] = servername
+*/
+int m_admin(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ aConfItem *aconf;
+
+ if (IsRegistered(cptr) && /* only local query for unregistered */
+ hunt_server(cptr,sptr,":%s ADMIN :%s",1,parc,parv) != HUNTED_ISME)
+ return 3;
+ if ((aconf = find_admin()) && aconf->host && aconf->passwd
+ && aconf->name)
+ {
+ sendto_one(sptr, rpl_str(RPL_ADMINME, parv[0]), ME);
+ sendto_one(sptr, rpl_str(RPL_ADMINLOC1, parv[0]), aconf->host);
+ sendto_one(sptr, rpl_str(RPL_ADMINLOC2, parv[0]),
+ aconf->passwd);
+ sendto_one(sptr, rpl_str(RPL_ADMINEMAIL, parv[0]),
+ aconf->name);
+ }
+ else
+ sendto_one(sptr, err_str(ERR_NOADMININFO, parv[0]), ME);
+ return 2;
+ }
+
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+/*
+** m_rehash
+**
+*/
+int m_rehash(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ sendto_one(sptr, rpl_str(RPL_REHASHING, parv[0]),
+ mybasename(configfile));
+ sendto_flag(SCH_NOTICE,
+ "%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, sptr, (parc > 1) ? ((*parv[1] == 'q')?2:0) : 0);
+}
+#endif
+
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+/*
+** m_restart
+**
+*/
+int m_restart(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aClient *acptr;
+ Reg int i;
+ char killer[HOSTLEN * 2 + USERLEN + 5];
+
+ strcpy(killer, get_client_name(sptr, TRUE));
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = local[i]))
+ continue;
+ if (IsClient(acptr) || IsService(acptr))
+ {
+ sendto_one(acptr,
+ ":%s NOTICE %s :Server Restarting. %s",
+ ME, acptr->name, killer);
+ acptr->exitc = EXITC_DIE;
+ if (IsClient(acptr))
+ exit_client(acptr, acptr, &me,
+ "Server Restarting");
+ /* services are kept for logging purposes */
+ }
+ else if (IsServer(acptr))
+ sendto_one(acptr, ":%s ERROR :Restarted by %s",
+ ME, killer);
+ }
+ flush_connections(me.fd);
+
+ SPRINTF(buf, "RESTART by %s", get_client_name(sptr, TRUE));
+ restart(buf);
+ /*NOT REACHED*/
+ return 0;
+}
+#endif
+
+/*
+** m_trace
+** parv[0] = sender prefix
+** parv[1] = servername
+*/
+int m_trace(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg int i;
+ Reg aClient *acptr;
+ aClass *cltmp;
+ char *tname;
+ int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
+ int wilds, dow;
+
+ if (parc > 1)
+ tname = parv[1];
+ else
+ tname = ME;
+
+ switch (hunt_server(cptr, sptr, ":%s TRACE :%s", 1, parc, parv))
+ {
+ case HUNTED_PASS: /* note: gets here only if parv[1] exists */
+ {
+ aClient *ac2ptr;
+
+ ac2ptr = next_client(client, parv[1]);
+ sendto_one(sptr, rpl_str(RPL_TRACELINK, parv[0]),
+ version, debugmode, tname, ac2ptr->from->name,
+ ac2ptr->from->serv->version,
+ (ac2ptr->from->flags & FLAGS_ZIP) ? "z" : "",
+ timeofday - ac2ptr->from->firsttime,
+ (int)DBufLength(&ac2ptr->from->sendQ),
+ (int)DBufLength(&sptr->from->sendQ));
+ return 5;
+ }
+ case HUNTED_ISME:
+ break;
+ default:
+ return 1;
+ }
+
+ doall = (parv[1] && (parc > 1)) ? !match(tname, ME): TRUE;
+ wilds = !parv[1] || index(tname, '*') || index(tname, '?');
+ dow = wilds || doall;
+
+ if (doall) {
+ for (i = 0; i < MAXCONNECTIONS; i++)
+ link_s[i] = 0, link_u[i] = 0;
+ for (acptr = client; acptr; acptr = acptr->next)
+#ifdef SHOW_INVISIBLE_LUSERS
+ if (IsPerson(acptr))
+ link_u[acptr->from->fd]++;
+#else
+ if (IsPerson(acptr) &&
+ (!IsInvisible(acptr) || IsOper(sptr)))
+ link_u[acptr->from->fd]++;
+#endif
+ else if (IsServer(acptr))
+ link_s[acptr->from->fd]++;
+ }
+
+ /* report all direct connections */
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ char *name;
+ int class;
+
+ if (!(acptr = local[i])) /* Local Connection? */
+ continue;
+ if (IsPerson(acptr) && IsInvisible(acptr) && dow &&
+ !(MyConnect(sptr) && IsAnOper(sptr)) &&
+ !IsAnOper(acptr) && (acptr != sptr))
+ continue;
+ if (!doall && wilds && match(tname, acptr->name))
+ continue;
+ if (!dow && mycmp(tname, acptr->name))
+ continue;
+ name = get_client_name(acptr,FALSE);
+ class = get_client_class(acptr);
+
+ switch(acptr->status)
+ {
+ case STAT_CONNECTING:
+ sendto_one(sptr, rpl_str(RPL_TRACECONNECTING,
+ parv[0]), class, name);
+ break;
+ case STAT_HANDSHAKE:
+ sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE, parv[0]),
+ class, name);
+ break;
+ case STAT_ME:
+ break;
+ case STAT_UNKNOWN:
+ if (IsAnOper(sptr) || MyClient(sptr))
+ sendto_one(sptr,
+ rpl_str(RPL_TRACEUNKNOWN, parv[0]),
+ class, name);
+ break;
+ case STAT_CLIENT:
+ /* Only opers see users if there is a wildcard
+ * but anyone can see all the opers.
+ */
+/*
+ if (IsOper(sptr) &&
+ (MyClient(sptr) || !(dow && IsInvisible(acptr)))
+ || !dow || IsAnOper(acptr))
+ {
+ if (IsOper(sptr) && !(dow || IsInvisible(acptr)) ||
+ (IsOper(sptr) && IsLocal(sptr)) ||
+ !dow || IsAnOper(acptr))
+*/
+ if (IsAnOper(acptr))
+ sendto_one(sptr,
+ rpl_str(RPL_TRACEOPERATOR, parv[0]),
+ class, name);
+ else if (!dow || (MyConnect(sptr) && IsAnOper(sptr)))
+ sendto_one(sptr,
+ rpl_str(RPL_TRACEUSER, parv[0]),
+ class, name);
+/*
+ {
+ if (IsAnOper(acptr))
+ sendto_one(sptr,
+ rpl_str(RPL_TRACEOPERATOR,
+ parv[0]), class, name);
+ else
+ sendto_one(sptr, rpl_str(RPL_TRACEUSER,
+ parv[0]), class, name);
+ }
+*/
+ break;
+ case STAT_SERVER:
+ if (acptr->serv->user)
+ sendto_one(sptr, rpl_str(RPL_TRACESERVER,
+ parv[0]), class, link_s[i],
+ link_u[i], name, acptr->serv->by,
+ acptr->serv->user->username,
+ acptr->serv->user->host,
+ acptr->serv->version,
+ (acptr->flags & FLAGS_ZIP) ?"z":"");
+ else
+ sendto_one(sptr, rpl_str(RPL_TRACESERVER,
+ parv[0]), class, link_s[i],
+ link_u[i], name,
+ *(acptr->serv->by) ?
+ acptr->serv->by : "*", "*", ME,
+ acptr->serv->version,
+ (acptr->flags & FLAGS_ZIP) ?"z":""); break;
+ case STAT_RECONNECT:
+ sendto_one(sptr, rpl_str(RPL_TRACERECONNECT, parv[0]),
+ class, name);
+ break;
+ case STAT_SERVICE:
+ sendto_one(sptr, rpl_str(RPL_TRACESERVICE, parv[0]),
+ class, name, acptr->service->type,
+ acptr->service->wants);
+ break;
+ case STAT_LOG:
+ sendto_one(sptr, rpl_str(RPL_TRACELOG, parv[0]),
+ ME, acptr->port);
+ break;
+ default: /* ...we actually shouldn't come here... --msa */
+ sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE, parv[0]),
+ name);
+ break;
+ }
+ }
+
+ /*
+ * Add these lines to summarize the above which can get rather long
+ * and messy when done remotely - Avalon
+ */
+ if (IsPerson(sptr) && SendWallops(sptr))
+ for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
+ if (Links(cltmp) > 0)
+ sendto_one(sptr, rpl_str(RPL_TRACECLASS, parv[0]),
+ Class(cltmp), Links(cltmp));
+ sendto_one(sptr, rpl_str(RPL_TRACEEND, parv[0]), tname, version,
+ debugmode);
+ return 2;
+ }
+
+/*
+** m_motd
+** parv[0] = sender prefix
+** parv[1] = servername
+*/
+int m_motd(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+#ifdef CACHED_MOTD
+ register aMotd *temp;
+ struct tm *tm;
+#else
+ int fd;
+ char line[80];
+ Reg char *tmp;
+ struct stat sb;
+ struct tm *tm;
+#endif
+
+ if (check_link(cptr))
+ {
+ sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]), "MOTD");
+ return 5;
+ }
+ if (hunt_server(cptr, sptr, ":%s MOTD :%s", 1,parc,parv)!=HUNTED_ISME)
+ return 5;
+#ifdef CACHED_MOTD
+ tm = &motd_tm;
+ if (motd == NULL)
+ {
+ sendto_one(sptr, err_str(ERR_NOMOTD, parv[0]));
+ return 1;
+ }
+ sendto_one(sptr, rpl_str(RPL_MOTDSTART, parv[0]), ME);
+ sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", ME, RPL_MOTD,
+ parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
+ tm->tm_hour, tm->tm_min);
+ temp = motd;
+ for(temp=motd;temp != NULL;temp = temp->next)
+ sendto_one(sptr, rpl_str(RPL_MOTD, parv[0]), temp->line);
+ sendto_one(sptr, rpl_str(RPL_ENDOFMOTD, parv[0]));
+ return 2;
+#else
+ /*
+ * stop NFS hangs...most systems should be able to open a file in
+ * 3 seconds. -avalon (curtesy of wumpus)
+ */
+ (void)alarm(3);
+ fd = open(IRCDMOTD_PATH, O_RDONLY);
+ (void)alarm(0);
+ if (fd == -1)
+ {
+ sendto_one(sptr, err_str(ERR_NOMOTD, parv[0]));
+ return 1;
+ }
+ (void)fstat(fd, &sb);
+ sendto_one(sptr, rpl_str(RPL_MOTDSTART, parv[0]), ME);
+ tm = localtime(&sb.st_mtime);
+ sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", ME, RPL_MOTD,
+ parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
+ tm->tm_hour, tm->tm_min);
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ while (dgets(fd, line, sizeof(line)-1) > 0)
+ {
+ if ((tmp = (char *)index(line,'\n')))
+ *tmp = '\0';
+ if ((tmp = (char *)index(line,'\r')))
+ *tmp = '\0';
+ sendto_one(sptr, rpl_str(RPL_MOTD, parv[0]), line);
+ }
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ sendto_one(sptr, rpl_str(RPL_ENDOFMOTD, parv[0]));
+ (void)close(fd);
+ return 2;
+#endif /* CACHED_MOTD */
+}
+
+/*
+** m_close - added by Darren Reed Jul 13 1992.
+*/
+int m_close(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aClient *acptr;
+ Reg int i;
+ int closed = 0;
+
+ for (i = highest_fd; i; i--)
+ {
+ if (!(acptr = local[i]))
+ continue;
+ if (!IsUnknown(acptr) && !IsConnecting(acptr) &&
+ !IsHandshake(acptr))
+ continue;
+ sendto_one(sptr, rpl_str(RPL_CLOSING, parv[0]),
+ get_client_name(acptr, TRUE), acptr->status);
+ (void)exit_client(acptr, acptr, &me, "Oper Closing");
+ closed++;
+ }
+ sendto_one(sptr, rpl_str(RPL_CLOSEEND, parv[0]), closed);
+ return 1;
+}
+
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+int m_die(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aClient *acptr;
+ Reg int i;
+ char killer[HOSTLEN * 2 + USERLEN + 5];
+
+ strcpy(killer, get_client_name(sptr, TRUE));
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = local[i]))
+ continue;
+ if (IsClient(acptr) || IsService(acptr))
+ {
+ sendto_one(acptr,
+ ":%s NOTICE %s :Server Terminating. %s",
+ ME, acptr->name, killer);
+ acptr->exitc = EXITC_DIE;
+ if (IsClient(acptr))
+ exit_client(acptr, acptr, &me, "Server died");
+ /* services are kept for logging purposes */
+ }
+ else if (IsServer(acptr))
+ sendto_one(acptr, ":%s ERROR :Terminated by %s",
+ ME, killer);
+ }
+ flush_connections(me.fd);
+ (void)s_die(0);
+ return 0;
+}
+#endif
+
+/*
+** storing server names in User structures is a real waste,
+** the following functions change it to only store a pointer.
+** A better way might be to store in Server structure and use servp. -krys
+*/
+
+static char **server_name = NULL;
+static int server_max = 0, server_num = 0;
+
+/*
+** find_server_string
+**
+** Given an index, this will return a pointer to the corresponding
+** (already allocated) string
+*/
+char *
+find_server_string(snum)
+int snum;
+{
+ if (snum < server_num && snum >= 0)
+ return server_name[snum];
+ /* request for a bogus snum value, something is wrong */
+ sendto_flag(SCH_ERROR, "invalid index for server_name[] : %d (%d,%d)",
+ snum, server_num, server_max);
+ return NULL;
+}
+
+/*
+** find_server_num
+**
+** Given a server name, this will return the index of the corresponding
+** string. This index can be used with find_server_name_from_num().
+** If the string doesn't exist already, it will be allocated.
+*/
+int
+find_server_num(sname)
+char *sname;
+{
+ Reg int i = 0;
+
+ while (i < server_num)
+ {
+ if (!strcasecmp(server_name[i], sname))
+ break;
+ i++;
+ }
+ if (i < server_num)
+ return i;
+ if (i == server_max)
+ {
+ /* server_name[] array is full, let's make it bigger! */
+ if (server_name)
+ server_name = (char **) MyRealloc((char *)server_name,
+ sizeof(char *)*(server_max+=50));
+ else
+ server_name = (char **) MyMalloc(sizeof(char *)*(server_max=50));
+ }
+ server_name[server_num] = mystrdup(sname);
+ return server_num++;
+}
+
+/*
+** check_link (added 97/12 to prevent abuse)
+** routine which tries to find out how healthy a link is.
+** useful to know if more strain may be imposed on the link or not.
+**
+** returns 0 if link load is light, -1 otherwise.
+*/
+static int
+check_link(cptr)
+aClient *cptr;
+{
+ if (!IsServer(cptr))
+ return 0;
+ if (!(bootopt & BOOT_PROT))
+ return 0;
+
+ ircstp->is_ckl++;
+ if ((int)DBufLength(&cptr->sendQ) > 65536) /* SendQ is already (too) high*/
+ {
+ cptr->serv->lastload = timeofday;
+ ircstp->is_cklQ++;
+ return -1;
+ }
+ if (timeofday - cptr->firsttime < 60) /* link is too young */
+ {
+ ircstp->is_ckly++;
+ return -1;
+ }
+ if (timeofday - cptr->serv->lastload > 30)
+ /* last request more than 30 seconds ago => OK */
+ {
+ cptr->serv->lastload = timeofday;
+ ircstp->is_cklok++;
+ return 0;
+ }
+ if (timeofday - cptr->serv->lastload > 15
+ && (int)DBufLength(&cptr->sendQ) < CHREPLLEN)
+ /* last request between 15 and 30 seconds ago, but little SendQ */
+ {
+ cptr->serv->lastload = timeofday;
+ ircstp->is_cklq++;
+ return 0;
+ }
+ ircstp->is_cklno++;
+ return -1;
+}
diff --git a/ircd/s_serv_ext.h b/ircd/s_serv_ext.h
new file mode 100644
index 0000000..4beb828
--- /dev/null
+++ b/ircd/s_serv_ext.h
@@ -0,0 +1,72 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_serv_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_serv.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef S_SERV_C
+#define EXTERN extern
+#else /* S_SERV_C */
+#define EXTERN
+#endif /* S_SERV_C */
+EXTERN int m_version __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_squit __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int check_version __P((aClient *cptr));
+EXTERN int m_server __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_server_estab __P((Reg aClient *cptr));
+EXTERN int m_reconnect __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_info __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_links __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_summon __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_stats __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_users __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_error __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_help __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_lusers __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_connect __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_wallops __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_time __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_admin __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_trace __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_motd __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_close __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN char *find_server_string __P((int snum));
+EXTERN int find_server_num __P((char *sname));
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+EXTERN int m_rehash __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+#endif /* OPER_REHASH || LOCOP_REHASH */
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+EXTERN int m_restart __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+#endif /* OPER_RESTART || LOCOP_RESTART */
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+EXTERN int m_die __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+#endif /* OPER_DIE || LOCOP_DIE */
+#undef EXTERN
diff --git a/ircd/s_service.c b/ircd/s_service.c
new file mode 100644
index 0000000..a86bd02
--- /dev/null
+++ b/ircd/s_service.c
@@ -0,0 +1,708 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_service.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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_service.c,v 1.30 1999/07/02 16:49:37 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_SERVICE_C
+#include "s_externs.h"
+#undef S_SERVICE_C
+
+aService *svctop = NULL;
+
+aService *make_service(cptr)
+aClient *cptr;
+{
+ Reg aService *svc = cptr->service;
+
+ if (svc)
+ return svc;
+
+ cptr->service = svc = (aService *)MyMalloc(sizeof(*svc));
+ bzero((char *)svc, sizeof(*svc));
+ svc->bcptr = cptr;
+ if (svctop)
+ svctop->prevs = svc;
+ svc->nexts = svctop;
+ svc->prevs = NULL; /* useless */
+ svctop = svc;
+ return svc;
+}
+
+
+void free_service(cptr)
+aClient *cptr;
+{
+ Reg aService *serv;
+
+ if ((serv = cptr->service))
+ {
+ if (serv->nexts)
+ serv->nexts->prevs = serv->prevs;
+ if (serv->prevs)
+ serv->prevs->nexts = serv->nexts;
+ if (svctop == serv)
+ svctop = serv->nexts;
+ if (serv->servp)
+ free_server(serv->servp, cptr);
+ if (serv->server)
+ MyFree(serv->server);
+ MyFree((char *)serv);
+ cptr->service = NULL;
+ }
+}
+
+
+static aClient *best_service(name, cptr)
+char *name;
+aClient *cptr;
+{
+ Reg aClient *acptr = NULL;
+ Reg aClient *bcptr;
+ Reg aService *sp;
+ int len = strlen(name);
+
+ if (!index(name, '@') || !(acptr = find_service(name, cptr)))
+ for (sp = svctop; sp; sp = sp->nexts)
+ if ((bcptr = sp->bcptr) &&
+ !myncmp(name, bcptr->name, len))
+ {
+ acptr = bcptr;
+ break;
+ }
+ return (acptr ? acptr : cptr);
+}
+
+
+#ifdef USE_SERVICES
+/*
+** check_services_butone
+** check all local services except `cptr', and send `fmt' according to:
+** action type on notice
+** server origin
+*/
+#if ! USE_STDARG
+void check_services_butone(action, server, cptr, fmt, p1, p2, p3, p4,
+ p5, p6, p7, p8)
+long action;
+aClient *cptr; /* shouldn't this be named sptr? */
+char *fmt, *server;
+void *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+#else
+void check_services_butone(long action, char *server, aClient *cptr, char *fmt, ...)
+#endif
+{
+ char nbuf[NICKLEN + USERLEN + HOSTLEN + 3];
+ Reg aClient *acptr;
+ Reg int i;
+
+ *nbuf = '\0';
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = local[i]) || !IsService(acptr) ||
+ (cptr && acptr == cptr->from))
+ continue;
+ /*
+ ** found a (local) service, check if action matches what's
+ ** wanted AND if it comes from a server matching the dist
+ */
+ if ((acptr->service->wants & action)
+ && (!server || !match(acptr->service->dist, server)))
+ if ((acptr->service->wants & SERVICE_WANT_PREFIX) &&
+ cptr && IsRegisteredUser(cptr) &&
+ (action & SERVICE_MASK_PREFIX))
+ {
+#if USE_STDARG
+ char buf[2048];
+ va_list va;
+ va_start(va, fmt);
+ va_arg(va, char *);
+ vsprintf(buf, fmt+3, va);
+ va_end(va);
+#endif
+ sprintf(nbuf, "%s!%s@%s", cptr->name,
+ cptr->user->username,cptr->user->host);
+
+#if ! USE_STDARG
+ sendto_one(acptr, fmt, nbuf, p2, p3, p4, p5,
+ p6, p7, p8);
+#else
+ sendto_one(acptr, ":%s%s", nbuf, buf);
+#endif
+ }
+ else
+ {
+#if ! USE_STDARG
+ sendto_one(acptr, fmt, p1, p2, p3, p4, p5,
+ p6, p7, p8);
+#else
+ va_list va;
+ va_start(va, fmt);
+ vsendto_one(acptr, fmt, va);
+ va_end(va);
+#endif
+ }
+ }
+ return;
+}
+
+/*
+** sendnum_toone
+** send the NICK + USER + UMODE for sptr to cptr according to wants
+*/
+static void sendnum_toone (cptr, wants, sptr, umode)
+aClient *cptr, *sptr;
+char *umode;
+int wants;
+{
+
+ if (!*umode)
+ umode = "+";
+
+ if (wants & SERVICE_WANT_EXTNICK)
+ /* extended NICK syntax */
+ sendto_one(cptr, "NICK %s %d %s %s %s %s :%s",
+ (wants & SERVICE_WANT_NICK) ? sptr->name : ".",
+ sptr->hopcount + 1,
+ (wants & SERVICE_WANT_USER) ? sptr->user->username
+ : ".",
+ (wants & SERVICE_WANT_USER) ? sptr->user->host :".",
+ (wants & SERVICE_WANT_USER) ?
+ ((wants & SERVICE_WANT_TOKEN) ?
+ sptr->user->servp->tok : sptr->user->server) : ".",
+ (wants & SERVICE_WANT_UMODE) ? umode : "+",
+ (wants & SERVICE_WANT_USER) ? sptr->info : "");
+ else
+ /* old style NICK + USER + UMODE */
+ {
+ char nbuf[NICKLEN + USERLEN + HOSTLEN + 3];
+ char *prefix;
+
+ if (wants & SERVICE_WANT_PREFIX)
+ {
+ sprintf(nbuf, "%s!%s@%s", sptr->name,
+ sptr->user->username, sptr->user->host);
+ prefix = nbuf;
+ }
+ else
+ prefix = sptr->name;
+
+ if (wants & SERVICE_WANT_NICK)
+ sendto_one(cptr, "NICK %s :%d", sptr->name,
+ sptr->hopcount+1);
+ if (wants & SERVICE_WANT_USER)
+ sendto_one(cptr, ":%s USER %s %s %s :%s", prefix,
+ sptr->user->username, sptr->user->host,
+ (wants & SERVICE_WANT_TOKEN)?
+ sptr->user->servp->tok : sptr->user->server,
+ sptr->info);
+ if (wants & SERVICE_WANT_UMODE|SERVICE_WANT_OPER)
+ sendto_one(cptr, ":%s MODE %s %s", prefix, sptr->name,
+ umode);
+ }
+}
+
+/*
+** check_services_num
+** check all local services to eventually send NICK + USER + UMODE
+** for new client sptr
+*/
+void check_services_num(sptr, umode)
+aClient *sptr;
+char *umode;
+{
+ Reg aClient *acptr;
+ Reg int i;
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = local[i]) || !IsService(acptr))
+ continue;
+ /*
+ ** found a (local) service, check if action matches what's
+ ** wanted AND if it comes from a server matching the dist
+ */
+ if ((acptr->service->wants & SERVICE_MASK_NUM)
+ && !match(acptr->service->dist, sptr->user->server))
+ sendnum_toone(acptr, acptr->service->wants, sptr,
+ umode);
+ }
+}
+
+
+aConfItem *find_conf_service(cptr, type, aconf)
+aClient *cptr;
+aConfItem *aconf;
+int type;
+{
+ static char uhost[HOSTLEN+USERLEN+3];
+ Reg aConfItem *tmp;
+ char *s;
+ struct hostent *hp;
+ int i;
+
+ for (tmp = conf; tmp; tmp = tmp->next)
+ {
+ /*
+ ** Accept if the *real* hostname (usually sockethost)
+ ** matches host field of the configuration, the name field
+ ** is the same, the type match is correct and nobody else
+ ** is using this S-line.
+ */
+ if (!(tmp->status & CONF_SERVICE))
+ continue;
+ Debug((DEBUG_INFO,"service: cl=%d host (%s) name (%s) port=%d",
+ tmp->clients, tmp->host, tmp->name, tmp->port));
+ Debug((DEBUG_INFO,"service: host (%s) name (%s) type=%d",
+ cptr->sockhost, cptr->name, type));
+ if (tmp->clients || (type && tmp->port != type) ||
+ mycmp(tmp->name, cptr->name))
+ continue;
+ if ((hp = cptr->hostp))
+ for (s = hp->h_name, i = 0; s; s = hp->h_aliases[i++])
+ {
+ SPRINTF(uhost, "%s@%s", cptr->username, s);
+ if (match(tmp->host, uhost) == 0)
+ return tmp;
+ }
+ SPRINTF(uhost, "%s@%s", cptr->username, cptr->sockhost);
+ if (match(tmp->host, uhost) == 0)
+ return tmp;
+ }
+ return aconf;
+}
+#endif
+
+
+/*
+** m_service
+**
+** parv[0] = sender prefix
+** parv[1] = service name
+** parv[2] = server token
+** parv[3] = distribution code
+** parv[4] = service type
+** parv[5] = hopcount
+** parv[6] = info
+*/
+int m_service(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aClient *acptr = NULL;
+ Reg aService *svc;
+#ifdef USE_SERVICES
+ Reg aConfItem *aconf;
+#endif
+ aServer *sp = NULL;
+ char *dist, *server = NULL, *info, *stok;
+ int type, metric = 0, i;
+ char *mlname;
+
+ if (sptr->user)
+ {
+ sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0]));
+ return 1;
+ }
+
+ if (parc < 7 || *parv[1] == '\0' || *parv[2] == '\0' ||
+ *parv[3] == '\0' || *parv[6] == '\0')
+ {
+ sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS,
+ BadPtr(parv[0]) ? "*" : parv[0]), "SERVICE");
+ return 1;
+ }
+
+ /* Copy parameters into better documenting variables */
+
+ /*
+ * Change the sender's origin.
+ */
+ if (IsServer(cptr))
+ {
+ sptr = make_client(cptr);
+ add_client_to_list(sptr);
+ strncpyzt(sptr->name, parv[1], sizeof(sptr->name));
+ server = parv[2];
+ metric = atoi(parv[5]);
+ sp = find_tokserver(atoi(server), cptr, NULL);
+ if (!sp)
+ {
+ sendto_flag(SCH_ERROR,
+ "ERROR: SERVICE:%s without SERVER:%s from %s",
+ sptr->name, server,
+ get_client_name(cptr, FALSE));
+ return exit_client(NULL, sptr, &me, "No Such Server");
+ }
+ if (match(parv[3], ME))
+ {
+ sendto_flag(SCH_ERROR,
+ "ERROR: SERVICE:%s DIST:%s from %s", sptr->name,
+ parv[3], get_client_name(cptr, FALSE));
+ return exit_client(NULL, sptr, &me,
+ "Distribution code mismatch");
+ }
+ }
+#ifndef USE_SERVICES
+ else
+ {
+ sendto_one(cptr, "ERROR :Server doesn't support services");
+ return 1;
+ }
+#endif
+
+ dist = parv[3];
+ type = atoi(parv[4]);
+ info = parv[6];
+
+#ifdef USE_SERVICES
+ if (!IsServer(cptr))
+ {
+ metric = 0;
+ server = ME;
+ sp = me.serv;
+ if (!do_nick_name(parv[1], 0))
+ {
+ sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME,
+ parv[0]), parv[1]);
+ return 1;
+ }
+ if (strlen(parv[1]) + strlen(server) + 2 >= (size_t) HOSTLEN)
+ {
+ sendto_one(acptr, "ERROR :Servicename is too long.");
+ sendto_flag(SCH_ERROR,
+ "Access for service %d (%s) denied (%s)",
+ type, parv[1], "servicename too long");
+ return exit_client(cptr, sptr, &me, "Name too long");
+ }
+
+ strncpyzt(sptr->name, parv[1], sizeof(sptr->name));
+ if (!(aconf = find_conf_service(sptr, type, NULL)))
+ {
+ sendto_one(sptr,
+ "ERROR :Access denied (service %d) %s",
+ type, get_client_name(sptr, TRUE));
+ sendto_flag(SCH_ERROR,
+ "Access denied (service %d) %s", type,
+ get_client_name(sptr, TRUE));
+ return exit_client(cptr, sptr, &me, "Not enabled");
+ }
+
+ if (!BadPtr(aconf->passwd) &&
+ !StrEq(aconf->passwd, sptr->passwd))
+ {
+ sendto_flag(SCH_ERROR,
+ "Access denied: (passwd mismatch) %s",
+ get_client_name(sptr, TRUE));
+ return exit_client(cptr, sptr, &me, "Bad Password");
+ }
+
+ (void)strcat(sptr->name, "@"), strcat(sptr->name, server);
+ if (find_service(sptr->name, NULL))
+ {
+ sendto_flag(SCH_ERROR, "Service %s already exists",
+ get_client_name(sptr, TRUE));
+ return exit_client(cptr, sptr, &me, "Service Exists");
+ }
+ attach_conf(sptr, aconf);
+ sendto_one(sptr, rpl_str(RPL_YOURESERVICE, sptr->name),
+ sptr->name);
+ sendto_one(sptr, rpl_str(RPL_YOURHOST, sptr->name),
+ get_client_name(&me, FALSE), version);
+ sendto_one(sptr, rpl_str(RPL_MYINFO, sptr->name), ME, version);
+ sendto_flag(SCH_NOTICE, "Service %s connected",
+ get_client_name(sptr, TRUE));
+ istat.is_unknown--;
+ istat.is_myservice++;
+ }
+#endif
+
+ istat.is_service++;
+ svc = make_service(sptr);
+ SetService(sptr);
+ svc->servp = sp;
+ sp->refcnt++;
+ svc->server = mystrdup(sp->bcptr->name);
+ strncpyzt(svc->dist, dist, HOSTLEN);
+ if (sptr->info != DefInfo)
+ MyFree(sptr->info);
+ if (strlen(info) > REALLEN) info[REALLEN] = '\0';
+ sptr->info = mystrdup(info);
+ svc->wants = 0;
+ svc->type = type;
+ sptr->hopcount = metric;
+ reorder_client_in_list(sptr);
+ (void)add_to_client_hash_table(sptr->name, sptr);
+
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_SERVICE, NULL, sptr,
+ "SERVICE %s %s %s %d %d :%s", sptr->name,
+ server, dist, type, metric, info);
+#endif
+ sendto_flag(SCH_SERVICE, "Received SERVICE %s from %s (%s %d %s)",
+ sptr->name, get_client_name(cptr, TRUE), dist, metric,
+ info);
+
+ for (i = fdas.highest; i >= 0; i--)
+ {
+ if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) ||
+ acptr == cptr)
+ continue;
+ if (match(dist, acptr->name))
+ continue;
+ mlname = my_name_for_link(ME, acptr->serv->nline->port);
+ if (*mlname == '*' && match(mlname, sptr->service->server)== 0)
+ stok = me.serv->tok;
+ else
+ stok = sp->tok;
+ sendto_one(acptr, "SERVICE %s %s %s %d %d :%s", sptr->name,
+ stok, dist, type, metric+1, info);
+ }
+ return 0;
+}
+
+
+/*
+** Returns list of all matching services.
+** parv[1] - string to match names against
+** parv[2] - type of service
+*/
+int m_servlist(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aService *sp;
+ Reg aClient *acptr;
+ char *mask = BadPtr(parv[1]) ? "*" : parv[1];
+ int type = 0;
+
+ if (parc > 2)
+ type = BadPtr(parv[2]) ? 0 : atoi(parv[2]);
+ for (sp = svctop; sp; sp = sp->nexts)
+ if ((acptr = sp->bcptr) && (!type || type == sp->type) &&
+ (match(mask, acptr->name) == 0))
+ sendto_one(sptr, rpl_str(RPL_SERVLIST, parv[0]),
+ acptr->name, sp->server, sp->dist,
+ sp->type, acptr->hopcount, acptr->info);
+ sendto_one(sptr, rpl_str(RPL_SERVLISTEND, parv[0]), mask, type);
+ return 2;
+}
+
+
+#ifdef USE_SERVICES
+/*
+** m_servset
+**
+** parv[0] = sender prefix
+** parv[1] = data requested
+** parv[2] = burst requested (optional)
+*/
+int m_servset(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+ int burst = 0;
+
+ if (!MyConnect(sptr))
+ {
+ sendto_flag(SCH_ERROR, "%s issued a SERVSET (from %s)",
+ sptr->name, get_client_name(cptr, TRUE));
+ return 1;
+ }
+ if (!IsService(sptr) || (IsService(sptr) && sptr->service->wants))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
+ return 1;
+ }
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
+ "SERVSET");
+ return 1;
+ }
+ if (sptr->service->wants)
+ return 1;
+
+ /* check against configuration */
+ sptr->service->wants = atoi(parv[1]) & sptr->service->type;
+ /* check that service is global for some requests */
+ if (strcmp(sptr->service->dist, "*"))
+ sptr->service->wants &= ~SERVICE_MASK_GLOBAL;
+ /* allow options */
+ sptr->service->wants |= (atoi(parv[1]) & ~SERVICE_MASK_ALL);
+ /* send accepted SERVSET */
+ sendto_one(sptr, ":%s SERVSET %s :%d", sptr->name, sptr->name,
+ sptr->service->wants);
+
+ if (parc < 3 ||
+ ((burst = sptr->service->wants & atoi(parv[2])) == 0))
+ return 0;
+
+ /*
+ ** services can request a connect burst.
+ ** it is optional, because most services should not need it,
+ ** so let's save some bandwidth.
+ **
+ ** tokens are NOT used. (2.8.x like burst)
+ ** distribution code is respected.
+ ** service type also respected.
+ */
+ /* cptr->flags |= FLAGS_CBURST; doesn't work.. */
+ if (burst & SERVICE_WANT_SERVER)
+ {
+ int split;
+
+ for (acptr = &me; acptr; acptr = acptr->prev)
+ {
+ if (!IsServer(acptr) && !IsMe(acptr))
+ continue;
+ if (match(sptr->service->dist, acptr->name))
+ continue;
+ split = (MyConnect(acptr) &&
+ mycmp(acptr->name, acptr->sockhost));
+ if (split)
+ sendto_one(sptr,":%s SERVER %s %d %s :[%s] %s",
+ acptr->serv->up, acptr->name,
+ acptr->hopcount+1,
+ acptr->serv->tok,
+ acptr->sockhost, acptr->info);
+ else
+ sendto_one(sptr, ":%s SERVER %s %d %s :%s",
+ acptr->serv->up, acptr->name,
+ acptr->hopcount+1,
+ acptr->serv->tok,
+ acptr->info);
+ }
+ }
+
+ if (burst & (SERVICE_WANT_NICK|SERVICE_WANT_USER|SERVICE_WANT_SERVICE))
+ {
+ char buf[BUFSIZE] = "+";
+
+ for (acptr = &me; acptr; acptr = acptr->prev)
+ {
+ /* acptr->from == acptr for acptr == cptr */
+ if (acptr->from == cptr)
+ continue;
+ if (IsPerson(acptr))
+ {
+ if (match(sptr->service->dist,
+ acptr->user->server))
+ continue;
+ if (burst & SERVICE_WANT_UMODE)
+ send_umode(NULL, acptr, 0, SEND_UMODES,
+ buf);
+ else if (burst & SERVICE_WANT_OPER)
+ send_umode(NULL, acptr, 0, FLAGS_OPER,
+ buf);
+ sendnum_toone(sptr, burst, acptr, buf);
+ }
+ else if (IsService(acptr))
+ {
+ if (!(burst & SERVICE_WANT_SERVICE))
+ continue;
+ if (match(sptr->service->dist,
+ acptr->service->server))
+ continue;
+ sendto_one(sptr, "SERVICE %s %s %s %d %d :%s",
+ acptr->name, acptr->service->server,
+ acptr->service->dist,
+ acptr->service->type,
+ acptr->hopcount + 1, acptr->info);
+ }
+ }
+ }
+
+ if (burst & (SERVICE_WANT_CHANNEL|SERVICE_WANT_VCHANNEL|SERVICE_WANT_MODE|SERVICE_WANT_TOPIC))
+ {
+ char modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];
+ aChannel *chptr;
+
+ for (chptr = channel; chptr; chptr = chptr->nextch)
+ {
+ if (chptr->users == 0)
+ continue;
+ if (burst&(SERVICE_WANT_CHANNEL|SERVICE_WANT_VCHANNEL))
+ sendto_one(sptr, "CHANNEL %s %d",
+ chptr->chname, chptr->users);
+ if (burst & SERVICE_WANT_MODE)
+ {
+ *modebuf = *parabuf = '\0';
+ modebuf[1] = '\0';
+ channel_modes(&me, modebuf, parabuf, chptr);
+ sendto_one(sptr, "MODE %s %s", chptr->chname,
+ modebuf);
+ }
+ if ((burst & SERVICE_WANT_TOPIC) && *chptr->topic)
+ sendto_one(sptr, "TOPIC %s :%s",
+ chptr->chname, chptr->topic);
+ }
+ }
+ /* cptr->flags ^= FLAGS_CBURST; */
+ return 0;
+}
+#endif
+
+
+/*
+** Send query to service.
+** parv[1] - string to match name against
+** parv[2] - string to send to service
+*/
+int m_squery(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+
+ if (parc <= 2)
+ {
+ if (parc == 1)
+ sendto_one(sptr, err_str(ERR_NORECIPIENT, parv[0]),
+ "SQUERY");
+ else if (parc == 2 || BadPtr(parv[1]))
+ sendto_one(sptr, err_str(ERR_NOTEXTTOSEND, parv[0]));
+ return 1;
+ }
+
+ if ((acptr = best_service(parv[1], NULL)))
+ if (MyConnect(acptr) &&
+ (acptr->service->wants & SERVICE_WANT_PREFIX))
+ sendto_one(acptr, ":%s!%s@%s SQUERY %s :%s", parv[0],
+ sptr->user->username, sptr->user->host,
+ acptr->name, parv[2]);
+ else
+ sendto_one(acptr, ":%s SQUERY %s :%s",
+ parv[0], acptr->name, parv[2]);
+ else
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVICE, parv[0]), parv[1]);
+ return 2;
+}
diff --git a/ircd/s_service_ext.h b/ircd/s_service_ext.h
new file mode 100644
index 0000000..a809ff5
--- /dev/null
+++ b/ircd/s_service_ext.h
@@ -0,0 +1,58 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_service_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_service.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef S_SERVICE_C
+extern aService *svctop;
+#endif
+
+/* External definitions for global functions.
+ */
+#ifndef S_SERVICE_C
+#define EXTERN extern
+#else /* S_SERVICE_C */
+#define EXTERN
+#endif /* S_SERVICE_C */
+EXTERN aService *make_service __P((aClient *cptr));
+EXTERN void free_service __P((aClient *cptr));
+#ifdef USE_SERVICES
+#ifndef USE_STDARG
+EXTERN void check_services_butone();
+#else /* USE_STDARG */
+EXTERN void check_services_butone __P((long action, char *server,
+ aClient *cptr, char *fmt, ...));
+#endif /* USE_STDARG */
+EXTERN void check_services_num __P((aClient *sptr, char *umode));
+EXTERN aConfItem *find_conf_service __P((aClient *cptr, int type,
+ aConfItem *aconf));
+EXTERN int m_servset __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+#endif /* USE_SERVICES */
+EXTERN int m_service __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_servlist __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_squery __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+#undef EXTERN
diff --git a/ircd/s_user.c b/ircd/s_user.c
new file mode 100644
index 0000000..1ba4c32
--- /dev/null
+++ b/ircd/s_user.c
@@ -0,0 +1,2869 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_user.c,v 1.86 1999/07/17 11:47:49 q Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_USER_C
+#include "s_externs.h"
+#undef S_USER_C
+
+static char buf[BUFSIZE], buf2[BUFSIZE];
+
+static int user_modes[] = { FLAGS_OPER, 'o',
+ FLAGS_LOCOP, 'O',
+ FLAGS_INVISIBLE, 'i',
+ FLAGS_WALLOP, 'w',
+ FLAGS_RESTRICTED, 'r',
+ FLAGS_AWAY, 'a',
+ 0, 0 };
+
+/*
+** 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(next, ch)
+Reg aClient *next; /* First client to check */
+Reg char *ch; /* search string (may include wilds) */
+{
+ Reg aClient *tmp = next;
+
+ next = find_client(ch, tmp);
+ if (tmp && tmp->prev == next)
+ return NULL;
+ if (next != tmp)
+ return next;
+ for ( ; next; next = next->next)
+ if (!match(ch,next->name) || !match(next->name,ch))
+ 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(cptr, sptr, command, server, parc, parv)
+aClient *cptr, *sptr;
+char *command, *parv[];
+int server, parc;
+ {
+ aClient *acptr;
+
+ /*
+ ** Assume it's me, if no server
+ */
+ if (parc <= server || BadPtr(parv[server]) ||
+ match(ME, parv[server]) == 0 ||
+ match(parv[server], ME) == 0)
+ return (HUNTED_ISME);
+ /*
+ ** These are to pickup matches that would cause the following
+ ** message to go in the wrong direction while doing quick fast
+ ** non-matching lookups.
+ */
+ if ((acptr = find_client(parv[server], NULL)))
+ if (acptr->from == sptr->from && !MyConnect(acptr))
+ acptr = NULL;
+ /* Match *.masked.servers */
+ if (!acptr && (acptr = find_server(parv[server], NULL)))
+ if (acptr->from == sptr->from && !MyConnect(acptr))
+ acptr = NULL;
+ /* Remote services@servers */
+ if (!acptr && (acptr = find_service(parv[server], NULL)))
+ if (acptr->from == sptr->from && !MyConnect(acptr))
+ acptr = NULL;
+ if (!acptr)
+ for (acptr = client, (void)collapse(parv[server]);
+ (acptr = next_client(acptr, parv[server]));
+ acptr = acptr->next)
+ {
+ if (acptr->from == sptr->from && !MyConnect(acptr))
+ continue;
+ /*
+ * Fix to prevent looping in case the parameter for
+ * some reason happens to match someone from the from
+ * link --jto
+ */
+ if (IsRegistered(acptr) && (acptr != cptr))
+ break;
+ }
+ if (acptr)
+ {
+ if (!IsRegistered(acptr))
+ return HUNTED_ISME;
+ if (IsMe(acptr) || MyClient(acptr) || MyService(acptr))
+ return HUNTED_ISME;
+ if (match(acptr->name, parv[server]))
+ parv[server] = acptr->name;
+ if (IsService(sptr)
+ && (IsServer(acptr->from)
+ && match(sptr->service->dist,acptr->name) != 0))
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]),
+ parv[server]);
+ return(HUNTED_NOSUCH);
+ }
+ 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);
+ }
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]), parv[server]);
+ return(HUNTED_NOSUCH);
+ }
+
+/*
+** '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 illegal)
+**
+** 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.
+** Finally forbid the use of "anonymous" because of possible
+** abuses related to anonymous channnels. -kalt
+**
+** Note:
+** '~'-character should be allowed, but
+** a change should be global, some confusion would
+** result if only few servers allowed it...
+*/
+
+int do_nick_name(nick, server)
+char *nick;
+int server;
+{
+ Reg char *ch;
+
+ if (*nick == '-') /* first character '-' */
+ return 0;
+
+ if (isdigit(*nick) && !server) /* first character in [0..9] */
+ return 0;
+
+ if (!strcasecmp(nick, "anonymous"))
+ return 0;
+
+ for (ch = nick; *ch && (ch - nick) < NICKLEN; ch++)
+ if (!isvalid(*ch) || isspace(*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(buffer)
+char *buffer;
+{
+ static char cbuf[BUFSIZ];
+ Reg char *s, *t, *cp = cbuf;
+ Reg int l = 0;
+ char *p = NULL, *p2;
+
+ *cp = '\0';
+
+ for (s = strtoken(&p, buffer, ","); s; s = strtoken(&p, NULL, ","))
+ {
+ if (l)
+ {
+ for (p2 = NULL, t = strtoken(&p2, cbuf, ","); t;
+ t = strtoken(&p2, NULL, ","))
+ if (!mycmp(s, t))
+ break;
+ else if (p2)
+ p2[-1] = ',';
+ }
+ else
+ t = NULL;
+ if (!t)
+ {
+ if (l)
+ *(cp-1) = ',';
+ else
+ l = 1;
+ (void)strcpy(cp, s);
+ if (p)
+ cp += (p - s);
+ }
+ else if (p2)
+ p2[-1] = ',';
+ }
+ return cbuf;
+}
+
+/*
+** ereject_user
+** extracted from register_user for clarity
+** early rejection of a user connection, with logging.
+*/
+int
+ereject_user(cptr, shortm, longm)
+aClient *cptr;
+char *shortm, *longm;
+{
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONN)
+ syslog(LOG_NOTICE, "%s ( %s ): <none>@%s [%s] %c\n",
+ myctime(cptr->firsttime), shortm, longm,
+ (IsUnixSocket(cptr)) ? me.sockhost :
+ ((cptr->hostp) ? cptr->hostp->h_name : cptr->sockhost),
+ cptr->auth, cptr->exitc);
+#endif
+#if defined(FNAME_CONNLOG) || defined(USE_SERVICES)
+ sendto_flog(cptr, shortm, 0, "<none>",
+ (IsUnixSocket(cptr)) ? me.sockhost :
+ ((cptr->hostp) ? cptr->hostp->h_name : cptr->sockhost));
+#endif
+ return exit_client(cptr, cptr, &me, longm);
+}
+
+/*
+** 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...
+*/
+
+int register_user(cptr, sptr, nick, username)
+aClient *cptr;
+aClient *sptr;
+char *nick, *username;
+{
+ Reg aConfItem *aconf;
+ aClient *acptr;
+ aServer *sp = NULL;
+ anUser *user = sptr->user;
+ short oldstatus = sptr->status;
+ char *parv[3];
+#ifndef NO_PREFIX
+ char prefix;
+#endif
+ int i;
+
+ user->last = timeofday;
+ parv[0] = sptr->name;
+ parv[1] = parv[2] = NULL;
+
+ if (MyConnect(sptr))
+ {
+ char *reason = NULL;
+
+#if defined(USE_IAUTH)
+ static time_t last = 0;
+ static u_int count = 0;
+
+ if (iauth_options & XOPT_EARLYPARSE && DoingXAuth(cptr))
+ {
+ cptr->flags |= FLAGS_WXAUTH;
+ /* fool check_pings() and give iauth more time! */
+ cptr->firsttime = timeofday;
+ cptr->lasttime = timeofday;
+ strncpyzt(sptr->user->username, username, USERLEN+1);
+ if (sptr->passwd[0])
+ sendto_iauth("%d P %s", sptr->fd,sptr->passwd);
+ sendto_iauth("%d U %s", sptr->fd, username);
+ return 1;
+ }
+ if (!DoneXAuth(sptr) && (iauth_options & XOPT_REQUIRED))
+ {
+ char *reason;
+
+ if (iauth_options & XOPT_NOTIMEOUT)
+ {
+ count += 1;
+ if (timeofday - last > 300)
+ {
+ sendto_flag(SCH_AUTH,
+ "iauth may not be running! (refusing new user connections)");
+ last = timeofday;
+ }
+ reason = "No iauth!";
+ }
+ else
+ reason = "iauth t/o";
+ sptr->exitc = EXITC_AUTHFAIL;
+ return ereject_user(cptr, reason,
+ "Authentication failure!");
+ }
+ if (timeofday - last > 300 && count)
+ {
+ sendto_flag(SCH_AUTH, "%d users rejected.", count);
+ count = 0;
+ }
+
+ /* this should not be needed, but there's a bug.. -kalt */
+ /* haven't seen any notice like this, ever.. no bug no more? */
+ if (*cptr->username == '\0')
+ {
+ sendto_flag(SCH_AUTH,
+ "Ouch! Null username for %s (%d %X)",
+ get_client_name(cptr, TRUE), cptr->fd,
+ cptr->flags);
+ sendto_iauth("%d E Null username [%s] %X", cptr->fd,
+ get_client_name(cptr, TRUE), cptr->flags);
+ return exit_client(cptr, sptr, &me,
+ "Fatal Bug - Try Again");
+ }
+#endif
+ /*
+ ** the following insanity used to be after check_client()
+ ** but check_client()->attach_Iline() now needs to know the
+ ** username for global u@h limits.
+ ** moving this shit here shouldn't be a problem. -krys
+ ** what a piece of $#@!.. restricted can only be known
+ ** *after* attach_Iline(), so it matters and I have to move
+ ** come of it back below. so global u@h limits really suck.
+ */
+#ifndef NO_PREFIX
+ /*
+ ** ident is fun.. ahem
+ ** prefixes used:
+ ** none I line with ident
+ ** ^ I line with OTHER type ident
+ ** ~ I line, no ident
+ ** + i line with ident
+ ** = i line with OTHER type ident
+ ** - i line, no ident
+ */
+ if (!(sptr->flags & FLAGS_GOTID))
+ prefix = '~';
+ else
+ if (*sptr->username == '-' ||
+ index(sptr->username, '@'))
+ prefix = '^';
+ else
+ prefix = '\0';
+
+ /* OTHER type idents have '-' prefix (from s_auth.c), */
+ /* and they are not supposed to be used as userid (rfc1413) */
+ /* @ isn't valid in usernames (m_user()) */
+ if (sptr->flags & FLAGS_GOTID && *sptr->username != '-' &&
+ index(sptr->username, '@') == NULL)
+ strncpyzt(buf2, sptr->username, USERLEN+1);
+ else /* No ident, or unusable ident string */
+ /* because username may point to user->username */
+ strncpyzt(buf2, username, USERLEN+1);
+
+ if (prefix)
+ {
+ *user->username = prefix;
+ strncpy(&user->username[1], buf2, USERLEN);
+ }
+ else
+ strncpy(user->username, buf2, USERLEN+1);
+ user->username[USERLEN] = '\0';
+ /* eos */
+#else
+ strncpyzt(user->username, username, USERLEN+1);
+#endif
+
+ if (sptr->exitc == EXITC_AREF || sptr->exitc == EXITC_AREFQ)
+ {
+ if (sptr->exitc == EXITC_AREF)
+ sendto_flag(SCH_LOCAL,
+ "Denied connection from %s.",
+ get_client_host(sptr));
+ return ereject_user(cptr, " Denied ","Denied access");
+ }
+ if ((i = check_client(sptr)))
+ {
+ struct msg_set { char *shortm; char *longm; };
+
+ static struct msg_set exit_msg[7] = {
+ { "G u@h max", "Too many user connections (global)" },
+ { "G IP max", "Too many host connections (global)" },
+ { "L u@h max", "Too many user connections (local)" },
+ { "L IP max", "Too many host connections (local)" },
+ { " max ", "Too many connections" },
+ { " No Auth ", "Unauthorized connection" },
+ { " Failure ", "Connect failure" } };
+
+ i += 7;
+ if (i < 0 || i > 6) /* in case.. */
+ i = 6;
+
+ ircstp->is_ref++;
+ sptr->exitc = EXITC_REF;
+ sendto_flag(SCH_LOCAL, "%s from %s.",
+ exit_msg[i].longm, get_client_host(sptr));
+ return ereject_user(cptr, exit_msg[i].shortm,
+ exit_msg[i].longm);
+ }
+
+#ifndef NO_PREFIX
+ if (IsRestricted(sptr))
+ {
+ if (!(sptr->flags & FLAGS_GOTID))
+ prefix = '-';
+ else
+ if (*sptr->username == '-' ||
+ index(sptr->username, '@'))
+ prefix = '=';
+ else
+ prefix = '+';
+ *user->username = prefix;
+ strncpy(&user->username[1], buf2, USERLEN);
+ user->username[USERLEN] = '\0';
+ }
+#endif
+
+ aconf = sptr->confs->value.aconf;
+ if (IsUnixSocket(sptr))
+ strncpyzt(user->host, me.sockhost, HOSTLEN+1);
+ else
+ strncpyzt(user->host, sptr->sockhost, HOSTLEN+1);
+
+ if (!BadPtr(aconf->passwd) &&
+ !StrEq(sptr->passwd, aconf->passwd))
+ {
+ ircstp->is_ref++;
+ sendto_one(sptr, err_str(ERR_PASSWDMISMATCH, parv[0]));
+ return exit_client(cptr, sptr, &me, "Bad Password");
+ }
+ bzero(sptr->passwd, sizeof(sptr->passwd));
+ /*
+ * following block for the benefit of time-dependent K:-lines
+ */
+ if (find_kill(sptr, 1, &reason))
+ {
+ /*char buf[100];*/
+
+ sendto_flag(SCH_LOCAL, "K-lined %s@%s.",
+ user->username, sptr->sockhost);
+ ircstp->is_ref++;
+ sptr->exitc = EXITC_REF;
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONN)
+ syslog(LOG_NOTICE, "%s ( K lined ): %s@%s [%s] %c\n",
+ myctime(sptr->firsttime), user->username,
+ user->host, sptr->auth, '-');
+#endif
+#if defined(FNAME_CONNLOG) || defined(USE_SERVICES)
+ sendto_flog(sptr, " K lined ", 0, user->username,
+ user->host);
+#endif
+ if (reason)
+ sprintf(buf, "K-lined: %.80s", reason);
+ return exit_client(cptr, sptr, &me, (reason) ? buf :
+ "K-lined");
+ }
+#ifdef R_LINES
+ if (find_restrict(sptr))
+ {
+ sendto_flag(SCH_LOCAL, "R-lined %s@%s.",
+ user->username, sptr->sockhost);
+ ircstp->is_ref++;
+ sptr->exitc = EXITC_REF;
+# if defined(USE_SYSLOG) && defined(SYSLOG_CONN)
+ syslog(LOG_NOTICE, "%s ( R lined ): %s@%s [%s] %c\n",
+ myctime(sptr->firsttime), user->username,
+ user->host, sptr->username, '-');
+# endif
+# if defined(FNAME_CONNLOG) || defined(USE_SERVICES)
+ sendto_flog(sptr, " R lined ", 0, user->username,
+ user->host);
+# endif
+ return exit_client(cptr, sptr, &me , "R-lined");
+ }
+#endif
+ if (oldstatus == STAT_MASTER && MyConnect(sptr))
+ (void)m_oper(&me, sptr, 1, parv);
+/* *user->tok = '1';
+ user->tok[1] = '\0';*/
+ sp = user->servp;
+ }
+ else
+ strncpyzt(user->username, username, USERLEN+1);
+ SetClient(sptr);
+ if (MyConnect(sptr))
+ {
+ sprintf(buf, "%s!%s@%s", nick, user->username, user->host);
+ sptr->exitc = EXITC_REG;
+ sendto_one(sptr, rpl_str(RPL_WELCOME, nick), buf);
+ /* This is a duplicate of the NOTICE but see below...*/
+ sendto_one(sptr, rpl_str(RPL_YOURHOST, nick),
+ get_client_name(&me, FALSE), version);
+ sendto_one(sptr, rpl_str(RPL_CREATED, nick), creation);
+ sendto_one(sptr, rpl_str(RPL_MYINFO, parv[0]),
+ ME, version);
+ (void)m_lusers(sptr, sptr, 1, parv);
+ (void)m_motd(sptr, sptr, 1, parv);
+ nextping = timeofday;
+ }
+ else if (IsServer(cptr))
+ {
+ acptr = find_server(user->server, NULL);
+ if (acptr && acptr->from != cptr)
+ {
+ sendto_one(cptr, ":%s KILL %s :%s (%s != %s[%s])",
+ ME, sptr->name, ME, user->server,
+ acptr->from->name, acptr->from->sockhost);
+ sptr->flags |= FLAGS_KILLED;
+ return exit_client(cptr, sptr, &me,
+ "USER server wrong direction");
+ }
+ }
+
+ send_umode(NULL, sptr, 0, SEND_UMODES, buf);
+ for (i = fdas.highest; i >= 0; i--)
+ { /* Find my leaf servers and feed the new client to them */
+ if ((acptr = local[fdas.fd[i]]) == cptr || IsMe(acptr))
+ continue;
+ if ((aconf = acptr->serv->nline) &&
+ !match(my_name_for_link(ME, aconf->port),
+ user->server))
+ sendto_one(acptr, "NICK %s %d %s %s %s %s :%s",
+ nick, sptr->hopcount+1,
+ user->username, user->host,
+ me.serv->tok, (*buf) ? buf : "+",
+ sptr->info);
+ else
+ sendto_one(acptr, "NICK %s %d %s %s %s %s :%s",
+ nick, sptr->hopcount+1,
+ user->username, user->host,
+ user->servp->tok,
+ (*buf) ? buf : "+", sptr->info);
+ } /* for(my-leaf-servers) */
+ if (MyConnect(sptr))
+ {
+ if (IsRestricted(sptr))
+ sendto_one(sptr, err_str(ERR_RESTRICTED, nick));
+ send_umode(sptr, sptr, 0, ALL_UMODES, buf);
+ }
+
+ if (IsInvisible(sptr)) /* Can be initialized in m_user() */
+ istat.is_user[1]++; /* Local and server defaults +i */
+ else
+ istat.is_user[0]++;
+ if (MyConnect(sptr))
+ {
+ istat.is_unknown--;
+ istat.is_myclnt++;
+ }
+#ifdef USE_SERVICES
+#if 0
+ check_services_butone(SERVICE_WANT_NICK, user->server, NULL,
+ "NICK %s :%d", nick, sptr->hopcount+1);
+ check_services_butone(SERVICE_WANT_USER, user->server, sptr,
+ ":%s USER %s %s %s :%s", nick, user->username,
+ user->host, user->server, sptr->info);
+ if (MyConnect(sptr)) /* all modes about local users */
+ send_umode(NULL, sptr, 0, ALL_UMODES, buf);
+ check_services_butone(SERVICE_WANT_UMODE, user->server, sptr,
+ ":%s MODE %s :%s", nick, nick, buf);
+#endif
+ if (MyConnect(sptr)) /* all modes about local users */
+ send_umode(NULL, sptr, 0, ALL_UMODES, buf);
+ check_services_num(sptr, buf);
+#endif
+ return 1;
+ }
+
+/*
+** m_nick
+** parv[0] = sender prefix
+** parv[1] = nickname
+** the following are only used between servers since version 2.9
+** parv[2] = hopcount
+** parv[3] = username (login name, account)
+** parv[4] = client host name
+** parv[5] = server token
+** parv[6] = users mode
+** parv[7] = users real name info
+*/
+int m_nick(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+ int delayed = 0;
+ char nick[NICKLEN+2], *s, *user, *host;
+ Link *lp = NULL;
+
+ if (IsService(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0]));
+ return 1;
+ }
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN, parv[0]));
+ return 1;
+ }
+ if (MyConnect(sptr) && (s = (char *)index(parv[1], '~')))
+ *s = '\0';
+ strncpyzt(nick, parv[1], NICKLEN+1);
+
+ if (parc == 8 && cptr->serv)
+ {
+ user = parv[3];
+ host = parv[4];
+ }
+ else
+ {
+ if (sptr->user)
+ {
+ user = sptr->username;
+ host = sptr->user->host;
+ }
+ else
+ user = host = "";
+ }
+ /*
+ * 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, IsServer(cptr)) == 0 ||
+ (IsServer(cptr) && strcmp(nick, parv[1])))
+ {
+ sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME, parv[0]),
+ parv[1]);
+
+ if (IsServer(cptr))
+ {
+ ircstp->is_kill++;
+ sendto_flag(SCH_KILL, "Bad Nick: %s From: %s %s",
+ parv[1], parv[0],
+ get_client_name(cptr, FALSE));
+ sendto_one(cptr, ":%s KILL %s :%s (%s <- %s[%s])",
+ ME, parv[1], ME, parv[1],
+ nick, cptr->name);
+ if (sptr != cptr) /* bad nick change */
+ {
+ sendto_serv_butone(cptr,
+ ":%s KILL %s :%s (%s <- %s!%s@%s)",
+ ME, parv[0], ME,
+ get_client_name(cptr, FALSE),
+ parv[0], user, host);
+ sptr->flags |= FLAGS_KILLED;
+ return exit_client(cptr,sptr,&me,"BadNick");
+ }
+ }
+ return 2;
+ }
+
+ /*
+ ** 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 = find_server(nick, NULL)))
+ if (MyConnect(sptr))
+ {
+ sendto_one(sptr, err_str(ERR_NICKNAMEINUSE, parv[0]),
+ nick);
+ return 2; /* NICK message ignored */
+ }
+ /*
+ ** acptr already has result from previous find_server()
+ */
+ 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_flag(SCH_KILL,
+ "Nick collision on %s (%s@%s)%s <- (%s@%s)%s",
+ sptr->name,
+ (acptr->user) ? acptr->user->username : "???",
+ (acptr->user) ? acptr->user->host : "???",
+ acptr->from->name, user, host,
+ get_client_name(cptr, FALSE));
+ ircstp->is_kill++;
+ sendto_one(cptr, ":%s KILL %s :%s (%s <- %s)",
+ ME, sptr->name, ME, 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 = find_client(nick, NULL)))
+ {
+ aClient *acptr2;
+
+ if (IsServer(cptr) || !(bootopt & BOOT_PROT))
+ goto nickkilldone;
+ if ((acptr2 = get_history(nick, (long)(KILLCHASETIMELIMIT))) &&
+ !MyConnect(acptr2))
+ /*
+ ** Lock nick for KCTL so one cannot nick collide
+ ** (due to kill chase) people who recently changed
+ ** their nicks. --Beeth
+ */
+ delayed = 1;
+ else
+ /*
+ ** Let ND work
+ */
+ delayed = find_history(nick, (long)(DELAYCHASETIMELIMIT));
+ if (!delayed)
+ 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 2; /* 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 (acptr && IsUnknown(acptr) && MyConnect(acptr))
+ {
+ (void) exit_client(acptr, acptr, &me, "Overridden");
+ 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((delayed) ? ERR_UNAVAILRESOURCE
+ : ERR_NICKNAMEINUSE,
+ parv[0]), nick);
+ return 2; /* NICK message ignored */
+ }
+ /*
+ ** NICK was coming from a server connection. Means that the same
+ ** nick is registered for different users by different server.
+ ** This is either a race condition (two users coming online about
+ ** same time, or net reconnecting) or just two net fragments becoming
+ ** joined and having same nicks in use. We cannot have TWO users with
+ ** same nick--purge this NICK from the system with a KILL... >;)
+ **
+ ** The client indicated by 'acptr' is dead meat, give at least some
+ ** indication of the reason why we are just dropping it cold.
+ */
+ sendto_one(acptr, err_str(ERR_NICKCOLLISION, acptr->name),
+ acptr->name, user, host);
+ /*
+ ** This seemingly obscure test (sptr == cptr) differentiates
+ ** between "NICK new" (TRUE) and ":old NICK new" (FALSE) forms.
+ */
+ if (sptr == cptr)
+ {
+ sendto_flag(SCH_KILL,
+ "Nick collision on %s (%s@%s)%s <- (%s@%s)%s",
+ acptr->name,
+ (acptr->user) ? acptr->user->username : "???",
+ (acptr->user) ? acptr->user->host : "???",
+ acptr->from->name,
+ user, host, get_client_name(cptr, FALSE));
+ /*
+ ** A new NICK being introduced by a neighbouring
+ ** server (e.g. message type "NICK new" received)
+ */
+ ircstp->is_kill++;
+ sendto_serv_butone(NULL,
+ ":%s KILL %s :%s ((%s@%s)%s <- (%s@%s)%s)",
+ ME, acptr->name, ME,
+ (acptr->user) ? acptr->user->username:"???",
+ (acptr->user) ? acptr->user->host : "???",
+ acptr->from->name, user, host,
+ /* NOTE: Cannot use get_client_name twice
+ ** here, it returns static string pointer:
+ ** the other info would be lost
+ */
+ get_client_name(cptr, FALSE));
+ acptr->flags |= FLAGS_KILLED;
+ return exit_client(NULL, acptr, &me, "Nick collision");
+ }
+ /*
+ ** A NICK change has collided (e.g. message type
+ ** ":old NICK new". This requires more complex cleanout.
+ ** Both clients must be purged from this server, the "new"
+ ** must be killed from the incoming connection, and "old" must
+ ** be purged from all outgoing connections.
+ */
+ sendto_flag(SCH_KILL, "Nick change collision %s!%s@%s to %s %s <- %s",
+ sptr->name, user, host, acptr->name, acptr->from->name,
+ get_client_name(cptr, FALSE));
+ ircstp->is_kill++;
+ sendto_serv_butone(NULL, /* KILL old from outgoing servers */
+ ":%s KILL %s :%s (%s(%s) <- %s)",
+ ME, sptr->name, ME, acptr->from->name,
+ acptr->name, get_client_name(cptr, FALSE));
+ ircstp->is_kill++;
+ sendto_serv_butone(NULL, /* Kill new from incoming link */
+ ":%s KILL %s :%s (%s <- %s(%s))",
+ ME, acptr->name, ME, acptr->from->name,
+ get_client_name(cptr, FALSE), sptr->name);
+ acptr->flags |= FLAGS_KILLED;
+ (void)exit_client(NULL, acptr, &me, "Nick collision(new)");
+ sptr->flags |= FLAGS_KILLED;
+ return exit_client(cptr, sptr, &me, "Nick collision(old)");
+
+nickkilldone:
+ if (IsServer(sptr))
+ {
+ char *pv[7];
+
+ if (parc != 8)
+ {
+ sendto_flag(SCH_NOTICE,
+ "Bad NICK param count (%d) for %s from %s via %s",
+ parc, parv[1], sptr->name,
+ get_client_name(cptr, FALSE));
+ sendto_one(cptr, ":%s KILL %s :%s (Bad NICK %d)",
+ ME, nick, ME, parc);
+ return 0;
+ }
+ /* A server introducing a new client, change source */
+ sptr = make_client(cptr);
+ add_client_to_list(sptr);
+ if (parc > 2)
+ sptr->hopcount = atoi(parv[2]);
+ (void)strcpy(sptr->name, nick);
+
+ pv[0] = sptr->name;
+ pv[1] = parv[3];
+ pv[2] = parv[4];
+ pv[3] = parv[5];
+ pv[4] = parv[7];
+ pv[5] = parv[6];
+ pv[6] = NULL;
+ (void)add_to_client_hash_table(nick, sptr);
+ return m_user(cptr, sptr, 6, pv);
+ }
+ else if (sptr->name[0]) /* NICK received before, changing */
+ {
+ if (MyConnect(sptr))
+ {
+ if (!IsPerson(sptr)) /* Unregistered client */
+ return 2; /* Ignore new NICKs */
+ if (IsRestricted(sptr))
+ {
+ sendto_one(sptr,
+ err_str(ERR_RESTRICTED, nick));
+ return 2;
+ }
+ /* is the user banned on any channel ? */
+ for (lp = sptr->user->channel; lp; lp = lp->next)
+ if (can_send(sptr, lp->value.chptr) ==MODE_BAN)
+ break;
+ }
+ /*
+ ** 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.
+ */
+ sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
+ if (sptr->user) /* should always be true.. */
+ {
+ add_history(sptr, sptr);
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_NICK,
+ sptr->user->server, sptr,
+ ":%s NICK :%s", parv[0], nick);
+#endif
+ }
+ else
+ sendto_flag(SCH_NOTICE,
+ "Illegal NICK change: %s -> %s from %s",
+ parv[0], nick, get_client_name(cptr,TRUE));
+ sendto_serv_butone(cptr, ":%s NICK :%s", parv[0], nick);
+ if (sptr->name[0])
+ (void)del_from_client_hash_table(sptr->name, sptr);
+ (void)strcpy(sptr->name, nick);
+ }
+ else
+ {
+ /* Client setting NICK the first time */
+
+ /* This had to be copied here to avoid problems.. */
+ (void)strcpy(sptr->name, nick);
+ if (sptr->user)
+ /*
+ ** USER 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
+ ** may reject the client and call exit_client for it
+ ** --must test this and exit m_nick too!!!
+ */
+ if (register_user(cptr, sptr, nick,
+ sptr->user->username)
+ == FLUSH_BUFFER)
+ return FLUSH_BUFFER;
+ }
+ /*
+ ** Finally set new nick name.
+ */
+ (void)add_to_client_hash_table(nick, sptr);
+ if (lp)
+ return 15;
+ else
+ return 3;
+}
+
+/*
+** 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[2] = message text
+**
+** massive cleanup
+** rev argv 6/91
+**
+*/
+
+static int m_message(cptr, sptr, parc, parv, notice)
+aClient *cptr, *sptr;
+char *parv[];
+int parc, notice;
+{
+ Reg aClient *acptr;
+ Reg char *s;
+ aChannel *chptr;
+ char *nick, *server, *p, *cmd, *user, *host;
+ int count = 0, penalty = 0;
+
+ cmd = notice ? MSG_NOTICE : MSG_PRIVATE;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NORECIPIENT, parv[0]), cmd);
+ return 1;
+ }
+
+ if (parc < 3 || *parv[2] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOTEXTTOSEND, parv[0]));
+ return 1;
+ }
+
+ if (MyConnect(sptr))
+ parv[1] = canonize(parv[1]);
+ for (p = NULL, nick = strtoken(&p, parv[1], ","); nick;
+ nick = strtoken(&p, NULL, ","), penalty++)
+ {
+ /*
+ ** restrict destination list to MAXPENALTY/2 recipients to
+ ** solve SPAM problem --Yegg
+ */
+ if (2*penalty >= MAXPENALTY) {
+ if (!notice)
+ sendto_one(sptr, err_str(ERR_TOOMANYTARGETS,
+ parv[0]),
+ "Too many",nick,"No Message Delivered");
+ continue;
+ }
+ /*
+ ** nickname addressed?
+ */
+ if ((acptr = find_person(nick, NULL)))
+ {
+ if (!notice && MyConnect(sptr) &&
+ acptr->user && (acptr->user->flags & FLAGS_AWAY))
+ sendto_one(sptr, rpl_str(RPL_AWAY, parv[0]),
+ acptr->name,
+ (acptr->user->away) ?
+ acptr->user->away : "Gone");
+ sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
+ parv[0], cmd, nick, parv[2]);
+ continue;
+ }
+ /*
+ ** channel msg?
+ */
+ if ((IsPerson(sptr) || IsService(sptr) || IsServer(sptr)) &&
+ (chptr = find_channel(nick, NullChn)))
+ {
+ if (can_send(sptr, chptr) == 0 || IsServer(sptr))
+ sendto_channel_butone(cptr, sptr, chptr,
+ ":%s %s %s :%s",
+ parv[0], cmd, nick,
+ parv[2]);
+ else if (!notice)
+ sendto_one(sptr, err_str(ERR_CANNOTSENDTOCHAN,
+ parv[0]), nick);
+ 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 (!(s = (char *)rindex(nick, '.')))
+ {
+ sendto_one(sptr, err_str(ERR_NOTOPLEVEL,
+ parv[0]), nick);
+ continue;
+ }
+ while (*++s)
+ if (*s == '.' || *s == '*' || *s == '?')
+ break;
+ if (*s == '*' || *s == '?')
+ {
+ sendto_one(sptr, err_str(ERR_WILDTOPLEVEL,
+ 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[2]);
+ continue;
+ }
+
+ /*
+ ** nick!user@host addressed?
+ */
+ if ((user = (char *)index(nick, '!')) &&
+ (host = (char *)index(nick, '@')))
+ {
+ *user = '\0';
+ *host = '\0';
+ if ((acptr = find_person(nick, NULL)) &&
+ !mycmp(user+1, acptr->user->username) &&
+ !mycmp(host+1, acptr->user->host))
+ {
+ sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
+ parv[0], cmd, nick, parv[2]);
+ *user = '!';
+ *host = '@';
+ continue;
+ }
+ *user = '!';
+ *host = '@';
+ }
+
+ /*
+ ** user[%host]@server addressed?
+ */
+ if ((server = (char *)index(nick, '@')) &&
+ (acptr = find_server(server + 1, NULL)))
+ {
+ /*
+ ** Not destined for a user on me :-(
+ */
+ if (!IsMe(acptr))
+ {
+ sendto_one(acptr,":%s %s %s :%s", parv[0],
+ cmd, nick, parv[2]);
+ continue;
+ }
+ *server = '\0';
+
+ if ((host = (char *)index(nick, '%')))
+ *host++ = '\0';
+
+ /*
+ ** Look for users which match the destination host
+ ** (no host == wildcard) and if one and one only is
+ ** found connected to me, deliver message!
+ */
+ acptr = find_userhost(nick, host, NULL, &count);
+ if (server)
+ *server = '@';
+ if (host)
+ *--host = '%';
+ if (acptr)
+ {
+ if (count == 1)
+ sendto_prefix_one(acptr, sptr,
+ ":%s %s %s :%s",
+ parv[0], cmd,
+ nick, parv[2]);
+ else if (!notice)
+ sendto_one(sptr, err_str(
+ ERR_TOOMANYTARGETS,
+ parv[0]), "Duplicate", nick,
+ "No Message Delivered");
+ continue;
+ }
+ }
+ else if ((host = (char *)index(nick, '%')))
+ {
+ /*
+ ** user%host addressed?
+ */
+ *host++ = '\0';
+ acptr = find_userhost(nick, host, NULL, &count);
+ *--host = '%';
+ if (acptr)
+ {
+ if (count == 1)
+ sendto_prefix_one(acptr, sptr,
+ ":%s %s %s :%s",
+ parv[0], cmd,
+ nick, parv[2]);
+ else if (!notice)
+ sendto_one(sptr, err_str(
+ ERR_TOOMANYTARGETS,
+ parv[0]), "Duplicate", nick,
+ "No Message Delivered");
+ continue;
+ }
+ }
+ if (!notice)
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK, parv[0]),
+ nick);
+ }
+ return penalty;
+}
+
+/*
+** m_private
+** parv[0] = sender prefix
+** parv[1] = receiver list
+** parv[2] = message text
+*/
+
+int m_private(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ return m_message(cptr, sptr, parc, parv, 0);
+}
+
+/*
+** m_notice
+** parv[0] = sender prefix
+** parv[1] = receiver list
+** parv[2] = notice text
+*/
+
+int m_notice(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ return m_message(cptr, sptr, parc, parv, 1);
+}
+
+/*
+** who_one
+** sends one RPL_WHOREPLY to sptr concerning acptr & repchan
+*/
+static void who_one(sptr, acptr, repchan, lp)
+aClient *sptr, *acptr;
+aChannel *repchan;
+Link *lp;
+{
+ char status[5];
+ int i = 0;
+
+ if (acptr->user->flags & FLAGS_AWAY)
+ status[i++] = 'G';
+ else
+ status[i++] = 'H';
+ if (IsAnOper(acptr))
+ status[i++] = '*';
+ if ((repchan != NULL) && (lp == NULL))
+ lp = find_user_link(repchan->members, acptr);
+ if (lp != NULL)
+ {
+ if (lp->flags & CHFL_CHANOP)
+ status[i++] = '@';
+ else if (lp->flags & CHFL_VOICE)
+ status[i++] = '+';
+ }
+ status[i] = '\0';
+ sendto_one(sptr, rpl_str(RPL_WHOREPLY, sptr->name),
+ (repchan) ? (repchan->chname) : "*", acptr->user->username,
+ acptr->user->host, acptr->user->server, acptr->name,
+ status, acptr->hopcount, acptr->info);
+}
+
+
+/*
+** who_channel
+** lists all users on a given channel
+*/
+static void who_channel(sptr, chptr, oper)
+aClient *sptr;
+aChannel *chptr;
+int oper;
+{
+ Reg Link *lp;
+ int member;
+
+ if (!IsAnonymous(chptr))
+ {
+ member = IsMember(sptr, chptr);
+ if (member || !SecretChannel(chptr))
+ for (lp = chptr->members; lp; lp = lp->next)
+ {
+ if (oper && !IsAnOper(lp->value.cptr))
+ continue;
+ if (IsInvisible(lp->value.cptr) && !member)
+ continue;
+ who_one(sptr, lp->value.cptr, chptr, lp);
+ }
+ }
+ else if (lp = find_user_link(chptr->members, sptr))
+ who_one(sptr, lp->value.cptr, chptr, lp);
+}
+
+/*
+** who_find
+** lists all (matching) users.
+** CPU intensive, but what can be done?
+*/
+static void who_find(sptr, mask, oper)
+aClient *sptr;
+char *mask;
+int oper;
+{
+ aChannel *chptr, *ch2ptr;
+ Link *lp;
+ int member;
+ int showperson, isinvis;
+ aClient *acptr;
+
+ for (acptr = client; acptr; acptr = acptr->next)
+ {
+ ch2ptr = NULL;
+
+ if (!IsPerson(acptr))
+ continue;
+ if (oper && !IsAnOper(acptr))
+ continue;
+ showperson = 0;
+ /*
+ * Show user if they are on the same channel, or not
+ * invisible and on a non secret channel (if any).
+ * Do this before brute force match on all relevant
+ * fields since these are less cpu intensive (I
+ * hope :-) and should provide better/more shortcuts
+ * -avalon
+ */
+ isinvis = IsInvisible(acptr);
+ for (lp = acptr->user->channel; lp; lp = lp->next)
+ {
+ chptr = lp->value.chptr;
+ if (IsAnonymous(chptr))
+ continue;
+ member = IsMember(sptr, chptr);
+ if (isinvis && !member)
+ continue;
+ if (member || (!isinvis && PubChannel(chptr)))
+ {
+ showperson = 1;
+ if (!IsAnonymous(chptr) ||
+ acptr != sptr)
+ {
+ ch2ptr = chptr;
+ break;
+ }
+ }
+ if (HiddenChannel(chptr) &&
+ !SecretChannel(chptr) && !isinvis)
+ showperson = 1;
+ }
+ if (!acptr->user->channel && !isinvis)
+ showperson = 1;
+ /*
+ ** This is brute force solution, not efficient...? ;(
+ ** Show entry, if no mask or any of the fields match
+ ** the mask. --msa
+ */
+ if (showperson &&
+ (!mask ||
+ match(mask, acptr->name) == 0 ||
+ match(mask, acptr->user->username) == 0 ||
+ match(mask, acptr->user->host) == 0 ||
+ match(mask, acptr->user->server) == 0 ||
+ match(mask, acptr->info) == 0))
+ who_one(sptr, acptr, ch2ptr, NULL);
+ }
+}
+
+/*
+** m_who
+** parv[0] = sender prefix
+** parv[1] = nickname mask list
+** parv[2] = additional selection flag, only 'o' for now.
+*/
+int m_who(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Link *lp;
+ aChannel *chptr, *mychannel;
+ char *channame = NULL;
+ int oper = parc > 2 ? (*parv[2] == 'o' ): 0; /* Show OPERS only */
+ int penalty = 0;
+ char *p, *mask;
+
+ if (parc < 2)
+ {
+ who_find(sptr, NULL, oper);
+ sendto_one(sptr, rpl_str(RPL_ENDOFWHO, parv[0]), "*");
+ return 5;
+ }
+
+ for (p = NULL, mask = strtoken(&p, parv[1], ",");
+ mask && penalty < MAXPENALTY;
+ mask = strtoken(&p, NULL, ","))
+ {
+ channame = NULL;
+ mychannel = NullChn;
+ clean_channelname(mask);
+ if (sptr->user && (lp = sptr->user->channel))
+ mychannel = lp->value.chptr;
+ /*
+ ** Following code is some ugly hacking to preserve the
+ ** functions of the old implementation. (Also, people
+ ** will complain when they try to use masks like "12tes*"
+ ** and get people on channel 12 ;) --msa
+ */
+ if (!mask || *mask == '\0') /* !mask always false? */
+ mask = NULL;
+ else if (mask[1] == '\0' && mask[0] == '*')
+ {
+ mask = NULL;
+ if (mychannel)
+ channame = mychannel->chname;
+ }
+ else if (mask[1] == '\0' && mask[0] == '0')
+ /* "WHO 0" for irc.el */
+ mask = NULL;
+ else
+ channame = mask;
+ (void)collapse(mask);
+
+ if (IsChannelName(channame))
+ {
+ chptr = find_channel(channame, NULL);
+ if (chptr)
+ who_channel(sptr, chptr, oper);
+ penalty += 1;
+ }
+ else
+ {
+ who_find(sptr, mask, oper);
+ if (mask && (int)strlen(mask) > 4)
+ penalty += 3;
+ else
+ penalty += 5;
+ }
+ sendto_one(sptr, rpl_str(RPL_ENDOFWHO, parv[0]),
+ BadPtr(mask) ? "*" : mask);
+ }
+ return penalty;
+}
+
+/* send_whois() is used by m_whois() to send whois reply to sptr, for acptr */
+static void
+send_whois(sptr, acptr)
+aClient *sptr, *acptr;
+{
+ static anUser UnknownUser =
+ {
+ NULL, /* channel */
+ NULL, /* invited */
+ NULL, /* uwas */
+ NULL, /* away */
+ 0, /* last */
+ 1, /* refcount */
+ 0, /* joined */
+ 0, /* flags */
+ NULL, /* servp */
+ NULL, /* next, prev, bcptr */
+ "<Unknown>", /* user */
+ "<Unknown>", /* host */
+ "<Unknown>", /* server */
+ };
+ Link *lp;
+ anUser *user;
+ aChannel *chptr;
+ aClient *a2cptr;
+ int len, mlen;
+ char *name;
+
+ user = acptr->user ? acptr->user : &UnknownUser;
+ name = (!*acptr->name) ? "?" : acptr->name;
+
+ a2cptr = find_server(user->server, NULL);
+
+ sendto_one(sptr, rpl_str(RPL_WHOISUSER, sptr->name),
+ name, user->username, user->host, acptr->info);
+
+ mlen = strlen(ME) + strlen(sptr->name) + 6 + strlen(name);
+
+ for (len = 0, *buf = '\0', lp = user->channel; lp; lp = lp->next)
+ {
+ chptr = lp->value.chptr;
+ if ((!IsAnonymous(chptr) || acptr == sptr) &&
+ ShowChannel(sptr, chptr))
+ {
+ if (len + strlen(chptr->chname)
+ > (size_t) BUFSIZE - 4 - mlen)
+ {
+ sendto_one(sptr, ":%s %d %s %s :%s", ME,
+ RPL_WHOISCHANNELS, sptr->name, name,
+ buf);
+ *buf = '\0';
+ len = 0;
+ }
+ if (is_chan_op(acptr, chptr))
+ *(buf + len++) = '@';
+ else if (has_voice(acptr, chptr))
+ *(buf + len++) = '+';
+ if (len)
+ *(buf + len) = '\0';
+ (void)strcpy(buf + len, chptr->chname);
+ len += strlen(chptr->chname);
+ (void)strcat(buf + len, " ");
+ len++;
+ }
+ }
+ if (buf[0] != '\0')
+ sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS, sptr->name), name,
+ buf);
+
+ sendto_one(sptr, rpl_str(RPL_WHOISSERVER, sptr->name),
+ name, user->server,
+ a2cptr ? a2cptr->info:"*Not On This Net*");
+
+ if (user->flags & FLAGS_AWAY)
+ sendto_one(sptr, rpl_str(RPL_AWAY, sptr->name), name,
+ (user->away) ? user->away : "Gone");
+
+ if (IsAnOper(acptr))
+ sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR, sptr->name), name);
+
+ if (acptr->user && MyConnect(acptr))
+ sendto_one(sptr, rpl_str(RPL_WHOISIDLE, sptr->name),
+ name, timeofday - user->last);
+}
+
+/*
+** m_whois
+** parv[0] = sender prefix
+** parv[1] = nickname masklist
+*/
+int m_whois(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Link *lp;
+ aClient *acptr;
+ aChannel *chptr;
+ char *nick, *tmp;
+ char *p = NULL;
+ int found = 0;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN, parv[0]));
+ return 1;
+ }
+
+ if (parc > 2)
+ {
+ if (hunt_server(cptr,sptr,":%s WHOIS %s :%s", 1,parc,parv) !=
+ HUNTED_ISME)
+ return 3;
+ parv[1] = parv[2];
+ }
+
+ parv[1] = canonize(parv[1]);
+
+ for (tmp = parv[1]; (nick = strtoken(&p, tmp, ",")); tmp = NULL)
+ {
+ int invis, showperson, member, wilds;
+
+ found &= 0x0f; /* high/boolean, low/counter */
+ (void)collapse(nick);
+ wilds = (index(nick, '?') || index(nick, '*'));
+ /*
+ * We're no longer allowing remote users to generate
+ * requests with wildcard, nor local users with more
+ * than one wildcard target per command.
+ * Max 3 targets per command allowed.
+ */
+ if ((wilds && (!MyConnect(sptr) || p)) || found++ > 3)
+ break;
+
+ if (!wilds)
+ {
+ acptr = hash_find_client(nick, (aClient *)NULL);
+ if (!acptr || !IsPerson(acptr))
+ sendto_one(sptr,
+ err_str(ERR_NOSUCHNICK, parv[0]),
+ nick);
+ else
+ send_whois(sptr, acptr);
+ continue;
+ }
+
+ for (acptr = client; (acptr = next_client(acptr, nick));
+ acptr = acptr->next)
+ {
+ if (IsServer(acptr) || IsService(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 don't 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;
+ */
+ invis = (acptr->user) ?
+ (acptr->user->flags & FLAGS_INVISIBLE) : 0;
+ member = (acptr->user && acptr->user->channel) ? 1 : 0;
+ showperson = (wilds && !invis && !member) || !wilds;
+ for (lp = (acptr->user) ? acptr->user->channel : NULL;
+ lp; lp = lp->next)
+ {
+ chptr = lp->value.chptr;
+ if (IsAnonymous(chptr))
+ continue;
+ member = IsMember(sptr, chptr);
+ if (invis && !member)
+ continue;
+ if (member || (!invis && PubChannel(chptr)))
+ {
+ showperson = 1;
+ break;
+ }
+ if (!invis && HiddenChannel(chptr) &&
+ !SecretChannel(chptr))
+ showperson = 1;
+ }
+ if (!showperson)
+ continue;
+
+ found |= 0x10;
+
+ send_whois(sptr, acptr);
+ }
+ if (!(found & 0x10))
+ {
+ if (strlen(nick) > (size_t) NICKLEN)
+ nick[NICKLEN] = '\0';
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK, parv[0]),
+ nick);
+ }
+ if (p)
+ p[-1] = ',';
+ }
+ sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS, parv[0]), parv[1]);
+
+ return 2;
+}
+
+/*
+** m_user
+** parv[0] = sender prefix
+** parv[1] = username (login name, account)
+** parv[2] = client host name (used only from other servers)
+** parv[3] = server host name (used only from other servers)
+** parv[4] = users real name info
+** parv[5] = users mode (is only used internally by the server,
+** NULL otherwise)
+*/
+int m_user(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+#define UFLAGS (FLAGS_INVISIBLE|FLAGS_WALLOP)
+ char *username, *host, *server, *realname;
+ anUser *user;
+
+ /* Reject new USER */
+ if (IsServer(sptr) || IsService(sptr) || sptr->user)
+ {
+ sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0]));
+ return 1;
+ }
+ if (parc > 2 && (username = (char *)index(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, parv[0]), "USER");
+ if (IsServer(cptr))
+ {
+ /* send error */
+ sendto_flag(SCH_NOTICE,
+ "bad USER param count for %s from %s",
+ parv[0], get_client_name(cptr, FALSE));
+ /*
+ ** and kill it, as there's no reason to expect more
+ ** USER messages about it, or we'll create a ghost.
+ */
+ sendto_one(cptr,
+ ":%s KILL %s :%s (bad USER param count)",
+ ME, parv[0], ME);
+ sptr->flags |= FLAGS_KILLED;
+ exit_client(NULL, sptr, &me, "bad USER param count");
+ }
+ return 1;
+ }
+
+ /* Copy parameters into better documenting variables */
+
+ username = (parc < 2 || BadPtr(parv[1])) ? "<bad-boy>" : parv[1];
+ host = (parc < 3 || BadPtr(parv[2])) ? "<nohost>" : parv[2];
+ server = (parc < 4 || BadPtr(parv[3])) ? "<noserver>" : parv[3];
+ realname = (parc < 5 || BadPtr(parv[4])) ? "<bad-realname>" : parv[4];
+
+ user = make_user(sptr);
+
+ if (!MyConnect(sptr))
+ {
+ aClient *acptr = NULL;
+ aServer *sp = NULL;
+
+ if (!(sp = find_tokserver(atoi(server), cptr, NULL)))
+ {
+ /*
+ ** Why? Why do we keep doing this?
+ ** s_service.c had the same kind of kludge.
+ ** Can't we get rid of this? - krys
+ */
+ acptr = find_server(server, NULL);
+ if (acptr)
+ sendto_flag(SCH_ERROR,
+ "ERROR: SERVER:%s uses wrong syntax for NICK (%s)",
+ get_client_name(cptr, FALSE),
+ parv[0]);
+ }
+ if (acptr)
+ sp = acptr->serv;
+ else if (!sp)
+ {
+ sendto_flag(SCH_ERROR,
+ "ERROR: USER:%s without SERVER:%s from %s",
+ parv[0], server,
+ get_client_name(cptr, FALSE));
+ ircstp->is_nosrv++;
+ return exit_client(NULL, sptr, &me, "No Such Server");
+ }
+ user->servp = sp;
+ user->servp->refcnt++;
+
+ Debug((DEBUG_DEBUG, "from %s user %s server %s -> %#x %s",
+ parv[0], username, server, sp, sp->bcptr->name));
+ strncpyzt(user->host, host, sizeof(user->host));
+ user->server = find_server_string(sp->snum);
+ goto user_finish;
+ }
+
+ user->servp = me.serv;
+ me.serv->refcnt++;
+#ifndef NO_DEFAULT_INVISIBLE
+ SetInvisible(sptr);
+#endif
+ if (sptr->flags & FLAGS_RILINE)
+ sptr->user->flags |= FLAGS_RESTRICTED;
+ sptr->user->flags |= (UFLAGS & atoi(host));
+ strncpyzt(user->host, host, sizeof(user->host));
+ user->server = find_server_string(me.serv->snum);
+
+user_finish:
+ reorder_client_in_list(sptr);
+ if (sptr->info != DefInfo)
+ MyFree(sptr->info);
+ if (strlen(realname) > REALLEN)
+ realname[REALLEN] = '\0';
+ sptr->info = mystrdup(realname);
+ if (sptr->name[0]) /* NICK already received, now we have USER... */
+ {
+ if ((parc == 6) && IsServer(cptr)) /* internal m_user() */
+ {
+ char *pv[4];
+
+ pv[0] = ME;
+ pv[1] = sptr->name;
+ pv[2] = parv[5];
+ pv[3] = NULL;
+ m_umode(NULL, sptr, 3, pv);/*internal fake call again*/
+ /* The internal m_umode does NOT propagate to 2.8
+ ** servers. (it can NOT since NICK/USER hasn't been
+ ** sent yet). See register_user()
+ */
+ }
+ return register_user(cptr, sptr, sptr->name, username);
+ }
+ else
+ strncpyzt(sptr->user->username, username, USERLEN+1);
+ return 2;
+}
+
+/*
+** m_quit
+** parv[0] = sender prefix
+** parv[1] = comment
+*/
+int m_quit(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ static char quitc[] = "I Quit";
+ register char *comment = (parc > 1 && parv[1]) ? parv[1] : quitc;
+
+ if (MyClient(sptr) || MyService(sptr))
+ if (!strncmp("Local Kill", comment, 10) ||
+ !strncmp(comment, "Killed", 6))
+ comment = quitc;
+ 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[2] = kill path
+*/
+int m_kill(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+ char *inpath = get_client_name(cptr,FALSE);
+ char *user, *path, *killer;
+ int chasing = 0;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "KILL");
+ return 1;
+ }
+
+ user = parv[1];
+ path = parv[2]; /* Either defined or NULL (parc >= 2!!) */
+
+ if (IsAnOper(cptr))
+ {
+ if (BadPtr(path))
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
+ "KILL");
+ return 1;
+ }
+ if (strlen(path) > (size_t) TOPICLEN)
+ path[TOPICLEN] = '\0';
+ }
+
+ if (!(acptr = find_client(user, NULL)))
+ {
+ /*
+ ** 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)KILLCHASETIMELIMIT)))
+ {
+ if (!IsServer(sptr))
+ sendto_one(sptr, err_str(ERR_NOSUCHNICK,
+ parv[0]), user);
+ return 1;
+ }
+ sendto_one(sptr,":%s NOTICE %s :KILL changed from %s to %s",
+ ME, parv[0], user, acptr->name);
+ chasing = 1;
+ }
+ if (!MyConnect(acptr) && IsLocOp(cptr))
+ {
+ sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
+ return 1;
+ }
+ if (IsServer(acptr) || IsMe(acptr))
+ {
+ sendto_flag(SCH_ERROR, "%s tried to KILL server %s: %s %s %s",
+ sptr->name, acptr->name, parv[0], parv[1], parv[2]);
+ sendto_one(sptr, err_str(ERR_CANTKILLSERVER, parv[0]),
+ acptr->name);
+ return 1;
+ }
+
+#ifdef LOCAL_KILL_ONLY
+ if (MyOper(sptr) && !MyConnect(acptr))
+ {
+ sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server",
+ ME, parv[0], acptr->name);
+ return 1;
+ }
+#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(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, parv[0]));
+ return 1;
+ }
+ sendto_flag(SCH_KILL,
+ "Received KILL message for %s. From %s Path: %s!%s",
+ acptr->name, parv[0], inpath, path);
+#if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
+ 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_serv_butone(cptr, ":%s KILL %s :%s!%s",
+ parv[0], acptr->name, inpath, path);
+ if (chasing && !IsClient(cptr))
+ sendto_one(cptr, ":%s KILL %s :%s!%s",
+ ME, acptr->name, inpath, path);
+ acptr->flags |= FLAGS_KILLED;
+ }
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_KILL, NULL, sptr,
+ ":%s KILL %s :%s!%s", parv[0], acptr->name,
+ inpath, path);
+#endif
+
+ /*
+ ** 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))
+ {
+ acptr->exitc = EXITC_KILL;
+ SPRINTF(buf2, "Local Kill by %s (%s)", sptr->name,
+ BadPtr(parv[2]) ? sptr->name : parv[2]);
+ }
+ else
+ {
+ if ((killer = index(path, ' ')))
+ {
+ while (killer > path && *killer != '!')
+ killer--;
+ if (killer != path)
+ killer++;
+ }
+ else
+ killer = path;
+ SPRINTF(buf2, "Killed (%s)", killer);
+ }
+ return exit_client(cptr, acptr, sptr, buf2);
+}
+
+/***********************************************************************
+ * m_away() - Added 14 Dec 1988 by jto.
+ * Not currently really working, I don't like this
+ * call at all...
+ *
+ * ...trying to make it work. I don't like it either,
+ * but perhaps it's worth the load it causes to net.
+ * This requires flooding of the whole net like NICK,
+ * USER, MODE, etc messages... --msa
+ ***********************************************************************/
+
+/*
+** m_away
+** parv[0] = sender prefix
+** parv[1] = away message
+*/
+int m_away(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg char *away, *awy2 = parv[1];
+ int len;
+
+ away = sptr->user->away;
+
+ if (parc < 2 || !*awy2) /* Marking as not away */
+ {
+ if (away)
+ {
+ istat.is_away--;
+ istat.is_awaymem -= (strlen(away) + 1);
+ MyFree(away);
+ sptr->user->away = NULL;
+ }
+ if (sptr->user->flags & FLAGS_AWAY)
+ sendto_serv_butone(cptr, ":%s MODE %s :-a", parv[0],
+ parv[0]);
+ /* sendto_serv_butone(cptr, ":%s AWAY", parv[0]); */
+ if (MyConnect(sptr))
+ sendto_one(sptr, rpl_str(RPL_UNAWAY, parv[0]));
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_AWAY, NULL, sptr,
+ ":%s AWAY", parv[0]);
+#endif
+ sptr->user->flags &= ~FLAGS_AWAY;
+ return 1;
+ }
+
+ /* Marking as away */
+
+ if ((len = strlen(awy2)) > (size_t) TOPICLEN)
+ {
+ len = TOPICLEN;
+ awy2[TOPICLEN] = '\0';
+ }
+ len++;
+ /* sendto_serv_butone(cptr, ":%s AWAY :%s", parv[0], awy2); */
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_AWAY, NULL, sptr,
+ ":%s AWAY :%s", parv[0], awy2);
+#endif
+
+ if (away)
+ {
+ istat.is_awaymem -= (strlen(away) + 1);
+ away = (char *)MyRealloc(away, len);
+ istat.is_awaymem += len;
+ }
+ else
+ {
+ istat.is_away++;
+ istat.is_awaymem += len;
+ away = (char *)MyMalloc(len);
+ sendto_serv_butone(cptr, ":%s MODE %s :+a", parv[0], parv[0]);
+ }
+
+ sptr->user->flags |= FLAGS_AWAY;
+ if (MyConnect(sptr))
+ {
+ sptr->user->away = away;
+ (void)strcpy(away, awy2);
+ sendto_one(sptr, rpl_str(RPL_NOWAWAY, parv[0]));
+ }
+ return 2;
+}
+
+/*
+** m_ping
+** parv[0] = sender prefix
+** parv[1] = origin
+** parv[2] = destination
+*/
+int m_ping(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+ char *origin, *destination;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOORIGIN, parv[0]));
+ return 1;
+ }
+ origin = parv[1];
+ destination = parv[2]; /* Will get NULL or pointer (parc >= 2!!) */
+
+ acptr = find_client(origin, NULL);
+ if (!acptr)
+ acptr = find_server(origin, NULL);
+ if (!acptr || acptr != sptr)
+ origin = cptr->name;
+ if (!BadPtr(destination) && mycmp(destination, ME) != 0)
+ {
+ if ((acptr = find_server(destination, NULL)))
+ sendto_one(acptr,":%s PING %s :%s", parv[0],
+ origin, destination);
+ else
+ {
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]),
+ destination);
+ return 1;
+ }
+ }
+ else
+ sendto_one(sptr, ":%s PONG %s :%s", ME,
+ (destination) ? destination : ME, origin);
+ return 1;
+ }
+
+/*
+** m_pong
+** parv[0] = sender prefix
+** parv[1] = origin
+** parv[2] = destination
+*/
+int m_pong(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ aClient *acptr;
+ char *origin, *destination;
+
+ if (parc < 2 || *parv[1] == '\0')
+ {
+ sendto_one(sptr, err_str(ERR_NOORIGIN, parv[0]));
+ return 1;
+ }
+
+ origin = parv[1];
+ destination = parv[2];
+ cptr->flags &= ~FLAGS_PINGSENT;
+ sptr->flags &= ~FLAGS_PINGSENT;
+
+ if (!BadPtr(destination) && mycmp(destination, ME) != 0)
+ {
+ if ((acptr = find_client(destination, NULL)) ||
+ (acptr = find_server(destination, NULL))) {
+ if (!(MyClient(sptr) && mycmp(origin, sptr->name)))
+ sendto_one(acptr,":%s PONG %s %s",
+ parv[0], origin, destination);
+ } else
+ sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]),
+ destination);
+ return 2;
+ }
+#ifdef DEBUGMODE
+ else
+ Debug((DEBUG_NOTICE, "PONG: %s %s", origin,
+ destination ? destination : "*"));
+#endif
+ return 1;
+ }
+
+
+/*
+** m_oper
+** parv[0] = sender prefix
+** parv[1] = oper name
+** parv[2] = oper password
+*/
+int m_oper(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ aConfItem *aconf;
+ char *name, *password, *encr;
+#ifdef CRYPT_OPER_PASSWORD
+ char salt[3];
+ extern char *crypt();
+#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, parv[0]), "OPER");
+ return 1;
+ }
+
+ /* if message arrived from server, trust it, and set to oper */
+
+ if ((IsServer(cptr) || IsMe(cptr)) && !IsOper(sptr))
+ {
+ /* we never get here, do we?? (counters!) -krys */
+ sptr->user->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, parv[0]));
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
+ sptr, ":%s MODE %s :+o", parv[0],
+ parv[0]);
+#endif
+ return 1;
+ }
+ else if (IsAnOper(sptr))
+ {
+ if (MyConnect(sptr))
+ sendto_one(sptr, rpl_str(RPL_YOUREOPER, parv[0]));
+ return 1;
+ }
+ if (!(aconf = find_conf_exact(name, sptr->username, sptr->sockhost,
+ CONF_OPS)) &&
+ !(aconf = find_conf_exact(name, sptr->username,
+#ifdef INET6
+ (char *)inetntop(AF_INET6,
+ (char *)&cptr->ip,
+ mydummy, MYDUMMY_SIZE),
+#else
+ (char *)inetntoa((char *)&cptr->ip),
+#endif
+ CONF_OPS)))
+ {
+ sendto_one(sptr, err_str(ERR_NOOPERHOST, parv[0]));
+ return 1;
+ }
+#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)
+ {
+ /* Determine if MD5 or DES */
+ if (strncmp(aconf->passwd, "$1$", 3))
+ {
+ salt[0] = aconf->passwd[0];
+ salt[1] = aconf->passwd[1];
+ }
+ else
+ {
+ salt[0] = aconf->passwd[3];
+ salt[1] = aconf->passwd[4];
+ }
+ salt[2] = '\0';
+ encr = crypt(password, salt);
+ }
+ else
+ encr = "";
+#else
+ encr = password;
+#endif /* CRYPT_OPER_PASSWORD */
+
+ if ((aconf->status & CONF_OPS) &&
+ StrEq(encr, aconf->passwd) && !attach_conf(sptr, aconf))
+ {
+ int old = (sptr->user->flags & ALL_UMODES);
+ char *s;
+
+ s = index(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
+ SetLocOp(sptr);
+ else
+ SetOper(sptr);
+ *--s = '@';
+ sendto_flag(SCH_NOTICE, "%s (%s@%s) is now operator (%c)",
+ parv[0], sptr->user->username, sptr->user->host,
+ IsOper(sptr) ? 'o' : 'O');
+ send_umode_out(cptr, sptr, old);
+ sendto_one(sptr, rpl_str(RPL_YOUREOPER, 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) [%s@%s]",
+ name, encr,
+ parv[0], sptr->user->username, sptr->user->host,
+ sptr->auth, IsUnixSocket(sptr) ? sptr->sockhost :
+#ifdef INET6
+ inet_ntop(AF_INET6, (char *)&sptr->ip), mydummy, MYDUMMY_SIZE);
+#else
+ inetntoa((char *)&sptr->ip));
+#endif
+#endif
+#ifdef FNAME_OPERLOG
+ {
+ int logfile;
+
+ /*
+ * This conditional makes the logfile active only after
+ * it's been created - thus logging can be turned off by
+ * removing the file.
+ *
+ * stop NFS hangs...most systems should be able to open a
+ * file in 3 seconds. -avalon (curtesy of wumpus)
+ */
+ (void)alarm(3);
+ if (IsPerson(sptr) &&
+ (logfile = open(FNAME_OPERLOG, O_WRONLY|O_APPEND)) != -1)
+ {
+ (void)alarm(0);
+ SPRINTF(buf, "%s OPER (%s) (%s) by (%s!%s@%s) [%s@%s]\n",
+ myctime(timeofday), name, encr,
+ parv[0], sptr->user->username, sptr->user->host,
+ sptr->auth, IsUnixSocket(sptr) ? sptr->sockhost :
+#ifdef INET6
+ inetntop(AF_INET6, (char *)&sptr->ip, mydummy,
+ MYDUMMY_SIZE));
+#else
+ inetntoa((char *)&sptr->ip));
+#endif
+ (void)alarm(3);
+ (void)write(logfile, buf, strlen(buf));
+ (void)alarm(0);
+ (void)close(logfile);
+ }
+ (void)alarm(0);
+ /* Modification by pjg */
+ }
+#endif
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
+ sptr, ":%s MODE %s :+%c", parv[0],
+ parv[0], IsOper(sptr) ? 'O' : 'o');
+#endif
+ if (IsAnOper(sptr))
+ istat.is_oper++;
+ }
+ else
+ {
+ (void)detach_conf(sptr, aconf);
+ sendto_one(sptr,err_str(ERR_PASSWDMISMATCH, parv[0]));
+ }
+ return 3;
+ }
+
+/***************************************************************************
+ * m_pass() - Added Sat, 4 March 1989
+ ***************************************************************************/
+
+/*
+** m_pass
+** parv[0] = sender prefix
+** parv[1] = password
+** parv[2] = protocol & server versions (server only)
+** parv[3] = server id & options (server only)
+** parv[4] = (optional) link options (server only)
+*/
+int m_pass(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+ {
+ char *password = parc > 1 ? parv[1] : NULL;
+
+ if (BadPtr(password))
+ {
+ sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "PASS");
+ return 1;
+ }
+ /* Temporarily store PASS pwd *parameters* into info field */
+ if (parc > 2 && parv[2])
+ {
+ strncpyzt(buf, parv[2], 15);
+ if (parc > 3 && parv[3])
+ {
+ strcat(buf, " ");
+ strncat(buf, parv[3], 100);
+ if (parc > 4 && parv[4])
+ {
+ strcat(buf, " ");
+ strncat(buf, parv[4], 5);
+ }
+ }
+ if (cptr->info != DefInfo)
+ MyFree(cptr->info);
+ cptr->info = mystrdup(buf);
+ }
+ strncpyzt(cptr->passwd, password, sizeof(cptr->passwd));
+ return 1;
+ }
+
+/*
+ * m_userhost added by Darren Reed 13/8/91 to aid clients and reduce
+ * the need for complicated requests like WHOIS. It returns user/host
+ * information only (no spurious AWAY labels or channels).
+ */
+int m_userhost(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ char *p = NULL;
+ aClient *acptr;
+ Reg char *s;
+ Reg int i, len;
+ int idx = 1;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
+ "USERHOST");
+ return 1;
+ }
+
+ (void)strcpy(buf, rpl_str(RPL_USERHOST, parv[0]));
+ len = strlen(buf);
+ *buf2 = '\0';
+
+ for (i = 5, s = strtoken(&p, parv[idx], " "); i && s; i--)
+ {
+ if ((acptr = find_person(s, NULL)))
+ {
+ if (*buf2)
+ (void)strcat(buf, " ");
+ SPRINTF(buf2, "%s%s=%c%s@%s", acptr->name,
+ IsAnOper(acptr) ? "*" : "",
+ (acptr->user->flags & FLAGS_AWAY) ? '-' : '+',
+ acptr->user->username, acptr->user->host);
+ (void)strncat(buf, buf2, sizeof(buf) - len);
+ len += strlen(buf2);
+ if (len > BUFSIZE - (NICKLEN + 5 + HOSTLEN + USERLEN))
+ {
+ sendto_one(sptr, "%s", buf);
+ (void)strcpy(buf, rpl_str(RPL_USERHOST,
+ parv[0]));
+ len = strlen(buf);
+ *buf2 = '\0';
+ }
+ }
+ s = strtoken(&p, (char *)NULL, " ");
+ if (!s && parv[++idx])
+ {
+ p = NULL;
+ s = strtoken(&p, parv[idx], " ");
+ }
+ }
+ sendto_one(sptr, "%s", buf);
+ return 1;
+}
+
+/*
+ * 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(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aClient *acptr;
+ Reg char *s, **pav = parv;
+ Reg int len = 0;
+ char *p = NULL;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "ISON");
+ return 1;
+ }
+
+ (void)strcpy(buf, rpl_str(RPL_ISON, *parv));
+ len = strlen(buf);
+
+ for (s = strtoken(&p, *++pav, " "); s; s = strtoken(&p, NULL, " "))
+ if ((acptr = find_person(s, NULL)))
+ {
+ (void) strcpy(buf + len, acptr->name);
+ len += strlen(acptr->name);
+ (void) strcpy(buf + len++, " ");
+ }
+ sendto_one(sptr, "%s", buf);
+ return 1;
+}
+
+/*
+ * m_umode() added 15/10/91 By Darren Reed.
+ * parv[0] - sender (can be NULL, see below..)
+ * parv[1] - username to change mode for
+ * parv[2] - modes to change
+ */
+int m_umode(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg int flag;
+ Reg int *s;
+ Reg char **p, *m;
+ aClient *acptr = NULL;
+ int what, setflags, penalty = 0;
+
+ what = MODE_ADD;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), "MODE");
+ return 1;
+ }
+
+ if (cptr && !(acptr = find_person(parv[1], NULL)))
+ {
+ if (MyConnect(sptr))
+ sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL, parv[0]),
+ parv[1]);
+ return 1;
+ }
+ if (cptr == NULL)
+ /* internal call which has to be handled in a special way */
+ acptr = sptr;
+
+ if ((cptr != NULL) &&
+ ((IsServer(sptr) || sptr != acptr || acptr->from != sptr->from)))
+ {
+ if (IsServer(cptr))
+ sendto_ops_butone(NULL, &me,
+ ":%s WALLOPS :MODE for User %s From %s!%s",
+ ME, parv[1],
+ get_client_name(cptr, FALSE), sptr->name);
+ else
+ sendto_one(sptr, err_str(ERR_USERSDONTMATCH, parv[0]));
+ return 1;
+ }
+
+ if (parc < 3)
+ {
+ m = buf;
+ *m++ = '+';
+ for (s = user_modes; (flag = *s) && (m - buf < BUFSIZE - 4);
+ s += 2)
+ if (sptr->user->flags & flag)
+ *m++ = (char)(*(s+1));
+ *m = '\0';
+ sendto_one(sptr, rpl_str(RPL_UMODEIS, parv[0]), buf);
+ return 1;
+ }
+
+ /* find flags already set for user */
+ setflags = 0;
+ for (s = user_modes; (flag = *s); s += 2)
+ if (sptr->user->flags & flag)
+ setflags |= flag;
+
+ /*
+ * parse mode change string(s)
+ */
+ for (p = &parv[2]; p && *p; p++ )
+ for (m = *p; *m; m++)
+ switch(*m)
+ {
+ case '+' :
+ what = MODE_ADD;
+ break;
+ case '-' :
+ what = MODE_DEL;
+ break;
+ /* we may not get these,
+ * but they shouldnt be in default
+ */
+ case ' ' :
+ case '\n' :
+ case '\r' :
+ case '\t' :
+ break;
+ case 'a' : /* fall through case */
+ /* users should use the AWAY message */
+ if (cptr && !IsServer(cptr))
+ break;
+ if (what == MODE_DEL && sptr->user->away)
+ {
+ istat.is_away--;
+ istat.is_awaymem -= (strlen(sptr->user->away) + 1);
+ MyFree(sptr->user->away);
+ sptr->user->away = NULL;
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_AWAY,
+ sptr->user->server, sptr,
+ ":%s AWAY", parv[0]);
+#endif
+ }
+#ifdef USE_SERVICES
+ if (what == MODE_ADD)
+ check_services_butone(SERVICE_WANT_AWAY,
+ sptr->user->server, sptr,
+ ":%s AWAY :", parv[0]);
+#endif
+ default :
+ for (s = user_modes; (flag = *s); s += 2)
+ if (*m == (char)(*(s+1)))
+ {
+ if (what == MODE_ADD)
+ sptr->user->flags |= flag;
+ else
+ sptr->user->flags &= ~flag;
+ penalty += 1;
+ break;
+ }
+ if (flag == 0 && MyConnect(sptr))
+ sendto_one(sptr, err_str(
+ ERR_UMODEUNKNOWNFLAG, parv[0]),
+ *m);
+ break;
+ }
+ /*
+ * stop users making themselves operators too easily
+ */
+ if (cptr)
+ {
+ if (!(setflags & FLAGS_OPER) && IsOper(sptr) &&
+ !IsServer(cptr))
+ ClearOper(sptr);
+ if (!(setflags & FLAGS_LOCOP) && IsLocOp(sptr) &&
+ !IsServer(cptr))
+ sptr->user->flags &= ~FLAGS_LOCOP;
+ if ((setflags & FLAGS_RESTRICTED) &&
+ !(sptr->user->flags & FLAGS_RESTRICTED))
+ {
+ sendto_one(sptr, err_str(ERR_RESTRICTED, parv[0]));
+ SetRestricted(sptr);
+ /* Can't return; here since it could mess counters */
+ }
+ if ((setflags & (FLAGS_OPER|FLAGS_LOCOP)) && !IsAnOper(sptr) &&
+ MyConnect(sptr))
+ det_confs_butmask(sptr, CONF_CLIENT);
+
+ /*
+ * compare new flags with old flags and send string which
+ * will cause servers to update correctly.
+ */
+ if (!IsInvisible(sptr) && (setflags & FLAGS_INVISIBLE))
+ {
+ istat.is_user[1]--;
+ istat.is_user[0]++;
+ }
+ if (IsInvisible(sptr) && !(setflags & FLAGS_INVISIBLE))
+ {
+ istat.is_user[1]++;
+ istat.is_user[0]--;
+ }
+ send_umode_out(cptr, sptr, setflags);
+ }
+
+ /* update counters */
+ if (IsOper(sptr) && !(setflags & FLAGS_OPER))
+ {
+ istat.is_oper++;
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
+ sptr, ":%s MODE %s :+o", parv[0],
+ parv[0]);
+#endif
+ }
+ else if (!IsOper(sptr) && (setflags & FLAGS_OPER))
+ {
+ istat.is_oper--;
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
+ sptr, ":%s MODE %s :-o", parv[0],
+ parv[0]);
+#endif
+ }
+ else if (MyConnect(sptr) && !IsLocOp(sptr) && (setflags & FLAGS_LOCOP))
+ {
+ istat.is_oper--;
+#ifdef USE_SERVICES
+ check_services_butone(SERVICE_WANT_OPER, sptr->user->server,
+ sptr, ":%s MODE %s :-O", parv[0],
+ parv[0]);
+#endif
+ }
+
+ return penalty;
+}
+
+/*
+ * send the MODE string for user (user) to connection cptr
+ * -avalon
+ */
+void send_umode(cptr, sptr, old, sendmask, umode_buf)
+aClient *cptr, *sptr;
+int old, sendmask;
+char *umode_buf;
+{
+ Reg int *s, flag;
+ Reg char *m;
+ int what = MODE_NULL;
+
+ if (!sptr->user)
+ return;
+ /*
+ * 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 (MyClient(sptr) && !(flag & sendmask))
+ continue;
+ if ((flag & old) && !(sptr->user->flags & flag))
+ {
+ if (what == MODE_DEL)
+ *m++ = *(s+1);
+ else
+ {
+ what = MODE_DEL;
+ *m++ = '-';
+ *m++ = *(s+1);
+ }
+ }
+ else if (!(flag & old) && (sptr->user->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);
+}
+
+/*
+ * added Sat Jul 25 07:30:42 EST 1992
+ */
+void send_umode_out(cptr, sptr, old)
+aClient *cptr, *sptr;
+int old;
+{
+ Reg int i;
+ Reg aClient *acptr;
+
+ send_umode(NULL, sptr, old, SEND_UMODES, buf);
+
+ if (*buf)
+ for (i = fdas.highest; i >= 0; i--)
+ {
+ if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr))
+ continue;
+ if (acptr == cptr || acptr == sptr)
+ continue;
+ sendto_one(acptr, ":%s MODE %s :%s",
+ sptr->name, sptr->name, buf);
+ }
+
+ if (cptr && MyClient(cptr))
+ send_umode(cptr, sptr, old, ALL_UMODES, buf);
+#ifdef USE_SERVICES
+ /* buf contains all modes for local users, and iow only for remotes */
+ if (*buf)
+ check_services_butone(SERVICE_WANT_UMODE, NULL, sptr,
+ ":%s MODE %s :%s", sptr->name,
+ sptr->name, buf);
+#endif
+}
diff --git a/ircd/s_user_ext.h b/ircd/s_user_ext.h
new file mode 100644
index 0000000..2feec20
--- /dev/null
+++ b/ircd/s_user_ext.h
@@ -0,0 +1,60 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_user_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_user.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef S_USER_C
+#define EXTERN extern
+#else /* S_USER_C */
+#define EXTERN
+#endif /* S_USER_C */
+EXTERN aClient *next_client __P((Reg aClient *next, Reg char *ch));
+EXTERN int hunt_server __P((aClient *cptr, aClient *sptr, char *command,
+ int server, int parc, char *parv[]));
+EXTERN int do_nick_name __P((char *nick, int server));
+EXTERN int ereject_user __P((aClient *, char *, char *));
+EXTERN int register_user __P((aClient *, aClient *, char *, char *));
+EXTERN char *canonize __P((char *buffer));
+EXTERN int m_nick __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_private __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_notice __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_who __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_whois __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_user __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_quit __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_kill __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_away __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_ping __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_pong __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_oper __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_pass __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_userhost __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN int m_ison __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN int m_umode __P((aClient *cptr, aClient *sptr, int parc, char *parv[]));
+EXTERN void send_umode __P((aClient *cptr, aClient *sptr, int old,
+ int sendmask, char *umode_buf));
+EXTERN void send_umode_out __P((aClient *cptr, aClient *sptr, int old));
+#undef EXTERN
diff --git a/ircd/s_zip.c b/ircd/s_zip.c
new file mode 100644
index 0000000..819c9a9
--- /dev/null
+++ b/ircd/s_zip.c
@@ -0,0 +1,261 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_zip.c
+ * Copyright (C) 1996 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: s_zip.c,v 1.7 1998/12/24 16:29:17 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_ZIP_C
+#include "s_externs.h"
+#undef S_ZIP_C
+
+#ifdef ZIP_LINKS
+
+/*
+** Important note:
+** The provided buffers for compression *MUST* be big
+** enough for any operation to complete.
+**
+** s_bsd.c current settings are that the biggest packet size is 16k
+** (but socket buffers are set to 8k..)
+*/
+
+/*
+** size of the buffer holding compressed data
+**
+** outgoing data:
+** must be enough to hold compressed data resulting of the compression
+** of up to ZIP_MAXIMUM bytes
+** incoming data:
+** must be enough to hold what was just read
+** (cptr->zip->inbuf should never hold more than ONE compression block.
+** The biggest block allowed for compression is ZIP_MAXIMUM bytes)
+*/
+#define ZIP_BUFFER_SIZE (MAX(ZIP_MAXIMUM, READBUF_SIZE))
+
+/*
+** size of the buffer where zlib puts compressed data
+** should be enough to hold uncompressed data resulting of the
+** uncompression of zipbuffer
+**
+** tests show that an average ratio is around 40%,
+** in some very particular cases, ratio can be VERY low, BUT:
+**
+** s_bsd.c/read_packet() is now smart enough to detect when uncompression
+** stopped because the buffer is too small, and calls dopacket() again
+** to finish the work (as many times as needed).
+*/
+#define UNZIP_BUFFER_SIZE 4*ZIP_BUFFER_SIZE
+
+/* buffers */
+static char unzipbuf[UNZIP_BUFFER_SIZE];
+static char zipbuf[ZIP_BUFFER_SIZE];
+
+/*
+** zip_init
+** Initialize compression structures for a server.
+** If failed, zip_free() has to be called.
+*/
+int zip_init(cptr)
+aClient *cptr;
+{
+ cptr->zip = (aZdata *) MyMalloc(sizeof(aZdata));
+ cptr->zip->outcount = 0;
+
+ cptr->zip->in = (z_stream *) MyMalloc(sizeof(z_stream));
+ cptr->zip->in->avail_in = 0;
+ cptr->zip->in->total_in = 0;
+ cptr->zip->in->total_out = 0;
+ cptr->zip->in->zalloc = (alloc_func)0;
+ cptr->zip->in->zfree = (free_func)0;
+ cptr->zip->in->data_type = Z_ASCII;
+ if (inflateInit(cptr->zip->in) != Z_OK)
+ {
+ cptr->zip->out = NULL;
+ return -1;
+ }
+
+ cptr->zip->out = (z_stream *) MyMalloc(sizeof(z_stream));
+ cptr->zip->out->total_in = 0;
+ cptr->zip->out->total_out = 0;
+ cptr->zip->out->zalloc = (alloc_func)0;
+ cptr->zip->out->zfree = (free_func)0;
+ cptr->zip->out->data_type = Z_ASCII;
+ if (deflateInit(cptr->zip->out, ZIP_LEVEL) != Z_OK)
+ return -1;
+
+ return 0;
+}
+
+/*
+** zip_free
+*/
+void zip_free(cptr)
+aClient *cptr;
+{
+ cptr->flags &= ~FLAGS_ZIP;
+ if (cptr->zip)
+ {
+ if (cptr->zip->in)
+ inflateEnd(cptr->zip->in);
+ MyFree((char *)cptr->zip->in);
+ if (cptr->zip->out)
+ deflateEnd(cptr->zip->out);
+ MyFree((char *)cptr->zip->out);
+ MyFree((char *)cptr->zip);
+ cptr->zip = NULL;
+ }
+}
+
+/*
+** unzip_packet
+** Unzip the content of the buffer, don't worry about any leftover.
+**
+** will return the uncompressed buffer, length will be updated.
+** if a fatal error occurs, length will be set to -1
+*/
+char * unzip_packet(cptr, buffer, length)
+aClient *cptr;
+char *buffer;
+int *length;
+{
+ Reg z_stream *zin = cptr->zip->in;
+ int r;
+
+ if (*length != 0 && zin->avail_in != 0)
+ {
+ sendto_flag(SCH_ERROR,
+ "assertion failed in unzip_packet(): %d %d",
+ *length, zin->avail_in);
+ sendto_flag(SCH_ERROR, "Please report to ircd-bugs@irc.org");
+ *length = -1;
+ return NULL;
+ }
+ if (*length)
+ {
+ zin->next_in = buffer;
+ zin->avail_in = *length;
+ }
+ zin->next_out = unzipbuf;
+ zin->avail_out = UNZIP_BUFFER_SIZE;
+ switch (r = inflate(zin, Z_PARTIAL_FLUSH))
+ {
+ case Z_OK:
+ cptr->flags &= ~FLAGS_ZIPRQ;
+ *length = UNZIP_BUFFER_SIZE - zin->avail_out;
+ return unzipbuf;
+
+ case Z_BUF_ERROR: /*no progress possible or output buffer too small*/
+ if (zin->avail_out == 0)
+ {
+ sendto_flag(SCH_ERROR,
+ "inflate() returned Z_BUF_ERROR: %s",
+ (zin->msg) ? zin->msg : "?");
+ *length = -1;
+ }
+ break;
+
+ case Z_DATA_ERROR: /* the buffer might not be compressed.. */
+ if ((cptr->flags & FLAGS_ZIPRQ) &&
+ !strncmp("ERROR ", buffer, 6))
+ {
+ cptr->flags &= ~(FLAGS_ZIP | FLAGS_ZIPRQ);
+ /*
+ * This is not sane at all. But if other server
+ * has sent an error now, it is probably closing
+ * the link as well.
+ */
+ return buffer;
+ }
+
+ /* no break */
+
+ default: /* error ! */
+ /* should probably mark link as dead or something... */
+ sendto_flag(SCH_ERROR, "inflate() error(%d): %s", r,
+ (zin->msg) ? zin->msg : "?");
+ *length = -1; /* report error condition */
+ break;
+ }
+ return NULL;
+}
+
+/*
+** zip_buffer
+** Zip the content of cptr->zip->outbuf and of the buffer,
+** put anything left in cptr->zip->outbuf, update cptr->zip->outcount
+**
+** if flush is set, then all available data will be compressed,
+** otherwise, compression only occurs if there's enough to compress,
+** or if we are reaching the maximum allowed size during a connect burst.
+**
+** will return the uncompressed buffer, length will be updated.
+** if a fatal error occurs, length will be set to -1
+*/
+char * zip_buffer(cptr, buffer, length, flush)
+aClient *cptr;
+char *buffer;
+int *length, flush;
+{
+ Reg z_stream *zout = cptr->zip->out;
+ int r;
+
+ if (buffer)
+ {
+ /* concatenate buffer in cptr->zip->outbuf */
+ bcopy(buffer, cptr->zip->outbuf + cptr->zip->outcount,*length);
+ cptr->zip->outcount += *length;
+ }
+ *length = 0;
+
+ if (!flush && ((cptr->zip->outcount < ZIP_MINIMUM) ||
+ ((cptr->zip->outcount < (ZIP_MAXIMUM - BUFSIZE)) &&
+ CBurst(cptr))))
+ return NULL;
+
+ zout->next_in = cptr->zip->outbuf;
+ zout->avail_in = cptr->zip->outcount;
+ zout->next_out = zipbuf;
+ zout->avail_out = ZIP_BUFFER_SIZE;
+
+ switch (r = deflate(zout, Z_PARTIAL_FLUSH))
+ {
+ case Z_OK:
+ if (zout->avail_in)
+ {
+ /* can this occur?? I hope not... */
+ sendto_flag(SCH_ERROR,
+ "deflate() didn't process all available data!");
+ }
+ cptr->zip->outcount = 0;
+ *length = ZIP_BUFFER_SIZE - zout->avail_out;
+ return zipbuf;
+
+ default: /* error ! */
+ sendto_flag(SCH_ERROR, "deflate() error(%d): %s", r,
+ (zout->msg) ? zout->msg : "?");
+ *length = -1;
+ break;
+ }
+ return NULL;
+}
+
+#endif /* ZIP_LINKS */
diff --git a/ircd/s_zip_ext.h b/ircd/s_zip_ext.h
new file mode 100644
index 0000000..6ab86d1
--- /dev/null
+++ b/ircd/s_zip_ext.h
@@ -0,0 +1,38 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_zip_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/s_zip.c.
+ */
+
+/* External definitions for global functions.
+ */
+#ifndef S_ZIP_C
+#define EXTERN extern
+#else /* S_ZIP_C */
+#define EXTERN
+#endif /* S_ZIP_C */
+#ifdef ZIP_LINKS
+EXTERN int zip_init __P((aClient *cptr));
+EXTERN void zip_free __P((aClient *cptr));
+EXTERN char *unzip_packet __P((aClient *cptr, char *buffer, int *length));
+EXTERN char *zip_buffer __P((aClient *cptr, char *buffer, int *length,
+ int flush));
+#endif /* ZIP_LINKS */
+#undef EXTERN
diff --git a/ircd/service_def.h b/ircd/service_def.h
new file mode 100644
index 0000000..02a1d9d
--- /dev/null
+++ b/ircd/service_def.h
@@ -0,0 +1,54 @@
+
+/* The different things a service can `sniff' */
+
+#define SERVICE_WANT_SERVICE 0x00000001 /* other services signing on/off */
+#define SERVICE_WANT_OPER 0x00000002 /* operators, included in _UMODE */
+#define SERVICE_WANT_UMODE 0x00000004 /* user modes, iow + local modes */
+#define SERVICE_WANT_AWAY 0x00000008 /* away isn't propaged anymore.. */
+#define SERVICE_WANT_KILL 0x00000010 /* KILLs */
+#define SERVICE_WANT_NICK 0x00000020 /* all NICKs (new user, change) */
+#define SERVICE_WANT_USER 0x00000040 /* USER signing on */
+#define SERVICE_WANT_QUIT 0x00000080 /* all QUITs (users signing off) */
+#define SERVICE_WANT_SERVER 0x00000100 /* servers signing on */
+#define SERVICE_WANT_WALLOP 0x00000200 /* wallops */
+#define SERVICE_WANT_SQUIT 0x00000400 /* servers signing off */
+#define SERVICE_WANT_RQUIT 0x00000800 /* regular user QUITs (these which
+ are also sent between servers) */
+#define SERVICE_WANT_MODE 0x00001000 /* channel modes (not +ov) */
+#define SERVICE_WANT_CHANNEL 0x00002000 /* channel creations/destructions */
+#define SERVICE_WANT_VCHANNEL 0x00004000 /* channel joins/parts */
+#define SERVICE_WANT_TOPIC 0x00008000 /* channel topics */
+
+#define SERVICE_WANT_ERRORS 0x01000000 /* &ERRORS */
+#define SERVICE_WANT_NOTICES 0x02000000 /* &NOTICES */
+#define SERVICE_WANT_LOCAL 0x04000000 /* &LOCAL */
+#define SERVICE_WANT_NUMERICS 0x08000000 /* &NUMERICS */
+
+#define SERVICE_WANT_USERLOG 0x10000000 /* FNAME_USERLOG */
+#define SERVICE_WANT_CONNLOG 0x20000000 /* FNAME_CONNLOG */
+
+/* masks */
+#define SERVICE_MASK_GLOBAL 0x00007000 /*for these,service must be global*/
+#define SERVICE_MASK_PREFIX 0x00000FFF /* these actions have a prefix */
+#define SERVICE_MASK_ALL 0x3F00FFFF /* all possible actions */
+#define SERVICE_MASK_NUM (SERVICE_WANT_NICK|SERVICE_WANT_USER|\
+ SERVICE_WANT_UMODE)
+
+/* options */
+#define SERVICE_WANT_PREFIX 0x00010000 /* to receive n!u@h instead of n */
+#define SERVICE_WANT_TOKEN 0x00020000 /* use serv token instead of name */
+#define SERVICE_WANT_EXTNICK 0x00040000 /* user extended NICK syntax */
+
+/* A couple example types of services */
+#define SERVICE_ALL SERVICE_MASK_ALL /* 4095 */
+#define SERVICE_NICK SERVICE_WANT_NICK | \
+ SERVICE_WANT_QUIT | \
+ SERVICE_WANT_AWAY /* 168 */
+#define SERVICE_USERS SERVICE_WANT_NICK | \
+ SERVICE_WANT_USER | \
+ SERVICE_WANT_QUIT | \
+ SERVICE_WANT_AWAY | \
+ SERVICE_WANT_UMODE /* 236 */
+#define SERVICE_LINKS SERVICE_WANT_SERVER | \
+ SERVICE_WANT_SQUIT | \
+ SERVICE_WANT_WALLOP /* 1792 */
diff --git a/ircd/sys_def.h b/ircd/sys_def.h
new file mode 100644
index 0000000..72cd68e
--- /dev/null
+++ b/ircd/sys_def.h
@@ -0,0 +1,31 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/sys_def.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.
+ */
+
+#if defined(DEBUGMODE) && !defined(CLIENT_COMPILE) && \
+ !defined(CHKCONF_COMPILE) && defined(DO_DEBUG_MALLOC)
+# define free(x) MyFree(x)
+#else
+# define MyFree(x) if ((x) != NULL) free(x)
+#endif
+
+#define SETSOCKOPT(fd, o1, o2, p1, o3) setsockopt(fd, o1, o2, (char *)p1,\
+ (SOCK_LEN_TYPE) sizeof(o3))
+
+#define GETSOCKOPT(fd, o1, o2, p1, p2) getsockopt(fd, o1, o2, (char *)p1,\
+ (SOCK_LEN_TYPE *)p2)
diff --git a/ircd/version.c.SH.in b/ircd/version.c.SH.in
new file mode 100644
index 0000000..9bdb9c4
--- /dev/null
+++ b/ircd/version.c.SH.in
@@ -0,0 +1,113 @@
+#! /bin/sh
+
+echo "Building 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`
+
+sumsserv="`(cd ../ircd; @SUM@ s_serv.c)`"
+sumsuser="`(cd ../ircd; @SUM@ s_user.c)`"
+sumchan="`(cd ../ircd; @SUM@ channel.c)`"
+sumsbsd="`(cd ../ircd; @SUM@ s_bsd.c)`"
+sumhash="`(cd ../ircd; @SUM@ hash.c)`"
+sumsmisc="`(cd ../ircd; @SUM@ s_misc.c)`"
+sumircd="`(cd ../ircd; @SUM@ ircd.c)`"
+
+creation=`date | \
+awk '{if (NF == 6) \
+ { print $1 " " $2 " " $3 " " $6 " at " $4 " " $5 } \
+else \
+ { print $1 " " $2 " " $3 " " $7 " at " $4 " " $5 " " $6 }}'`
+
+cat >version.c <<!SUB!THIS!
+/*
+ * IRC - Internet Relay Chat, ircd/version.c
+ * Copyright (C) 1990 Chelsea Ashley Dyerman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file is generated by version.c.SH. Any changes made will go away.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)\$Id: version.c.SH.in,v 1.9 1999/07/21 22:59:54 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define VERSION_C
+#include "s_externs.h"
+#undef VERSION_C
+
+char *generation = "$generation";
+char *creation = "$creation";
+char *version; /* Filled by make_version() */
+char *pass_version = PATCHLEVEL;
+
+char *infotext[] =
+ {
+ "IRC --",
+ "Based on the original code written by Jarkko Oikarinen",
+ "Copyright 1988, 1989, 1990, 1991 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.",
+ "",
+ "The following persons have made many changes and enhancements to the",
+ "code and still know how IRC really works if you have questions about it:",
+ "",
+ "syrk Christophe Kalt kalt@stealth.net",
+ "",
+ "Thanks to the following people for help with preparing 2.10",
+ "",
+ "Beeth Piotr Kucharski chopin@sgh.waw.pl",
+ "Q Kurt Roeckx Q@ping.be",
+ "Core Magnus Tjernstrom d92-mtm@ludd.luth.se",
+ "",
+ "Those who helped in prior versions and continue to be helpful:",
+ "",
+ "Jarkko Oikarinen",
+ "Darren Reed Vesa Ruokonen",
+ "Matthew Green Chuck Kane Matt Lyle",
+ "Markku Savela Greg Lindahl Armin Gruner",
+ "Stellan Klebom Dan Goodwin Mike Bolotski",
+ "Ian Frechette Markku Jarvinen Kimmo Suominen",
+ "Jeff Trim Vijay Subramaniam Karl Kleinpaste",
+ "Bill Wisner Tom Davis Hugo Calendar",
+ "Tom Hopkins Stephen van den Berg",
+ "Bo Adler Michael Sandrof Jon Solomon",
+ "Jan Peterson Helen Rose Paul Graham",
+ "",
+ "Thanks also goes to those persons not mentioned here who have added",
+ "their advice, opinions, and code to IRC.",
+ "Thanks also to those who provide 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/version_ext.h b/ircd/version_ext.h
new file mode 100644
index 0000000..5729c6f
--- /dev/null
+++ b/ircd/version_ext.h
@@ -0,0 +1,32 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/version_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/version.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef VERSION_C
+extern char *generation;
+extern char *creation;
+extern char *version;
+extern char *pass_version;
+extern char *infotext[];
+#endif /* VERSION_C */
diff --git a/ircd/whowas.c b/ircd/whowas.c
new file mode 100644
index 0000000..0dec472
--- /dev/null
+++ b/ircd/whowas.c
@@ -0,0 +1,464 @@
+/************************************************************************
+ * 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.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: whowas.c,v 1.6 1999/06/27 19:08:46 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define WHOWAS_C
+#include "s_externs.h"
+#undef WHOWAS_C
+
+static aName *was;
+int ww_index = 0, ww_size = MAXCONNECTIONS*2;
+static aLock *locked;
+int lk_index = 0, lk_size = MAXCONNECTIONS*2;
+
+static void grow_history()
+{
+ int osize = ww_size;
+
+ Debug((DEBUG_ERROR, "Whowas/grow_history ww:%d, lk:%d, #%d, %#x/%#x",
+ ww_size, lk_size, numclients, was, locked));
+ ww_size = (int)((float)numclients * 1.1);
+ was = (aName *)MyRealloc((char *)was, sizeof(*was) * ww_size);
+ bzero((char *)(was + osize), sizeof(*was) * (ww_size - osize));
+ lk_size = (int)((float)numclients * 1.1);
+ locked = (aLock *)MyRealloc((char *)locked, sizeof(*locked) * lk_size);
+ bzero((char *)(locked + osize), sizeof(*locked) * (lk_size - osize));
+ Debug((DEBUG_ERROR, "Whowas/grow_history %#x/%#x", was, locked));
+ ircd_writetune(tunefile);
+}
+
+
+/*
+** add_history
+** Add the currently defined name of the client to history.
+** usually called before changing to a new name (nick).
+** Client must be a fully registered user (specifically,
+** the user structure must have been allocated).
+** if nodelay is NULL, then the nickname will be subject to NickDelay
+*/
+void add_history(cptr, nodelay)
+Reg aClient *cptr, *nodelay;
+{
+ Reg aName *np;
+ Reg Link *uwas;
+
+ cptr->user->refcnt++;
+
+ np = &was[ww_index];
+
+ if ((np->ww_online && (np->ww_online != &me))
+ && !(np->ww_user && np->ww_user->uwas))
+#ifdef DEBUGMODE
+ dumpcore("whowas[%d] %#x %#x %#x", ww_index, np->ww_online,
+ np->ww_user, np->ww_user->uwas);
+#else
+ sendto_flag(SCH_ERROR, "whowas[%d] %#x %#x %#x", ww_index,
+ np->ww_online, np->ww_user, np->ww_user->uwas);
+#endif
+ /*
+ ** The entry to be overwritten in was[] is still online.
+ ** its uwas has to be updated
+ */
+ if (np->ww_online && (np->ww_online != &me))
+ {
+ Reg Link **old_uwas;
+
+ old_uwas = &(np->ww_user->uwas);
+ /* (*old_uwas) should NEVER happen to be NULL. -krys */
+ while ((*old_uwas)->value.i != ww_index)
+ old_uwas = &((*old_uwas)->next);
+ uwas = *old_uwas;
+ *old_uwas = uwas->next;
+ free_link(uwas);
+ free_user(np->ww_user, np->ww_online);
+ istat.is_wwuwas--;
+ }
+ else if (np->ww_user)
+ {
+ /*
+ ** Testing refcnt here is quite ugly, and unexact.
+ ** Nonetheless, the result is almost correct, and another
+ ** ugly thing in free_server() shoud make it exact.
+ ** The problem is that 1 anUser structure might be
+ ** referenced several times from whowas[] but is only counted
+ ** once. One knows when to add, not when to substract - krys
+ */
+ if (np->ww_user->refcnt == 1)
+ {
+ istat.is_wwusers--;
+ if (np->ww_user->away)
+ {
+ istat.is_wwaways--;
+ istat.is_wwawaysmem -=strlen(np->ww_user->away)
+ + 1;
+ }
+ }
+ free_user(np->ww_user, NULL);
+ }
+
+ if (np->ww_logout != 0)
+ {
+ int elapsed = timeofday - np->ww_logout;
+
+ /* some stats */
+ ircstp->is_wwcnt++;
+ ircstp->is_wwt += elapsed;
+ if (elapsed < ircstp->is_wwmt)
+ ircstp->is_wwmt = elapsed;
+ if (elapsed > ircstp->is_wwMt)
+ ircstp->is_wwMt = elapsed;
+
+ if (np->ww_online == NULL)
+ {
+ if (locked[lk_index].logout)
+ {
+ elapsed = timeofday - locked[lk_index].logout;
+ /* some stats first */
+ ircstp->is_lkcnt++;
+ ircstp->is_lkt += elapsed;
+ if (elapsed < ircstp->is_lkmt)
+ ircstp->is_lkmt = elapsed;
+ if (elapsed > ircstp->is_lkMt)
+ ircstp->is_lkMt = elapsed;
+ }
+
+ /*
+ ** This nickname has to be locked, thus copy it to the
+ ** lock[] array.
+ */
+ strncpyzt(locked[lk_index].nick, np->ww_nick, NICKLEN);
+ locked[lk_index++].logout = np->ww_logout;
+ if (lk_index >= lk_size)
+ lk_index = 0;
+ }
+ }
+
+ if (nodelay == cptr) /* &me is NOT a valid value, see off_history() */
+ {
+ /*
+ ** The client is online, np->ww_online is going to point to
+ ** it. The client user struct has to point to this entry
+ ** as well for faster off_history()
+ ** this uwas, and the ww_online form a pair.
+ */
+ uwas = make_link();
+ istat.is_wwuwas++;
+ /*
+ ** because of reallocs, one can not store a pointer inside
+ ** the array. store the index instead.
+ */
+ uwas->value.i = ww_index;
+ uwas->flags = timeofday;
+ uwas->next = cptr->user->uwas;
+ cptr->user->uwas = uwas;
+ }
+
+ np->ww_logout = timeofday;
+ np->ww_user = cptr->user;
+ np->ww_online = (nodelay != NULL) ? nodelay : NULL;
+
+ strncpyzt(np->ww_nick, cptr->name, NICKLEN+1);
+ strncpyzt(np->ww_info, cptr->info, REALLEN+1);
+
+ ww_index++;
+ if ((ww_index == ww_size) && (numclients > ww_size))
+ grow_history();
+ if (ww_index >= ww_size)
+ ww_index = 0;
+ return;
+}
+
+/*
+** get_history
+** Return the current client that was using the given
+** nickname within the timelimit. Returns NULL, if no
+** one found...
+*/
+aClient *get_history(nick, timelimit)
+char *nick;
+time_t timelimit;
+{
+ Reg aName *wp, *wp2;
+ Reg int i = 0;
+
+ wp = wp2 = &was[ww_index];
+ timelimit = timeofday - timelimit;
+
+ do {
+ if (!mycmp(nick, wp->ww_nick) && wp->ww_logout >= timelimit)
+ break;
+ wp++;
+ if (wp == &was[ww_size])
+ i = 1, wp = was;
+ } while (wp != wp2);
+
+ if (wp != wp2 || !i)
+ if (wp->ww_online == &me)
+ return (NULL);
+ else
+ return (wp->ww_online);
+ return (NULL);
+}
+
+/*
+** find_history
+** Returns 1 if a user was using the given nickname within
+** the timelimit. Returns 0, if none found...
+*/
+int find_history(nick, timelimit)
+char *nick;
+time_t timelimit;
+{
+ Reg aName *wp, *wp2;
+ Reg aLock *lp, *lp2;
+ Reg int i = 0;
+
+ wp = wp2 = &was[ww_index];
+#ifdef RANDOM_NDELAY
+ timelimit = timeofday - timelimit - (lk_index % 60);
+#else
+ timelimit = timeofday - timelimit;
+#endif
+
+ do {
+ if (!mycmp(nick, wp->ww_nick) &&
+ (wp->ww_logout >= timelimit) && (wp->ww_online == NULL))
+ break;
+ wp++;
+ if (wp == &was[ww_size])
+ i = 1, wp = was;
+ } while (wp != wp2);
+ if ((wp != wp2 || !i) && (wp->ww_online == NULL))
+ return (1);
+
+ lp = lp2 = &locked[lk_index];
+ i = 0;
+ do {
+ if (!myncmp(nick, lp->nick, NICKLEN) &&
+ (lp->logout >= timelimit))
+ break;
+ lp++;
+ if (lp == &locked[lk_size])
+ i = 1, lp = locked;
+ } while (lp != lp2);
+ if (lp != lp2 || !i)
+ return (1);
+
+ return (0);
+}
+
+/*
+** off_history
+** This must be called when the client structure is about to
+** be released. History mechanism keeps pointers to client
+** structures and it must know when they cease to exist. This
+** also implicitly calls AddHistory.
+*/
+void off_history(cptr)
+Reg aClient *cptr;
+{
+ Reg Link *uwas;
+
+ /*
+ ** If the client has uwas entry/ies, there are also entries in
+ ** the whowas array which point back to it.
+ ** They have to be removed, by pairs
+ */
+ while ((uwas = cptr->user->uwas))
+ {
+ if (was[uwas->value.i].ww_online != cptr)
+#ifdef DEBUGMODE
+ dumpcore("was[%d]: %#x != %#x", uwas->value.i,
+ was[uwas->value.i].ww_online, cptr);
+#else
+ sendto_flag(SCH_ERROR, "was[%d]: %#x != %#x",
+ uwas->value.i,
+ was[uwas->value.i].ww_online, cptr);
+#endif
+ /*
+ ** using &me to make ww_online non NULL (nicknames to be
+ ** locked). &me can safely be used, it is constant.
+ */
+ was[uwas->value.i].ww_online = &me;
+ cptr->user->uwas = uwas->next;
+ free_link(uwas);
+ istat.is_wwuwas--;
+ }
+
+ istat.is_wwusers++;
+ if (cptr->user->away)
+ {
+ istat.is_wwaways++;
+ istat.is_wwawaysmem += strlen(cptr->user->away) + 1;
+ }
+
+ return;
+}
+
+void initwhowas()
+{
+ Reg int i;
+
+ was = (aName *)MyMalloc(sizeof(*was) * ww_size);
+
+ for (i = 0; i < ww_size; i++)
+ bzero((char *)&was[i], sizeof(aName));
+ locked = (aLock *)MyMalloc(sizeof(*locked) * lk_size);
+ for (i = 0; i < lk_size; i++)
+ bzero((char *)&locked[i], sizeof(aLock));
+
+ ircstp->is_wwmt = ircstp->is_lkmt = DELAYCHASETIMELIMIT
+ * DELAYCHASETIMELIMIT;
+ return;
+}
+
+
+/*
+** m_whowas
+** parv[0] = sender prefix
+** parv[1] = nickname queried
+*/
+int m_whowas(cptr, sptr, parc, parv)
+aClient *cptr, *sptr;
+int parc;
+char *parv[];
+{
+ Reg aName *wp, *wp2 = NULL;
+ Reg int j = 0;
+ Reg anUser *up = NULL;
+ int max = -1;
+ char *p, *nick, *s;
+
+ if (parc < 2)
+ {
+ sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN, parv[0]));
+ return 1;
+ }
+ if (parc > 2)
+ max = atoi(parv[2]);
+ if (parc > 3)
+ if (hunt_server(cptr,sptr,":%s WHOWAS %s %s :%s", 3,parc,parv))
+ return 3;
+
+ parv[1] = canonize(parv[1]);
+ if (!MyConnect(sptr))
+ max = MIN(max, 20);
+
+ for (s = parv[1]; (nick = strtoken(&p, s, ",")); s = NULL)
+ {
+ wp = wp2 = &was[ww_index - 1];
+
+ do {
+ if (wp < was)
+ wp = &was[ww_size - 1];
+ if (mycmp(nick, wp->ww_nick) == 0)
+ {
+ up = wp->ww_user;
+ sendto_one(sptr, rpl_str(RPL_WHOWASUSER,
+ parv[0]), wp->ww_nick, up->username,
+ up->host, wp->ww_info);
+ sendto_one(sptr, rpl_str(RPL_WHOISSERVER,
+ parv[0]), wp->ww_nick, up->server,
+ myctime(wp->ww_logout));
+ if (up->away)
+ sendto_one(sptr, rpl_str(RPL_AWAY,
+ parv[0]),
+ wp->ww_nick, up->away);
+ j++;
+ }
+ if (max > 0 && j >= max)
+ break;
+ wp--;
+ } while (wp != wp2);
+
+ if (up == NULL)
+ {
+ if (strlen(parv[1]) > (size_t) NICKLEN)
+ parv[1][NICKLEN] = '\0';
+ sendto_one(sptr, err_str(ERR_WASNOSUCHNICK, parv[0]),
+ parv[1]);
+ }
+
+ if (p)
+ p[-1] = ',';
+ }
+ sendto_one(sptr, rpl_str(RPL_ENDOFWHOWAS, parv[0]), parv[1]);
+ return 2;
+ }
+
+
+/*
+** for debugging...counts related structures stored in whowas array.
+*/
+void count_whowas_memory(wwu, wwa, wwam, wwuw)
+int *wwu, *wwa, *wwuw;
+u_long *wwam;
+{
+ Reg anUser *tmp;
+ Reg Link *tmpl;
+ Reg int i, j;
+ int u = 0, a = 0, w = 0;
+ u_long am = 0;
+
+ for (i = 0; i < ww_size; i++)
+ if ((tmp = was[i].ww_user))
+ {
+ for (j = 0; j < i; j++)
+ if (was[j].ww_user == tmp)
+ break;
+ if (j < i)
+ continue;
+ if (was[i].ww_online == NULL ||
+ was[i].ww_online == &me)
+ {
+ u++;
+ if (tmp->away)
+ {
+ a++;
+ am += (strlen(tmp->away)+1);
+ }
+ }
+ else
+ {
+ tmpl = tmp->uwas;
+ while (tmpl)
+ {
+ w++;
+ tmpl = tmpl->next;
+ }
+ }
+ }
+ *wwu = u;
+ *wwa = a;
+ *wwam = am;
+ *wwuw = w;
+
+ return;
+}
diff --git a/ircd/whowas_def.h b/ircd/whowas_def.h
new file mode 100644
index 0000000..d1ba12f
--- /dev/null
+++ b/ircd/whowas_def.h
@@ -0,0 +1,34 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/whowas_def.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.
+ */
+
+/*
+** WHOWAS structure moved here from whowas.c
+*/
+typedef struct aname {
+ anUser *ww_user;
+ aClient *ww_online;
+ time_t ww_logout;
+ char ww_nick[NICKLEN+1];
+ char ww_info[REALLEN+1];
+} aName;
+
+typedef struct alock {
+ time_t logout;
+ char nick[NICKLEN];
+} aLock;
diff --git a/ircd/whowas_ext.h b/ircd/whowas_ext.h
new file mode 100644
index 0000000..f65d437
--- /dev/null
+++ b/ircd/whowas_ext.h
@@ -0,0 +1,47 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/whowas_ext.h
+ * Copyright (C) 1997 Alain Nissen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file contains external definitions for global variables and functions
+ defined in ircd/whowas.c.
+ */
+
+/* External definitions for global variables.
+ */
+#ifndef WHOWAS_C
+extern int ww_index, ww_size;
+extern int lk_index, lk_size;
+#endif /* WHOWAS_C */
+
+/* External definitions for global functions.
+ */
+#ifndef WHOWAS_C
+#define EXTERN extern
+#else /* WHOWAS_C */
+#define EXTERN
+#endif /* WHOWAS_C */
+EXTERN void add_history __P((Reg aClient *cptr, Reg aClient *nodelay));
+EXTERN aClient *get_history __P((char *nick, time_t timelimit));
+EXTERN int find_history __P((char *nick, time_t timelimit));
+EXTERN void off_history __P((Reg aClient *cptr));
+EXTERN void initwhowas();
+EXTERN int m_whowas __P((aClient *cptr, aClient *sptr, int parc,
+ char *parv[]));
+EXTERN void count_whowas_memory __P((int *wwu, int *wwa, u_long *wwam,
+ int *wwuw));
+#undef EXTERN