iptogeo/src/ip_to_geo.c

160 lines
4.3 KiB
C

/*
Copyright 2016 Grégory Soutadé
This file is part of iptogeo.
iptogeo 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 3 of the License, or
(at your option) any later version.
iptogeo 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 iptogeo. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ip_to_geo.h"
#include "ip_data.c"
static const uint8_t* ip_to_geo_rec(uint8_t* ip, unsigned level, const ip_level* root)
{
uint8_t cur_average;
const ip_level* cur_ip;
uint8_t cur_addr;
while (1)
{
start_loop:
cur_ip = root;
cur_addr = ip[level];
// Optimistic search
if (cur_addr && level != 1)
{
cur_average = cur_addr >> root->average;
while (cur_average-- && cur_ip->next)
cur_ip = cur_ip->next;
}
#define IP_TEST \
{ \
if (cur_addr >= cur_ip->start && cur_addr <= cur_ip->end) \
{ \
if (cur_ip->childs) \
{ \
level++; \
root = cur_ip->childs; \
goto start_loop; \
} \
else \
return &cur_ip->code; \
} \
}
if (cur_addr < cur_ip->start)
{
for (cur_ip = cur_ip->prev; cur_ip; cur_ip = cur_ip->prev)
IP_TEST;
}
else if (cur_addr > cur_ip->end)
{
for (cur_ip = cur_ip->next; cur_ip; cur_ip = cur_ip->next)
IP_TEST;
}
else
IP_TEST;
break;
}
return NULL;
}
const uint8_t* ip_to_geo(uint8_t* ip, unsigned ip_size)
{
const ip_level* first_level;
if (ip_size == 4)
first_level = s_root_ipv4[ip[0]];
else if (ip_size == 16)
first_level = s_root_ipv6[ip[0]];
else
return NULL;
if (!first_level) return NULL;
return ip_to_geo_rec(ip, 1, first_level);
}
const uint8_t* get_country_code(const uint8_t* idx)
{
if (!idx || *idx >= sizeof(country_codes)/sizeof(country_codes[0]))
return NULL;
return country_codes[*idx];
}
int interactive(struct gengetopt_args_info* params)
{
uint8_t ip[16];
const uint8_t* cc;
int ret, ip_size=4;
ret = inet_pton(AF_INET, params->ip_arg, ip);
if (ret != 1)
{
ip_size = 16;
ret = inet_pton(AF_INET6, params->ip_arg, ip);
if (ret != 1)
{
if (!params->quiet_flag)
fprintf(stderr, "Invalid IP %s\n", params->ip_arg);
return -1;
}
}
cc = ip_to_geo(ip, ip_size);
if (params->quiet_flag)
printf("%s\n", (cc)?(char*)get_country_code(cc):"<none>");
else
printf("IP %s : %s\n", params->ip_arg, (cc)?(char*)get_country_code(cc):"<none>");
return 0;
}
int main(int argc, char** argv)
{
int ret;
struct gengetopt_args_info params;
ret = cmdline_parser (argc, argv, &params);
if (ret) return ret;
/* self_test(); */
if (params.ip_given)
return interactive(&params);
else if (params.daemon_flag)
return daemonize(&params);
else
cmdline_parser_print_help();
return 0;
}