pissircd/src/modules/geoip_classic.c

298 lines
7.8 KiB
C

/* GEOIP Classic module
* (C) Copyright 2021 Bram Matthys and the UnrealIRCd team
* License: GPLv2 or later
*/
#include "unrealircd.h"
#include <GeoIP.h>
ModuleHeader MOD_HEADER
= {
"geoip_classic",
"5.0",
"GEOIP using classic databases",
"UnrealIRCd Team",
"unrealircd-6",
};
struct geoip_classic_config_s {
char *v4_db_file;
char *v6_db_file;
/* for config reading only */
int have_config;
int have_ipv4_database;
int have_ipv6_database;
};
/* Variables */
struct geoip_classic_config_s geoip_classic_config;
GeoIP *gi4 = NULL;
GeoIP *gi6 = NULL;
/* Forward declarations */
int geoip_classic_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
int geoip_classic_configposttest(int *errs);
int geoip_classic_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
void geoip_classic_free(void);
GeoIPResult *geoip_lookup_classic(char *ip);
int geoip_classic_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-classic"))
return 0;
geoip_classic_config.have_config = 1;
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "ipv4-database"))
{
if (geoip_classic_config.have_ipv4_database)
{
config_error("%s:%i: duplicate item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name);
continue;
}
if (!is_file_readable(cep->value, PERMDATADIR))
{
config_error("%s:%i: set::geoip-classic::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno));
errors++;
continue;
}
geoip_classic_config.have_ipv4_database = 1;
continue;
}
if (!strcmp(cep->name, "ipv6-database"))
{
if (geoip_classic_config.have_ipv6_database)
{
config_error("%s:%i: duplicate item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name);
continue;
}
if (!is_file_readable(cep->value, PERMDATADIR))
{
config_error("%s:%i: set::geoip-classic::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno));
errors++;
continue;
}
geoip_classic_config.have_ipv6_database = 1;
continue;
}
config_warn("%s:%i: unknown item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name);
}
*errs = errors;
return errors ? -1 : 1;
}
int geoip_classic_configposttest(int *errs)
{
int errors = 0;
if (geoip_classic_config.have_config)
{
if (!geoip_classic_config.have_ipv4_database && !geoip_classic_config.have_ipv6_database)
{
config_error("geoip_classic: no database files specified! Remove set::geoip-classic to use defaults");
errors++;
}
} else
{
safe_strdup(geoip_classic_config.v4_db_file, "GeoIP.dat");
safe_strdup(geoip_classic_config.v6_db_file, "GeoIPv6.dat");
if (is_file_readable(geoip_classic_config.v4_db_file, PERMDATADIR))
{
geoip_classic_config.have_ipv4_database = 1;
} else
{
config_warn("[geoip_classic] cannot open IPv4 database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_classic_config.v4_db_file, strerror(errno));
safe_free(geoip_classic_config.v4_db_file);
}
if (is_file_readable(geoip_classic_config.v6_db_file, PERMDATADIR))
{
geoip_classic_config.have_ipv6_database = 1;
} else
{
config_warn("[geoip_classic] cannot open IPv6 database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_classic_config.v6_db_file, strerror(errno));
safe_free(geoip_classic_config.v6_db_file);
}
if (!geoip_classic_config.have_ipv4_database && !geoip_classic_config.have_ipv6_database)
{
config_error("[geoip_classic] couldn't read any database! Either put these in %s location "
"or specify another in set::geoip-classic config block", PERMDATADIR);
errors++;
}
}
*errs = errors;
return errors ? -1 : 1;
}
int geoip_classic_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-classic"))
return 0;
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "ipv4-database") && geoip_classic_config.have_ipv4_database)
safe_strdup(geoip_classic_config.v4_db_file, cep->value);
if (!strcmp(cep->name, "ipv6-database") && geoip_classic_config.have_ipv6_database)
safe_strdup(geoip_classic_config.v6_db_file, cep->value);
}
return 1;
}
MOD_TEST()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
if (!CallbackAddPVoid(modinfo->handle, CALLBACKTYPE_GEOIP_LOOKUP, TO_PVOIDFUNC(geoip_lookup_classic)))
{
unreal_log(ULOG_ERROR, "geoip_classic", "GEOIP_ADD_CALLBACK_FAILED", NULL,
"geoip_classic: Could not install GEOIP_LOOKUP callback. "
"Most likely another geoip module is already loaded. "
"You can only load one!");
return MOD_FAILED;
}
geoip_classic_config.have_config = 0;
geoip_classic_config.have_ipv4_database = 0;
geoip_classic_config.have_ipv6_database = 0;
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_classic_configtest);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_classic_configposttest);
return MOD_SUCCESS;
}
MOD_INIT()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
geoip_classic_free();
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_classic_configrun);
return MOD_SUCCESS;
}
MOD_LOAD()
{
int found_good_file = 0;
if (geoip_classic_config.v4_db_file)
{
convert_to_absolute_path(&geoip_classic_config.v4_db_file, PERMDATADIR);
gi4 = GeoIP_open(geoip_classic_config.v4_db_file, GEOIP_STANDARD | GEOIP_CHECK_CACHE | GEOIP_SILENCE);
if (gi4)
{
found_good_file = 1;
} else
{
int save_err = errno;
unreal_log(ULOG_WARNING, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL,
"[IPv4] Could not open '$filename': $system_error",
log_data_string("filename", geoip_classic_config.v4_db_file),
log_data_string("system_error", strerror(save_err)));
}
}
if (geoip_classic_config.v6_db_file)
{
convert_to_absolute_path(&geoip_classic_config.v6_db_file, PERMDATADIR);
gi6 = GeoIP_open(geoip_classic_config.v6_db_file, GEOIP_STANDARD | GEOIP_CHECK_CACHE | GEOIP_SILENCE);
if (gi6)
{
found_good_file = 1;
} else
{
int save_err = errno;
unreal_log(ULOG_WARNING, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL,
"[IPv6] Could not open '$filename': $system_error",
log_data_string("filename", geoip_classic_config.v6_db_file),
log_data_string("system_error", strerror(save_err)));
}
convert_to_absolute_path(&geoip_classic_config.v6_db_file, PERMDATADIR);
}
if (!found_good_file)
{
unreal_log(ULOG_ERROR, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL,
"could not open any database!");
return MOD_FAILED;
}
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
geoip_classic_free();
return MOD_SUCCESS;
}
void geoip_classic_free(void)
{
if (gi4)
GeoIP_delete(gi4);
if (gi6)
GeoIP_delete(gi6);
gi4 = NULL;
gi6 = NULL;
safe_free(geoip_classic_config.v4_db_file);
safe_free(geoip_classic_config.v6_db_file);
}
GeoIPResult *geoip_lookup_classic(char *ip)
{
static char buf[256];
const char *country_code, *country_name;
GeoIPLookup gl;
GeoIP *gi;
int geoid;
GeoIPResult *r;
if (!ip)
return NULL;
if (strchr(ip, ':'))
{
if (!gi6)
return NULL;
geoid = GeoIP_id_by_addr_v6_gl(gi6, ip, &gl);
gi = gi6;
} else
{
if (!gi4 || !strcmp(ip, "255.255.255.255"))
return NULL;
geoid = GeoIP_id_by_addr_gl(gi4, ip, &gl);
gi = gi4;
}
if (geoid == 0)
return NULL;
country_code = GeoIP_code_by_id(geoid);
country_name = GeoIP_country_name_by_id(gi, geoid);
if (!country_code || !country_name)
return NULL;
r = safe_alloc(sizeof(GeoIPResult));
safe_strdup(r->country_code, country_code);
safe_strdup(r->country_name, country_name);
return r;
}