/* * add_to_channel_hash_table */ void add_to_channel_hash_table(const char* name, struct Channel* chptr) { struct Channel *achptr; unsigned int hashv; assert(0 != name); assert(0 != chptr); if ((achptr = hash_find_channel(name, NULL))) { sendto_ops_flag(UMODE_DEBUG, "WTF: Already got %s in hash table but something tried to add it again, dumping core", name); logprintf(L_WARN, "WTF: Already got %s in hash table but something tried to add it again, dumping core", name); flush_connections(0); abort(); } hashv = hash_channel_name(name); if ((channelTable[hashv].links == 0) && (channelTable[hashv].list != NULL)) { sendto_ops_flag(UMODE_DEBUG, "WTF: channelTable[%d].links == 0 but list not NULL. Whacking list...", hashv); logprintf(L_WARN, "WTF: channelTable[%d].links == 0 but list not NULL. Whacking list...", hashv); channelTable[hashv].list = NULL; } chptr->hnextch = (struct Channel*) channelTable[hashv].list; channelTable[hashv].list = (void*) chptr; ++channelTable[hashv].links; ++channelTable[hashv].hits; }
/** Restart the server with a message. * @param[in] message Message to log and send to operators. */ void server_restart(const char *message) { static int restarting = 0; /* inhibit sending any server notices; we may be in a loop */ log_write(LS_SYSTEM, L_WARNING, LOG_NOSNOTICE, "Restarting Server: %s", message); if (restarting++) /* increment restarting to prevent looping */ return; sendto_opmask_butone(0, SNO_OLDSNO, "Restarting server: %s", message); Debug((DEBUG_NOTICE, "Restarting server...")); flush_connections(0); log_close(); close_connections(!(thisServer.bootopt & (BOOT_TTY | BOOT_DEBUG | BOOT_CHKCONF))); reap_children(); execv(SPATH, thisServer.argv); /* Have to reopen since it has been closed above */ log_reopen(); log_write(LS_SYSTEM, L_CRIT, 0, "execv(%s,%s) failed: %m", SPATH, *thisServer.argv); Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s", SPATH, (strerror(errno)) ? strerror(errno) : "")); exit(8); }
VOIDSIG s_die() { #ifdef USE_SYSLOG (void)syslog(LOG_CRIT, "Server Killed By SIGTERM"); #endif flush_connections(me.fd); exit(-1); }
/** Terminate the server with a message. * @param[in] message Message to log and send to operators. */ void server_die(const char *message) { /* log_write will send out message to both log file and as server notice */ log_write(LS_SYSTEM, L_CRIT, 0, "Server terminating: %s", message); flush_connections(0); close_connections(1); running = 0; }
/** Immediately terminate the server with a message. * @param[in] message Message to log, but not send to operators. */ void server_panic(const char *message) { /* inhibit sending server notice--we may be panicking due to low memory */ log_write(LS_SYSTEM, L_CRIT, LOG_NOSNOTICE, "Server panic: %s", message); flush_connections(0); log_close(); close_connections(1); exit(1); }
RETSIGTYPE s_die(int s) { sendto_serv_v(NULL, SV_UID, ":%s SDIE", me.serv->sid); #ifdef USE_SYSLOG (void)syslog(LOG_CRIT, "Server Killed By SIGTERM"); (void)closelog(); #endif logfiles_close(); ircd_writetune(tunefile); flush_connections(me.fd); exit(-1); }
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); }
void server_reboot() { Reg int i; sendto_ops("Aieeeee!!! Restarting server... sbrk(0)-etext: %d", (u_int)sbrk((size_t)0)-(u_int)sbrk0); Debug((DEBUG_NOTICE,"Restarting server...")); flush_connections(me.fd); /* ** fd 0 must be 'preserved' if either the -d or -i options have ** been passed to us before restarting. */ #ifdef USE_SYSLOG (void)closelog(); #endif for (i = 3; i < MAXCONNECTIONS; i++) (void)close(i); if (!(bootopt & (BOOT_TTY|BOOT_DEBUG))) (void)close(2); (void)close(1); if ((bootopt & BOOT_CONSOLE) || isatty(0)) (void)close(0); if (!(bootopt & (BOOT_INETD|BOOT_OPER))) (void)execv(MYNAME, myargv); #ifdef USE_SYSLOG /* Have to reopen since it has been closed above */ openlog(myargv[0], LOG_PID|LOG_NDELAY, LOG_FACILITY); syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", MYNAME, myargv[0]); closelog(); #endif Debug((DEBUG_FATAL,"Couldn't restart server: %s", strerror(errno))); exit(-1); }
/** Append bytes to a data buffer. * @param[in] dyn Buffer to append to. * @param[in] buf Data to append. * @param[in] length Number of bytes to append. * @return Non-zero on success, or zero on failure. */ int dbuf_put(struct DBuf *dyn, const char *buf, unsigned int length) { struct DBufBuffer** h; struct DBufBuffer* db; unsigned int chunk; assert(0 != dyn); assert(0 != buf); /* * Locate the last non-empty buffer. If the last buffer is full, * the loop will terminate with 'db==NULL'. * This loop assumes that the 'dyn->length' field is correctly * maintained, as it should--no other check really needed. */ if (!dyn->length) h = &(dyn->head); else h = &(dyn->tail); /* * Append users data to buffer, allocating buffers as needed */ dyn->length += length; for (; length > 0; h = &(db->next)) { if (0 == (db = *h)) { if (0 == (db = dbuf_alloc())) { if (feature_bool(FEAT_HAS_FERGUSON_FLUSHER)) { /* * from "Married With Children" episode were Al bought a REAL toilet * on the black market because he was tired of the wimpy water * conserving toilets they make these days --Bleep */ /* * Apparently this doesn't work, the server _has_ to * dump a few clients to handle the load. A fully loaded * server cannot handle a net break without dumping some * clients. If we flush the connections here under a full * load we may end up starving the kernel for mbufs and * crash the machine */ /* * attempt to recover from buffer starvation before * bailing this may help servers running out of memory */ flush_connections(0); db = dbuf_alloc(); } if (0 == db) return dbuf_malloc_error(dyn); } dyn->tail = db; *h = db; db->next = 0; db->start = db->end = db->data; } chunk = (db->data + DBUF_SIZE) - db->end; if (chunk) { if (chunk > length) chunk = length; memcpy(db->end, buf, chunk); length -= chunk; buf += chunk; db->end += chunk; } } return 1; }
static time_t io_loop(time_t delay) { static char to_send[200]; static time_t lasttime = 0; static long lastrecvK = 0; static int lrv = 0; time_t lasttimeofday; lasttimeofday = CurrentTime; if (CurrentTime < lasttimeofday) { ircsprintf(to_send, "System clock is running backwards - (%d < %d)", CurrentTime, lasttimeofday); report_error(to_send, me.name, 0); } if(!next_gc) { next_gc = CurrentTime + 600; } /* * This chunk of code determines whether or not * "life sucks", that is to say if the traffic * level is so high that standard server * commands should be restricted * * Changed by Taner so that it tells you what's going on * as well as allows forced on (long LCF), etc... */ if ((CurrentTime - lasttime) >= LCF) { lrv = LRV * LCF; lasttime = CurrentTime; currlife = (float)((long)me.receiveK - lastrecvK)/(float)LCF; if (((long)me.receiveK - lrv) > lastrecvK ) { if (!LIFESUX) { LIFESUX = 1; if (NOISYHTM) { sprintf(to_send, "Entering high-traffic mode - (%.1fk/s > %dk/s)", (float)currlife, LRV); sendto_ops("%s", to_send); } } else { LIFESUX++; /* Ok, life really sucks! */ LCF += 2; /* Wait even longer */ if (NOISYHTM) { sprintf(to_send, "Still high-traffic mode %d%s (%d delay): %.1fk/s", LIFESUX, (LIFESUX & 0x04) ? " (TURBO)" : "", (int)LCF, (float)currlife); sendto_ops("%s", to_send); } } } else { LCF = LOADCFREQ; if (LIFESUX) { LIFESUX = 0; if (NOISYHTM) sendto_ops("Resuming standard operation . . . ."); } } lastrecvK = (long)me.receiveK; } /* ** We only want to connect if a connection is due, ** not every time through. Note, if there are no ** active C lines, this call to Tryconnections is ** made once only; it will return 0. - avalon */ if (nextconnect && CurrentTime >= nextconnect) nextconnect = try_connections(CurrentTime); /* ** take the smaller of the two 'timed' event times as ** the time of next event (stops us being late :) - avalon ** WARNING - nextconnect can return 0! */ if (nextconnect) delay = IRCD_MIN(nextping, nextconnect); delay -= CurrentTime; /* ** Adjust delay to something reasonable [ad hoc values] ** (one might think something more clever here... --msa) ** We don't really need to check that often and as long ** as we don't delay too long, everything should be ok. ** waiting too long can cause things to timeout... ** i.e. PINGS -> a disconnection :( ** - avalon */ if (delay < 1) delay = 1; else delay = IRCD_MIN(delay, TIMESEC); /* * We want to read servers on every io_loop, as well * as "busy" clients (which again, includes servers. * If "lifesux", then we read servers AGAIN, and then * flush any data to servers. * -Taner */ #ifndef NO_PRIORITY read_message(0, FDL_SERVER); read_message(0, FDL_BUSY); if (LIFESUX) { read_message(0, FDL_SERVER); if (LIFESUX & 0x4) { /* life really sucks */ read_message(0, FDL_BUSY); read_message(0, FDL_SERVER); } flush_server_connections(); } /* * CLIENT_SERVER = TRUE: * If we're in normal mode, or if "lifesux" and a few * seconds have passed, then read everything. * CLIENT_SERVER = FALSE: * If it's been more than lifesux*2 seconds (that is, * at most 1 second, or at least 2s when lifesux is * != 0) check everything. * -Taner */ { static time_t lslasttime=0; #ifdef CLIENT_SERVER if (!LIFESUX || (lslasttime + LIFESUX) < CurrentTime) { #else if ((lslasttime + (LIFESUX + 1)) < CurrentTime) { #endif read_message(0, FDL_ALL); /* check everything! */ lslasttime = CurrentTime; } } #else read_message(0, FDL_ALL); /* check everything! */ flush_server_connections(); #endif /* ** ...perhaps should not do these loops every time, ** but only if there is some chance of something ** happening (but, note that conf->hold times may ** be changed elsewhere--so precomputed next event ** time might be too far away... (similarly with ** ping times) --msa */ if (CurrentTime >= nextping) { nextping = check_pings(CurrentTime); timeout_auth_queries(CurrentTime); } if (dorehash && !LIFESUX) { rehash(&me, &me, 1); dorehash = 0; } /* ** Flush output buffers on all connections now if they ** have data in them (or at least try to flush) ** -avalon */ flush_connections(0); #ifndef NO_PRIORITY fdlist_check(CurrentTime); #endif if(CurrentTime >= next_gc) { block_garbage_collect(); next_gc = CurrentTime + 600; } return delay; } /* * initalialize_global_set_options * * inputs - none * output - none * side effects - This sets all global set options needed */ static void initialize_global_set_options(void) { memset( &GlobalSetOptions, 0, sizeof(GlobalSetOptions)); MAXCLIENTS = MAX_CLIENTS; NOISYHTM = NOISY_HTM; GlobalSetOptions.autoconn = 1; #ifdef FLUD FLUDNUM = FLUD_NUM; FLUDTIME = FLUD_TIME; FLUDBLOCK = FLUD_BLOCK; #endif #ifdef IDLE_CHECK IDLETIME = MIN_IDLETIME; #endif #ifdef ANTI_SPAMBOT SPAMTIME = MIN_JOIN_LEAVE_TIME; SPAMNUM = MAX_JOIN_LEAVE_COUNT; #endif #ifdef ANTI_DRONE_FLOOD DRONETIME = DEFAULT_DRONE_TIME; DRONECOUNT = DEFAULT_DRONE_COUNT; #endif #ifdef NEED_SPLITCODE SPLITDELAY = (DEFAULT_SERVER_SPLIT_RECOVERY_TIME * 60); SPLITNUM = SPLIT_SMALLNET_SIZE; SPLITUSERS = SPLIT_SMALLNET_USER_SIZE; server_split_time = CurrentTime; #endif /* End of global set options */ }
/* * m_die - DIE command handler */ int m_die(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client* acptr; int i; if (!MyClient(sptr) || !IsAnOper(sptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (!IsOperDie(sptr)) { sendto_one(sptr,":%s NOTICE %s :You have no D flag", me.name, parv[0]); return 0; } if (parc < 2) { sendto_one(sptr,":%s NOTICE %s :Need server name /die %s", me.name,sptr->name,me.name); return 0; } else { if (irccmp(parv[1], me.name)) { sendto_one(sptr,":%s NOTICE %s :Mismatch on /die %s", me.name,sptr->name,me.name); return 0; } } for (i = 0; i <= highest_fd; i++) { if (!(acptr = local[i])) continue; if (IsClient(acptr)) { if(IsAnOper(acptr)) sendto_one(acptr, ":%s NOTICE %s :Server Terminating. %s", me.name, acptr->name, get_client_name(sptr, HIDE_IP)); else sendto_one(acptr, ":%s NOTICE %s :Server Terminating. %s", me.name, acptr->name, get_client_name(sptr, MASK_IP)); } else if (IsServer(acptr)) sendto_one(acptr, ":%s ERROR :Terminated by %s", me.name, get_client_name(sptr, MASK_IP)); } flush_connections(0); irclog(L_NOTICE, "Server terminated by %s", get_client_name(sptr, HIDE_IP)); /* * this is a normal exit, tell the os it's ok */ exit(0); /* NOT REACHED */ return 0; }
void io_loop() { char to_send[200]; time_t lasttime = 0; long lastrecvK = 0; int lrv = 0; time_t lasttimeofday; int delay = 0; while(1) { lasttimeofday = timeofday; if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } if (timeofday < lasttimeofday) { ircsprintf(to_send, "System clock is running backwards - (%d < %d)", timeofday, lasttimeofday); report_error(to_send, &me); } NOW = timeofday; /* * This chunk of code determines whether or not "life sucks", that * is to say if the traffic level is so high that standard server * commands should be restricted * * Changed by Taner so that it tells you what's going on as well as * allows forced on (long LCF), etc... */ if ((timeofday - lasttime) >= LCF) { lrv = LRV * LCF; lasttime = timeofday; currlife = (me.receiveK - lastrecvK) / LCF; if ((me.receiveK - lrv) > lastrecvK || HTMLOCK == YES) { if (!lifesux) { /* * In the original +th code Taner had * * LCF << 1; / * add hysteresis * / * * which does nothing... so, the hybrid team changed it to * * LCF <<= 1; / * add hysteresis * / * * suddenly, there were reports of clients mysteriously just * dropping off... Neither rodder or I can see why it makes * a difference, but lets try it this way... * * The original dog3 code, does not have an LCF variable * * -Dianora * */ lifesux = 1; if (noisy_htm) sendto_ops("Entering high-traffic mode - (%dk/s > %dk/s)", currlife, LRV); } else { lifesux++; /* Ok, life really sucks! */ LCF += 2; /* Wait even longer */ if (noisy_htm) sendto_ops("Still high-traffic mode %d%s (%d delay): %dk/s", lifesux, (lifesux > 9) ? " (TURBO)" : "", (int) LCF, currlife); /* Reset htm here, because its been on a little too long. * Bad Things(tm) tend to happen with HTM on too long -epi */ if (lifesux>15) { if (noisy_htm) sendto_ops("Resetting HTM and raising limit to: %dk/s\n", LRV + 5); LCF=LOADCFREQ; lifesux=0; LRV+=5; } } } else { LCF = LOADCFREQ; if (lifesux) { lifesux = 0; if (noisy_htm) sendto_ops("Resuming standard operation . . . ."); } } lastrecvK = me.receiveK; } /* * * We only want to connect if a connection is due, not every * time through. Note, if there are no active C lines, this call * to Tryconnections is made once only; it will return 0. - avalon */ if (nextconnect && timeofday >= nextconnect) nextconnect = try_connections(timeofday); /* DNS checks. One to timeout queries, one for cache expiries.*/ if (timeofday >= nextdnscheck) nextdnscheck = timeout_query_list(timeofday); if (timeofday >= nextexpire) nextexpire = expire_cache(timeofday); /* * * take the smaller of the two 'timed' event times as the time * of next event (stops us being late :) - avalon WARNING - * nextconnect can return 0! */ if (nextconnect) delay = MIN(nextping, nextconnect); else delay = nextping; delay = MIN(nextdnscheck, delay); delay = MIN(nextexpire, delay); delay -= timeofday; /* * * Adjust delay to something reasonable [ad hoc values] (one * might think something more clever here... --msa) * We don't really need to check that often and as long * as we don't delay too long, everything should be ok. * waiting too long can cause things to timeout... * i.e. PINGS -> a disconnection :( * - avalon */ if (delay < 1) delay = 1; else delay = MIN(delay, TIMESEC); /* * We want to read servers on every io_loop, as well as "busy" * clients (which again, includes servers. If "lifesux", then we * read servers AGAIN, and then flush any data to servers. -Taner */ #ifndef NO_PRIORITY read_message(0, &serv_fdlist); read_message(1, &busycli_fdlist); if (lifesux) { (void) read_message(1, &serv_fdlist); if (lifesux > 9) /* life really sucks */ { (void) read_message(1, &busycli_fdlist); (void) read_message(1, &serv_fdlist); } flush_fdlist_connections(&serv_fdlist); } if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } /* * CLIENT_SERVER = TRUE: If we're in normal mode, or if "lifesux" * and a few seconds have passed, then read everything. * CLIENT_SERVER = FALSE: If it's been more than lifesux*2 seconds * (that is, at most 1 second, or at least 2s when lifesux is != 0) * check everything. -Taner */ { static time_t lasttime = 0; # ifdef CLIENT_SERVER if (!lifesux || (lasttime + lifesux) < timeofday) { # else if ((lasttime + (lifesux + 1)) < timeofday) { # endif (void) read_message(delay ? delay : 1, NULL); /* check everything! */ lasttime = timeofday; } } #else (void) read_message(delay, NULL); /* check everything! */ #endif /* * * ...perhaps should not do these loops every time, but only if * there is some chance of something happening (but, note that * conf->hold times may be changed elsewhere--so precomputed next * event time might be too far away... (similarly with ping * times) --msa */ if ((timeofday >= nextping)) nextping = check_pings(timeofday); if (dorehash && !lifesux) { (void) rehash(&me, &me, 1); dorehash = 0; } /* * * Flush output buffers on all connections now if they * have data in them (or at least try to flush) -avalon * * flush_connections(me.fd); * * avalon, what kind of crack have you been smoking? why * on earth would we flush_connections blindly when * we already check to see if we can write (and do) * in read_message? There is no point, as this causes * lots and lots of unnecessary sendto's which * 99% of the time will fail because if we couldn't * empty them in read_message we can't empty them here. * one effect: during htm, output to normal lusers * will lag. */ /* Now we've made this call a bit smarter. */ /* Only flush non-blocked sockets. */ flush_connections(me.fd); #ifndef NO_PRIORITY check_fdlists(); #endif #ifdef LOCKFILE /* * * If we have pending klines and CHECK_PENDING_KLINES minutes * have passed, try writing them out. -ThemBones */ if ((pending_klines) && ((timeofday - pending_kline_time) >= (CHECK_PENDING_KLINES * 60))) do_pending_klines(); #endif } } /* * open_debugfile * * If the -t option is not given on the command line when the server is * started, all debugging output is sent to the file set by LPATH in * config.h Here we just open that file and make sure it is opened to * fd 2 so that any fprintf's to stderr also goto the logfile. If the * debuglevel is not set from the command line by -x, use /dev/null as * the dummy logfile as long as DEBUGMODE has been defined, else dont * waste the fd. */ static void open_debugfile() { #ifdef DEBUGMODE int fd; aClient *cptr; if (debuglevel >= 0) { cptr = make_client(NULL, NULL); cptr->fd = 2; SetLog(cptr); cptr->port = debuglevel; cptr->flags = 0; cptr->acpt = cptr; local[2] = cptr; (void) strcpy(cptr->sockhost, me.sockhost); (void) printf("isatty = %d ttyname = %#x\n", isatty(2), (u_int) ttyname(2)); if (!(bootopt & BOOT_TTY)) /*) leave debugging output on fd */ { (void) truncate(LOGFILE, 0); if ((fd = open(LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0) if ((fd = open("/dev/null", O_WRONLY)) < 0) exit(-1); if (fd != 2) { (void) dup2(fd, 2); (void) close(fd); } strncpyzt(cptr->name, LOGFILE, sizeof(cptr->name)); } else if (isatty(2) && ttyname(2)) strncpyzt(cptr->name, ttyname(2), sizeof(cptr->name)); else (void) strcpy(cptr->name, "FD2-Pipe"); Debug((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s", cptr->name, cptr->port, myctime(time(NULL)))); } else local[2] = NULL; #endif return; }
/** Attempt to send a sequence of bytes to the connection. * As a side effect, updates \a cptr's FLAG_BLOCKED setting * and sendB/sendK fields. * @param cptr Client that should receive data. * @param buf Message buffer to send to client. * @return Negative on connection-fatal error; otherwise * number of bytes sent. */ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf) { unsigned int bytes_written = 0; unsigned int bytes_count = 0; assert(0 != cptr); #if defined(USE_SSL) switch (client_sendv(cptr, buf, &bytes_count, &bytes_written)) { #else switch (os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written)) { #endif case IO_SUCCESS: ClrFlag(cptr, FLAG_BLOCKED); cli_sendB(cptr) += bytes_written; cli_sendB(&me) += bytes_written; /* A partial write implies that future writes will block. */ if (bytes_written < bytes_count) SetFlag(cptr, FLAG_BLOCKED); break; case IO_BLOCKED: SetFlag(cptr, FLAG_BLOCKED); break; case IO_FAILURE: cli_error(cptr) = errno; SetFlag(cptr, FLAG_DEADSOCKET); break; } return bytes_written; } /** Complete non-blocking connect()-sequence. Check access and * terminate connection, if trouble detected. * @param cptr Client to which we have connected, with all ConfItem structs attached. * @return Zero on failure (caller should exit_client()), non-zero on success. */ static int completed_connection(struct Client* cptr) { struct ConfItem *aconf; time_t newts; struct Client *acptr; int i; #if defined(USE_SSL) char *sslfp; int r; #endif assert(0 != cptr); /* * get the socket status from the fd first to check if * connection actually succeeded */ if ((cli_error(cptr) = os_get_sockerr(cli_fd(cptr)))) { const char* msg = strerror(cli_error(cptr)); if (!msg) msg = "Unknown error"; sendto_opmask(0, SNO_OLDSNO, "Connection failed to %s: %s", cli_name(cptr), msg); return 0; } if (!(aconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER))) { sendto_opmask(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr)); return 0; } #if defined(USE_SSL) if (aconf->flags & CONF_SSL) { r = ssl_connect(&(cli_socket(cptr))); if (r == -1) { sendto_opmask(0, SNO_OLDSNO, "Connection failed to %s: SSL error", cli_name(cptr)); return 0; } else if (r == 0) return 1; sslfp = ssl_get_fingerprint(cli_socket(cptr).s_ssl); if (sslfp) ircd_strncpy(cli_sslclifp(cptr), sslfp, BUFSIZE+1); SetSSL(cptr); } #endif if (s_state(&(cli_socket(cptr))) == SS_CONNECTING) socket_state(&(cli_socket(cptr)), SS_CONNECTED); if (!EmptyString(aconf->passwd)) sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd); /* * Create a unique timestamp */ newts = TStime(); for (i = HighestFd; i > -1; --i) { if ((acptr = LocalClientArray[i]) && (IsServer(acptr) || IsHandshake(acptr))) { if (cli_serv(acptr)->timestamp >= newts) newts = cli_serv(acptr)->timestamp + 1; } } assert(0 != cli_serv(cptr)); cli_serv(cptr)->timestamp = newts; SetHandshake(cptr); /* * Make us timeout after twice the timeout for DNS look ups */ cli_lasttime(cptr) = CurrentTime; ClearPingSent(cptr); /* TODO: NEGOCIACION envia_config_req(cptr); */ sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s", cli_name(&me), cli_serv(&me)->timestamp, newts, MAJOR_PROTOCOL, NumServCap(&me), feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me)); #if defined(DDB) ddb_burst(cptr); #endif return (IsDead(cptr)) ? 0 : 1; } /** Close the physical connection. Side effects: MyConnect(cptr) * becomes false and cptr->from becomes NULL. * @param cptr Client to disconnect. */ void close_connection(struct Client *cptr) { struct ConfItem* aconf; if (IsServer(cptr)) { ServerStats->is_sv++; ServerStats->is_sbs += cli_sendB(cptr); ServerStats->is_sbr += cli_receiveB(cptr); ServerStats->is_sti += CurrentTime - cli_firsttime(cptr); /* * If the connection has been up for a long amount of time, schedule * a 'quick' reconnect, else reset the next-connect cycle. */ if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) { /* * Reschedule a faster reconnect, if this was a automatically * connected configuration entry. (Note that if we have had * a rehash in between, the status has been changed to * CONF_ILLEGAL). But only do this if it was a "good" link. */ aconf->hold = CurrentTime; aconf->hold += ((aconf->hold - cli_since(cptr) > feature_int(FEAT_HANGONGOODLINK)) ? feature_int(FEAT_HANGONRETRYDELAY) : ConfConFreq(aconf)); /* if (nextconnect > aconf->hold) */ /* nextconnect = aconf->hold; */ } } else if (IsUser(cptr)) { ServerStats->is_cl++; ServerStats->is_cbs += cli_sendB(cptr); ServerStats->is_cbr += cli_receiveB(cptr); ServerStats->is_cti += CurrentTime - cli_firsttime(cptr); } else ServerStats->is_ni++; #if defined(USE_ZLIB) /* * Siempre es una conexion nuestra */ if (cli_connect(cptr)->zlib_negociation & ZLIB_IN) { inflateEnd(cli_connect(cptr)->comp_in); MyFree(cli_connect(cptr)->comp_in); } if (cli_connect(cptr)->zlib_negociation & ZLIB_OUT) { deflateEnd(cli_connect(cptr)->comp_out); MyFree(cli_connect(cptr)->comp_out); } #endif if (-1 < cli_fd(cptr)) { flush_connections(cptr); LocalClientArray[cli_fd(cptr)] = 0; close(cli_fd(cptr)); socket_del(&(cli_socket(cptr))); /* queue a socket delete */ cli_fd(cptr) = -1; cli_freeflag(cptr) &= ~FREEFLAG_SOCKET; } SetFlag(cptr, FLAG_DEADSOCKET); MsgQClear(&(cli_sendQ(cptr))); client_drop_sendq(cli_connect(cptr)); DBufClear(&(cli_recvQ(cptr))); memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); set_snomask(cptr, 0, SNO_SET); det_confs_butmask(cptr, 0); if (cli_listener(cptr)) { release_listener(cli_listener(cptr)); cli_listener(cptr) = 0; } for ( ; HighestFd > 0; --HighestFd) { if (LocalClientArray[HighestFd]) break; } } /** Close all unregistered connections. * @param source Oper who requested the close. * @return Number of closed connections. */ int net_close_unregistered_connections(struct Client* source) { int i; struct Client* cptr; int count = 0; assert(0 != source); for (i = HighestFd; i > 0; --i) { if ((cptr = LocalClientArray[i]) && !IsRegistered(cptr)) { send_reply(source, RPL_CLOSING, get_client_name(source, HIDE_IP)); exit_client(source, cptr, &me, "Oper Closing"); ++count; } } return count; }
/** Close the physical connection. Side effects: MyConnect(cptr) * becomes false and cptr->from becomes NULL. * @param cptr Client to disconnect. */ void close_connection(struct Client *cptr) { struct ConfItem* aconf; if (IsServer(cptr)) { ServerStats->is_sv++; ServerStats->is_sbs += cli_sendB(cptr); ServerStats->is_sbr += cli_receiveB(cptr); ServerStats->is_sti += CurrentTime - cli_firsttime(cptr); /* * If the connection has been up for a long amount of time, schedule * a 'quick' reconnect, else reset the next-connect cycle. */ if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) { /* * Reschedule a faster reconnect, if this was a automatically * connected configuration entry. (Note that if we have had * a rehash in between, the status has been changed to * CONF_ILLEGAL). But only do this if it was a "good" link. */ aconf->hold = CurrentTime; aconf->hold += ((aconf->hold - cli_since(cptr) > feature_int(FEAT_HANGONGOODLINK)) ? feature_int(FEAT_HANGONRETRYDELAY) : ConfConFreq(aconf)); /* if (nextconnect > aconf->hold) */ /* nextconnect = aconf->hold; */ } } else if (IsUser(cptr)) { ServerStats->is_cl++; ServerStats->is_cbs += cli_sendB(cptr); ServerStats->is_cbr += cli_receiveB(cptr); ServerStats->is_cti += CurrentTime - cli_firsttime(cptr); } else ServerStats->is_ni++; if (-1 < cli_fd(cptr)) { flush_connections(cptr); LocalClientArray[cli_fd(cptr)] = 0; close(cli_fd(cptr)); socket_del(&(cli_socket(cptr))); /* queue a socket delete */ cli_fd(cptr) = -1; } SetFlag(cptr, FLAG_DEADSOCKET); MsgQClear(&(cli_sendQ(cptr))); client_drop_sendq(cli_connect(cptr)); DBufClear(&(cli_recvQ(cptr))); memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); set_snomask(cptr, 0, SNO_SET); det_confs_butmask(cptr, 0); if (cli_listener(cptr)) { release_listener(cli_listener(cptr)); cli_listener(cptr) = 0; } for ( ; HighestFd > 0; --HighestFd) { if (LocalClientArray[HighestFd]) break; } }