1
0
mirror of https://github.com/osmarks/ngircd.git synced 2025-01-08 14:50:27 +00:00

Rework cloaked hostname handling, implement "METADATA cloakhost"

Now ngIRCd uses two fields internally, one to store the "real" hostname
and one to save the "cloaked" hostname. And both fields can be set
independently using the "METADATA host" and "METADATA cloakhost" commands.

This allows "foreign servers" (aka "IRC services") to alter the real and
cloaked hostnames of clients without problems, even when the user itself
issues additional "MODE +x" and "MODE -x" commands.
This commit is contained in:
Alexander Barton 2012-11-24 16:15:35 +01:00
parent dc89e42ef5
commit 35e2dcff88
6 changed files with 121 additions and 60 deletions

View File

@ -225,6 +225,7 @@ new server link", <serverflag> "M"), even if it doesn't support the given
The following <key> names are defined: The following <key> names are defined:
- "host": the hostname of a client (can't be empty) - "host": the hostname of a client (can't be empty)
- "cloakhost": the cloaked hostname of a client
- "info": info text ("real name") of a client - "info": info text ("real name") of a client
- "user": the user name of a client (can't be empty) - "user": the user name of a client (can't be empty)

View File

@ -671,7 +671,6 @@ Client_OrigUser(CLIENT *Client) {
#endif #endif
/** /**
* Return the hostname of a client. * Return the hostname of a client.
* @param Client Pointer to client structure * @param Client Pointer to client structure
@ -682,8 +681,19 @@ Client_Hostname(CLIENT *Client)
{ {
assert (Client != NULL); assert (Client != NULL);
return Client->host; return Client->host;
} /* Client_Hostname */ }
/**
* Return the cloaked hostname of a client, if set.
* @param Client Pointer to the client structure.
* @return Pointer to the cloaked hostname or NULL if not set.
*/
GLOBAL char *
Client_HostnameCloaked(CLIENT *Client)
{
assert(Client != NULL);
return Client->cloaked;
}
/** /**
* Get (potentially cloaked) hostname of a client to display it to other users. * Get (potentially cloaked) hostname of a client to display it to other users.
@ -698,33 +708,61 @@ Client_Hostname(CLIENT *Client)
GLOBAL char * GLOBAL char *
Client_HostnameDisplayed(CLIENT *Client) Client_HostnameDisplayed(CLIENT *Client)
{ {
static char Cloak_Buffer[CLIENT_HOST_LEN];
assert(Client != NULL); assert(Client != NULL);
/* Client isn't cloaked at all, return real hostname: */ /* Client isn't cloaked at all, return real hostname: */
if (!Client_HasMode(Client, 'x')) if (!Client_HasMode(Client, 'x'))
return Client_Hostname(Client); return Client_Hostname(Client);
/* Client has received METADATA command, so it got the eventually /* Use an already saved cloaked hostname, if there is one */
* cloaked hostname set correctly and this server doesn't need if (Client->cloaked[0])
* to cloak it on its own: */ return Client->cloaked;
if (strchr(Client_Flags(Client), 'M'))
return Client_Hostname(Client);
/* Do simple mapping to the server ID? */ Client_UpdateCloakedHostname(Client, NULL, NULL);
if (!*Conf_CloakHostModeX) return Client->cloaked;
return Client_ID(Client->introducer); }
/**
* Update (and generate, if necessary) the cloaked hostname of a client.
*
* The newly set cloaked hostname is announced in the network using METADATA
* commands to peers that support this feature.
*
* @param Client The client of which the cloaked hostname should be updated.
* @param Origin The originator of the hostname change, or NULL if this server.
* @param Hostname The new cloaked hostname, or NULL if it should be generated.
*/
GLOBAL void
Client_UpdateCloakedHostname(CLIENT *Client, CLIENT *Origin,
const char *Hostname)
{
static char Cloak_Buffer[CLIENT_HOST_LEN];
assert(Client != NULL);
if (!Origin)
Origin = Client_ThisServer();
if (!Hostname) {
/* Generate new cloaked hostname */
if (*Conf_CloakHostModeX) {
strlcpy(Cloak_Buffer, Client->host, CLIENT_HOST_LEN); strlcpy(Cloak_Buffer, Client->host, CLIENT_HOST_LEN);
strlcat(Cloak_Buffer, Conf_CloakHostSalt, CLIENT_HOST_LEN); strlcat(Cloak_Buffer, Conf_CloakHostSalt,
CLIENT_HOST_LEN);
snprintf(Cloak_Buffer, CLIENT_HOST_LEN, Conf_CloakHostModeX, snprintf(Client->cloaked, sizeof(Client->cloaked),
Hash(Cloak_Buffer)); Conf_CloakHostModeX, Hash(Cloak_Buffer));
} else
return Cloak_Buffer; strlcpy(Client->cloaked, Client_ID(Client->introducer),
} /* Client_HostnameCloaked */ sizeof(Client->cloaked));
} else
strlcpy(Client->cloaked, Hostname, sizeof(Client->cloaked));
LogDebug("Cloaked hostname of \"%s\" updated to \"%s\"",
Client_ID(Client), Client->cloaked);
/* Inform other servers in the network */
IRC_WriteStrServersPrefixFlag(Client_NextHop(Origin), Origin, 'M',
"METADATA %s cloakhost :%s",
Client_ID(Client), Client->cloaked);
}
GLOBAL char * GLOBAL char *
Client_Modes( CLIENT *Client ) Client_Modes( CLIENT *Client )

View File

@ -48,6 +48,7 @@ typedef struct _CLIENT
struct _CLIENT *introducer; /* ID of the servers which the client is connected to */ struct _CLIENT *introducer; /* ID of the servers which the client is connected to */
struct _CLIENT *topserver; /* toplevel servers (only valid if client is a server) */ struct _CLIENT *topserver; /* toplevel servers (only valid if client is a server) */
char host[CLIENT_HOST_LEN]; /* hostname of the client */ char host[CLIENT_HOST_LEN]; /* hostname of the client */
char cloaked[CLIENT_HOST_LEN]; /* cloaked hostname of the client */
char user[CLIENT_USER_LEN]; /* user name ("login") */ char user[CLIENT_USER_LEN]; /* user name ("login") */
#if defined(PAM) && defined(IDENTAUTH) #if defined(PAM) && defined(IDENTAUTH)
char orig_user[CLIENT_USER_LEN];/* user name supplied by USER command */ char orig_user[CLIENT_USER_LEN];/* user name supplied by USER command */
@ -107,6 +108,7 @@ GLOBAL char *Client_User PARAMS(( CLIENT *Client ));
GLOBAL char *Client_OrigUser PARAMS(( CLIENT *Client )); GLOBAL char *Client_OrigUser PARAMS(( CLIENT *Client ));
#endif #endif
GLOBAL char *Client_Hostname PARAMS(( CLIENT *Client )); GLOBAL char *Client_Hostname PARAMS(( CLIENT *Client ));
GLOBAL char *Client_HostnameCloaked PARAMS((CLIENT *Client));
GLOBAL char *Client_HostnameDisplayed PARAMS(( CLIENT *Client )); GLOBAL char *Client_HostnameDisplayed PARAMS(( CLIENT *Client ));
GLOBAL char *Client_Modes PARAMS(( CLIENT *Client )); GLOBAL char *Client_Modes PARAMS(( CLIENT *Client ));
GLOBAL char *Client_Flags PARAMS(( CLIENT *Client )); GLOBAL char *Client_Flags PARAMS(( CLIENT *Client ));
@ -166,6 +168,10 @@ GLOBAL void Client_Reject PARAMS((CLIENT *Client, const char *Reason,
bool InformClient)); bool InformClient));
GLOBAL void Client_Introduce PARAMS((CLIENT *From, CLIENT *Client, int Type)); GLOBAL void Client_Introduce PARAMS((CLIENT *From, CLIENT *Client, int Type));
GLOBAL void Client_UpdateCloakedHostname PARAMS((CLIENT *Client,
CLIENT *Originator,
const char *hostname));
#ifdef DEBUG #ifdef DEBUG
GLOBAL void Client_DebugDump PARAMS((void)); GLOBAL void Client_DebugDump PARAMS((void));

View File

@ -66,7 +66,7 @@ IRC_METADATA(CLIENT *Client, REQUEST *Req)
Client_ID(Client), Req->argv[0]); Client_ID(Client), Req->argv[0]);
LogDebug("Got \"METADATA\" command from \"%s\" for client \"%s\": \"%s=%s\".", LogDebug("Got \"METADATA\" command from \"%s\" for client \"%s\": \"%s=%s\".",
Client_ID(Client), Client_ID(target), Client_ID(prefix), Client_ID(target),
Req->argv[1], Req->argv[2]); Req->argv[1], Req->argv[2]);
/* Mark client: it has receiveda a METADATA command */ /* Mark client: it has receiveda a METADATA command */
@ -76,9 +76,23 @@ IRC_METADATA(CLIENT *Client, REQUEST *Req)
Client_SetFlags(target, new_flags); Client_SetFlags(target, new_flags);
} }
if (*Req->argv[2] && strcasecmp(Req->argv[1], "host") == 0) if (strcasecmp(Req->argv[1], "cloakhost") == 0) {
Client_UpdateCloakedHostname(target, prefix, Req->argv[2]);
if (Client_Conn(target) > NONE && Client_HasMode(target, 'x'))
IRC_WriteStrClientPrefix(target, prefix,
RPL_HOSTHIDDEN_MSG, Client_ID(target),
Client_HostnameDisplayed(target));
/* The Client_UpdateCloakedHostname() function already
* forwarded the METADATA command, don't do it twice: */
return CONNECTED;
}
else if (*Req->argv[2] && strcasecmp(Req->argv[1], "host") == 0) {
Client_SetHostname(target, Req->argv[2]); Client_SetHostname(target, Req->argv[2]);
else if (strcasecmp(Req->argv[1], "info") == 0) if (Client_Conn(target) > NONE && !Client_HasMode(target, 'x'))
IRC_WriteStrClientPrefix(target, prefix,
RPL_HOSTHIDDEN_MSG, Client_ID(target),
Client_HostnameDisplayed(target));
} else if (strcasecmp(Req->argv[1], "info") == 0)
Client_SetInfo(target, Req->argv[2]); Client_SetInfo(target, Req->argv[2]);
else if (*Req->argv[2] && strcasecmp(Req->argv[1], "user") == 0) else if (*Req->argv[2] && strcasecmp(Req->argv[1], "user") == 0)
Client_SetUser(target, Req->argv[2], true); Client_SetUser(target, Req->argv[2], true);
@ -88,6 +102,7 @@ IRC_METADATA(CLIENT *Client, REQUEST *Req)
Client_ID(Client), Client_ID(target), Client_ID(Client), Client_ID(target),
Req->argv[1], Req->argv[2]); Req->argv[1], Req->argv[2]);
/* Forward the METADATA command to peers that support it: */
IRC_WriteStrServersPrefixFlag(Client, prefix, 'M', "METADATA %s %s :%s", IRC_WriteStrServersPrefixFlag(Client, prefix, 'M', "METADATA %s %s :%s",
Client_ID(target), Req->argv[1], Req->argv[2]); Client_ID(target), Req->argv[1], Req->argv[2]);
return CONNECTED; return CONNECTED;

View File

@ -36,8 +36,6 @@
#include "irc-mode.h" #include "irc-mode.h"
static void Announce_Client_Hostname PARAMS((CLIENT *Origin, CLIENT *Client));
static bool Client_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin, static bool Client_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
CLIENT *Target)); CLIENT *Target));
static bool Channel_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin, static bool Channel_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
@ -368,9 +366,17 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
"MODE %s :%s", "MODE %s :%s",
Client_ID(Target), Client_ID(Target),
the_modes); the_modes);
if (send_RPL_HOSTHIDDEN_MSG)
Announce_Client_Hostname(Origin, Client);
} }
if (send_RPL_HOSTHIDDEN_MSG && Client_Conn(Target) > NONE) {
/* A new (cloaked) hostname must be annoucned */
IRC_WriteStrClientPrefix(Target, Origin,
RPL_HOSTHIDDEN_MSG,
Client_ID(Target),
Client_HostnameDisplayed(Target));
}
LogDebug("%s \"%s\": Mode change, now \"%s\".", LogDebug("%s \"%s\": Mode change, now \"%s\".",
Client_TypeText(Target), Client_Mask(Target), Client_TypeText(Target), Client_Mask(Target),
Client_Modes(Target)); Client_Modes(Target));
@ -381,27 +387,6 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
} /* Client_Mode */ } /* Client_Mode */
/**
* Announce changed client hostname in the network.
*
* @param Client The client of which the hostname changed.
*/
static void
Announce_Client_Hostname(CLIENT *Origin, CLIENT *Client)
{
assert(Client != NULL);
/* Inform the client itself */
IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG, Client_ID(Client),
Client_HostnameDisplayed(Client));
/* Inform other servers in the network */
IRC_WriteStrServersPrefixFlag(Origin, Client_ThisServer(), 'M',
"METADATA %s host :%s", Client_ID(Client),
Client_HostnameDisplayed(Client));
}
static bool static bool
Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel) Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
{ {

View File

@ -179,24 +179,40 @@ Announce_User(CLIENT * Client, CLIENT * User)
Client_ID(User), Client_ID(User), Client_ID(User), Client_ID(User),
modes); modes);
} }
return CONNECTED;
} else { } else {
/* RFC 2813 mode: one combined NICK or SERVICE command */ /* RFC 2813 mode: one combined NICK or SERVICE command */
if (Client_Type(User) == CLIENT_SERVICE if (Client_Type(User) == CLIENT_SERVICE
&& strchr(Client_Flags(Client), 'S')) && strchr(Client_Flags(Client), 'S')) {
return IRC_WriteStrClient(Client, if (!IRC_WriteStrClient(Client,
"SERVICE %s %d * +%s %d :%s", Client_Mask(User), "SERVICE %s %d * +%s %d :%s",
Client_Mask(User),
Client_MyToken(Client_Introducer(User)), Client_MyToken(Client_Introducer(User)),
Client_Modes(User), Client_Hops(User) + 1, Client_Modes(User), Client_Hops(User) + 1,
Client_Info(User)); Client_Info(User)))
else return DISCONNECTED;
return IRC_WriteStrClient(Client, } else {
if (!IRC_WriteStrClient(Client,
"NICK %s %d %s %s %d +%s :%s", "NICK %s %d %s %s %d +%s :%s",
Client_ID(User), Client_Hops(User) + 1, Client_ID(User), Client_Hops(User) + 1,
Client_User(User), Client_Hostname(User), Client_User(User), Client_Hostname(User),
Client_MyToken(Client_Introducer(User)), Client_MyToken(Client_Introducer(User)),
Client_Modes(User), Client_Info(User)); Client_Modes(User), Client_Info(User)))
return DISCONNECTED;
} }
}
if (strchr(Client_Flags(Client), 'M')) {
/* Synchronize metadata */
if (Client_HostnameCloaked(User)) {
if (!IRC_WriteStrClient(Client,
"METADATA %s cloakhost :%s",
Client_ID(User),
Client_HostnameCloaked(User)))
return DISCONNECTED;
}
}
return CONNECTED;
} /* Announce_User */ } /* Announce_User */