unrealircd

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

geoip_classic.c (8024B)

      1 /* GEOIP Classic module
      2  * (C) Copyright 2021 Bram Matthys and the UnrealIRCd team
      3  * License: GPLv2 or later
      4  */
      5 
      6 #include "unrealircd.h"
      7 #include <GeoIP.h>
      8 
      9 ModuleHeader MOD_HEADER
     10   = {
     11 	"geoip_classic",
     12 	"5.0",
     13 	"GEOIP using classic databases", 
     14 	"UnrealIRCd Team",
     15 	"unrealircd-6",
     16     };
     17 
     18 struct geoip_classic_config_s {
     19 	char *v4_db_file;
     20 	char *v6_db_file;
     21 /* for config reading only */
     22 	int have_config;
     23 	int have_ipv4_database;
     24 	int have_ipv6_database;
     25 };
     26 
     27 /* Variables */
     28 
     29 struct geoip_classic_config_s geoip_classic_config;
     30 GeoIP *gi4 = NULL;
     31 GeoIP *gi6 = NULL;
     32 
     33 /* Forward declarations */
     34 int geoip_classic_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
     35 int geoip_classic_configposttest(int *errs);
     36 int geoip_classic_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
     37 void geoip_classic_free(void);
     38 GeoIPResult *geoip_lookup_classic(char *ip);
     39 
     40 int geoip_classic_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
     41 {
     42 	ConfigEntry *cep;
     43 	int errors = 0;
     44 	int i;
     45 	
     46 	if (type != CONFIG_SET)
     47 		return 0;
     48 
     49 	if (!ce || !ce->name)
     50 		return 0;
     51 
     52 	if (strcmp(ce->name, "geoip-classic"))
     53 		return 0;
     54 
     55 	geoip_classic_config.have_config = 1;
     56 
     57 	for (cep = ce->items; cep; cep = cep->next)
     58 	{
     59 		if (!strcmp(cep->name, "ipv4-database"))
     60 		{
     61 			if (geoip_classic_config.have_ipv4_database)
     62 			{
     63 				config_error("%s:%i: duplicate item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name);
     64 				continue;
     65 			}
     66 			if (!is_file_readable(cep->value, PERMDATADIR))
     67 			{
     68 				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));
     69 				errors++;
     70 				continue;
     71 			}
     72 			geoip_classic_config.have_ipv4_database = 1;
     73 			continue;
     74 		}
     75 		if (!strcmp(cep->name, "ipv6-database"))
     76 		{
     77 			if (geoip_classic_config.have_ipv6_database)
     78 			{
     79 				config_error("%s:%i: duplicate item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name);
     80 				continue;
     81 			}
     82 			if (!is_file_readable(cep->value, PERMDATADIR))
     83 			{
     84 				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));
     85 				errors++;
     86 				continue;
     87 			}
     88 			geoip_classic_config.have_ipv6_database = 1;
     89 			continue;
     90 		}
     91 		config_warn("%s:%i: unknown item set::geoip-classic::%s", cep->file->filename, cep->line_number, cep->name);
     92 	}
     93 	
     94 	*errs = errors;
     95 	return errors ? -1 : 1;
     96 }
     97 
     98 int geoip_classic_configposttest(int *errs)
     99 {
    100 	int errors = 0;
    101 	if (geoip_classic_config.have_config)
    102 	{
    103 		if (!geoip_classic_config.have_ipv4_database && !geoip_classic_config.have_ipv6_database)
    104 		{
    105 			config_error("geoip_classic: no database files specified! Remove set::geoip-classic to use defaults");
    106 			errors++;
    107 		}
    108 	} else
    109 	{
    110 		safe_strdup(geoip_classic_config.v4_db_file, "GeoIP.dat");
    111 		safe_strdup(geoip_classic_config.v6_db_file, "GeoIPv6.dat");
    112 
    113 		if (is_file_readable(geoip_classic_config.v4_db_file, PERMDATADIR))
    114 		{
    115 			geoip_classic_config.have_ipv4_database = 1;
    116 		} else
    117 		{
    118 			config_warn("[geoip_classic] cannot open IPv4 database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_classic_config.v4_db_file, strerror(errno));
    119 			safe_free(geoip_classic_config.v4_db_file);
    120 		}
    121 		if (is_file_readable(geoip_classic_config.v6_db_file, PERMDATADIR))
    122 		{
    123 			geoip_classic_config.have_ipv6_database = 1;
    124 		} else
    125 		{
    126 			config_warn("[geoip_classic] cannot open IPv6 database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_classic_config.v6_db_file, strerror(errno));
    127 			safe_free(geoip_classic_config.v6_db_file);
    128 		}
    129 		if (!geoip_classic_config.have_ipv4_database && !geoip_classic_config.have_ipv6_database)
    130 		{
    131 			config_error("[geoip_classic] couldn't read any database! Either put these in %s location "
    132 					"or specify another in set::geoip-classic config block", PERMDATADIR);
    133 			errors++;
    134 		}
    135 	}
    136 
    137 	*errs = errors;
    138 	return errors ? -1 : 1;
    139 }
    140 
    141 int geoip_classic_configrun(ConfigFile *cf, ConfigEntry *ce, int type)
    142 {
    143 	ConfigEntry *cep;
    144 
    145 	if (type != CONFIG_SET)
    146 		return 0;
    147 
    148 	if (!ce || !ce->name)
    149 		return 0;
    150 
    151 	if (strcmp(ce->name, "geoip-classic"))
    152 		return 0;
    153 
    154 	for (cep = ce->items; cep; cep = cep->next)
    155 	{
    156 		if (!strcmp(cep->name, "ipv4-database") && geoip_classic_config.have_ipv4_database)
    157 			safe_strdup(geoip_classic_config.v4_db_file, cep->value);
    158 		if (!strcmp(cep->name, "ipv6-database") && geoip_classic_config.have_ipv6_database)
    159 			safe_strdup(geoip_classic_config.v6_db_file, cep->value);
    160 	}
    161 	return 1;
    162 }
    163 
    164 MOD_TEST()
    165 {
    166 	MARK_AS_OFFICIAL_MODULE(modinfo);
    167 	if (!CallbackAddPVoid(modinfo->handle, CALLBACKTYPE_GEOIP_LOOKUP, TO_PVOIDFUNC(geoip_lookup_classic)))
    168 	{
    169 		unreal_log(ULOG_ERROR, "geoip_classic", "GEOIP_ADD_CALLBACK_FAILED", NULL,
    170 		           "geoip_classic: Could not install GEOIP_LOOKUP callback. "
    171 		           "Most likely another geoip module is already loaded. "
    172 		           "You can only load one!");
    173 		return MOD_FAILED;
    174 	}
    175 
    176 	geoip_classic_config.have_config = 0;
    177 	geoip_classic_config.have_ipv4_database = 0;
    178 	geoip_classic_config.have_ipv6_database = 0;
    179 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_classic_configtest);
    180 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_classic_configposttest);
    181 	return MOD_SUCCESS;
    182 }
    183 
    184 MOD_INIT()
    185 {
    186 	MARK_AS_OFFICIAL_MODULE(modinfo);
    187 	geoip_classic_free();
    188 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_classic_configrun);
    189 	return MOD_SUCCESS;
    190 }
    191 
    192 MOD_LOAD()
    193 {
    194 	int found_good_file = 0;
    195 
    196 	if (geoip_classic_config.v4_db_file)
    197 	{
    198 		convert_to_absolute_path(&geoip_classic_config.v4_db_file, PERMDATADIR);
    199 		gi4 = GeoIP_open(geoip_classic_config.v4_db_file, GEOIP_STANDARD | GEOIP_CHECK_CACHE | GEOIP_SILENCE);
    200 		if (gi4)
    201 		{
    202 			found_good_file = 1;
    203 		} else
    204 		{
    205 			int save_err = errno;
    206 			unreal_log(ULOG_WARNING, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL,
    207 				       "[IPv4] Could not open '$filename': $system_error",
    208 				       log_data_string("filename", geoip_classic_config.v4_db_file),
    209 				       log_data_string("system_error", strerror(save_err)));
    210 		}
    211 	}
    212 	if (geoip_classic_config.v6_db_file)
    213 	{
    214 		convert_to_absolute_path(&geoip_classic_config.v6_db_file, PERMDATADIR);
    215 		gi6 = GeoIP_open(geoip_classic_config.v6_db_file, GEOIP_STANDARD | GEOIP_CHECK_CACHE | GEOIP_SILENCE);
    216 		if (gi6)
    217 		{
    218 			found_good_file = 1;
    219 		} else
    220 		{
    221 			int save_err = errno;
    222 			unreal_log(ULOG_WARNING, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL,
    223 				       "[IPv6] Could not open '$filename': $system_error",
    224 				       log_data_string("filename", geoip_classic_config.v6_db_file),
    225 				       log_data_string("system_error", strerror(save_err)));
    226 		}
    227 		convert_to_absolute_path(&geoip_classic_config.v6_db_file, PERMDATADIR);
    228 	}
    229 
    230 	if (!found_good_file)
    231 	{
    232 		unreal_log(ULOG_ERROR, "geoip_classic", "GEOIP_CANNOT_OPEN_DB", NULL,
    233 					"could not open any database!");
    234 		return MOD_FAILED;
    235 	}
    236 	return MOD_SUCCESS;
    237 }
    238 
    239 MOD_UNLOAD()
    240 {
    241 	geoip_classic_free();
    242 	return MOD_SUCCESS;
    243 }
    244 
    245 void geoip_classic_free(void)
    246 {
    247 	if (gi4)
    248 		GeoIP_delete(gi4);
    249 	if (gi6)
    250 		GeoIP_delete(gi6);
    251 	gi4 = NULL;
    252 	gi6 = NULL;
    253 	safe_free(geoip_classic_config.v4_db_file);
    254 	safe_free(geoip_classic_config.v6_db_file);
    255 }
    256 
    257 GeoIPResult *geoip_lookup_classic(char *ip)
    258 {
    259 	static char buf[256];
    260 	const char *country_code, *country_name;
    261 	GeoIPLookup gl;
    262 	GeoIP *gi;
    263 	int geoid;
    264 	GeoIPResult *r;
    265 
    266 	if (!ip)
    267 		return NULL;
    268 
    269 	if (strchr(ip, ':'))
    270 	{
    271 		if (!gi6)
    272 			return NULL;
    273 		geoid = GeoIP_id_by_addr_v6_gl(gi6, ip, &gl);
    274 		gi = gi6;
    275 	} else
    276 	{
    277 		if (!gi4 || !strcmp(ip, "255.255.255.255"))
    278 			return NULL;
    279 		geoid = GeoIP_id_by_addr_gl(gi4, ip, &gl);
    280 		gi = gi4;
    281 	}
    282 
    283 	if (geoid == 0)
    284 		return NULL;
    285 
    286 	country_code = GeoIP_code_by_id(geoid);
    287 	country_name = GeoIP_country_name_by_id(gi, geoid);
    288 
    289 	if (!country_code || !country_name)
    290 		return NULL;
    291 
    292 	r = safe_alloc(sizeof(GeoIPResult));
    293 	safe_strdup(r->country_code, country_code);
    294 	safe_strdup(r->country_name, country_name);
    295 	return r;
    296 }
    297