ip2region/binding/nginx/ngx_ip2region_module.c
2019-08-22 10:35:26 +08:00

261 lines
7.0 KiB
C

#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_ip2region.h"
static void *ngx_ip2region_create_conf(ngx_cycle_t *cycle);
static char *ngx_ip2region_init_conf(ngx_cycle_t *cycle, void *conf);
static ngx_int_t ngx_ip2region_init_process(ngx_cycle_t *cycle);
static void ngx_ip2region_exit_process(ngx_cycle_t *cycle);
static ip2region_entry g_ip2region_entry;
search_func_ptr ip2region_search_func;
#define NGX_IP2REGION_MEMORY 0
#define NGX_IP2REGION_BINARY 1
#define NGX_IP2REGION_BTREE 2
static ngx_conf_enum_t ngx_ip2region_search_algo[] = {
{ ngx_string("memory"), NGX_IP2REGION_MEMORY },
{ ngx_string("binary"), NGX_IP2REGION_BINARY },
{ ngx_string("btree"), NGX_IP2REGION_BTREE},
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_ip2region_commands[] = {
{
ngx_string("ip2region_db_file"),
NGX_MAIN_CONF | NGX_DIRECT_CONF | NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
0,
offsetof(ngx_ip2region_conf_t, db_file),
NULL
},
{
ngx_string("ip2region_algo"),
NGX_MAIN_CONF | NGX_DIRECT_CONF | NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
0,
offsetof(ngx_ip2region_conf_t, algo),
&ngx_ip2region_search_algo
},
ngx_null_command
};
static ngx_core_module_t ngx_ip2region_module_ctx = {
ngx_string("ip2region"),
ngx_ip2region_create_conf,
ngx_ip2region_init_conf,
};
ngx_module_t ngx_ip2region_module = {
NGX_MODULE_V1,
&ngx_ip2region_module_ctx, /* module context */
ngx_ip2region_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_ip2region_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
ngx_ip2region_exit_process, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_ip2region_create_conf(ngx_cycle_t *cycle)
{
ngx_ip2region_conf_t *ip2region_conf;
ip2region_conf = ngx_pcalloc(cycle->pool, sizeof(ngx_ip2region_conf_t));
if(ip2region_conf == NULL) {
return NULL;
}
ip2region_conf->algo = NGX_CONF_UNSET_UINT;
return ip2region_conf;
}
static char *
ngx_ip2region_init_conf(ngx_cycle_t *cycle, void *conf)
{
ngx_ip2region_conf_t *ip2region_conf = (ngx_ip2region_conf_t *)conf;
if(ip2region_conf->db_file.len == 0) {
//ngx_str_set(&ip2region_conf->db_file, "conf/ip2region.db");
return NGX_CONF_OK;
}
if(ngx_conf_full_name(cycle, &ip2region_conf->db_file, 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
ngx_log_debug1(NGX_LOG_INFO, ngx_cycle->log, 0, "%V", &ip2region_conf->db_file);
if(ip2region_conf->algo == NGX_CONF_UNSET_UINT) {
ip2region_conf->algo = NGX_IP2REGION_MEMORY;
}
return NGX_CONF_OK;
}
static ngx_int_t
ngx_ip2region_init_process(ngx_cycle_t *cycle)
{
ngx_ip2region_conf_t *conf = (ngx_ip2region_conf_t *)ngx_get_conf(ngx_cycle->conf_ctx, ngx_ip2region_module);
if(conf->db_file.len == 0) {
ngx_log_debug0(NGX_LOG_INFO, ngx_cycle->log, 0, "ip2region not init");
return NGX_OK;
}
if(!ip2region_create(&g_ip2region_entry, (char *)conf->db_file.data)) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "ip2region create failed: %V", &conf->db_file);
return NGX_ERROR;
}
switch(conf->algo) {
default:
case NGX_IP2REGION_MEMORY:
ip2region_search_func = ip2region_memory_search;
break;
case NGX_IP2REGION_BINARY:
ip2region_search_func = ip2region_binary_search;
break;
case NGX_IP2REGION_BTREE:
ip2region_search_func = ip2region_btree_search;
break;
}
ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, "ip2region create succcess: algo=%ui", conf->algo);
return NGX_OK;
}
static void
ngx_ip2region_exit_process(ngx_cycle_t *cycle)
{
ip2region_destroy(&g_ip2region_entry);
ngx_log_debug0(NGX_LOG_INFO, ngx_cycle->log, 0, "ip2region destroy");
}
static ngx_inline u_char *
ngx_strrchr(u_char *first, u_char *p, u_char c)
{
while(p >= first) {
if(*p == c) {
return p;
}
p--;
}
return NULL;
}
static ngx_inline uint32_t
ngx_inet_aton(u_char *data, ngx_uint_t len)
{
long long result = 0;
long long temp = 0;
u_char *last = data + len;
int point = 0;
while(data <= last) {
if(*data == '.' || data == last) {
if(temp > 255) {
return NGX_ERROR;
}
result = (result << 8) + temp;
temp = 0;
++point;
if(point == 4) {
break;
}
} else if(*data <= '9' && *data >= '0') {
temp = temp * 10 + (*data - '0');
} else {
return NGX_ERROR;
}
++data;
}
if(point != 4) {
return NGX_ERROR;
}
return result;
}
ngx_int_t
ngx_ip2region_search(ngx_str_t *addr_text, ngx_str_t *isp, ngx_str_t *city)
{
if(g_ip2region_entry.dbHandler == NULL) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "search error ip %V, ip2region doesn't create", addr_text);
return NGX_ERROR;
}
uint32_t addr_binary = ngx_inet_aton(addr_text->data, addr_text->len);
if(addr_binary == (uint32_t)NGX_ERROR) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "search error ip %V: bad ip address", addr_text);
return NGX_ERROR;
}
datablock_entry datablock;
ngx_memzero(&datablock, sizeof(datablock));
uint_t rc = ip2region_search_func(&g_ip2region_entry, (uint_t)addr_binary, &datablock);
if(!rc) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "%V search failed", addr_text);
return NGX_ERROR;
}
ngx_log_debug2(NGX_LOG_INFO, ngx_cycle->log, 0, "%V: %s", addr_text, datablock.region);
//城市Id 国家|区域|省份|城市|ISP
u_char *end = (u_char *)(datablock.region + ngx_strlen(datablock.region));
u_char *pos = ngx_strrchr((u_char *)datablock.region, end, '|');
if(!pos) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "search isp failed");
return NGX_ERROR;
}
u_char isp_buffer[64] = {0};
ngx_snprintf(isp_buffer, sizeof(isp_buffer), "%*s%Z", end - pos, pos + 1);
ngx_log_debug3(NGX_LOG_INFO, ngx_cycle->log, 0, "%V: %s [%s]", addr_text, datablock.region, (char *)isp_buffer);
if(city) {
end = pos - 1;
u_char *pos_city = ngx_strrchr((u_char *)datablock.region, end, '|');
if(!pos_city) {
ngx_log_debug0(NGX_LOG_ERR, ngx_cycle->log, 0, "search city failed");
return NGX_ERROR;
}
u_char city_buffer[64] = {0};
ngx_snprintf(city_buffer, sizeof(city_buffer), "%*s%Z", end - pos_city, pos_city + 1);
}
return NGX_OK;
}