unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

geoip_base.c (8183B)

      1 /*
      2  * GEOIP Base module, needed for all geoip functions
      3  * as this stores the geo information in ModData.
      4  * (C) Copyright 2021-.. Syzop and The UnrealIRCd Team
      5  * License: GPLv2 or later
      6  */
      7 
      8 #include "unrealircd.h"
      9 
     10 ModuleHeader MOD_HEADER
     11   = {
     12 	"geoip_base",
     13 	"5.0",
     14 	"Base module for geoip",
     15 	"UnrealIRCd Team",
     16 	"unrealircd-6",
     17     };
     18 
     19 struct geoip_base_config_s {
     20 	int check_on_load;
     21 };
     22 
     23 /* Forward declarations */
     24 void geoip_base_free(ModData *m);
     25 const char *geoip_base_serialize(ModData *m);
     26 void geoip_base_unserialize(const char *str, ModData *m);
     27 int geoip_base_handshake(Client *client);
     28 int geoip_base_ip_change(Client *client, const char *oldip);
     29 int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list);
     30 int geoip_connect_extinfo(Client *client, NameValuePrioList **list);
     31 int geoip_json_expand_client(Client *client, int detail, json_t *j);
     32 int geoip_base_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
     33 int geoip_base_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
     34 EVENT(geoip_base_set_existing_users_evt);
     35 CMD_FUNC(cmd_geoip);
     36 
     37 ModDataInfo *geoip_md; /* Module Data structure which we acquire */
     38 struct geoip_base_config_s geoip_base_config;
     39 
     40 /* We can use GEOIPDATA() and GEOIPDATARAW() for fast access.
     41  * People wanting to get this information from outside this module
     42  * should use geoip_client(client) !
     43  */
     44 
     45 #define GEOIPDATARAW(x)	(moddata_client((x), geoip_md).ptr)
     46 #define GEOIPDATA(x)	((GeoIPResult *)moddata_client((x), geoip_md).ptr)
     47 
     48 int geoip_base_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
     49 {
     50 	ConfigEntry *cep;
     51 	int errors = 0;
     52 	int i;
     53 	
     54 	if (type != CONFIG_SET)
     55 		return 0;
     56 
     57 	if (!ce || !ce->name)
     58 		return 0;
     59 
     60 	if (strcmp(ce->name, "geoip"))
     61 		return 0;
     62 
     63 	for (cep = ce->items; cep; cep = cep->next)
     64 	{
     65 		if (!strcmp(cep->name, "check-on-load"))
     66 		{
     67 			CheckNull(cep);
     68 			continue;
     69 		}
     70 		config_warn("%s:%i: unknown item geoip::%s", cep->file->filename, cep->line_number, cep->name);
     71 	}
     72 	
     73 	*errs = errors;
     74 	return errors ? -1 : 1;
     75 }
     76 
     77 int geoip_base_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
     78 {
     79 	ConfigEntry *cep;
     80 
     81 	if (type != CONFIG_SET)
     82 		return 0;
     83 
     84 	if (!ce || !ce->name)
     85 		return 0;
     86 
     87 	if (strcmp(ce->name, "geoip"))
     88 		return 0;
     89 
     90 	for (cep = ce->items; cep; cep = cep->next)
     91 	{
     92 		if (!strcmp(cep->name, "check-on-load"))
     93 			geoip_base_config.check_on_load = config_checkval(cep->value, CFG_YESNO);
     94 	}
     95 	return 1;
     96 }
     97 
     98 MOD_TEST()
     99 {
    100 	MARK_AS_OFFICIAL_MODULE(modinfo);
    101 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_base_configtest);
    102 	return MOD_SUCCESS;
    103 }
    104 
    105 MOD_INIT()
    106 {
    107 	ModDataInfo mreq;
    108 
    109 	MARK_AS_OFFICIAL_MODULE(modinfo);
    110 
    111 	memset(&mreq, 0, sizeof(mreq));
    112 	mreq.name = "geoip";
    113 	mreq.free = geoip_base_free;
    114 	mreq.serialize = geoip_base_serialize;
    115 	mreq.unserialize = geoip_base_unserialize;
    116 	mreq.sync = MODDATA_SYNC_EARLY;
    117 	mreq.type = MODDATATYPE_CLIENT;
    118 	geoip_md = ModDataAdd(modinfo->handle, mreq);
    119 	if (!geoip_md)
    120 		abort();
    121 
    122 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_base_configrun);
    123 	HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, 0, geoip_base_handshake);
    124 	HookAdd(modinfo->handle, HOOKTYPE_IP_CHANGE, 0, geoip_base_ip_change);
    125 	HookAdd(modinfo->handle, HOOKTYPE_SERVER_HANDSHAKE_OUT, 0, geoip_base_handshake);
    126 	HookAdd(modinfo->handle, HOOKTYPE_CONNECT_EXTINFO, 1, geoip_connect_extinfo); /* (prio: near-first) */
    127 	HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0,geoip_base_handshake); /* in case the IP changed in registration phase (WEBIRC, HTTP Forwarded) */
    128 	HookAdd(modinfo->handle, HOOKTYPE_WHOIS, 0, geoip_base_whois);
    129 	HookAdd(modinfo->handle, HOOKTYPE_JSON_EXPAND_CLIENT, 0, geoip_json_expand_client);
    130 
    131 	CommandAdd(modinfo->handle, "GEOIP", cmd_geoip, MAXPARA, CMD_USER);
    132 
    133 	/* set defaults */
    134 	geoip_base_config.check_on_load = 1;
    135 
    136 	return MOD_SUCCESS;
    137 }
    138 
    139 MOD_LOAD()
    140 {
    141 	/* add info for all users upon module loading if enabled, but delay it a bit for data provider module to load */
    142 	if (geoip_base_config.check_on_load)
    143 	{
    144 		EventAdd(modinfo->handle, "geoip_base_set_existing_users", geoip_base_set_existing_users_evt, NULL, 1000, 1);
    145 	}
    146 	return MOD_SUCCESS;
    147 }
    148 
    149 
    150 MOD_UNLOAD()
    151 {
    152 	return MOD_SUCCESS;
    153 }
    154 
    155 int geoip_base_handshake(Client *client)
    156 {
    157 	if (!client->ip)
    158 		return 0;
    159 	GeoIPResult *res = geoip_lookup(client->ip);
    160 
    161 	if (!res)
    162 		return 0;
    163 
    164 	if (GEOIPDATA(client))
    165 	{
    166 		free_geoip_result(GEOIPDATA(client));
    167 		GEOIPDATARAW(client) = NULL;
    168 	}
    169 	GEOIPDATARAW(client) = res;
    170 	return 0;
    171 }
    172 
    173 int geoip_base_ip_change(Client *client, const char *oldip)
    174 {
    175 	geoip_base_handshake(client);
    176 	return 0;
    177 }
    178 
    179 void geoip_base_free(ModData *m)
    180 {
    181 	if (m->ptr)
    182 	{
    183 		free_geoip_result((GeoIPResult *)m->ptr);
    184 		m->ptr = NULL;
    185 	}
    186 }
    187 
    188 const char *geoip_base_serialize(ModData *m)
    189 {
    190 	static char buf[512];
    191 	GeoIPResult *geo;
    192 
    193 	if (!m->ptr)
    194 		return NULL;
    195 
    196 	geo = m->ptr;
    197 	snprintf(buf, sizeof(buf), "cc=%s|cd=%s",
    198 	         geo->country_code,
    199 	         geo->country_name);
    200 
    201 	return buf;
    202 }
    203 
    204 void geoip_base_unserialize(const char *str, ModData *m)
    205 {
    206 	char buf[512], *p=NULL, *varname, *value;
    207 	char *country_name = NULL;
    208 	char *country_code = NULL;
    209 	GeoIPResult *res;
    210 
    211 	if (m->ptr)
    212 	{
    213 		free_geoip_result((GeoIPResult *)m->ptr);
    214 		m->ptr = NULL;
    215 	}
    216 	if (str == NULL)
    217 		return;
    218 
    219 	strlcpy(buf, str, sizeof(buf));
    220 	for (varname = strtoken(&p, buf, "|"); varname; varname = strtoken(&p, NULL, "|"))
    221 	{
    222 		value = strchr(varname, '=');
    223 		if (!value)
    224 			continue;
    225 		*value++ = '\0';
    226 		if (!strcmp(varname, "cc"))
    227 			country_code = value;
    228 		else if (!strcmp(varname, "cd"))
    229 			country_name = value;
    230 	}
    231 
    232 	if (!country_code || !country_name)
    233 		return; /* does not meet minimum criteria */
    234 
    235 	res = safe_alloc(sizeof(GeoIPResult));
    236 	safe_strdup(res->country_name, country_name);
    237 	safe_strdup(res->country_code, country_code);
    238 	m->ptr = res;
    239 }
    240 
    241 EVENT(geoip_base_set_existing_users_evt)
    242 {
    243 	Client *client;
    244 	list_for_each_entry(client, &client_list, client_node)
    245 	{
    246 		if (MyUser(client))
    247 			geoip_base_handshake(client);
    248 	}
    249 }
    250 
    251 int geoip_connect_extinfo(Client *client, NameValuePrioList **list)
    252 {
    253 	GeoIPResult *geo = GEOIPDATA(client);
    254 	if (geo)
    255 		add_nvplist(list, 0, "country", geo->country_code);
    256 	return 0;
    257 }
    258 
    259 int geoip_json_expand_client(Client *client, int detail, json_t *j)
    260 {
    261 	GeoIPResult *geo = GEOIPDATA(client);
    262 	json_t *geoip;
    263 
    264 	if (!geo)
    265 		return 0;
    266 
    267 	geoip = json_object();
    268 	json_object_set_new(j, "geoip", geoip);
    269 	json_object_set_new(geoip, "country_code", json_string_unreal(geo->country_code));
    270 
    271 	return 0;
    272 }
    273 
    274 int geoip_base_whois(Client *client, Client *target, NameValuePrioList **list)
    275 {
    276 	GeoIPResult *geo;
    277 	char buf[512];
    278 	int policy = whois_get_policy(client, target, "geo");
    279 
    280 	if (policy == WHOIS_CONFIG_DETAILS_NONE)
    281 		return 0;
    282 
    283 	geo = GEOIPDATA(target);
    284 	if (!geo)
    285 		return 0;
    286 
    287 	// we only have country atm, but if we add city then city goes in 'full' and
    288 	// country goes in 'limited'
    289 	// if policy == WHOIS_CONFIG_DETAILS_LIMITED ...
    290 	add_nvplist_numeric_fmt(list, 0, "geo", client, RPL_WHOISCOUNTRY,
    291 	                        "%s %s :is connecting from %s",
    292 	                        target->name,
    293 	                        geo->country_code,
    294 	                        geo->country_name);
    295 	return 0;
    296 }
    297 
    298 CMD_FUNC(cmd_geoip)
    299 {
    300 	const char *ip = NULL;
    301 	Client *target;
    302 	GeoIPResult *res;
    303 
    304 	if (!IsOper(client))
    305 	{
    306 		sendnumeric(client, ERR_NOPRIVILEGES);
    307 		return;
    308 	}
    309 
    310 	if ((parc < 2) || BadPtr(parv[1]))
    311 	{
    312 		/* Maybe some report */
    313 		return;
    314 	}
    315 
    316 	if (strchr(parv[1], '.') || strchr(parv[1], ':'))
    317 	{
    318 		ip = parv[1];
    319 	} else {
    320 		target = find_user(parv[1], NULL);
    321 		if (!target)
    322 		{
    323 			sendnumeric(client, ERR_NOSUCHNICK, parv[1]);
    324 			return;
    325 		}
    326 		ip = target->ip;
    327 		if (!ip)
    328 		{
    329 			sendnotice(client, "User %s has no known IP address", client->name); // (eg: services bot)
    330 			return;
    331 		}
    332 	}
    333 
    334 	res = geoip_lookup(ip);
    335 
    336 	sendnotice(client, "*** GEOIP information for IP %s ***", ip);
    337 	if (!res)
    338 	{
    339 		sendnotice(client, "- No information available");
    340 		return;
    341 	} else {
    342 		if (res->country_code)
    343 			sendnotice(client, "- Country code: %s", res->country_code);
    344 		if (res->country_name)
    345 			sendnotice(client, "- Country name: %s", res->country_name);
    346 	}
    347 
    348 	free_geoip_result(res);
    349 
    350 	sendnotice(client, "*** End of information ***");
    351 }