Nginx的geoip模块详解

1.相关指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Syntax:	geoip_country file;
Default: —
Context: http

Syntax: geoip_city file;
Default: —
Context: http

Syntax: geoip_org file;
Default: —
Context: http
This directive appeared in version 1.0.3.


Syntax: geoip_proxy address | CIDR;
Default: —
Context: http
This directive appeared in versions 1.3.0 and 1.2.1.

Syntax: geoip_proxy_recursive on | off;
Default:
geoip_proxy_recursive off;
Context: http
This directive appeared in versions 1.3.0 and 1.2.1.

2.配置解析函数

首先看下配置解析函数ngx_http_geoip_country

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
static char *
ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;

ngx_str_t *value;
//重复配置校验
if (gcf->country) {
return "is duplicate";
}
// 获取指令后面的参数
value = cf->args->elts;
// 打开数据文件
gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

if (gcf->country == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"GeoIP_open(\"%V\") failed", &value[1]);

return NGX_CONF_ERROR;
}
// 判断参数个数,设置编码格式
if (cf->args->nelts == 3) {
if (ngx_strcmp(value[2].data, "utf8") == 0) {
GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);

} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
}

switch (gcf->country->databaseType) {

case GEOIP_COUNTRY_EDITION:

return NGX_CONF_OK;

#if (NGX_HAVE_GEOIP_V6)
case GEOIP_COUNTRY_EDITION_V6:

gcf->country_v6 = 1;
return NGX_CONF_OK;
#endif

default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
&value[1], gcf->country->databaseType);
return NGX_CONF_ERROR;
}
}


接下来看下配置解析函数ngx_http_geoip_org
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
static char *
ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;

ngx_str_t *value;
// 重复配置检查
if (gcf->org) {
return "is duplicate";
}

value = cf->args->elts;
// 打开数据文件
gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

if (gcf->org == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"GeoIP_open(\"%V\") failed", &value[1]);

return NGX_CONF_ERROR;
}

// 设置文件编码格式
if (cf->args->nelts == 3) {
if (ngx_strcmp(value[2].data, "utf8") == 0) {
GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);

} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
}
//选择数据类型
switch (gcf->org->databaseType) {

case GEOIP_ISP_EDITION:
case GEOIP_ORG_EDITION:
case GEOIP_DOMAIN_EDITION:
case GEOIP_ASNUM_EDITION:

return NGX_CONF_OK;

#if (NGX_HAVE_GEOIP_V6)
case GEOIP_ISP_EDITION_V6:
case GEOIP_ORG_EDITION_V6:
case GEOIP_DOMAIN_EDITION_V6:
case GEOIP_ASNUM_EDITION_V6:

gcf->org_v6 = 1;
return NGX_CONF_OK;
#endif

default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP database \"%V\" type:%d",
&value[1], gcf->org->databaseType);
return NGX_CONF_ERROR;
}
}

接下来看下配置解析函数ngx_http_geoip_city
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
static char *
ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;

ngx_str_t *value;
// 重复设置判断
if (gcf->city) {
return "is duplicate";
}

value = cf->args->elts;
// 打开数据文件
gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

if (gcf->city == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"GeoIP_open(\"%V\") failed", &value[1]);

return NGX_CONF_ERROR;
}
//处理编码格式
if (cf->args->nelts == 3) {
if (ngx_strcmp(value[2].data, "utf8") == 0) {
GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);

} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid parameter \"%V\"", &value[2]);
return NGX_CONF_ERROR;
}
}
// 针对数据类型做处理
switch (gcf->city->databaseType) {

case GEOIP_CITY_EDITION_REV0:
case GEOIP_CITY_EDITION_REV1:

return NGX_CONF_OK;

#if (NGX_HAVE_GEOIP_V6)
case GEOIP_CITY_EDITION_REV0_V6:
case GEOIP_CITY_EDITION_REV1_V6:

gcf->city_v6 = 1;
return NGX_CONF_OK;
#endif

default:
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid GeoIP City database \"%V\" type:%d",
&value[1], gcf->city->databaseType);
return NGX_CONF_ERROR;
}
}

接下来看下配置解析函数ngx_http_geoip_proxy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static char *
ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_srv_conf_t *gscf = conf;

ngx_str_t *value;
ngx_cidr_t cidr, *c;

value = cf->args->elts;
// 解析cidr地址
if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
return NGX_CONF_ERROR;
}
// 初始化proxy数组
if (gscf->proxies == NGX_CONF_UNSET_PTR) {
gscf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));
if (gscf->proxies == NULL) {
return NGX_CONF_ERROR;
}
}
// push 一个元素用于保存cidr
c = ngx_array_push(gscf->proxies);
if (c == NULL) {
return NGX_CONF_ERROR;
}
// 保存cidr地址
*c = cidr;

return NGX_CONF_OK;
}

指令geoip_proxy_recursive使用原生函数ngx_conf_set_flag_slot,这里就不再赘述

3.hander函数

此模块没有handler函数

4.变量处理函数

此模块功能都是为变量实现,所以主要函数都是为变量实现
首先来看下变量geoip_country_code的处理函数ngx_http_geoip_country_variable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
static ngx_int_t
ngx_http_geoip_country_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
// 根据data类型获取对应函数指针,ngx_http_geoip_country_functions在代码有定义
ngx_http_geoip_variable_handler_pt handler =
ngx_http_geoip_country_functions[data];
#if (NGX_HAVE_GEOIP_V6)
ngx_http_geoip_variable_handler_v6_pt handler_v6 =
ngx_http_geoip_country_v6_functions[data];
#endif

const char *val;
ngx_http_geoip_conf_t *gcf;
// 获取main级别conf
gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
// 没有配置country数据
if (gcf->country == NULL) {
goto not_found;
}
//在handler内部调用ngx_http_geoip_addr
#if (NGX_HAVE_GEOIP_V6)
val = gcf->country_v6
? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf))
: handler(gcf->country, ngx_http_geoip_addr(r, gcf));
#else
val = handler(gcf->country, ngx_http_geoip_addr(r, gcf));
#endif

if (val == NULL) {
goto not_found;
}
//为变量赋值
v->len = ngx_strlen(val);
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = (u_char *) val;

return NGX_OK;

not_found:

v->not_found = 1;

return NGX_OK;
}

接下来看看ngx_http_geoip_addr函数实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
static u_long
ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
{
ngx_addr_t addr;
ngx_array_t *xfwd;
struct sockaddr_in *sin;

addr.sockaddr = r->connection->sockaddr;
addr.socklen = r->connection->socklen;
/* addr.name = r->connection->addr_text; */

xfwd = &r->headers_in.x_forwarded_for;
//获取server级别配置
ngx_http_geoip_srv_conf_t *gscf = ngx_http_get_module_srv_conf(r, ngx_http_geoip_module);
//从xff头中获取ip
if (xfwd->nelts > 0 && gscf->proxies != NULL) {
(void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
gscf->proxies, gscf->proxy_recursive);
}

#if (NGX_HAVE_INET6)
//判断是否为IPV6
if (addr.sockaddr->sa_family == AF_INET6) {
u_char *p;
in_addr_t inaddr;
struct in6_addr *inaddr6;

inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;

if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
p = inaddr6->s6_addr;

inaddr = p[12] << 24;
inaddr += p[13] << 16;
inaddr += p[14] << 8;
inaddr += p[15];

return inaddr;
}
}

#endif
//非ipv4协议
if (addr.sockaddr->sa_family != AF_INET) {
return INADDR_NONE;
}
//返回主机字节序
sin = (struct sockaddr_in *) addr.sockaddr;
return ntohl(sin->sin_addr.s_addr);
}

接下来看下ngx_http_geoip_city_variable函数实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
static ngx_int_t
ngx_http_geoip_city_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
char *val;
size_t len;
GeoIPRecord *gr;
//获取city record
gr = ngx_http_geoip_get_city_record(r);
if (gr == NULL) {
goto not_found;
}

//获取val的值
val = *(char **) ((char *) gr + data);
if (val == NULL) {
goto no_value;
}
//获取变量长度
len = ngx_strlen(val);
v->data = ngx_pnalloc(r->pool, len);
if (v->data == NULL) {
GeoIPRecord_delete(gr);
return NGX_ERROR;
}
//复制变量值
ngx_memcpy(v->data, val, len);

v->len = len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
//回收geoip资源
GeoIPRecord_delete(gr);

return NGX_OK;

no_value:

GeoIPRecord_delete(gr);

not_found:

v->not_found = 1;

return NGX_OK;
}

以上就是geoip模块的所有内容