X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fm_burst.c;h=00e426545003b62c48a8c8738b8862e9a3c9d2f6;hb=2fbe1c3ccc35099d105552c0eef8eb566c97662d;hp=0f1e8bfcdad2e172e01138c7a514744ea4575ac9;hpb=362239ace2f74ae33afd0e0b935f9e3acb736968;p=ircu2.10.12-pk.git diff --git a/ircd/m_burst.c b/ircd/m_burst.c index 0f1e8bf..00e4265 100644 --- a/ircd/m_burst.c +++ b/ircd/m_burst.c @@ -86,7 +86,8 @@ #include "hash.h" #include "ircd.h" #include "ircd_alloc.h" -#include "ircd_policy.h" +#include "ircd_features.h" +#include "ircd_log.h" #include "ircd_reply.h" #include "ircd_string.h" #include "list.h" @@ -98,14 +99,42 @@ #include "s_misc.h" #include "send.h" #include "struct.h" -#include "support.h" #include "ircd_snprintf.h" -#include +/* #include -- Now using assert in ircd_log.h */ #include #include #include +static int +netride_modes(int parc, char **parv, const char *curr_key) +{ + char *modes = parv[0]; + int result = 0; + + assert(modes && modes[0] == '+'); + while (*modes) { + switch (*modes++) { + case '-': + return -1; + case 'i': + result |= MODE_INVITEONLY; + break; + case 'k': + if (strcmp(curr_key, *++parv)) + result |= MODE_KEY; + break; + case 'l': + ++parv; + break; + case 'r': + result |= MODE_REGONLY; + break; + } + } + return result; +} + /* * ms_burst - server message handler * @@ -167,14 +196,19 @@ * the TS in the BURST message, then we cancel all existing modes. * If its is smaller then the received BURST message is ignored. * If it's equal, then the received modes are just added. + * + * BURST is also accepted outside a netburst now because it + * is sent upstream as reaction to a DESTRUCT message. For + * these BURST messages it is possible that the listed channel + * members are already joined. */ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct ModeBuf modebuf, *mbuf = 0; struct Channel *chptr; time_t timestamp; - struct Membership *member; - struct SLink *lp, **lp_p; + struct Membership *member, *nmember; + struct Ban *lp, **lp_p; unsigned int parse_flags = (MODE_PARSE_FORCE | MODE_PARSE_BURST); int param, nickpos = 0, banpos = 0; char modestr[BUFSIZE], nickstr[BUFSIZE], banstr[BUFSIZE]; @@ -182,18 +216,113 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) if (parc < 3) return protocol_violation(sptr,"Too few parameters for BURST"); - if (!IsBurst(sptr)) /* don't allow BURST outside of burst */ - return exit_client_msg(cptr, cptr, &me, "HACK: BURST message outside " - "net.burst from %s", cli_name(sptr)); - if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE))) return 0; /* can't create the channel? */ timestamp = atoi(parv[2]); + if (chptr->creationtime) /* 0 for new (empty) channels, + i.e. when this server just restarted. */ + { + if (parc == 3) /* Zannel BURST? */ + { + /* An empty channel without +A set, will cause a BURST message + with exactly 3 parameters (because all modes have been reset). + If the timestamp on such channels is only a few seconds older + from our own, then we ignore this burst: we do not deop our + own side. + Likewise, we expect the other (empty) side to copy our timestamp + from our own BURST message, even though it is slightly larger. + + The reason for this is to allow people to join an empty + non-A channel (a zannel) during a net.split, and not be + deopped when the net reconnects (with another zannel). When + someone joins a split zannel, their side increments the TS by one. + If they cycle a few times then we still don't have a reason to + deop them. Theoretically I see no reason not to accept ANY timestamp, + but to be sure, we only accept timestamps that are just a few + seconds off (one second for each time they cycled the channel). */ + + /* Don't even deop users who cycled four times during the net.break. */ + if (timestamp < chptr->creationtime && + chptr->creationtime <= timestamp + 4 && + chptr->users != 0) /* Only do this when WE have users, so that + if we do this the BURST that we sent has + parc > 3 and the other side will use the + test below: */ + timestamp = chptr->creationtime; /* Do not deop our side. */ + } + else if (chptr->creationtime < timestamp && + timestamp <= chptr->creationtime + 4 && + chptr->users == 0) + { + /* If one side of the net.junction does the above + timestamp = chptr->creationtime, then the other + side must do this: */ + chptr->creationtime = timestamp; /* Use the same TS on both sides. */ + } + /* In more complex cases, we might still end up with a + creationtime desync of a few seconds, but that should + be synced automatically rather quickly (every JOIN + caries a timestamp and will sync it; modes by users do + not carry timestamps and are accepted regardless). + Only when nobody joins the channel on the side with + the oldest timestamp before a new net.break occurs + precisely inbetween the desync, an unexpected bounce + might happen on reconnect. */ + } + + if (!chptr->creationtime || chptr->creationtime > timestamp) { + /* + * Kick local members if channel is +i or +k and our TS was larger + * than the burst TS (anti net.ride). The modes hack is here because + * we have to do this before mode_parse, as chptr may go away. + */ + for (param = 3; param < parc; param++) + { + int check_modes; + if (parv[param][0] != '+') + continue; + check_modes = netride_modes(parc - param, parv + param, chptr->mode.key); + if (check_modes < 0) + { + if (chptr->users == 0) + sub1_from_channel(chptr); + return protocol_violation(sptr, "Invalid mode string in BURST"); + } + else if (check_modes) + { + /* Clear any outstanding rogue invites */ + mode_invite_clear(chptr); + for (member = chptr->members; member; member = nmember) + { + nmember = member->next_member; + if (!MyUser(member->user) || IsZombie(member)) + continue; + /* Kick as netrider if key mismatch *or* remote channel is + * +i (unless user is an oper) *or* remote channel is +r + * (unless user has an account). + */ + if (!(check_modes & MODE_KEY) + && (!(check_modes & MODE_INVITEONLY) || IsAnOper(member->user)) + && (!(check_modes & MODE_REGONLY) || IsAccount(member->user))) + continue; + sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Net Rider", chptr, member->user); + sendcmdto_channel_butserv_butone(&his, CMD_KICK, chptr, NULL, 0, "%H %C :Net Rider", chptr, member->user); + make_zombie(member, member->user, &me, &me, chptr); + } + } + break; + } + + /* If the channel had only locals, it went away by now. */ + if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE))) + return 0; /* can't create the channel? */ + } + /* turn off burst joined flag */ for (member = chptr->members; member; member = member->next_member) - member->status &= ~CHFL_BURST_JOINED; + member->status &= ~(CHFL_BURST_JOINED|CHFL_BURST_ALREADY_OPPED|CHFL_BURST_ALREADY_VOICED); if (!chptr->creationtime) /* mark channel as created during BURST */ chptr->mode.mode |= MODE_BURSTADDED; @@ -202,19 +331,29 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) if (!chptr->creationtime || chptr->creationtime > timestamp) { chptr->creationtime = timestamp; - modebuf_init(mbuf = &modebuf, &me, cptr, chptr, MODEBUF_DEST_CHANNEL); + modebuf_init(mbuf = &modebuf, &me, cptr, chptr, + MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY); modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */ - chptr->mode.mode &= ~(MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | - MODE_MODERATED | MODE_TOPICLIMIT | MODE_INVITEONLY | - MODE_NOPRIVMSGS); + chptr->mode.mode &= MODE_BURSTADDED | MODE_WASDELJOINS; - parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */ + /* wipeout any limit and keys that are set */ + parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* mark bans for wipeout */ for (lp = chptr->banlist; lp; lp = lp->next) - lp->flags |= CHFL_BURST_BAN_WIPEOUT; + lp->flags |= BAN_BURST_WIPEOUT; + + /* clear topic set by netrider (if set) */ + if (*chptr->topic) { + *chptr->topic = '\0'; + *chptr->topic_nick = '\0'; + chptr->topic_time = 0; + sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0, + "%H :%s", chptr, chptr->topic); + } } else if (chptr->creationtime == timestamp) { - modebuf_init(mbuf = &modebuf, &me, cptr, chptr, MODEBUF_DEST_CHANNEL); + modebuf_init(mbuf = &modebuf, &me, cptr, chptr, + MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY); parse_flags |= MODE_PARSE_SET; /* set new modes */ } @@ -230,7 +369,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) case '%': /* parameter contains bans */ if (parse_flags & MODE_PARSE_SET) { char *banlist = parv[param] + 1, *p = 0, *ban, *ptr; - struct SLink *newban; + struct Ban *newban; for (ban = ircd_strtok(&p, banlist, " "); ban; ban = ircd_strtok(&p, 0, " ")) { @@ -245,16 +384,16 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) * shown below *sigh* */ for (lp = chptr->banlist; lp; lp = lp->next) { - if (!ircd_strcmp(lp->value.ban.banstr, ban)) { + if (!ircd_strcmp(lp->banstr, ban)) { ban = 0; /* don't add ban */ - lp->flags &= ~CHFL_BURST_BAN_WIPEOUT; /* not wiping out */ + lp->flags &= ~BAN_BURST_WIPEOUT; /* not wiping out */ break; /* new ban already existed; don't even repropagate */ - } else if (!(lp->flags & CHFL_BURST_BAN_WIPEOUT) && - !mmatch(lp->value.ban.banstr, ban)) { + } else if (!(lp->flags & BAN_BURST_WIPEOUT) && + !mmatch(lp->banstr, ban)) { ban = 0; /* don't add ban unless wiping out bans */ break; /* new ban is encompassed by an existing one; drop */ - } else if (!mmatch(ban, lp->value.ban.banstr)) - lp->flags |= CHFL_BAN_OVERLAPPED; /* remove overlapping ban */ + } else if (!mmatch(ban, lp->banstr)) + lp->flags |= BAN_OVERLAPPED; /* remove overlapping ban */ if (!lp->next) break; @@ -271,20 +410,10 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) for (ptr = ban; *ptr; ptr++) /* add ban to buffer */ banstr[banpos++] = *ptr; - newban = make_link(); /* create new ban */ - - DupString(newban->value.ban.banstr, ban); -#ifdef HEAD_IN_SAND_BANWHO - DupString(newban->value.ban.who, cli_name(&me)); -#else - DupString(newban->value.ban.who, cli_name(sptr)); -#endif - newban->value.ban.when = TStime(); - - newban->flags = CHFL_BAN | CHFL_BURST_BAN; /* set flags */ - if ((ptr = strrchr(ban, '@')) && check_if_ipmask(ptr + 1)) - newban->flags |= CHFL_BAN_IPMASK; - + newban = make_ban(ban); /* create new ban */ + strcpy(newban->who, "*"); + newban->when = TStime(); + newban->flags |= BAN_BURSTED; newban->next = 0; if (lp) lp->next = newban; /* link it in */ @@ -300,10 +429,15 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *acptr; char *nicklist = parv[param], *p = 0, *nick, *ptr; - int current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; - int last_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + int current_mode, last_mode, base_mode; int oplevel = -1; /* Mark first field with digits: means the same as 'o' (but with level). */ int last_oplevel = 0; + struct Membership* member; + + base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + if (chptr->mode.mode & MODE_DELJOINS) + base_mode |= CHFL_DELAYED; + current_mode = last_mode = base_mode; for (nick = ircd_strtok(&p, nicklist, ","); nick; nick = ircd_strtok(&p, 0, ",")) { @@ -322,36 +456,52 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) */ oplevel = MAXOPLEVEL; if (current_mode_needs_reset) { - current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + current_mode = base_mode; current_mode_needs_reset = 0; } - current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP; + current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP; + /* + * Older servers may send XXYYY:ov, in which case we + * do not want to use the code for 'v' below. + */ + if (ptr[1] == 'v') { + current_mode |= CHFL_VOICE; + ptr++; + } } else if (*ptr == 'v') { /* has voice status */ if (current_mode_needs_reset) { - current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + current_mode = base_mode; current_mode_needs_reset = 0; } - current_mode |= CHFL_VOICE; - oplevel = -1; /* subsequential digits are an absolute op-level value. */ + current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE; + oplevel = -1; /* subsequent digits are an absolute op-level value. */ } - else if (isdigit(*ptr)) { + else if (IsDigit(*ptr)) { int level_increment = 0; if (oplevel == -1) { /* op-level is absolute value? */ if (current_mode_needs_reset) { - current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + current_mode = base_mode; current_mode_needs_reset = 0; } oplevel = 0; } - current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP; + current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP; do { level_increment = 10 * level_increment + *ptr++ - '0'; - } while(isdigit(*ptr)); + } while (IsDigit(*ptr)); + --ptr; oplevel += level_increment; + if (oplevel > MAXOPLEVEL) { + protocol_violation(sptr, "Invalid cumulative oplevel %u during burst", oplevel); + oplevel = MAXOPLEVEL; + break; + } } - else /* I don't recognize that flag */ + else { /* I don't recognize that flag */ + protocol_violation(sptr, "Invalid flag '%c' in nick part of burst", *ptr); break; /* so stop processing */ + } } } } @@ -374,21 +524,42 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) if (current_mode & CHFL_VOICE) nickstr[nickpos++] = 'v'; if (current_mode & CHFL_CHANOP) - nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel); + { + if (oplevel != MAXOPLEVEL) + nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel); + else + nickstr[nickpos++] = 'o'; + } } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */ nickstr[nickpos++] = ':'; /* add a specifier */ nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel); last_oplevel = oplevel; } - add_user_to_channel(chptr, acptr, current_mode, oplevel); - sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr); + if (!(member = find_member_link(chptr, acptr))) + { + add_user_to_channel(chptr, acptr, current_mode, oplevel); + if (!(current_mode & CHFL_DELAYED)) + sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr); + } + else + { + /* The member was already joined (either by CREATE or JOIN). + Remember the current mode. */ + if (member->status & CHFL_CHANOP) + member->status |= CHFL_BURST_ALREADY_OPPED; + if (member->status & CHFL_VOICE) + member->status |= CHFL_BURST_ALREADY_VOICED; + /* Synchronize with the burst. */ + member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE)); + SetOpLevel(member, oplevel); + } } } param++; break; - } /* switch (*parv[param]) { */ - } /* while (param < parc) { */ + } /* switch (*parv[param]) */ + } /* while (param < parc) */ nickstr[nickpos] = '\0'; banstr[banpos] = '\0'; @@ -409,17 +580,18 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) /* first deal with channel members */ for (member = chptr->members; member; member = member->next_member) { if (member->status & CHFL_BURST_JOINED) { /* joined during burst */ - if (member->status & CHFL_CHANOP) - modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user); - if (member->status & CHFL_VOICE) - modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user); + if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED)) + modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member)); + if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED)) + modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member)); } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */ if (member->status & CHFL_CHANOP) - modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user); + modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member)); if (member->status & CHFL_VOICE) - modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user); - member->status = ((member->status & ~(CHFL_CHANOP | CHFL_VOICE)) | - CHFL_DEOPPED); + modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member)); + member->status = (member->status + & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE)) + | CHFL_DEOPPED; } } @@ -429,19 +601,19 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) lp = *lp_p; /* remove ban from channel */ - if (lp->flags & (CHFL_BAN_OVERLAPPED | CHFL_BURST_BAN_WIPEOUT)) { + if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) { + char *bandup; + DupString(bandup, lp->banstr); modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN, - lp->value.ban.banstr, 1); /* let it free banstr */ - + bandup, 1); *lp_p = lp->next; /* clip out of list */ - MyFree(lp->value.ban.who); /* free who */ - free_link(lp); /* free ban */ + free_ban(lp); continue; - } else if (lp->flags & CHFL_BURST_BAN) /* add ban to channel */ + } else if (lp->flags & BAN_BURSTED) /* add ban to channel */ modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN, - lp->value.ban.banstr, 0); /* don't free banstr */ + lp->banstr, 0); /* don't free banstr */ - lp->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* reset the flag */ + lp->flags &= BAN_IPMASK; /* reset the flag */ lp_p = &(*lp_p)->next; } }