void masterhandler(nick *np, int message, void **args) { char *msg; switch(message) { case LU_KILLED: cmnick=NULL; scheduleoneshot(time(NULL)+1, makenick, NULL); break; case LU_CHANMSG: msg=args[2]; if (!ircd_strncmp(msg, "!spawn ",7)) { spawnclones(strtoul(msg+7,NULL,10)); } if (!ircd_strncmp(msg,"!join ",6)) { join(msg+6); } if (!ircd_strncmp(msg,"!spam ", 6)) { spam(strtoul(msg+6, NULL, 10)); } break; } }
/** Set a channel topic or report an error. * @param[in] sptr Original topic setter. * @param[in] cptr Neighbor that sent the topic message. * @param[in] chptr Channel to set topic on. * @param[in] topic New topic. * @param[in] ts Timestamp that topic was set (0 for current time). */ static void do_settopic(struct Client *sptr, struct Client *cptr, struct Channel *chptr, char *topic, time_t ts) { struct Client *from; int newtopic; if (feature_bool(FEAT_HIS_BANWHO) && IsServer(sptr)) from = &his; else from = sptr; /* Note if this is just a refresh of an old topic, and don't * send it to all the clients to save bandwidth. We still send * it to other servers as they may have split and lost the topic. */ newtopic=ircd_strncmp(chptr->topic,topic,TOPICLEN)!=0; /* setting a topic */ ircd_strncpy(chptr->topic, topic, TOPICLEN); ircd_strncpy(chptr->topic_nick, cli_name(from), NICKLEN); chptr->topic_time = ts ? ts : TStime(); /* Fixed in 2.10.11: Don't propagate local topics */ if (!IsLocalChannel(chptr->chname)) sendcmdto_serv(sptr, CMD_TOPIC, cptr, "%H %Tu %Tu :%s", chptr, chptr->creationtime, chptr->topic_time, chptr->topic); if (newtopic) sendcmdto_channel(from, CMD_TOPIC, chptr, NULL, SKIP_SERVERS, "%H :%s", chptr, chptr->topic); /* if this is the same topic as before we send it to the person that * set it (so they knew it went through ok), but don't bother sending * it to everyone else on the channel to save bandwidth */ else if (MyUser(sptr)) sendcmdto_one(sptr, CMD_TOPIC, sptr, "%H :%s", chptr, chptr->topic); }
/** Handle a WALLVOICES message from a local client. * * \a parv has the following elements: * \li \a parv[1] is the name of the channel to which to send * \li \a parv[\a parc - 1] is the message to send * * 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_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; const char *ch; assert(0 != cptr); assert(cptr == sptr); if (parc < 2 || EmptyString(parv[1])) return send_reply(sptr, ERR_NORECIPIENT, "WALLVOICES"); if (parc < 3 || EmptyString(parv[parc - 1])) return send_reply(sptr, ERR_NOTEXTTOSEND); if (IsChannelName(parv[1]) && (chptr = FindChannel(parv[1]))) { if (client_can_send_to_channel(sptr, chptr, 0) && !(chptr->mode.mode & MODE_NONOTICE)) { if ((chptr->mode.mode & MODE_NOPRIVMSGS) && check_target_limit(sptr, chptr, chptr->chname, 0)) return 0; #if 0 /* +cC checks */ if (chptr->mode.mode & MODE_NOCOLOUR) for (ch=parv[parc - 1];*ch;ch++) if (*ch==2 || *ch==3 || *ch==22 || *ch==27 || *ch==31) { return 0; } if ((chptr->mode.mode & MODE_NOCTCP) && ircd_strncmp(parv[parc - 1],"\001ACTION ",8)) for (ch=parv[parc - 1];*ch;) if (*ch++==1) { return 0; } #endif RevealDelayedJoinIfNeeded(sptr, chptr); sendcmdto_channel(sptr, CMD_WALLVOICES, chptr, cptr, SKIP_DEAF | SKIP_BURST | SKIP_NONVOICES, "%H :+ %s", chptr, parv[parc - 1]); } else send_reply(sptr, ERR_CANNOTSENDTOCHAN, parv[1]); } else send_reply(sptr, ERR_NOSUCHCHANNEL, parv[1]); return 0; }
/* * ms_xreply - extension message reply handler * * parv[0] = sender prefix * parv[1] = target server numeric * parv[2] = routing information * parv[3] = extension message reply */ int ms_xreply(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client* acptr; const char* routing; const char* reply; if (parc < 4) /* have enough parameters? */ return need_more_params(sptr, "XREPLY"); routing = parv[2]; reply = parv[3]; /* Look up the target */ if (!(acptr = findNUser(parv[1])) && !(acptr = FindNServer(parv[1]))) return send_reply(sptr, SND_EXPLICIT | ERR_NOSUCHSERVER, "* :Server has disconnected"); /* If it's not to us, forward the reply */ if (!IsMe(acptr)) { sendcmdto_one(sptr, CMD_XREPLY, acptr, "%C %s :%s", acptr, routing, reply); return 0; } /* OK, figure out where to route the message */ if (!ircd_strncmp("iauth:", routing, 6)) { /* Forward the reply to the iauth */ routing += 6; auth_send_xreply(sptr, routing, reply); } else /* If we don't know where to route it, log it and drop it */ log_write(LS_SYSTEM, L_NOTICE, 0, "Received unroutable extension reply " "from %#C to %#C routing %s; message: %s", sptr, acptr, routing, reply); return 0; }
/** Check whether the introduction of a new server would cause a loop * or be disallowed by leaf and hub configuration directives. * @param[in] cptr Neighbor who sent the message. * @param[in] sptr Client that originated the message. * @param[out] ghost If non-NULL, receives ghost timestamp for new server. * @param[in] host Name of new server. * @param[in] numnick Numnick mask of new server. * @param[in] timestamp Claimed link timestamp of new server. * @param[in] hop Number of hops to the new server. * @param[in] junction Non-zero if the new server is still bursting. * @return CPTR_KILLED if \a cptr was SQUIT. 0 if some other server * was SQUIT. 1 if the new server is allowed. */ static int check_loop_and_lh(struct Client* cptr, struct Client *sptr, time_t *ghost, const char *host, const char *numnick, time_t timestamp, unsigned int hop, int junction) { struct Client* acptr; struct Client* LHcptr = NULL; struct ConfItem* lhconf; enum lh_type active_lh_line = ALLOWED; int ii; if (ghost) *ghost = 0; /* * Calculate type of connect limit and applicable config item. */ lhconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER); assert(lhconf != NULL); if (ghost) { if (!feature_bool(FEAT_HUB)) for (ii = 0; ii <= HighestFd; ii++) if (LocalClientArray[ii] && IsServer(LocalClientArray[ii])) { active_lh_line = I_AM_NOT_HUB; break; } } else if (hop > lhconf->maximum) { /* Because "maximum" should be 0 for non-hub links, check whether * there is a hub mask -- if not, complain that the server isn't * allowed to hub. */ active_lh_line = lhconf->hub_limit ? MAX_HOPS_EXCEEDED : NOT_ALLOWED_TO_HUB; } else if (lhconf->hub_limit && match(lhconf->hub_limit, host)) { struct Client *ac3ptr; active_lh_line = NOT_ALLOWED_TO_HUB; if (junction) for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (IsJunction(ac3ptr)) { LHcptr = ac3ptr; break; } } /* * We want to find IsConnecting() and IsHandshake() too, * use FindClient(). * The second finds collisions with numeric representation of existing * servers - these shouldn't happen anymore when all upgraded to 2.10. * -- Run */ while ((acptr = FindClient(host)) || (numnick && (acptr = FindNServer(numnick)))) { /* * This link is trying feed me a server that I already have * access through another path * * Do not allow Uworld to do this. * Do not allow servers that are juped. * Do not allow servers that have older link timestamps * then this try. * Do not allow servers that use the same numeric as an existing * server, but have a different name. * * If my ircd.conf sucks, I can try to connect to myself: */ if (acptr == &me) return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host); /* * Detect wrong numeric. */ if (0 != ircd_strcmp(cli_name(acptr), host)) { sendcmdto_serv(&me, CMD_WALLOPS, cptr, ":SERVER Numeric Collision: %s != %s", cli_name(acptr), host); return exit_client_msg(cptr, cptr, &me, "NUMERIC collision between %s and %s." " Is your server numeric correct ?", host, cli_name(acptr)); } /* * Kill our try, if we had one. */ if (IsConnecting(acptr)) { if (active_lh_line == ALLOWED && exit_client(cptr, acptr, &me, "Just connected via another link") == CPTR_KILLED) return CPTR_KILLED; /* * We can have only ONE 'IsConnecting', 'IsHandshake' or * 'IsServer', because new 'IsConnecting's are refused to * the same server if we already had it. */ break; } /* * Avoid other nick collisions... * This is a doubtful test though, what else would it be * when it has a server.name ? */ else if (!IsServer(acptr) && !IsHandshake(acptr)) return exit_client_msg(cptr, cptr, &me, "Nickname %s already exists!", host); /* * Our new server might be a juped server: */ else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4))) { if (!IsServer(sptr)) return exit_client(cptr, sptr, &me, cli_info(acptr)); sendcmdto_serv(&me, CMD_WALLOPS, cptr, ":Received :%s SERVER %s from %s !?!", NumServ(cptr), host, cli_name(cptr)); return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr)); } /* * Of course we find the handshake this link was before :) */ else if (IsHandshake(acptr) && acptr == cptr) break; /* * Here we have a server nick collision... * We don't want to kill the link that was last /connected, * but we neither want to kill a good (old) link. * Therefor we kill the second youngest link. */ if (1) { struct Client* c2ptr = 0; struct Client* c3ptr = acptr; struct Client* ac2ptr; struct Client* ac3ptr; /* Search youngest link: */ for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; if (IsServer(sptr)) { for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; } if (timestamp > cli_serv(c3ptr)->timestamp) { c3ptr = 0; c2ptr = acptr; /* Make sure they differ */ } /* Search second youngest link: */ for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && cli_serv(ac2ptr)->timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; if (IsServer(sptr)) { for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && cli_serv(ac2ptr)->timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; } if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = 0; /* If timestamps are equal, decide which link to break * by name. */ if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) == (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp)) { const char *n2, *n2up, *n3, *n3up; if (c2ptr) { n2 = cli_name(c2ptr); n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up); } else { n2 = host; n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (c3ptr) { n3 = cli_name(c3ptr); n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up); } else { n3 = host; n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (strcmp(n2, n2up) > 0) n2 = n2up; if (strcmp(n3, n3up) > 0) n3 = n3up; if (strcmp(n3, n2) > 0) { ac2ptr = c2ptr; c2ptr = c3ptr; c3ptr = ac2ptr; } } /* Now squit the second youngest link: */ if (!c2ptr) return exit_new_server(cptr, sptr, host, timestamp, "server %s already exists and is %ld seconds younger.", host, (long)cli_serv(acptr)->timestamp - (long)timestamp); else if (cli_from(c2ptr) == cptr || IsServer(sptr)) { struct Client *killedptrfrom = cli_from(c2ptr); if (active_lh_line != ALLOWED) { /* * If the L: or H: line also gets rid of this link, * we sent just one squit. */ if (LHcptr && a_kills_b_too(LHcptr, c2ptr)) break; /* * If breaking the loop here solves the L: or H: * line problem, we don't squit that. */ if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr))) active_lh_line = ALLOWED; else { /* * If we still have a L: or H: line problem, * we prefer to squit the new server, solving * loop and L:/H: line problem with only one squit. */ LHcptr = 0; break; } } /* * If the new server was introduced by a server that caused a * Ghost less then 20 seconds ago, this is probably also * a Ghost... (20 seconds is more then enough because all * SERVER messages are at the beginning of a net.burst). --Run */ if (CurrentTime - cli_serv(cptr)->ghost < 20) { killedptrfrom = cli_from(acptr); if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED) return CPTR_KILLED; } else if (exit_client_msg(cptr, c2ptr, &me, "Loop <-- %s (new link is %ld seconds younger)", host, (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) - (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED) return CPTR_KILLED; /* * Did we kill the incoming server off already ? */ if (killedptrfrom == cptr) return 0; } else { if (active_lh_line != ALLOWED) { if (LHcptr && a_kills_b_too(LHcptr, acptr)) break; if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr))) active_lh_line = ALLOWED; else { LHcptr = 0; break; } } /* * We can't believe it is a lagged server message * when it directly connects to us... * kill the older link at the ghost, rather then * at the second youngest link, assuming it isn't * a REAL loop. */ if (ghost) *ghost = CurrentTime; /* Mark that it caused a ghost */ if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED) return CPTR_KILLED; break; } } } if (active_lh_line != ALLOWED) { if (!LHcptr) LHcptr = sptr; if (active_lh_line == MAX_HOPS_EXCEEDED) { return exit_client_msg(cptr, LHcptr, &me, "Maximum hops exceeded for %s at %s", cli_name(cptr), host); } else if (active_lh_line == NOT_ALLOWED_TO_HUB) { return exit_client_msg(cptr, LHcptr, &me, "%s is not allowed to hub for %s", cli_name(cptr), host); } else /* I_AM_NOT_HUB */ { ServerStats->is_ref++; return exit_client(cptr, LHcptr, &me, "I'm a leaf, define the HUB feature"); } } return 1; }
/** 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); }
/** 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 */ }
/** Given a feature vector string, set the value of a feature. * @param[in] from Client trying to set the feature, or NULL. * @param[in] fields Parameters to set, starting with feature name. * @param[in] count Number of fields in \a fields. * @return Zero (or, theoretically, CPTR_KILLED). */ int feature_set(struct Client* from, const char* const* fields, int count) { int i, change = 0, tmp; const char *t_str; struct FeatureDesc *feat; if (from && !HasPriv(from, PRIV_SET) && !IsServer(from)) return send_reply(from, ERR_NOPRIVILEGES); if (count < 1) { if (from) /* report an error in the number of arguments */ need_more_params(from, "SET"); else log_write(LS_CONFIG, L_ERROR, 0, "Not enough fields in F line"); } else if ((feat = feature_desc(from, fields[0]))) { /* find feature */ if (from && feat->flags & FEAT_READ) return send_reply(from, ERR_NOFEATURE, fields[0]); switch (feat_type(feat)) { case FEAT_NONE: if (feat->set && (i = (*feat->set)(from, fields + 1, count - 1))) { change++; /* feature handler wants a change recorded */ if (i > 0) /* call the set callback and do marking */ feat->flags |= FEAT_MARK; else /* i < 0 */ feat->flags &= ~FEAT_MARK; break; } case FEAT_INT: /* an integer value */ case FEAT_UINT: tmp = feat->v_int; /* detect changes... */ if (count < 2) { /* reset value */ feat->v_int = feat->def_int; feat->flags &= ~FEAT_MARK; } else { /* ok, figure out the value and whether to mark it */ feat->v_int = strtoul(fields[1], 0, 0); if (feat->v_int == feat->def_int) feat->flags &= ~FEAT_MARK; else feat->flags |= FEAT_MARK; } if (feat->v_int != tmp) /* check for change */ change++; break; case FEAT_BOOL: /* it's a boolean value--true or false */ tmp = feat->v_int; /* detect changes... */ if (count < 2) { /* reset value */ feat->v_int = feat->def_int; feat->flags &= ~FEAT_MARK; } else { /* figure out the value and whether to mark it */ if (!ircd_strncmp(fields[1], "TRUE", strlen(fields[1])) || !ircd_strncmp(fields[1], "YES", strlen(fields[1])) || (strlen(fields[1]) >= 2 && !ircd_strncmp(fields[1], "ON", strlen(fields[1])))) feat->v_int = 1; else if (!ircd_strncmp(fields[1], "FALSE", strlen(fields[1])) || !ircd_strncmp(fields[1], "NO", strlen(fields[1])) || (strlen(fields[1]) >= 2 && !ircd_strncmp(fields[1], "OFF", strlen(fields[1])))) feat->v_int = 0; else if (from) /* report an error... */ return send_reply(from, ERR_BADFEATVALUE, fields[1], feat->type); else { log_write(LS_CONFIG, L_ERROR, 0, "Bad value \"%s\" for feature %s", fields[1], feat->type); return 0; } if (feat->v_int == feat->def_int) /* figure out whether to mark it */ feat->flags &= ~FEAT_MARK; else feat->flags |= FEAT_MARK; } if (feat->v_int != tmp) /* check for change */ change++; break; case FEAT_STR: /* it's a string value */ if (count < 2) t_str = feat->def_str; /* changing to default */ else t_str = *fields[1] ? fields[1] : 0; if (!t_str && !(feat->flags & FEAT_NULL)) { /* NULL value permitted? */ if (from) return send_reply(from, ERR_BADFEATVALUE, "NULL", feat->type); else { log_write(LS_CONFIG, L_ERROR, 0, "Bad value \"NULL\" for feature %s", feat->type); return 0; } } if (t_str == feat->def_str || (t_str && feat->def_str && !(feat->flags & FEAT_CASE ? strcmp(t_str, feat->def_str) : ircd_strcmp(t_str, feat->def_str)))) { /* resetting to default */ if (feat->v_str != feat->def_str) { change++; /* change from previous value */ if (feat->v_str) MyFree(feat->v_str); /* free old value */ } feat->v_str = feat->def_str; /* very special... */ feat->flags &= ~FEAT_MARK; } else if (!t_str) { if (feat->v_str) { change++; /* change from previous value */ if (feat->v_str != feat->def_str) MyFree(feat->v_str); /* free old value */ } feat->v_str = 0; /* set it to NULL */ feat->flags |= FEAT_MARK; } else if (!feat->v_str || (feat->flags & FEAT_CASE ? strcmp(t_str, feat->v_str) : ircd_strcmp(t_str, feat->v_str))) { /* new value */ change++; /* change from previous value */ if (feat->v_str && feat->v_str != feat->def_str) MyFree(feat->v_str); /* free old value */ DupString(feat->v_str, t_str); /* store new value */ feat->flags |= FEAT_MARK; } else /* they match, but don't match the default */ feat->flags |= FEAT_MARK; break; } if (change && feat->notify) /* call change notify function */ (*feat->notify)(); if (change && from) { if(!IsServer(from) && !IsMe(from)) send_reply(from, SND_EXPLICIT | RPL_FEATURE, ":Value of %s changed", feat->type); switch (feat_type(feat)) { case FEAT_NONE: sendto_opmask_butone(0, SNO_OLDSNO, "%C changed %s", from, feat->type); break; case FEAT_INT: sendto_opmask_butone(0, SNO_OLDSNO, "%C changed %s to %d", from, feat->type, feat->v_int); break; case FEAT_BOOL: sendto_opmask_butone(0, SNO_OLDSNO, "%C changed %s to %s", from, feat->type, (feat->v_int ? "TRUE" : "FALSE")); break; case FEAT_STR: if(feat->v_str) sendto_opmask_butone(0, SNO_OLDSNO, "%C changed %s to: %s", from, feat->type, feat->v_str); else sendto_opmask_butone(0, SNO_OLDSNO, "%C unset %s", from, feat->type); break; } /* switch */ } /* if (change) */ } return 0; }