ircu2/ircd/ircd_geoip.c

357 lines
10 KiB
C

/*
* IRC - Internet Relay Chat, ircd/ircd.c
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Computing Center
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file
* @brief GeoIP routines.
* @version $Id$
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include "config.h"
#include "client.h"
#include "ircd_geoip.h"
#include "ircd_features.h"
#include "ircd_string.h"
#include "res.h"
#include "s_debug.h"
#ifdef USE_GEOIP
#include <GeoIP.h>
#endif /* USE_GEOIP */
#ifdef USE_MMDB
#include <maxminddb.h>
#endif /* USE_MMDB */
/** GeoIP structs. */
#ifdef USE_GEOIP
GeoIP *gi4 = NULL;
GeoIP *gi6 = NULL;
#endif /* USE_GEOIP */
#ifdef USE_MMDB
MMDB_s mmdb;
int mmdb_loaded = 0;
#endif /* USE_MMDB */
const char geoip_continent_code[7][3] = {"--", "AF", "AS", "EU", "NA", "OC", "SA"};
const char *geoip_continent_name[7] = {"N/A", "Africa", "Asia", "Europe",
"North America", "Oceania", "South America"};
/** Find continent name by 2 letter code.
* @param[in] cc 2 letter continent code
* @return Continent name
*/
const char* geoip_continent_name_by_code(const char* cc)
{
int i = 0;
for (i=0; i<7; i++) {
if (strncasecmp(cc, (char *)&geoip_continent_code[i], 2) == 0)
break;
}
return geoip_continent_name[i];
}
/** Initialize GeoIP global states.
*/
void geoip_init(void)
{
#ifdef USE_MMDB
int status = 0;
#endif /* USE_MMDB */
#ifdef USE_GEOIP
if (gi4 != NULL)
GeoIP_delete(gi4);
if (gi6 != NULL)
GeoIP_delete(gi6);
gi4 = NULL;
gi6 = NULL;
#endif /* USE_GEOIP */
#ifdef USE_MMDB
if (mmdb_loaded) {
MMDB_close(&mmdb);
mmdb_loaded = 0;
}
#endif /* USE_MMDB */
if (!feature_bool(FEAT_GEOIP_ENABLE))
return;
#ifdef USE_MMDB
status = MMDB_open(feature_str(FEAT_MMDB_FILE), MMDB_MODE_MMAP, &mmdb);
if (MMDB_SUCCESS == status) {
mmdb_loaded = -1;
return;
}
else {
Debug((DEBUG_NOTICE, "Unable to load MaxMindDB file: %s", MMDB_strerror(status)));
if (MMDB_IO_ERROR == status)
Debug((DEBUG_NOTICE, " MaxMindDB IO Error: %s", strerror(errno)));
}
#endif /* USE_MMDB */
#ifdef USE_GEOIP
/* Load IPv4 GeoIP database */
if (feature_str(FEAT_GEOIP_FILE)) {
gi4 = GeoIP_open(feature_str(FEAT_GEOIP_FILE), GEOIP_STANDARD);
}
/* Try to load GeoIP.dat from lib/ if FEAT_GEOIP_FILE was not loaded. */
if (gi4 == NULL)
gi4 = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD);
/* Load IPv6 GeoIP database */
if (feature_str(FEAT_GEOIP_IPV6_FILE)) {
gi6 = GeoIP_open(feature_str(FEAT_GEOIP_IPV6_FILE), GEOIP_STANDARD);
}
/* Try to load GeoIPv6.dat from lib/ if FEAT_GEOIP_IPV6_FILE was not loaded. */
if (gi6 == NULL)
gi6 = GeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_STANDARD);
#endif /* USE_GEOIP */
}
/** Apply GeoIP country data to a client.
* @param[in] cptr Client to apply GeoIP country data to.
*/
void geoip_apply(struct Client* cptr)
{
#ifdef USE_GEOIP
int gcid = 0;
#endif /* USE_GEOIP */
#ifdef USE_GEOIP_GL
GeoIPLookup gl;
#endif /* USE_GEOIP_GL */
#ifdef USE_MMDB
MMDB_lookup_result_s result;
MMDB_entry_data_s entry_data;
int gai_error, mmdb_error, status;
#endif /* USE_MMDB */
if (!feature_bool(FEAT_GEOIP_ENABLE))
return;
if (!(cptr))
return;
#ifdef USE_MMDB
if (mmdb_loaded) {
result = MMDB_lookup_string(&mmdb, ircd_ntoa(&cli_ip(cptr)), &gai_error, &mmdb_error);
if ((gai_error == 0) && (mmdb_error == MMDB_SUCCESS)) {
if (result.found_entry) {
status = MMDB_get_value(&result.entry, &entry_data, "country", "iso_code", NULL);
if ((MMDB_SUCCESS == status) && (entry_data.has_data) && (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING))
ircd_strncpy((char *)&cli_countrycode(cptr), entry_data.utf8_string, (entry_data.data_size > 3 ? 3 : entry_data.data_size));
else
ircd_strncpy((char *)&cli_countrycode(cptr), "--", 3);
status = MMDB_get_value(&result.entry, &entry_data, "country", "names", "en", NULL);
if ((MMDB_SUCCESS == status) && (entry_data.has_data) && (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING))
ircd_strncpy((char *)&cli_countryname(cptr), entry_data.utf8_string, (entry_data.data_size > 256 ? 256 : entry_data.data_size));
else
ircd_strncpy((char *)&cli_countryname(cptr), "Unknown", 8);
status = MMDB_get_value(&result.entry, &entry_data, "continent", "code", NULL);
if ((MMDB_SUCCESS == status) && (entry_data.has_data) && (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING))
ircd_strncpy((char *)&cli_continentcode(cptr), entry_data.utf8_string, (entry_data.data_size > 3 ? 3 : entry_data.data_size));
else
ircd_strncpy((char *)&cli_continentcode(cptr), "--", 3);
status = MMDB_get_value(&result.entry, &entry_data, "continent", "names", "en", NULL);
if ((MMDB_SUCCESS == status) && (entry_data.has_data) && (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING))
ircd_strncpy((char *)&cli_continentname(cptr), entry_data.utf8_string, (entry_data.data_size > 256 ? 256 : entry_data.data_size));
else
ircd_strncpy((char *)&cli_continentname(cptr), "Unknown", 8);
SetGeoIP(cptr);
return;
}
}
}
#endif /* USE_MMDB */
#ifdef USE_GEOIP
if (irc_in_addr_is_ipv4(&cli_ip(cptr))) {
/* User is IPv4 so use gi4. */
if (gi4 != NULL)
#ifdef USE_GEOIP_GL
gcid = GeoIP_id_by_addr_gl(gi4, cli_sock_ip(cptr), &gl);
#else
gcid = GeoIP_id_by_addr(gi4, cli_sock_ip(cptr));
#endif /* USE_GEOIP_GL */
} else {
/* User is IPv6 so use gi6. */
if (gi6 != NULL)
#ifdef USE_GEOIP_GL
gcid = GeoIP_id_by_addr_v6_gl(gi6, cli_sock_ip(cptr), &gl);
#else
gcid = GeoIP_id_by_addr_v6(gi6, cli_sock_ip(cptr));
#endif /* USE_GEOIP_GL */
}
#endif /* USE_GEOIP */
#ifdef USE_GEOIP
if (gcid == 0) {
#endif /* USE_GEOIP */
ircd_strncpy((char *)&cli_countrycode(cptr), "--", 3);
ircd_strncpy((char *)&cli_countryname(cptr), "Unknown", 8);
ircd_strncpy((char *)&cli_continentcode(cptr), "--", 3);
ircd_strncpy((char *)&cli_continentname(cptr), "Unknown", 8);
#ifdef USE_GEOIP
} else {
ircd_strncpy((char *)&cli_countrycode(cptr), GeoIP_code_by_id(gcid), 3);
ircd_strncpy((char *)&cli_countryname(cptr), GeoIP_name_by_id(gcid), 256);
ircd_strncpy((char *)&cli_continentcode(cptr), GeoIP_continent_by_id(gcid), 3);
ircd_strncpy((char *)&cli_continentname(cptr), geoip_continent_name_by_code(GeoIP_continent_by_id(gcid)), 256);
}
#endif /* USE_GEOIP */
SetGeoIP(cptr);
}
/** Apply GeoIP country data to a client from a MARK.
* @param[in] cptr Client to apply GeoIP country data to.
* @param[in] country Country code from MARK.
* @param[in] continent Continent code from MARK.
*/
void geoip_apply_mark(struct Client* cptr, char* country, char* continent, char* countryname)
{
ircd_strncpy((char *)&cli_countrycode(cptr), (!country ? "--" : country), 3);
ircd_strncpy((char *)&cli_continentcode(cptr), (!continent ? "--" : continent), 3);
if (countryname != NULL)
ircd_strncpy((char *)&cli_countryname(cptr), countryname, 256);
else {
#ifdef USE_GEOIP
if (!country || !strcmp(country, "--"))
#endif /* USE_GEOIP */
ircd_strncpy((char *)&cli_countryname(cptr), "Unknown", 8);
#ifdef USE_GEOIP
else
ircd_strncpy((char *)&cli_countryname(cptr), GeoIP_name_by_id(GeoIP_id_by_code(country)), 256);
#endif /* USE_GEOIP */
}
if (!continent || !strcmp(continent, "--"))
ircd_strncpy((char *)&cli_continentname(cptr), "Unknown", 8);
else
ircd_strncpy((char *)&cli_continentname(cptr), geoip_continent_name_by_code(continent), 256);
SetGeoIP(cptr);
}
/** Handle an update to FEAT_GEOIP_ENABLE. */
void geoip_handle_enable(void)
{
geoip_init();
}
/** Handle an update to FEAT_MMDB_FILE. */
void geoip_handle_mmdb_file(void)
{
#ifdef USE_MMDB
int status;
#endif /* USE_MMDB */
if (!feature_bool(FEAT_GEOIP_ENABLE))
return;
#ifdef USE_MMDB
if (mmdb_loaded) {
MMDB_close(&mmdb);
mmdb_loaded = 0;
}
status = MMDB_open(feature_str(FEAT_MMDB_FILE), MMDB_MODE_MMAP, &mmdb);
if (MMDB_SUCCESS == status) {
mmdb_loaded = -1;
return;
}
else {
Debug((DEBUG_NOTICE, "Unable to load MaxMindDB file: %s", MMDB_strerror(status)));
if (MMDB_IO_ERROR == status)
Debug((DEBUG_NOTICE, " MaxMindDB IO Error: %s", strerror(errno)));
}
#endif /* USE_MMDB */
}
/** Handle an update to FEAT_GEOIP_FILE. */
void geoip_handle_file(void)
{
if (!feature_bool(FEAT_GEOIP_ENABLE))
return;
#ifdef USE_GEOIP
if (gi4 != NULL)
GeoIP_delete(gi4);
gi4 = NULL;
/* Load IPv4 GeoIP database */
if (feature_str(FEAT_GEOIP_FILE)) {
gi4 = GeoIP_open(feature_str(FEAT_GEOIP_FILE), GEOIP_STANDARD);
}
/* Try to load GeoIP.dat from lib/ if FEAT_GEOIP_FILE was not loaded. */
if (gi4 == NULL)
gi4 = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD);
#endif /* USE_GEOIP */
}
/** Handle an update to FEAT_GEOIP_IPV6_FILE. */
void geoip_handle_ipv6_file(void)
{
if (!feature_bool(FEAT_GEOIP_ENABLE))
return;
#ifdef USE_GEOIP
if (gi6 != NULL)
GeoIP_delete(gi6);
gi6 = NULL;
/* Load IPv6 GeoIP database */
if (feature_str(FEAT_GEOIP_IPV6_FILE)) {
gi6 = GeoIP_open(feature_str(FEAT_GEOIP_IPV6_FILE), GEOIP_STANDARD);
}
/* Try to load GeoIPv6.dat from lib/ if FEAT_GEOIP_IPV6_FILE was not loaded. */
if (gi6 == NULL)
gi6 = GeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_STANDARD);
#endif /* USE_GEOIP */
}
const char* geoip_version(void)
{
#ifdef USE_GEOIP
return GeoIP_lib_version();
#else
return NULL;
#endif /* USE_GEOIP */
}
const char* geoip_libmmdb_version(void)
{
#ifdef USE_MMDB
return MMDB_lib_version();
#else
return NULL;
#endif /* USE_MMDB */
}