From e93ba1690ac9c73ad17a9fdf41e672bb6ccac466 Mon Sep 17 00:00:00 2001 From: Perry Lorier Date: Sun, 9 Jul 2000 04:52:59 +0000 Subject: [PATCH] Author: Isomer Log message: * Updated doc/ircd.8, it was 12 years out of date. * rewrote m_whois.c, much clearer how it works, fixed a couple of it's quirks and documented how it works. Should work the same as the old version. If the phase of the moon is in the right quarter it may even use less cpu. * updated rfc1459.unet * fixed some IPcheck funnies. * fixed the numeric nick collision funnies? (unsure) * We're now keeping track of server connections in IPcheck to prevent us having to special case them all over the place. Testing required: * does it run. * does whois work as expected? * does ipcheck still core? * are the docs all up to date? git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@265 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- ChangeLog | 11 +- RELEASE.NOTES | 5 - config/config-sh.in | 3 +- doc/Configure.help | 9 + doc/example.conf | 4 + doc/ircd.8 | 72 ++-- doc/p10.html | 17 +- include/IPcheck.h | 1 + ircd/IPcheck.c | 33 +- ircd/channel.c | 2 +- ircd/m_nick.c | 20 +- ircd/m_whois.c | 981 +++++++++++--------------------------------- ircd/s_bsd.c | 2 + 13 files changed, 347 insertions(+), 813 deletions(-) diff --git a/ChangeLog b/ChangeLog index bef1a32..f1feee4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2000-07-08 Perry Lorier + * doc/ircd.8: Updated the documentation, it was slightly out of date + being updated around the 05 March 1988. + * ircd/m_whois.c: Rewrote for clarity, and probably a bit more speed. + fixed a few minor glitches. + * doc/rfc1459.unet: Updated. + * ircd/IPcheck.c: Fixed more bugs. + * ircd/s_bsd.c: We now keep track of servers we've conected. + 2000-07-02 Perry Lorier * ircd/s_misc.c: Fixed remote IPcheck bug. Ok, I'm a moron, so sue me. Thanks to Hektik, thanks thanks thanks thanks @@ -1260,7 +1269,7 @@ # # ChangeLog for ircu2.10.11 # -# $Id: ChangeLog,v 1.153 2000-07-02 01:06:17 isomer Exp $ +# $Id: ChangeLog,v 1.154 2000-07-09 04:52:58 isomer Exp $ # # Insert new changes at beginning of the change list. # diff --git a/RELEASE.NOTES b/RELEASE.NOTES index 47c4f2b..42dbf24 100644 --- a/RELEASE.NOTES +++ b/RELEASE.NOTES @@ -68,11 +68,6 @@ run make config. -DEXTENDED_NUMERICS This option configures the server to send extended numerics as well as parse them. This option should only be used on networks that run ircu2.10.07 and above only. --DFERGUSON_FLUSHER This causes your server to attempt to flush -it's internal buffers before dropping clients during a netbreak. -This option *may* panic your server - especially if your running -on a unconfigured FreeBSD box. If you can define this we recommend -you do - but beware of spontanious reboots on netbreak. -DWALLOPS_OPER_ONLY Setting this option removes the ability for clients that are not opered to see wallops messages. -DNOOPER Disallow the /oper command. diff --git a/config/config-sh.in b/config/config-sh.in index 9fc336e..1c26420 100644 --- a/config/config-sh.in +++ b/config/config-sh.in @@ -317,6 +317,7 @@ comment 'Configuration' else int 'Max size of the total of of all sendqs (bytes)' BUFFERPOOL 9000000 fi + bool 'Aggressively empty the sendqpool (Read Help!)' HAS_FERGUSON_FLUSHER n int 'Max receive queue for clients (bytes)' CLIENT_FLOOD 1024 int 'Maximum number of network connections (23 - (FD_SETSIZE-4))' MAXCONNECTIONS 252 int 'Default port for connections to other servers' SERVER_PORT 4400 @@ -384,7 +385,7 @@ comment 'Mandatory defines (you should leave these untouched)' define_bool LOCAL_KILL_ONLY $LOCAL_KILL_ONLY fi int 'KILL nick chase time limit (30)' KILLCHASETIMELIMIT 30 - int 'Max number of channels per user (recommended: 10)' MAXCHANNELSPERUSER 10 + int 'Max number of channels per user' MAXCHANNELSPERUSER 10 int 'Max number of silence masks (15!)' MAXSILES 15 int 'Expected average banmask length (40!)' AVBANLEN 40 eval define_macro MAXSILELENGTH \'\($AVBANLEN * MAXSILES\)\' diff --git a/doc/Configure.help b/doc/Configure.help index 965c7f9..d8cf814 100644 --- a/doc/Configure.help +++ b/doc/Configure.help @@ -652,6 +652,15 @@ BUFFERPOOL have less than 4000 local clients. Don't forget to specify this value in bytes. +Aggressively empty the sendqpool (Read Help!) +HAS_FERGUSON_FLUSHER + Instead of dropping clients with 'Buffer Allocation Error', try to send + data to clients to try and drop the sendq buffer size as a last resort + before dropping clients. This doesn't get rid of the problem, it just + tries hard to limit it's impact. + WARNING: *THIS PANICS UNCONFIGURED FREEBSD MACHINES*, make sure you've + read doc/freebsd.txt before enabling this feature on a FreeBSD machine. + Max receive queue for clients (bytes) CLIENT_FLOOD Currently, everything that a client sends to a server is read by the server diff --git a/doc/example.conf b/doc/example.conf index 5bcc393..f867212 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -333,11 +333,15 @@ O:*@*.cs.vu.nl:VRKLKuGKn0jLs:Niels::10 # ports; # Undernet uses 4400 for server listener ports. # These are just hints, they are in no way official IANA or IETF policies. +# IANA says we should use port 194, but that requires us to run as root, so +# we don't do that. # # The interface setting allows multiply homed hosts to specify which # interface to use on a port by port basis, if an interface is not specified # the default interface will be used. The interface MUST be the complete # IP address for a real hardware interface on the machine running ircd. +# If you want to use virtual hosting *YOU* *MUST* *USE* *THIS* otherwise it +# WILL bind to all interfaces - not what most people seem to expect. # # The [CS][H] field is an optional field to specify that a port is a # server port or a client port and whether it's hidden or not. diff --git a/doc/ircd.8 b/doc/ircd.8 index be36b88..92a1369 100644 --- a/doc/ircd.8 +++ b/doc/ircd.8 @@ -1,12 +1,11 @@ .\" @(#)ircd.8 2.0 (beta version) 29 Mar 1989 -.TH IRCD 8 "29 March 1989" +.TH IRCD 8 "8 July 2000" .SH NAME -ircd \- The Internet Relay Chat Program Server +ircd \- The Undernet Internet Relay Chat Daemon .SH SYNOPSIS .hy 0 .IP \fBircd\fP -[-a] [-c] [-i] [-o] [-q] [-t] [-d directory] -[-f configfile] [-w interface] [-x debuglevel] [-h hostname] [-p portnum] +[-t] [-d directory] [-f configfile] [-x debuglevel] [-h hostname] .SH DESCRIPTION .LP \fIircd\fP is the server (daemon) program for the Internet Relay Chat @@ -16,7 +15,10 @@ and user messages are passed directly to the \fIircd\fP for processing and relaying to other ircd sites. The \fIirc(1)\fP program depends upon there being an \fIircd\fP server running somewhere (either on your local UNIX site or a remote ircd site) so that it will have somewhere to connect -to and thus allow the user to begin talking to other users. +to and thus allow the user to begin talking to other users. +.LP +There are many common clients including ircii, epic, BitchX and other ones +for Non unix platforms as well, such as mIRC, Pirch, Ircle etc. .SH OPTIONS .TP .B \-d directory @@ -25,26 +27,14 @@ that as a reference point when opening \fIircd.conf\fP and other startup files. .TP .B \-o -Starts up a local ircdaemon. Standard input can be used to send IRC -commands to the daemon. The user logging in from standard input will -be given operator privileges on this local ircd. If ircd is a setuid program, -it will call setuid(getuid()) before going to local mode. This option -can be used in inetd.conf to allow users to open their own irc clients -by simply connecting their clients to the correct ports. For example: -.TP -.B -irc stream tcp nowait irc /etc/ircd ircd \\-f/etc/ircd.conf \\-o - -allows users connecting to irc port (specified in /etc/services) to start -up their own ircdaemon. The configuration file should be used to check from -which hosts these connections are allowed from. This option also turns -on the autodie option -a. +This option is obsolete. .TP .B \-a -Instructs the server to automatically die off if it loses all it's clients. +This option is obsolete. .TP .B \-t -Instructs the server run in the foreground and to direct debugging output to standard output. +Instructs the server run in the foreground and to direct debugging output to +standard output. .TP .B \-x# Defines the debuglevel for ircd. The higher the debuglevel, the more stuff @@ -52,21 +42,12 @@ gets directed to debugging file (or standard output if -t option was used as well). .TP .B \-i -The server was started by inetd and it should start accepting connections -from standard input. The following inetd.conf-line could be used to start -up ircd automatically when needed: -.TP -.B -ircd stream tcp wait irc /etc/ircd ircd \-i - -allows inetd to start up ircd on request. +This option is obsolete. .TP .B \-w interface -If the server was compiled with VIRTUAL_HOST (run 'make config' to toggle -this compile option), then \fIinterface\fP is passed to gethostbyname(3) in -order to retrieve the IP-number of the interface to bind to. An example -would be to use '-w localhost', after which the server only listens on the -loopback interface. Run `ifconfig -a' to see which interfaces you have. +This command is depreciated. Outgoing connections are bound to the +interface specified in the M:, and Incoming connections are accepted only on +interfaces specified in the P:'s. .TP .B \-f filename Specifies the ircd.conf file to be used for this ircdaemon. The option @@ -79,25 +60,21 @@ off and run in the background. This needs to be given if you are starting \fIircd\fP from an \fIrc\fP (such as \fI/etc/rc.local\fP) file. .TP .B \-q -Using the -q option stops the server from doing DNS lookups on all the -servers in your \fIircd.conf\fP file when it boots. This can take a lengthy -amount of time if you have a large number of servers and they are not all -close by. +This option is obsolete. .TP .B \-h hostname Allows the user to manually set the server name at startup. The default name is hostname.domainname. .B \-p portname -Specifies the server port where the daemon should start waiting for connections -from other servers. Clients should connect to ports as specified in the ircd.conf file by means of a P: line. -.TP -.SH +This is depreciated in favour of specifying server ports in P:'s + +.SH CONFIGURATION If you plan to connect your \fIircd\fP server to an existing Irc-Network, you will need to alter your local IRC CONFIGURATION FILE (typically named "ircd.conf") so that it will accept and make connections to other \fIircd\fP servers. This file contains the hostnames, Network Addresses, and sometimes passwords for connections to other ircds around the world. Because -description of the actual file format of the "ircs.conf" file is beyond the +description of the actual file format of the "ircd.conf" file is beyond the scope of this document, please refer to the file INSTALL in the IRC source files documentation directory. .LP @@ -140,8 +117,9 @@ For full COPYRIGHT see LICENSE file with IRC package. .SH "SEE ALSO" irc(1) .SH BUGS -None... ;-) if somebody finds one, please inform author +See the file 'BUGS' included in the distribution. .SH AUTHOR -Jarkko Oikarinen, currently jto@tolsun.oulu.fi, -manual page written by Jeff Trim, jtrim@orion.cair.du.edu, -later modified by jto@tolsun.oulu.fi. +The current authors of the undernet IRC daemon are coder-com@undernet.org, +the original author was Jarkko Oikarinen, currently jto@tolsun.oulu.fi, +manual page written by Jeff Trim, jtrim@orion.cair.du.edu, later modified by +jto@tolsun.oulu.fi and then isomer@coders.net. diff --git a/doc/p10.html b/doc/p10.html index eafab9f..c900715 100644 --- a/doc/p10.html +++ b/doc/p10.html @@ -12,7 +12,7 @@ (As of ircu 2.10.11)

Undernet Coder-com, coder-com@undernet.org

-$Id: p10.html,v 1.4 2000-04-11 05:51:25 gte Exp $ +$Id: p10.html,v 1.5 2000-07-09 04:52:58 isomer Exp $


This document aims to be a practical guide for implementing and maintaining the protocol, not just a reference @@ -623,7 +623,13 @@ This token is formatted exactly the same as a client numeric is formatted. The first 2 characters identify the server's numeric, whilst in this situation, the final 3 characters define the maximum number of clients that this server can hold (and more importantly, the maximum number of numerics it will -generate). +generate). This is always one less than a power of two, because the server +uses this as a bitmask. A server can give out a higher numeric than this, +however it will be "anded" with this number to find it's entry slot. The +reason for this is so a server which is near the maximum number of clients +can give out more numerics than it's using to prevent a new client getting a +numeric that was used only seconds ago and maybe get messages destined to +the old user.
  •  The example "AA]]]" shows that this is a server with numeric 0, which @@ -633,7 +639,8 @@ will generate client numerics up to 262,143.
  • This final parameter simply consists of a textual description of the server prefixed by a colon. This is displayed in a clients WHOIS line, aswell -as in the LINKS reply.
  • +as in the LINKS reply. By convention, if this is a leaf server it contains +the servers IP in square brackets at the beginning of the string, 3.2 Network Database resyncronisation

    After the connection has been established and verified, the next step @@ -652,7 +659,8 @@ the originally recieved server message, with the only exception being that the message is numeric prefixed, to indicate which server sent this message (and also therefore, which hub this new server is linked too). There is also a fixed "0" present before the Description field, this is a placeholder -for future use and currently unused. +for future use and currently unused. [Isomer: Question, what IS this +reserved for?]

    3.2.2 - NICK Messages

    Client information is transmitted via "NICK" messages, of the @@ -1137,6 +1145,7 @@ from msg.h. -Gte.
    [2000-04-10]: Added information about OPMODE and CLEARMODE tokens. -Kev
    [2000-04-11]: Started work on chapter 4. -Gte +
    [2000-06-01]: Changed some info about the max number of clients -Isomer

    8.1 TODO

    • diff --git a/include/IPcheck.h b/include/IPcheck.h index e47eb31..0c7f23f 100644 --- a/include/IPcheck.h +++ b/include/IPcheck.h @@ -18,6 +18,7 @@ struct Client; *--------------------------------------------------------------------------*/ extern void ip_registry_expire(void); extern int ip_registry_check_local(unsigned int addr, time_t* next_target_out); +extern void ip_registry_add_local(unsigned int addr); extern int ip_registry_remote_connect(struct Client *cptr); extern void ip_registry_connect_succeeded(struct Client *cptr); extern void ip_registry_local_disconnect(struct Client *cptr); diff --git a/ircd/IPcheck.c b/ircd/IPcheck.c index 2e9dce2..47bb61b 100644 --- a/ircd/IPcheck.c +++ b/ircd/IPcheck.c @@ -113,7 +113,7 @@ static unsigned int ip_registry_hash(unsigned int ip) *--------------------------------------------------------------------------*/ static struct IPRegistryEntry *ip_registry_find(unsigned int ip) { - struct IPRegistryEntry *entry = NULL; + struct IPRegistryEntry *entry = 0; for (entry = hashTable[ip_registry_hash(ip)]; entry; entry = entry->next) { if (entry->addr == ip) @@ -361,6 +361,28 @@ int ip_registry_check_local(unsigned int addr, time_t *next_target_out) return 0; } +/* + * Add someone to the ip registry without throttling them. + * This is used for server connections. + */ +void ip_registry_add_local(unsigned int addr) +{ + struct IPRegistryEntry *entry = ip_registry_find(addr); + + /* If they've never connected before, let them on */ + if (0 == entry) { + Debug((DEBUG_DEBUG,"IPcheck: Local user allowed - unseen")); + entry = ip_registry_new_entry(addr, 1); + return; + } + + /* Keep track of how many people have connected */ + entry->connected++; + + assert(250 <= entry->connected); + + return; +} /*---------------------------------------------------------------------------- * ip_registry_remote_connect @@ -460,15 +482,6 @@ void ip_registry_local_disconnect(struct Client *cptr) entry = ip_registry_find(cptr->ip.s_addr); - /* Servers might not be in IPcheck because we connected to them, not visa - * versa. - * We can't use IsServer() here, because it might be in the 'unregistered' - * state. - */ - if (0 != cptr->serv && !entry) { - Debug((DEBUG_DEBUG,"IPcheck: Server ignored")); - return; - } Debug((DEBUG_DEBUG,"IPcheck: Local Disconnect")); assert(IsIPChecked(cptr)); diff --git a/ircd/channel.c b/ircd/channel.c index 560bcad..76a20e8 100644 --- a/ircd/channel.c +++ b/ircd/channel.c @@ -227,7 +227,7 @@ int sub1_from_channel(struct Channel* chptr) int i; for (i = 0; i <= HighestFd; i++) { - struct Client *acptr; + struct Client *acptr = 0; if ((acptr = LocalClientArray[i]) && acptr->listing && acptr->listing->chptr == chptr) { diff --git a/ircd/m_nick.c b/ircd/m_nick.c index b8cf167..aa38882 100644 --- a/ircd/m_nick.c +++ b/ircd/m_nick.c @@ -269,17 +269,15 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) ircd_strncpy(nick, parv[1], NICKLEN); nick[NICKLEN] = '\0'; - if (!IsBurstOrBurstAck(sptr)) { - if (IsServer(sptr)) { - lastnick = atoi(parv[3]); - if (lastnick > OLDEST_TS) - sptr->serv->lag = TStime() - lastnick; - } - else { - lastnick = atoi(parv[2]); - if (lastnick > OLDEST_TS) - sptr->user->server->serv->lag = TStime() - lastnick; - } + if (IsServer(sptr)) { + lastnick = atoi(parv[3]); + if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) + sptr->serv->lag = TStime() - lastnick; + } + else { + lastnick = atoi(parv[2]); + if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) + sptr->user->server->serv->lag = TStime() - lastnick; } /* * If do_nick_name() returns a null name OR if the server sent a nick diff --git a/ircd/m_whois.c b/ircd/m_whois.c index 0f2bf93..c2b3fa1 100644 --- a/ircd/m_whois.c +++ b/ircd/m_whois.c @@ -105,451 +105,234 @@ #include /* - * m_whois - generic message handler - * - * parv[0] = sender prefix - * parv[1] = nickname masklist - * - * or - * - * parv[1] = target server - * parv[2] = nickname masklist + * 2000-07-01: Isomer + * * Rewritten to make this understandable + * * You can nolonger /whois unregistered clients. + * + * + * General rules: + * /whois nick always shows the nick. + * /whois wild* shows the nick if: + * * they aren't +i and aren't on any channels. + * * they are on a common channel. + * * they aren't +i and are on a public channel. (not +p and not +s) + * * they aren't +i and are on a private channel. (+p but not +s) + * Or to look at it another way (I think): + * * +i users are only shown if your on a common channel. + * * users on +s channels only aren't shown. + * + * whois doesn't show what channels a +k client is on, for the reason that + * /whois X or /whois W floods a user off the server. :) + * + * nb: if the code and this comment disagree, the codes right and I screwed + * up. */ -int m_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) -{ - struct User* user; - struct Client* acptr; - struct Client* a2cptr; - struct Channel* chptr; - char* nick; - char* tmp; - const char* name; - char* p = 0; - int found; - int len; - int mlen; - int total; - static char buf[512]; - if (parc < 2) - { - send_reply(sptr, ERR_NONICKNAMEGIVEN); - return 0; - } - - if (parc > 2) - { - struct Client *acptr; - /* For convenience: Accept a nickname as first parameter, by replacing - it with the correct servername - as is needed by hunt_server() */ - if (MyUser(sptr) && (acptr = FindUser(parv[1]))) - parv[1] = acptr->user->server->name; - if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) != - HUNTED_ISME) - return 0; - parv[1] = parv[2]; - } +/* + * Send whois information for acptr to sptr + */ +static void do_whois(struct Client* sptr, struct Client *acptr) +{ + struct Client *a2cptr=0; + struct Channel *chptr=0; + int mlen; + int len; + static char buf[512]; + + const struct User* user = acptr->user; + const char* name = (!*acptr->name) ? "?" : acptr->name; + a2cptr = user->server; + assert(user); + send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, + acptr->info); - total = 0; - for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0) + /* Display the channels this user is on. */ + if (!IsChannelService(acptr)) { - int invis, showperson, member, wilds; - - found = 0; - collapse(nick); - wilds = (strchr(nick, '?') || strchr(nick, '*')); - /* Do a hash lookup if the nick does not contain wilds */ - if (wilds) + struct Membership* chan; + mlen = strlen(me.name) + strlen(sptr->name) + 12 + strlen(name); + len = 0; + *buf = '\0'; + for (chan = user->channel; chan; chan = chan->next_channel) { - /* - * We're no longer allowing remote users to generate requests with wildcards. - */ - if (!MyConnect(sptr)) - continue; - for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); - acptr = acptr->next) - { - if (!IsRegistered(acptr) || IsServer(acptr)) + chptr = chan->channel; + + if (!ShowChannel(sptr, chptr)) continue; - /* - * I'm always last :-) and acptr->next == 0!! - */ - if (IsMe(acptr)) - break; - /* - * 'Rules' established for sending a WHOIS reply: - * - * - if wildcards are being used dont send a reply if - * the querier isnt any common channels and the - * client in question is invisible and wildcards are - * in use (allow exact matches only); - * - * - only send replies about common or public channels - * the target user(s) are on; - */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; - - invis = acptr != sptr && IsInvisible(acptr); - member = (user && user->channel) ? 1 : 0; - showperson = (wilds && !invis && !member) || !wilds; - if (user) { - struct Membership* chan; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - member = find_channel_member(sptr, chptr) ? 1 : 0; - if (invis && !member) - continue; - if (IsZombie(chan)) - continue; - if (member || (!invis && PubChannel(chptr))) - { - showperson = 1; - break; - } - if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) - showperson = 1; - } - } - if (!showperson) + + if (acptr != sptr && IsZombie(chan)) continue; - - if (user) - { - a2cptr = user->server; - send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, - acptr->info); - } - else - { - a2cptr = &me; - send_reply(sptr, RPL_WHOISUSER, name, "", "", - ""); - } - - found = 1; - -exact_match: - if (user && !IsChannelService(acptr)) - { - struct Membership* chan; - mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name); - len = 0; + + if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5) + { + send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf); *buf = '\0'; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - if (ShowChannel(sptr, chptr) && - (acptr == sptr || !IsZombie(chan))) - { - if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) - { - send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, - "%s :%s", name, buf); - *buf = '\0'; - len = 0; - } - if (IsDeaf(acptr)) - *(buf + len++) = '-'; - if (is_chan_op(acptr, chptr)) - *(buf + len++) = '@'; - else if (has_voice(acptr, chptr)) - *(buf + len++) = '+'; - else if (IsZombie(chan)) - *(buf + len++) = '!'; - if (len) - *(buf + len) = '\0'; - strcpy(buf + len, chptr->chname); - len += strlen(chptr->chname); - strcat(buf + len, " "); - len++; - } - } - if (buf[0] != '\0') - send_reply(sptr, RPL_WHOISCHANNELS, name, buf); - } - - send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info); - - if (user) - { - if (user->away) - send_reply(sptr, RPL_AWAY, name, user->away); - - if (IsAnOper(acptr)) - send_reply(sptr, RPL_WHOISOPERATOR, name); - - if (MyConnect(acptr)) - send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, - acptr->firsttime); - } - if (found == 2 || total++ >= MAX_WHOIS_LINES) - break; - } - } - else - { - /* No wildcards */ - if ((acptr = FindUser(nick))) - { - found = 2; /* Make sure we exit the loop after passing it once */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; - a2cptr = user->server; - send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, - acptr->info); - goto exact_match; - } - } - if (!found) - send_reply(sptr, ERR_NOSUCHNICK, nick); - if (p) - p[-1] = ','; - if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES) - break; + len = 0; + } + if (IsDeaf(acptr)) + *(buf + len++) = '-'; + if (is_chan_op(acptr, chptr)) + *(buf + len++) = '@'; + else if (has_voice(acptr, chptr)) + *(buf + len++) = '+'; + else if (IsZombie(chan)) + *(buf + len++) = '!'; + if (len) + *(buf + len) = '\0'; + strcpy(buf + len, chptr->chname); + len += strlen(chptr->chname); + strcat(buf + len, " "); + len++; + } + if (buf[0] != '\0') + send_reply(sptr, RPL_WHOISCHANNELS, name, buf); } - send_reply(sptr, RPL_ENDOFWHOIS, parv[1]); + send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info); - return 0; + if (user) + { + if (user->away) + send_reply(sptr, RPL_AWAY, name, user->away); + + if (IsAnOper(acptr)) + send_reply(sptr, RPL_WHOISOPERATOR, name); + + /* Hint: if your looking to add more flags to a user, eg +h, here's + * probably a good place to add them :) + */ + + if (MyConnect(acptr)) + send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, + acptr->firsttime); + } } /* - * ms_whois - server message handler - * - * parv[0] = sender prefix - * parv[1] = nickname masklist - * - * or - * - * parv[1] = target server - * parv[2] = nickname masklist + * Search and return as many people as matched by the wild 'nick'. + * returns the number of people found (or, obviously, 0, if none where + * found). */ -int ms_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +static int do_wilds(struct Client* sptr,char *nick,int count) { - struct User* user; - struct Client* acptr; - struct Client* a2cptr; - struct Channel* chptr; - char* nick; - char* tmp; - const char* name; - char* p = 0; - int found; - int len; - int mlen; - int total; - static char buf[512]; - - if (parc < 2) - { - send_reply(sptr, ERR_NONICKNAMEGIVEN); - return 0; - } - - if (parc > 2) - { - struct Client *acptr; - /* For convenience: Accept a nickname as first parameter, by replacing - it with the correct servername - as is needed by hunt_server() */ - if (MyUser(sptr) && (acptr = FindUser(parv[1]))) - parv[1] = acptr->user->server->name; - if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) != - HUNTED_ISME) - return 0; - parv[1] = parv[2]; - } - - total = 0; - for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0) + struct Client *acptr; /* Current client we're concidering */ + struct User *user; /* the user portion of the client */ + char *name; /* the name of this client */ + struct Membership* chan; + int invis; /* does +i apply? */ + int member; /* Is this user on any channels? */ + int showperson; /* Should we show this person? */ + int found = 0 ; /* How many were found? */ + + /* Ech! This is hidious! */ + for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); + acptr = acptr->next) { - int invis, showperson, member, wilds; - - found = 0; - collapse(nick); - wilds = (strchr(nick, '?') || strchr(nick, '*')); - /* Do a hash lookup if the nick does not contain wilds */ - if (wilds) + if (!IsRegistered(acptr)) + continue; + + if (IsServer(acptr)) + continue; + /* + * I'm always last :-) and acptr->next == 0!! + * + * Isomer: Does this strike anyone else as being a horrible hidious + * hack? + */ + if (IsMe(acptr)) { + assert(!acptr->next); + break; + } + + /* + * 'Rules' established for sending a WHOIS reply: + * + * - if wildcards are being used dont send a reply if + * the querier isnt any common channels and the + * client in question is invisible. + * + * - only send replies about common or public channels + * the target user(s) are on; + */ + user = acptr->user; + name = (!*acptr->name) ? "?" : acptr->name; + assert(user); + + invis = (acptr != sptr) && IsInvisible(acptr); + member = (user && user->channel) ? 1 : 0; + showperson = !invis && !member; + + /* Should we show this person now? */ + if (showperson) { + found++; + do_whois(sptr,acptr); + if (count+found>MAX_WHOIS_LINES) + return found; + continue; + } + + /* Step through the channels this user is on */ + for (chan = user->channel; chan; chan = chan->next_channel) { - /* - * We're no longer allowing remote users to generate requests with wildcards. - */ - if (!MyConnect(sptr)) - continue; - for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); - acptr = acptr->next) - { - if (!IsRegistered(acptr) || IsServer(acptr)) - continue; - /* - * I'm always last :-) and acptr->next == 0!! - */ - if (IsMe(acptr)) - break; - /* - * 'Rules' established for sending a WHOIS reply: - * - * - if wildcards are being used dont send a reply if - * the querier isnt any common channels and the - * client in question is invisible and wildcards are - * in use (allow exact matches only); - * - * - only send replies about common or public channels - * the target user(s) are on; - */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; - - invis = acptr != sptr && IsInvisible(acptr); - member = (user && user->channel) ? 1 : 0; - showperson = (wilds && !invis && !member) || !wilds; - if (user) { - struct Membership* chan; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - member = find_channel_member(sptr, chptr) ? 1 : 0; - if (invis && !member) - continue; - if (IsZombie(chan)) - continue; - if (member || (!invis && PubChannel(chptr))) - { - showperson = 1; - break; - } - if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) - showperson = 1; - } - } - if (!showperson) - continue; - - if (user) - { - a2cptr = user->server; - send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, - acptr->info); - } - else - { - a2cptr = &me; - send_reply(sptr, RPL_WHOISUSER, name, "", "", - ""); - } - - found = 1; - -exact_match: - if (user && !IsChannelService(acptr)) - { - struct Membership* chan; - mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name); - len = 0; - *buf = '\0'; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - if (ShowChannel(sptr, chptr) && - (acptr == sptr || !IsZombie(chan))) - { - if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) - { - send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, - "%s :%s", name, buf); - *buf = '\0'; - len = 0; - } - if (IsDeaf(acptr)) - *(buf + len++) = '-'; - if (is_chan_op(acptr, chptr)) - *(buf + len++) = '@'; - else if (has_voice(acptr, chptr)) - *(buf + len++) = '+'; - else if (IsZombie(chan)) - *(buf + len++) = '!'; - if (len) - *(buf + len) = '\0'; - strcpy(buf + len, chptr->chname); - len += strlen(chptr->chname); - strcat(buf + len, " "); - len++; - } - } - if (buf[0] != '\0') - send_reply(sptr, RPL_WHOISCHANNELS, name, buf); - } + struct Channel *chptr = chan->channel; - send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info); - - if (user) - { - if (user->away) - send_reply(sptr, RPL_AWAY, name, user->away); - - if (IsAnOper(acptr)) - send_reply(sptr, RPL_WHOISOPERATOR, name); - - if (MyConnect(acptr)) - send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, - acptr->firsttime); - } - if (found == 2 || total++ >= MAX_WHOIS_LINES) - break; + /* If this is a public channel, show the person */ + if (!invis && PubChannel(chptr)) { + showperson = 1; + break; } - } - else - { - /* No wildcards */ - if ((acptr = FindUser(nick))) - { - found = 2; /* Make sure we exit the loop after passing it once */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; - a2cptr = user->server; - send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, - acptr->info); - goto exact_match; + + /* if this channel is +p and not +s, show them */ + if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) { + showperson = 1; + break; } - } - if (!found) - send_reply(sptr, ERR_NOSUCHNICK, nick); - if (p) - p[-1] = ','; - if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES) - break; - } - send_reply(sptr, RPL_ENDOFWHOIS, parv[1]); + + member = find_channel_member(sptr, chptr) ? 1 : 0; + if (invis && !member) + continue; - return 0; + /* If sptr isn't really on this channel, skip it */ + if (IsZombie(chan)) + continue; + + /* Is this a common channel? */ + if (member) { + showperson = 1; + break; + } + } /* of for (chan in channels) */ + + /* Don't show this person */ + if (!showperson) + continue; + + do_whois(sptr,acptr); + found++; + if (count+found>MAX_WHOIS_LINES) + return found; + } /* of global client list */ + + return found; } /* - * mo_whois - oper message handler + * m_whois - generic message handler * * parv[0] = sender prefix * parv[1] = nickname masklist * * or * - * parv[1] = target server + * parv[1] = target server, or a nickname representing a server to target. * parv[2] = nickname masklist */ -int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +int m_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - struct User* user; - struct Client* acptr; - struct Client* a2cptr; - struct Channel* chptr; char* nick; char* tmp; - const char* name; char* p = 0; - int found; - int len; - int mlen; - int total; - static char buf[512]; + int found = 0; + int total = 0; if (parc < 2) { @@ -561,8 +344,11 @@ int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; /* For convenience: Accept a nickname as first parameter, by replacing - it with the correct servername - as is needed by hunt_server() */ - if (MyUser(sptr) && (acptr = FindUser(parv[1]))) + * it with the correct servername - as is needed by hunt_server(). + * This is the secret behind the /whois nick nick trick. + */ + acptr = FindUser(parv[1]); + if (acptr) parv[1] = acptr->user->server->name; if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) != HUNTED_ISME) @@ -570,203 +356,64 @@ int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) parv[1] = parv[2]; } - total = 0; for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0) { - int invis, showperson, member, wilds; + int wilds; found = 0; + collapse(nick); + wilds = (strchr(nick, '?') || strchr(nick, '*')); - /* Do a hash lookup if the nick does not contain wilds */ - if (wilds) - { - /* - * We're no longer allowing remote users to generate requests with wildcards. - */ - if (!MyConnect(sptr)) - continue; - for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); - acptr = acptr->next) - { - if (!IsRegistered(acptr) || IsServer(acptr)) - continue; - /* - * I'm always last :-) and acptr->next == 0!! - */ - if (IsMe(acptr)) - break; - /* - * 'Rules' established for sending a WHOIS reply: - * - * - if wildcards are being used dont send a reply if - * the querier isnt any common channels and the - * client in question is invisible and wildcards are - * in use (allow exact matches only); - * - * - only send replies about common or public channels - * the target user(s) are on; - */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; - - invis = acptr != sptr && IsInvisible(acptr); - member = (user && user->channel) ? 1 : 0; - showperson = (wilds && !invis && !member) || !wilds; - if (user) { - struct Membership* chan; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - member = find_channel_member(sptr, chptr) ? 1 : 0; - if (invis && !member) - continue; - if (IsZombie(chan)) - continue; - if (member || (!invis && PubChannel(chptr))) - { - showperson = 1; - break; - } - if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) - showperson = 1; - } - } - if (!showperson) - continue; - - if (user) - { - a2cptr = user->server; - send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, - acptr->info); - } - else - { - a2cptr = &me; - send_reply(sptr, RPL_WHOISUSER, name, "", "", - ""); - } - - found = 1; - -exact_match: - if (user && !IsChannelService(acptr)) - { - struct Membership* chan; - mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name); - len = 0; - *buf = '\0'; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - if (ShowChannel(sptr, chptr) && - (acptr == sptr || !IsZombie(chan))) - { - if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) - { - send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, - "%s :%s", name, buf); - *buf = '\0'; - len = 0; - } - if (IsDeaf(acptr)) - *(buf + len++) = '-'; - if (is_chan_op(acptr, chptr)) - *(buf + len++) = '@'; - else if (has_voice(acptr, chptr)) - *(buf + len++) = '+'; - else if (IsZombie(chan)) - *(buf + len++) = '!'; - if (len) - *(buf + len) = '\0'; - strcpy(buf + len, chptr->chname); - len += strlen(chptr->chname); - strcat(buf + len, " "); - len++; - } - } - if (buf[0] != '\0') - send_reply(sptr, RPL_WHOISCHANNELS, name, buf); - } - - send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info); - - if (user) - { - if (user->away) - send_reply(sptr, RPL_AWAY, name, user->away); - - if (IsAnOper(acptr)) - send_reply(sptr, RPL_WHOISOPERATOR, name); - - if (MyConnect(acptr)) - send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, - acptr->firsttime); - } - if (found == 2 || total++ >= MAX_WHOIS_LINES) - break; - } - } - else - { + if (!wilds) { + struct Client *acptr = 0; /* No wildcards */ - if ((acptr = FindUser(nick))) - { - found = 2; /* Make sure we exit the loop after passing it once */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; - a2cptr = user->server; - send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, - acptr->info); - goto exact_match; + acptr = FindUser(nick); + if (acptr && !IsServer(acptr)) { + do_whois(sptr,acptr); + found = 1; } } + else /* wilds */ + found=do_wilds(sptr,nick,total); + if (!found) send_reply(sptr, ERR_NOSUCHNICK, nick); + total+=found; + if (total >= MAX_WHOIS_LINES) { + send_reply(sptr, ERR_QUERYTOOLONG, parv[1]); + break; + } if (p) p[-1] = ','; - if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES) - break; - } + } /* of tokenised parm[1] */ send_reply(sptr, RPL_ENDOFWHOIS, parv[1]); return 0; } - - -#if 0 /* - * m_whois + * ms_whois - server message handler * * parv[0] = sender prefix * parv[1] = nickname masklist * * or * - * parv[1] = target server + * parv[1] = target server, or a nickname representing a server to target. * parv[2] = nickname masklist */ -int m_whois(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) +int ms_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - struct User* user; - struct Client* acptr; - struct Client* a2cptr; - struct Channel* chptr; char* nick; char* tmp; - char* name; char* p = 0; - int found; - int len; - int mlen; - int total; - static char buf[512]; + int found = 0; + int total = 0; if (parc < 2) { - sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); /* XXX DEAD */ + send_reply(sptr, ERR_NONICKNAMEGIVEN); return 0; } @@ -774,181 +421,49 @@ int m_whois(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *acptr; /* For convenience: Accept a nickname as first parameter, by replacing - it with the correct servername - as is needed by hunt_server() */ - if (MyUser(sptr) && (acptr = FindUser(parv[1]))) + * it with the correct servername - as is needed by hunt_server(). + * This is the secret behind the /whois nick nick trick. + */ + acptr = FindUser(parv[1]); + if (acptr) parv[1] = acptr->user->server->name; - if (hunt_server(0, cptr, sptr, "%s%s " TOK_WHOIS " %s :%s", 1, parc, parv) != /* XXX DEAD */ + if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) != HUNTED_ISME) return 0; parv[1] = parv[2]; } total = 0; + for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0) { - int invis, showperson, member, wilds; + struct Client *acptr = 0; found = 0; + collapse(nick); - wilds = (strchr(nick, '?') || strchr(nick, '*')); - /* Do a hash lookup if the nick does not contain wilds */ - if (wilds) - { - /* - * We're no longer allowing remote users to generate requests with wildcards. - */ - if (!MyConnect(sptr)) - continue; - for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); - acptr = acptr->next) - { - if (!IsRegistered(acptr) || IsServer(acptr)) - continue; - /* - * I'm always last :-) and acptr->next == NULL!! - */ - if (IsMe(acptr)) - break; - /* - * 'Rules' established for sending a WHOIS reply: - * - * - if wildcards are being used dont send a reply if - * the querier isnt any common channels and the - * client in question is invisible and wildcards are - * in use (allow exact matches only); - * - * - only send replies about common or public channels - * the target user(s) are on; - */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; + - invis = acptr != sptr && IsInvisible(acptr); - member = (user && user->channel) ? 1 : 0; - showperson = (wilds && !invis && !member) || !wilds; - if (user) { - struct Membership* chan; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - member = find_channel_member(sptr, chptr) ? 1 : 0; - if (invis && !member) - continue; - if (IsZombie(chan)) - continue; - if (member || (!invis && PubChannel(chptr))) - { - showperson = 1; - break; - } - if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) - showperson = 1; - } - } - if (!showperson) - continue; - - if (user) - { - a2cptr = user->server; - sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */ - parv[0], name, user->username, user->host, acptr->info); - } - else - { - a2cptr = &me; - sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */ - parv[0], name, "", "", ""); - } - - found = 1; - -exact_match: - if (user && !IsChannelService(acptr)) - { - struct Membership* chan; - mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name); - len = 0; - *buf = '\0'; - for (chan = user->channel; chan; chan = chan->next_channel) - { - chptr = chan->channel; - if (ShowChannel(sptr, chptr) && - (acptr == sptr || !IsZombie(chan))) - { - if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) - { - sendto_one(sptr, ":%s %d %s %s :%s", /* XXX DEAD */ - me.name, RPL_WHOISCHANNELS, parv[0], name, buf); - *buf = '\0'; - len = 0; - } - if (IsDeaf(acptr)) - *(buf + len++) = '-'; - if (is_chan_op(acptr, chptr)) - *(buf + len++) = '@'; - else if (has_voice(acptr, chptr)) - *(buf + len++) = '+'; - else if (IsZombie(chan)) - *(buf + len++) = '!'; - if (len) - *(buf + len) = '\0'; - strcpy(buf + len, chptr->chname); - len += strlen(chptr->chname); - strcat(buf + len, " "); - len++; - } - } - if (buf[0] != '\0') - sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS), /* XXX DEAD */ - me.name, parv[0], name, buf); - } - - sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name, /* XXX DEAD */ - parv[0], name, a2cptr->name, a2cptr->info); - - if (user) - { - if (user->away) - sendto_one(sptr, rpl_str(RPL_AWAY), me.name, /* XXX DEAD */ - parv[0], name, user->away); - - if (IsAnOper(acptr)) - sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR), /* XXX DEAD */ - me.name, parv[0], name); - - if (MyConnect(acptr)) - sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name, /* XXX DEAD */ - parv[0], name, CurrentTime - user->last, acptr->firsttime); - } - if (found == 2 || total++ >= MAX_WHOIS_LINES) - break; - } - } - else - { - /* No wildcards */ - if ((acptr = FindUser(nick))) - { - found = 2; /* Make sure we exit the loop after passing it once */ - user = acptr->user; - name = (!*acptr->name) ? "?" : acptr->name; - a2cptr = user->server; - sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */ - parv[0], name, user->username, user->host, acptr->info); - goto exact_match; - } + acptr = FindUser(nick); + if (acptr && !IsServer(acptr)) { + found++; + do_whois(sptr,acptr); } + if (!found) - sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick); /* XXX DEAD */ + send_reply(sptr, ERR_NOSUCHNICK, nick); + + total+=found; + + if (total >= MAX_WHOIS_LINES) { + send_reply(sptr, ERR_QUERYTOOLONG, parv[1]); + break; + } + if (p) p[-1] = ','; - if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES) - break; - } - sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]); /* XXX DEAD */ + } /* of tokenised parm[1] */ + send_reply(sptr, RPL_ENDOFWHOIS, parv[1]); return 0; -} -#endif /* 0 */ - +} \ No newline at end of file diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c index 15d53b8..5e39f22 100644 --- a/ircd/s_bsd.c +++ b/ircd/s_bsd.c @@ -1417,6 +1417,8 @@ int connect_server(struct ConfItem* aconf, struct Client* by, LocalClientArray[cptr->fd] = cptr; Count_newunknown(UserStats); + ip_registry_add_local(aconf->ipnum.s_addr); + add_client_to_list(cptr); hAddClient(cptr); nextping = CurrentTime; -- 2.20.1