mirror of https://github.com/pissnet/pissircd.git
352 lines
8.0 KiB
C
352 lines
8.0 KiB
C
/*
|
|
* GEOIP Base module, needed for all geoip functions
|
|
* as this stores the geo information in ModData.
|
|
* (C) Copyright 2021-.. Syzop and The UnrealIRCd Team
|
|
* License: GPLv2 or later
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"geoip_base",
|
|
"5.0",
|
|
"Base module for geoip",
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
struct geoip_base_config_s {
|
|
int check_on_load;
|
|
};
|
|
|
|
/* Forward declarations */
|
|
void geoip_base_free(ModData *m);
|
|
const char *geoip_base_serialize(ModData *m);
|
|
void geoip_base_unserialize(const char *str, ModData *m);
|
|
int geoip_base_handshake(Client *client);
|
|
int geoip_base_ip_change(Client *client, const char *oldip);
|
|
int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list);
|
|
int geoip_connect_extinfo(Client *client, NameValuePrioList **list);
|
|
int geoip_json_expand_client(Client *client, int detail, json_t *j);
|
|
int geoip_base_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
|
int geoip_base_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
|
|
EVENT(geoip_base_set_existing_users_evt);
|
|
CMD_FUNC(cmd_geoip);
|
|
|
|
ModDataInfo *geoip_md; /* Module Data structure which we acquire */
|
|
struct geoip_base_config_s geoip_base_config;
|
|
|
|
/* We can use GEOIPDATA() and GEOIPDATARAW() for fast access.
|
|
* People wanting to get this information from outside this module
|
|
* should use geoip_client(client) !
|
|
*/
|
|
|
|
#define GEOIPDATARAW(x) (moddata_client((x), geoip_md).ptr)
|
|
#define GEOIPDATA(x) ((GeoIPResult *)moddata_client((x), geoip_md).ptr)
|
|
|
|
int geoip_base_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
|
|
{
|
|
ConfigEntry *cep;
|
|
int errors = 0;
|
|
int i;
|
|
|
|
if (type != CONFIG_SET)
|
|
return 0;
|
|
|
|
if (!ce || !ce->name)
|
|
return 0;
|
|
|
|
if (strcmp(ce->name, "geoip"))
|
|
return 0;
|
|
|
|
for (cep = ce->items; cep; cep = cep->next)
|
|
{
|
|
if (!strcmp(cep->name, "check-on-load"))
|
|
{
|
|
CheckNull(cep);
|
|
continue;
|
|
}
|
|
config_warn("%s:%i: unknown item geoip::%s", cep->file->filename, cep->line_number, cep->name);
|
|
}
|
|
|
|
*errs = errors;
|
|
return errors ? -1 : 1;
|
|
}
|
|
|
|
int geoip_base_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
|
|
{
|
|
ConfigEntry *cep;
|
|
|
|
if (type != CONFIG_SET)
|
|
return 0;
|
|
|
|
if (!ce || !ce->name)
|
|
return 0;
|
|
|
|
if (strcmp(ce->name, "geoip"))
|
|
return 0;
|
|
|
|
for (cep = ce->items; cep; cep = cep->next)
|
|
{
|
|
if (!strcmp(cep->name, "check-on-load"))
|
|
geoip_base_config.check_on_load = config_checkval(cep->value, CFG_YESNO);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
MOD_TEST()
|
|
{
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_base_configtest);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_INIT()
|
|
{
|
|
ModDataInfo mreq;
|
|
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
mreq.name = "geoip";
|
|
mreq.free = geoip_base_free;
|
|
mreq.serialize = geoip_base_serialize;
|
|
mreq.unserialize = geoip_base_unserialize;
|
|
mreq.sync = MODDATA_SYNC_EARLY;
|
|
mreq.type = MODDATATYPE_CLIENT;
|
|
geoip_md = ModDataAdd(modinfo->handle, mreq);
|
|
if (!geoip_md)
|
|
abort();
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_base_configrun);
|
|
HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, geoip_base_handshake);
|
|
HookAdd(modinfo->handle, HOOKTYPE_IP_CHANGE, 0, geoip_base_ip_change);
|
|
HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, geoip_base_handshake);
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONNECT_EXTINFO, 1, geoip_connect_extinfo); /* (prio: near-first) */
|
|
HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0,geoip_base_handshake); /* in case the IP changed in registration phase (WEBIRC, HTTP Forwarded) */
|
|
HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, geoip_base_whois);
|
|
HookAdd(modinfo->handle, HOOKTYPE_JSON_EXPAND_CLIENT, 0, geoip_json_expand_client);
|
|
|
|
CommandAdd(modinfo->handle, "GEOIP", cmd_geoip, MAXPARA, CMD_USER);
|
|
|
|
/* set defaults */
|
|
geoip_base_config.check_on_load = 1;
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
/* add info for all users upon module loading if enabled, but delay it a bit for data provider module to load */
|
|
if (geoip_base_config.check_on_load)
|
|
{
|
|
EventAdd(modinfo->handle, "geoip_base_set_existing_users", geoip_base_set_existing_users_evt, NULL, 1000, 1);
|
|
}
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
int geoip_base_handshake(Client *client)
|
|
{
|
|
if (!client->ip)
|
|
return 0;
|
|
GeoIPResult *res = geoip_lookup(client->ip);
|
|
|
|
if (!res)
|
|
return 0;
|
|
|
|
if (GEOIPDATA(client))
|
|
{
|
|
free_geoip_result(GEOIPDATA(client));
|
|
GEOIPDATARAW(client) = NULL;
|
|
}
|
|
GEOIPDATARAW(client) = res;
|
|
return 0;
|
|
}
|
|
|
|
int geoip_base_ip_change(Client *client, const char *oldip)
|
|
{
|
|
geoip_base_handshake(client);
|
|
return 0;
|
|
}
|
|
|
|
void geoip_base_free(ModData *m)
|
|
{
|
|
if (m->ptr)
|
|
{
|
|
free_geoip_result((GeoIPResult *)m->ptr);
|
|
m->ptr = NULL;
|
|
}
|
|
}
|
|
|
|
const char *geoip_base_serialize(ModData *m)
|
|
{
|
|
static char buf[512];
|
|
GeoIPResult *geo;
|
|
|
|
if (!m->ptr)
|
|
return NULL;
|
|
|
|
geo = m->ptr;
|
|
snprintf(buf, sizeof(buf), "cc=%s|cd=%s",
|
|
geo->country_code,
|
|
geo->country_name);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void geoip_base_unserialize(const char *str, ModData *m)
|
|
{
|
|
char buf[512], *p=NULL, *varname, *value;
|
|
char *country_name = NULL;
|
|
char *country_code = NULL;
|
|
GeoIPResult *res;
|
|
|
|
if (m->ptr)
|
|
{
|
|
free_geoip_result((GeoIPResult *)m->ptr);
|
|
m->ptr = NULL;
|
|
}
|
|
if (str == NULL)
|
|
return;
|
|
|
|
strlcpy(buf, str, sizeof(buf));
|
|
for (varname = strtoken(&p, buf, "|"); varname; varname = strtoken(&p, NULL, "|"))
|
|
{
|
|
value = strchr(varname, '=');
|
|
if (!value)
|
|
continue;
|
|
*value++ = '\0';
|
|
if (!strcmp(varname, "cc"))
|
|
country_code = value;
|
|
else if (!strcmp(varname, "cd"))
|
|
country_name = value;
|
|
}
|
|
|
|
if (!country_code || !country_name)
|
|
return; /* does not meet minimum criteria */
|
|
|
|
res = safe_alloc(sizeof(GeoIPResult));
|
|
safe_strdup(res->country_name, country_name);
|
|
safe_strdup(res->country_code, country_code);
|
|
m->ptr = res;
|
|
}
|
|
|
|
EVENT(geoip_base_set_existing_users_evt)
|
|
{
|
|
Client *client;
|
|
list_for_each_entry(client, &client_list, client_node)
|
|
{
|
|
if (MyUser(client))
|
|
geoip_base_handshake(client);
|
|
}
|
|
}
|
|
|
|
int geoip_connect_extinfo(Client *client, NameValuePrioList **list)
|
|
{
|
|
GeoIPResult *geo = GEOIPDATA(client);
|
|
if (geo)
|
|
add_nvplist(list, 0, "country", geo->country_code);
|
|
return 0;
|
|
}
|
|
|
|
int geoip_json_expand_client(Client *client, int detail, json_t *j)
|
|
{
|
|
GeoIPResult *geo = GEOIPDATA(client);
|
|
json_t *geoip;
|
|
|
|
if (!geo)
|
|
return 0;
|
|
|
|
geoip = json_object();
|
|
json_object_set_new(j, "geoip", geoip);
|
|
json_object_set_new(geoip, "country_code", json_string_unreal(geo->country_code));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list)
|
|
{
|
|
GeoIPResult *geo;
|
|
char buf[512];
|
|
int policy = whois_get_policy(client, target, "geo");
|
|
|
|
if (policy == WHOIS_CONFIG_DETAILS_NONE)
|
|
return 0;
|
|
|
|
geo = GEOIPDATA(target);
|
|
if (!geo)
|
|
return 0;
|
|
|
|
// we only have country atm, but if we add city then city goes in 'full' and
|
|
// country goes in 'limited'
|
|
// if policy == WHOIS_CONFIG_DETAILS_LIMITED ...
|
|
add_nvplist_numeric_fmt(list, 0, "geo", client, RPL_WHOISCOUNTRY,
|
|
"%s %s :is connecting from %s",
|
|
target->name,
|
|
geo->country_code,
|
|
geo->country_name);
|
|
return 0;
|
|
}
|
|
|
|
CMD_FUNC(cmd_geoip)
|
|
{
|
|
const char *ip = NULL;
|
|
Client *target;
|
|
GeoIPResult *res;
|
|
|
|
if (!IsOper(client))
|
|
{
|
|
sendnumeric(client, ERR_NOPRIVILEGES);
|
|
return;
|
|
}
|
|
|
|
if ((parc < 2) || BadPtr(parv[1]))
|
|
{
|
|
/* Maybe some report */
|
|
return;
|
|
}
|
|
|
|
if (strchr(parv[1], '.') || strchr(parv[1], ':'))
|
|
{
|
|
ip = parv[1];
|
|
} else {
|
|
target = find_user(parv[1], NULL);
|
|
if (!target)
|
|
{
|
|
sendnumeric(client, ERR_NOSUCHNICK, parv[1]);
|
|
return;
|
|
}
|
|
ip = target->ip;
|
|
if (!ip)
|
|
{
|
|
sendnotice(client, "User %s has no known IP address", client->name); // (eg: services bot)
|
|
return;
|
|
}
|
|
}
|
|
|
|
res = geoip_lookup(ip);
|
|
|
|
sendnotice(client, "*** GEOIP information for IP %s ***", ip);
|
|
if (!res)
|
|
{
|
|
sendnotice(client, "- No information available");
|
|
return;
|
|
} else {
|
|
if (res->country_code)
|
|
sendnotice(client, "- Country code: %s", res->country_code);
|
|
if (res->country_name)
|
|
sendnotice(client, "- Country name: %s", res->country_name);
|
|
}
|
|
|
|
free_geoip_result(res);
|
|
|
|
sendnotice(client, "*** End of information ***");
|
|
}
|