3 Copyright (C) 2004-2023 Chris Porter.
6 #include "../nick/nick.h"
7 #include "../core/error.h"
8 #include "../core/config.h"
9 #include "../core/hooks.h"
10 #include "../control/control.h"
11 #include "../lib/version.h"
15 #include <maxminddb.h>
20 #define COUNTRY_MAX (26 * 26 + 1)
21 #define UNKNOWN_COUNTRY (COUNTRY_MAX - 1)
22 #define COUNTRY_NAME_LEN 40
27 char name
[COUNTRY_NAME_LEN
+ 1];
30 static struct country countries
[COUNTRY_MAX
] = { [UNKNOWN_COUNTRY
] = { 0, "??", "Unknown" } };
31 static struct country
*unknown_country
= &countries
[UNKNOWN_COUNTRY
];
33 static int nickext
= -1;
36 static struct country
*lookup_country(struct sockaddr
*addr
) {
38 MMDB_lookup_result_s result
= MMDB_lookup_sockaddr(&db
, addr
, &mmdb_error
);
39 if (mmdb_error
!= MMDB_SUCCESS
|| !result
.found_entry
) {
43 MMDB_entry_data_s entry_data
;
44 int status
= MMDB_get_value(&result
.entry
, &entry_data
, "country", "iso_code", NULL
);
45 if (status
!= MMDB_SUCCESS
|| !entry_data
.has_data
|| entry_data
.type
!= MMDB_DATA_TYPE_UTF8_STRING
|| entry_data
.data_size
!= 2) {
49 /* not null terminated, sad */
50 const char *code_u
= entry_data
.utf8_string
;
51 const char code
[3] = { code_u
[0], code_u
[1], '\0' };
53 struct country
*c
= geoip_lookup_code(code
);
58 if (c
->code
[0] == '\0') {
59 status
= MMDB_get_value(&result
.entry
, &entry_data
, "country", "names", "en", NULL
);
60 if (status
!= MMDB_SUCCESS
|| !entry_data
.has_data
|| entry_data
.type
!= MMDB_DATA_TYPE_UTF8_STRING
) {
64 /* not null terminated, sad */
65 const char *name
= entry_data
.utf8_string
;
66 size_t name_len
= entry_data
.data_size
;
67 if (name_len
> COUNTRY_NAME_LEN
) {
68 name_len
= COUNTRY_NAME_LEN
;
71 memcpy(c
->code
, code
, 3);
72 memcpy(c
->name
, name
, name_len
);
73 c
->name
[name_len
] = '\0';
79 static void nick_setup(nick
*np
) {
81 struct sockaddr_in sin
;
82 struct sockaddr_in6 sin6
;
85 struct irc_in_addr
*ip
= &np
->ipaddress
;
86 if (irc_in_addr_is_ipv4(ip
)) {
87 u
.sin
.sin_family
= AF_INET
;
88 u
.sin
.sin_addr
.s_addr
= htonl(irc_in_addr_v4_to_int(ip
));
90 u
.sin6
.sin6_family
= AF_INET6
;
91 memcpy(&u
.sin6
.sin6_addr
.s6_addr
, ip
->in6_16
, sizeof(ip
->in6_16
));
94 struct country
*c
= lookup_country((struct sockaddr
*)&u
);
100 np
->exts
[nickext
] = c
;
103 static void nick_new(int hook
, void *args
) {
107 static void nick_lost(int hook
, void *args
) {
110 struct country
*c
= geoip_lookup_nick(np
);
118 static void whois_handler(int hooknum
, void *arg
) {
124 struct country
*c
= geoip_lookup_nick(np
);
130 snprintf(buf
, sizeof(buf
), "Country : %s (%s)", geoip_code(c
), geoip_name(c
));
131 triggerhook(HOOK_CONTROL_WHOISREPLY
, buf
);
134 struct country
*geoip_lookup_code(const char *code
) {
135 if (code
[0] < 'A' || code
[0] > 'Z' || code
[1] < 'A' || code
[1] > 'Z' || code
[2] != '\0') {
136 if (!strcmp("??", code
)) {
137 return unknown_country
;
143 return &countries
[(code
[0] - 'A') * 26 + (code
[1] - 'A')];
146 const char *geoip_code(struct country
*c
) {
150 const char *geoip_name(struct country
*c
) {
154 int geoip_total(struct country
*c
) {
158 struct country
*geoip_next(struct country
*c
) {
163 pos
= c
- countries
+ 1;
166 for (; pos
< COUNTRY_MAX
; pos
++) {
178 struct country
*geoip_lookup_nick(nick
*np
) {
183 return np
->exts
[nickext
];
187 nickext
= registernickext("geoip");
192 sstring
*filename
= getcopyconfigitem("geoip", "db", "GeoLite2-Country.mmdb", 256);
193 int result
= MMDB_open(filename
->content
, MMDB_MODE_MMAP
, &db
);
194 freesstring(filename
);
195 if (result
!= MMDB_SUCCESS
) {
196 Error("geoip", ERR_WARNING
, "Unable to load geoip database [filename: %s] [code: %d]", filename
->content
, result
);
200 for (int i
= 0; i
< NICKHASHSIZE
; i
++) {
201 for (nick
*np
= nicktable
[i
]; np
; np
=np
->next
) {
206 registerhook(HOOK_NICK_NEWNICK
, nick_new
);
207 registerhook(HOOK_NICK_LOSTNICK
, nick_lost
);
208 registerhook(HOOK_CONTROL_WHOISREQUEST
, whois_handler
);
213 releasenickext(nickext
);
218 deregisterhook(HOOK_NICK_NEWNICK
, nick_new
);
219 deregisterhook(HOOK_NICK_LOSTNICK
, nick_lost
);
220 deregisterhook(HOOK_CONTROL_WHOISREQUEST
, whois_handler
);