diff --git a/doc/technical/ts6-protocol.txt b/doc/technical/ts6-protocol.txt index 004aebf7..def7329d 100644 --- a/doc/technical/ts6-protocol.txt +++ b/doc/technical/ts6-protocol.txt @@ -319,6 +319,21 @@ Sets a D:line (IP ban checked directly after accepting connection). The mask must be an IP address or CIDR mask. +EBMASK +source: server +propagation: broadcast +parameters: channelTS, channel, type, space separated "masks ts hostmask" chunks + +If the channelTS in the message is greater (newer) than the current TS of +the channel, drop the message and do not propagate it. + +Type is the mode letter of a ban-like mode. In efnet TS6 this is 'b', 'e' or +'I'. In charybdis TS6 additionally 'q' is possible. + +Add all the masks and their set at/by to the given list of the channel. + +All ban-like modes must be bursted using this command, not using MODE or TMODE. + ECHO source: user parameters: "P"/"N", target, text diff --git a/include/channel.h b/include/channel.h index 19ca532f..082d0d0e 100644 --- a/include/channel.h +++ b/include/channel.h @@ -302,7 +302,7 @@ extern void set_channel_mlock(struct Client *client_p, struct Client *source_p, extern struct ChannelMode chmode_table[256]; -extern bool add_id(struct Client *source_p, struct Channel *chptr, const char *banid, +extern struct Ban * add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const char *forward, rb_dlink_list * list, long mode_type); extern struct Ban * del_id(struct Channel *chptr, const char *banid, rb_dlink_list * list, diff --git a/include/s_serv.h b/include/s_serv.h index b917aa6f..3a99b6e8 100644 --- a/include/s_serv.h +++ b/include/s_serv.h @@ -92,6 +92,7 @@ extern unsigned int CAP_EUID; /* supports EUID (ext UID + nonencap CHGHOST) */ extern unsigned int CAP_EOPMOD; /* supports EOPMOD (ext +z + ext topic) */ extern unsigned int CAP_BAN; /* supports propagated bans */ extern unsigned int CAP_MLOCK; /* supports MLOCK messages */ +extern unsigned int CAP_EBMASK; /* supports sending BMASK set by/at metadata */ /* XXX: added for backwards compatibility. --nenolod */ #define CAP_MASK (capability_index_mask(serv_capindex) & ~(CAP_TS6 | CAP_CAP)) diff --git a/ircd/chmode.c b/ircd/chmode.c index 1b812eab..c99f9506 100644 --- a/ircd/chmode.c +++ b/ircd/chmode.c @@ -237,10 +237,10 @@ allow_mode_change(struct Client *source_p, struct Channel *chptr, int alevel, /* add_id() * * inputs - client, channel, id to add, type, forward - * outputs - false on failure, true on success + * outputs - NULL on failure, allocated Ban on success * side effects - given id is added to the appropriate list */ -bool +struct Ban * add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const char *forward, rb_dlink_list * list, long mode_type) { @@ -256,7 +256,7 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const { sendto_one(source_p, form_str(ERR_BANLISTFULL), me.name, source_p->name, chptr->chname, realban); - return false; + return NULL; } } @@ -265,7 +265,7 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const { actualBan = ptr->data; if(!irccmp(actualBan->banstr, realban)) - return false; + return NULL; } if(IsPerson(source_p)) @@ -282,7 +282,7 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION) chptr->bants = rb_current_time(); - return true; + return actualBan; } /* del_id() diff --git a/ircd/s_serv.c b/ircd/s_serv.c index 7337db8f..dca66b2b 100644 --- a/ircd/s_serv.c +++ b/ircd/s_serv.c @@ -86,6 +86,7 @@ unsigned int CAP_EUID; unsigned int CAP_EOPMOD; unsigned int CAP_BAN; unsigned int CAP_MLOCK; +unsigned int CAP_EBMASK; unsigned int CLICAP_MULTI_PREFIX; unsigned int CLICAP_ACCOUNT_NOTIFY; @@ -126,6 +127,7 @@ init_builtin_capabs(void) CAP_EOPMOD = capability_put(serv_capindex, "EOPMOD", NULL); CAP_BAN = capability_put(serv_capindex, "BAN", NULL); CAP_MLOCK = capability_put(serv_capindex, "MLOCK", NULL); + CAP_EBMASK = capability_put(serv_capindex, "EBMASK", NULL); capability_require(serv_capindex, "QS"); capability_require(serv_capindex, "EX"); @@ -511,8 +513,9 @@ burst_modes_TS6(struct Client *client_p, struct Channel *chptr, rb_dlink_node *ptr; struct Ban *banptr; - send_multiline_init(client_p, " ", ":%s BMASK %ld %s %c :", + send_multiline_init(client_p, " ", ":%s %s %ld %s %c :", me.id, + IsCapable(client_p, CAP_EBMASK) ? "EBMASK" : "BMASK", (long)chptr->channelts, chptr->chname, flag); @@ -522,11 +525,18 @@ burst_modes_TS6(struct Client *client_p, struct Channel *chptr, banptr = ptr->data; if (banptr->forward) - send_multiline_item(client_p, "%s$%s", - banptr->banstr, - banptr->forward); + sprintf(buf, "%s$%s", banptr->banstr, banptr->forward); else - send_multiline_item(client_p, "%s", banptr->banstr); + strcpy(buf, banptr->banstr); + + if IsCapable(client_p, CAP_EBMASK) + send_multiline_item(client_p, "%s %ld %s", + buf, + (long)banptr->when, + banptr->who); + else + send_multiline_item(client_p, "%s", buf); + } send_multiline_fini(client_p, NULL); diff --git a/modules/core/m_mode.c b/modules/core/m_mode.c index f78b59f4..b42875ef 100644 --- a/modules/core/m_mode.c +++ b/modules/core/m_mode.c @@ -48,6 +48,7 @@ static void ms_mode(struct MsgBuf *, struct Client *, struct Client *, int, cons static void ms_tmode(struct MsgBuf *, struct Client *, struct Client *, int, const char **); static void ms_mlock(struct MsgBuf *, struct Client *, struct Client *, int, const char **); static void ms_bmask(struct MsgBuf *, struct Client *, struct Client *, int, const char **); +static void ms_ebmask(struct MsgBuf *, struct Client *, struct Client *, int, const char **); struct Message mode_msgtab = { "MODE", 0, 0, 0, 0, @@ -65,8 +66,12 @@ struct Message bmask_msgtab = { "BMASK", 0, 0, 0, 0, {mg_ignore, mg_ignore, mg_ignore, {ms_bmask, 5}, mg_ignore, mg_ignore} }; +struct Message ebmask_msgtab = { + "EBMASK", 0, 0, 0, 0, + {mg_ignore, mg_ignore, mg_ignore, {ms_ebmask, 5}, mg_ignore, mg_ignore} +}; -mapi_clist_av1 mode_clist[] = { &mode_msgtab, &tmode_msgtab, &mlock_msgtab, &bmask_msgtab, NULL }; +mapi_clist_av1 mode_clist[] = { &mode_msgtab, &tmode_msgtab, &mlock_msgtab, &bmask_msgtab, &ebmask_msgtab, NULL }; DECLARE_MODULE_AV2(mode, NULL, NULL, mode_clist, NULL, NULL, NULL, NULL, mode_desc); @@ -256,16 +261,19 @@ possibly_remove_lower_forward(struct Client *fakesource_p, int mems, } static void -ms_bmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +do_bmask(bool extended, struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { - static char modebuf[BUFSIZE]; + static char output[BUFSIZE]; static char parabuf[BUFSIZE]; + static char degrade[BUFSIZE]; + static char squitreason[120]; struct Channel *chptr; + struct Ban *banptr; rb_dlink_list *banlist; - char *s, *forward; - char *t; - char *mbuf; - char *pbuf; + char *s, *mask, *forward, *who; + char *output_ptr; + char *param_ptr; + char *degrade_ptr; long mode_type; int mlen; int plen = 0; @@ -274,6 +282,7 @@ ms_bmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source int modecount = 0; int needcap = NOCAPS; int mems; + time_t when = (long)rb_current_time(); struct Client *fakesource_p; if(!IsChanPrefix(parv[2][0]) || !check_channel_name(parv[2])) @@ -327,29 +336,29 @@ ms_bmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source fakesource_p = &me; else fakesource_p = source_p; - mlen = sprintf(modebuf, ":%s MODE %s +", fakesource_p->name, chptr->chname); - mbuf = modebuf + mlen; - pbuf = parabuf; + who = fakesource_p->name; + + mlen = sprintf(output, ":%s MODE %s +", fakesource_p->name, chptr->chname); + output_ptr = output + mlen; + param_ptr = parabuf; + degrade_ptr = degrade; while(*s == ' ') s++; - /* next char isnt a space, point t to the next one */ - if((t = strchr(s, ' ')) != NULL) - { - *t++ = '\0'; + s = strtok(s, " "); - /* double spaces will break the parser */ - while(*t == ' ') - t++; - } - - /* couldve skipped spaces and got nothing.. */ while(!EmptyString(s)) { - /* ban with a leading ':' -- this will break the protocol */ if(*s == ':') - goto nextban; + { + /* ban with a leading ':' -- this will break the protocol */ + sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, + "Link %s dropped, invalid BMASK mask (%s)", source_p->name, s); + snprintf(squitreason, sizeof squitreason, "Invalid BMASK mask (%s)", s); + exit_client(client_p, client_p, client_p, squitreason); + return; + } tlen = strlen(s); @@ -368,53 +377,83 @@ ms_bmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source parv[3][0], s, forward); } - if(add_id(fakesource_p, chptr, s, forward, banlist, mode_type)) + mask = s; + if (extended) { + when = atol(strtok(NULL, " ")); + who = strtok(NULL, " "); + if (who == NULL) + { + /* EBMASK params don't divide by 3, so we have an incomplete chunk */ + sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, + "Link %s dropped, invalid EBMASK chunk", source_p->name); + snprintf(squitreason, sizeof squitreason, "Invalid EBMASK chunk"); + exit_client(client_p, client_p, client_p, squitreason); + return; + } + + arglen = sprintf(degrade_ptr, "%s ", mask); + degrade_ptr += arglen; + } + + if((banptr = add_id(fakesource_p, chptr, mask, forward, banlist, mode_type)) != NULL) { + banptr->when = when; + rb_free(banptr->who); + banptr->who = rb_strdup(who); + /* this new one wont fit.. */ if(mlen + MAXMODEPARAMS + plen + tlen > BUFSIZE - 5 || modecount >= MAXMODEPARAMS) { - *mbuf = '\0'; - *(pbuf - 1) = '\0'; - sendto_channel_local(fakesource_p, mems, chptr, "%s %s", modebuf, parabuf); + *output_ptr = '\0'; + *(param_ptr - 1) = '\0'; + sendto_channel_local(fakesource_p, mems, chptr, "%s %s", output, parabuf); - mbuf = modebuf + mlen; - pbuf = parabuf; + output_ptr = output + mlen; + param_ptr = parabuf; plen = modecount = 0; } if (forward != NULL) forward[-1] = '$'; - *mbuf++ = parv[3][0]; - arglen = sprintf(pbuf, "%s ", s); - pbuf += arglen; + *output_ptr++ = parv[3][0]; + arglen = sprintf(param_ptr, "%s ", mask); + param_ptr += arglen; plen += arglen; modecount++; } - nextban: - s = t; - - if(s != NULL) - { - if((t = strchr(s, ' ')) != NULL) - { - *t++ = '\0'; - - while(*t == ' ') - t++; - } - } + s = strtok(NULL, " "); } if(modecount) { - *mbuf = '\0'; - *(pbuf - 1) = '\0'; - sendto_channel_local(fakesource_p, mems, chptr, "%s %s", modebuf, parabuf); + *output_ptr = '\0'; + *(param_ptr - 1) = '\0'; + sendto_channel_local(fakesource_p, mems, chptr, "%s %s", output, parabuf); } - sendto_server(client_p, chptr, CAP_TS6 | needcap, NOCAPS, ":%s BMASK %ld %s %s :%s", - source_p->id, (long) chptr->channelts, chptr->chname, parv[3], parv[4]); + if (extended) { + *(degrade_ptr - 1) = '\0'; + sendto_server(client_p, chptr, CAP_EBMASK | CAP_TS6 | needcap, NOCAPS, ":%s EBMASK %ld %s %s :%s", + source_p->id, (long) chptr->channelts, chptr->chname, parv[3], parv[4]); + sendto_server(client_p, chptr, CAP_TS6 | needcap, CAP_EBMASK, ":%s BMASK %ld %s %s :%s", + source_p->id, (long) chptr->channelts, chptr->chname, parv[3], degrade); + } + else + sendto_server(client_p, chptr, CAP_TS6 | needcap, NOCAPS, ":%s BMASK %ld %s %s :%s", + source_p->id, (long) chptr->channelts, chptr->chname, parv[3], parv[4]); } + +static void +ms_bmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +{ + do_bmask(false, msgbuf_p, client_p, source_p, parc, parv); +} +static void +ms_ebmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +{ + do_bmask(true, msgbuf_p, client_p, source_p, parc, parv); +} +