diff options
author | 2020-05-25 20:09:04 +0200 | |
---|---|---|
committer | 2020-05-25 20:09:04 +0200 | |
commit | 4440a86cfa359b8e40a484a2cd46d33db5455d8a (patch) | |
tree | f5c0c59aebf0058ae97e7ef8b5fb8017f459a05a /ircd | |
download | ircd-4440a86cfa359b8e40a484a2cd46d33db5455d8a.tar.gz |
Initial
Diffstat (limited to 'ircd')
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 |