void check_split(void) { if ((bootopt & BOOT_STANDALONE)) return; if (istat.is_eobservers < iconf.split_minservers || istat.is_user[0] + istat.is_user[1] < iconf.split_minusers) { /* Split detected */ if (!IsSplit()) { sendto_flag(SCH_NOTICE, "Network split detected, split mode activated"); iconf.split = timeofday; } } else { /* End of split */ if (IsSplit()) { sendto_flag(SCH_NOTICE, "Network rejoined, split mode deactivated"); iconf.split = 0; if (!firstrejoindone) { firstrejoindone = 1; activate_delayed_listeners(); #ifdef CACCEPT_DEFAULT iconf.caccept = CACCEPT_DEFAULT; #endif } } } }
void free_server(aServer *serv) { aClient *cptr = serv->bcptr; /* decrement reference counter, and eventually free it */ if (--serv->refcnt <= 0) { if (serv->refcnt == -211001) { /* Loop detected, break it. * XXX: Remove loop detection before 2.11.0 - jv */ sendto_flag(SCH_DEBUG, "* %#x free_server loop %s *", serv, serv->namebuf); return; } /* Decrease (and possibly free) refcnt of the user struct * of who connected this server. */ if (serv->user) { int cnt = serv->refcnt; serv->refcnt = -211000; /* Loop detection */ free_user(serv->user); serv->user = NULL; serv->refcnt = cnt; } if (serv->refcnt < 0 || serv->prevs || serv->nexts || serv->bcptr || serv->user) { char buf[512]; sprintf(buf, "%d %p %p %p %p (%s)", serv->refcnt, (void *)serv->prevs, (void *)serv->nexts, (void *)serv->user, (void *)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(serv); } }
void free_client(aClient *cptr) { if (cptr->info != DefInfo) MyFree(cptr->info); /* True only for local clients */ if (cptr->hopcount == 0 || (IsServer(cptr) && cptr->hopcount == 1)) { if (cptr->auth != cptr->username) { sendto_flag(SCH_ERROR, "Please report to ircd-bug@" "irc.org about cptr->auth allocated but not" " free()d!"); istat.is_authmem -= strlen(cptr->auth) + 1; istat.is_auth -= 1; MyFree(cptr->auth); } if (cptr->reason) { MyFree(cptr->reason); } #ifdef XLINE if (cptr->user2) MyFree(cptr->user2); if (cptr->user3) MyFree(cptr->user3); #endif } MyFree(cptr); }
/* ** exit_server(): Removes all dependent servers and clients, and ** sends the right messages to the right client/servers. ** ** We will send all SQUITs to &servers, and QUITs to local users. ** We only send 1 SQUIT to a 2.11 servers. ** We send all SQUITs to a 2.10 servers that can see it, or QUITs otherwise. ** ** Argument: ** cptr: The real server to SQUIT. ** acptr: One of the depended servers to SQUIT. ** from: Originator of SQUIT. ** comment: The original comment for the SQUIT. (Only for cptr itself.) ** comment2: The comment for (S)QUIT reasons for the rest. */ static void exit_server(aClient *cptr, aClient *acptr, aClient *from, const char *comment, const char *comment2) { aClient *acptr2; int flags; /* Remove all the servers recursively. */ while (acptr->serv->down) { exit_server(cptr, acptr->serv->down, from, comment, comment2); } /* Here we should send "Received SQUIT" for last server, ** but exit_client() is doing (well, almost) this --Beeth */ /* This server doesn't have any depedent servers anymore, only ** users/services left. */ flags = FLAGS_SPLIT; /* ** We'll mark all servers that can't see that server as hidden. ** If we found any, we'll also mark all users on that server hidden. ** If a user is marked hidden, and we try to send it to a currently ** marked server, the server can't see that user's server. ** Note that a 2.11 can see it, so we don't have to send the QUITs ** to it. */ if (mark_blind_servers(cptr, acptr)) { flags |= FLAGS_HIDDEN; } /* Quit all users and services. */ while (GotDependantClient(acptr)) { acptr2 = acptr->prev; acptr2->flags |= flags; exit_one_client(cptr->from, acptr2, from, comment2); } /* Make sure we only send the last SQUIT to a 2.11 server. */ if (acptr == cptr) { acptr->flags |= FLAGS_SQUIT; } if (!IsMasked(acptr)) { sendto_flag(SCH_SERVER, "Received SQUIT %s from %s (%s)", acptr->name, from->name, acptr == cptr ? comment : comment2); } exit_one_client(cptr->from, acptr, from, acptr == cptr ? comment : comment2); return; }
void restart(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(); }
void outofmemory(void) { if (serverbooting) { fprintf(stderr,"Fatal Error: Out of memory.\n"); exit(-1); } Debug((DEBUG_FATAL, "Out of memory: restarting server...")); sendto_flag(SCH_NOTICE, "Ouch!!! Out of memory..."); restart("Out of Memory"); }
void server_reboot(void) { 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 logfiles_close(); 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)) { (void)execv(IRCD_PATH, myargv); #ifdef USE_SYSLOG /* Have to reopen since it has been closed above */ openlog(mybasename(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); }
/* ** 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_DEADSOCK. This should ** generate ExitClient from the main loop. ** ** The notice is skipped for "uninteresting" cases, ** like Persons and yet unknown connections... */ static int dead_link(aClient *to, char *pattern, ...) { char notice[BUFSIZE]; va_list va; va_start(va, pattern); vsprintf(notice, pattern, va); va_end(va); SetDead(to); /* * If because of BUFFERPOOL problem then clean dbufs now so that * notices don't hurt operators below. */ DBufClear(&to->recvQ); DBufClear(&to->sendQ); if (!IsPerson(to) && !IsUnknown(to) && !(to->flags & FLAGS_CLOSING)) sendto_flag(SCH_ERROR, notice); Debug((DEBUG_ERROR, notice)); return -1; }
/* ** 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(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; }
/* * move the client aClient struct before its server's */ void reorder_client_in_list(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; }
/* * sendto_iauth * * Send the buffer to the authentication slave process. * Return 0 if everything went well, -1 otherwise. */ int vsendto_iauth(char *pattern, va_list va) { static char abuf[BUFSIZ], *p; int i, len; if (adfd < 0) { return -1; } vsprintf(abuf, pattern, va); strcat(abuf, "\n"); p = abuf; len = strlen(p); do { i = write(adfd, abuf, len); if ( i == -1 ) { if (errno != EAGAIN && errno != EWOULDBLOCK) { sendto_flag(SCH_AUTH, "Aiiie! lost slave " "authentication process"); close(adfd); adfd = -1; start_iauth(0); return -1; } i = 0; } p += i; len -= i; } while (len > 0); return 0; }
/* Checks all clients against KILL lines. (And remove them, if found.) ** Only MAXDELAYEDKILLS at a time or all, if not defined. ** Returns 1, if still work to do, 0 if finished. */ static int delayed_kills(time_t currenttime) { static time_t dk_rehashed = 0; /* time of last rehash we're processing */ static int dk_lastfd; /* fd we last checked */ static int dk_checked; /* # clients we checked */ static int dk_killed; /* # clients we killed */ Reg aClient *cptr; Reg int i, j; if (dk_rehashed == 0) { dk_rehashed = currenttime; dk_checked = 0; dk_killed = 0; dk_lastfd = highest_fd; } #ifdef MAXDELAYEDKILLS /* checking only this many clients each time */ j = dk_lastfd - MAXDELAYEDKILLS + 1; if (j < 0) #endif j = 0; for (i = dk_lastfd; i >= j; i--) { int kflag = 0; char *reason = NULL; if (!(cptr = local[i]) || !IsPerson(cptr)) { /* for K:lines we're interested only in local, ** fully registered clients */ if (j > 0) j--; continue; } dk_checked++; kflag = find_kill(cptr, 0, &reason); /* If the client is a user and a KILL line was found ** to be active, close this connection. */ if (kflag == -1) { char buf[100]; dk_killed++; 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"); } } dk_lastfd = i; /* from which fd to start next time */ Debug((DEBUG_DEBUG, "DelayedKills killed %d and counting...", dk_killed)); if (dk_lastfd < 0) { sendto_flag(SCH_NOTICE, "DelayedKills checked %d killed %d " "in %d sec", dk_checked, dk_killed, currenttime - dk_rehashed); dk_rehashed = 0; if (rehashed == 2) { /* there was rehash queued, start again */ return 1; } return 0; } return rehashed; }
static time_t check_pings(time_t currenttime) { #ifdef TIMEDKLINES static time_t lkill = 0; #endif Reg aClient *cptr; Reg int kflag = 0; aClient *bysptr = NULL; int ping = 0, i; time_t oldest = 0, timeout; char *reason = NULL; for (i = highest_fd; i >= 0; i--) { if (!(cptr = local[i]) || IsListener(cptr)) continue; #ifdef TIMEDKLINES kflag = 0; reason = NULL; /* ** Once per TIMEDKLINES seconds. ** (1 minute is minimum resolution in K-line field) */ if ((currenttime - lkill > TIMEDKLINES) && IsPerson(cptr) && !IsKlineExempt(cptr)) { kflag = find_kill(cptr, 1, &reason); } #endif ping = IsRegistered(cptr) ? cptr->ping : ACCEPTTIMEOUT; Debug((DEBUG_DEBUG, "c(%s) %d p %d k %d a %d", cptr->name, cptr->status, ping, kflag, 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 && 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 || ((currenttime - cptr->lasttime) >= (2 * ping) && (cptr->flags & FLAGS_PINGSENT)) || (!IsRegistered(cptr) && (currenttime - cptr->firsttime) >= ping)) { 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); exit_client(cptr, cptr, &me, "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)) { if (cptr->serv && cptr->serv->byuid[0]) { bysptr = find_uid(cptr->serv->byuid, NULL); } /* we are interested only in *remote* opers */ if (bysptr && !MyConnect(bysptr)) { sendto_one(bysptr, ":%s NOTICE %s :" "No response from %s, closing" " link", ME, bysptr->name, get_client_name(cptr, FALSE)); } 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 message 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"); } 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; } #ifdef TIMEDKLINES if (currenttime - lkill > 60) lkill = currenttime; #endif if (!oldest || oldest < currenttime) oldest = currenttime + PINGFREQUENCY; if (oldest < currenttime + 30) oldest += 30; Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d", myctime(oldest), ping, oldest, currenttime)); return (oldest); }
/* * 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(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, ipv6string, sizeof(ipv6string)), ntohs(them.SIN_PORT)); sprintf(abuf+strlen(abuf), "%s %u", inetntop(AF_INET6, (char *)&us.sin6_addr, ipv6string, sizeof(ipv6string)), 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, ipv6string, sizeof(ipv6string)), 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, ipv6string, sizeof(ipv6string)))); # 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, ipv6string, sizeof(ipv6string)), 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, ipv6string, sizeof(ipv6string)), 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; }
/* ** 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(time_t currenttime) { Reg aConfItem *aconf; Reg aClient *cptr; aConfItem **pconf; int confrq; time_t next = 0; aClass *cltmp; aConfItem *con_conf = NULL; int allheld = 1; #ifdef DISABLE_DOUBLE_CONNECTS int i; #endif if ((bootopt & BOOT_STANDALONE)) return 0; Debug((DEBUG_NOTICE,"Connection check at : %s", myctime(currenttime))); for (aconf = conf; aconf; aconf = aconf->next ) { /* not a C-line */ if (!(aconf->status & (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER))) continue; /* not a candidate for AC */ if (aconf->port <= 0) continue; cltmp = Class(aconf); /* not a candidate for AC */ if (MaxLinks(cltmp) == 0) continue; /* minimize next to lowest hold time of all AC-able C-lines */ if (next > aconf->hold || next == 0) next = aconf->hold; /* skip conf if the use of it is on hold until future. */ if (aconf->hold > currenttime) continue; /* at least one candidate not held for future, good */ allheld = 0; /* see if another link in this conf is allowed */ if (Links(cltmp) >= MaxLinks(cltmp)) continue; /* next possible check after connfreq secs for this C-line */ confrq = get_con_freq(cltmp); aconf->hold = currenttime + confrq; /* is this server already connected? */ cptr = find_name(aconf->name, (aClient *)NULL); if (!cptr) cptr = find_mask(aconf->name, (aClient *)NULL); /* matching client already exists, no AC to it */ if (cptr) continue; /* no such server, check D-lines */ if (find_denied(aconf->name, Class(cltmp))) continue; #ifdef DISABLE_DOUBLE_CONNECTS /* Much better would be traversing only unknown ** connections, but this requires another global ** variable, adding and removing from there in ** proper places etc. Some day. --B. */ for (i = highest_fd; i >= 0; i--) { if (!(cptr = local[i]) || cptr->status > STAT_UNKNOWN) { continue; } /* an unknown traveller we have */ if ( #ifndef INET6 cptr->ip.s_addr == aconf->ipnum.s_addr #else !memcmp(cptr->ip.s6_addr, aconf->ipnum.s6_addr, 16) #endif ) { /* IP the same. Coincidence? Maybe. ** Do not cause havoc with double connect. */ break; } cptr = NULL; } if (cptr) { sendto_flag(SCH_SERVER, "AC to %s postponed", aconf->name); continue; } #endif /* we have a candidate! */ /* choose the best. */ if (!con_conf || (con_conf->pref > aconf->pref && aconf->pref >= 0) || (con_conf->pref == -1 && Class(cltmp) > ConfClass(con_conf))) { con_conf = aconf; } /* above is my doubt: if we always choose best connection ** and it always fails connecting, we may never try another, ** even "worse"; what shall we do? --Beeth */ } 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; } /* "Penalty" for being the best, so in next call of * try_connections() other servers have chance. --B. */ con_conf->hold += get_con_freq(Class(con_conf)); if (!iconf.aconnect) { sendto_flag(SCH_NOTICE, "Connection to %s deferred. Autoconnect " "administratively disabled", con_conf->name); } else 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); } } else if (allheld == 0) /* disable AC only when some C: got checked */ { /* No suitable conf for AC was found, so why bother checking ** again? If some server quits, it'd get reenabled --B. */ next = 0; } Debug((DEBUG_NOTICE,"Next connection check : %s", myctime(next))); return (next); }
/* ** 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(aClient *cptr, aClient *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); 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); } 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. */ strcpy(locked[lk_index].nick, np->ww_nick); locked[lk_index++].logout = np->ww_logout; if ((lk_index == lk_size) && (lk_size != ww_size)) { grow_locked(); } 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_whowas(); if (ww_index >= ww_size) ww_index = 0; return; }
/* ** 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) ** ** Parameters: ** ** 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 */ int exit_client(aClient *cptr, aClient *sptr, aClient *from, const char *comment) { char comment1[HOSTLEN + HOSTLEN + 2]; if (MyConnect(sptr)) { if (sptr->flags & FLAGS_KILLED) { sendto_flag(SCH_NOTICE, "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)) { # if defined(FNAME_USERLOG) || defined(USE_SERVICES) || \ (defined(USE_SYSLOG) && defined(SYSLOG_USERS)) sendto_flog(sptr, EXITC_REG, sptr->user->username, sptr->user->host); # endif # if defined(CLIENTS_CHANNEL) && (CLIENTS_CHANNEL_LEVEL & CCL_QUIT) sendto_flag(SCH_CLIENT, "%s %s %s %s QUIT %c" # if (CLIENTS_CHANNEL_LEVEL & CCL_QUITINFO) " :%s" # endif , sptr->user->uid, sptr->name, sptr->user->username, sptr->user->host, sptr->exitc # if (CLIENTS_CHANNEL_LEVEL & CCL_QUITINFO) , comment # endif ); # endif } else if (!IsService(sptr)) { # if defined(FNAME_CONNLOG) || defined(USE_SERVICES) || \ (defined(USE_SYSLOG) && defined(SYSLOG_CONN)) if (sptr->exitc == '\0' || sptr->exitc == EXITC_REG) { sptr->exitc = EXITC_UNDEF; } sendto_flog(sptr, sptr->exitc, sptr->user && sptr->user->username ? sptr->user->username : "", #ifdef UNIXPORT (IsUnixSocket(sptr)) ? me.sockhost : #endif ((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 (istat.is_myclnt % CLCHNO == 0 && istat.is_myclnt != istat.is_l_myclnt) { sendto_flag(SCH_NOTICE, "Local %screase from %d to %d clients " "in %d seconds", istat.is_myclnt>istat.is_l_myclnt?"in":"de", istat.is_l_myclnt, istat.is_myclnt, timeofday - istat.is_l_myclnt_t); istat.is_l_myclnt_t = timeofday; istat.is_l_myclnt = istat.is_myclnt; } /* Send SQUIT message to 2.11 servers to tell them * the squit reason for rebroadcast on the other side * - jv */ if (IsServer(sptr)) { sendto_one(sptr, ":%s SQUIT %s :%s", me.serv->sid, sptr->serv->sid, comment); } 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 -= strlen(sptr->auth) + 1; 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 (MyConnect(sptr) */ if (IsServer(sptr)) { /* Remove all dependent servers and clients. */ if (!IsMasked(sptr)) { sprintf(comment1, "%s %s", sptr->serv->up->name, sptr->name); } else { /* It was a masked server, the squit reason should ** give the right quit reason for clients. */ strncpyzt(comment1, comment, sizeof(comment1)); } /* cptr != sptr means non-local server */ if (cptr != sptr && nextconnect == 0 && find_conf_name(sptr->name, (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER))) { /* try AC */ nextconnect = timeofday + HANGONRETRYDELAY; } exit_server(sptr, sptr, from, comment, comment1); check_split(); if ((cptr == sptr)) { /* It serves no purpose. --B. sendto_flag(SCH_SERVER, "Sending SQUIT %s (%s)", cptr->name, comment); */ return FLUSH_BUFFER; } return 0; } /* ** 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 ((sptr->flags & FLAGS_KILLED) == 0) { if (comment[0] == '"') { /* definitely user quit, see m_quit */ sptr->flags |= FLAGS_QUIT; } else { const 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; } } exit_one_client(cptr, sptr, from, (*comment1) ? comment1 : comment); /* XXX: we probably should not call it every client exit */ /* checking every server quit should suffice --B. */ /* check_split(); */ 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(aClient *cptr, aClient *sptr, aClient *from, const char *comment) { Reg aClient *acptr; Reg int i; Reg Link *lp; invLink *ilp; /* ** 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) */ if (!IsMasked(sptr)) { istat.is_serv--; } if (!IsBursting(sptr)) { istat.is_eobservers--; } for (i = fdas.highest; i >= 0; i--) { if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) { continue; } if (!(sptr->flags & FLAGS_SQUIT)) { /* Make sure we only send the last SQUIT ** to a 2.11. */ continue; } if ((acptr->flags & FLAGS_HIDDEN) && !IsMasked(sptr)) { /* We need a special SQUIT reason, so ** the remote server can send the ** right quit message. */ sendto_one(acptr, ":%s SQUIT %s :%s %s", sptr->serv->up->serv->sid, sptr->serv->sid, sptr->serv->up->name, sptr->name); } else { sendto_one(acptr, ":%s SQUIT %s :%s", sptr->serv->up->serv->sid, sptr->serv->sid, comment); } } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_SQUIT, sptr->serv, sptr, ":%s SQUIT %s :%s", from->name, sptr->name, comment); #endif del_from_sid_hash_table(sptr->serv); remove_server_from_tree(sptr); /* remove server from svrtop */ unregister_server(sptr); } 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->user->uid, comment); #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT| SERVICE_WANT_RQUIT, (sptr->user) ? sptr->user->servp : 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]]) || acptr == cptr || IsMe(acptr)) continue; if (acptr->flags & FLAGS_HIDDEN) sendto_one(acptr, ":%s QUIT :%s", sptr->user->uid, comment); } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); #endif } } #ifdef USE_SERVICES else { /* Send QUIT to services which desire such as well. ** Services with both _QUIT and _KILL will get both ** for now --jv */ check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : 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]--; sptr->user->servp->usercnt[1]--; } else { istat.is_user[0]--; sptr->user->servp->usercnt[0]--; } if (IsAnOper(sptr)) { sptr->user->servp->usercnt[2]--; 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 ((ilp = sptr->user->invited)) { del_invite(sptr, ilp->chptr); /* again, this is all that is needed */ } /* remove from uid hash table */ if (sptr->user) { del_from_uid_hash_table(sptr->user->uid, sptr); } /* 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); #ifdef USE_HOSTHASH del_from_hostname_hash_table(sptr->user->host, sptr->user); #endif #ifdef USE_IPHASH del_from_ip_hash_table(sptr->user->sip, sptr->user); #endif } } 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) && match(sptr->service->dist, acptr->serv->sid)) { 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; }
/* ** free_user ** Decrease user reference count by one and release block, ** if count reaches 0 */ void free_user(anUser *user) { aServer *serv; aClient *cptr = user->bcptr; if (--user->refcnt <= 0) { /* Loop: This would be second deallocation of this structure. * XXX: Remove loop detection before 2.11.0 - jv */ if (user->refcnt == -211001) { sendto_flag(SCH_ERROR, "* %p free_user loop (%s!%s@%s) %p *", (void *)cptr, cptr ? cptr->name : "<noname>", user->username, user->host, user); return; } /* * 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, "%p %p %p %p %d %d %p (%s)", (void *)user, (void *)user->invited, (void *)user->channel, (void *)user->uwas, user->joined, user->refcnt, (void *)user->bcptr, (user->bcptr) ? user->bcptr->name :"none"); #ifdef DEBUGMODE dumpcore("%p user (%s!%s@%s) %s", (void *)cptr, cptr ? cptr->name : "<noname>", user->username, user->host, buf); #else sendto_flag(SCH_ERROR, "* %p user (%s!%s@%s) %s *", (void *)cptr, cptr ? cptr->name : "<noname>", user->username, user->host, buf); #endif } if ((serv = user->servp)) { user->servp = NULL; /* to avoid some impossible loop */ user->refcnt = -211000; /* For loop detection */ free_server(serv); } if (user->away) { istat.is_away--; istat.is_awaymem -= (strlen(user->away) + 1); MyFree(user->away); } MyFree(user); #ifdef DEBUGMODE users.inuse--; #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. ** ** If msg is a null pointer, we are flushing connection */ int send_message(aClient *to, char *msg, int len) { 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) > (i=get_sendq(to, CBurst(to)))) { to->exitc = EXITC_SENDQ; if (IsService(to) || IsServer(to)) { return dead_link(to, "Max SendQ limit exceeded for %s: %d > %d", get_client_name(to, FALSE), DBufLength(&to->sendQ), i); } return dead_link(to, "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); # endif /* ZIP_LINKS */ tryagain: if (len && (i = dbuf_put(&to->sendQ, msg, len)) < 0) { if (i == -2 /* Poolsize was exceeded. */ #ifdef POOLSIZE_LIMITED /* ** Defining this retains old ircd behaviour (will ** allow client quit with buffer allocation error ** as a result of poolsize starvation). As it may ** happen to all clients on a big channel without ** their fault, I think this is not right. ** In the long run it should not matter (poolsize ** or memory usage-wise), because if client lacks ** the poolsize, the poolsize is too small anyway ** and next netburst would probably make it grow. ** IMO increasing poolsize with no limits is good ** for clients -- hence this is not defined. --B. */ && CBurst(to) #endif ) { /* Anyway, 10% increase. */ poolsize *= 1.1; sendto_flag(SCH_NOTICE, "New poolsize %u. (reached)", poolsize); istat.is_dbufmore++; goto tryagain; } else { to->exitc = EXITC_MBUF; return dead_link(to, "Buffer allocation error for %s", get_client_name(to, FALSE)); } } /* ** 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; }
void outofmemory() { Debug((DEBUG_FATAL, "Out of memory: restarting server...")); sendto_flag(SCH_NOTICE, "Ouch!!! Out of memory..."); restart("Out of Memory"); }
/* * read_iauth * * read and process data from the authentication slave process. */ void read_iauth(void) { 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; olen = 0; 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; sendto_iauth("0 M %s", me.name); 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; } #ifndef INET6 sprintf(tbuf, "%c %d %s %u ", start[0], i, inetntoa((char *)&cptr->ip), cptr->port); #else sprintf(tbuf, "%c %d %s %u ", start[0], i, inetntop(AF_INET6, (char *)&cptr->ip, ipv6string, sizeof(ipv6string)), cptr->port); #endif 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 -= strlen(cptr->auth) + 1; 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 -= strlen(cptr->auth) + 1; 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 { char *reason; /* Copy kill reason received from iauth */ reason = strstr(start, " :"); if (reason && (reason + 2 != '\0')) { if (cptr->reason) { MyFree(cptr->reason); } cptr->reason = mystrdup(reason + 2); } /* ** 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.. */ /* Finally, working after registration. --B. */ if (IsRegisteredUser(cptr)) { if (cptr->exitc == EXITC_AREF) { sendto_flag(SCH_LOCAL, "Denied after connection " "from %s.", get_client_host(cptr)); } (void) exit_client(cptr, cptr, &me, cptr->reason ? cptr->reason : "Denied access"); } } start = end; } olen -= start - buf; if (olen) memcpy(obuf, start, olen); } }