1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-06-28 20:36:36 +02:00

Add "CAP chghost" support. Internal recode of userhost changes.

Fix force-rejoin not working if doing SVSMODE -x/+x (Koragg, #5015).

Note to module coders:
Please use the following procedure in case of an user/host change:
* userhost_save_current(acptr);
* << change username or hostname here (or both) >>
* userhost_changed(acptr);
This function will take care of notifying other clients about
the userhost change, such as doing PART+JOIN+MODE if force-rejoin
is enabled, and sending :xx CHGHOST user host messages to
"CAP chghost" capable clients.

Also, small note to everyone:
If force-rejoin is enabled we will not send the PART+JOIN+MODE to
"CAP chghost" capable clients. Doing so is just a hack to notify
people of a userhost change. "CAP chghost" users can thus benefit
from the reduced noise in this respect.
This commit is contained in:
Bram Matthys
2017-10-07 13:31:30 +02:00
parent 0fd265349a
commit 5124e60b7c
15 changed files with 251 additions and 153 deletions
+4 -2
View File
@@ -515,8 +515,6 @@ extern u_char getrandom8();
extern u_int16_t getrandom16();
extern u_int32_t getrandom32();
#define EVENT_DRUGS BASE_VERSION
extern void rejoin_leave(aClient *sptr);
extern void rejoin_joinandmode(aClient *sptr);
extern void ident_failed(aClient *cptr);
extern MODVAR char extchmstr[4][64];
@@ -669,6 +667,8 @@ extern MODVAR int (*check_banned)(aClient *cptr);
extern MODVAR void (*introduce_user)(aClient *to, aClient *acptr);
extern MODVAR int (*check_deny_version)(aClient *cptr, char *version_string, int protocol, char *flags);
extern MODVAR int (*match_user)(char *rmask, aClient *acptr, int options);
extern MODVAR void (*userhost_save_current)(aClient *sptr);
extern MODVAR void (*userhost_changed)(aClient *sptr);
/* /Efuncs */
extern MODVAR aMotdFile opermotd, svsmotd, motd, botmotd, smotd, rules;
@@ -798,3 +798,5 @@ extern int cipher_check(SSL_CTX *ctx, char **errstr);
extern void clicap_pre_rehash(void);
extern void clicap_post_rehash(void);
extern void send_cap_notify(int add, char *token);
extern void sendbufto_one(aClient *to, char *msg, unsigned int quick);
extern MODVAR int current_serial;
+2
View File
@@ -1044,6 +1044,8 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
#define EFUNC_SEND_MODDATA_MEMBERS 51
#define EFUNC_BROADCAST_MODDATA_CLIENT 52
#define EFUNC_MATCH_USER 53
#define EFUNC_USERHOST_SAVE_CURRENT 54
#define EFUNC_USERHOST_CHANGED 55
/* Module flags */
#define MODFLAG_NONE 0x0000
+1
View File
@@ -357,6 +357,7 @@ typedef OperPermission (*OperClassEntryEvalCallback)(OperClassACLEntryVar* varia
#define PROTO_ACCOUNT_NOTIFY 0x200000 /* client supports account-notify */
#define PROTO_MLOCK 0x400000 /* server supports MLOCK */
#define PROTO_EXTSWHOIS 0x800000 /* extended SWHOIS support */
#define PROTO_CAP_CHGHOST 0x1000000 /* CAP chghost */
/*
* flags macros.
-98
View File
@@ -1269,104 +1269,6 @@ void send_user_joins(aClient *cptr, aClient *user)
return;
}
/*
* rejoin_leave:
* sends a PART to all channels (to local users only)
* TODO: use QUIT instead of PART if configured to do so
*/
void rejoin_leave(aClient *sptr)
{
Membership *tmp;
aChannel *chptr;
char *comment = "Changing host";
int i = 0;
for (tmp = sptr->user->channel; tmp; tmp = tmp->next)
{
tmp->flags &= ~CHFL_REJOINING;
chptr = tmp->chptr;
if (!chptr)
continue; /* Possible? */
/* If the user is banned, don't do it */
if (is_banned(sptr, chptr, BANCHK_JOIN))
continue;
/* Ok, we will now part/quit/whatever the user, so tag it.. */
tmp->flags |= CHFL_REJOINING;
if (invisible_user_in_channel(sptr, chptr))
{
sendto_chanops_butone(sptr, chptr, ":%s!%s@%s PART %s :%s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname, comment);
} else
sendto_channel_butserv_butone(chptr, sptr, sptr, ":%s PART %s :%s", sptr->name, chptr->chname, comment);
}
}
/*
* rejoin_joinandmode:
* sends a JOIN and a MODE (if needed) to restore qaohv modes (to local users only)
*/
void rejoin_joinandmode(aClient *sptr)
{
Membership *tmp;
aChannel *chptr;
int i, j = 0, n, flags;
int k = 0;
char flagbuf[8]; /* For holding "qohva" and "*~@%+" */
for (tmp = sptr->user->channel; tmp; tmp = tmp->next)
{
flags = tmp->flags;
chptr = tmp->chptr;
if (!chptr)
continue; /* Is it possible? */
/* If the user is banned, don't do it */
if (!(flags & CHFL_REJOINING))
continue;
if (invisible_user_in_channel(sptr, chptr))
{
sendto_chanops_butone(sptr, chptr, ":%s!%s@%s JOIN :%s", sptr->name, sptr->user->username, GetHost(sptr), chptr->chname);
} else
sendto_channel_butserv_butone(chptr, sptr, sptr, ":%s JOIN :%s", sptr->name, chptr->chname);
/* Set the modes (if any) */
if (flags)
{
char *p = flagbuf;
if (flags & MODE_CHANOP)
*p++ = 'o';
if (flags & MODE_VOICE)
*p++ = 'v';
if (flags & MODE_HALFOP)
*p++ = 'h';
if (flags & MODE_CHANOWNER)
*p++ = 'q';
if (flags & MODE_CHANPROT)
*p++ = 'a';
*p = '\0';
parabuf[0] = '\0';
n = strlen(flagbuf);
if (n)
{
for (i=0; i < n; i++)
{
strcat(parabuf, sptr->name);
if (i < n - 1)
strcat(parabuf, " ");
}
sendto_channel_butserv_butone(chptr, &me, sptr, ":%s MODE %s +%s %s",
me.name, chptr->chname, flagbuf, parabuf);
}
}
tmp->flags &= ~CHFL_REJOINING; /* esthetics.. ;) */
}
}
/* set_channel_mlock()
*
* inputs - client, source, channel, params
+5 -1
View File
@@ -132,6 +132,8 @@ void (*send_moddata_channel)(aClient *srv, aChannel *chptr);
void (*send_moddata_members)(aClient *srv);
void (*broadcast_moddata_client)(aClient *acptr);
int (*match_user)(char *rmask, aClient *acptr, int options);
void (*userhost_changed)(aClient *sptr);
void (*userhost_save_current)(aClient *sptr);
static const EfunctionsList efunction_table[MAXEFUNCTIONS] = {
/* 00 */ {NULL, NULL},
@@ -188,7 +190,9 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = {
/* 51 */ {"send_moddata_members", (void *)&send_moddata_members},
/* 52 */ {"broadcast_moddata_client", (void *)&broadcast_moddata_client},
/* 53 */ {"match_user", (void *)&match_user},
/* 54 */ {NULL, NULL}
/* 54 */ {"userhost_save_current", (void *)&userhost_save_current},
/* 55 */ {"userhost_changed", (void *)&userhost_changed},
/* 56 */ {NULL, NULL}
};
#ifdef UNDERSCORE
+5
View File
@@ -69,6 +69,11 @@ MOD_INIT(m_cap)
c.cap = PROTO_CAP_NOTIFY;
ClientCapabilityAdd(modinfo->handle, &c);
memset(&c, 0, sizeof(c));
c.name = "chghost";
c.cap = PROTO_CAP_CHGHOST;
ClientCapabilityAdd(modinfo->handle, &c);
return MOD_SUCCESS;
}
+6 -4
View File
@@ -110,6 +110,9 @@ CMD_FUNC(m_chghost)
sendnotice(sptr, "*** /ChgHost Error: requested host is same as current host.");
return 0;
}
userhost_save_current(acptr);
switch (UHOST_ALLOWED)
{
case UHALLOW_NEVER:
@@ -130,8 +133,7 @@ CMD_FUNC(m_chghost)
}
break;
case UHALLOW_REJOIN:
rejoin_leave(acptr);
/* join sent later when the host has been changed */
/* rejoin sent later when the host has been changed */
break;
}
@@ -158,9 +160,9 @@ CMD_FUNC(m_chghost)
acptr->user->virthost = 0;
}
acptr->user->virthost = strdup(parv[2]);
if (UHOST_ALLOWED == UHALLOW_REJOIN)
rejoin_joinandmode(acptr);
userhost_changed(acptr);
if (MyClient(acptr))
sendto_one(acptr, err_str(RPL_HOSTHIDDEN), me.name, acptr->name, parv[2]);
+4 -5
View File
@@ -113,6 +113,8 @@ CMD_FUNC(m_chgident)
if ((acptr = find_person(parv[1], NULL)))
{
userhost_save_current(acptr);
switch (UHOST_ALLOWED)
{
case UHALLOW_NEVER:
@@ -133,7 +135,6 @@ CMD_FUNC(m_chgident)
}
break;
case UHALLOW_REJOIN:
rejoin_leave(acptr);
/* join sent later when the ident has been changed */
break;
}
@@ -150,13 +151,11 @@ CMD_FUNC(m_chgident)
GetHost(acptr), parv[2]);
}
sendto_server(cptr, 0, 0, ":%s CHGIDENT %s %s",
sptr->name, acptr->name, parv[2]);
ircsnprintf(acptr->user->username, sizeof(acptr->user->username), "%s", parv[2]);
if (UHOST_ALLOWED == UHALLOW_REJOIN)
rejoin_joinandmode(acptr);
userhost_changed(acptr);
return 0;
}
else
+199 -1
View File
@@ -27,7 +27,8 @@ CMD_FUNC(m_join);
DLLFUNC void _join_channel(aChannel *chptr, aClient *cptr, aClient *sptr, int flags);
CMD_FUNC(_do_join);
DLLFUNC int _can_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *key, char *parv[]);
#define MAXBOUNCE 5 /** Most sensible */
void _userhost_save_current(aClient *sptr);
void _userhost_changed(aClient *sptr);
/* Externs */
extern MODVAR int spamf_ugly_vchanoverride;
@@ -36,6 +37,8 @@ extern int find_invex(aChannel *chptr, aClient *sptr);
/* Local vars */
static int bouncedtimes = 0;
/* Macros */
#define MAXBOUNCE 5 /** Most sensible */
#define MSG_JOIN "JOIN"
ModuleHeader MOD_HEADER(m_join)
@@ -53,6 +56,9 @@ MOD_TEST(m_join)
EfunctionAddVoid(modinfo->handle, EFUNC_JOIN_CHANNEL, _join_channel);
EfunctionAdd(modinfo->handle, EFUNC_DO_JOIN, _do_join);
EfunctionAdd(modinfo->handle, EFUNC_CAN_JOIN, _can_join);
EfunctionAddVoid(modinfo->handle, EFUNC_USERHOST_SAVE_CURRENT, _userhost_save_current);
EfunctionAddVoid(modinfo->handle, EFUNC_USERHOST_CHANGED, _userhost_changed);
return MOD_SUCCESS;
}
@@ -526,3 +532,195 @@ CMD_FUNC(_do_join)
RET(0)
#undef RET
}
/* Additional channel-related functions. I've put it here instead
* of the core so it could be upgraded on the fly should it be necessary.
*/
char *get_chmodes_for_user(aClient *sptr, int flags)
{
static char modebuf[512]; /* returned */
char flagbuf[8]; /* For holding "vhoaq" */
char *p = flagbuf;
char parabuf[512];
int n, i;
if (!flags)
return "";
if (flags & MODE_CHANOWNER)
*p++ = 'q';
if (flags & MODE_CHANPROT)
*p++ = 'a';
if (flags & MODE_CHANOP)
*p++ = 'o';
if (flags & MODE_VOICE)
*p++ = 'v';
if (flags & MODE_HALFOP)
*p++ = 'h';
*p = '\0';
parabuf[0] = '\0';
n = strlen(flagbuf);
if (n)
{
for (i=0; i < n; i++)
{
strlcat(parabuf, sptr->name, sizeof(parabuf));
if (i < n - 1)
strlcat(parabuf, " ", sizeof(parabuf));
}
/* And we have our mode line! */
snprintf(modebuf, sizeof(modebuf), "+%s %s", flagbuf, parabuf);
return modebuf;
}
return "";
}
static char remember_nick[NICKLEN+1];
static char remember_user[USERLEN+1];
static char remember_host[HOSTLEN+1];
/** Save current nick/user/host. Used later by userhost_changed(). */
void _userhost_save_current(aClient *sptr)
{
strlcpy(remember_nick, sptr->name, sizeof(remember_nick));
strlcpy(remember_user, sptr->user->username, sizeof(remember_user));
strlcpy(remember_host, GetHost(sptr), sizeof(remember_host));
}
/** User/Host changed for user.
* Note that userhost_save_current() needs to be called before this
* to save the old username/hostname.
* This userhost_changed() function deals with notifying local clients
* about the user/host change by sending PART+JOIN+MODE if
* set::allow-userhost-change force-rejoin is in use,
* and it wills end "CAP chghost" to such capable clients.
* It will also deal with bumping fakelag for the user since a user/host
* change is costly, doesn't matter if it was self-induced or not.
*
* Please call this function for any user/host change by doing:
* userhost_save_current(acptr);
* << change username or hostname here >>
* userhost_changed(acptr);
*/
void _userhost_changed(aClient *sptr)
{
Membership *channels;
aChannel *chptr;
Member *lp;
aClient *acptr;
int i = 0;
int impact = 0;
char buf[512];
if (strcmp(remember_nick, sptr->name))
{
ircd_log(LOG_ERROR, "[BUG] userhost_changed() was called but without calling userhost_save_current() first! Affected user: %s",
sptr->name);
ircd_log(LOG_ERROR, "Please report above bug on https://bugs.unrealircd.org/");
sendto_realops("[BUG] userhost_changed() was called but without calling userhost_save_current() first! Affected user: %s",
sptr->name);
sendto_realops("Please report above bug on https://bugs.unrealircd.org/");
return; /* We cannot safely process this request anymore */
}
/* It's perfectly acceptable to call us even if the userhost didn't change. */
if (!strcmp(remember_user, sptr->user->username) && !strcmp(remember_host, GetHost(sptr)))
return; /* Nothing to do */
/* Most of the work is only necessary for set::allow-userhost-change force-rejoin */
if (UHOST_ALLOWED == UHALLOW_REJOIN)
{
/* Walk through all channels of this user.. */
for (channels = sptr->user->channel; channels; channels = channels->next)
{
aChannel *chptr = channels->chptr;
int flags = channels->flags;
char *modes;
char partbuf[512]; /* PART */
char joinbuf[512]; /* JOIN */
char modebuf[512]; /* MODE (if any) */
int chanops_only = invisible_user_in_channel(sptr, chptr);
modebuf[0] = '\0';
/* If the user is banned, don't send any rejoins, it would only be annoying */
if (is_banned(sptr, chptr, BANCHK_JOIN))
continue;
/* Prepare buffers for PART, JOIN, MODE */
ircsnprintf(partbuf, sizeof(partbuf), ":%s!%s@%s PART %s :%s",
remember_nick, remember_user, remember_host,
chptr->chname,
"Changing host");
ircsnprintf(joinbuf, sizeof(joinbuf), ":%s!%s@%s JOIN %s",
sptr->name, sptr->user->username, GetHost(sptr), chptr->chname);
modes = get_chmodes_for_user(sptr, flags);
if (modes)
ircsnprintf(modebuf, sizeof(modebuf), ":%s MODE %s %s", me.name, chptr->chname, modes);
for (lp = chptr->members; lp; lp = lp->next)
{
acptr = lp->cptr;
if (acptr == sptr)
continue; /* skip self */
if (!MyConnect(acptr))
continue; /* only locally connected clients */
if (chanops_only && !(lp->flags & (CHFL_CHANOP|CHFL_CHANOWNER|CHFL_CHANPROT)))
continue; /* skip non-ops if requested to (used for mode +D) */
if (acptr->local->proto & PROTO_CAP_CHGHOST)
continue; /* we notify 'CAP chghost' users in a different way, so don't send it here. */
impact++;
sendbufto_one(acptr, partbuf, 0);
sendbufto_one(acptr, joinbuf, 0);
if (*modebuf)
sendbufto_one(acptr, modebuf, 0);
}
}
}
/* Now deal with "CAP chghost" clients.
* This only needs to be sent one per "common channel".
* This would normally call sendto_common_channels_local_butone() but the user already
* has the new user/host.. so we do it here..
*/
ircsnprintf(buf, sizeof(buf), ":%s!%s@%s CHGHOST %s %s",
remember_nick, remember_user, remember_host,
sptr->user->username,
GetHost(sptr));
current_serial++;
for (channels = sptr->user->channel; channels; channels = channels->next)
{
for (lp = channels->chptr->members; lp; lp = lp->next)
{
acptr = lp->cptr;
if (MyClient(acptr) && (acptr->local->proto & PROTO_CAP_CHGHOST) &&
(acptr->local->serial != current_serial) && (sptr != acptr))
{
sendbufto_one(acptr, buf, 0);
acptr->local->serial = current_serial;
}
}
}
/* A userhost change always generates the following network traffic:
* server to server traffic, CAP "chghost" notifications, and
* possibly PART+JOIN+MODE if force-rejoin had work to do.
* We give the user a penalty so they don't flood...
*/
if (impact)
sptr->local->since += 7; /* Resulted in rejoins and such. */
else
sptr->local->since += 4; /* No rejoins */
}
+8 -29
View File
@@ -1599,6 +1599,8 @@ CMD_FUNC(_m_umode)
return 0;
}
userhost_save_current(sptr); /* save host, in case we do any +x/-x or similar */
/* find flags already set for user */
for (i = 0; i <= Usermode_highest; i++)
if ((sptr->umodes & Usermode_Table[i].mode))
@@ -1769,28 +1771,12 @@ CMD_FUNC(_m_umode)
sendto_server(cptr, PROTO_VHP, 0, ":%s SETHOST :%s",
sptr->name, sptr->user->virthost);
if (UHOST_ALLOWED == UHALLOW_REJOIN)
{
/* Damn, this is ugly: we have to restore umodes to old state,
* do the PART and then set umodes to the new modes again.
*/
long newmodes = sptr->umodes;
sptr->umodes = oldumodes;
rejoin_leave(sptr);
sptr->umodes = newmodes;
}
/* Set the vhost */
safefree(sptr->user->virthost);
sptr->user->virthost = strdup(sptr->user->cloakedhost);
if (UHOST_ALLOWED == UHALLOW_REJOIN)
{
sptr->umodes |= UMODE_HIDE;
rejoin_joinandmode(sptr);
if (MyClient(sptr))
sptr->local->since += 7; /* Add fake lag */
}
/* Notify */
userhost_changed(sptr);
if (MyClient(sptr))
sendto_one(sptr, err_str(RPL_HOSTHIDDEN), me.name, sptr->name, sptr->user->virthost);
}
@@ -1798,22 +1784,15 @@ CMD_FUNC(_m_umode)
/* -x */
if (!IsHidden(sptr) && (oldumodes & UMODE_HIDE))
{
if (UHOST_ALLOWED == UHALLOW_REJOIN)
{
/* LOL, this is ugly ;) */
sptr->umodes |= UMODE_HIDE;
rejoin_leave(sptr);
sptr->umodes &= ~UMODE_HIDE;
rejoin_joinandmode(sptr);
if (MyClient(sptr))
sptr->local->since += 7; /* Add fake lag */
}
/* (Re)create the cloaked virthost, because it will be used
* for ban-checking... free+recreate here because it could have
* been a vhost for example. -- Syzop
*/
safefree(sptr->user->virthost);
sptr->user->virthost = strdup(sptr->user->cloakedhost);
/* Notify */
userhost_changed(sptr);
if (MyClient(sptr))
sendto_one(sptr, err_str(RPL_HOSTHIDDEN), me.name, sptr->name, sptr->user->realhost);
}
+3 -3
View File
@@ -128,6 +128,8 @@ CMD_FUNC(m_sethost)
}
{
userhost_save_current(sptr);
switch (UHOST_ALLOWED)
{
case UHALLOW_NEVER:
@@ -147,7 +149,6 @@ CMD_FUNC(m_sethost)
}
break;
case UHALLOW_REJOIN:
rejoin_leave(sptr);
/* join sent later when the host has been changed */
break;
}
@@ -165,8 +166,7 @@ CMD_FUNC(m_sethost)
/* spread it out */
sendto_server(cptr, 0, 0, ":%s SETHOST %s", sptr->name, parv[1]);
if (UHOST_ALLOWED == UHALLOW_REJOIN)
rejoin_joinandmode(sptr);
userhost_changed(sptr);
}
if (MyConnect(sptr))
+4 -3
View File
@@ -155,6 +155,8 @@ CMD_FUNC(m_setident)
}
{
userhost_save_current(sptr);
switch (UHOST_ALLOWED)
{
case UHALLOW_ALWAYS:
@@ -174,7 +176,7 @@ CMD_FUNC(m_setident)
}
break;
case UHALLOW_REJOIN:
rejoin_leave(sptr);
/* dealt with later */
break;
}
@@ -183,8 +185,7 @@ CMD_FUNC(m_setident)
/* spread it out */
sendto_server(cptr, 0, 0, ":%s SETIDENT %s", sptr->name, parv[1]);
if (UHOST_ALLOWED == UHALLOW_REJOIN)
rejoin_joinandmode(sptr);
userhost_changed(sptr);
}
if (MyConnect(sptr))
+4
View File
@@ -387,6 +387,8 @@ int do_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[], int show_c
if (!(acptr = find_person(parv[1], NULL)))
return 0;
userhost_save_current(acptr);
/* initialize setflag to be the user's pre-SVSMODE flags */
for (i = 0; i <= Usermode_highest; i++)
if (Usermode_Table[i].flag && (acptr->umodes & Usermode_Table[i].mode))
@@ -550,6 +552,8 @@ int do_svsmode(aClient *cptr, aClient *sptr, int parc, char *parv[], int show_c
sendto_one(acptr, ":%s MODE %s :%s", sptr->name, acptr->name, buf);
}
userhost_changed(acptr); /* we can safely call this, even if nothing changed */
VERIFY_OPERCOUNT(acptr, "svsmodeX");
return 0;
}
+3 -3
View File
@@ -109,6 +109,8 @@ CMD_FUNC(m_vhost)
{
char olduser[USERLEN+1];
userhost_save_current(sptr);
switch (UHOST_ALLOWED)
{
case UHALLOW_NEVER:
@@ -128,7 +130,6 @@ CMD_FUNC(m_vhost)
}
break;
case UHALLOW_REJOIN:
rejoin_leave(sptr);
/* join sent later when the host has been changed */
break;
}
@@ -162,8 +163,7 @@ CMD_FUNC(m_vhost)
vhost->virtuser ? olduser : sptr->user->username,
sptr->user->realhost, vhost->virtuser ? vhost->virtuser : "",
vhost->virtuser ? "@" : "", vhost->virthost);
if (UHOST_ALLOWED == UHALLOW_REJOIN)
rejoin_joinandmode(sptr);
userhost_changed(sptr);
return 0;
}
if (i == -1)
+3 -4
View File
@@ -65,8 +65,8 @@ void iNAH_host(aClient *sptr, char *host)
if (!sptr->user)
return;
if (UHOST_ALLOWED == UHALLOW_REJOIN)
rejoin_leave(sptr);
userhost_save_current(sptr);
if (sptr->user->virthost)
{
MyFree(sptr->user->virthost);
@@ -77,8 +77,7 @@ void iNAH_host(aClient *sptr, char *host)
sendto_server(&me, 0, 0, ":%s SETHOST :%s", sptr->name, sptr->user->virthost);
sptr->umodes |= UMODE_SETHOST;
if (UHOST_ALLOWED == UHALLOW_REJOIN)
rejoin_joinandmode(sptr);
userhost_changed(sptr);
sendto_one(sptr, err_str(RPL_HOSTHIDDEN), me.name, sptr->name, sptr->user->virthost);
}