/* * mr_error - unregistered client message handler * * parv[0] = sender prefix * parv[parc-1] = text */ int mr_error(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { const char *para; if (!IsHandshake(cptr) && !IsConnecting(cptr)) return 0; /* ignore ERROR from regular clients */ para = (parc > 1 && *parv[parc - 1] != '\0') ? parv[parc - 1] : "<>"; Debug((DEBUG_ERROR, "Received ERROR message from %s: %s", cli_name(sptr), para)); if (cptr == sptr) sendto_opmask_butone(0, SNO_OLDSNO, "ERROR :from %C -- %s", cptr, para); else sendto_opmask_butone(0, SNO_OLDSNO, "ERROR :from %C via %C -- %s", sptr, cptr, para); if (cli_serv(sptr)) { MyFree(cli_serv(sptr)->last_error_msg); DupString(cli_serv(sptr)->last_error_msg, para); } return 0; }
/** 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; 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_butone(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_butone(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr)); return 0; } 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); sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6n :%s", cli_name(&me), cli_serv(&me)->timestamp, newts, MAJOR_PROTOCOL, NumServCap(&me), feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me)); return (IsDead(cptr)) ? 0 : 1; }
/* * ms_end_of_burst - server message handler * - Added Xorath 6-14-96, rewritten by Run 24-7-96 * - and fixed by record and Kev 8/1/96 * - and really fixed by Run 15/8/96 :p * This the last message in a net.burst. * It clears a flag for the server sending the burst. * * As of 10.11, to fix a bug in the way BURST is processed, it also * makes sure empty channels are deleted * * parv[0] - sender prefix */ int ms_end_of_burst(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chan, *next_chan; assert(0 != cptr); assert(0 != sptr); sendto_opmask_butone(0, SNO_NETWORK, "Completed net.burst from %C.", sptr); sendcmdto_serv_butone(sptr, CMD_END_OF_BURST, cptr, ""); ClearBurst(sptr); SetBurstAck(sptr); if (MyConnect(sptr)) sendcmdto_one(&me, CMD_END_OF_BURST_ACK, sptr, ""); /* Count through channels... */ for (chan = GlobalChannelList; chan; chan = next_chan) { next_chan = chan->next; if (!chan->members && (chan->mode.mode & MODE_BURSTADDED)) { /* Newly empty channel, schedule it for removal. */ chan->mode.mode &= ~MODE_BURSTADDED; sub1_from_channel(chan); } else chan->mode.mode &= ~MODE_BURSTADDED; } return 0; }
/* * mo_rehash - oper message handler * * parv[1] = 'm' flushes the MOTD cache and returns * parv[1] = 'l' reopens the log files and returns * parv[1] = 'q' to not rehash the resolver (optional) */ int mo_rehash(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { int flag = 0; if (!HasPriv(sptr, PRIV_REHASH)) return send_reply(sptr, ERR_NOPRIVILEGES); if (parc > 1) { /* special processing */ if (*parv[1] == 'm') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Flushing MOTD cache"); motd_recache(); /* flush MOTD cache */ return 0; } else if (*parv[1] == 'l') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Reopening log files"); log_reopen(); /* reopen log files */ return 0; } else if (*parv[1] == 'q') flag = 2; } send_reply(sptr, RPL_REHASHING, configfile); sendto_opmask_butone(0, SNO_OLDSNO, "%C is rehashing Server config file", sptr); log_write(LS_SYSTEM, L_INFO, 0, "REHASH From %#C", sptr); return rehash(cptr, flag); }
/** 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); }
/* * do_kill - Performs the generic work involved in killing a client * */ static int do_kill(struct Client* cptr, struct Client* sptr, struct Client* victim, char* inpath, char* path, char* msg) { assert(0 != cptr); assert(0 != sptr); assert(!IsServer(victim)); /* * Notify all *local* opers about the KILL (this includes the one * originating the kill, if from this server--the special numeric * reply message is not generated anymore). * * Note: "victim->name" is used instead of "user" because we may * have changed the target because of the nickname change. */ sendto_opmask_butone(0, IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL, "Received KILL message for %s. From %s Path: %s!%s %s", get_client_name(victim, SHOW_IP), cli_name(sptr), inpath, path, msg); log_write_kill(victim, sptr, inpath, path, msg); /* * And pass on the message to other servers. Note, that if KILL * was changed, the message has to be sent to all links, also * back. * Client suicide kills are NOT passed on --SRB */ if (IsServer(cptr) || !MyConnect(victim)) { sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s %s", victim, inpath, path, msg); /* * Set FLAG_KILLED. This prevents exit_one_client from sending * the unnecessary QUIT for this. (This flag should never be * set in any other place) */ SetFlag(victim, FLAG_KILLED); } /* * Tell the victim she/he has been zapped, but *only* if * the victim is on current server--no sense in sending the * notification chasing the above kill, it won't get far * anyway (as this user don't exist there any more either) * In accordance with the new hiding rules, the victim * always sees the kill as coming from me. */ if (MyConnect(victim)) sendcmdto_one(feature_bool(FEAT_HIS_KILLWHO) ? &me : sptr, CMD_KILL, victim, "%C :%s %s", victim, feature_bool(FEAT_HIS_KILLWHO) ? feature_str(FEAT_HIS_SERVERNAME) : cli_name(sptr), msg); return exit_client_msg(cptr, victim, feature_bool(FEAT_HIS_KILLWHO) ? &me : sptr, "Killed (%s %s)", feature_bool(FEAT_HIS_KILLWHO) ? feature_str(FEAT_HIS_SERVERNAME) : cli_name(sptr), msg); }
/** Deactivate a Z-line. * @param[in] cptr Peer that gave us the message. * @param[in] sptr Client that initiated the deactivation. * @param[in] zline Z-line to deactivate. * @param[in] lastmod New value for Z-line last modification timestamp. * @param[in] flags ZLINE_LOCAL to only deactivate locally, 0 to propagate. * @return Zero. */ int zline_deactivate(struct Client *cptr, struct Client *sptr, struct Zline *zline, time_t lastmod, unsigned int flags) { unsigned int saveflags = 0; char *msg; assert(0 != zline); saveflags = zline->zl_flags; if (ZlineIsLocal(zline)) msg = "removing local"; else if (!zline->zl_lastmod && !(flags & ZLINE_LOCAL)) { msg = "removing global"; zline->zl_flags &= ~ZLINE_ACTIVE; /* propagate a -<mask> */ } else { msg = "deactivating global"; if (flags & ZLINE_LOCAL) zline->zl_flags |= ZLINE_LDEACT; else { zline->zl_flags &= ~ZLINE_ACTIVE; if (zline->zl_lastmod) { if (zline->zl_lastmod >= lastmod) zline->zl_lastmod++; else zline->zl_lastmod = lastmod; } } if ((saveflags & ZLINE_ACTMASK) != ZLINE_ACTIVE) return 0; /* was inactive to begin with */ } /* Inform ops and log it */ sendto_opmask_butone(0, SNO_GLINE, "%s %s ZLINE for %s, expiring at %Tu: " "%s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), msg, zline->zl_mask, zline->zl_expire, zline->zl_reason); log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, "%#C %s ZLINE for %s, expiring at %Tu: %s", sptr, msg, zline->zl_mask, zline->zl_expire, zline->zl_reason); if (!(flags & ZLINE_LOCAL)) /* don't propagate local changes */ zline_propagate(cptr, sptr, zline); /* if it's a local zline or a Uworld zline (and not locally deactivated).. */ if (ZlineIsLocal(zline) || (!zline->zl_lastmod && !(flags & ZLINE_LOCAL))) zline_free(zline); /* get rid of it */ return 0; }
/* * mo_rehash - oper message handler * * parv[1] = 'm' flushes the MOTD cache and returns * parv[1] = 'l' reopens the log files and returns * parv[1] = 'q' to not rehash the resolver (optional) * parv[1] = 's' to reload SSL certificates * parv[1] = 'a' to restart the IAuth program */ int mo_rehash(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { int flag = 0; if (!HasPriv(sptr, PRIV_REHASH) || ((parc == 3) && !HasPriv(sptr, PRIV_REMOTEREHASH))) return send_reply(sptr, ERR_NOPRIVILEGES); if ((parc == 3) && (hunt_server_cmd(sptr, CMD_REHASH, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME)) return 0; if (parc == 2) { /* special processing */ if (parv[1][1] == '\0') { /* one character server name */ if (*parv[1] == 'm') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Flushing MOTD cache"); motd_recache(); /* flush MOTD cache */ return 0; } else if (*parv[1] == 'l') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Reopening log files"); log_reopen(); /* reopen log files */ return 0; } else if (*parv[1] == 'a') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Restarting IAuth"); auth_restart(); /* Restart IAuth program */ return 0; #ifdef USE_SSL } else if (*parv[1] == 's') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Reloading SSL certificates"); ssl_reinit(); return 0; #endif } else if (*parv[1] == 'q') flag = 2; } /* * Maybe the user wants to rehash another server with no parameters. * NOTE: Here we assume that there are no servers named * 'm', 'l', 's', or 'q'. */ else if (HasPriv(sptr, PRIV_REMOTEREHASH)) { if (hunt_server_cmd(sptr, CMD_REHASH, cptr, 1, "%C", 1, parc, parv) != HUNTED_ISME) return 0; } else return send_reply(sptr, ERR_NOPRIVILEGES); } send_reply(sptr, RPL_REHASHING, configfile); sendto_opmask_butone(0, SNO_OLDSNO, "%C is rehashing Server config file", sptr); log_write(LS_SYSTEM, L_INFO, 0, "REHASH From %#C", sptr); return rehash(cptr, flag); }
/** Report an error message about the configuration file. * @param msg The error to report. */ void yyerror(const char *msg) { sendto_opmask_butone(0, SNO_ALL, "Config parse error in file %s on line %d: %s", linefile, lineno, msg); log_write(LS_CONFIG, L_ERROR, 0, "Config parse error in file %s on line %d: %s", linefile, lineno, msg); if (!conf_already_read) fprintf(stderr, "Config parse error in file %s on line %d: %s\n", linefile, lineno, msg); conf_error = 1; }
/* * ms_end_of_burst_ack - server message handler * * This the acknowledge message of the `END_OF_BURST' message. * It clears a flag for the server receiving the burst. * * parv[0] - sender prefix */ int ms_end_of_burst_ack(struct Client *cptr, struct Client *sptr, int parc, char **parv) { if (!IsServer(sptr)) return 0; sendto_opmask_butone(0, SNO_NETWORK, "%C acknowledged end of net.burst.", sptr); sendcmdto_serv_butone(sptr, CMD_END_OF_BURST_ACK, cptr, ""); ClearBurstAck(sptr); return 0; }
/** Called when resolver query finishes. If the DNS lookup was * successful, start the connection; otherwise notify opers of the * failure. * @param vptr The struct ConfItem representing the Connect block. * @param hp A pointer to the DNS lookup results (NULL on failure). */ static void connect_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name) { struct ConfItem* aconf = (struct ConfItem*) vptr; assert(aconf); aconf->dns_pending = 0; if (addr) { memcpy(&aconf->address, addr, sizeof(aconf->address)); connect_server(aconf, 0); } else sendto_opmask_butone(0, SNO_OLDSNO, "Connect to %s failed: host lookup", aconf->name); }
int ms_rehash(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { int flag = 0; if ((parc > 2) && (hunt_server_cmd(sptr, CMD_REHASH, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME)) return 0; /* OK, the message has been forwarded, but before we can act... */ if (!feature_bool(FEAT_NETWORK_REHASH)) return 0; if (parc > 1) { /* special processing */ if (parv[1][1] == '\0') { /* one character server name */ if (*parv[1] == 'm') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Flushing MOTD cache"); motd_recache(); /* flush MOTD cache */ return 0; } else if (*parv[1] == 'l') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Reopening log files"); log_reopen(); /* reopen log files */ return 0; } else if (*parv[1] == 'a') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Restarting IAuth"); auth_restart(); /* Restart IAuth program */ return 0; #ifdef USE_SSL } else if (*parv[1] == 's') { send_reply(sptr, SND_EXPLICIT | RPL_REHASHING, ":Reloading SSL certificates"); ssl_reinit(); return 0; #endif } else if (*parv[1] == 'q') flag = 2; } /* * Maybe the user wants to rehash another server with no parameters. * NOTE: Here we assume that there are no servers named * 'm', 'l', 's', or 'q'. */ else if ((parc == 2) && (hunt_server_cmd(sptr, CMD_REHASH, cptr, 1, "%C", 1, parc, parv) != HUNTED_ISME)) return 0; } send_reply(sptr, RPL_REHASHING, configfile); sendto_opmask_butone(0, SNO_OLDSNO, "%C [%s] is remotely rehashing Server config file", sptr, cli_name(cli_user(sptr)->server)); log_write(LS_SYSTEM, L_INFO, 0, "Remote REHASH From %#C [%s]", sptr, cli_name(cli_user(sptr)->server)); return rehash(cptr, flag); }
/** Update server start timestamps and TS offsets. * @param[in] cptr Server that just connected. * @param[in] timestamp Current time according to \a cptr. * @param[in] start_timestamp Time that \a cptr started. * @param[in] recv_time Current time as we know it. */ static void check_start_timestamp(struct Client *cptr, time_t timestamp, time_t start_timestamp, time_t recv_time) { Debug((DEBUG_DEBUG, "My start time: %Tu; other's start time: %Tu", cli_serv(&me)->timestamp, start_timestamp)); Debug((DEBUG_DEBUG, "Receive time: %Tu; received timestamp: %Tu; " "difference %ld", recv_time, timestamp, timestamp - recv_time)); if (feature_bool(FEAT_RELIABLE_CLOCK)) { if (start_timestamp < cli_serv(&me)->timestamp) cli_serv(&me)->timestamp = start_timestamp; if (IsUnknown(cptr)) cli_serv(cptr)->timestamp = TStime(); } else if (start_timestamp < cli_serv(&me)->timestamp) { sendto_opmask_butone(0, SNO_OLDSNO, "got earlier start time: " "%Tu < %Tu", start_timestamp, cli_serv(&me)->timestamp); cli_serv(&me)->timestamp = start_timestamp; TSoffset += timestamp - recv_time; sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d", (int)(timestamp - recv_time)); } else if ((start_timestamp > cli_serv(&me)->timestamp) && IsUnknown(cptr)) { cli_serv(cptr)->timestamp = TStime(); } else if (timestamp != recv_time) { /* * Equal start times, we have a collision. Let the connected-to * server decide. This assumes leafs issue more than half of the * connection attempts. */ if (IsUnknown(cptr)) cli_serv(cptr)->timestamp = TStime(); else if (IsHandshake(cptr)) { sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d", (int)(timestamp - recv_time)); TSoffset += timestamp - recv_time; } } }
/** Deactivate a jupe. * @param[in] cptr Local client that sent us the jupe. * @param[in] sptr Originator of the jupe. * @param[in] jupe Jupe to deactivate. * @param[in] lastmod New timestamp for last modification of the jupe. * @param[in] flags Flags to set on the jupe. * @return Zero. */ int jupe_deactivate(struct Client *cptr, struct Client *sptr, struct Jupe *jupe, time_t lastmod, unsigned int flags) { unsigned int saveflags = 0; assert(0 != jupe); saveflags = jupe->ju_flags; if (!JupeIsLocal(jupe)) { if (flags & JUPE_LOCAL) jupe->ju_flags |= JUPE_LDEACT; else { jupe->ju_flags &= ~JUPE_ACTIVE; if (jupe->ju_lastmod >= lastmod) /* force lastmod to increase */ jupe->ju_lastmod++; else jupe->ju_lastmod = lastmod; } if ((saveflags & JUPE_ACTMASK) != JUPE_ACTIVE) return 0; /* was inactive to begin with */ } /* Inform ops and log it */ sendto_opmask_butone(0, SNO_NETWORK, "%s %s JUPE for %s, expiring at %Tu: " "%s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), JupeIsLocal(jupe) ? "removing local" : "deactivating", jupe->ju_server, jupe->ju_expire + TSoffset, jupe->ju_reason); log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE, "%#C %s JUPE for %s, expiring at %Tu: %s", sptr, JupeIsLocal(jupe) ? "removing local" : "deactivating", jupe->ju_server, jupe->ju_expire + TSoffset, jupe->ju_reason); if (JupeIsLocal(jupe)) jupe_free(jupe); else if (!(flags & JUPE_LOCAL)) /* don't propagate local changes */ propagate_jupe(cptr, sptr, jupe); return 0; }
/** Try to send a buffer to a client, queueing it if needed. * @param[in,out] to Client to send message to. * @param[in] buf Message to send. * @param[in] prio If non-zero, send as high priority. */ void send_buffer(struct Client* to, struct MsgBuf* buf, int prio) { assert(0 != to); assert(0 != buf); if (cli_from(to)) to = cli_from(to); if (!can_send(to)) /* * This socket has already been marked as dead */ return; if (MsgQLength(&(cli_sendQ(to))) > get_sendq(to)) { if (IsServer(to)) sendto_opmask_butone(0, SNO_OLDSNO, "Max SendQ limit exceeded for %C: " "%zu > %zu", to, MsgQLength(&(cli_sendQ(to))), get_sendq(to)); dead_link(to, "Max sendQ exceeded"); return; } Debug((DEBUG_SEND, "Sending [%p] to %s", buf, cli_name(to))); msgq_add(&(cli_sendQ(to)), buf, prio); client_add_sendq(cli_connect(to), &send_queues); update_write(to); /* * 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. */ ++(cli_sendM(to)); ++(cli_sendM(&me)); /* * 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 (MsgQLength(&(cli_sendQ(to))) / 1024 > cli_lastsq(to)) send_queued(to); }
/** Destroy a local Z-line. * @param[in] cptr Peer that gave us the message. * @param[in] sptr Client that initiated the destruction. * @param[in] zline Z-line to destroy. * @return Zero. */ int zline_destroy(struct Client *cptr, struct Client *sptr, struct Zline *zline) { assert(zline); assert(ZlineIsLocal(zline)); /* Inform ops and log it */ sendto_opmask_butone(0, SNO_GLINE, "%s removing local ZLINE for %s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), zline->zl_mask); log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, "%#C removing local ZLINE for %s", sptr, zline->zl_mask); zline_free(zline); /* get rid of the Z-line */ return 0; /* convenience return */ }
/** Activate a currently inactive Z-line. * @param[in] cptr Peer that told us to activate the Z-line. * @param[in] sptr Client that originally thought it was a good idea. * @param[in] zline Z-line to activate. * @param[in] lastmod New value for last modification timestamp. * @param[in] flags 0 if the activation should be propagated, ZLINE_LOCAL if not. * @return Zero, unless \a sptr had a death wish (in which case CPTR_KILLED). */ int zline_activate(struct Client *cptr, struct Client *sptr, struct Zline *zline, time_t lastmod, unsigned int flags) { unsigned int saveflags = 0; assert(0 != zline); saveflags = zline->zl_flags; if (flags & ZLINE_LOCAL) zline->zl_flags &= ~ZLINE_LDEACT; else { zline->zl_flags |= ZLINE_ACTIVE; if (zline->zl_lastmod) { if (zline->zl_lastmod >= lastmod) /* force lastmod to increase */ zline->zl_lastmod++; else zline->zl_lastmod = lastmod; } } if ((saveflags & ZLINE_ACTMASK) == ZLINE_ACTIVE) return 0; /* was active to begin with */ /* Inform ops and log it */ sendto_opmask_butone(0, SNO_GLINE, "%s activating global ZLINE for %s, " "expiring at %Tu: %s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), zline->zl_mask, zline->zl_expire, zline->zl_reason); log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, "%#C activating global ZLINE for %s, expiring at %Tu: %s", sptr, zline->zl_mask, zline->zl_expire, zline->zl_reason); if (!(flags & ZLINE_LOCAL)) /* don't propagate local changes */ zline_propagate(cptr, sptr, zline); return do_zline(cptr, sptr, zline); }
/** Add a new server jupe. * @param[in] cptr Local client that sent us the jupe. * @param[in] sptr Originator of the jupe. * @param[in] server Server name to jupe. * @param[in] reason Reason for the jupe. * @param[in] expire Jupe duration in seconds. * @param[in] lastmod Last modification timestamp (or NULL). * @param[in] flags Flags to set on jupe. * @return Zero, unless the jupe causes \a cptr to be SQUIT, in which * case CPTR_KILLED. */ int jupe_add(struct Client *cptr, struct Client *sptr, char *server, char *reason, time_t expire, time_t lastmod, unsigned int flags) { struct Jupe *ajupe; assert(0 != server); assert(0 != reason); /* * You cannot set a negative (or zero) expire time, nor can you set an * expiration time for greater than JUPE_MAX_EXPIRE. */ if (expire <= 0 || expire > JUPE_MAX_EXPIRE) { if (!IsServer(cptr) && MyConnect(cptr)) send_reply(cptr, ERR_BADEXPIRE, expire); return 0; } expire += CurrentTime; /* convert from lifetime to timestamp */ /* Inform ops and log it */ sendto_opmask_butone(0, SNO_NETWORK, "%s adding %sJUPE for %s, expiring at " "%Tu: %s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), flags & JUPE_LOCAL ? "local " : "", server, expire + TSoffset, reason); log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE, "%#C adding %sJUPE for %s, expiring at %Tu: %s", sptr, flags & JUPE_LOCAL ? "local " : "", server, expire + TSoffset, reason); /* make the jupe */ ajupe = make_jupe(server, reason, expire, lastmod, flags); propagate_jupe(cptr, sptr, ajupe); return do_jupe(cptr, sptr, ajupe); /* remove server if necessary */ }
/** Mark a client as dead, even if they are not the current message source. * This is done by setting the DEADSOCKET flag on the user and letting the * main loop perform the actual exit logic. * @param[in,out] to Client being killed. * @param[in] notice Message for local opers. */ static void dead_link(struct Client *to, char *notice) { SetFlag(to, FLAG_DEADSOCKET); /* * If because of BUFFERPOOL problem then clean dbuf's now so that * notices don't hurt operators below. */ DBufClear(&(cli_recvQ(to))); MsgQClear(&(cli_sendQ(to))); client_drop_sendq(cli_connect(to)); /* * Keep a copy of the last comment, for later use... */ ircd_strncpy(cli_info(to), notice, REALLEN); if (!IsUser(to) && !IsUnknown(to) && !HasFlag(to, FLAG_CLOSING)) sendto_opmask_butone(0, SNO_OLDSNO, "%s for %s", cli_info(to), cli_name(to)); Debug((DEBUG_ERROR, cli_info(to))); }
/* * ms_nick - server message handler for nicks * parv[0] = sender prefix * parv[1] = nickname * * If from server, source is client: * parv[2] = timestamp * * Source is server: * parv[2] = hopcount * parv[3] = timestamp * parv[4] = username * parv[5] = hostname * parv[6] = umode (optional) * parv[parc-3] = IP# <- Only Protocol >= 10 * parv[parc-2] = YXX, numeric nick <- Only Protocol >= 10 * parv[parc-1] = info * parv[0] = server */ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; char nick[NICKLEN + 2]; time_t lastnick = 0; int differ = 1; const char *type; assert(0 != cptr); assert(0 != sptr); assert(IsServer(cptr)); if ((IsServer(sptr) && parc < 8) || parc < 3) { sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C", parv[1], cptr); return need_more_params(sptr, "NICK"); } ircd_strncpy(nick, parv[1], NICKLEN); nick[NICKLEN] = '\0'; if (IsServer(sptr)) { lastnick = atoi(parv[3]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(sptr)->lag = TStime() - lastnick; } else { lastnick = atoi(parv[2]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick; } /* * If do_nick_name() returns a null name OR if the server sent a nick * name and do_nick_name() changed it in some way (due to rules of nick * creation) then reject it. If from a server and we reject it, * and KILL it. -avalon 4/4/92 */ if (!do_nick_name(nick) || strcmp(nick, parv[1])) { send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]); ++ServerStats->is_kill; sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1], parv[0], cptr); sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])", IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1], nick, cli_name(cptr)); if (!IsServer(sptr)) { /* * bad nick _change_ */ sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)", parv[0], cli_name(&me), cli_name(cptr), parv[0], cli_user(sptr) ? cli_username(sptr) : "", cli_user(sptr) ? cli_name(cli_user(sptr)->server) : cli_name(cptr)); } return 0; } /* Check against nick name collisions. */ if ((acptr = FindClient(nick)) == NULL) /* No collisions, all clear... */ return set_nick_name(cptr, sptr, nick, parc, parv); /* * If acptr == sptr, then we have a client doing a nick * change between *equivalent* nicknames as far as server * is concerned (user is changing the case of his/her * nickname or somesuch) */ if (acptr == sptr) { if (strcmp(cli_name(acptr), nick) != 0) /* Allows change of case in his/her nick */ return set_nick_name(cptr, sptr, nick, parc, parv); else /* Setting their nick to what it already is? Ignore it. */ return 0; } /* now we know we have a real collision. */ /* * Note: From this point forward it can be assumed that * acptr != sptr (point to different client structures). */ assert(acptr != sptr); /* * If the older one is "non-person", the new entry is just * allowed to overwrite it. Just silently drop non-person, * and proceed with the nick. This should take care of the * "dormant nick" way of generating collisions... */ if (IsUnknown(acptr) && MyConnect(acptr)) { ServerStats->is_ref++; IPcheck_connect_fail(acptr); exit_client(cptr, acptr, &me, "Overridden by other sign on"); return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Decide, we really have a nick collision and deal with it */ /* * NICK was coming from a server connection. * This means we have a race condition (two users signing on * at the same time), or two net fragments reconnecting with the same nick. * The latter can happen because two different users connected * or because one and the same user switched server during a net break. * If the TimeStamps are equal, we kill both (or only 'new' * if it was a ":server NICK new ..."). * Otherwise we kill the youngest when user@host differ, * or the oldest when they are the same. * We treat user and ~user as different, because if it wasn't * a faked ~user the AUTH wouldn't have added the '~'. * --Run * */ if (IsServer(sptr)) { struct irc_in_addr ip; /* * A new NICK being introduced by a neighbouring * server (e.g. message type ":server NICK new ..." received) * * compare IP address and username */ base64toip(parv[parc - 3], &ip); differ = (0 != memcmp(&cli_ip(acptr), &ip, sizeof(cli_ip(acptr)))) || (0 != ircd_strcmp(cli_user(acptr)->username, parv[4])); sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- " "%C %Tu (%s user@host))", acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick, differ ? "Different" : "Same"); } else { /* * A NICK change has collided (e.g. message type ":old NICK new"). * * compare IP address and username */ differ = (0 != memcmp(&cli_ip(acptr), &cli_ip(sptr), sizeof(cli_ip(acptr)))) || (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username)); sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to " "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick); } type = differ ? "overruled by older nick" : "nick collision from same user@host"; /* * Now remove (kill) the nick on our side if it is the youngest. * If no timestamp was received, we ignore the incoming nick * (and expect a KILL for our legit nick soon ): * When the timestamps are equal we kill both nicks. --Run * acptr->from != cptr should *always* be true (?). * * This exits the client sending the NICK message */ if ((differ && lastnick >= cli_lastnick(acptr)) || (!differ && lastnick <= cli_lastnick(acptr))) { ServerStats->is_kill++; if (!IsServer(sptr)) { /* If this was a nick change and not a nick introduction, we * need to ensure that we remove our record of the client, and * send a KILL to the whole network. */ assert(!MyConnect(sptr)); /* Inform the rest of the net... */ sendcmdto_serv_butone(&me, CMD_KILL, 0, "%C :%s (%s)", sptr, cli_name(&me), type); /* Don't go sending off a QUIT message... */ SetFlag(sptr, FLAG_KILLED); /* Remove them locally. */ exit_client_msg(cptr, sptr, &me, "Killed (%s (%s))", feature_str(FEAT_HIS_SERVERNAME), type); } else { /* If the origin is a server, this was a new client, so we only * send the KILL in the direction it came from. We have no * client record that we would have to clean up. */ sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s)", parv[parc - 2], cli_name(&me), type); } /* If the timestamps differ and we just killed sptr, we don't need to kill * acptr as well. */ if (lastnick != cli_lastnick(acptr)) return 0; } /* Tell acptr why we are killing it. */ send_reply(acptr, ERR_NICKCOLLISION, nick); ServerStats->is_kill++; SetFlag(acptr, FLAG_KILLED); /* * This exits the client we had before getting the NICK message */ sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (%s)", acptr, feature_str(FEAT_HIS_SERVERNAME), type); exit_client_msg(cptr, acptr, &me, "Killed (%s (%s))", feature_str(FEAT_HIS_SERVERNAME), type); if (lastnick == cli_lastnick(acptr)) return 0; if (sptr == NULL) return 0; return set_nick_name(cptr, sptr, nick, parc, parv); }
/* * ms_nick - server message handler for nicks * parv[0] = sender prefix * parv[1] = nickname * * If from server, source is client: * parv[2] = timestamp * * Source is server: * parv[2] = hopcount * parv[3] = timestamp * parv[4] = username * parv[5] = hostname * parv[6] = umode (optional) * parv[parc-3] = IP# <- Only Protocol >= 10 * parv[parc-2] = YXX, numeric nick <- Only Protocol >= 10 * parv[parc-1] = info * parv[0] = server */ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client* acptr; char nick[NICKLEN + 2]; time_t lastnick = 0; int differ = 1; int samelastnick = 0; assert(0 != cptr); assert(0 != sptr); assert(IsServer(cptr)); if ((IsServer(sptr) && parc < 8) || parc < 3) { sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C", parv[1], cptr); return need_more_params(sptr, "NICK"); } ircd_strncpy(nick, parv[1], NICKLEN); nick[NICKLEN] = '\0'; if (IsServer(sptr)) { lastnick = atoi(parv[3]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(sptr)->lag = TStime() - lastnick; } else { lastnick = atoi(parv[2]); if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick; } /* * If do_nick_name() returns a null name OR if the server sent a nick * name and do_nick_name() changed it in some way (due to rules of nick * creation) then reject it. If from a server and we reject it, * and KILL it. -avalon 4/4/92 */ if (0 == do_nick_name(nick) || 0 != strcmp(nick, parv[1])) { send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]); ++ServerStats->is_kill; sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1], parv[0], cptr); sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])", IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1], nick, cli_name(cptr)); if (!IsServer(sptr)) { /* * bad nick _change_ */ sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)", parv[0], cli_name(&me), cli_name(cptr), parv[0], cli_user(sptr) ? cli_username(sptr) : "", cli_user(sptr) ? cli_name(cli_user(sptr)->server) : cli_name(cptr)); } return 0; } /* * Check against nick name collisions. * * Put this 'if' here so that the nesting goes nicely on the screen :) * We check against server name list before determining if the nickname * is present in the nicklist (due to the way the below for loop is * constructed). -avalon */ assert(NULL == strchr(nick,'.')); acptr = FindClient(nick); if (!acptr) { /* * No collisions, all clear... */ return set_nick_name(cptr, sptr, nick, parc, parv); } assert(0 != acptr); /* * If acptr == sptr, then we have a client doing a nick * change between *equivalent* nicknames as far as server * is concerned (user is changing the case of his/her * nickname or somesuch) */ if (acptr == sptr) { if (strcmp(cli_name(acptr), nick) == 0) /* * This is just ':old NICK old' type thing. * Just forget the whole thing here. There is * no point forwarding it to anywhere, * especially since servers prior to this * version would treat it as nick collision. */ return 0; /* NICK Message ignored */ else /* * Allows change of case in his/her nick */ return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Note: From this point forward it can be assumed that * acptr != sptr (point to different client structures). */ assert(acptr != sptr); /* * If the older one is "non-person", the new entry is just * allowed to overwrite it. Just silently drop non-person, * and proceed with the nick. This should take care of the * "dormant nick" way of generating collisions... */ if (IsUnknown(acptr) && MyConnect(acptr)) { ++ServerStats->is_ref; IPcheck_connect_fail(cli_ip(acptr)); exit_client(cptr, acptr, &me, "Overridden by other sign on"); return set_nick_name(cptr, sptr, nick, parc, parv); } /* * Decide, we really have a nick collision and deal with it */ /* * NICK was coming from a server connection. * This means we have a race condition (two users signing on * at the same time), or two net fragments reconnecting with the same nick. * The latter can happen because two different users connected * or because one and the same user switched server during a net break. * If the TimeStamps are equal, we kill both (or only 'new' * if it was a ":server NICK new ..."). * Otherwise we kill the youngest when user@host differ, * or the oldest when they are the same. * We treat user and ~user as different, because if it wasn't * a faked ~user the AUTH wouldn't have added the '~'. * --Run * */ if (IsServer(sptr)) { /* * A new NICK being introduced by a neighbouring * server (e.g. message type ":server NICK new ..." received) * * compare IP address and username */ differ = (cli_ip(acptr).s_addr != htonl(base64toint(parv[parc - 3]))) || (0 != ircd_strcmp(cli_user(acptr)->username, parv[4])); sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- " "%C %Tu (%s user@host))", acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick, differ ? "Different" : "Same"); } else { /* * A NICK change has collided (e.g. message type ":old NICK new"). * * compare IP address and username */ differ = (cli_ip(acptr).s_addr != cli_ip(sptr).s_addr) || (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username)); sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to " "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr), cli_lastnick(acptr), cptr, lastnick); } /* * Now remove (kill) the nick on our side if it is the youngest. * If no timestamp was received, we ignore the incoming nick * (and expect a KILL for our legit nick soon ): * When the timestamps are equal we kill both nicks. --Run * acptr->from != cptr should *always* be true (?). * * This exits the client sending the NICK message */ if (cli_from(acptr) != cptr) { if ((differ && lastnick >= cli_lastnick(acptr)) || (!differ && lastnick <= cli_lastnick(acptr))) { if (!IsServer(sptr)) { ++ServerStats->is_kill; sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (Nick collision)", sptr, cli_name(&me)); assert(!MyConnect(sptr)); SetFlag(sptr, FLAG_KILLED); exit_client_msg(cptr, sptr, &me, "Killed (%s (Nick collision))", feature_str(FEAT_HIS_SERVERNAME)); sptr = 0; /* Make sure we don't use the dead client */ } else { /* We need to kill this incoming client, which hasn't been properly registered yet. * Send a KILL message upstream to the server it came from */ sendcmdto_one(&me, CMD_KILL, sptr, "%s :%s (Nick collision)", parv[parc-2], cli_name(&me)); } /* If the two have the same TS then we want to kill both sides, so * don't leave yet! */ if (lastnick != cli_lastnick(acptr)) return 0; /* Ignore the NICK */ } send_reply(acptr, ERR_NICKCOLLISION, nick); } ++ServerStats->is_kill; SetFlag(acptr, FLAG_KILLED); if (lastnick == cli_lastnick(acptr)) samelastnick = 1; /* * This exits the client we had before getting the NICK message */ if (differ) { sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (older nick " "overruled)", acptr, cli_name(&me)); if (MyConnect(acptr)) { sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (older " "nick overruled))", feature_str(FEAT_HIS_SERVERNAME)); sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick " "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME)); } exit_client_msg(cptr, acptr, &me, "Killed (%s (older nick " "overruled))", feature_str(FEAT_HIS_SERVERNAME)); } else { sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (nick collision from " "same user@host)", acptr, cli_name(&me)); if (MyConnect(acptr)) { sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (nick " "collision from same user@host))", feature_str(FEAT_HIS_SERVERNAME)); sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick " "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME)); } exit_client_msg(cptr, acptr, &me, "Killed (%s (nick collision from " "same user@host))", feature_str(FEAT_HIS_SERVERNAME)); } if (samelastnick) return 0; assert(0 != sptr); return set_nick_name(cptr, sptr, nick, parc, parv); }
/** Create a new Z-line and add it to global lists. * \a ipmask must be an IP mask to create an IP-based ban. * * @param[in] cptr Client that sent us the Z-line. * @param[in] sptr Client that originated the Z-line. * @param[in] ipmask Text mask for the Z-line. * @param[in] reason Reason for Z-line. * @param[in] expire Expiration time of Z-line. * @param[in] lastmod Last modification time of Z-line. * @param[in] lifetime Lifetime of Z-line. * @param[in] flags Bitwise combination of ZLINE_* flags. * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal. */ int zline_add(struct Client *cptr, struct Client *sptr, char *ipmask, char *reason, time_t expire, time_t lastmod, time_t lifetime, unsigned int flags) { struct Zline *azline; char imask[HOSTLEN + 2]; char *mask; int tmp; assert(0 != ipmask); assert(0 != reason); assert(((flags & (ZLINE_GLOBAL | ZLINE_LOCAL)) == ZLINE_GLOBAL) || ((flags & (ZLINE_GLOBAL | ZLINE_LOCAL)) == ZLINE_LOCAL)); Debug((DEBUG_DEBUG, "zline_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu " "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), ipmask, reason, expire, lastmod, lifetime, flags)); mask = ipmask; if (sizeof(imask) < ircd_snprintf(0, imask, sizeof(imask), "%s", mask)) return send_reply(sptr, ERR_LONGMASK); else if (MyUser(sptr) || (IsUser(sptr) && flags & ZLINE_LOCAL)) { switch (zline_checkmask(mask)) { case CHECK_OVERRIDABLE: /* oper overrided restriction */ if (flags & ZLINE_OPERFORCE) break; /*FALLTHROUGH*/ case CHECK_REJECTED: return send_reply(sptr, ERR_MASKTOOWIDE, imask); break; } if ((tmp = count_users(imask, flags)) >= feature_int(FEAT_ZLINEMAXUSERCOUNT) && !(flags & ZLINE_OPERFORCE)) return send_reply(sptr, ERR_TOOMANYUSERS, tmp); } if (!check_if_ipmask(ipmask)) return send_reply(sptr, ERR_INVALIDMASK); /* * You cannot set a negative (or zero) expire time, nor can you set an * expiration time for greater than ZLINE_MAX_EXPIRE. */ if (!(flags & ZLINE_FORCE) && (expire <= TStime() || expire > TStime() + ZLINE_MAX_EXPIRE)) { if (!IsServer(sptr) && MyConnect(sptr)) send_reply(sptr, ERR_BADEXPIRE, expire); return 0; } else if (expire <= TStime()) { /* This expired Z-line was forced to be added, so mark it inactive. */ flags &= ~ZLINE_ACTIVE; } if (!lifetime) /* no lifetime set, use expiration time */ lifetime = expire; /* lifetime is already an absolute timestamp */ /* Inform ops... */ sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE : SNO_AUTO, "%s adding %s%s ZLINE for %s, expiring at " "%Tu: %s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), (flags & ZLINE_ACTIVE) ? "" : "deactivated ", (flags & ZLINE_LOCAL) ? "local" : "global", mask, expire, reason); /* and log it */ log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, "%#C adding %s ZLINE for %s, expiring at %Tu: %s", sptr, flags & ZLINE_LOCAL ? "local" : "global", mask, expire, reason); /* make the zline */ azline = make_zline(mask, reason, expire, lastmod, lifetime, flags); /* since we've disabled overlapped Z-line checking, azline should * never be NULL... */ assert(azline); zline_propagate(cptr, sptr, azline); return do_zline(cptr, sptr, azline); /* knock off users if necessary */ }
/* * ms_kick - server message handler * * parv[0] = sender prefix * parv[1] = channel * parv[2] = client to kick * parv[parc-1] = kick comment */ int ms_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *who; struct Channel *chptr; struct Membership *member = 0, *sptr_link = 0; char *name, *comment; ClrFlag(sptr, FLAG_TS8); if (parc < 3 || *parv[1] == '\0') return need_more_params(sptr, "KICK"); name = parv[1]; comment = parv[parc - 1]; /* figure out who gets kicked from what */ if (IsLocalChannel(name) || !(chptr = get_channel(sptr, name, CGT_NO_CREATE)) || !(who = findNUser(parv[2]))) return 0; /* We go ahead and pass on the KICK for users not on the channel */ member = find_member_link(chptr, who); if (member && IsZombie(member)) { /* We might get a KICK from a zombie's own server because the user * net-rode during a burst (which always generates a KICK) *and* * was kicked via another server. In that case, we must remove * the user from the channel. */ if (sptr == cli_user(who)->server) { remove_user_from_channel(who, chptr); } /* Otherwise, we treat zombies like they are not channel members. */ member = 0; } /* Send HACK notice, but not for servers in BURST */ /* 2002-10-17: Don't send HACK if the users local server is kicking them */ if (IsServer(sptr) && !IsBurstOrBurstAck(sptr) && sptr!=cli_user(who)->server) sendto_opmask_butone(0, SNO_HACK4, "HACK: %C KICK %H %C %s", sptr, chptr, who, comment); /* Unless someone accepted it downstream (or the user isn't on the channel * here), if kicker is not on channel, or if kicker is not a channel * operator, bounce the kick */ if (!IsServer(sptr) && member && cli_from(who) != cptr && (!(sptr_link = find_member_link(chptr, sptr)) || !IsChanOp(sptr_link))) { sendto_opmask_butone(0, SNO_HACK2, "HACK: %C KICK %H %C %s", sptr, chptr, who, comment); sendcmdto_one(who, CMD_JOIN, cptr, "%H", chptr); /* Reop/revoice member */ if (IsChanOp(member) || HasVoice(member)) { struct ModeBuf mbuf; modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_SERVER | /* Send mode to a server */ MODEBUF_DEST_DEOP | /* Deop the source */ MODEBUF_DEST_BOUNCE)); /* And bounce the MODE */ if (IsChanOp(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, who, OpLevel(member)); if (HasVoice(member)) modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who, MAXOPLEVEL + 1); modebuf_flush(&mbuf); } } else { /* Propagate kick... */ sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who, comment); if (member) { /* and tell the channel about it */ if (IsDelayedJoin(member)) { if (MyUser(who)) sendcmdto_one(IsServer(sptr) ? &his : sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment); } else { sendcmdto_channel_butserv_butone(IsServer(sptr) ? &his : sptr, CMD_KICK, chptr, NULL, 0, "%H %C :%s", chptr, who, comment); } make_zombie(member, who, cptr, sptr, chptr); } } return 0; }
/** Handle a SERVER message from another server. * * \a parv has the following elements: * \li \a parv[1] is the server name * \li \a parv[2] is the hop count to the server * \li \a parv[3] is the start timestamp for the server * \li \a parv[4] is the link timestamp * \li \a parv[5] is the protocol version (P10 or J10) * \li \a parv[6] is the numnick mask for the server * \li \a parv[7] is a string of flags like +hs to mark hubs and services * \li \a parv[\a parc - 1] is the server description * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int ms_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { int i; char* host; struct Client* acptr; struct Client* bcptr; int hop; int ret; unsigned short prot; time_t start_timestamp; time_t timestamp; if (parc < 8) { return need_more_params(sptr, "SERVER"); return exit_client(cptr, cptr, &me, "Need more parameters"); } host = clean_servername(parv[1]); if (!host) { sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s", host, cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); } /* * Detect protocol */ hop = atoi(parv[2]); start_timestamp = atoi(parv[3]); timestamp = atoi(parv[4]); prot = parse_protocol(parv[5]); if (!prot) return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); else if (prot < atoi(MINOR_PROTOCOL)) return exit_new_server(cptr, sptr, host, timestamp, "Incompatible protocol: %s", parv[5]); Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)", host, parv[4], start_timestamp, cli_serv(&me)->timestamp)); if (timestamp < OLDEST_TS) return exit_client_msg(cptr, sptr, &me, "Bogus timestamps (%s %s)", parv[3], parv[4]); if (parv[parc - 1][0] == '\0') return exit_client_msg(cptr, cptr, &me, "No server info specified for %s", host); ret = check_loop_and_lh(cptr, sptr, NULL, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, parv[5][0] == 'J'); if (ret != 1) return ret; /* * Server is informing about a new server behind * this link. Create REMOTE server structure, * add it to list and propagate word to my other * server links... */ acptr = make_client(cptr, STAT_SERVER); make_server(acptr); cli_serv(acptr)->prot = prot; cli_serv(acptr)->timestamp = timestamp; cli_hopcount(acptr) = hop; ircd_strncpy(cli_name(acptr), host, HOSTLEN); ircd_strncpy(cli_info(acptr), parv[parc-1], REALLEN); cli_serv(acptr)->up = sptr; cli_serv(acptr)->updown = add_dlink(&(cli_serv(sptr))->down, acptr); /* Use cptr, because we do protocol 9 -> 10 translation for numeric nicks ! */ SetServerYXX(cptr, acptr, parv[6]); /* Attach any necessary UWorld config items. */ attach_confs_byhost(cptr, host, CONF_UWORLD); if (*parv[7] == '+') set_server_flags(acptr, parv[7] + 1); Count_newremoteserver(UserStats); if (Protocol(acptr) < 10) SetFlag(acptr, FLAG_TS8); add_client_to_list(acptr); hAddClient(acptr); if (*parv[5] == 'J') { SetBurst(acptr); SetJunction(acptr); for (bcptr = cli_serv(acptr)->up; !IsMe(bcptr); bcptr = cli_serv(bcptr)->up) if (IsBurstOrBurstAck(bcptr)) break; if (IsMe(bcptr)) sendto_opmask_butone(0, SNO_NETWORK, "Net junction: %s %s", cli_name(sptr), cli_name(acptr)); } /* * Old sendto_serv_but_one() call removed because we now need to send * different names to different servers (domain name matching). */ for (i = 0; i <= HighestFd; i++) { if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) || bcptr == cptr || IsMe(bcptr)) continue; if (0 == match(cli_name(&me), cli_name(acptr))) continue; sendcmdto_one(sptr, CMD_SERVER, bcptr, "%s %d 0 %s %s %s%s +%s%s%s :%s", cli_name(acptr), hop + 1, parv[4], parv[5], NumServCap(acptr), IsHub(acptr) ? "h" : "", IsService(acptr) ? "s" : "", IsIPv6(acptr) ? "6" : "", cli_info(acptr)); } return 0; }
/** Handle a SERVER message from an unregistered connection. * * \a parv has the following elements: * \li \a parv[1] is the server name * \li \a parv[2] is the hop count to the server * \li \a parv[3] is the start timestamp for the server * \li \a parv[4] is the link timestamp * \li \a parv[5] is the protocol version (P10 or J10) * \li \a parv[6] is the numnick mask for the server * \li \a parv[7] is a string of flags like +hs to mark hubs and services * \li \a parv[\a parc - 1] is the server description * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* host; struct ConfItem* aconf; struct Jupe* ajupe; int hop; int ret; unsigned short prot; time_t start_timestamp; time_t timestamp; time_t recv_time; time_t ghost; if (IsUserPort(cptr)) return exit_client_msg(cptr, cptr, &me, "Cannot connect a server to a user port"); if (parc < 8) { need_more_params(sptr, "SERVER"); return exit_client(cptr, cptr, &me, "Need more parameters"); } host = clean_servername(parv[1]); if (!host) { sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s", host, cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); } if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe)) return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe)); /* check connection rules */ if (0 != conf_eval_crule(host, CRULE_ALL)) { ServerStats->is_ref++; sendto_opmask_butone(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr)); return exit_client(cptr, cptr, &me, "Disallowed by connection rule"); } log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", host, cli_sockhost(cptr), cli_sock_ip(cptr)); /* * Detect protocol */ hop = atoi(parv[2]); start_timestamp = atoi(parv[3]); timestamp = atoi(parv[4]); prot = parse_protocol(parv[5]); if (!prot) return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); else if (prot < atoi(MINOR_PROTOCOL)) return exit_new_server(cptr, sptr, host, timestamp, "Incompatible protocol: %s", parv[5]); Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)", host, parv[4], start_timestamp, cli_serv(&me)->timestamp)); if (timestamp < OLDEST_TS || start_timestamp < OLDEST_TS) return exit_client_msg(cptr, sptr, &me, "Bogus timestamps (%s %s)", parv[3], parv[4]); /* If the server had a different name before, change it. */ if (!EmptyString(cli_name(cptr)) && (IsUnknown(cptr) || IsHandshake(cptr)) && 0 != ircd_strcmp(cli_name(cptr), host)) hChangeClient(cptr, host); ircd_strncpy(cli_name(cptr), host, HOSTLEN); ircd_strncpy(cli_info(cptr), parv[parc-1][0] ? parv[parc-1] : cli_name(&me), REALLEN); cli_hopcount(cptr) = hop; if (conf_check_server(cptr)) { ++ServerStats->is_ref; sendto_opmask_butone(0, SNO_OLDSNO, "Received unauthorized connection " "from %s.", cli_name(cptr)); log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "Received unauthorized " "connection from %C [%s]", cptr, ircd_ntoa(&cli_ip(cptr))); return exit_client(cptr, cptr, &me, "No Connect block"); } host = cli_name(cptr); update_load(); if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) { ++ServerStats->is_ref; sendto_opmask_butone(0, SNO_OLDSNO, "Access denied. No conf line for " "server %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "Access denied. No conf line for server %s", cli_name(cptr)); } if (*aconf->passwd && !!strcmp(aconf->passwd, cli_passwd(cptr))) { ++ServerStats->is_ref; sendto_opmask_butone(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "No Access (passwd mismatch) %s", cli_name(cptr)); } memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); ret = check_loop_and_lh(cptr, sptr, &ghost, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, 1); if (ret != 1) return ret; make_server(cptr); cli_serv(cptr)->timestamp = timestamp; cli_serv(cptr)->prot = prot; cli_serv(cptr)->ghost = ghost; memset(cli_privs(cptr), 255, sizeof(struct Privs)); ClrPriv(cptr, PRIV_SET); SetServerYXX(cptr, cptr, parv[6]); /* Attach any necessary UWorld config items. */ attach_confs_byhost(cptr, host, CONF_UWORLD); if (*parv[7] == '+') set_server_flags(cptr, parv[7] + 1); recv_time = TStime(); check_start_timestamp(cptr, timestamp, start_timestamp, recv_time); ret = server_estab(cptr, aconf); if (feature_bool(FEAT_RELIABLE_CLOCK) && abs(cli_serv(cptr)->timestamp - recv_time) > 30) { sendto_opmask_butone(0, SNO_OLDSNO, "Connected to a net with a " "timestamp-clock difference of %Td seconds! " "Used SETTIME to correct this.", timestamp - recv_time); sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(), cli_name(&me)); } return ret; }
/** Modify a global Z-line. * @param[in] cptr Client that sent us the Z-line modification. * @param[in] sptr Client that originated the Z-line modification. * @param[in] zline Z-line being modified. * @param[in] action Resultant status of the Z-line. * @param[in] reason Reason for Z-line. * @param[in] expire Expiration time of Z-line. * @param[in] lastmod Last modification time of Z-line. * @param[in] lifetime Lifetime of Z-line. * @param[in] flags Bitwise combination of ZLINE_* flags. * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal. */ int zline_modify(struct Client *cptr, struct Client *sptr, struct Zline *zline, enum ZlineAction action, char *reason, time_t expire, time_t lastmod, time_t lifetime, unsigned int flags) { char buf[BUFSIZE], *op = ""; int pos = 0, non_auto = 0; assert(zline); assert(!ZlineIsLocal(zline)); Debug((DEBUG_DEBUG, "zline_modify(\"%s\", \"%s\", \"%s\", %s, \"%s\", " "%Tu, %Tu, %Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), zline->zl_mask, action == ZLINE_ACTIVATE ? "ZLINE_ACTIVATE" : (action == ZLINE_DEACTIVATE ? "ZLINE_DEACTIVATE" : (action == ZLINE_LOCAL_ACTIVATE ? "ZLINE_LOCAL_ACTIVATE" : (action == ZLINE_LOCAL_DEACTIVATE ? "ZLINE_LOCAL_DEACTIVATE" : (action == ZLINE_MODIFY ? "ZLINE_MODIFY" : "<UNKNOWN>")))), reason, expire, lastmod, lifetime, flags)); /* First, let's check lastmod... */ if (action != ZLINE_LOCAL_ACTIVATE && action != ZLINE_LOCAL_DEACTIVATE) { if (ZlineLastMod(zline) > lastmod) { /* we have a more recent version */ if (IsBurstOrBurstAck(cptr)) return 0; /* middle of a burst, it'll resync on its own */ return zline_resend(cptr, zline); /* resync the server */ } else if (ZlineLastMod(zline) == lastmod) return 0; /* we have that version of the Z-line... */ } /* All right, we know that there's a change of some sort. What is it? */ /* first, check out the expiration time... */ if ((flags & ZLINE_EXPIRE) && expire) { if (!(flags & ZLINE_FORCE) && (expire <= TStime() || expire > TStime() + ZLINE_MAX_EXPIRE)) { if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */ send_reply(sptr, ERR_BADEXPIRE, expire); return 0; } } else flags &= ~ZLINE_EXPIRE; /* Now check to see if there's any change... */ if ((flags & ZLINE_EXPIRE) && expire == zline->zl_expire) { flags &= ~ZLINE_EXPIRE; /* no change to expiration time... */ expire = 0; } /* Next, check out lifetime--this one's a bit trickier... */ if (!(flags & ZLINE_LIFETIME) || !lifetime) lifetime = zline->zl_lifetime; /* use Z-line lifetime */ lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */ /* OK, let's see which is greater... */ if (lifetime > zline->zl_lifetime) flags |= ZLINE_LIFETIME; /* have to update lifetime */ else { flags &= ~ZLINE_LIFETIME; /* no change to lifetime */ lifetime = 0; } /* Finally, let's see if the reason needs to be updated */ if ((flags & ZLINE_REASON) && reason && !ircd_strcmp(zline->zl_reason, reason)) flags &= ~ZLINE_REASON; /* no changes to the reason */ /* OK, now let's take a look at the action... */ if ((action == ZLINE_ACTIVATE && (zline->zl_flags & ZLINE_ACTIVE)) || (action == ZLINE_DEACTIVATE && !(zline->zl_flags & ZLINE_ACTIVE)) || (action == ZLINE_LOCAL_ACTIVATE && (zline->zl_state == ZLOCAL_ACTIVATED)) || (action == ZLINE_LOCAL_DEACTIVATE && (zline->zl_state == ZLOCAL_DEACTIVATED)) || /* can't activate an expired Z-line */ IRCD_MAX(zline->zl_expire, expire) <= TStime()) action = ZLINE_MODIFY; /* no activity state modifications */ Debug((DEBUG_DEBUG, "About to perform changes; flags 0x%04x, action %s", flags, action == ZLINE_ACTIVATE ? "ZLINE_ACTIVATE" : (action == ZLINE_DEACTIVATE ? "ZLINE_DEACTIVATE" : (action == ZLINE_LOCAL_ACTIVATE ? "ZLINE_LOCAL_ACTIVATE" : (action == ZLINE_LOCAL_DEACTIVATE ? "ZLINE_LOCAL_DEACTIVATE" : (action == ZLINE_MODIFY ? "ZLINE_MODIFY" : "<UNKNOWN>")))))); /* If there are no changes to perform, do no changes */ if (!(flags & ZLINE_UPDATE) && action == ZLINE_MODIFY) return 0; /* Now we know what needs to be changed, so let's process the changes... */ /* Start by updating lastmod, if indicated... */ if (action != ZLINE_LOCAL_ACTIVATE && action != ZLINE_LOCAL_DEACTIVATE) zline->zl_lastmod = lastmod; /* Then move on to activity status changes... */ switch (action) { case ZLINE_ACTIVATE: /* Globally activating Z-line */ zline->zl_flags |= ZLINE_ACTIVE; /* make it active... */ zline->zl_state = ZLOCAL_GLOBAL; /* reset local activity state */ pos += ircd_snprintf(0, buf, sizeof(buf), " globally activating Z-line"); op = "+"; /* operation for Z-line propagation */ break; case ZLINE_DEACTIVATE: /* Globally deactivating Z-line */ zline->zl_flags &= ~ZLINE_ACTIVE; /* make it inactive... */ zline->zl_state = ZLOCAL_GLOBAL; /* reset local activity state */ pos += ircd_snprintf(0, buf, sizeof(buf), " globally deactivating Z-line"); op = "-"; /* operation for Z-line propagation */ break; case ZLINE_LOCAL_ACTIVATE: /* Locally activating Z-line */ zline->zl_state = ZLOCAL_ACTIVATED; /* make it locally active */ pos += ircd_snprintf(0, buf, sizeof(buf), " locally activating Z-line"); break; case ZLINE_LOCAL_DEACTIVATE: /* Locally deactivating Z-line */ zline->zl_state = ZLOCAL_DEACTIVATED; /* make it locally inactive */ pos += ircd_snprintf(0, buf, sizeof(buf), " locally deactivating Z-line"); break; case ZLINE_MODIFY: /* no change to activity status */ break; } /* Handle expiration changes... */ if (flags & ZLINE_EXPIRE) { zline->zl_expire = expire; /* save new expiration time */ if (pos < BUFSIZE) pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos, "%s%s changing expiration time to %Tu", pos ? ";" : "", pos && !(flags & (ZLINE_LIFETIME | ZLINE_REASON)) ? " and" : "", expire); } /* Next, handle lifetime changes... */ if (flags & ZLINE_LIFETIME) { zline->zl_lifetime = lifetime; /* save new lifetime */ if (pos < BUFSIZE) pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos, "%s%s extending record lifetime to %Tu", pos ? ";" : "", pos && !(flags & ZLINE_REASON) ? " and" : "", lifetime); } /* Now, handle reason changes... */ if (flags & ZLINE_REASON) { non_auto = non_auto || ircd_strncmp(zline->zl_reason, "AUTO", 4); MyFree(zline->zl_reason); /* release old reason */ DupString(zline->zl_reason, reason); /* store new reason */ if (pos < BUFSIZE) pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos, "%s%s changing reason to \"%s\"", pos ? ";" : "", pos ? " and" : "", reason); } /* All right, inform ops... */ non_auto = non_auto || ircd_strncmp(zline->zl_reason, "AUTO", 4); sendto_opmask_butone(0, non_auto ? SNO_GLINE : SNO_AUTO, "%s modifying global ZLINE for %s:%s", (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? cli_name(sptr) : cli_name((cli_user(sptr))->server), zline->zl_mask, buf); /* and log the change */ log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, "%#C modifying global ZLINE for %s:%s", sptr, zline->zl_mask, buf); /* We'll be simple for this release, but we can update this to change * the propagation syntax on future updates */ if (action != ZLINE_LOCAL_ACTIVATE && action != ZLINE_LOCAL_DEACTIVATE) sendcmdto_serv_butone(sptr, CMD_ZLINE, cptr, "* %s%s%s %Tu %Tu %Tu :%s", flags & ZLINE_OPERFORCE ? "!" : "", op, zline->zl_mask, zline->zl_expire - TStime(), zline->zl_lastmod, zline->zl_lifetime, zline->zl_reason); /* OK, let's do the Z-line... */ return do_zline(cptr, sptr, zline); }
/* * m_oper - generic message handler */ int m_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct ConfItem* aconf; char* name; char* password; assert(0 != cptr); assert(cptr == sptr); name = parc > 1 ? parv[1] : 0; password = parc > 2 ? parv[2] : 0; if (EmptyString(name) || EmptyString(password)) return need_more_params(sptr, "OPER"); aconf = find_conf_exact(name, sptr, CONF_OPERATOR); if (!aconf || IsIllegal(aconf)) { send_reply(sptr, ERR_NOOPERHOST); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed staff authentication attempt by %s (%s@%s)", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); return 0; } assert(0 != (aconf->status & CONF_OPERATOR)); if (oper_password_match(password, aconf->passwd)) { struct Flags old_mode = cli_flags(sptr); if (ACR_OK != attach_conf(sptr, aconf)) { send_reply(sptr, ERR_NOOPERHOST); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed staff authentication attempt by %s " "(%s@%s)", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); return 0; } SetLocOp(sptr); client_set_privs(sptr, aconf); if (HasPriv(sptr, PRIV_PROPAGATE)) { ClearLocOp(sptr); SetOper(sptr); ++UserStats.opers; } cli_handler(cptr) = OPER_HANDLER; SetFlag(sptr, FLAG_WALLOP); SetFlag(sptr, FLAG_SERVNOTICE); SetFlag(sptr, FLAG_DEBUG); set_snomask(sptr, SNO_OPERDEFAULT, SNO_ADD); cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */ send_umode_out(cptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE)); send_reply(sptr, RPL_YOUREOPER); sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) has authenticated as staff", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#C)", name, sptr); } else { send_reply(sptr, ERR_PASSWDMISMATCH); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed staff authentication attempt by %s (%s@%s)", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); } return 0; }
/* * mo_settime - oper message handler * * parv[0] = sender prefix * parv[1] = new time * parv[2] = servername (Only used when sptr is an Oper). */ int mo_settime(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { time_t t; long dt; static char tbuf[11]; /* Must be a global oper */ if (!IsOper(sptr)) return send_reply(sptr, ERR_NOPRIVILEGES); if (parc < 2) /* verify argument count */ return need_more_params(sptr, "SETTIME"); if (parc == 2 && MyUser(sptr)) /* default to me */ parv[parc++] = cli_name(&me); t = atoi(parv[1]); /* convert the time */ /* If we're reliable_clock or if the oper specified a 0 time, use current */ if (!t || feature_bool(FEAT_RELIABLE_CLOCK)) { t = TStime(); ircd_snprintf(0, tbuf, sizeof(tbuf), "%Tu", TStime()); parv[1] = tbuf; } dt = TStime() - t; /* calculate the delta */ if (t < OLDEST_TS || dt < -9000000) /* verify value */ { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :SETTIME: Bad value", sptr); return 0; } /* OK, send the message off to its destination */ if (hunt_server_prio_cmd(sptr, CMD_SETTIME, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME) return 0; if (feature_bool(FEAT_RELIABLE_CLOCK)) /* don't apply settime--reliable */ { if ((dt > 600) || (dt < -600)) sendcmdto_serv_butone(&me, CMD_DESYNCH, 0, ":Bad SETTIME from %s: %Tu " "(delta %ld)", cli_name(sptr), t, dt); if (IsUser(sptr)) /* Let user know we're ignoring him */ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is not set %ld seconds " "%s: RELIABLE_CLOCK is defined", sptr, (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); } else /* tell opers about time change */ { sendto_opmask_butone(0, SNO_OLDSNO, "SETTIME from %s, clock is set %ld " "seconds %s", cli_name(sptr), (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); TSoffset -= dt; /* apply time change */ if (IsUser(sptr)) /* let user know what we did */ sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is set %ld seconds %s", sptr, (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); } return 0; }
/** Handle a JOIN message from a client connection. * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr; struct JoinBuf join; struct JoinBuf create; struct Gline *gline; char *p = 0; char *chanlist; char *name; char *keys; if (parc < 2 || *parv[1] == '\0') return need_more_params(sptr, "JOIN"); joinbuf_init(&join, sptr, cptr, JOINBUF_TYPE_JOIN, 0, 0); joinbuf_init(&create, sptr, cptr, JOINBUF_TYPE_CREATE, 0, TStime()); chanlist = last0(cptr, sptr, parv[1]); /* find last "JOIN 0" */ keys = parv[2]; /* remember where keys are */ for (name = ircd_strtok(&p, chanlist, ","); name; name = ircd_strtok(&p, 0, ",")) { char *key = 0; /* If we have any more keys, take the first for this channel. */ if (!BadPtr(keys) && (keys = strchr(key = keys, ','))) *keys++ = '\0'; /* Empty keys are the same as no keys. */ if (key && !key[0]) key = 0; if (!IsChannelName(name) || !strIsIrcCh(name)) { /* bad channel name */ send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (cli_user(sptr)->joined >= feature_int(FEAT_MAXCHANNELSPERUSER) && !HasPriv(sptr, PRIV_CHAN_LIMIT)) { send_reply(sptr, ERR_TOOMANYCHANNELS, name); break; /* no point processing the other channels */ } /* BADCHANed channel */ if ((gline = gline_find(name, GLINE_BADCHAN | GLINE_EXACT)) && GlineIsActive(gline) && !IsAnOper(sptr)) { send_reply(sptr, ERR_BANNEDFROMCHAN, name); continue; } if (!(chptr = FindChannel(name))) { if (((name[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) || strlen(name) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) { send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } if (!(chptr = get_channel(sptr, name, CGT_CREATE))) continue; /* Try to add the new channel as a recent target for the user. */ if (check_target_limit(sptr, chptr, chptr->chname, 0)) { chptr->members = 0; destruct_channel(chptr); continue; } joinbuf_join(&create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER); } else if (find_member_link(chptr, sptr)) { continue; /* already on channel */ } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) { continue; } else { int flags = CHFL_DEOPPED; int err = 0; /* Check Apass/Upass -- since we only ever look at a single * "key" per channel now, this hampers brute force attacks. */ if (key && !strcmp(key, chptr->mode.apass)) flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; else if (key && !strcmp(key, chptr->mode.upass)) flags = CHFL_CHANOP; else if (chptr->users == 0 && !chptr->mode.apass[0]) { /* Joining a zombie channel (zannel): give ops and increment TS. */ flags = CHFL_CHANOP; chptr->creationtime++; } else if (IsInvited(sptr, chptr)) { /* Invites bypass these other checks. */ } else if (chptr->mode.mode & MODE_INVITEONLY) err = ERR_INVITEONLYCHAN; else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit)) err = ERR_CHANNELISFULL; else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr)) err = ERR_NEEDREGGEDNICK; else if (find_ban(sptr, chptr->banlist)) err = ERR_BANNEDFROMCHAN; else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key))) err = ERR_BADCHANNELKEY; /* An oper with WALK_LCHAN privilege can join a local channel * he otherwise could not join by using "OVERRIDE" as the key. * This will generate a HACK(4) notice, but fails if the oper * could normally join the channel. */ if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) && !(flags & CHFL_CHANOP) && key && !strcmp(key, "OVERRIDE")) { switch (err) { case 0: if (strcmp(chptr->mode.key, "OVERRIDE") && strcmp(chptr->mode.apass, "OVERRIDE") && strcmp(chptr->mode.upass, "OVERRIDE")) { send_reply(sptr, ERR_DONTCHEAT, chptr->chname); continue; } break; case ERR_INVITEONLYCHAN: err = 'i'; break; case ERR_CHANNELISFULL: err = 'l'; break; case ERR_BANNEDFROMCHAN: err = 'b'; break; case ERR_BADCHANNELKEY: err = 'k'; break; case ERR_NEEDREGGEDNICK: err = 'r'; break; default: err = '?'; break; } /* send accountability notice */ if (err) sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " "(overriding +%c)", sptr, chptr, err); err = 0; } /* Is there some reason the user may not join? */ if (err) { switch(err) { case ERR_NEEDREGGEDNICK: send_reply(sptr, ERR_NEEDREGGEDNICK, chptr->chname, feature_str(FEAT_URLREG)); break; default: send_reply(sptr, err, chptr->chname); break; } continue; } joinbuf_join(&join, chptr, flags); if (flags & CHFL_CHANOP) { struct ModeBuf mbuf; /* Always let the server op him: this is needed on a net with older servers because they 'destruct' channels immediately when they become empty without sending out a DESTRUCT message. As a result, they would always bounce a mode (as HACK(2)) when the user ops himself. (There is also no particularly good reason to have the user op himself.) */ modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER); modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL); modebuf_flush(&mbuf); } } del_invite(sptr, chptr); if (chptr->topic[0]) { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */ } joinbuf_flush(&join); /* must be first, if there's a JOIN 0 */ joinbuf_flush(&create); return 0; }
/* * ms_settime - server message handler * * parv[0] = sender prefix * parv[1] = new time * parv[2] = server name (Only used when sptr is an Oper). */ int ms_settime(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { time_t t; long dt; static char tbuf[11]; struct DLink *lp; if (parc < 2) /* verify argument count */ return need_more_params(sptr, "SETTIME"); t = atoi(parv[1]); /* convert time and compute delta */ dt = TStime() - t; /* verify value */ if (t < OLDEST_TS || dt < -9000000) { if (IsServer(sptr)) /* protocol violation if it's from a server */ protocol_violation(sptr, "SETTIME: Bad value (%Tu, delta %ld)", t, dt); else sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :SETTIME: Bad value (%Tu, " "delta %ld)", sptr, t, dt); sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :SETTIME: Bad value", sptr); return 0; } /* reset time... */ if (feature_bool(FEAT_RELIABLE_CLOCK)) { ircd_snprintf(0, tbuf, sizeof(tbuf), "%Tu", TStime()); parv[1] = tbuf; } if (BadPtr(parv[2])) /* spam the network */ { for (lp = cli_serv(&me)->down; lp; lp = lp->next) if (cptr != lp->value.cptr) sendcmdto_prio_one(sptr, CMD_SETTIME, lp->value.cptr, "%s", parv[1]); } else { if (hunt_server_prio_cmd(sptr, CMD_SETTIME, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME) { /* If the destination was *not* me, but I'm RELIABLE_CLOCK and the * delta is more than 30 seconds off, bounce back a corrected * SETTIME */ if (feature_bool(FEAT_RELIABLE_CLOCK) && (dt > 30 || dt < -30)) sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%s %C", parv[1], cptr); return 0; } } if (feature_bool(FEAT_RELIABLE_CLOCK)) { /* don't apply settime--reliable */ if ((dt > 600) || (dt < -600)) sendcmdto_serv_butone(&me, CMD_DESYNCH, 0, ":Bad SETTIME from %s: %Tu " "(delta %ld)", cli_name(sptr), t, dt); /* Let user know we're ignoring him */ if (IsUser(sptr)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is not set %ld " "seconds %s : RELIABLE_CLOCK is defined", sptr, (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); } } else /* tell opers about time change */ { sendto_opmask_butone(0, SNO_OLDSNO, "SETTIME from %s, clock is set %ld " "seconds %s", cli_name(sptr), (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); /* Apply time change... */ TSoffset -= dt; /* Let the issuing user know what we did... */ if (IsUser(sptr)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is set %ld seconds %s", sptr, (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards"); } } return 0; }