X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fm_burst.c;h=00e426545003b62c48a8c8738b8862e9a3c9d2f6;hb=2fbe1c3ccc35099d105552c0eef8eb566c97662d;hp=c03a6c004bcfc5ab16d2b5dc30b57de86dee3929;hpb=0066009565b01e5c5e52000d97cb7b20ae8c667c;p=ircu2.10.12-pk.git diff --git a/ircd/m_burst.c b/ircd/m_burst.c index c03a6c0..00e4265 100644 --- a/ircd/m_burst.c +++ b/ircd/m_burst.c @@ -87,6 +87,7 @@ #include "ircd.h" #include "ircd_alloc.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 * @@ -179,7 +208,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) struct Channel *chptr; time_t timestamp; struct Membership *member, *nmember; - struct SLink *lp, **lp_p; + 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]; @@ -192,6 +221,57 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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 @@ -200,9 +280,17 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) */ for (param = 3; param < parc; param++) { + int check_modes; if (parv[param][0] != '+') continue; - if (strchr(parv[param], 'i') || strchr(parv[param], 'k')) + 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); @@ -211,8 +299,16 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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(&me, CMD_KICK, chptr, NULL, 0, "%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); } } @@ -238,22 +334,21 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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 | MODE_DELJOINS); + 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(&me, CMD_TOPIC, chptr, NULL, 0, + sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0, "%H :%s", chptr, chptr->topic); } } else if (chptr->creationtime == timestamp) { @@ -274,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, " ")) { @@ -289,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; @@ -315,18 +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); - - DupString(newban->value.ban.who, - cli_name(feature_bool(FEAT_HIS_BANWHO) ? &me : sptr)); - 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 */ @@ -373,6 +460,14 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) current_mode_needs_reset = 0; } 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) { @@ -380,9 +475,9 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) current_mode_needs_reset = 0; } current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE; - oplevel = -1; /* subsequential digits are an absolute op-level value. */ + 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) { @@ -394,11 +489,19 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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 */ + } } } } @@ -421,14 +524,19 @@ 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; } - if (IsBurst(sptr) || !(member = find_member_link(chptr, acptr))) + if (!(member = find_member_link(chptr, acptr))) { add_user_to_channel(chptr, acptr, current_mode, oplevel); if (!(current_mode & CHFL_DELAYED)) @@ -444,7 +552,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) member->status |= CHFL_BURST_ALREADY_VOICED; /* Synchronize with the burst. */ member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE)); - member->oplevel = oplevel; + SetOpLevel(member, oplevel); } } } @@ -473,16 +581,17 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) for (member = chptr->members; member; member = member->next_member) { if (member->status & CHFL_BURST_JOINED) { /* joined during burst */ if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED)) - modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user); + 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); + 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; } } @@ -492,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; } }