3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
13 #include <GeoIPCity.h>
16 #define NGX_GEOIP_COUNTRY_CODE 0
17 #define NGX_GEOIP_COUNTRY_CODE3 1
18 #define NGX_GEOIP_COUNTRY_NAME 2
25 ngx_array_t
*proxies
; /* array of ngx_cidr_t */
26 ngx_flag_t proxy_recursive
;
27 #if (NGX_HAVE_GEOIP_V6)
28 unsigned country_v6
:1;
32 } ngx_http_geoip_conf_t
;
38 } ngx_http_geoip_var_t
;
41 typedef const char *(*ngx_http_geoip_variable_handler_pt
)(GeoIP
*,
45 ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions
[] = {
46 GeoIP_country_code_by_ipnum
,
47 GeoIP_country_code3_by_ipnum
,
48 GeoIP_country_name_by_ipnum
,
52 #if (NGX_HAVE_GEOIP_V6)
54 typedef const char *(*ngx_http_geoip_variable_handler_v6_pt
)(GeoIP
*,
58 ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions
[] = {
59 GeoIP_country_code_by_ipnum_v6
,
60 GeoIP_country_code3_by_ipnum_v6
,
61 GeoIP_country_name_by_ipnum_v6
,
67 static ngx_int_t
ngx_http_geoip_country_variable(ngx_http_request_t
*r
,
68 ngx_http_variable_value_t
*v
, uintptr_t data
);
69 static ngx_int_t
ngx_http_geoip_org_variable(ngx_http_request_t
*r
,
70 ngx_http_variable_value_t
*v
, uintptr_t data
);
71 static ngx_int_t
ngx_http_geoip_city_variable(ngx_http_request_t
*r
,
72 ngx_http_variable_value_t
*v
, uintptr_t data
);
73 static ngx_int_t
ngx_http_geoip_region_name_variable(ngx_http_request_t
*r
,
74 ngx_http_variable_value_t
*v
, uintptr_t data
);
75 static ngx_int_t
ngx_http_geoip_city_float_variable(ngx_http_request_t
*r
,
76 ngx_http_variable_value_t
*v
, uintptr_t data
);
77 static ngx_int_t
ngx_http_geoip_city_int_variable(ngx_http_request_t
*r
,
78 ngx_http_variable_value_t
*v
, uintptr_t data
);
79 static GeoIPRecord
*ngx_http_geoip_get_city_record(ngx_http_request_t
*r
);
81 static ngx_int_t
ngx_http_geoip_add_variables(ngx_conf_t
*cf
);
82 static void *ngx_http_geoip_create_conf(ngx_conf_t
*cf
);
83 static char *ngx_http_geoip_init_conf(ngx_conf_t
*cf
, void *conf
);
84 static char *ngx_http_geoip_country(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
86 static char *ngx_http_geoip_org(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
88 static char *ngx_http_geoip_city(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
90 static char *ngx_http_geoip_proxy(ngx_conf_t
*cf
, ngx_command_t
*cmd
,
92 static ngx_int_t
ngx_http_geoip_cidr_value(ngx_conf_t
*cf
, ngx_str_t
*net
,
94 static void ngx_http_geoip_cleanup(void *data
);
97 static ngx_command_t ngx_http_geoip_commands
[] = {
99 { ngx_string("geoip_country"),
100 NGX_HTTP_MAIN_CONF
|NGX_CONF_TAKE12
,
101 ngx_http_geoip_country
,
102 NGX_HTTP_MAIN_CONF_OFFSET
,
106 { ngx_string("geoip_org"),
107 NGX_HTTP_MAIN_CONF
|NGX_CONF_TAKE12
,
109 NGX_HTTP_MAIN_CONF_OFFSET
,
113 { ngx_string("geoip_city"),
114 NGX_HTTP_MAIN_CONF
|NGX_CONF_TAKE12
,
116 NGX_HTTP_MAIN_CONF_OFFSET
,
120 { ngx_string("geoip_proxy"),
121 NGX_HTTP_MAIN_CONF
|NGX_CONF_TAKE1
,
122 ngx_http_geoip_proxy
,
123 NGX_HTTP_MAIN_CONF_OFFSET
,
127 { ngx_string("geoip_proxy_recursive"),
128 NGX_HTTP_MAIN_CONF
|NGX_CONF_FLAG
,
129 ngx_conf_set_flag_slot
,
130 NGX_HTTP_MAIN_CONF_OFFSET
,
131 offsetof(ngx_http_geoip_conf_t
, proxy_recursive
),
138 static ngx_http_module_t ngx_http_geoip_module_ctx
= {
139 ngx_http_geoip_add_variables
, /* preconfiguration */
140 NULL
, /* postconfiguration */
142 ngx_http_geoip_create_conf
, /* create main configuration */
143 ngx_http_geoip_init_conf
, /* init main configuration */
145 NULL
, /* create server configuration */
146 NULL
, /* merge server configuration */
148 NULL
, /* create location configuration */
149 NULL
/* merge location configuration */
153 ngx_module_t ngx_http_geoip_module
= {
155 &ngx_http_geoip_module_ctx
, /* module context */
156 ngx_http_geoip_commands
, /* module directives */
157 NGX_HTTP_MODULE
, /* module type */
158 NULL
, /* init master */
159 NULL
, /* init module */
160 NULL
, /* init process */
161 NULL
, /* init thread */
162 NULL
, /* exit thread */
163 NULL
, /* exit process */
164 NULL
, /* exit master */
165 NGX_MODULE_V1_PADDING
169 static ngx_http_variable_t ngx_http_geoip_vars
[] = {
171 { ngx_string("geoip_country_code"), NULL
,
172 ngx_http_geoip_country_variable
,
173 NGX_GEOIP_COUNTRY_CODE
, 0, 0 },
175 { ngx_string("geoip_country_code3"), NULL
,
176 ngx_http_geoip_country_variable
,
177 NGX_GEOIP_COUNTRY_CODE3
, 0, 0 },
179 { ngx_string("geoip_country_name"), NULL
,
180 ngx_http_geoip_country_variable
,
181 NGX_GEOIP_COUNTRY_NAME
, 0, 0 },
183 { ngx_string("geoip_org"), NULL
,
184 ngx_http_geoip_org_variable
,
187 { ngx_string("geoip_city_continent_code"), NULL
,
188 ngx_http_geoip_city_variable
,
189 offsetof(GeoIPRecord
, continent_code
), 0, 0 },
191 { ngx_string("geoip_city_country_code"), NULL
,
192 ngx_http_geoip_city_variable
,
193 offsetof(GeoIPRecord
, country_code
), 0, 0 },
195 { ngx_string("geoip_city_country_code3"), NULL
,
196 ngx_http_geoip_city_variable
,
197 offsetof(GeoIPRecord
, country_code3
), 0, 0 },
199 { ngx_string("geoip_city_country_name"), NULL
,
200 ngx_http_geoip_city_variable
,
201 offsetof(GeoIPRecord
, country_name
), 0, 0 },
203 { ngx_string("geoip_region"), NULL
,
204 ngx_http_geoip_city_variable
,
205 offsetof(GeoIPRecord
, region
), 0, 0 },
207 { ngx_string("geoip_region_name"), NULL
,
208 ngx_http_geoip_region_name_variable
,
211 { ngx_string("geoip_city"), NULL
,
212 ngx_http_geoip_city_variable
,
213 offsetof(GeoIPRecord
, city
), 0, 0 },
215 { ngx_string("geoip_postal_code"), NULL
,
216 ngx_http_geoip_city_variable
,
217 offsetof(GeoIPRecord
, postal_code
), 0, 0 },
219 { ngx_string("geoip_latitude"), NULL
,
220 ngx_http_geoip_city_float_variable
,
221 offsetof(GeoIPRecord
, latitude
), 0, 0 },
223 { ngx_string("geoip_longitude"), NULL
,
224 ngx_http_geoip_city_float_variable
,
225 offsetof(GeoIPRecord
, longitude
), 0, 0 },
227 { ngx_string("geoip_dma_code"), NULL
,
228 ngx_http_geoip_city_int_variable
,
229 offsetof(GeoIPRecord
, dma_code
), 0, 0 },
231 { ngx_string("geoip_area_code"), NULL
,
232 ngx_http_geoip_city_int_variable
,
233 offsetof(GeoIPRecord
, area_code
), 0, 0 },
235 { ngx_null_string
, NULL
, NULL
, 0, 0, 0 }
240 ngx_http_geoip_addr(ngx_http_request_t
*r
, ngx_http_geoip_conf_t
*gcf
)
244 struct sockaddr_in
*sin
;
246 addr
.sockaddr
= r
->connection
->sockaddr
;
247 addr
.socklen
= r
->connection
->socklen
;
248 /* addr.name = r->connection->addr_text; */
250 xfwd
= &r
->headers_in
.x_forwarded_for
;
252 if (xfwd
->nelts
> 0 && gcf
->proxies
!= NULL
) {
253 (void) ngx_http_get_forwarded_addr(r
, &addr
, xfwd
, NULL
,
254 gcf
->proxies
, gcf
->proxy_recursive
);
259 if (addr
.sockaddr
->sa_family
== AF_INET6
) {
262 struct in6_addr
*inaddr6
;
264 inaddr6
= &((struct sockaddr_in6
*) addr
.sockaddr
)->sin6_addr
;
266 if (IN6_IS_ADDR_V4MAPPED(inaddr6
)) {
267 p
= inaddr6
->s6_addr
;
269 inaddr
= p
[12] << 24;
270 inaddr
+= p
[13] << 16;
271 inaddr
+= p
[14] << 8;
280 if (addr
.sockaddr
->sa_family
!= AF_INET
) {
284 sin
= (struct sockaddr_in
*) addr
.sockaddr
;
285 return ntohl(sin
->sin_addr
.s_addr
);
289 #if (NGX_HAVE_GEOIP_V6)
292 ngx_http_geoip_addr_v6(ngx_http_request_t
*r
, ngx_http_geoip_conf_t
*gcf
)
297 struct in6_addr addr6
;
298 struct sockaddr_in
*sin
;
299 struct sockaddr_in6
*sin6
;
301 addr
.sockaddr
= r
->connection
->sockaddr
;
302 addr
.socklen
= r
->connection
->socklen
;
303 /* addr.name = r->connection->addr_text; */
305 xfwd
= &r
->headers_in
.x_forwarded_for
;
307 if (xfwd
->nelts
> 0 && gcf
->proxies
!= NULL
) {
308 (void) ngx_http_get_forwarded_addr(r
, &addr
, xfwd
, NULL
,
309 gcf
->proxies
, gcf
->proxy_recursive
);
312 switch (addr
.sockaddr
->sa_family
) {
315 /* Produce IPv4-mapped IPv6 address. */
316 sin
= (struct sockaddr_in
*) addr
.sockaddr
;
317 addr4
= ntohl(sin
->sin_addr
.s_addr
);
319 ngx_memzero(&addr6
, sizeof(struct in6_addr
));
320 addr6
.s6_addr
[10] = 0xff;
321 addr6
.s6_addr
[11] = 0xff;
322 addr6
.s6_addr
[12] = addr4
>> 24;
323 addr6
.s6_addr
[13] = addr4
>> 16;
324 addr6
.s6_addr
[14] = addr4
>> 8;
325 addr6
.s6_addr
[15] = addr4
;
329 sin6
= (struct sockaddr_in6
*) addr
.sockaddr
;
330 return sin6
->sin6_addr
;
341 ngx_http_geoip_country_variable(ngx_http_request_t
*r
,
342 ngx_http_variable_value_t
*v
, uintptr_t data
)
344 ngx_http_geoip_variable_handler_pt handler
=
345 ngx_http_geoip_country_functions
[data
];
346 #if (NGX_HAVE_GEOIP_V6)
347 ngx_http_geoip_variable_handler_v6_pt handler_v6
=
348 ngx_http_geoip_country_v6_functions
[data
];
352 ngx_http_geoip_conf_t
*gcf
;
354 gcf
= ngx_http_get_module_main_conf(r
, ngx_http_geoip_module
);
356 if (gcf
->country
== NULL
) {
360 #if (NGX_HAVE_GEOIP_V6)
361 val
= gcf
->country_v6
362 ? handler_v6(gcf
->country
, ngx_http_geoip_addr_v6(r
, gcf
))
363 : handler(gcf
->country
, ngx_http_geoip_addr(r
, gcf
));
365 val
= handler(gcf
->country
, ngx_http_geoip_addr(r
, gcf
));
372 v
->len
= ngx_strlen(val
);
376 v
->data
= (u_char
*) val
;
389 ngx_http_geoip_org_variable(ngx_http_request_t
*r
,
390 ngx_http_variable_value_t
*v
, uintptr_t data
)
394 ngx_http_geoip_conf_t
*gcf
;
396 gcf
= ngx_http_get_module_main_conf(r
, ngx_http_geoip_module
);
398 if (gcf
->org
== NULL
) {
402 #if (NGX_HAVE_GEOIP_V6)
404 ? GeoIP_name_by_ipnum_v6(gcf
->org
,
405 ngx_http_geoip_addr_v6(r
, gcf
))
406 : GeoIP_name_by_ipnum(gcf
->org
,
407 ngx_http_geoip_addr(r
, gcf
));
409 val
= GeoIP_name_by_ipnum(gcf
->org
, ngx_http_geoip_addr(r
, gcf
));
416 len
= ngx_strlen(val
);
417 v
->data
= ngx_pnalloc(r
->pool
, len
);
418 if (v
->data
== NULL
) {
423 ngx_memcpy(v
->data
, val
, len
);
443 ngx_http_geoip_city_variable(ngx_http_request_t
*r
,
444 ngx_http_variable_value_t
*v
, uintptr_t data
)
450 gr
= ngx_http_geoip_get_city_record(r
);
455 val
= *(char **) ((char *) gr
+ data
);
460 len
= ngx_strlen(val
);
461 v
->data
= ngx_pnalloc(r
->pool
, len
);
462 if (v
->data
== NULL
) {
463 GeoIPRecord_delete(gr
);
467 ngx_memcpy(v
->data
, val
, len
);
474 GeoIPRecord_delete(gr
);
480 GeoIPRecord_delete(gr
);
491 ngx_http_geoip_region_name_variable(ngx_http_request_t
*r
,
492 ngx_http_variable_value_t
*v
, uintptr_t data
)
498 gr
= ngx_http_geoip_get_city_record(r
);
503 val
= GeoIP_region_name_by_code(gr
->country_code
, gr
->region
);
505 GeoIPRecord_delete(gr
);
511 len
= ngx_strlen(val
);
512 v
->data
= ngx_pnalloc(r
->pool
, len
);
513 if (v
->data
== NULL
) {
517 ngx_memcpy(v
->data
, val
, len
);
535 ngx_http_geoip_city_float_variable(ngx_http_request_t
*r
,
536 ngx_http_variable_value_t
*v
, uintptr_t data
)
541 gr
= ngx_http_geoip_get_city_record(r
);
547 v
->data
= ngx_pnalloc(r
->pool
, NGX_INT64_LEN
+ 5);
548 if (v
->data
== NULL
) {
549 GeoIPRecord_delete(gr
);
553 val
= *(float *) ((char *) gr
+ data
);
555 v
->len
= ngx_sprintf(v
->data
, "%.4f", val
) - v
->data
;
557 GeoIPRecord_delete(gr
);
564 ngx_http_geoip_city_int_variable(ngx_http_request_t
*r
,
565 ngx_http_variable_value_t
*v
, uintptr_t data
)
570 gr
= ngx_http_geoip_get_city_record(r
);
576 v
->data
= ngx_pnalloc(r
->pool
, NGX_INT64_LEN
);
577 if (v
->data
== NULL
) {
578 GeoIPRecord_delete(gr
);
582 val
= *(int *) ((char *) gr
+ data
);
584 v
->len
= ngx_sprintf(v
->data
, "%d", val
) - v
->data
;
586 GeoIPRecord_delete(gr
);
593 ngx_http_geoip_get_city_record(ngx_http_request_t
*r
)
595 ngx_http_geoip_conf_t
*gcf
;
597 gcf
= ngx_http_get_module_main_conf(r
, ngx_http_geoip_module
);
600 #if (NGX_HAVE_GEOIP_V6)
602 ? GeoIP_record_by_ipnum_v6(gcf
->city
,
603 ngx_http_geoip_addr_v6(r
, gcf
))
604 : GeoIP_record_by_ipnum(gcf
->city
,
605 ngx_http_geoip_addr(r
, gcf
));
607 return GeoIP_record_by_ipnum(gcf
->city
, ngx_http_geoip_addr(r
, gcf
));
616 ngx_http_geoip_add_variables(ngx_conf_t
*cf
)
618 ngx_http_variable_t
*var
, *v
;
620 for (v
= ngx_http_geoip_vars
; v
->name
.len
; v
++) {
621 var
= ngx_http_add_variable(cf
, &v
->name
, v
->flags
);
626 var
->get_handler
= v
->get_handler
;
635 ngx_http_geoip_create_conf(ngx_conf_t
*cf
)
637 ngx_pool_cleanup_t
*cln
;
638 ngx_http_geoip_conf_t
*conf
;
640 conf
= ngx_pcalloc(cf
->pool
, sizeof(ngx_http_geoip_conf_t
));
645 conf
->proxy_recursive
= NGX_CONF_UNSET
;
647 cln
= ngx_pool_cleanup_add(cf
->pool
, 0);
652 cln
->handler
= ngx_http_geoip_cleanup
;
660 ngx_http_geoip_init_conf(ngx_conf_t
*cf
, void *conf
)
662 ngx_http_geoip_conf_t
*gcf
= conf
;
664 ngx_conf_init_value(gcf
->proxy_recursive
, 0);
671 ngx_http_geoip_country(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
673 ngx_http_geoip_conf_t
*gcf
= conf
;
678 return "is duplicate";
681 value
= cf
->args
->elts
;
683 gcf
->country
= GeoIP_open((char *) value
[1].data
, GEOIP_MEMORY_CACHE
);
685 if (gcf
->country
== NULL
) {
686 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
687 "GeoIP_open(\"%V\") failed", &value
[1]);
689 return NGX_CONF_ERROR
;
692 if (cf
->args
->nelts
== 3) {
693 if (ngx_strcmp(value
[2].data
, "utf8") == 0) {
694 GeoIP_set_charset (gcf
->country
, GEOIP_CHARSET_UTF8
);
697 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
698 "invalid parameter \"%V\"", &value
[2]);
699 return NGX_CONF_ERROR
;
703 switch (gcf
->country
->databaseType
) {
705 case GEOIP_COUNTRY_EDITION
:
709 #if (NGX_HAVE_GEOIP_V6)
710 case GEOIP_COUNTRY_EDITION_V6
:
717 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
718 "invalid GeoIP database \"%V\" type:%d",
719 &value
[1], gcf
->country
->databaseType
);
720 return NGX_CONF_ERROR
;
726 ngx_http_geoip_org(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
728 ngx_http_geoip_conf_t
*gcf
= conf
;
733 return "is duplicate";
736 value
= cf
->args
->elts
;
738 gcf
->org
= GeoIP_open((char *) value
[1].data
, GEOIP_MEMORY_CACHE
);
740 if (gcf
->org
== NULL
) {
741 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
742 "GeoIP_open(\"%V\") failed", &value
[1]);
744 return NGX_CONF_ERROR
;
747 if (cf
->args
->nelts
== 3) {
748 if (ngx_strcmp(value
[2].data
, "utf8") == 0) {
749 GeoIP_set_charset (gcf
->org
, GEOIP_CHARSET_UTF8
);
752 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
753 "invalid parameter \"%V\"", &value
[2]);
754 return NGX_CONF_ERROR
;
758 switch (gcf
->org
->databaseType
) {
760 case GEOIP_ISP_EDITION
:
761 case GEOIP_ORG_EDITION
:
762 case GEOIP_DOMAIN_EDITION
:
763 case GEOIP_ASNUM_EDITION
:
767 #if (NGX_HAVE_GEOIP_V6)
768 case GEOIP_ISP_EDITION_V6
:
769 case GEOIP_ORG_EDITION_V6
:
770 case GEOIP_DOMAIN_EDITION_V6
:
771 case GEOIP_ASNUM_EDITION_V6
:
778 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
779 "invalid GeoIP database \"%V\" type:%d",
780 &value
[1], gcf
->org
->databaseType
);
781 return NGX_CONF_ERROR
;
787 ngx_http_geoip_city(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
789 ngx_http_geoip_conf_t
*gcf
= conf
;
794 return "is duplicate";
797 value
= cf
->args
->elts
;
799 gcf
->city
= GeoIP_open((char *) value
[1].data
, GEOIP_MEMORY_CACHE
);
801 if (gcf
->city
== NULL
) {
802 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
803 "GeoIP_open(\"%V\") failed", &value
[1]);
805 return NGX_CONF_ERROR
;
808 if (cf
->args
->nelts
== 3) {
809 if (ngx_strcmp(value
[2].data
, "utf8") == 0) {
810 GeoIP_set_charset (gcf
->city
, GEOIP_CHARSET_UTF8
);
813 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
814 "invalid parameter \"%V\"", &value
[2]);
815 return NGX_CONF_ERROR
;
819 switch (gcf
->city
->databaseType
) {
821 case GEOIP_CITY_EDITION_REV0
:
822 case GEOIP_CITY_EDITION_REV1
:
826 #if (NGX_HAVE_GEOIP_V6)
827 case GEOIP_CITY_EDITION_REV0_V6
:
828 case GEOIP_CITY_EDITION_REV1_V6
:
835 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0,
836 "invalid GeoIP City database \"%V\" type:%d",
837 &value
[1], gcf
->city
->databaseType
);
838 return NGX_CONF_ERROR
;
844 ngx_http_geoip_proxy(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
846 ngx_http_geoip_conf_t
*gcf
= conf
;
851 value
= cf
->args
->elts
;
853 if (ngx_http_geoip_cidr_value(cf
, &value
[1], &cidr
) != NGX_OK
) {
854 return NGX_CONF_ERROR
;
857 if (gcf
->proxies
== NULL
) {
858 gcf
->proxies
= ngx_array_create(cf
->pool
, 4, sizeof(ngx_cidr_t
));
859 if (gcf
->proxies
== NULL
) {
860 return NGX_CONF_ERROR
;
864 c
= ngx_array_push(gcf
->proxies
);
866 return NGX_CONF_ERROR
;
875 ngx_http_geoip_cidr_value(ngx_conf_t
*cf
, ngx_str_t
*net
, ngx_cidr_t
*cidr
)
879 if (ngx_strcmp(net
->data
, "255.255.255.255") == 0) {
880 cidr
->family
= AF_INET
;
881 cidr
->u
.in
.addr
= 0xffffffff;
882 cidr
->u
.in
.mask
= 0xffffffff;
887 rc
= ngx_ptocidr(net
, cidr
);
889 if (rc
== NGX_ERROR
) {
890 ngx_conf_log_error(NGX_LOG_EMERG
, cf
, 0, "invalid network \"%V\"", net
);
894 if (rc
== NGX_DONE
) {
895 ngx_conf_log_error(NGX_LOG_WARN
, cf
, 0,
896 "low address bits of %V are meaningless", net
);
904 ngx_http_geoip_cleanup(void *data
)
906 ngx_http_geoip_conf_t
*gcf
= data
;
909 GeoIP_delete(gcf
->country
);
913 GeoIP_delete(gcf
->org
);
917 GeoIP_delete(gcf
->city
);