/** Report server statistics to a client. * @param cptr Client who wants statistics. * @param sd StatDesc structure being looked up (unused). * @param param Extra parameter passed by user (unused). */ void tstats(struct Client *cptr, const struct StatDesc *sd, char *param) { struct Client *acptr; int i; struct ServerStatistics *sp; struct ServerStatistics tmp; sp = &tmp; memcpy(sp, ServerStats, sizeof(struct ServerStatistics)); for (i = 0; i < MAXCONNECTIONS; i++) { if (!(acptr = LocalClientArray[i])) continue; if (IsServer(acptr)) { sp->is_sbs += cli_sendB(acptr); sp->is_sbr += cli_receiveB(acptr); sp->is_sti += CurrentTime - cli_firsttime(acptr); sp->is_sv++; } else if (IsUser(acptr)) { sp->is_cbs += cli_sendB(acptr); sp->is_cbr += cli_receiveB(acptr); sp->is_cti += CurrentTime - cli_firsttime(acptr); sp->is_cl++; } else if (IsUnknown(acptr)) sp->is_ni++; } send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":accepts %u refused %u", sp->is_ac, sp->is_ref); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":unknown commands %u prefixes %u", sp->is_unco, sp->is_unpf); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":nick collisions %u unknown closes %u", sp->is_kill, sp->is_ni); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":wrong direction %u empty %u", sp->is_wrdi, sp->is_empt); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":numerics seen %u mode fakes %u", sp->is_num, sp->is_fake); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":auth successes %u fails %u", sp->is_asuc, sp->is_abad); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":local connections %u", sp->is_loc); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Client server"); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":connected %u %u", sp->is_cl, sp->is_sv); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":bytes sent %Lu %Lu", sp->is_cbs, sp->is_sbs); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":bytes recv %Lu %Lu", sp->is_cbr, sp->is_sbr); send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":time connected %Lu %Lu", sp->is_cti, sp->is_sti); }
/* * mo_info - oper message handler * * parv[0] = sender prefix * parv[1] = servername */ int mo_info(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { const char **text = infotext; if (hunt_server_cmd(sptr, CMD_INFO, cptr, 1, ":%C", 1, parc, parv) == HUNTED_ISME) { while (text[212]) { if (!IsOper(sptr)) send_reply(sptr, RPL_INFO, *text); text++; } if (IsOper(sptr) && (NULL != parv[1])) { while (*text) send_reply(sptr, RPL_INFO, *text++); send_reply(sptr, RPL_INFO, ""); } send_reply(sptr, SND_EXPLICIT | RPL_INFO, ":Birth Date: %s, compile # %s", creation, generation); send_reply(sptr, SND_EXPLICIT | RPL_INFO, ":On-line since %s", myctime(cli_firsttime(&me))); send_reply(sptr, RPL_ENDOFINFO); } return 0; }
/* * Create a new struct Client structure and set it to initial state. * * from == NULL, create local client (a client connected to a socket). * * from != NULL, create remote client (behind a socket associated with * the client defined by 'from'). * ('from' is a local client!!). */ struct Client* make_client(struct Client *from, int status) { struct Client* cptr = 0; struct Connection* con = 0; assert(!from || cli_verify(from)); cptr = alloc_client(); assert(0 != cptr); assert(!cli_magic(cptr)); assert(0 == from || 0 != cli_connect(from)); if (!from) { /* local client, allocate a struct Connection */ con = alloc_connection(); assert(0 != con); assert(!con_magic(con)); con_magic(con) = CONNECTION_MAGIC; con_fd(con) = -1; /* initialize struct Connection */ con_freeflag(con) = 0; con_nextnick(con) = CurrentTime - NICK_DELAY; con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1)); con_handler(con) = UNREGISTERED_HANDLER; con_client(con) = cptr; cli_local(cptr) = 1; /* Set certain fields of the struct Client */ cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime; cli_lastnick(cptr) = TStime(); } else con = cli_connect(from); /* use 'from's connection */ assert(0 != con); assert(con_verify(con)); cli_magic(cptr) = CLIENT_MAGIC; cli_connect(cptr) = con; /* set the connection and other fields */ cli_status(cptr) = status; cli_hnext(cptr) = cptr; strcpy(cli_username(cptr), "unknown"); return cptr; }
static void stats_links(struct Client* sptr, struct StatDesc* sd, int stat, char* name) { struct Client *acptr; int i; int wilds = 0; if (name) wilds = string_has_wildcards(name); /* * Send info about connections which match, or all if the * mask matches me.name. Only restrictions are on those who * are invisible not being visible to 'foreigners' who use * a wild card based search to list it. */ send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "Connection SendQ " "SendM SendKBytes RcveM RcveKBytes :Open since"); for (i = 0; i <= HighestFd; i++) { if (!(acptr = LocalClientArray[i])) continue; /* Don't return clients when this is a request for `all' */ if (!name && IsUser(acptr)) continue; /* Don't show invisible people to non opers unless they know the nick */ if (IsInvisible(acptr) && (!name || wilds) && !IsAnOper(acptr) && (acptr != sptr)) continue; /* Only show the ones that match the given mask - if any */ if (name && wilds && match(name, cli_name(acptr))) continue; /* Skip all that do not match the specific query */ if (!(!name || wilds) && 0 != ircd_strcmp(name, cli_name(acptr))) continue; send_reply(sptr, SND_EXPLICIT | RPL_STATSLINKINFO, "%s %u %u %u %u %u :%Tu", (*(cli_name(acptr))) ? cli_name(acptr) : "<unregistered>", (int)MsgQLength(&(cli_sendQ(acptr))), (int)cli_sendM(acptr), (int)cli_sendK(acptr), (int)cli_receiveM(acptr), (int)cli_receiveK(acptr), CurrentTime - cli_firsttime(acptr)); } }
/** Handle a client that decided to disconnect (or was killed after * completing his connection). This updates the free target * information for his IP registry entry. * @param[in] cptr Client that has exited. */ void ip_registry_disconnect(struct Client *cptr) { struct IPRegistryEntry* entry = ip_registry_find(&cli_ip(cptr)); if (!irc_in_addr_valid(&cli_ip(cptr))) { Debug((DEBUG_DNS, "IPcheck noting dicconnect from invalid %s.", ircd_ntoa(&cli_ip(cptr)))); return; } assert(entry); assert(entry->connected > 0); Debug((DEBUG_DNS, "IPcheck noting disconnect from %s.", ircd_ntoa(&entry->addr))); /* * If this was the last one, set `last_connect' to disconnect time (used for expiration) */ if (0 == --entry->connected) { if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD) { /* * Otherwise we'd penalize for this old value if the client reconnects within 20 seconds */ entry->attempts = 0; } ip_registry_update_free_targets(entry); entry->last_connect = NOW; } if (MyConnect(cptr)) { unsigned int free_targets; /* * Copy the clients targets */ if (0 == entry->target) { entry->target = (struct IPTargetEntry*) MyMalloc(sizeof(struct IPTargetEntry)); entry->target->count = STARTTARGETS; } assert(0 != entry->target); memcpy(entry->target->targets, cli_targets(cptr), MAXTARGETS); /* * This calculation can be pretty unfair towards large multi-user hosts, but * there is "nothing" we can do without also allowing spam bots to send more * messages or by drastically increasing the amount of memory used in the IPregistry. * * The problem is that when a client disconnects, leaving no free targets, then * the next client from that IP number has to pay for it (getting no free targets). * But ALSO the next client, and the next client, and the next client etc - until * another client disconnects that DOES leave free targets. The reason for this * is that if there are 10 SPAM bots, and they all disconnect at once, then they * ALL should get no free targets when reconnecting. We'd need to store an entry * per client (instead of per IP number) to avoid this. */ if (cli_nexttarget(cptr) < CurrentTime) { /* * Number of free targets */ free_targets = (CurrentTime - cli_nexttarget(cptr)) / TARGET_DELAY + 1; } else free_targets = 0; /* * Add bonus, this is pretty fuzzy, but it will help in some cases. */ if ((CurrentTime - cli_firsttime(cptr)) > 600) /* * Was longer then 10 minutes online? */ free_targets += (CurrentTime - cli_firsttime(cptr) - 600) / TARGET_DELAY; /* * Finally, store smallest value for Judgment Day */ if (free_targets < entry->target->count) entry->target->count = free_targets; } }
/** * Exits a client of *any* type (user, server, etc) * from this server. Also, this generates all necessary prototol * messages that this exit may cause. * * This function implicitly exits all other clients depending on * this connection. * * For convenience, this function returns a suitable value for * m_function return value: * * CPTR_KILLED if (cptr == bcptr) * 0 if (cptr != bcptr) * * This function can be called in two ways: * 1) From before or in parse(), exiting the 'cptr', in which case it was * invoked as exit_client(cptr, cptr, &me,...), causing it to always * return CPTR_KILLED. * 2) Via parse from a m_function call, in which case it was invoked as * exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client * that generated the message in a way that we can assume he already * did remove acptr from memory himself (or in other cases we don't mind * because he will be delinked.) Or invoked as: * exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should * be removed. * In general: No generated SQUIT or QUIT should be sent to source link * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too). * * --Run * @param cptr Connection currently being handled by read_message. * @param victim Client being killed. * @param killer Client that made the decision to remove \a victim. * @param comment Reason for the exit. * @return CPTR_KILLED if cptr == bcptr, else 0. */ int exit_client(struct Client *cptr, struct Client* victim, struct Client* killer, const char* comment) { struct Client* acptr = 0; struct DLink *dlp; time_t on_for; char comment1[HOSTLEN + HOSTLEN + 2]; assert(killer); if (MyConnect(victim)) { SetFlag(victim, FLAG_CLOSING); if (feature_bool(FEAT_CONNEXIT_NOTICES) && IsUser(victim)) sendto_opmask_butone(0, SNO_CONNEXIT, "Client exiting: %s (%s@%s) [%s] [%s] <%s%s>", cli_name(victim), cli_user(victim)->username, cli_user(victim)->host, comment, ircd_ntoa(&cli_ip(victim)), NumNick(victim) /* two %s's */); update_load(); on_for = CurrentTime - cli_firsttime(victim); if (IsUser(victim) || IsUserPort(victim)) auth_send_exit(victim); if (IsUser(victim)) log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s", cli_firsttime(victim), on_for, cli_user(victim)->username, cli_sockhost(victim), ircd_ntoa(&cli_ip(victim)), IsAccount(victim) ? cli_username(victim) : "0", NumNick(victim), /* two %s's */ cli_name(victim), cli_info(victim)); if (victim != cli_from(killer) /* The source knows already */ && IsClient(victim)) /* Not a Ping struct or Log file */ { if (IsServer(victim) || IsHandshake(victim)) sendcmdto_one(killer, CMD_SQUIT, victim, "%s 0 :%s", cli_name(&me), comment); else if (!IsConnecting(victim)) { if (!IsDead(victim)) { if (IsServer(victim)) sendcmdto_one(killer, CMD_ERROR, victim, ":Closing Link: %s by %s (%s)", cli_name(victim), cli_name(killer), comment); else sendrawto_one(victim, MSG_ERROR " :Closing Link: %s by %s (%s)", cli_name(victim), cli_name(IsServer(killer) ? &his : killer), comment); } } if ((IsServer(victim) || IsHandshake(victim) || IsConnecting(victim)) && (killer == &me || (IsServer(killer) && (strncmp(comment, "Leaf-only link", 14) || strncmp(comment, "Non-Hub link", 12))))) { /* * Note: check user == user needed to make sure we have the same * client */ if (cli_serv(victim)->user && *(cli_serv(victim))->by && (acptr = findNUser(cli_serv(victim)->by))) { if (cli_user(acptr) == cli_serv(victim)->user) { sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s canceled: %s", acptr, cli_name(victim), comment); } else { /* * not right client, set by to empty string */ acptr = 0; *(cli_serv(victim))->by = '\0'; } } if (killer == &me) sendto_opmask_butone(acptr, SNO_OLDSNO, "Link with %s canceled: %s", cli_name(victim), comment); } } /* * Close the Client connection first. */ close_connection(victim); } if (IsServer(victim)) { if (feature_bool(FEAT_HIS_NETSPLIT)) strcpy(comment1, "*.net *.split"); else { strcpy(comment1, cli_name(cli_serv(victim)->up)); strcat(comment1, " "); strcat(comment1, cli_name(victim)); } if (IsUser(killer)) sendto_opmask_butone(killer, SNO_OLDSNO, "%s SQUIT by %s [%s]:", (cli_user(killer)->server == victim || cli_user(killer)->server == cli_serv(victim)->up) ? "Local" : "Remote", get_client_name(killer, HIDE_IP), cli_name(cli_user(killer)->server)); else if (killer != &me && cli_serv(victim)->up != killer) sendto_opmask_butone(0, SNO_OLDSNO, "Received SQUIT %s from %s :", cli_name(victim), IsServer(killer) ? cli_name(killer) : get_client_name(killer, HIDE_IP)); sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)", cli_serv(victim)->up, victim, comment); } /* * First generate the needed protocol for the other server links * except the source: */ for (dlp = cli_serv(&me)->down; dlp; dlp = dlp->next) { if (dlp->value.cptr != cli_from(killer) && dlp->value.cptr != victim) { if (IsServer(victim)) sendcmdto_one(killer, CMD_SQUIT, dlp->value.cptr, "%s %Tu :%s", cli_name(victim), cli_serv(victim)->timestamp, comment); else if (IsUser(victim) && !HasFlag(victim, FLAG_KILLED)) sendcmdto_one(victim, CMD_QUIT, dlp->value.cptr, ":%s", comment); } } /* Then remove the client structures */ if (IsServer(victim)) exit_downlinks(victim, killer, comment1); exit_one_client(victim, comment); /* * cptr can only have been killed if it was cptr itself that got killed here, * because cptr can never have been a dependent of victim --Run */ return (cptr == victim) ? CPTR_KILLED : 0; }
/** Run the daemon. * @param[in] argc Number of arguments in \a argv. * @param[in] argv Arguments to program execution. */ int main(int argc, char **argv) { CurrentTime = time(NULL); thisServer.argc = argc; thisServer.argv = argv; thisServer.uid = getuid(); thisServer.euid = geteuid(); #ifdef MDEBUG mem_dbg_initialise(); #endif #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE) set_core_limit(); #endif umask(077); /* better safe than sorry --SRB */ memset(&me, 0, sizeof(me)); memset(&me_con, 0, sizeof(me_con)); cli_connect(&me) = &me_con; cli_fd(&me) = -1; parse_command_line(argc, argv); if (chdir(dpath)) { fprintf(stderr, "Fail: Cannot chdir(%s): %s, check DPATH\n", dpath, strerror(errno)); return 2; } if (!set_userid_if_needed()) return 3; /* Check paths for accessibility */ if (!check_file_access(SPATH, 'S', X_OK) || !check_file_access(configfile, 'C', R_OK)) return 4; if (!init_connection_limits()) return 9; close_connections(!(thisServer.bootopt & (BOOT_DEBUG | BOOT_TTY | BOOT_CHKCONF))); /* daemon_init() must be before event_init() because kqueue() FDs * are, perversely, not inherited across fork(). */ daemon_init(thisServer.bootopt & BOOT_TTY); #ifdef DEBUGMODE /* Must reserve fd 2... */ if (debuglevel >= 0 && !(thisServer.bootopt & BOOT_TTY)) { int fd; if ((fd = open("/dev/null", O_WRONLY)) < 0) { fprintf(stderr, "Unable to open /dev/null (to reserve fd 2): %s\n", strerror(errno)); return 8; } if (fd != 2 && dup2(fd, 2) < 0) { fprintf(stderr, "Unable to reserve fd 2; dup2 said: %s\n", strerror(errno)); return 8; } } #endif event_init(MAXCONNECTIONS); setup_signals(); feature_init(); /* initialize features... */ log_init(*argv); set_nomem_handler(outofmemory); initload(); init_list(); init_hash(); init_class(); initwhowas(); initmsgtree(); initstats(); /* we need this for now, when we're modular this should be removed -- hikari */ ircd_crypt_init(); motd_init(); if (!init_conf()) { log_write(LS_SYSTEM, L_CRIT, 0, "Failed to read configuration file %s", configfile); return 7; } if (thisServer.bootopt & BOOT_CHKCONF) { if (dbg_client) conf_debug_iline(dbg_client); fprintf(stderr, "Configuration file %s checked okay.\n", configfile); return 0; } debug_init(thisServer.bootopt & BOOT_TTY); if (check_pid()) { Debug((DEBUG_FATAL, "Failed to acquire PID file lock after fork")); exit(2); } init_server_identity(); uping_init(); stats_init(); IPcheck_init(); timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1); timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1); timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60); timer_add(timer_init(&mute_timer), check_expired_mutes, 0, TT_PERIODIC, 30); CurrentTime = time(NULL); SetMe(&me); cli_magic(&me) = CLIENT_MAGIC; cli_from(&me) = &me; make_server(&me); cli_serv(&me)->timestamp = TStime(); /* Abuse own link timestamp as start TS */ cli_serv(&me)->prot = atoi(MAJOR_PROTOCOL); cli_serv(&me)->up = &me; cli_serv(&me)->down = NULL; cli_handler(&me) = SERVER_HANDLER; SetYXXCapacity(&me, MAXCLIENTS); cli_lasttime(&me) = cli_since(&me) = cli_firsttime(&me) = CurrentTime; hAddClient(&me); write_pidfile(); init_counters(); Debug((DEBUG_NOTICE, "Server ready...")); log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready"); event_loop(); return 0; }
/** Check for clients that have not sent a ping response recently. * Reschedules itself to run again at the appropriate time. * @param[in] ev Timer event (ignored). */ static void check_pings(struct Event* ev) { int expire = 0; int next_check = CurrentTime; int max_ping = 0; int i; assert(ET_EXPIRE == ev_type(ev)); assert(0 != ev_timer(ev)); next_check += feature_int(FEAT_PINGFREQUENCY); /* Scan through the client table */ for (i=0; i <= HighestFd; i++) { struct Client *cptr = LocalClientArray[i]; if (!cptr) continue; assert(&me != cptr); /* I should never be in the local client array! */ /* Remove dead clients. */ if (IsDead(cptr)) { exit_client(cptr, cptr, &me, cli_info(cptr)); continue; } max_ping = IsRegistered(cptr) ? client_get_ping(cptr) : feature_int(FEAT_CONNECTTIMEOUT); Debug((DEBUG_DEBUG, "check_pings(%s)=status:%s limit: %d current: %d", cli_name(cptr), IsPingSent(cptr) ? "[Ping Sent]" : "[]", max_ping, (int)(CurrentTime - cli_lasttime(cptr)))); /* If it's a server and we have not sent an AsLL lately, do so. */ if (IsServer(cptr)) { if (CurrentTime - cli_serv(cptr)->asll_last >= max_ping) { char *asll_ts; SetPingSent(cptr); cli_serv(cptr)->asll_last = CurrentTime; expire = cli_serv(cptr)->asll_last + max_ping; asll_ts = militime_float(NULL); sendcmdto_prio_one(&me, CMD_PING, cptr, "!%s %s %s", asll_ts, cli_name(cptr), asll_ts); } expire = cli_serv(cptr)->asll_last + max_ping; if (expire < next_check) next_check = expire; } /* Ok, the thing that will happen most frequently, is that someone will * have sent something recently. Cover this first for speed. * -- * If it's an unregistered client and hasn't managed to register within * max_ping then it's obviously having problems (broken client) or it's * just up to no good, so we won't skip it, even if its been sending * data to us. * -- hikari */ if ((CurrentTime-cli_lasttime(cptr) < max_ping) && IsRegistered(cptr)) { expire = cli_lasttime(cptr) + max_ping; if (expire < next_check) next_check = expire; continue; } /* Unregistered clients pingout after max_ping seconds, they don't * get given a second chance - if they were then people could not quite * finish registration and hold resources without being subject to k/g * lines */ if (!IsRegistered(cptr)) { assert(!IsServer(cptr)); /* If client authorization time has expired, ask auth whether they * should be checked again later. */ if ((CurrentTime-cli_firsttime(cptr) >= max_ping) && auth_ping_timeout(cptr)) continue; /* OK, they still have enough time left, so we'll just skip to the * next client. Set the next check to be when their time is up, if * that's before the currently scheduled next check -- hikari */ expire = cli_firsttime(cptr) + max_ping; if (expire < next_check) next_check = expire; continue; } /* Quit the client after max_ping*2 - they should have answered by now */ if (CurrentTime-cli_lasttime(cptr) >= (max_ping*2) ) { /* If it was a server, then tell ops about it. */ if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) sendto_opmask_butone(0, SNO_OLDSNO, "No response from %s, closing link", cli_name(cptr)); exit_client_msg(cptr, cptr, &me, "Ping timeout"); continue; } if (!IsPingSent(cptr)) { /* If we haven't PINGed the connection and we haven't heard from it in a * while, PING it to make sure it is still alive. */ SetPingSent(cptr); /* If we're late in noticing don't hold it against them :) */ cli_lasttime(cptr) = CurrentTime - max_ping; if (IsUser(cptr)) sendrawto_one(cptr, MSG_PING " :%s", cli_name(&me)); else sendcmdto_prio_one(&me, CMD_PING, cptr, ":%s", cli_name(&me)); } expire = cli_lasttime(cptr) + max_ping * 2; if (expire < next_check) next_check=expire; } assert(next_check >= CurrentTime); Debug((DEBUG_DEBUG, "[%i] check_pings() again in %is", CurrentTime, next_check-CurrentTime)); timer_add(&ping_timer, check_pings, 0, TT_ABSOLUTE, next_check); }
/* * Send whois information for acptr to sptr */ static void do_whois(struct Client* sptr, struct Client *acptr, int parc) { struct Client *a2cptr=0; struct Channel *chptr=0; int mlen; int len; static char buf[512]; const struct User* user = cli_user(acptr); const char* name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr); a2cptr = feature_bool(FEAT_HIS_WHOIS_SERVERNAME) && !IsAnOper(sptr) && sptr != acptr ? &his : user->server; assert(user); send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, cli_info(acptr)); /* Display the channels this user is on. */ if (!IsChannelService(acptr)) { struct Membership* chan; mlen = strlen(cli_name(&me)) + strlen(cli_name(sptr)) + 12 + strlen(name); len = 0; *buf = '\0'; for (chan = user->channel; chan; chan = chan->next_channel) { chptr = chan->channel; if (!ShowChannel(sptr, chptr) && !(IsOper(sptr) && IsLocalChannel(chptr->chname))) continue; if (acptr != sptr && IsZombie(chan)) continue; /* Don't show local channels when HIS is defined, unless it's a * remote WHOIS --ULtimaTe_ */ if (IsLocalChannel(chptr->chname) && (acptr != sptr) && (parc == 2) && feature_bool(FEAT_HIS_WHOIS_LOCALCHAN) && !IsAnOper(sptr)) continue; if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf); *buf = '\0'; len = 0; } if (IsDeaf(acptr)) *(buf + len++) = '-'; if (!ShowChannel(sptr, chptr)) *(buf + len++) = '*'; if (IsDelayedJoin(chan) && (sptr != acptr)) *(buf + len++) = '<'; else if (IsChanOp(chan)) *(buf + len++) = '@'; else if (HasVoice(chan)) *(buf + len++) = '+'; else if (IsZombie(chan)) *(buf + len++) = '!'; if (len) *(buf + len) = '\0'; strcpy(buf + len, chptr->chname); len += strlen(chptr->chname); strcat(buf + len, " "); len++; } if (buf[0] != '\0') send_reply(sptr, RPL_WHOISCHANNELS, name, buf); } send_reply(sptr, RPL_WHOISSERVER, name, cli_name(a2cptr), cli_info(a2cptr)); if (user) { if (user->away) send_reply(sptr, RPL_AWAY, name, user->away); if (SeeOper(sptr,acptr)) send_reply(sptr, RPL_WHOISOPERATOR, name); if (IsAccount(acptr)) send_reply(sptr, RPL_WHOISACCOUNT, name, user->account); if (HasHiddenHost(acptr) && (IsAnOper(sptr) || acptr == sptr)) send_reply(sptr, RPL_WHOISACTUALLY, name, user->username, user->realhost, ircd_ntoa(&cli_ip(acptr))); /* Hint: if your looking to add more flags to a user, eg +h, here's * probably a good place to add them :) */ if (MyConnect(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) || (sptr == acptr || IsAnOper(sptr) || parc >= 3))) send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, cli_firsttime(acptr)); } }
/** 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; } }