/* * IRC - Internet Relay Chat, common/send.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef lint static char rcsid[] = "@(#)$Id: send.c,v 1.9 2003/12/09 17:25:09 gvs Exp $"; #endif #include "os.h" #ifndef CLIENT_COMPILE # include "s_defines.h" #else # include "c_defines.h" #endif #define SEND_C #ifndef CLIENT_COMPILE # include "s_externs.h" #else # include "c_externs.h" #endif #undef SEND_C static char sendbuf[2048]; static int send_message __P((aClient *, char *, int)); #if USE_STDARG static void vsendto_prefix_one(aClient *, aClient *, char *, va_list); #endif #ifndef CLIENT_COMPILE static char psendbuf[2048]; static int sentalong[MAXCONNECTIONS]; #endif /* ** dead_link ** An error has been detected. The link *must* be closed, ** but *cannot* call ExitClient (m_bye) from here. ** Instead, mark it with FLAGS_DEADSOCKET. This should ** generate ExitClient from the main loop. ** ** If 'notice' is not NULL, it is assumed to be a format ** for a message to local opers. It can contain only one ** '%s', which will be replaced by the sockhost field of ** the failing link. ** ** Also, the notice is skipped for "uninteresting" cases, ** like Persons and yet unknown connections... */ static int dead_link(to, notice) aClient *to; char *notice; { if (IsHeld(to)) return -1; to->flags |= FLAGS_DEADSOCKET; /* * If because of BUFFERPOOL problem then clean dbufs now so that * notices don't hurt operators below. */ DBufClear(&to->recvQ); DBufClear(&to->sendQ); #ifndef CLIENT_COMPILE if (!IsPerson(to) && !IsUnknown(to) && !(to->flags & FLAGS_CLOSING)) sendto_flag(SCH_ERROR, notice, get_client_name(to, FALSE)); Debug((DEBUG_ERROR, notice, get_client_name(to, FALSE))); #endif return -1; } #ifndef CLIENT_COMPILE /* ** flush_fdary ** Used to empty all output buffers for connections in fdary. */ void flush_fdary(fdp) FdAry *fdp; { int i; aClient *cptr; for (i = 0; i <= fdp->highest; i++) { if (!(cptr = local[fdp->fd[i]])) continue; if (!IsRegistered(cptr)) /* is this needed?? -kalt */ continue; if (DBufLength(&cptr->sendQ) > 0) (void)send_queued(cptr); } } /* ** flush_connections ** Used to empty all output buffers for all connections. Should only ** be called once per scan of connections. There should be a select in ** here perhaps but that means either forcing a timeout or doing a poll. ** When flushing, all we do is empty the obuffer array for each local ** client and try to send it. if we can't send it, it goes into the sendQ ** -avalon */ void flush_connections(fd) int fd; { Reg int i; Reg aClient *cptr; if (fd == me.fd) { for (i = highest_fd; i >= 0; i--) if ((cptr = local[i]) && DBufLength(&cptr->sendQ) > 0) (void)send_queued(cptr); } else if (fd >= 0 && (cptr = local[fd]) && DBufLength(&cptr->sendQ) > 0) (void)send_queued(cptr); } #endif /* ** send_message ** Internal utility which delivers one message buffer to the ** socket. Takes care of the error handling and buffering, if ** needed. ** if ZIP_LINKS is defined, the message will eventually be compressed, ** anything stored in the sendQ is compressed. */ static int send_message(to, msg, len) aClient *to; char *msg; /* if msg is a null pointer, we are flushing connection */ int len; #if !defined(CLIENT_COMPILE) { int i; Debug((DEBUG_SEND,"Sending %s %d [%s] ", to->name, to->fd, msg)); if (to->from) to = to->from; if (to->fd < 0) { Debug((DEBUG_ERROR, "Local socket %s with negative fd... AARGH!", to->name)); } if (IsMe(to)) { sendto_flag(SCH_ERROR, "Trying to send to myself! [%s]", msg); return 0; } if (IsDead(to)) return 0; /* This socket has already been marked as dead */ if (DBufLength(&to->sendQ) > get_sendq(to)) { # ifdef HUB if (CBurst(to)) { aConfItem *aconf = to->serv->nline; poolsize -= MaxSendq(aconf->class) >> 1; IncSendq(aconf->class); poolsize += MaxSendq(aconf->class) >> 1; sendto_flag(SCH_NOTICE, "New poolsize %d. (sendq adjusted)", poolsize); istat.is_dbufmore++; } else # endif { char ebuf[BUFSIZE]; ebuf[0] = '\0'; if (IsService(to) || IsServer(to)) { SPRINTF(ebuf, "Max SendQ limit exceeded for %s: %d > %d", get_client_name(to, FALSE), DBufLength(&to->sendQ), get_sendq(to)); } to->exitc = EXITC_SENDQ; return dead_link(to, ebuf[0] ? ebuf : "Max Sendq exceeded"); } } # ifdef ZIP_LINKS /* ** data is first stored in to->zip->outbuf until ** it's big enough to be compressed and stored in the sendq. ** send_queued is then responsible to never let the sendQ ** be empty and to->zip->outbuf not empty. */ if (to->flags & FLAGS_ZIP) msg = zip_buffer(to, msg, &len, 0); tryagain: if (len && (i = dbuf_put(&to->sendQ, msg, len)) < 0) # else /* ZIP_LINKS */ tryagain: if ((i = dbuf_put(&to->sendQ, msg, len)) < 0) # endif /* ZIP_LINKS */ { if (i == -2 && CBurst(to)) { /* poolsize was exceeded while connect burst */ aConfItem *aconf = to->serv->nline; poolsize -= MaxSendq(aconf->class) >> 1; IncSendq(aconf->class); poolsize += MaxSendq(aconf->class) >> 1; sendto_flag(SCH_NOTICE, "New poolsize %d. (reached)", poolsize); istat.is_dbufmore++; goto tryagain; } else { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s"); } } /* ** Update statistics. The following is slightly incorrect ** because it counts messages even if queued, but bytes ** only really sent. Queued bytes get updated in SendQueued. */ to->sendM += 1; me.sendM += 1; if (to->acpt != &me) to->acpt->sendM += 1; /* ** This little bit is to stop the sendQ from growing too large when ** there is no need for it to. Thus we call send_queued() every time ** 2k has been added to the queue since the last non-fatal write. ** Also stops us from deliberately building a large sendQ and then ** trying to flood that link with data (possible during the net ** relinking done by servers with a large load). */ if (DBufLength(&to->sendQ)/1024 > to->lastsq) send_queued(to); return 0; } #else /* CLIENT_COMPILE */ { int rlen = 0, i; Debug((DEBUG_SEND,"Sending %s %d [%s] ", to->name, to->fd, msg)); if (to->from) to = to->from; if (to->fd < 0) { Debug((DEBUG_ERROR, "Local socket %s with negative fd... AARGH!", to->name)); } if (IsDead(to)) return 0; /* This socket has already been marked as dead */ if ((rlen = deliver_it(to, msg, len)) < 0 && rlen < len) return dead_link(to,"Write error to %s, closing link"); /* ** Update statistics. The following is slightly incorrect ** because it counts messages even if queued, but bytes ** only really sent. Queued bytes get updated in SendQueued. */ to->sendM += 1; me.sendM += 1; if (to->acpt != &me) to->acpt->sendM += 1; return 0; } #endif /* ** send_queued ** This function is called from the main select-loop (or whatever) ** when there is a chance the some output would be possible. This ** attempts to empty the send queue as far as possible... */ int send_queued(to) aClient *to; { char *msg; int len, rlen, more = 0; /* ** Once socket is marked dead, we cannot start writing to it, ** even if the error is removed... */ if (IsDead(to)) { /* ** Actually, we should *NEVER* get here--something is ** not working correct if send_queued is called for a ** dead socket... --msa */ return -1; } #ifdef ZIP_LINKS /* ** Here, we must make sure than nothing will be left in to->zip->outbuf ** This buffer needs to be compressed and sent if all the sendQ is sent */ if ((to->flags & FLAGS_ZIP) && to->zip->outcount) { if (DBufLength(&to->sendQ) > 0) more = 1; else { msg = zip_buffer(to, NULL, &len, 1); if (len == -1) return dead_link(to, "fatal error in zip_buffer()"); if (dbuf_put(&to->sendQ, msg, len) < 0) { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s"); } } } #endif while (DBufLength(&to->sendQ) > 0 || more) { msg = dbuf_map(&to->sendQ, &len); /* Returns always len > 0 */ if ((rlen = deliver_it(to, msg, len)) < 0) return dead_link(to,"Write error to %s, closing link"); (void)dbuf_delete(&to->sendQ, rlen); to->lastsq = DBufLength(&to->sendQ)/1024; if (rlen < len) /* ..or should I continue until rlen==0? */ break; #ifdef ZIP_LINKS if (DBufLength(&to->sendQ) == 0 && more) { /* ** The sendQ is now empty, compress what's left ** uncompressed and try to send it too */ more = 0; msg = zip_buffer(to, NULL, &len, 1); if (len == -1) return dead_link(to, "fatal error in zip_buffer()"); if (dbuf_put(&to->sendQ, msg, len) < 0) { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s"); } } #endif } return (IsDead(to)) ? -1 : 0; } #ifndef CLIENT_COMPILE static anUser ausr = { NULL, NULL, NULL, NULL, 0, 0, 0, 0, NULL, NULL, "anonymous", "anonymous.", "anonymous."}; static aClient anon = { NULL, NULL, NULL, &ausr, NULL, NULL, 0, 0,/*flags*/ &anon, -2, 0, STAT_CLIENT, "anonymous", "anonymous", "anonymous identity hider", "anonymous.in.IRC", 0, "", # ifdef ZIP_LINKS NULL, # endif 0, {0, 0, NULL }, {0, 0, NULL }, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, 0, NULL, 0 # if defined(__STDC__) /* hack around union{} initialization -Vesa */ ,{0}, NULL, "" # endif }; #endif /* * */ #if ! USE_STDARG static int sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else static int vsendprep(char *pattern, va_list va) #endif { int len; Debug((DEBUG_L10, "sendprep(%s)", pattern)); #if ! USE_STDARG len = irc_sprintf(sendbuf, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); if (len == -1) len = strlen(sendbuf); #else len = vsprintf(sendbuf, pattern, va); #endif if (len > 510) #ifdef IRCII_KLUDGE len = 511; #else len = 510; sendbuf[len++] = '\r'; #endif sendbuf[len++] = '\n'; sendbuf[len] = '\0'; return len; } #ifndef CLIENT_COMPILE #if ! USE_STDARG static int sendpreprep(to, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aClient *to, *from; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; { static char sender[HOSTLEN+NICKLEN+USERLEN+5]; Reg anUser *user; char *par; int flag = 0, len; Debug((DEBUG_L10, "sendpreprep(%#x(%s),%#x(%s),%s)", to, to->name, from, from->name, pattern)); par = p1; if (to && from && MyClient(to) && IsPerson(from) && !strcasecmp(par, from->name)) { user = from->user; (void)strcpy(sender, from->name); if (user) { if (*user->username) { (void)strcat(sender, "!"); (void)strcat(sender, user->username); } if (*user->host && !MyConnect(from)) { (void)strcat(sender, "@"); (void)strcat(sender, #ifdef RUSNET_IRCD (user->flags & FLAGS_VHOST) ? #endif user->host #ifdef RUSNET_IRCD : from->sockhost #endif ); flag = 1; } } /* ** flag is used instead of index(sender, '@') for speed and ** also since username/nick may have had a '@' in them. -avalon */ if (!flag && MyConnect(from) && *user->host) { (void)strcat(sender, "@"); #ifdef RUSNET_IRCD if (user->flags & FLAGS_VHOST || IsUnixSocket(from)) #else if (IsUnixSocket(from)) #endif (void)strcat(sender, user->host); else (void)strcat(sender, from->sockhost); } par = sender; } len = irc_sprintf(psendbuf, pattern, par, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); if (len == -1) len = strlen(psendbuf); if (len > 510) #ifdef IRCII_KLUDGE len = 511; #else len = 510; psendbuf[len++] = '\r'; #endif psendbuf[len++] = '\n'; psendbuf[len] = '\0'; return len; } #else /* USE_STDARG */ static int vsendpreprep(aClient *to, aClient *from, char *pattern, va_list va) { Reg anUser *user; int flag = 0, len; Debug((DEBUG_L10, "sendpreprep(%#x(%s),%#x(%s),%s)", to, to->name, from, from->name, pattern)); if (to && from && MyClient(to) && IsPerson(from) && !strncmp(pattern, ":%s", 3)) { char *par = va_arg(va, char *); if (from == &anon || !strcasecmp(par, from->name)) { user = from->user; (void)strcpy(psendbuf, ":"); (void)strcat(psendbuf, from->name); if (user) { if (*user->username) { (void)strcat(psendbuf, "!"); (void)strcat(psendbuf, user->username); } if (*user->host && !MyConnect(from)) { (void)strcat(psendbuf, "@"); (void)strcat(psendbuf, #ifdef RUSNET_IRCD (user->flags & FLAGS_VHOST) ? #endif user->host #ifdef RUSNET_IRCD : from->sockhost #endif ); flag = 1; } } /* ** flag is used instead of index(newpat, '@') for speed and ** also since username/nick may have had a '@' in them. -avalon */ if (!flag && MyConnect(from) && *user->host) { (void)strcat(psendbuf, "@"); #ifdef RUSNET_IRCD if (user->flags & FLAGS_VHOST || IsUnixSocket(from)) #else if (IsUnixSocket(from)) #endif (void)strcat(psendbuf, user->host); else (void)strcat(psendbuf, from->sockhost); } } else { (void)strcpy(psendbuf, ":"); (void)strcat(psendbuf, par); } len = strlen(psendbuf); len += vsprintf(psendbuf+len, pattern+3, va); } else len = vsprintf(psendbuf, pattern, va); if (len > 510) #ifdef IRCII_KLUDGE len = 511; #else len = 510; psendbuf[len++] = '\r'; #endif psendbuf[len++] = '\n'; psendbuf[len] = '\0'; return len; } #endif /* USE_STDARG */ #endif /* CLIENT_COMPILE */ /* ** send message to single client */ #if ! USE_STDARG int sendto_one(to, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aClient *to; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else int vsendto_one(aClient *to, char *pattern, va_list va) #endif { int len; #if ! USE_STDARG len = sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else len = vsendprep(pattern, va); #endif (void)send_message(to, sendbuf, len); return len; } #if USE_STDARG int sendto_one(aClient *to, char *pattern, ...) { int len; va_list va; va_start(va, pattern); len = vsendto_one(to, pattern, va); va_end(va); return len; } #endif /* * sendto_channel_butone * * Send a message to all members of a channel that are connected to this * server except client 'one'. */ #ifndef CLIENT_COMPILE #if ! USE_STDARG /*VARARGS*/ void sendto_channel_butone(one, from, chptr, ops, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aClient *one, *from; aChannel *chptr; int ops; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_channel_butone(aClient *one, aClient *from, aChannel *chptr, int ops, char *pattern, ...) #endif { Reg Link *lp; Reg aClient *acptr, *lfrm = from; int len1, len2 = 0; if (IsAnonymous(chptr) && IsClient(from)) { #if ! USE_STDARG if (p1 && *p1 && !strcasecmp(p1, from->name)) p1 = anon.name; #endif lfrm = &anon; } if (one != from && MyConnect(from) && IsRegisteredUser(from)) { /* useless junk? */ /* who said that and why? --B. */ #if ! USE_STDARG sendto_prefix_one(from, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); vsendto_prefix_one(from, from, pattern, va); va_end(va); #endif } #if ! USE_STDARG len1 = sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else { va_list va; va_start(va, pattern); len1 = vsendprep(pattern, va); va_end(va); } #endif for (lp = chptr->clist; lp; lp = lp->next) { acptr = lp->value.cptr; if (acptr->from == one || IsMe(acptr)) continue; /* ...was the one I should skip */ /* for ops only: get user flags and check it */ if (ops && !IsServer(acptr)) { Reg Link *tmp = find_user_link(chptr->members, acptr); if (!tmp) /* this should NOT happen but... */ continue; if (!(tmp->flags & CHFL_CHANOP)) continue; } if (MyConnect(acptr) && IsRegisteredUser(acptr)) { if (!len2) { #if ! USE_STDARG len2 = sendpreprep(acptr, lfrm, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len2 = vsendpreprep(acptr, lfrm, pattern, va); va_end(va); #endif } if (acptr != from) (void)send_message(acptr, psendbuf, len2); } else (void)send_message(acptr, sendbuf, len1); } return; } /* * sendto_server_butone * * Send a message to all connected servers except the client 'one'. */ #if ! USE_STDARG /*VARARGS*/ void sendto_serv_butone(one, pattern, p1, p2, p3, p4,p5,p6,p7,p8,p9,p10,p11) aClient *one; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_serv_butone(aClient *one, char *pattern, ...) #endif { Reg int i, len=0; Reg aClient *cptr; for (i = fdas.highest; i >= 0; i--) if ((cptr = local[fdas.fd[i]]) && (!one || cptr != one->from) && !IsMe(cptr)) { if (!len) { #if ! USE_STDARG len = sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendprep(pattern, va); va_end(va); #endif } (void)send_message(cptr, sendbuf, len); } return; } #if ! USE_STDARG int sendto_serv_v(one, ver, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aClient *one; int ver; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else int sendto_serv_v(aClient *one, int ver, char *pattern, ...) #endif { Reg int i, len=0, rc=0; Reg aClient *cptr; for (i = fdas.highest; i >= 0; i--) if ((cptr = local[fdas.fd[i]]) && (!one || cptr != one->from) && !IsMe(cptr)) if (cptr->serv->version & ver) { if (!len) { #if ! USE_STDARG len = sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendprep(pattern, va); va_end(va); #endif } (void)send_message(cptr, sendbuf, len); } else rc = 1; return rc; } #if ! USE_STDARG int sendto_serv_notv(one, ver, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9,p10,p11) aClient *one; int ver; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else int sendto_serv_notv(aClient *one, int ver, char *pattern, ...) #endif { Reg int i, len=0, rc=0; Reg aClient *cptr; for (i = fdas.highest; i >= 0; i--) if ((cptr = local[fdas.fd[i]]) && (!one || cptr != one->from) && !IsMe(cptr)) if ((cptr->serv->version & ver) == 0) { if (!len) { #if ! USE_STDARG len = sendprep(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendprep(pattern, va); va_end(va); #endif } (void)send_message(cptr, sendbuf, len); } else rc = 1; return rc; } /* * sendto_common_channels() * * Sends a message to all people (inclusing user) on local server who are * in same channel with user, except for channels set Quiet or Anonymous * The calling procedure must take the necessary steps for such channels. */ #if ! USE_STDARG /*VARARGS*/ void sendto_common_channels(user,pattern,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11) aClient *user; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_common_channels(aClient *user, char *pattern, ...) #endif { Reg int i; Reg aClient *cptr; Reg Link *channels, *lp; int len = 0; /* This is kind of funky, but should work. The first part below is optimized for HUB servers or servers with few clients on them. The second part is optimized for bigger client servers where looping through the whole client list is bad. I'm not really certain of the point at which each function equals out...but I do know the 2nd part will help big client servers fairly well... - Comstud 97/04/24 */ if (highest_fd < 50) /* This part optimized for HUB servers... */ { if (MyConnect(user)) { #if ! USE_STDARG len = sendpreprep(user, user, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11); #else va_list va; va_start(va, pattern); len = vsendpreprep(user, user, pattern, va); va_end(va); #endif (void)send_message(user, psendbuf, len); } for (i = 0; i <= highest_fd; i++) { if (!(cptr = local[i]) || IsServer(cptr) || user == cptr || !user->user) continue; for (lp = user->user->channel; lp; lp = lp->next) { if (!IsMember(cptr, lp->value.chptr)) continue; if (IsAnonymous(lp->value.chptr)) continue; if (!IsQuiet(lp->value.chptr)) { #ifndef DEBUGMODE if (!len) /* This saves little cpu, but breaks the debug code.. */ #endif { #if ! USE_STDARG len = sendpreprep(cptr, user, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendpreprep(cptr, user, pattern, va); va_end(va); #endif } (void)send_message(cptr, psendbuf, len); break; } } } } else { /* This part optimized for client servers */ bzero((char *)&sentalong[0], sizeof(int) * MAXCONNECTIONS); if (MyConnect(user)) { #if ! USE_STDARG len = sendpreprep(user, user, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendpreprep(user, user, pattern, va); va_end(va); #endif (void)send_message(user, psendbuf, len); sentalong[user->fd] = 1; } if (!user->user) return; for (channels=user->user->channel; channels; channels=channels->next) { if (IsQuiet(channels->value.chptr)) continue; if (IsAnonymous(channels->value.chptr)) continue; for (lp=channels->value.chptr->clist;lp; lp=lp->next) { cptr = lp->value.cptr; if (user == cptr) continue; if (!cptr->user || sentalong[cptr->fd]) continue; sentalong[cptr->fd]++; #ifndef DEBUGMODE if (!len) /* This saves little cpu, but breaks the debug code.. */ #endif { #if ! USE_STDARG len = sendpreprep(cptr, user, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendpreprep(cptr, user, pattern, va); va_end(va); #endif } (void)send_message(cptr, psendbuf, len); } } } return; } /* * sendto_channel_butserv * * Send a message to all members of a channel that are connected to this * server. */ #if ! USE_STDARG /*VARARGS*/ void sendto_channel_butserv(chptr, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aChannel *chptr; aClient *from; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_channel_butserv(aChannel *chptr, aClient *from, char *pattern, ...) #endif { Reg Link *lp; Reg aClient *acptr, *lfrm = from; int len = 0; if (MyClient(from)) { /* Always send to the client itself */ #if ! USE_STDARG sendto_prefix_one(from, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); vsendto_prefix_one(from, from, pattern, va); va_end(va); #endif if (IsQuiet(chptr)) /* Really shut up.. */ return; } if (IsAnonymous(chptr) && IsClient(from)) { #if ! USE_STDARG if (p1 && *p1 && !strcasecmp(p1, from->name)) p1 = anon.name; #endif lfrm = &anon; } for (lp = chptr->clist; lp; lp = lp->next) if (MyClient(acptr = lp->value.cptr) && acptr != from) { if (!len) { #if ! USE_STDARG len = sendpreprep(acptr, lfrm, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendpreprep(acptr, lfrm, pattern, va); va_end(va); #endif } (void)send_message(acptr, psendbuf, len); } return; } /* ** send a msg to all ppl on servers/hosts that match a specified mask ** (used for enhanced PRIVMSGs) ** ** addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de) */ static int match_it(one, mask, what) aClient *one; char *mask; int what; { switch (what) { case MATCH_HOST: #ifdef RUSNET_IRCD return (match(mask, one->sockhost) == 0); #else return (match(mask, one->user->host) == 0); #endif case MATCH_SERVER: default: return (match(mask, one->user->server)==0); } } /* * sendto_match_servs * * send to all servers which match the mask at the end of a channel name * (if there is a mask present) or to all if no mask. */ #if ! USE_STDARG /*VARARGS*/ void sendto_match_servs(chptr, from, format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aChannel *chptr; aClient *from; char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_match_servs(aChannel *chptr, aClient *from, char *format, ...) #endif { Reg int i, len=0; Reg aClient *cptr; char *mask; if (chptr) { if (*chptr->chname == '&') return; if ((mask = (char *)rindex(chptr->chname, ':'))) mask++; } else mask = (char *)NULL; for (i = fdas.highest; i >= 0; i--) { if (!(cptr = local[fdas.fd[i]]) || (cptr == from) || IsMe(cptr)) continue; if (!BadPtr(mask) && match(mask, cptr->name)) continue; if (chptr && *chptr->chname == '!' && !(cptr->serv->version & SV_NJOIN)) continue; if (!len) { #if ! USE_STDARG len = sendprep(format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, format); len = vsendprep(format, va); va_end(va); #endif } (void)send_message(cptr, sendbuf, len); } } #if ! USE_STDARG /*VARARGS*/ int sendto_match_servs_v(chptr, from, ver, format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aChannel *chptr; aClient *from; char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; int ver; #else int sendto_match_servs_v(aChannel *chptr, aClient *from, int ver, char *format, ...) #endif { Reg int i, len=0, rc=0; Reg aClient *cptr; char *mask; if (chptr) { if (*chptr->chname == '&') return 0; if ((mask = (char *)rindex(chptr->chname, ':'))) mask++; } else mask = (char *)NULL; for (i = fdas.highest; i >= 0; i--) { if (!(cptr = local[fdas.fd[i]]) || (cptr == from) || IsMe(cptr)) continue; if (!BadPtr(mask) && match(mask, cptr->name)) continue; if (chptr && *chptr->chname == '!' && !(cptr->serv->version & SV_NJOIN)) continue; if ((ver & cptr->serv->version) == 0) { rc = 1; continue; } if (!len) { #if ! USE_STDARG len = sendprep(format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, format); len = vsendprep(format, va); va_end(va); #endif } (void)send_message(cptr, sendbuf, len); } return rc; } #if ! USE_STDARG /*VARARGS*/ int sendto_match_servs_notv(chptr, from, ver, format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aChannel *chptr; aClient *from; char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; int ver; #else int sendto_match_servs_notv(aChannel *chptr, aClient *from, int ver, char *format, ...) #endif { Reg int i, len=0, rc=0; Reg aClient *cptr; char *mask; if (chptr) { if (*chptr->chname == '&') return 0; if ((mask = (char *)rindex(chptr->chname, ':'))) mask++; } else mask = (char *)NULL; for (i = fdas.highest; i >= 0; i--) { if (!(cptr = local[fdas.fd[i]]) || (cptr == from) || IsMe(cptr)) continue; if (!BadPtr(mask) && match(mask, cptr->name)) continue; if (chptr && *chptr->chname == '!' && !(cptr->serv->version & SV_NJOIN)) continue; if ((ver & cptr->serv->version) != 0) { rc = 1; continue; } if (!len) { #if ! USE_STDARG len = sendprep(format, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, format); len = vsendprep(format, va); va_end(va); #endif } (void)send_message(cptr, sendbuf, len); } return rc; } /* * sendto_match_butone * * Send to all clients which match the mask in a way defined on 'what'; * either by user hostname or user servername. */ /*VARARGS*/ #if ! USE_STDARG void sendto_match_butone(one, from, mask, what, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aClient *one, *from; int what; char *mask, *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9,*p10,*p11; #else void sendto_match_butone(aClient *one, aClient *from, char *mask, int what, char *pattern, ...) #endif { int i; aClient *cptr, *srch; for (i = 0; i <= highest_fd; i++) { if (!(cptr = local[i])) continue; /* that clients are not mine */ if (cptr == one) /* must skip the origin !! */ continue; if (IsServer(cptr)) { /* ** we can save some CPU here by not searching the ** entire list of users since it is ordered! ** original idea/code from pht. ** it could be made better by looping on the list of ** servers to avoid non matching blocks in the list ** (srch->from != cptr), but then again I never ** bothered to worry or optimize this routine -kalt */ for (srch = cptr->prev; srch; srch = srch->prev) { if (!IsRegisteredUser(srch)) continue; if (srch->from == cptr && match_it(srch, mask, what)) break; } if (srch == NULL) continue; } /* my client, does he match ? */ else if (!(IsRegisteredUser(cptr) && match_it(cptr, mask, what))) continue; #if ! USE_STDARG sendto_prefix_one(cptr, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11); #else { va_list va; va_start(va, pattern); vsendto_prefix_one(cptr, from, pattern, va); va_end(va); } #endif } return; } /* ** sendto_ops_butone ** Send message to all operators. ** one - client not to send message to ** from- client which message is from *NEVER* NULL!! */ #if ! USE_STDARG /*VARARGS*/ void sendto_ops_butone(one, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) aClient *one, *from; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...) #endif { Reg int i; Reg aClient *cptr; bzero((char *)&sentalong[0], sizeof(int) * MAXCONNECTIONS); for (cptr = client; cptr; cptr = cptr->next) { if (IsService(cptr) || !IsRegistered(cptr)) continue; if ((IsPerson(cptr) && !SendWallops(cptr)) || IsMe(cptr)) continue; #ifdef NOWALLOPS_TO_USERS if (IsPerson(cptr) && !IsAnOper(cptr)) continue; #endif if (MyClient(cptr) && !(IsServer(from) || IsMe(from))) continue; i = cptr->from->fd; /* find connection oper is on */ if (sentalong[i]) /* sent message along it already ? */ continue; if (cptr->from == one) continue; /* ...was the one I should skip */ sentalong[i] = 1; #if ! USE_STDARG sendto_prefix_one(cptr->from, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11); #else { va_list va; va_start(va, pattern); vsendto_prefix_one(cptr->from, from, pattern, va); va_end(va); } #endif } return; } /* * to - destination client * from - client which message is from * * NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!! * -avalon */ #if ! USE_STDARG /*VARARGS*/ void sendto_prefix_one(to, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) Reg aClient *to; Reg aClient *from; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_prefix_one(aClient *to, aClient *from, char *pattern, ...) #endif { int len; #if ! USE_STDARG len = sendpreprep(to, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else va_list va; va_start(va, pattern); len = vsendpreprep(to, from, pattern, va); va_end(va); #endif send_message(to, psendbuf, len); return; } #if USE_STDARG static void vsendto_prefix_one(aClient *to, aClient *from, char *pattern, va_list va) { int len; len = vsendpreprep(to, from, pattern, va); send_message(to, psendbuf, len); return; } #endif /* * sends a message to a server-owned channel */ static SChan svchans[SCH_MAX] = { { SCH_ERROR, "&ERRORS", NULL }, { SCH_NOTICE, "&NOTICES", NULL }, { SCH_KILL, "&KILLS", NULL }, { SCH_CHAN, "&CHANNEL", NULL }, { SCH_NUM, "&NUMERICS", NULL }, { SCH_SERVER, "&SERVERS", NULL }, { SCH_HASH, "&HASH", NULL }, { SCH_LOCAL, "&LOCAL", NULL }, { SCH_SERVICE, "&SERVICES", NULL }, { SCH_DEBUG, "&DEBUG", NULL }, { SCH_AUTH, "&AUTH", NULL }, #ifdef EXTRA_NOTICES { SCH_OPER, "&OPER", NULL }, #endif }; void setup_svchans() { int i; SChan *shptr; for (i = SCH_MAX, shptr = svchans + (i - 1); i > 0; i--, shptr--) shptr->svc_ptr = find_channel(shptr->svc_chname, NULL); } #ifdef LOG_EVENTS static int killlog; static int userlog; static int connlog; static int locallog; static int authlog; static int noticelog; static int errorlog; /* last resort */ #endif /* * sendto_log * type message type/syslog severity * source message source * channel message channel (e.g. &ERRORS) * msg message itself */ void sendto_log(type, source, channel, msg) int type; const char *source, *channel, *msg; { #ifdef LOG_EVENTS int l #ifdef USE_SYSLOG , s = LOG_INFO #endif ; switch (type) { #ifdef SVLOG_KILL case SVLOG_KILL: l = killlog; #ifdef SYSLOG_KILL s = SYSLOG_KILL; #endif break; #endif #ifdef SVLOG_NOTICES case SVLOG_NOTICES: l = noticelog; #ifdef SYSLOG_NOTICES s = SYSLOG_NOTICES; #endif break; #endif #ifdef SVLOG_LOCAL case SVLOG_LOCAL: l = locallog; #ifdef SYSLOG_LOCAL s = SYSLOG_LOCAL; #endif break; #endif #ifdef SVLOG_AUTH case SVLOG_AUTH: l = userlog; #ifdef SYSLOG_AUTH s = SYSLOG_AUTH; #endif break; #endif #ifdef SVLOG_ERRS case SVLOG_ERRS: #endif default: l = errorlog; #ifdef SYSLOG_ERRS s = SYSLOG_ERRS; #endif } #ifdef USE_SYSLOG syslog(s, "%s: %s: %s", source, channel, msg); #endif if (l != -1) { char *buf = MyMalloc(strlen(source) + strlen(channel) + strlen(msg) + 26); time_t t = time(NULL); int i = strftime(buf, 20, "%b %e %T ", localtime( &t ) ); i += sprintf(buf + i, "%s: %s: %s\n", source, channel, msg); (void)write(l, buf, i); MyFree(buf); } #endif /* LOG_EVENTS */ } #if ! USE_STDARG void sendto_flag(chan, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,p11) u_int chan; char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11; #else void sendto_flag(u_int chan, char *pattern, ...) #endif { Reg aChannel *chptr = NULL; SChan *shptr; char nbuf[1024]; if (chan < 1 || chan > SCH_MAX) chan = SCH_NOTICE; shptr = svchans + (chan - 1); if ((chptr = shptr->svc_ptr)) { #if ! USE_STDARG (void)sprintf(nbuf, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); #else { va_list va; va_start(va, pattern); (void)vsprintf(nbuf, pattern, va); va_end(va); } #endif sendto_channel_butserv(chptr, &me, ":%s NOTICE %s :%s", ME, chptr->chname, nbuf); #ifdef LOG_EVENTS switch (chan) { #ifdef SVLOG_ERRS case SCH_ERROR: sendto_log(SVLOG_ERRS, ME, chptr->chname, nbuf); break; #endif #ifdef SVLOG_NOTICES case SCH_NOTICE: sendto_log(SVLOG_NOTICES, ME, chptr->chname, nbuf); break; #endif #ifdef SVLOG_KILL case SCH_KILL: sendto_log(SVLOG_KILL, ME, chptr->chname, nbuf); break; #endif #ifdef SVLOG_LOCAL case SCH_LOCAL: sendto_log(SVLOG_LOCAL, ME, chptr->chname, nbuf); break; #endif #ifdef SVLOG_AUTH case SCH_AUTH: sendto_log(SVLOG_AUTH, ME, chptr->chname, nbuf); break; #endif } #endif /* LOG_EVENTS */ #ifdef USE_SERVICES switch (chan) { case SCH_ERROR: check_services_butone(SERVICE_WANT_ERRORS, NULL, &me, "&ERRORS :%s", nbuf); break; case SCH_NOTICE: check_services_butone(SERVICE_WANT_NOTICES, NULL, &me, "&NOTICES :%s", nbuf); break; case SCH_LOCAL: check_services_butone(SERVICE_WANT_LOCAL, NULL, &me, "&LOCAL :%s", nbuf); break; case SCH_NUM: check_services_butone(SERVICE_WANT_NUMERICS, NULL, &me, "&NUMERICS :%s", nbuf); break; } #endif } return; } void logfiles_open() { #ifdef LOG_EVENTS #ifdef FNAME_KILLLOG killlog = open(LOG_DIR "/" FNAME_KILLLOG, O_WRONLY|O_APPEND|O_NDELAY); #else killlog = -1; #endif #ifdef FNAME_USERLOG userlog = open(LOG_DIR "/" FNAME_USERLOG, O_WRONLY|O_APPEND|O_NDELAY); #else userlog = -1; #endif #ifdef FNAME_CONNLOG connlog = open(LOG_DIR "/" FNAME_CONNLOG, O_WRONLY|O_APPEND|O_NDELAY); #else connlog = -1; #endif #ifdef FNAME_LOCALLOG locallog = open(LOG_DIR "/" FNAME_LOCALLOG, O_WRONLY|O_APPEND|O_NDELAY); #else locallog = -1; #endif #ifdef FNAME_ERRORLOG errorlog = open(LOG_DIR "/" FNAME_ERRORLOG, O_WRONLY|O_APPEND|O_NDELAY); #else errorlog = -1; #endif #ifdef FNAME_NOTICELOG noticelog = open(LOG_DIR "/" FNAME_NOTICELOG, O_WRONLY|O_APPEND|O_NDELAY); #else noticelog = -1; #endif #endif } void logfiles_close() { #ifdef LOG_EVENTS #ifdef FNAME_KILLLOG if (killlog != -1) (void)close(killlog); #endif #ifdef FNAME_USERLOG if (userlog != -1) (void)close(userlog); #endif #ifdef FNAME_CONNLOG if (connlog != -1) (void)close(connlog); #endif #ifdef FNAME_LOCALLOG if (locallog != -1) (void)close(locallog); #endif #ifdef FNAME_ERRORLOG if (errorlog != -1) (void)close(errorlog); #endif #ifdef FNAME_NOTICELOG if (noticelog != -1) (void)close(noticelog); #endif #endif } /* * sendto_flog * cptr used for firsttime, auth, exitc, send/received M/K * msg if this contains a message, it means rejected client, * hence we will log it in connlog; otherwise it's client * quitting, so it ends up in userlog --Beeth * username can't get it from cptr * hostname i.e. */ void sendto_flog(cptr, msg, username, hostname) aClient *cptr; char *msg, *username, *hostname; { #ifdef LOG_EVENTS /* ** One day we will rewrite linebuf to malloc()s, but for now ** we are lazy. The longest linebuf I saw during last year ** was 216. Max auth reply can be 1024, see rfc931_work() and ** if iauth is disabled, read_authports() makes it max 513. ** And the rest... just count, I got 154 --Beeth */ char linebuf[1500]; /* ** This is a potential buffer overflow. ** I mean, when you manage to keep ircd ** running for almost 12 years ;-) --B. */ char buf[12], *s; int logfile; logfile = msg ? connlog : userlog; #if !defined(USE_SERVICES) && !( defined(USE_SYSLOG) && \ (defined(SVLOG_USERS) || defined(SVLOG_CONN)) ) if (logfile == -1) return; #endif if (!msg) { time_t duration; duration = timeofday - cptr->firsttime + 1; (void)sprintf(buf, "%3d:%02d:%02d", (int) (duration / 3600), (int) ((duration % 3600) / 60), (int) (duration % 60)); } /* ** Aha, cptr->firsttime is time when user connected, ** so we syslog() it anyway. I'm waving big ** "rewrite log format" flag --Beeth. */ (void)sprintf(linebuf, "%s (%s): %s@%s [%s] %c %lu %luKb %lu %luKb\n", myctime(cptr->firsttime), msg ? msg : buf, username, hostname, cptr->auth, cptr->exitc, cptr->sendM, cptr->sendK, cptr->receiveM, cptr->receiveK); #if defined(USE_SYSLOG) && (defined(SVLOG_USERS) || defined(SVLOG_CONN)) for (s = linebuf; *s != '('; s++); syslog(LOG_NOTICE, "%s", s); #endif #ifdef USE_SERVICES if (!msg) check_services_butone(SERVICE_WANT_USERLOG, NULL, &me, "USERLOG :%s", linebuf); else check_services_butone(SERVICE_WANT_CONNLOG, NULL, &me, "CONNLOG :%s", linebuf); #endif if (logfile != -1) (void)write(logfile, linebuf, strlen(linebuf)); #endif /* LOG_EVENTS */ } #endif /* CLIENT_COMPILE */