src/http/modules/ngx_http_geoip_module.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>

  8. #if (NGX_HAVE_GEOIP)
  9. #include <GeoIP.h>
  10. #include <GeoIPCity.h>
  11. #endif
  12. #if (NGX_HAVE_MAXMINDDB)
  13. #include <maxminddb.h>
  14. #endif


  15. ‌#define NGX_GEOIP_COUNTRY_CODE   0
  16. ‌#define NGX_GEOIP_COUNTRY_CODE3  1
  17. ‌#define NGX_GEOIP_COUNTRY_NAME   2

  18. ‌#define NGX_GEOIP_CITY_CONTINENT_CODE  0
  19. ‌#define NGX_GEOIP_CITY_COUNTRY_CODE    1
  20. ‌#define NGX_GEOIP_CITY_COUNTRY_CODE3   2
  21. ‌#define NGX_GEOIP_CITY_COUNTRY_NAME    3
  22. ‌#define NGX_GEOIP_CITY_REGION          4
  23. ‌#define NGX_GEOIP_CITY_REGION_NAME     5
  24. ‌#define NGX_GEOIP_CITY_NAME            6
  25. ‌#define NGX_GEOIP_CITY_POSTAL_CODE     7
  26. ‌#define NGX_GEOIP_CITY_LATITUDE        8
  27. ‌#define NGX_GEOIP_CITY_LONGITUDE       9
  28. ‌#define NGX_GEOIP_CITY_DMA_CODE        10
  29. ‌#define NGX_GEOIP_CITY_AREA_CODE       11

  30. ‌#define NGX_GEOIP_MAX_COORD            180


  31. ‌typedef struct {
  32. #if (NGX_HAVE_GEOIP)
  33.     GeoIP        *country;
  34.     GeoIP        *org;
  35.     GeoIP        *city;
  36. #endif
  37. #if (NGX_HAVE_MAXMINDDB)
  38.     MMDB_s       *country_mmdb;
  39.     MMDB_s       *org_mmdb;
  40.     MMDB_s       *city_mmdb;
  41. #endif
  42.     ngx_array_t  *proxies;    /* array of ngx_cidr_t */
  43.     ngx_flag_t    proxy_recursive;
  44. #if (NGX_HAVE_GEOIP_V6)
  45.     unsigned      country_v6:1;
  46.     unsigned      org_v6:1;
  47.     unsigned      city_v6:1;
  48. #endif
  49. ‌} ngx_http_geoip_conf_t;


  50. ‌typedef struct {
  51.     ngx_str_t    *name;
  52.     uintptr_t     data;
  53. ‌} ngx_http_geoip_var_t;


  54. #if (NGX_HAVE_MAXMINDDB)

  55. ‌typedef struct {
  56.     const char   *alpha2;
  57.     const char   *alpha3;
  58. ‌} ngx_http_geoip_ccode_t;

  59. #endif


  60. #if (NGX_HAVE_GEOIP)

  61. ‌typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *,
  62.     u_long addr);


  63. ‌ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = {
  64.     GeoIP_country_code_by_ipnum,
  65.     GeoIP_country_code3_by_ipnum,
  66.     GeoIP_country_name_by_ipnum,
  67. };


  68. #if (NGX_HAVE_GEOIP_V6)

  69. ‌typedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *,
  70.     geoipv6_t addr);


  71. ‌ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = {
  72.     GeoIP_country_code_by_ipnum_v6,
  73.     GeoIP_country_code3_by_ipnum_v6,
  74.     GeoIP_country_name_by_ipnum_v6,
  75. };

  76. #endif

  77. #endif


  78. static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
  79.     ngx_http_variable_value_t *v, uintptr_t data);
  80. static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,
  81.     ngx_http_variable_value_t *v, uintptr_t data);
  82. static ngx_int_t ngx_http_geoip_city_variable(ngx_http_request_t *r,
  83.     ngx_http_variable_value_t *v, uintptr_t data);
  84. static ngx_int_t ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
  85.     ngx_http_variable_value_t *v, uintptr_t data);
  86. static ngx_int_t ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
  87.     ngx_http_variable_value_t *v, uintptr_t data);
  88. static ngx_int_t ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
  89.     ngx_http_variable_value_t *v, uintptr_t data);
  90. #if (NGX_HAVE_GEOIP)
  91. static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r);
  92. #endif
  93. #if (NGX_HAVE_MAXMINDDB)
  94. static ngx_int_t ngx_http_geoip_str(ngx_http_request_t *r, MMDB_entry_s *entry,
  95.     const char **path, ngx_str_t *value);
  96. static ngx_int_t ngx_http_geoip_code3(ngx_str_t *value);
  97. static ngx_int_t ngx_http_geoip_float(MMDB_entry_s *entry, const char **path,
  98.     double *value);
  99. static ngx_int_t ngx_http_geoip_int(MMDB_entry_s *entry, const char **path,
  100.     int64_t *value);
  101. static ngx_int_t ngx_http_geoip_lookup(ngx_http_request_t *r, MMDB_s *mmdb,
  102.     MMDB_entry_s *entry);
  103. #endif

  104. static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf);
  105. static void *ngx_http_geoip_create_conf(ngx_conf_t *cf);
  106. static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf);
  107. static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
  108.     void *conf);
  109. static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
  110.     void *conf);
  111. static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
  112.     void *conf);
  113. #if (NGX_HAVE_MAXMINDDB)
  114. static ngx_int_t ngx_http_geoip_open(ngx_conf_t *cf, ngx_str_t *name,
  115.     MMDB_s **db);
  116. static char *ngx_http_geoip_charset(ngx_conf_t *cf);
  117. #endif
  118. static char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
  119.     void *conf);
  120. static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
  121.     ngx_cidr_t *cidr);
  122. static void ngx_http_geoip_cleanup(void *data);


  123. ‌static ngx_command_t  ngx_http_geoip_commands[] = {

  124.     { ngx_string("geoip_country"),
  125.       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
  126.       ngx_http_geoip_country,
  127.       NGX_HTTP_MAIN_CONF_OFFSET,
  128.       0,
  129.       NULL },

  130.     { ngx_string("geoip_org"),
  131.       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
  132.       ngx_http_geoip_org,
  133.       NGX_HTTP_MAIN_CONF_OFFSET,
  134.       0,
  135.       NULL },

  136.     { ngx_string("geoip_city"),
  137.       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
  138.       ngx_http_geoip_city,
  139.       NGX_HTTP_MAIN_CONF_OFFSET,
  140.       0,
  141.       NULL },

  142.     { ngx_string("geoip_proxy"),
  143.       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
  144.       ngx_http_geoip_proxy,
  145.       NGX_HTTP_MAIN_CONF_OFFSET,
  146.       0,
  147.       NULL },

  148.     { ngx_string("geoip_proxy_recursive"),
  149.       NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,
  150.       ngx_conf_set_flag_slot,
  151.       NGX_HTTP_MAIN_CONF_OFFSET,
  152.       offsetof(ngx_http_geoip_conf_t, proxy_recursive),
  153.       NULL },

  154.       ngx_null_command
  155. };


  156. ‌static ngx_http_module_t  ngx_http_geoip_module_ctx = {
  157.     ngx_http_geoip_add_variables,          /* preconfiguration */
  158.     NULL,                                  /* postconfiguration */

  159.     ngx_http_geoip_create_conf,            /* create main configuration */
  160.     ngx_http_geoip_init_conf,              /* init main configuration */

  161.     NULL,                                  /* create server configuration */
  162.     NULL,                                  /* merge server configuration */

  163.     NULL,                                  /* create location configuration */
  164.     NULL                                   /* merge location configuration */
  165. };


  166. ‌ngx_module_t  ngx_http_geoip_module = {
  167.     NGX_MODULE_V1,
  168.     &ngx_http_geoip_module_ctx,            /* module context */
  169.     ngx_http_geoip_commands,               /* module directives */
  170.     NGX_HTTP_MODULE,                       /* module type */
  171.     NULL,                                  /* init master */
  172.     NULL,                                  /* init module */
  173.     NULL,                                  /* init process */
  174.     NULL,                                  /* init thread */
  175.     NULL,                                  /* exit thread */
  176.     NULL,                                  /* exit process */
  177.     NULL,                                  /* exit master */
  178.     NGX_MODULE_V1_PADDING
  179. };


  180. ‌static ngx_http_variable_t  ngx_http_geoip_vars[] = {

  181.     { ngx_string("geoip_country_code"), NULL,
  182.       ngx_http_geoip_country_variable,
  183.       NGX_GEOIP_COUNTRY_CODE, 0, 0 },

  184.     { ngx_string("geoip_country_code3"), NULL,
  185.       ngx_http_geoip_country_variable,
  186.       NGX_GEOIP_COUNTRY_CODE3, 0, 0 },

  187.     { ngx_string("geoip_country_name"), NULL,
  188.       ngx_http_geoip_country_variable,
  189.       NGX_GEOIP_COUNTRY_NAME, 0, 0 },

  190.     { ngx_string("geoip_org"), NULL,
  191.       ngx_http_geoip_org_variable,
  192.       0, 0, 0 },

  193.     { ngx_string("geoip_city_continent_code"), NULL,
  194.       ngx_http_geoip_city_variable,
  195.       NGX_GEOIP_CITY_CONTINENT_CODE, 0, 0 },

  196.     { ngx_string("geoip_city_country_code"), NULL,
  197.       ngx_http_geoip_city_variable,
  198.       NGX_GEOIP_CITY_COUNTRY_CODE, 0, 0 },

  199.     { ngx_string("geoip_city_country_code3"), NULL,
  200.       ngx_http_geoip_city_variable,
  201.       NGX_GEOIP_CITY_COUNTRY_CODE3, 0, 0 },

  202.     { ngx_string("geoip_city_country_name"), NULL,
  203.       ngx_http_geoip_city_variable,
  204.       NGX_GEOIP_CITY_COUNTRY_NAME, 0, 0 },

  205.     { ngx_string("geoip_region"), NULL,
  206.       ngx_http_geoip_city_variable,
  207.       NGX_GEOIP_CITY_REGION, 0, 0 },

  208.     { ngx_string("geoip_region_name"), NULL,
  209.       ngx_http_geoip_region_name_variable,
  210.       NGX_GEOIP_CITY_REGION_NAME, 0, 0 },

  211.     { ngx_string("geoip_city"), NULL,
  212.       ngx_http_geoip_city_variable,
  213.       NGX_GEOIP_CITY_NAME, 0, 0 },

  214.     { ngx_string("geoip_postal_code"), NULL,
  215.       ngx_http_geoip_city_variable,
  216.       NGX_GEOIP_CITY_POSTAL_CODE, 0, 0 },

  217.     { ngx_string("geoip_latitude"), NULL,
  218.       ngx_http_geoip_city_float_variable,
  219.       NGX_GEOIP_CITY_LATITUDE, 0, 0 },

  220.     { ngx_string("geoip_longitude"), NULL,
  221.       ngx_http_geoip_city_float_variable,
  222.       NGX_GEOIP_CITY_LONGITUDE, 0, 0 },

  223.     { ngx_string("geoip_dma_code"), NULL,
  224.       ngx_http_geoip_city_int_variable,
  225.       NGX_GEOIP_CITY_DMA_CODE, 0, 0 },

  226.     { ngx_string("geoip_area_code"), NULL,
  227.       ngx_http_geoip_city_int_variable,
  228.       NGX_GEOIP_CITY_AREA_CODE, 0, 0 },

  229.       ngx_http_null_variable
  230. };


  231. #if (NGX_HAVE_GEOIP)

  232. /* indexed by NGX_GEOIP_CITY_* */

  233. ‌static size_t  ngx_http_geoip_city_offsets[] = {
  234.     offsetof(GeoIPRecord, continent_code),
  235.     offsetof(GeoIPRecord, country_code),
  236.     offsetof(GeoIPRecord, country_code3),
  237.     offsetof(GeoIPRecord, country_name),
  238.     offsetof(GeoIPRecord, region),
  239.     0,                                     /* no region name */
  240.     offsetof(GeoIPRecord, city),
  241.     offsetof(GeoIPRecord, postal_code),
  242.     offsetof(GeoIPRecord, latitude),
  243.     offsetof(GeoIPRecord, longitude),
  244.     offsetof(GeoIPRecord, dma_code),
  245.     offsetof(GeoIPRecord, area_code)
  246. };

  247. #endif


  248. #if (NGX_HAVE_MAXMINDDB)

  249. ‌static const char  *ngx_http_geoip_path_country_code[] = {
  250.     "country", "iso_code", NULL
  251. };

  252. ‌static const char  *ngx_http_geoip_path_country_name[] = {
  253.     "country", "names", "en", NULL
  254. };

  255. ‌static const char  *ngx_http_geoip_path_continent_code[] = {
  256.     "continent", "code", NULL
  257. };

  258. ‌static const char  *ngx_http_geoip_path_region[] = {
  259.     "subdivisions", "0", "iso_code", NULL
  260. };

  261. ‌static const char  *ngx_http_geoip_path_region_name[] = {
  262.     "subdivisions", "0", "names", "en", NULL
  263. };

  264. ‌static const char  *ngx_http_geoip_path_city[] = {
  265.     "city", "names", "en", NULL
  266. };

  267. ‌static const char  *ngx_http_geoip_path_postal_code[] = {
  268.     "postal", "code", NULL
  269. };

  270. ‌static const char  *ngx_http_geoip_path_latitude[] = {
  271.     "location", "latitude", NULL
  272. };

  273. ‌static const char  *ngx_http_geoip_path_longitude[] = {
  274.     "location", "longitude", NULL
  275. };

  276. ‌static const char  *ngx_http_geoip_path_dma_code[] = {
  277.     "location", "metro_code", NULL
  278. };


  279. /* indexed by NGX_GEOIP_COUNTRY_* */

  280. ‌static const char  **ngx_http_geoip_country_paths[] = {
  281.     ngx_http_geoip_path_country_code,
  282.     ngx_http_geoip_path_country_code,      /* converted to alpha-3 */
  283.     ngx_http_geoip_path_country_name
  284. };


  285. /* indexed by NGX_GEOIP_CITY_* */

  286. ‌static const char  **ngx_http_geoip_city_paths[] = {
  287.     ngx_http_geoip_path_continent_code,
  288.     ngx_http_geoip_path_country_code,
  289.     ngx_http_geoip_path_country_code,      /* converted to alpha-3 */
  290.     ngx_http_geoip_path_country_name,
  291.     ngx_http_geoip_path_region,
  292.     ngx_http_geoip_path_region_name,
  293.     ngx_http_geoip_path_city,
  294.     ngx_http_geoip_path_postal_code,
  295.     ngx_http_geoip_path_latitude,
  296.     ngx_http_geoip_path_longitude,
  297.     ngx_http_geoip_path_dma_code,
  298.     NULL                                   /* no area code */
  299. };


  300. /* ASNum, ISP, Org, and Domain editions, looked up in turn */

  301. ‌static const char  *ngx_http_geoip_org_paths[][2] = {
  302.     { "autonomous_system_organization", NULL },
  303.     { "isp", NULL },
  304.     { "organization", NULL },
  305.     { "domain", NULL },
  306.     { NULL, NULL }
  307. };


  308. /* MaxMind DB provides alpha-2 country codes only */

  309. ‌static ngx_http_geoip_ccode_t  ngx_http_geoip_country_codes[] = {
  310.     { "AD", "AND" }, { "AE", "ARE" }, { "AF", "AFG" }, { "AG", "ATG" },
  311.     { "AI", "AIA" }, { "AL", "ALB" }, { "AM", "ARM" }, { "AO", "AGO" },
  312.     { "AQ", "ATA" }, { "AR", "ARG" }, { "AS", "ASM" }, { "AT", "AUT" },
  313.     { "AU", "AUS" }, { "AW", "ABW" }, { "AX", "ALA" }, { "AZ", "AZE" },
  314.     { "BA", "BIH" }, { "BB", "BRB" }, { "BD", "BGD" }, { "BE", "BEL" },
  315.     { "BF", "BFA" }, { "BG", "BGR" }, { "BH", "BHR" }, { "BI", "BDI" },
  316.     { "BJ", "BEN" }, { "BL", "BLM" }, { "BM", "BMU" }, { "BN", "BRN" },
  317.     { "BO", "BOL" }, { "BQ", "BES" }, { "BR", "BRA" }, { "BS", "BHS" },
  318.     { "BT", "BTN" }, { "BV", "BVT" }, { "BW", "BWA" }, { "BY", "BLR" },
  319.     { "BZ", "BLZ" }, { "CA", "CAN" }, { "CC", "CCK" }, { "CD", "COD" },
  320.     { "CF", "CAF" }, { "CG", "COG" }, { "CH", "CHE" }, { "CI", "CIV" },
  321.     { "CK", "COK" }, { "CL", "CHL" }, { "CM", "CMR" }, { "CN", "CHN" },
  322.     { "CO", "COL" }, { "CR", "CRI" }, { "CU", "CUB" }, { "CV", "CPV" },
  323.     { "CW", "CUW" }, { "CX", "CXR" }, { "CY", "CYP" }, { "CZ", "CZE" },
  324.     { "DE", "DEU" }, { "DJ", "DJI" }, { "DK", "DNK" }, { "DM", "DMA" },
  325.     { "DO", "DOM" }, { "DZ", "DZA" }, { "EC", "ECU" }, { "EE", "EST" },
  326.     { "EG", "EGY" }, { "EH", "ESH" }, { "ER", "ERI" }, { "ES", "ESP" },
  327.     { "ET", "ETH" }, { "FI", "FIN" }, { "FJ", "FJI" }, { "FK", "FLK" },
  328.     { "FM", "FSM" }, { "FO", "FRO" }, { "FR", "FRA" }, { "GA", "GAB" },
  329.     { "GB", "GBR" }, { "GD", "GRD" }, { "GE", "GEO" }, { "GF", "GUF" },
  330.     { "GG", "GGY" }, { "GH", "GHA" }, { "GI", "GIB" }, { "GL", "GRL" },
  331.     { "GM", "GMB" }, { "GN", "GIN" }, { "GP", "GLP" }, { "GQ", "GNQ" },
  332.     { "GR", "GRC" }, { "GS", "SGS" }, { "GT", "GTM" }, { "GU", "GUM" },
  333.     { "GW", "GNB" }, { "GY", "GUY" }, { "HK", "HKG" }, { "HM", "HMD" },
  334.     { "HN", "HND" }, { "HR", "HRV" }, { "HT", "HTI" }, { "HU", "HUN" },
  335.     { "ID", "IDN" }, { "IE", "IRL" }, { "IL", "ISR" }, { "IM", "IMN" },
  336.     { "IN", "IND" }, { "IO", "IOT" }, { "IQ", "IRQ" }, { "IR", "IRN" },
  337.     { "IS", "ISL" }, { "IT", "ITA" }, { "JE", "JEY" }, { "JM", "JAM" },
  338.     { "JO", "JOR" }, { "JP", "JPN" }, { "KE", "KEN" }, { "KG", "KGZ" },
  339.     { "KH", "KHM" }, { "KI", "KIR" }, { "KM", "COM" }, { "KN", "KNA" },
  340.     { "KP", "PRK" }, { "KR", "KOR" }, { "KW", "KWT" }, { "KY", "CYM" },
  341.     { "KZ", "KAZ" }, { "LA", "LAO" }, { "LB", "LBN" }, { "LC", "LCA" },
  342.     { "LI", "LIE" }, { "LK", "LKA" }, { "LR", "LBR" }, { "LS", "LSO" },
  343.     { "LT", "LTU" }, { "LU", "LUX" }, { "LV", "LVA" }, { "LY", "LBY" },
  344.     { "MA", "MAR" }, { "MC", "MCO" }, { "MD", "MDA" }, { "ME", "MNE" },
  345.     { "MF", "MAF" }, { "MG", "MDG" }, { "MH", "MHL" }, { "MK", "MKD" },
  346.     { "ML", "MLI" }, { "MM", "MMR" }, { "MN", "MNG" }, { "MO", "MAC" },
  347.     { "MP", "MNP" }, { "MQ", "MTQ" }, { "MR", "MRT" }, { "MS", "MSR" },
  348.     { "MT", "MLT" }, { "MU", "MUS" }, { "MV", "MDV" }, { "MW", "MWI" },
  349.     { "MX", "MEX" }, { "MY", "MYS" }, { "MZ", "MOZ" }, { "NA", "NAM" },
  350.     { "NC", "NCL" }, { "NE", "NER" }, { "NF", "NFK" }, { "NG", "NGA" },
  351.     { "NI", "NIC" }, { "NL", "NLD" }, { "NO", "NOR" }, { "NP", "NPL" },
  352.     { "NR", "NRU" }, { "NU", "NIU" }, { "NZ", "NZL" }, { "OM", "OMN" },
  353.     { "PA", "PAN" }, { "PE", "PER" }, { "PF", "PYF" }, { "PG", "PNG" },
  354.     { "PH", "PHL" }, { "PK", "PAK" }, { "PL", "POL" }, { "PM", "SPM" },
  355.     { "PN", "PCN" }, { "PR", "PRI" }, { "PS", "PSE" }, { "PT", "PRT" },
  356.     { "PW", "PLW" }, { "PY", "PRY" }, { "QA", "QAT" }, { "RE", "REU" },
  357.     { "RO", "ROU" }, { "RS", "SRB" }, { "RU", "RUS" }, { "RW", "RWA" },
  358.     { "SA", "SAU" }, { "SB", "SLB" }, { "SC", "SYC" }, { "SD", "SDN" },
  359.     { "SE", "SWE" }, { "SG", "SGP" }, { "SH", "SHN" }, { "SI", "SVN" },
  360.     { "SJ", "SJM" }, { "SK", "SVK" }, { "SL", "SLE" }, { "SM", "SMR" },
  361.     { "SN", "SEN" }, { "SO", "SOM" }, { "SR", "SUR" }, { "SS", "SSD" },
  362.     { "ST", "STP" }, { "SV", "SLV" }, { "SX", "SXM" }, { "SY", "SYR" },
  363.     { "SZ", "SWZ" }, { "TC", "TCA" }, { "TD", "TCD" }, { "TF", "ATF" },
  364.     { "TG", "TGO" }, { "TH", "THA" }, { "TJ", "TJK" }, { "TK", "TKL" },
  365.     { "TL", "TLS" }, { "TM", "TKM" }, { "TN", "TUN" }, { "TO", "TON" },
  366.     { "TR", "TUR" }, { "TT", "TTO" }, { "TV", "TUV" }, { "TW", "TWN" },
  367.     { "TZ", "TZA" }, { "UA", "UKR" }, { "UG", "UGA" }, { "UM", "UMI" },
  368.     { "US", "USA" }, { "UY", "URY" }, { "UZ", "UZB" }, { "VA", "VAT" },
  369.     { "VC", "VCT" }, { "VE", "VEN" }, { "VG", "VGB" }, { "VI", "VIR" },
  370.     { "VN", "VNM" }, { "VU", "VUT" }, { "WF", "WLF" }, { "WS", "WSM" },
  371.     { "YE", "YEM" }, { "YT", "MYT" }, { "ZA", "ZAF" }, { "ZM", "ZMB" },
  372.     { "ZW", "ZWE" },
  373.     { NULL, NULL }
  374. };

  375. #endif


  376. #if (NGX_HAVE_GEOIP)

  377. static u_long
  378. ‌ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
  379. {
  380.     ngx_addr_t           addr;
  381.     ngx_table_elt_t     *xfwd;
  382.     struct sockaddr_in  *sin;

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

  386.     xfwd = r->headers_in.x_forwarded_for;

  387.     if (xfwd != NULL && gcf->proxies != NULL) {
  388.         (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
  389.                                            gcf->proxies, gcf->proxy_recursive);
  390.     }

  391. #if (NGX_HAVE_INET6)

  392.     if (addr.sockaddr->sa_family == AF_INET6) {
  393.         u_char           *p;
  394.         in_addr_t         inaddr;
  395.         struct in6_addr  *inaddr6;

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

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

  399.             inaddr = (in_addr_t) p[12] << 24;
  400.             inaddr += p[13] << 16;
  401.             inaddr += p[14] << 8;
  402.             inaddr += p[15];

  403.             return inaddr;
  404.         }
  405.     }

  406. #endif

  407.     if (addr.sockaddr->sa_family != AF_INET) {
  408.         return INADDR_NONE;
  409.     }

  410.     sin = (struct sockaddr_in *) addr.sockaddr;
  411.     return ntohl(sin->sin_addr.s_addr);
  412. }


  413. #if (NGX_HAVE_GEOIP_V6)

  414. static geoipv6_t
  415. ‌ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
  416. {
  417.     ngx_addr_t            addr;
  418.     ngx_table_elt_t      *xfwd;
  419.     in_addr_t             addr4;
  420.     struct in6_addr       addr6;
  421.     struct sockaddr_in   *sin;
  422.     struct sockaddr_in6  *sin6;

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

  426.     xfwd = r->headers_in.x_forwarded_for;

  427.     if (xfwd != NULL && gcf->proxies != NULL) {
  428.         (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
  429.                                            gcf->proxies, gcf->proxy_recursive);
  430.     }

  431.     switch (addr.sockaddr->sa_family) {

  432.     case AF_INET:
  433.         /* Produce IPv4-mapped IPv6 address. */
  434.         sin = (struct sockaddr_in *) addr.sockaddr;
  435.         addr4 = ntohl(sin->sin_addr.s_addr);

  436.         ngx_memzero(&addr6, sizeof(struct in6_addr));
  437.         addr6.s6_addr[10] = 0xff;
  438.         addr6.s6_addr[11] = 0xff;
  439.         addr6.s6_addr[12] = addr4 >> 24;
  440.         addr6.s6_addr[13] = addr4 >> 16;
  441.         addr6.s6_addr[14] = addr4 >> 8;
  442.         addr6.s6_addr[15] = addr4;
  443.         return addr6;

  444.     case AF_INET6:
  445.         sin6 = (struct sockaddr_in6 *) addr.sockaddr;
  446.         return sin6->sin6_addr;

  447.     default:
  448.         return in6addr_any;
  449.     }
  450. }

  451. #endif

  452. #endif


  453. static ngx_int_t
  454. ‌ngx_http_geoip_country_variable(ngx_http_request_t *r,
  455.     ngx_http_variable_value_t *v, uintptr_t data)
  456. {
  457. #if (NGX_HAVE_GEOIP)
  458.     ngx_http_geoip_variable_handler_pt     handler =
  459.         ngx_http_geoip_country_functions[data];
  460. #if (NGX_HAVE_GEOIP_V6)
  461.     ngx_http_geoip_variable_handler_v6_pt  handler_v6 =
  462.         ngx_http_geoip_country_v6_functions[data];
  463. #endif

  464.     const char             *val;
  465. #endif
  466. #if (NGX_HAVE_MAXMINDDB)
  467.     ngx_int_t               rc;
  468.     ngx_str_t               value;
  469.     MMDB_entry_s            entry;
  470. #endif
  471.     ngx_http_geoip_conf_t  *gcf;

  472.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  473. #if (NGX_HAVE_MAXMINDDB)

  474.     if (gcf->country_mmdb) {

  475.         if (ngx_http_geoip_lookup(r, gcf->country_mmdb, &entry) != NGX_OK) {
  476.             goto not_found;
  477.         }

  478.         rc = ngx_http_geoip_str(r, &entry,
  479.                                 ngx_http_geoip_country_paths[data], &value);

  480.         if (rc == NGX_OK && data == NGX_GEOIP_COUNTRY_CODE3) {
  481.             rc = ngx_http_geoip_code3(&value);
  482.         }

  483.         if (rc == NGX_ERROR) {
  484.             return NGX_ERROR;
  485.         }

  486.         if (rc != NGX_OK) {
  487.             goto not_found;
  488.         }

  489.         v->len = value.len;
  490.         v->valid = 1;
  491.         v->no_cacheable = 0;
  492.         v->not_found = 0;
  493.         v->data = value.data;

  494.         return NGX_OK;
  495.     }

  496. #endif

  497. #if (NGX_HAVE_GEOIP)

  498.     if (gcf->country == NULL) {
  499.         goto not_found;
  500.     }

  501. #if (NGX_HAVE_GEOIP_V6)
  502.     val = gcf->country_v6
  503.               ? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf))
  504.               : handler(gcf->country, ngx_http_geoip_addr(r, gcf));
  505. #else
  506.     val = handler(gcf->country, ngx_http_geoip_addr(r, gcf));
  507. #endif

  508.     if (val == NULL) {
  509.         goto not_found;
  510.     }

  511.     v->len = ngx_strlen(val);
  512.     v->valid = 1;
  513.     v->no_cacheable = 0;
  514.     v->not_found = 0;
  515.     v->data = (u_char *) val;

  516.     return NGX_OK;

  517. #endif

  518. not_found:

  519.     v->not_found = 1;

  520.     return NGX_OK;
  521. }


  522. static ngx_int_t
  523. ‌ngx_http_geoip_org_variable(ngx_http_request_t *r,
  524.     ngx_http_variable_value_t *v, uintptr_t data)
  525. {
  526. #if (NGX_HAVE_GEOIP)
  527.     size_t                  len;
  528.     char                   *val;
  529. #endif
  530. #if (NGX_HAVE_MAXMINDDB)
  531.     ngx_int_t               rc;
  532.     ngx_str_t               value;
  533.     ngx_uint_t              i;
  534.     MMDB_entry_s            entry;
  535. #endif
  536.     ngx_http_geoip_conf_t  *gcf;

  537.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  538. #if (NGX_HAVE_MAXMINDDB)

  539.     if (gcf->org_mmdb) {

  540.         if (ngx_http_geoip_lookup(r, gcf->org_mmdb, &entry) != NGX_OK) {
  541.             goto not_found;
  542.         }

  543.         for (i = 0; ngx_http_geoip_org_paths[i][0]; i++) {

  544.             rc = ngx_http_geoip_str(r, &entry,
  545.                                     ngx_http_geoip_org_paths[i], &value);

  546.             if (rc == NGX_ERROR) {
  547.                 return NGX_ERROR;
  548.             }

  549.             if (rc == NGX_OK) {
  550.                 v->len = value.len;
  551.                 v->valid = 1;
  552.                 v->no_cacheable = 0;
  553.                 v->not_found = 0;
  554.                 v->data = value.data;

  555.                 return NGX_OK;
  556.             }
  557.         }

  558.         goto not_found;
  559.     }

  560. #endif

  561. #if (NGX_HAVE_GEOIP)

  562.     if (gcf->org == NULL) {
  563.         goto not_found;
  564.     }

  565. #if (NGX_HAVE_GEOIP_V6)
  566.     val = gcf->org_v6
  567.               ? GeoIP_name_by_ipnum_v6(gcf->org,
  568.                                        ngx_http_geoip_addr_v6(r, gcf))
  569.               : GeoIP_name_by_ipnum(gcf->org,
  570.                                     ngx_http_geoip_addr(r, gcf));
  571. #else
  572.     val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf));
  573. #endif

  574.     if (val == NULL) {
  575.         goto not_found;
  576.     }

  577.     len = ngx_strlen(val);
  578.     v->data = ngx_pnalloc(r->pool, len);
  579.     if (v->data == NULL) {
  580.         ngx_free(val);
  581.         return NGX_ERROR;
  582.     }

  583.     ngx_memcpy(v->data, val, len);

  584.     v->len = len;
  585.     v->valid = 1;
  586.     v->no_cacheable = 0;
  587.     v->not_found = 0;

  588.     ngx_free(val);

  589.     return NGX_OK;

  590. #endif

  591. not_found:

  592.     v->not_found = 1;

  593.     return NGX_OK;
  594. }


  595. static ngx_int_t
  596. ‌ngx_http_geoip_city_variable(ngx_http_request_t *r,
  597.     ngx_http_variable_value_t *v, uintptr_t data)
  598. {
  599. #if (NGX_HAVE_GEOIP)
  600.     char                   *val;
  601.     size_t                  len;
  602.     GeoIPRecord            *gr;
  603. #endif
  604. #if (NGX_HAVE_MAXMINDDB)
  605.     ngx_int_t               rc;
  606.     ngx_str_t               value;
  607.     MMDB_entry_s            entry;
  608.     ngx_http_geoip_conf_t  *gcf;

  609.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  610.     if (gcf->city_mmdb) {

  611.         if (ngx_http_geoip_lookup(r, gcf->city_mmdb, &entry) != NGX_OK) {
  612.             goto not_found;
  613.         }

  614.         rc = ngx_http_geoip_str(r, &entry,
  615.                                 ngx_http_geoip_city_paths[data], &value);

  616.         if (rc == NGX_OK && data == NGX_GEOIP_CITY_COUNTRY_CODE3) {
  617.             rc = ngx_http_geoip_code3(&value);
  618.         }

  619.         if (rc == NGX_ERROR) {
  620.             return NGX_ERROR;
  621.         }

  622.         if (rc != NGX_OK) {
  623.             goto not_found;
  624.         }

  625.         v->len = value.len;
  626.         v->valid = 1;
  627.         v->no_cacheable = 0;
  628.         v->not_found = 0;
  629.         v->data = value.data;

  630.         return NGX_OK;
  631.     }

  632. #endif

  633. #if (NGX_HAVE_GEOIP)

  634.     gr = ngx_http_geoip_get_city_record(r);
  635.     if (gr == NULL) {
  636.         goto not_found;
  637.     }

  638.     val = *(char **) ((char *) gr + ngx_http_geoip_city_offsets[data]);
  639.     if (val == NULL) {
  640.         goto no_value;
  641.     }

  642.     len = ngx_strlen(val);
  643.     v->data = ngx_pnalloc(r->pool, len);
  644.     if (v->data == NULL) {
  645.         GeoIPRecord_delete(gr);
  646.         return NGX_ERROR;
  647.     }

  648.     ngx_memcpy(v->data, val, len);

  649.     v->len = len;
  650.     v->valid = 1;
  651.     v->no_cacheable = 0;
  652.     v->not_found = 0;

  653.     GeoIPRecord_delete(gr);

  654.     return NGX_OK;

  655. no_value:

  656.     GeoIPRecord_delete(gr);

  657. #endif

  658. not_found:

  659.     v->not_found = 1;

  660.     return NGX_OK;
  661. }


  662. static ngx_int_t
  663. ‌ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
  664.     ngx_http_variable_value_t *v, uintptr_t data)
  665. {
  666. #if (NGX_HAVE_GEOIP)
  667.     size_t                  len;
  668.     const char             *val;
  669.     GeoIPRecord            *gr;
  670. #endif
  671. #if (NGX_HAVE_MAXMINDDB)
  672.     ngx_int_t               rc;
  673.     ngx_str_t               value;
  674.     MMDB_entry_s            entry;
  675.     ngx_http_geoip_conf_t  *gcf;

  676.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  677.     if (gcf->city_mmdb) {

  678.         if (ngx_http_geoip_lookup(r, gcf->city_mmdb, &entry) != NGX_OK) {
  679.             goto not_found;
  680.         }

  681.         rc = ngx_http_geoip_str(r, &entry,
  682.                                 ngx_http_geoip_city_paths[data], &value);

  683.         if (rc == NGX_ERROR) {
  684.             return NGX_ERROR;
  685.         }

  686.         if (rc != NGX_OK) {
  687.             goto not_found;
  688.         }

  689.         v->len = value.len;
  690.         v->valid = 1;
  691.         v->no_cacheable = 0;
  692.         v->not_found = 0;
  693.         v->data = value.data;

  694.         return NGX_OK;
  695.     }

  696. #endif

  697. #if (NGX_HAVE_GEOIP)

  698.     gr = ngx_http_geoip_get_city_record(r);
  699.     if (gr == NULL) {
  700.         goto not_found;
  701.     }

  702.     val = GeoIP_region_name_by_code(gr->country_code, gr->region);

  703.     GeoIPRecord_delete(gr);

  704.     if (val == NULL) {
  705.         goto not_found;
  706.     }

  707.     len = ngx_strlen(val);
  708.     v->data = ngx_pnalloc(r->pool, len);
  709.     if (v->data == NULL) {
  710.         return NGX_ERROR;
  711.     }

  712.     ngx_memcpy(v->data, val, len);

  713.     v->len = len;
  714.     v->valid = 1;
  715.     v->no_cacheable = 0;
  716.     v->not_found = 0;

  717.     return NGX_OK;

  718. #endif

  719. not_found:

  720.     v->not_found = 1;

  721.     return NGX_OK;
  722. }


  723. static ngx_int_t
  724. ‌ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
  725.     ngx_http_variable_value_t *v, uintptr_t data)
  726. {
  727. #if (NGX_HAVE_GEOIP)
  728.     float                   val;
  729.     GeoIPRecord            *gr;
  730. #endif
  731. #if (NGX_HAVE_MAXMINDDB)
  732.     double                  value;
  733.     ngx_int_t               rc;
  734.     MMDB_entry_s            entry;
  735.     ngx_http_geoip_conf_t  *gcf;

  736.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  737.     if (gcf->city_mmdb) {

  738.         if (ngx_http_geoip_lookup(r, gcf->city_mmdb, &entry) != NGX_OK) {
  739.             goto not_found;
  740.         }

  741.         rc = ngx_http_geoip_float(&entry,
  742.                                   ngx_http_geoip_city_paths[data], &value);

  743.         if (rc != NGX_OK) {
  744.             goto not_found;
  745.         }

  746.         v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);
  747.         if (v->data == NULL) {
  748.             return NGX_ERROR;
  749.         }

  750.         v->len = ngx_sprintf(v->data, "%.4f", value) - v->data;
  751.         v->valid = 1;
  752.         v->no_cacheable = 0;
  753.         v->not_found = 0;

  754.         return NGX_OK;
  755.     }

  756. #endif

  757. #if (NGX_HAVE_GEOIP)

  758.     gr = ngx_http_geoip_get_city_record(r);
  759.     if (gr == NULL) {
  760.         goto not_found;
  761.     }

  762.     v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);
  763.     if (v->data == NULL) {
  764.         GeoIPRecord_delete(gr);
  765.         return NGX_ERROR;
  766.     }

  767.     val = *(float *) ((char *) gr + ngx_http_geoip_city_offsets[data]);

  768.     v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
  769.     v->valid = 1;
  770.     v->no_cacheable = 0;
  771.     v->not_found = 0;

  772.     GeoIPRecord_delete(gr);

  773.     return NGX_OK;

  774. #endif

  775. not_found:

  776.     v->not_found = 1;

  777.     return NGX_OK;
  778. }


  779. static ngx_int_t
  780. ‌ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
  781.     ngx_http_variable_value_t *v, uintptr_t data)
  782. {
  783. #if (NGX_HAVE_GEOIP)
  784.     int                     val;
  785.     GeoIPRecord            *gr;
  786. #endif
  787. #if (NGX_HAVE_MAXMINDDB)
  788.     int64_t                 value;
  789.     ngx_int_t               rc;
  790.     MMDB_entry_s            entry;
  791.     ngx_http_geoip_conf_t  *gcf;

  792.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  793.     if (gcf->city_mmdb) {

  794.         if (ngx_http_geoip_lookup(r, gcf->city_mmdb, &entry) != NGX_OK) {
  795.             goto not_found;
  796.         }

  797.         rc = ngx_http_geoip_int(&entry,
  798.                                 ngx_http_geoip_city_paths[data], &value);

  799.         if (rc != NGX_OK) {
  800.             goto not_found;
  801.         }

  802.         v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);
  803.         if (v->data == NULL) {
  804.             return NGX_ERROR;
  805.         }

  806.         v->len = ngx_sprintf(v->data, "%L", value) - v->data;
  807.         v->valid = 1;
  808.         v->no_cacheable = 0;
  809.         v->not_found = 0;

  810.         return NGX_OK;
  811.     }

  812. #endif

  813. #if (NGX_HAVE_GEOIP)

  814.     gr = ngx_http_geoip_get_city_record(r);
  815.     if (gr == NULL) {
  816.         goto not_found;
  817.     }

  818.     v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);
  819.     if (v->data == NULL) {
  820.         GeoIPRecord_delete(gr);
  821.         return NGX_ERROR;
  822.     }

  823.     val = *(int *) ((char *) gr + ngx_http_geoip_city_offsets[data]);

  824.     v->len = ngx_sprintf(v->data, "%d", val) - v->data;
  825.     v->valid = 1;
  826.     v->no_cacheable = 0;
  827.     v->not_found = 0;

  828.     GeoIPRecord_delete(gr);

  829.     return NGX_OK;

  830. #endif

  831. not_found:

  832.     v->not_found = 1;

  833.     return NGX_OK;
  834. }


  835. #if (NGX_HAVE_GEOIP)

  836. static GeoIPRecord *
  837. ‌ngx_http_geoip_get_city_record(ngx_http_request_t *r)
  838. {
  839.     ngx_http_geoip_conf_t  *gcf;

  840.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  841.     if (gcf->city) {
  842. #if (NGX_HAVE_GEOIP_V6)
  843.         return gcf->city_v6
  844.                    ? GeoIP_record_by_ipnum_v6(gcf->city,
  845.                                               ngx_http_geoip_addr_v6(r, gcf))
  846.                    : GeoIP_record_by_ipnum(gcf->city,
  847.                                            ngx_http_geoip_addr(r, gcf));
  848. #else
  849.         return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf));
  850. #endif
  851.     }

  852.     return NULL;
  853. }

  854. #endif


  855. #if (NGX_HAVE_MAXMINDDB)

  856. static ngx_int_t
  857. ‌ngx_http_geoip_str(ngx_http_request_t *r, MMDB_entry_s *entry,
  858.     const char **path, ngx_str_t *value)
  859. {
  860.     MMDB_entry_data_s  entry_data;

  861.     if (path == NULL
  862.         || MMDB_aget_value(entry, &entry_data, path) != MMDB_SUCCESS
  863.         || !entry_data.has_data
  864.         || entry_data.type != MMDB_DATA_TYPE_UTF8_STRING)
  865.     {
  866.         return NGX_DECLINED;
  867.     }

  868.     value->data = ngx_pnalloc(r->pool, entry_data.data_size);
  869.     if (value->data == NULL) {
  870.         return NGX_ERROR;
  871.     }

  872.     ngx_memcpy(value->data, entry_data.utf8_string, entry_data.data_size);
  873.     value->len = entry_data.data_size;

  874.     return NGX_OK;
  875. }


  876. static ngx_int_t
  877. ‌ngx_http_geoip_code3(ngx_str_t *value)
  878. {
  879.     ngx_http_geoip_ccode_t  *cc;

  880.     if (value->len != 2) {
  881.         return NGX_DECLINED;
  882.     }

  883.     for (cc = ngx_http_geoip_country_codes; cc->alpha2; cc++) {
  884.         if (value->data[0] == cc->alpha2[0]
  885.             && value->data[1] == cc->alpha2[1])
  886.         {
  887.             value->len = 3;
  888.             value->data = (u_char *) cc->alpha3;
  889.             return NGX_OK;
  890.         }
  891.     }

  892.     return NGX_DECLINED;
  893. }


  894. static ngx_int_t
  895. ‌ngx_http_geoip_float(MMDB_entry_s *entry, const char **path, double *value)
  896. {
  897.     MMDB_entry_data_s  entry_data;

  898.     if (path == NULL
  899.         || MMDB_aget_value(entry, &entry_data, path) != MMDB_SUCCESS
  900.         || !entry_data.has_data)
  901.     {
  902.         return NGX_DECLINED;
  903.     }

  904.     switch (entry_data.type) {

  905.     case MMDB_DATA_TYPE_DOUBLE:
  906.         *value = entry_data.double_value;
  907.         break;

  908.     case MMDB_DATA_TYPE_FLOAT:
  909.         *value = entry_data.float_value;
  910.         break;

  911.     default:
  912.         return NGX_DECLINED;
  913.     }

  914.     /* the range of a coordinate in degrees; NaN fails the comparison */

  915.     if (!(*value >= -NGX_GEOIP_MAX_COORD && *value <= NGX_GEOIP_MAX_COORD)) {
  916.         return NGX_DECLINED;
  917.     }

  918.     return NGX_OK;
  919. }


  920. static ngx_int_t
  921. ‌ngx_http_geoip_int(MMDB_entry_s *entry, const char **path, int64_t *value)
  922. {
  923.     MMDB_entry_data_s  entry_data;

  924.     if (path == NULL
  925.         || MMDB_aget_value(entry, &entry_data, path) != MMDB_SUCCESS
  926.         || !entry_data.has_data)
  927.     {
  928.         return NGX_DECLINED;
  929.     }

  930.     switch (entry_data.type) {

  931.     case MMDB_DATA_TYPE_UINT16:
  932.         *value = entry_data.uint16;
  933.         return NGX_OK;

  934.     case MMDB_DATA_TYPE_UINT32:
  935.         *value = entry_data.uint32;
  936.         return NGX_OK;

  937.     case MMDB_DATA_TYPE_INT32:
  938.         *value = entry_data.int32;
  939.         return NGX_OK;

  940.     default:
  941.         return NGX_DECLINED;
  942.     }
  943. }


  944. static ngx_int_t
  945. ‌ngx_http_geoip_lookup(ngx_http_request_t *r, MMDB_s *mmdb, MMDB_entry_s *entry)
  946. {
  947.     int                     err;
  948.     ngx_addr_t              addr;
  949.     ngx_table_elt_t        *xfwd;
  950.     MMDB_lookup_result_s    result;
  951.     ngx_http_geoip_conf_t  *gcf;
  952. #if (NGX_HAVE_INET6)
  953.     struct in6_addr        *inaddr6;
  954.     struct sockaddr_in      sin;
  955. #endif

  956.     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

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

  960.     xfwd = r->headers_in.x_forwarded_for;

  961.     if (xfwd != NULL && gcf->proxies != NULL) {
  962.         (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
  963.                                            gcf->proxies, gcf->proxy_recursive);
  964.     }

  965.     switch (addr.sockaddr->sa_family) {

  966.     case AF_INET:
  967.         break;

  968. #if (NGX_HAVE_INET6)
  969.     case AF_INET6:
  970.         inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;

  971.         if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
  972.             ngx_memzero(&sin, sizeof(struct sockaddr_in));
  973.             sin.sin_family = AF_INET;
  974.             ngx_memcpy(&sin.sin_addr, &inaddr6->s6_addr[12], 4);

  975.             addr.sockaddr = (struct sockaddr *) &sin;
  976.             break;
  977.         }

  978.         if (mmdb->metadata.ip_version == 4) {
  979.             return NGX_DECLINED;
  980.         }

  981.         break;
  982. #endif

  983.     default:
  984.         return NGX_DECLINED;
  985.     }

  986.     result = MMDB_lookup_sockaddr(mmdb, addr.sockaddr, &err);

  987.     if (err != MMDB_SUCCESS) {
  988.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  989.                       "MMDB_lookup_sockaddr() failed: %s", MMDB_strerror(err));
  990.         return NGX_DECLINED;
  991.     }

  992.     if (!result.found_entry) {
  993.         return NGX_DECLINED;
  994.     }

  995.     *entry = result.entry;

  996.     return NGX_OK;
  997. }

  998. #endif


  999. static ngx_int_t
  1000. ‌ngx_http_geoip_add_variables(ngx_conf_t *cf)
  1001. {
  1002.     ngx_http_variable_t  *var, *v;

  1003.     for (v = ngx_http_geoip_vars; v->name.len; v++) {
  1004.         var = ngx_http_add_variable(cf, &v->name, v->flags);
  1005.         if (var == NULL) {
  1006.             return NGX_ERROR;
  1007.         }

  1008.         var->get_handler = v->get_handler;
  1009.         var->data = v->data;
  1010.     }

  1011.     return NGX_OK;
  1012. }


  1013. static void *
  1014. ‌ngx_http_geoip_create_conf(ngx_conf_t *cf)
  1015. {
  1016.     ngx_pool_cleanup_t     *cln;
  1017.     ngx_http_geoip_conf_t  *conf;

  1018.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip_conf_t));
  1019.     if (conf == NULL) {
  1020.         return NULL;
  1021.     }

  1022.     conf->proxy_recursive = NGX_CONF_UNSET;

  1023.     cln = ngx_pool_cleanup_add(cf->pool, 0);
  1024.     if (cln == NULL) {
  1025.         return NULL;
  1026.     }

  1027.     cln->handler = ngx_http_geoip_cleanup;
  1028.     cln->data = conf;

  1029.     return conf;
  1030. }


  1031. static char *
  1032. ‌ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf)
  1033. {
  1034.     ngx_http_geoip_conf_t  *gcf = conf;

  1035.     ngx_conf_init_value(gcf->proxy_recursive, 0);

  1036.     return NGX_CONF_OK;
  1037. }


  1038. static char *
  1039. ‌ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1040. {
  1041.     ngx_http_geoip_conf_t  *gcf = conf;

  1042.     ngx_str_t  *value;

  1043. #if (NGX_HAVE_GEOIP)
  1044.     if (gcf->country) {
  1045.         return "is duplicate";
  1046.     }
  1047. #endif
  1048. #if (NGX_HAVE_MAXMINDDB)
  1049.     if (gcf->country_mmdb) {
  1050.         return "is duplicate";
  1051.     }
  1052. #endif

  1053.     value = cf->args->elts;

  1054. #if (NGX_HAVE_MAXMINDDB)

  1055.     switch (ngx_http_geoip_open(cf, &value[1], &gcf->country_mmdb)) {

  1056.     case NGX_OK:
  1057.         return ngx_http_geoip_charset(cf);

  1058.     case NGX_ERROR:
  1059.         return NGX_CONF_ERROR;

  1060.     default:
  1061.         break;
  1062.     }

  1063. #endif

  1064. #if (NGX_HAVE_GEOIP)

  1065.     gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

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

  1069.         return NGX_CONF_ERROR;
  1070.     }

  1071.     if (cf->args->nelts == 3) {
  1072.         if (ngx_strcmp(value[2].data, "utf8") == 0) {
  1073.             GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);

  1074.         } else {
  1075.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1076.                                "invalid parameter \"%V\"", &value[2]);
  1077.             return NGX_CONF_ERROR;
  1078.         }
  1079.     }

  1080.     switch (gcf->country->databaseType) {

  1081.     case GEOIP_COUNTRY_EDITION:

  1082.         return NGX_CONF_OK;

  1083. #if (NGX_HAVE_GEOIP_V6)
  1084.     case GEOIP_COUNTRY_EDITION_V6:

  1085.         gcf->country_v6 = 1;
  1086.         return NGX_CONF_OK;
  1087. #endif

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

  1094. #else

  1095.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1096.                        "\"%V\" is not a MaxMind DB database", &value[1]);

  1097.     return NGX_CONF_ERROR;

  1098. #endif
  1099. }


  1100. static char *
  1101. ‌ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1102. {
  1103.     ngx_http_geoip_conf_t  *gcf = conf;

  1104.     ngx_str_t  *value;

  1105. #if (NGX_HAVE_GEOIP)
  1106.     if (gcf->org) {
  1107.         return "is duplicate";
  1108.     }
  1109. #endif
  1110. #if (NGX_HAVE_MAXMINDDB)
  1111.     if (gcf->org_mmdb) {
  1112.         return "is duplicate";
  1113.     }
  1114. #endif

  1115.     value = cf->args->elts;

  1116. #if (NGX_HAVE_MAXMINDDB)

  1117.     switch (ngx_http_geoip_open(cf, &value[1], &gcf->org_mmdb)) {

  1118.     case NGX_OK:
  1119.         return ngx_http_geoip_charset(cf);

  1120.     case NGX_ERROR:
  1121.         return NGX_CONF_ERROR;

  1122.     default:
  1123.         break;
  1124.     }

  1125. #endif

  1126. #if (NGX_HAVE_GEOIP)

  1127.     gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

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

  1131.         return NGX_CONF_ERROR;
  1132.     }

  1133.     if (cf->args->nelts == 3) {
  1134.         if (ngx_strcmp(value[2].data, "utf8") == 0) {
  1135.             GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);

  1136.         } else {
  1137.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1138.                                "invalid parameter \"%V\"", &value[2]);
  1139.             return NGX_CONF_ERROR;
  1140.         }
  1141.     }

  1142.     switch (gcf->org->databaseType) {

  1143.     case GEOIP_ISP_EDITION:
  1144.     case GEOIP_ORG_EDITION:
  1145.     case GEOIP_DOMAIN_EDITION:
  1146.     case GEOIP_ASNUM_EDITION:

  1147.         return NGX_CONF_OK;

  1148. #if (NGX_HAVE_GEOIP_V6)
  1149.     case GEOIP_ISP_EDITION_V6:
  1150.     case GEOIP_ORG_EDITION_V6:
  1151.     case GEOIP_DOMAIN_EDITION_V6:
  1152.     case GEOIP_ASNUM_EDITION_V6:

  1153.         gcf->org_v6 = 1;
  1154.         return NGX_CONF_OK;
  1155. #endif

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

  1162. #else

  1163.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1164.                        "\"%V\" is not a MaxMind DB database", &value[1]);

  1165.     return NGX_CONF_ERROR;

  1166. #endif
  1167. }


  1168. static char *
  1169. ‌ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1170. {
  1171.     ngx_http_geoip_conf_t  *gcf = conf;

  1172.     ngx_str_t  *value;

  1173. #if (NGX_HAVE_GEOIP)
  1174.     if (gcf->city) {
  1175.         return "is duplicate";
  1176.     }
  1177. #endif
  1178. #if (NGX_HAVE_MAXMINDDB)
  1179.     if (gcf->city_mmdb) {
  1180.         return "is duplicate";
  1181.     }
  1182. #endif

  1183.     value = cf->args->elts;

  1184. #if (NGX_HAVE_MAXMINDDB)

  1185.     switch (ngx_http_geoip_open(cf, &value[1], &gcf->city_mmdb)) {

  1186.     case NGX_OK:
  1187.         return ngx_http_geoip_charset(cf);

  1188.     case NGX_ERROR:
  1189.         return NGX_CONF_ERROR;

  1190.     default:
  1191.         break;
  1192.     }

  1193. #endif

  1194. #if (NGX_HAVE_GEOIP)

  1195.     gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);

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

  1199.         return NGX_CONF_ERROR;
  1200.     }

  1201.     if (cf->args->nelts == 3) {
  1202.         if (ngx_strcmp(value[2].data, "utf8") == 0) {
  1203.             GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);

  1204.         } else {
  1205.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1206.                                "invalid parameter \"%V\"", &value[2]);
  1207.             return NGX_CONF_ERROR;
  1208.         }
  1209.     }

  1210.     switch (gcf->city->databaseType) {

  1211.     case GEOIP_CITY_EDITION_REV0:
  1212.     case GEOIP_CITY_EDITION_REV1:

  1213.         return NGX_CONF_OK;

  1214. #if (NGX_HAVE_GEOIP_V6)
  1215.     case GEOIP_CITY_EDITION_REV0_V6:
  1216.     case GEOIP_CITY_EDITION_REV1_V6:

  1217.         gcf->city_v6 = 1;
  1218.         return NGX_CONF_OK;
  1219. #endif

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

  1226. #else

  1227.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1228.                        "\"%V\" is not a MaxMind DB database", &value[1]);

  1229.     return NGX_CONF_ERROR;

  1230. #endif
  1231. }


  1232. #if (NGX_HAVE_MAXMINDDB)

  1233. static ngx_int_t
  1234. ‌ngx_http_geoip_open(ngx_conf_t *cf, ngx_str_t *name, MMDB_s **db)
  1235. {
  1236.     int               status;
  1237.     MMDB_s           *mmdb;
  1238.     ngx_file_info_t   fi;

  1239.     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
  1240.         ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
  1241.                            ngx_file_info_n " \"%V\" failed", name);
  1242.         return NGX_ERROR;
  1243.     }

  1244.     mmdb = ngx_palloc(cf->pool, sizeof(MMDB_s));
  1245.     if (mmdb == NULL) {
  1246.         return NGX_ERROR;
  1247.     }

  1248.     status = MMDB_open((char *) name->data, MMDB_MODE_MMAP, mmdb);

  1249.     if (status == MMDB_SUCCESS) {
  1250.         *db = mmdb;
  1251.         return NGX_OK;
  1252.     }

  1253.     /* presumably a legacy GeoIP database */

  1254.     if (status == MMDB_INVALID_METADATA_ERROR) {
  1255.         return NGX_DECLINED;
  1256.     }

  1257.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "MMDB_open(\"%V\") failed: %s",
  1258.                        name, MMDB_strerror(status));

  1259.     return NGX_ERROR;
  1260. }


  1261. static char *
  1262. ‌ngx_http_geoip_charset(ngx_conf_t *cf)
  1263. {
  1264.     ngx_str_t  *value;

  1265.     value = cf->args->elts;

  1266.     /* MaxMind DB is always in UTF-8 */

  1267.     if (cf->args->nelts == 3 && ngx_strcmp(value[2].data, "utf8") != 0) {
  1268.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1269.                            "invalid parameter \"%V\"", &value[2]);
  1270.         return NGX_CONF_ERROR;
  1271.     }

  1272.     return NGX_CONF_OK;
  1273. }

  1274. #endif


  1275. static char *
  1276. ‌ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1277. {
  1278.     ngx_http_geoip_conf_t  *gcf = conf;

  1279.     ngx_str_t   *value;
  1280.     ngx_cidr_t  cidr, *c;

  1281.     value = cf->args->elts;

  1282.     if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
  1283.         return NGX_CONF_ERROR;
  1284.     }

  1285.     if (gcf->proxies == NULL) {
  1286.         gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));
  1287.         if (gcf->proxies == NULL) {
  1288.             return NGX_CONF_ERROR;
  1289.         }
  1290.     }

  1291.     c = ngx_array_push(gcf->proxies);
  1292.     if (c == NULL) {
  1293.         return NGX_CONF_ERROR;
  1294.     }

  1295.     *c = cidr;

  1296.     return NGX_CONF_OK;
  1297. }

  1298. static ngx_int_t
  1299. ‌ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
  1300. {
  1301.     ngx_int_t  rc;

  1302.     if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
  1303.         cidr->family = AF_INET;
  1304.         cidr->u.in.addr = 0xffffffff;
  1305.         cidr->u.in.mask = 0xffffffff;

  1306.         return NGX_OK;
  1307.     }

  1308.     rc = ngx_ptocidr(net, cidr);

  1309.     if (rc == NGX_ERROR) {
  1310.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
  1311.         return NGX_ERROR;
  1312.     }

  1313.     if (rc == NGX_DONE) {
  1314.         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  1315.                            "low address bits of %V are meaningless", net);
  1316.     }

  1317.     return NGX_OK;
  1318. }


  1319. static void
  1320. ‌ngx_http_geoip_cleanup(void *data)
  1321. {
  1322.     ngx_http_geoip_conf_t  *gcf = data;

  1323. #if (NGX_HAVE_GEOIP)

  1324.     if (gcf->country) {
  1325.         GeoIP_delete(gcf->country);
  1326.     }

  1327.     if (gcf->org) {
  1328.         GeoIP_delete(gcf->org);
  1329.     }

  1330.     if (gcf->city) {
  1331.         GeoIP_delete(gcf->city);
  1332.     }

  1333. #endif

  1334. #if (NGX_HAVE_MAXMINDDB)

  1335.     if (gcf->country_mmdb) {
  1336.         MMDB_close(gcf->country_mmdb);
  1337.     }

  1338.     if (gcf->org_mmdb) {
  1339.         MMDB_close(gcf->org_mmdb);
  1340.     }

  1341.     if (gcf->city_mmdb) {
  1342.         MMDB_close(gcf->city_mmdb);
  1343.     }

  1344. #endif
  1345. }