0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-25 23:14:13 +01:00

Refactor whowas.

This commit is contained in:
Jason Volk 2016-08-24 19:52:37 -07:00
parent 5f218cdbb2
commit 9c16de2d41
12 changed files with 275 additions and 258 deletions

View file

@ -94,8 +94,8 @@ struct client
struct client *servptr; /* Points to server this Client is on */
struct client *from; /* == self, if Local Client, *NEVER* NULL! */
rb_dlink_list whowas_clist;
// unique whowas id associating any history to *this (TODO: replace with connid)
whowas::id_t wwid;
time_t tsinfo; /* TS on the nick, SVINFO on server */
mode::mode mode;
uint64_t flags; /* client flags */
@ -927,7 +927,7 @@ extern void del_all_accepts(client *client_p);
extern void dead_link(client *client_p, int sendqex);
extern int show_ip(client *source_p, client *target_p);
extern int show_ip_conf(struct ConfItem *aconf, client *source_p);
extern int show_ip_whowas(struct Whowas *whowas, client *source_p);
extern int show_ip_whowas(const whowas::whowas &, client &source);
extern void close_connection(client *);
extern void init_uid(void);

View file

@ -52,7 +52,6 @@ namespace ircd
}
struct ConfItem;
struct Whowas;
struct DNSReply;
struct Listener;
struct Blacklist;
@ -75,6 +74,7 @@ namespace ircd
#include "match.h"
#include "cache.h"
#include "whowas.h"
#include "tgchange.h"
#include "msgbuf.h"
#include "msg.h"
@ -119,6 +119,5 @@ namespace ircd
#include "substitution.h"
#include "supported.h"
#include "s_user.h"
#include "whowas.h"
#include "wsproc.h"
#include "stringops.h"

View file

@ -5,6 +5,7 @@
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
* Copyright (C) 1996-2002 Hybrid Development Team
* Copyright (C) 2002-2004 ircd-ratbox development team
* Copyright (C) 2016 Charybdis Development Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -26,73 +27,60 @@
#define HAVE_IRCD_WHOWAS_H
#ifdef __cplusplus
namespace ircd {
namespace ircd {
namespace whowas {
struct User;
using client::client;
using id_t = uint64_t;
/*
lets speed this up...
also removed away information. *tough*
- Dianora
/* lets speed this up... also removed away information. *tough*
* - Dianora
*/
struct Whowas
struct whowas
{
struct whowas_top *wtop;
rb_dlink_node wnode; /* for the wtop linked list */
rb_dlink_node cnode; /* node for online clients */
rb_dlink_node whowas_node; /* node for the whowas linked list */
enum flag
{
IP_SPOOFING = 0x01,
DYNSPOOF = 0x02,
};
id_t wwid; // Unique whowas index ID
client *online; // Pointer to online client or nullptr
time_t logoff;
std::shared_ptr<cache::serv::entry> scache;
char name[NICKLEN + 1];
char username[USERLEN + 1];
char hostname[HOSTLEN + 1];
char sockhost[HOSTIPLEN + 1];
char realname[REALLEN + 1];
char suser[NICKLEN + 1];
unsigned char flags;
std::shared_ptr<cache::serv::entry> scache;
time_t logoff;
client::client *online; /* Pointer to new nickname for chasing or NULL */
enum flag flag;
whowas(client &client);
};
/* Flags */
#define WHOWAS_IP_SPOOFING 0x1
#define WHOWAS_DYNSPOOF 0x2
// Get a full history for nickname (may contain different users!)
std::vector<std::shared_ptr<whowas>> history(const std::string &name, const time_t &limit = 0, const bool &online = false);
/*
** initwhowas
*/
extern void whowas_init(void);
// Get a full history for this unique whowas ID. This is effectively similar to
// a lookup by a client pointer address; allocators may reuse the same address,
// so this ID is used as the index instead.
std::vector<std::shared_ptr<whowas>> history(const id_t &wwid);
/*
** add_history
** Add the currently defined name of the client to history.
** usually called before changing to a new name (nick).
** Client must be a fully registered user (specifically,
** the user structure must have been allocated).
*/
void whowas_add_history(client::client *, int);
// Get a full history for this client (must be online);
std::vector<std::shared_ptr<whowas>> history(const client &);
/*
** off_history
** This must be called when the client structure is about to
** be released. History mechanism keeps pointers to client
** structures and it must know when they cease to exist. This
** also implicitly calls AddHistory.
*/
void whowas_off_history(client::client *);
// Add whowas information about client *before* a nick change or logoff.
void add(client &);
/*
** get_history
** Return the current client that was using the given
** nickname within the timelimit. Returns NULL, if no
** one found...
*/
client::client *whowas_get_history(const char *, time_t);
/* Nick name */
/* Time limit in seconds */
// Notify this subsystem the client pointer is about to be invalidated (does not call add())
void off(client &);
rb_dlink_list *whowas_get_list(const char *name);
void whowas_set_size(int whowas_length);
void whowas_memory_usage(size_t *count, size_t *memused);
// Util
void memory_usage(size_t *const &count, size_t *const &memused);
void set_size(const size_t &max);
void init();
} // namespace whowas
} // namespace ircd
#endif // __cplusplus

View file

@ -90,6 +90,24 @@ using namespace ircd;
client::client::client()
:servptr{0}
,from{nullptr}
,wwid{0}
,tsinfo{0}
,mode{(umode)0}
,flags{(enum flags)0}
,snomask{0}
,hopcount{0}
,status{(enum status)0}
,handler{0}
,serial{0}
,first_received_message_time{0}
,received_number_of_privmsgs{0}
,flood_noticed{0}
,localClient{nullptr}
,preClient{nullptr}
,large_ctcp_sent{0}
,certfp{nullptr}
{
}
@ -773,7 +791,7 @@ client::resv_nick_fnc(const char *mask, const char *reason, int temp_time)
/* Do all of the nick-changing gymnastics. */
client_p->tsinfo = rb_current_time();
whowas_add_history(client_p, 1);
whowas::add(*client_p);
monitor_signoff(client_p);
@ -954,15 +972,17 @@ client::find_chasing(client *source_p, const char *user, int *chasing)
if(who || rfc1459::is_digit(*user))
return who;
if(!(who = whowas_get_history(user, (long) KILLCHASETIMELIMIT)))
const auto history(whowas::history(user, KILLCHASETIMELIMIT));
if(history.empty())
{
sendto_one_numeric(source_p, ERR_NOSUCHNICK,
form_str(ERR_NOSUCHNICK), user);
return (NULL);
sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), user);
return nullptr;
}
if(chasing)
*chasing = 1;
return who;
return history.back()->online;
}
/*
@ -1323,8 +1343,8 @@ client::exit_generic_client(client *client_p, client *source_p, client *from,
/* Clean up allow lists */
del_all_accepts(source_p);
whowas_add_history(source_p, 0);
whowas_off_history(source_p);
whowas::add(*source_p);
whowas::off(*source_p);
monitor_signoff(source_p);
@ -1806,14 +1826,16 @@ client::show_ip_conf(struct ConfItem *aconf, client *source_p)
}
int
client::show_ip_whowas(struct Whowas *whowas, client *source_p)
client::show_ip_whowas(const whowas::whowas &whowas, client &source)
{
if(whowas->flags & WHOWAS_IP_SPOOFING)
if(ConfigFileEntry.hide_spoof_ips || !my_oper(*source_p))
if(whowas.flag & whowas.IP_SPOOFING)
if(ConfigFileEntry.hide_spoof_ips || !my_oper(source))
return 0;
if(whowas->flags & WHOWAS_DYNSPOOF)
if(!is(*source_p, umode::OPER))
if(whowas.flag & whowas.DYNSPOOF)
if(!is(source, umode::OPER))
return 0;
return 1;
}

View file

@ -630,7 +630,7 @@ charybdis_main(int argc, char * const argv[])
init_hook();
chan::init();
initclass();
whowas_init();
whowas::init();
init_reject();
cache::init();
init_monitor();

View file

@ -1569,7 +1569,7 @@ change_nick_user_host(client::client *target_p, const char *nick, const char *us
rb_strlcpy(target_p->host, host, sizeof target_p->host);
if (changed)
whowas_add_history(target_p, 1);
whowas::add(*target_p);
del_from_client_hash(target_p->name, target_p);
rb_strlcpy(target_p->name, nick, NICKLEN);

View file

@ -5,7 +5,9 @@
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
* Copyright (C) 1996-2002 Hybrid Development Team
* Copyright (C) 2002-2012 ircd-ratbox development team
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 William Pitcock <nenolod@dereferenced.org>
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,185 +25,175 @@
* USA
*/
namespace ircd {
namespace ircd {
namespace whowas {
struct whowas_top
id_t id_ctr = 1;
size_t list_max;
std::multimap<id_t, std::shared_ptr<struct whowas>> ids;
std::multimap<std::string, std::shared_ptr<struct whowas>, rfc1459::less> nicks;
void trim();
} // namespace whowas
} // namespace ircd
using namespace ircd;
void
whowas::init()
{
char *name;
rb_dlink_list wwlist;
};
static rb_radixtree *whowas_tree = NULL;
static rb_dlink_list whowas_list = {NULL, NULL, 0};
static unsigned int whowas_list_length = NICKNAMEHISTORYLENGTH;
static void whowas_trim(void *unused);
static void
whowas_free_wtop(struct whowas_top *wtop)
{
if(rb_dlink_list_length(&wtop->wwlist) == 0)
{
rb_radixtree_delete(whowas_tree, wtop->name);
rb_free(wtop->name);
rb_free(wtop);
}
}
static struct whowas_top *
whowas_get_top(const char *name)
{
struct whowas_top *wtop;
wtop = (whowas_top *)rb_radixtree_retrieve(whowas_tree, name);
if (wtop != NULL)
return wtop;
wtop = (whowas_top *)rb_malloc(sizeof(struct whowas_top));
wtop->name = rb_strdup(name);
rb_radixtree_add(whowas_tree, wtop->name, wtop);
return wtop;
}
rb_dlink_list *
whowas_get_list(const char *name)
{
struct whowas_top *wtop;
wtop = (whowas_top *)rb_radixtree_retrieve(whowas_tree, name);
if(wtop == NULL)
return NULL;
return &wtop->wwlist;
if(!list_max)
list_max = NICKNAMEHISTORYLENGTH;
}
void
whowas_add_history(client::client *client_p, int online)
whowas::set_size(const size_t &max)
{
struct whowas_top *wtop;
struct Whowas *who;
s_assert(NULL != client_p);
if(client_p == NULL)
return;
/* trim some of the entries if we're getting well over our history length */
if(rb_dlink_list_length(&whowas_list) > whowas_list_length + 100)
whowas_trim(NULL);
wtop = whowas_get_top(client_p->name);
who = (Whowas *)rb_malloc(sizeof(struct Whowas));
who->wtop = wtop;
who->logoff = rb_current_time();
rb_strlcpy(who->name, client_p->name, sizeof(who->name));
rb_strlcpy(who->username, client_p->username, sizeof(who->username));
rb_strlcpy(who->hostname, client_p->host, sizeof(who->hostname));
rb_strlcpy(who->realname, client_p->info, sizeof(who->realname));
rb_strlcpy(who->sockhost, client_p->sockhost, sizeof(who->sockhost));
who->flags = (is_ip_spoof(*client_p) ? WHOWAS_IP_SPOOFING : 0) |
(is_dyn_spoof(*client_p) ? WHOWAS_DYNSPOOF : 0);
who->scache = nameinfo(serv(*client_p));
if(online)
{
who->online = client_p;
rb_dlinkAdd(who, &who->cnode, &client_p->whowas_clist);
}
else
who->online = NULL;
rb_dlinkAdd(who, &who->wnode, &wtop->wwlist);
rb_dlinkAdd(who, &who->whowas_node, &whowas_list);
list_max = max;
trim();
}
void
whowas_off_history(client::client *client_p)
whowas::memory_usage(size_t *const &count,
size_t *const &memused)
{
rb_dlink_node *ptr, *next;
RB_DLINK_FOREACH_SAFE(ptr, next, client_p->whowas_clist.head)
{
struct Whowas *who = (Whowas *)ptr->data;
who->online = NULL;
rb_dlinkDelete(&who->cnode, &client_p->whowas_clist);
}
*count = ids.size();
*memused = ids.size() * sizeof(struct whowas);
}
client::client *
whowas_get_history(const char *nick, time_t timelimit)
void
whowas::off(client &client)
{
struct whowas_top *wtop;
rb_dlink_node *ptr;
wtop = (whowas_top *)rb_radixtree_retrieve(whowas_tree, nick);
if(wtop == NULL)
return NULL;
timelimit = rb_current_time() - timelimit;
RB_DLINK_FOREACH_PREV(ptr, wtop->wwlist.tail)
const auto &id(client.wwid);
const auto ppit(ids.equal_range(id));
std::for_each(ppit.first, ppit.second, []
(const auto &pit)
{
struct Whowas *who = (Whowas *)ptr->data;
if(who->logoff >= timelimit)
auto &whowas(pit.second);
whowas->online = nullptr;
});
}
void
whowas::add(client &client)
{
// trim some of the entries if we're getting well over our history length
trim();
// Client's wwid will be 0 if never seen before
auto &id(client.wwid);
if(!id)
id = id_ctr++;
// This is an unconditional add to both maps.
auto it(ids.lower_bound(id));
it = ids.emplace_hint(it, id, std::make_shared<struct whowas>(client));
nicks.emplace(client.name, it->second);
}
std::vector<std::shared_ptr<whowas::whowas>>
whowas::history(const std::string &nick,
const time_t &limit,
const bool &online)
{
const auto ppit(nicks.equal_range(nick));
const auto num(std::distance(ppit.first, ppit.second));
std::vector<std::shared_ptr<struct whowas>> ret(num);
std::transform(ppit.first, ppit.second, begin(ret), []
(const auto &pit)
{
return pit.second;
});
// C++11 says the multimap has stronger ordering and preserves
// the insert order, which should already be the logoff time, so stuff below
// this comment can be optimized at a later pass.
std::sort(begin(ret), end(ret), []
(const auto &a, const auto &b)
{
return a->logoff < b->logoff;
});
const auto e(std::remove_if(begin(ret), end(ret), [&limit, &online]
(const auto &whowas)
{
if(online && !whowas->online)
return true;
if(limit && whowas->logoff + limit < rb_current_time())
return true;
return false;
}));
ret.erase(e, end(ret));
return ret;
}
std::vector<std::shared_ptr<whowas::whowas>>
whowas::history(const client &client)
{
return history(client.wwid);
}
std::vector<std::shared_ptr<whowas::whowas>>
whowas::history(const id_t &wwid)
{
const auto ppit(ids.equal_range(wwid));
const auto num(std::distance(ppit.first, ppit.second));
std::vector<std::shared_ptr<struct whowas>> ret(num);
std::transform(ppit.first, ppit.second, begin(ret), []
(const auto &pit)
{
return pit.second;
});
return ret;
}
void
whowas::trim()
{
// Trims by oldest ID until satisfied.
auto it(begin(ids));
while(it != end(ids) && nicks.size() > list_max)
{
const auto &id(it->first);
const auto &whowas(it->second);
const auto nick_ppit(nicks.equal_range(whowas->name));
for(auto pit(nick_ppit.first); pit != nick_ppit.second; )
{
return who->online;
const auto &nick_whowas(pit->second);
if(nick_whowas->wwid == whowas->wwid)
nicks.erase(pit++);
else
++pit;
}
}
return NULL;
}
static void
whowas_trim(void *unused)
{
long over;
if(rb_dlink_list_length(&whowas_list) < whowas_list_length)
return;
over = rb_dlink_list_length(&whowas_list) - whowas_list_length;
/* remove whowas entries over the configured length */
for(long i = 0; i < over; i++)
{
if(whowas_list.tail != NULL && whowas_list.tail->data != NULL)
{
struct Whowas *twho = (Whowas *)whowas_list.tail->data;
if(twho->online != NULL)
rb_dlinkDelete(&twho->cnode, &twho->online->whowas_clist);
rb_dlinkDelete(&twho->wnode, &twho->wtop->wwlist);
rb_dlinkDelete(&twho->whowas_node, &whowas_list);
whowas_free_wtop(twho->wtop);
rb_free(twho);
}
ids.erase(it++);
}
}
void
whowas_init(void)
whowas::whowas::whowas(client &client)
:wwid{client.wwid}
,online{&client}
,logoff{rb_current_time()}
,scache
{
whowas_tree = rb_radixtree_create("whowas", irccasecanon);
if(whowas_list_length == 0)
{
whowas_list_length = NICKNAMEHISTORYLENGTH;
}
rb_event_add("whowas_trim", whowas_trim, NULL, 10);
client.serv? nameinfo(serv(client)) : nullptr
}
void
whowas_set_size(int len)
,flag
{
whowas_list_length = len;
whowas_trim(NULL);
is_ip_spoof(client)? IP_SPOOFING : (enum flag)0 |
is_dyn_spoof(client)? DYNSPOOF : (enum flag)0
}
void
whowas_memory_usage(size_t * count, size_t * memused)
{
*count = rb_dlink_list_length(&whowas_list);
*memused += *count * sizeof(struct Whowas);
*memused += sizeof(struct whowas_top) * rb_radixtree_size(whowas_tree);
}
rb_strlcpy(name, client.name, sizeof(name));
rb_strlcpy(username, client.username, sizeof(username));
rb_strlcpy(hostname, client.host, sizeof(hostname));
rb_strlcpy(realname, client.info, sizeof(realname));
rb_strlcpy(sockhost, client.sockhost, sizeof(sockhost));
assert(wwid);
}

View file

@ -88,16 +88,21 @@ mo_kill(struct MsgBuf *msgbuf_p, client::client &client, client::client &source,
** rewrite the KILL for this new nickname--this keeps
** servers in synch when nick change and kill collide
*/
if((target_p = whowas_get_history(user, (long) KILLCHASETIMELIMIT)) == NULL)
const auto history(whowas::history(user, KILLCHASETIMELIMIT, true));
if(history.empty())
{
if (strchr(user, '.'))
sendto_one_numeric(&source, ERR_CANTKILLSERVER, form_str(ERR_CANTKILLSERVER));
else
sendto_one_numeric(&source, ERR_NOSUCHNICK,
form_str(ERR_NOSUCHNICK), user);
sendto_one_numeric(&source, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK),
user);
return;
}
sendto_one_notice(&source, ":KILL changed from %s to %s", user, target_p->name);
target_p = history.back()->online;
sendto_one_notice(&source, ":KILL changed from %s to %s",
user,
target_p->name);
}
if(!my_connect(*target_p) && (!IsOperGlobalKill(&source)))
@ -205,12 +210,20 @@ ms_kill(struct MsgBuf *msgbuf_p, client::client &client, client::client &source,
* not an uid, automatically rewrite the KILL for this new nickname.
* --this keeps servers in synch when nick change and kill collide
*/
if(rfc1459::is_digit(*user) || (!(target_p = whowas_get_history(user, (long) KILLCHASETIMELIMIT))))
if(rfc1459::is_digit(*user))
{
sendto_one_numeric(&source, ERR_NOSUCHNICK,
form_str(ERR_NOSUCHNICK), rfc1459::is_digit(*user) ? "*" : user);
sendto_one_numeric(&source, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*");
return;
}
const auto history(whowas::history(user, KILLCHASETIMELIMIT, true));
if(history.empty())
{
sendto_one_numeric(&source, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), user);
return;
}
target_p = history.back()->online;
sendto_one_notice(&source, ":KILL changed from %s to %s", user, target_p->name);
}

View file

@ -648,7 +648,7 @@ change_local_nick(client::client &client, client::client &source,
/* send the nick change to servers.. */
if(source.user)
{
whowas_add_history(&source, 1);
whowas::add(source);
if (dosend)
{
@ -708,7 +708,7 @@ change_remote_nick(client::client &client, client::client &source,
if(source.user)
{
whowas_add_history(&source, 1);
whowas::add(source);
if (dosend)
{
sendto_server(&client, NULL, CAP_TS6, NOCAPS, ":%s NICK %s :%ld",

View file

@ -223,7 +223,7 @@ doit:
target_p->name, target_p->username,
target_p->host, parv[2]);
whowas_add_history(target_p, 1);
whowas::add(*target_p);
sendto_server(NULL, NULL, CAP_TS6, NOCAPS, ":%s NICK %s :%ld",
use_id(target_p), parv[2], (long) target_p->tsinfo);

View file

@ -1322,7 +1322,7 @@ stats_memory (client::client &source)
size_t total_memory = 0;
whowas_memory_usage(&ww, &wwm);
whowas::memory_usage(&ww, &wwm);
RB_DLINK_FOREACH(ptr, global_client_list.head)
{

View file

@ -89,44 +89,47 @@ m_whowas(struct MsgBuf *msgbuf_p, client::client &client, client::client &source
nick = parv[1];
sendq_limit = get_sendq(&client) * 9 / 10;
whowas_list = whowas_get_list(nick);
if(whowas_list == NULL)
const auto history(whowas::history(nick));
if(history.empty())
{
sendto_one_numeric(&source, ERR_WASNOSUCHNICK, form_str(ERR_WASNOSUCHNICK), nick);
sendto_one_numeric(&source, RPL_ENDOFWHOWAS, form_str(RPL_ENDOFWHOWAS), parv[1]);
return;
}
RB_DLINK_FOREACH(ptr, whowas_list->head)
for(const auto &ww : history)
{
struct Whowas *temp = (Whowas *)ptr->data;
if(cur > 0 && rb_linebuf_len(&client.localClient->buf_sendq) > sendq_limit)
{
sendto_one(&source, form_str(ERR_TOOMANYMATCHES),
me.name, source.name, "WHOWAS");
me.name,
source.name,
"WHOWAS");
break;
}
sendto_one(&source, form_str(RPL_WHOWASUSER),
me.name, source.name, temp->name,
temp->username, temp->hostname, temp->realname);
if (!EmptyString(temp->sockhost) &&
strcmp(temp->sockhost, "0") &&
show_ip_whowas(temp, &source))
sendto_one_numeric(&source, RPL_WHOISACTUALLY,
form_str(RPL_WHOISACTUALLY),
temp->name, temp->sockhost);
me.name,
source.name,
ww->name,
ww->username,
ww->hostname,
ww->realname);
if (!EmptyString(temp->suser))
sendto_one_numeric(&source, RPL_WHOISLOGGEDIN,
"%s %s :was logged in as",
temp->name, temp->suser);
if(!EmptyString(ww->sockhost) && strcmp(ww->sockhost, "0") && show_ip_whowas(*ww, source))
sendto_one_numeric(&source, RPL_WHOISACTUALLY, form_str(RPL_WHOISACTUALLY),
ww->name,
ww->sockhost);
if(!EmptyString(ww->suser))
sendto_one_numeric(&source, RPL_WHOISLOGGEDIN, "%s %s :was logged in as",
ww->name,
ww->suser);
sendto_one_numeric(&source, RPL_WHOISSERVER, form_str(RPL_WHOISSERVER),
temp->name,
temp->scache? name(*temp->scache).c_str() : "*",
rb_ctime(temp->logoff, tbuf, sizeof(tbuf)));
ww->name,
ww->scache? name(*ww->scache).c_str() : "*",
rb_ctime(ww->logoff, tbuf, sizeof(tbuf)));
cur++;
if(max > 0 && cur >= max)