unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
geoip_maxmind.c (6503B)
1 /* GEOIP maxmind module 2 * (C) Copyright 2021 Bram Matthys and the UnrealIRCd team 3 * License: GPLv2 or later 4 */ 5 6 #include "unrealircd.h" 7 #include <maxminddb.h> 8 9 ModuleHeader MOD_HEADER 10 = { 11 "geoip_maxmind", 12 "5.0", 13 "GEOIP using maxmind databases", 14 "UnrealIRCd Team", 15 "unrealircd-6", 16 }; 17 18 struct geoip_maxmind_config_s { 19 char *db_file; 20 /* for config reading only */ 21 int have_config; 22 int have_database; 23 }; 24 25 /* Variables */ 26 27 struct geoip_maxmind_config_s geoip_maxmind_config; 28 MMDB_s mmdb; 29 30 /* Forward declarations */ 31 int geoip_maxmind_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); 32 int geoip_maxmind_configposttest(int *errs); 33 int geoip_maxmind_configrun(ConfigFile *cf, ConfigEntry *ce, int type); 34 void geoip_maxmind_free(void); 35 GeoIPResult *geoip_lookup_maxmind(char *ip); 36 37 int geoip_maxmind_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 38 { 39 ConfigEntry *cep; 40 int errors = 0; 41 int i; 42 43 if (type != CONFIG_SET) 44 return 0; 45 46 if (!ce || !ce->name) 47 return 0; 48 49 if (strcmp(ce->name, "geoip-maxmind")) 50 return 0; 51 52 geoip_maxmind_config.have_config = 1; 53 54 for (cep = ce->items; cep; cep = cep->next) 55 { 56 if (!strcmp(cep->name, "database")) 57 { 58 if (geoip_maxmind_config.have_database) 59 { 60 config_error("%s:%i: duplicate item set::geoip-maxmind::%s", cep->file->filename, cep->line_number, cep->name); 61 continue; 62 } 63 if (!is_file_readable(cep->value, PERMDATADIR)) 64 { 65 config_error("%s:%i: set::geoip-maxmind::%s: cannot open file \"%s/%s\" for reading (%s)", cep->file->filename, cep->line_number, cep->name, PERMDATADIR, cep->value, strerror(errno)); 66 errors++; 67 continue; 68 } 69 geoip_maxmind_config.have_database = 1; 70 continue; 71 } 72 config_warn("%s:%i: unknown item set::geoip-maxmind::%s", cep->file->filename, cep->line_number, cep->name); 73 } 74 75 *errs = errors; 76 return errors ? -1 : 1; 77 } 78 79 int geoip_maxmind_configposttest(int *errs) 80 { 81 int errors = 0; 82 if (geoip_maxmind_config.have_config) 83 { 84 if (!geoip_maxmind_config.have_database) 85 { 86 config_error("geoip_maxmind: no database file specified! Remove set::geoip-maxmind to use defaults"); 87 errors++; 88 } 89 } else 90 { 91 safe_strdup(geoip_maxmind_config.db_file, "GeoLite2-Country.mmdb"); 92 93 if (is_file_readable(geoip_maxmind_config.db_file, PERMDATADIR)) 94 { 95 geoip_maxmind_config.have_database = 1; 96 } else 97 { 98 config_error("[geoip_maxmind] cannot open database file \"%s/%s\" for reading (%s)", PERMDATADIR, geoip_maxmind_config.db_file, strerror(errno)); 99 safe_free(geoip_maxmind_config.db_file); 100 errors++; 101 } 102 } 103 104 *errs = errors; 105 return errors ? -1 : 1; 106 } 107 108 int geoip_maxmind_configrun(ConfigFile *cf, ConfigEntry *ce, int type) 109 { 110 ConfigEntry *cep; 111 112 if (type != CONFIG_SET) 113 return 0; 114 115 if (!ce || !ce->name) 116 return 0; 117 118 if (strcmp(ce->name, "geoip-maxmind")) 119 return 0; 120 121 for (cep = ce->items; cep; cep = cep->next) 122 { 123 if (!strcmp(cep->name, "database") && geoip_maxmind_config.have_database) 124 safe_strdup(geoip_maxmind_config.db_file, cep->value); 125 } 126 return 1; 127 } 128 129 MOD_TEST() 130 { 131 MARK_AS_OFFICIAL_MODULE(modinfo); 132 if (!CallbackAddPVoid(modinfo->handle, CALLBACKTYPE_GEOIP_LOOKUP, TO_PVOIDFUNC(geoip_lookup_maxmind))) 133 { 134 unreal_log(ULOG_ERROR, "geoip_maxmind", "GEOIP_ADD_CALLBACK_FAILED", NULL, 135 "geoip_maxmind: Could not install GEOIP_LOOKUP callback. " 136 "Most likely another geoip module is already loaded. " 137 "You can only load one!"); 138 return MOD_FAILED; 139 } 140 141 geoip_maxmind_config.have_config = 0; 142 geoip_maxmind_config.have_database = 0; 143 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, geoip_maxmind_configtest); 144 HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, geoip_maxmind_configposttest); 145 return MOD_SUCCESS; 146 } 147 148 MOD_INIT() 149 { 150 MARK_AS_OFFICIAL_MODULE(modinfo); 151 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, geoip_maxmind_configrun); 152 return MOD_SUCCESS; 153 } 154 155 MOD_LOAD() 156 { 157 geoip_maxmind_free(); 158 convert_to_absolute_path(&geoip_maxmind_config.db_file, PERMDATADIR); 159 160 int status = MMDB_open(geoip_maxmind_config.db_file, MMDB_MODE_MMAP, &mmdb); 161 162 if (status != MMDB_SUCCESS) { 163 int save_err = errno; 164 unreal_log(ULOG_WARNING, "geoip_maxmind", "GEOIP_CANNOT_OPEN_DB", NULL, 165 "Could not open '$filename' - $maxmind_error; IO error: $io_error", 166 log_data_string("filename", geoip_maxmind_config.db_file), 167 log_data_string("maxmind_error", MMDB_strerror(status)), 168 log_data_string("io_error", (status == MMDB_IO_ERROR)?strerror(save_err):"none")); 169 return MOD_FAILED; 170 } 171 return MOD_SUCCESS; 172 } 173 174 MOD_UNLOAD() 175 { 176 geoip_maxmind_free(); 177 return MOD_SUCCESS; 178 } 179 180 void geoip_maxmind_free(void) 181 { 182 MMDB_close(&mmdb); 183 } 184 185 GeoIPResult *geoip_lookup_maxmind(char *ip) 186 { 187 int gai_error, mmdb_error, status; 188 MMDB_lookup_result_s result; 189 MMDB_entry_data_s country_code; 190 MMDB_entry_data_s country_name; 191 char *country_code_str, *country_name_str; 192 GeoIPResult *r; 193 194 if (!ip) 195 return NULL; 196 197 result = MMDB_lookup_string(&mmdb, ip, &gai_error, &mmdb_error); 198 if (gai_error) 199 { 200 unreal_log(ULOG_DEBUG, "geoip_maxmind", "GEOIP_DB_ERROR", NULL, 201 "libmaxminddb: getaddrinfo error for $ip: $error", 202 log_data_string("ip", ip), 203 log_data_string("error", gai_strerror(gai_error))); 204 return NULL; 205 } 206 207 if (mmdb_error != MMDB_SUCCESS) 208 { 209 unreal_log(ULOG_DEBUG, "geoip_maxmind", "GEOIP_DB_ERROR", NULL, 210 "libmaxminddb: library error for $ip: $error", 211 log_data_string("ip", ip), 212 log_data_string("error", MMDB_strerror(mmdb_error))); 213 return NULL; 214 } 215 216 if (!result.found_entry) /* no result */ 217 return NULL; 218 219 status = MMDB_get_value(&result.entry, &country_code, "country", "iso_code", NULL); 220 if (status != MMDB_SUCCESS || !country_code.has_data || country_code.type != MMDB_DATA_TYPE_UTF8_STRING) 221 return NULL; 222 status = MMDB_get_value(&result.entry, &country_name, "country", "names", "en", NULL); 223 if (status != MMDB_SUCCESS || !country_name.has_data || country_name.type != MMDB_DATA_TYPE_UTF8_STRING) 224 return NULL; 225 226 /* these results are not null-terminated */ 227 country_code_str = safe_alloc(country_code.data_size + 1); 228 country_name_str = safe_alloc(country_name.data_size + 1); 229 memcpy(country_code_str, country_code.utf8_string, country_code.data_size); 230 country_code_str[country_code.data_size] = '\0'; 231 memcpy(country_name_str, country_name.utf8_string, country_name.data_size); 232 country_name_str[country_name.data_size] = '\0'; 233 234 r = safe_alloc(sizeof(GeoIPResult)); 235 r->country_code = country_code_str; 236 r->country_name = country_name_str; 237 return r; 238 } 239