src/stream/ngx_stream_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_stream.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. #if (NGX_HAVE_GEOIP_V6)
  43.     unsigned      country_v6:1;
  44.     unsigned      org_v6:1;
  45.     unsigned      city_v6:1;
  46. #endif
  47. ‌} ngx_stream_geoip_conf_t;


  48. ‌typedef struct {
  49.     ngx_str_t    *name;
  50.     uintptr_t     data;
  51. ‌} ngx_stream_geoip_var_t;


  52. #if (NGX_HAVE_MAXMINDDB)

  53. ‌typedef struct {
  54.     const char   *alpha2;
  55.     const char   *alpha3;
  56. ‌} ngx_stream_geoip_ccode_t;

  57. #endif


  58. #if (NGX_HAVE_GEOIP)

  59. ‌typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *,
  60.     u_long addr);


  61. ‌ngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = {
  62.     GeoIP_country_code_by_ipnum,
  63.     GeoIP_country_code3_by_ipnum,
  64.     GeoIP_country_name_by_ipnum,
  65. };


  66. #if (NGX_HAVE_GEOIP_V6)

  67. ‌typedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *,
  68.     geoipv6_t addr);


  69. ngx_stream_geoip_variable_handler_v6_pt
  70. ‌    ngx_stream_geoip_country_v6_functions[] =
  71. {
  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_stream_geoip_country_variable(ngx_stream_session_t *s,
  79.     ngx_stream_variable_value_t *v, uintptr_t data);
  80. static ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s,
  81.     ngx_stream_variable_value_t *v, uintptr_t data);
  82. static ngx_int_t ngx_stream_geoip_city_variable(ngx_stream_session_t *s,
  83.     ngx_stream_variable_value_t *v, uintptr_t data);
  84. static ngx_int_t ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,
  85.     ngx_stream_variable_value_t *v, uintptr_t data);
  86. static ngx_int_t ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,
  87.     ngx_stream_variable_value_t *v, uintptr_t data);
  88. static ngx_int_t ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
  89.     ngx_stream_variable_value_t *v, uintptr_t data);
  90. #if (NGX_HAVE_GEOIP)
  91. static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s);
  92. #endif
  93. #if (NGX_HAVE_MAXMINDDB)
  94. static ngx_int_t ngx_stream_geoip_str(ngx_stream_session_t *s,
  95.     MMDB_entry_s *entry, const char **path, ngx_str_t *value);
  96. static ngx_int_t ngx_stream_geoip_code3(ngx_str_t *value);
  97. static ngx_int_t ngx_stream_geoip_float(MMDB_entry_s *entry, const char **path,
  98.     double *value);
  99. static ngx_int_t ngx_stream_geoip_int(MMDB_entry_s *entry, const char **path,
  100.     int64_t *value);
  101. static ngx_int_t ngx_stream_geoip_lookup(ngx_stream_session_t *s, MMDB_s *mmdb,
  102.     MMDB_entry_s *entry);
  103. #endif

  104. static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf);
  105. static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf);
  106. static char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
  107.     void *conf);
  108. static char *ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
  109.     void *conf);
  110. static char *ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
  111.     void *conf);
  112. #if (NGX_HAVE_MAXMINDDB)
  113. static ngx_int_t ngx_stream_geoip_open(ngx_conf_t *cf, ngx_str_t *name,
  114.     MMDB_s **db);
  115. static char *ngx_stream_geoip_charset(ngx_conf_t *cf);
  116. #endif
  117. static void ngx_stream_geoip_cleanup(void *data);


  118. ‌static ngx_command_t  ngx_stream_geoip_commands[] = {

  119.     { ngx_string("geoip_country"),
  120.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
  121.       ngx_stream_geoip_country,
  122.       NGX_STREAM_MAIN_CONF_OFFSET,
  123.       0,
  124.       NULL },

  125.     { ngx_string("geoip_org"),
  126.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
  127.       ngx_stream_geoip_org,
  128.       NGX_STREAM_MAIN_CONF_OFFSET,
  129.       0,
  130.       NULL },

  131.     { ngx_string("geoip_city"),
  132.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
  133.       ngx_stream_geoip_city,
  134.       NGX_STREAM_MAIN_CONF_OFFSET,
  135.       0,
  136.       NULL },

  137.       ngx_null_command
  138. };


  139. ‌static ngx_stream_module_t  ngx_stream_geoip_module_ctx = {
  140.     ngx_stream_geoip_add_variables,        /* preconfiguration */
  141.     NULL,                                  /* postconfiguration */

  142.     ngx_stream_geoip_create_conf,          /* create main configuration */
  143.     NULL,                                  /* init main configuration */

  144.     NULL,                                  /* create server configuration */
  145.     NULL                                   /* merge server configuration */
  146. };


  147. ‌ngx_module_t  ngx_stream_geoip_module = {
  148.     NGX_MODULE_V1,
  149.     &ngx_stream_geoip_module_ctx,          /* module context */
  150.     ngx_stream_geoip_commands,             /* module directives */
  151.     NGX_STREAM_MODULE,                     /* module type */
  152.     NULL,                                  /* init master */
  153.     NULL,                                  /* init module */
  154.     NULL,                                  /* init process */
  155.     NULL,                                  /* init thread */
  156.     NULL,                                  /* exit thread */
  157.     NULL,                                  /* exit process */
  158.     NULL,                                  /* exit master */
  159.     NGX_MODULE_V1_PADDING
  160. };


  161. ‌static ngx_stream_variable_t  ngx_stream_geoip_vars[] = {

  162.     { ngx_string("geoip_country_code"), NULL,
  163.       ngx_stream_geoip_country_variable,
  164.       NGX_GEOIP_COUNTRY_CODE, 0, 0 },

  165.     { ngx_string("geoip_country_code3"), NULL,
  166.       ngx_stream_geoip_country_variable,
  167.       NGX_GEOIP_COUNTRY_CODE3, 0, 0 },

  168.     { ngx_string("geoip_country_name"), NULL,
  169.       ngx_stream_geoip_country_variable,
  170.       NGX_GEOIP_COUNTRY_NAME, 0, 0 },

  171.     { ngx_string("geoip_org"), NULL,
  172.       ngx_stream_geoip_org_variable,
  173.       0, 0, 0 },

  174.     { ngx_string("geoip_city_continent_code"), NULL,
  175.       ngx_stream_geoip_city_variable,
  176.       NGX_GEOIP_CITY_CONTINENT_CODE, 0, 0 },

  177.     { ngx_string("geoip_city_country_code"), NULL,
  178.       ngx_stream_geoip_city_variable,
  179.       NGX_GEOIP_CITY_COUNTRY_CODE, 0, 0 },

  180.     { ngx_string("geoip_city_country_code3"), NULL,
  181.       ngx_stream_geoip_city_variable,
  182.       NGX_GEOIP_CITY_COUNTRY_CODE3, 0, 0 },

  183.     { ngx_string("geoip_city_country_name"), NULL,
  184.       ngx_stream_geoip_city_variable,
  185.       NGX_GEOIP_CITY_COUNTRY_NAME, 0, 0 },

  186.     { ngx_string("geoip_region"), NULL,
  187.       ngx_stream_geoip_city_variable,
  188.       NGX_GEOIP_CITY_REGION, 0, 0 },

  189.     { ngx_string("geoip_region_name"), NULL,
  190.       ngx_stream_geoip_region_name_variable,
  191.       NGX_GEOIP_CITY_REGION_NAME, 0, 0 },

  192.     { ngx_string("geoip_city"), NULL,
  193.       ngx_stream_geoip_city_variable,
  194.       NGX_GEOIP_CITY_NAME, 0, 0 },

  195.     { ngx_string("geoip_postal_code"), NULL,
  196.       ngx_stream_geoip_city_variable,
  197.       NGX_GEOIP_CITY_POSTAL_CODE, 0, 0 },

  198.     { ngx_string("geoip_latitude"), NULL,
  199.       ngx_stream_geoip_city_float_variable,
  200.       NGX_GEOIP_CITY_LATITUDE, 0, 0 },

  201.     { ngx_string("geoip_longitude"), NULL,
  202.       ngx_stream_geoip_city_float_variable,
  203.       NGX_GEOIP_CITY_LONGITUDE, 0, 0 },

  204.     { ngx_string("geoip_dma_code"), NULL,
  205.       ngx_stream_geoip_city_int_variable,
  206.       NGX_GEOIP_CITY_DMA_CODE, 0, 0 },

  207.     { ngx_string("geoip_area_code"), NULL,
  208.       ngx_stream_geoip_city_int_variable,
  209.       NGX_GEOIP_CITY_AREA_CODE, 0, 0 },

  210.       ngx_stream_null_variable
  211. };


  212. #if (NGX_HAVE_GEOIP)

  213. /* indexed by NGX_GEOIP_CITY_* */

  214. ‌static size_t  ngx_stream_geoip_city_offsets[] = {
  215.     offsetof(GeoIPRecord, continent_code),
  216.     offsetof(GeoIPRecord, country_code),
  217.     offsetof(GeoIPRecord, country_code3),
  218.     offsetof(GeoIPRecord, country_name),
  219.     offsetof(GeoIPRecord, region),
  220.     0,                                     /* no region name */
  221.     offsetof(GeoIPRecord, city),
  222.     offsetof(GeoIPRecord, postal_code),
  223.     offsetof(GeoIPRecord, latitude),
  224.     offsetof(GeoIPRecord, longitude),
  225.     offsetof(GeoIPRecord, dma_code),
  226.     offsetof(GeoIPRecord, area_code)
  227. };

  228. #endif


  229. #if (NGX_HAVE_MAXMINDDB)

  230. ‌static const char  *ngx_stream_geoip_path_country_code[] = {
  231.     "country", "iso_code", NULL
  232. };

  233. ‌static const char  *ngx_stream_geoip_path_country_name[] = {
  234.     "country", "names", "en", NULL
  235. };

  236. ‌static const char  *ngx_stream_geoip_path_continent_code[] = {
  237.     "continent", "code", NULL
  238. };

  239. ‌static const char  *ngx_stream_geoip_path_region[] = {
  240.     "subdivisions", "0", "iso_code", NULL
  241. };

  242. ‌static const char  *ngx_stream_geoip_path_region_name[] = {
  243.     "subdivisions", "0", "names", "en", NULL
  244. };

  245. ‌static const char  *ngx_stream_geoip_path_city[] = {
  246.     "city", "names", "en", NULL
  247. };

  248. ‌static const char  *ngx_stream_geoip_path_postal_code[] = {
  249.     "postal", "code", NULL
  250. };

  251. ‌static const char  *ngx_stream_geoip_path_latitude[] = {
  252.     "location", "latitude", NULL
  253. };

  254. ‌static const char  *ngx_stream_geoip_path_longitude[] = {
  255.     "location", "longitude", NULL
  256. };

  257. ‌static const char  *ngx_stream_geoip_path_dma_code[] = {
  258.     "location", "metro_code", NULL
  259. };


  260. /* indexed by NGX_GEOIP_COUNTRY_* */

  261. ‌static const char  **ngx_stream_geoip_country_paths[] = {
  262.     ngx_stream_geoip_path_country_code,
  263.     ngx_stream_geoip_path_country_code,    /* converted to alpha-3 */
  264.     ngx_stream_geoip_path_country_name
  265. };


  266. /* indexed by NGX_GEOIP_CITY_* */

  267. ‌static const char  **ngx_stream_geoip_city_paths[] = {
  268.     ngx_stream_geoip_path_continent_code,
  269.     ngx_stream_geoip_path_country_code,
  270.     ngx_stream_geoip_path_country_code,    /* converted to alpha-3 */
  271.     ngx_stream_geoip_path_country_name,
  272.     ngx_stream_geoip_path_region,
  273.     ngx_stream_geoip_path_region_name,
  274.     ngx_stream_geoip_path_city,
  275.     ngx_stream_geoip_path_postal_code,
  276.     ngx_stream_geoip_path_latitude,
  277.     ngx_stream_geoip_path_longitude,
  278.     ngx_stream_geoip_path_dma_code,
  279.     NULL                                   /* no area code */
  280. };


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

  282. ‌static const char  *ngx_stream_geoip_org_paths[][2] = {
  283.     { "autonomous_system_organization", NULL },
  284.     { "isp", NULL },
  285.     { "organization", NULL },
  286.     { "domain", NULL },
  287.     { NULL, NULL }
  288. };


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

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

  356. #endif


  357. #if (NGX_HAVE_GEOIP)

  358. static u_long
  359. ‌ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)
  360. {
  361.     ngx_addr_t           addr;
  362.     struct sockaddr_in  *sin;

  363.     addr.sockaddr = s->connection->sockaddr;
  364.     addr.socklen = s->connection->socklen;
  365.     /* addr.name = s->connection->addr_text; */

  366. #if (NGX_HAVE_INET6)

  367.     if (addr.sockaddr->sa_family == AF_INET6) {
  368.         u_char           *p;
  369.         in_addr_t         inaddr;
  370.         struct in6_addr  *inaddr6;

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

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

  374.             inaddr = (in_addr_t) p[12] << 24;
  375.             inaddr += p[13] << 16;
  376.             inaddr += p[14] << 8;
  377.             inaddr += p[15];

  378.             return inaddr;
  379.         }
  380.     }

  381. #endif

  382.     if (addr.sockaddr->sa_family != AF_INET) {
  383.         return INADDR_NONE;
  384.     }

  385.     sin = (struct sockaddr_in *) addr.sockaddr;
  386.     return ntohl(sin->sin_addr.s_addr);
  387. }


  388. #if (NGX_HAVE_GEOIP_V6)

  389. static geoipv6_t
  390. ‌ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)
  391. {
  392.     ngx_addr_t            addr;
  393.     in_addr_t             addr4;
  394.     struct in6_addr       addr6;
  395.     struct sockaddr_in   *sin;
  396.     struct sockaddr_in6  *sin6;

  397.     addr.sockaddr = s->connection->sockaddr;
  398.     addr.socklen = s->connection->socklen;
  399.     /* addr.name = s->connection->addr_text; */

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

  401.     case AF_INET:
  402.         /* Produce IPv4-mapped IPv6 address. */
  403.         sin = (struct sockaddr_in *) addr.sockaddr;
  404.         addr4 = ntohl(sin->sin_addr.s_addr);

  405.         ngx_memzero(&addr6, sizeof(struct in6_addr));
  406.         addr6.s6_addr[10] = 0xff;
  407.         addr6.s6_addr[11] = 0xff;
  408.         addr6.s6_addr[12] = addr4 >> 24;
  409.         addr6.s6_addr[13] = addr4 >> 16;
  410.         addr6.s6_addr[14] = addr4 >> 8;
  411.         addr6.s6_addr[15] = addr4;
  412.         return addr6;

  413.     case AF_INET6:
  414.         sin6 = (struct sockaddr_in6 *) addr.sockaddr;
  415.         return sin6->sin6_addr;

  416.     default:
  417.         return in6addr_any;
  418.     }
  419. }

  420. #endif

  421. #endif


  422. static ngx_int_t
  423. ‌ngx_stream_geoip_country_variable(ngx_stream_session_t *s,
  424.     ngx_stream_variable_value_t *v, uintptr_t data)
  425. {
  426. #if (NGX_HAVE_GEOIP)
  427.     ngx_stream_geoip_variable_handler_pt     handler =
  428.         ngx_stream_geoip_country_functions[data];
  429. #if (NGX_HAVE_GEOIP_V6)
  430.     ngx_stream_geoip_variable_handler_v6_pt  handler_v6 =
  431.         ngx_stream_geoip_country_v6_functions[data];
  432. #endif

  433.     const char               *val;
  434. #endif
  435. #if (NGX_HAVE_MAXMINDDB)
  436.     ngx_int_t                 rc;
  437.     ngx_str_t                 value;
  438.     MMDB_entry_s              entry;
  439. #endif
  440.     ngx_stream_geoip_conf_t  *gcf;

  441.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  442. #if (NGX_HAVE_MAXMINDDB)

  443.     if (gcf->country_mmdb) {

  444.         if (ngx_stream_geoip_lookup(s, gcf->country_mmdb, &entry) != NGX_OK) {
  445.             goto not_found;
  446.         }

  447.         rc = ngx_stream_geoip_str(s, &entry,
  448.                                   ngx_stream_geoip_country_paths[data], &value);

  449.         if (rc == NGX_OK && data == NGX_GEOIP_COUNTRY_CODE3) {
  450.             rc = ngx_stream_geoip_code3(&value);
  451.         }

  452.         if (rc == NGX_ERROR) {
  453.             return NGX_ERROR;
  454.         }

  455.         if (rc != NGX_OK) {
  456.             goto not_found;
  457.         }

  458.         v->len = value.len;
  459.         v->valid = 1;
  460.         v->no_cacheable = 0;
  461.         v->not_found = 0;
  462.         v->data = value.data;

  463.         return NGX_OK;
  464.     }

  465. #endif

  466. #if (NGX_HAVE_GEOIP)

  467.     if (gcf->country == NULL) {
  468.         goto not_found;
  469.     }

  470. #if (NGX_HAVE_GEOIP_V6)
  471.     val = gcf->country_v6
  472.               ? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf))
  473.               : handler(gcf->country, ngx_stream_geoip_addr(s, gcf));
  474. #else
  475.     val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf));
  476. #endif

  477.     if (val == NULL) {
  478.         goto not_found;
  479.     }

  480.     v->len = ngx_strlen(val);
  481.     v->valid = 1;
  482.     v->no_cacheable = 0;
  483.     v->not_found = 0;
  484.     v->data = (u_char *) val;

  485.     return NGX_OK;

  486. #endif

  487. not_found:

  488.     v->not_found = 1;

  489.     return NGX_OK;
  490. }


  491. static ngx_int_t
  492. ‌ngx_stream_geoip_org_variable(ngx_stream_session_t *s,
  493.     ngx_stream_variable_value_t *v, uintptr_t data)
  494. {
  495. #if (NGX_HAVE_GEOIP)
  496.     size_t                    len;
  497.     char                     *val;
  498. #endif
  499. #if (NGX_HAVE_MAXMINDDB)
  500.     ngx_int_t                 rc;
  501.     ngx_str_t                 value;
  502.     ngx_uint_t                i;
  503.     MMDB_entry_s              entry;
  504. #endif
  505.     ngx_stream_geoip_conf_t  *gcf;

  506.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  507. #if (NGX_HAVE_MAXMINDDB)

  508.     if (gcf->org_mmdb) {

  509.         if (ngx_stream_geoip_lookup(s, gcf->org_mmdb, &entry) != NGX_OK) {
  510.             goto not_found;
  511.         }

  512.         for (i = 0; ngx_stream_geoip_org_paths[i][0]; i++) {

  513.             rc = ngx_stream_geoip_str(s, &entry,
  514.                                       ngx_stream_geoip_org_paths[i], &value);

  515.             if (rc == NGX_ERROR) {
  516.                 return NGX_ERROR;
  517.             }

  518.             if (rc == NGX_OK) {
  519.                 v->len = value.len;
  520.                 v->valid = 1;
  521.                 v->no_cacheable = 0;
  522.                 v->not_found = 0;
  523.                 v->data = value.data;

  524.                 return NGX_OK;
  525.             }
  526.         }

  527.         goto not_found;
  528.     }

  529. #endif

  530. #if (NGX_HAVE_GEOIP)

  531.     if (gcf->org == NULL) {
  532.         goto not_found;
  533.     }

  534. #if (NGX_HAVE_GEOIP_V6)
  535.     val = gcf->org_v6
  536.               ? GeoIP_name_by_ipnum_v6(gcf->org,
  537.                                        ngx_stream_geoip_addr_v6(s, gcf))
  538.               : GeoIP_name_by_ipnum(gcf->org,
  539.                                     ngx_stream_geoip_addr(s, gcf));
  540. #else
  541.     val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf));
  542. #endif

  543.     if (val == NULL) {
  544.         goto not_found;
  545.     }

  546.     len = ngx_strlen(val);
  547.     v->data = ngx_pnalloc(s->connection->pool, len);
  548.     if (v->data == NULL) {
  549.         ngx_free(val);
  550.         return NGX_ERROR;
  551.     }

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

  553.     v->len = len;
  554.     v->valid = 1;
  555.     v->no_cacheable = 0;
  556.     v->not_found = 0;

  557.     ngx_free(val);

  558.     return NGX_OK;

  559. #endif

  560. not_found:

  561.     v->not_found = 1;

  562.     return NGX_OK;
  563. }


  564. static ngx_int_t
  565. ‌ngx_stream_geoip_city_variable(ngx_stream_session_t *s,
  566.     ngx_stream_variable_value_t *v, uintptr_t data)
  567. {
  568. #if (NGX_HAVE_GEOIP)
  569.     char                     *val;
  570.     size_t                    len;
  571.     GeoIPRecord              *gr;
  572. #endif
  573. #if (NGX_HAVE_MAXMINDDB)
  574.     ngx_int_t                 rc;
  575.     ngx_str_t                 value;
  576.     MMDB_entry_s              entry;
  577.     ngx_stream_geoip_conf_t  *gcf;

  578.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  579.     if (gcf->city_mmdb) {

  580.         if (ngx_stream_geoip_lookup(s, gcf->city_mmdb, &entry) != NGX_OK) {
  581.             goto not_found;
  582.         }

  583.         rc = ngx_stream_geoip_str(s, &entry,
  584.                                   ngx_stream_geoip_city_paths[data], &value);

  585.         if (rc == NGX_OK && data == NGX_GEOIP_CITY_COUNTRY_CODE3) {
  586.             rc = ngx_stream_geoip_code3(&value);
  587.         }

  588.         if (rc == NGX_ERROR) {
  589.             return NGX_ERROR;
  590.         }

  591.         if (rc != NGX_OK) {
  592.             goto not_found;
  593.         }

  594.         v->len = value.len;
  595.         v->valid = 1;
  596.         v->no_cacheable = 0;
  597.         v->not_found = 0;
  598.         v->data = value.data;

  599.         return NGX_OK;
  600.     }

  601. #endif

  602. #if (NGX_HAVE_GEOIP)

  603.     gr = ngx_stream_geoip_get_city_record(s);
  604.     if (gr == NULL) {
  605.         goto not_found;
  606.     }

  607.     val = *(char **) ((char *) gr + ngx_stream_geoip_city_offsets[data]);
  608.     if (val == NULL) {
  609.         goto no_value;
  610.     }

  611.     len = ngx_strlen(val);
  612.     v->data = ngx_pnalloc(s->connection->pool, len);
  613.     if (v->data == NULL) {
  614.         GeoIPRecord_delete(gr);
  615.         return NGX_ERROR;
  616.     }

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

  618.     v->len = len;
  619.     v->valid = 1;
  620.     v->no_cacheable = 0;
  621.     v->not_found = 0;

  622.     GeoIPRecord_delete(gr);

  623.     return NGX_OK;

  624. no_value:

  625.     GeoIPRecord_delete(gr);

  626. #endif

  627. not_found:

  628.     v->not_found = 1;

  629.     return NGX_OK;
  630. }


  631. static ngx_int_t
  632. ‌ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,
  633.     ngx_stream_variable_value_t *v, uintptr_t data)
  634. {
  635. #if (NGX_HAVE_GEOIP)
  636.     size_t                    len;
  637.     const char               *val;
  638.     GeoIPRecord              *gr;
  639. #endif
  640. #if (NGX_HAVE_MAXMINDDB)
  641.     ngx_int_t                 rc;
  642.     ngx_str_t                 value;
  643.     MMDB_entry_s              entry;
  644.     ngx_stream_geoip_conf_t  *gcf;

  645.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  646.     if (gcf->city_mmdb) {

  647.         if (ngx_stream_geoip_lookup(s, gcf->city_mmdb, &entry) != NGX_OK) {
  648.             goto not_found;
  649.         }

  650.         rc = ngx_stream_geoip_str(s, &entry,
  651.                                   ngx_stream_geoip_city_paths[data], &value);

  652.         if (rc == NGX_ERROR) {
  653.             return NGX_ERROR;
  654.         }

  655.         if (rc != NGX_OK) {
  656.             goto not_found;
  657.         }

  658.         v->len = value.len;
  659.         v->valid = 1;
  660.         v->no_cacheable = 0;
  661.         v->not_found = 0;
  662.         v->data = value.data;

  663.         return NGX_OK;
  664.     }

  665. #endif

  666. #if (NGX_HAVE_GEOIP)

  667.     gr = ngx_stream_geoip_get_city_record(s);
  668.     if (gr == NULL) {
  669.         goto not_found;
  670.     }

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

  672.     GeoIPRecord_delete(gr);

  673.     if (val == NULL) {
  674.         goto not_found;
  675.     }

  676.     len = ngx_strlen(val);
  677.     v->data = ngx_pnalloc(s->connection->pool, len);
  678.     if (v->data == NULL) {
  679.         return NGX_ERROR;
  680.     }

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

  682.     v->len = len;
  683.     v->valid = 1;
  684.     v->no_cacheable = 0;
  685.     v->not_found = 0;

  686.     return NGX_OK;

  687. #endif

  688. not_found:

  689.     v->not_found = 1;

  690.     return NGX_OK;
  691. }


  692. static ngx_int_t
  693. ‌ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,
  694.     ngx_stream_variable_value_t *v, uintptr_t data)
  695. {
  696. #if (NGX_HAVE_GEOIP)
  697.     float                     val;
  698.     GeoIPRecord              *gr;
  699. #endif
  700. #if (NGX_HAVE_MAXMINDDB)
  701.     double                    value;
  702.     ngx_int_t                 rc;
  703.     MMDB_entry_s              entry;
  704.     ngx_stream_geoip_conf_t  *gcf;

  705.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  706.     if (gcf->city_mmdb) {

  707.         if (ngx_stream_geoip_lookup(s, gcf->city_mmdb, &entry) != NGX_OK) {
  708.             goto not_found;
  709.         }

  710.         rc = ngx_stream_geoip_float(&entry,
  711.                                     ngx_stream_geoip_city_paths[data], &value);

  712.         if (rc != NGX_OK) {
  713.             goto not_found;
  714.         }

  715.         v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5);
  716.         if (v->data == NULL) {
  717.             return NGX_ERROR;
  718.         }

  719.         v->len = ngx_sprintf(v->data, "%.4f", value) - v->data;
  720.         v->valid = 1;
  721.         v->no_cacheable = 0;
  722.         v->not_found = 0;

  723.         return NGX_OK;
  724.     }

  725. #endif

  726. #if (NGX_HAVE_GEOIP)

  727.     gr = ngx_stream_geoip_get_city_record(s);
  728.     if (gr == NULL) {
  729.         goto not_found;
  730.     }

  731.     v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5);
  732.     if (v->data == NULL) {
  733.         GeoIPRecord_delete(gr);
  734.         return NGX_ERROR;
  735.     }

  736.     val = *(float *) ((char *) gr + ngx_stream_geoip_city_offsets[data]);

  737.     v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
  738.     v->valid = 1;
  739.     v->no_cacheable = 0;
  740.     v->not_found = 0;

  741.     GeoIPRecord_delete(gr);

  742.     return NGX_OK;

  743. #endif

  744. not_found:

  745.     v->not_found = 1;

  746.     return NGX_OK;
  747. }


  748. static ngx_int_t
  749. ‌ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
  750.     ngx_stream_variable_value_t *v, uintptr_t data)
  751. {
  752. #if (NGX_HAVE_GEOIP)
  753.     int                       val;
  754.     GeoIPRecord              *gr;
  755. #endif
  756. #if (NGX_HAVE_MAXMINDDB)
  757.     int64_t                   value;
  758.     ngx_int_t                 rc;
  759.     MMDB_entry_s              entry;
  760.     ngx_stream_geoip_conf_t  *gcf;

  761.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  762.     if (gcf->city_mmdb) {

  763.         if (ngx_stream_geoip_lookup(s, gcf->city_mmdb, &entry) != NGX_OK) {
  764.             goto not_found;
  765.         }

  766.         rc = ngx_stream_geoip_int(&entry,
  767.                                   ngx_stream_geoip_city_paths[data], &value);

  768.         if (rc != NGX_OK) {
  769.             goto not_found;
  770.         }

  771.         v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);
  772.         if (v->data == NULL) {
  773.             return NGX_ERROR;
  774.         }

  775.         v->len = ngx_sprintf(v->data, "%L", value) - v->data;
  776.         v->valid = 1;
  777.         v->no_cacheable = 0;
  778.         v->not_found = 0;

  779.         return NGX_OK;
  780.     }

  781. #endif

  782. #if (NGX_HAVE_GEOIP)

  783.     gr = ngx_stream_geoip_get_city_record(s);
  784.     if (gr == NULL) {
  785.         goto not_found;
  786.     }

  787.     v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);
  788.     if (v->data == NULL) {
  789.         GeoIPRecord_delete(gr);
  790.         return NGX_ERROR;
  791.     }

  792.     val = *(int *) ((char *) gr + ngx_stream_geoip_city_offsets[data]);

  793.     v->len = ngx_sprintf(v->data, "%d", val) - v->data;
  794.     v->valid = 1;
  795.     v->no_cacheable = 0;
  796.     v->not_found = 0;

  797.     GeoIPRecord_delete(gr);

  798.     return NGX_OK;

  799. #endif

  800. not_found:

  801.     v->not_found = 1;

  802.     return NGX_OK;
  803. }


  804. #if (NGX_HAVE_GEOIP)

  805. static GeoIPRecord *
  806. ‌ngx_stream_geoip_get_city_record(ngx_stream_session_t *s)
  807. {
  808.     ngx_stream_geoip_conf_t  *gcf;

  809.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  810.     if (gcf->city) {
  811. #if (NGX_HAVE_GEOIP_V6)
  812.         return gcf->city_v6
  813.                    ? GeoIP_record_by_ipnum_v6(gcf->city,
  814.                                               ngx_stream_geoip_addr_v6(s, gcf))
  815.                    : GeoIP_record_by_ipnum(gcf->city,
  816.                                            ngx_stream_geoip_addr(s, gcf));
  817. #else
  818.         return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf));
  819. #endif
  820.     }

  821.     return NULL;
  822. }

  823. #endif


  824. #if (NGX_HAVE_MAXMINDDB)

  825. static ngx_int_t
  826. ‌ngx_stream_geoip_str(ngx_stream_session_t *s, MMDB_entry_s *entry,
  827.     const char **path, ngx_str_t *value)
  828. {
  829.     MMDB_entry_data_s  entry_data;

  830.     if (path == NULL
  831.         || MMDB_aget_value(entry, &entry_data, path) != MMDB_SUCCESS
  832.         || !entry_data.has_data
  833.         || entry_data.type != MMDB_DATA_TYPE_UTF8_STRING)
  834.     {
  835.         return NGX_DECLINED;
  836.     }

  837.     value->data = ngx_pnalloc(s->connection->pool, entry_data.data_size);
  838.     if (value->data == NULL) {
  839.         return NGX_ERROR;
  840.     }

  841.     ngx_memcpy(value->data, entry_data.utf8_string, entry_data.data_size);
  842.     value->len = entry_data.data_size;

  843.     return NGX_OK;
  844. }


  845. static ngx_int_t
  846. ‌ngx_stream_geoip_code3(ngx_str_t *value)
  847. {
  848.     ngx_stream_geoip_ccode_t  *cc;

  849.     if (value->len != 2) {
  850.         return NGX_DECLINED;
  851.     }

  852.     for (cc = ngx_stream_geoip_country_codes; cc->alpha2; cc++) {
  853.         if (value->data[0] == cc->alpha2[0]
  854.             && value->data[1] == cc->alpha2[1])
  855.         {
  856.             value->len = 3;
  857.             value->data = (u_char *) cc->alpha3;
  858.             return NGX_OK;
  859.         }
  860.     }

  861.     return NGX_DECLINED;
  862. }


  863. static ngx_int_t
  864. ‌ngx_stream_geoip_float(MMDB_entry_s *entry, const char **path, double *value)
  865. {
  866.     MMDB_entry_data_s  entry_data;

  867.     if (path == NULL
  868.         || MMDB_aget_value(entry, &entry_data, path) != MMDB_SUCCESS
  869.         || !entry_data.has_data)
  870.     {
  871.         return NGX_DECLINED;
  872.     }

  873.     switch (entry_data.type) {

  874.     case MMDB_DATA_TYPE_DOUBLE:
  875.         *value = entry_data.double_value;
  876.         break;

  877.     case MMDB_DATA_TYPE_FLOAT:
  878.         *value = entry_data.float_value;
  879.         break;

  880.     default:
  881.         return NGX_DECLINED;
  882.     }

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

  884.     if (!(*value >= -NGX_GEOIP_MAX_COORD && *value <= NGX_GEOIP_MAX_COORD)) {
  885.         return NGX_DECLINED;
  886.     }

  887.     return NGX_OK;
  888. }


  889. static ngx_int_t
  890. ‌ngx_stream_geoip_int(MMDB_entry_s *entry, const char **path, int64_t *value)
  891. {
  892.     MMDB_entry_data_s  entry_data;

  893.     if (path == NULL
  894.         || MMDB_aget_value(entry, &entry_data, path) != MMDB_SUCCESS
  895.         || !entry_data.has_data)
  896.     {
  897.         return NGX_DECLINED;
  898.     }

  899.     switch (entry_data.type) {

  900.     case MMDB_DATA_TYPE_UINT16:
  901.         *value = entry_data.uint16;
  902.         return NGX_OK;

  903.     case MMDB_DATA_TYPE_UINT32:
  904.         *value = entry_data.uint32;
  905.         return NGX_OK;

  906.     case MMDB_DATA_TYPE_INT32:
  907.         *value = entry_data.int32;
  908.         return NGX_OK;

  909.     default:
  910.         return NGX_DECLINED;
  911.     }
  912. }


  913. static ngx_int_t
  914. ‌ngx_stream_geoip_lookup(ngx_stream_session_t *s, MMDB_s *mmdb,
  915.     MMDB_entry_s *entry)
  916. {
  917.     int                    err;
  918.     struct sockaddr       *sa;
  919.     MMDB_lookup_result_s   result;
  920. #if (NGX_HAVE_INET6)
  921.     struct in6_addr       *inaddr6;
  922.     struct sockaddr_in     sin;
  923. #endif

  924.     sa = s->connection->sockaddr;

  925.     switch (sa->sa_family) {

  926.     case AF_INET:
  927.         break;

  928. #if (NGX_HAVE_INET6)
  929.     case AF_INET6:
  930.         inaddr6 = &((struct sockaddr_in6 *) sa)->sin6_addr;

  931.         if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
  932.             ngx_memzero(&sin, sizeof(struct sockaddr_in));
  933.             sin.sin_family = AF_INET;
  934.             ngx_memcpy(&sin.sin_addr, &inaddr6->s6_addr[12], 4);

  935.             sa = (struct sockaddr *) &sin;
  936.             break;
  937.         }

  938.         if (mmdb->metadata.ip_version == 4) {
  939.             return NGX_DECLINED;
  940.         }

  941.         break;
  942. #endif

  943.     default:
  944.         return NGX_DECLINED;
  945.     }

  946.     result = MMDB_lookup_sockaddr(mmdb, sa, &err);

  947.     if (err != MMDB_SUCCESS) {
  948.         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
  949.                       "MMDB_lookup_sockaddr() failed: %s", MMDB_strerror(err));
  950.         return NGX_DECLINED;
  951.     }

  952.     if (!result.found_entry) {
  953.         return NGX_DECLINED;
  954.     }

  955.     *entry = result.entry;

  956.     return NGX_OK;
  957. }

  958. #endif


  959. static ngx_int_t
  960. ‌ngx_stream_geoip_add_variables(ngx_conf_t *cf)
  961. {
  962.     ngx_stream_variable_t  *var, *v;

  963.     for (v = ngx_stream_geoip_vars; v->name.len; v++) {
  964.         var = ngx_stream_add_variable(cf, &v->name, v->flags);
  965.         if (var == NULL) {
  966.             return NGX_ERROR;
  967.         }

  968.         var->get_handler = v->get_handler;
  969.         var->data = v->data;
  970.     }

  971.     return NGX_OK;
  972. }


  973. static void *
  974. ‌ngx_stream_geoip_create_conf(ngx_conf_t *cf)
  975. {
  976.     ngx_pool_cleanup_t       *cln;
  977.     ngx_stream_geoip_conf_t  *conf;

  978.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip_conf_t));
  979.     if (conf == NULL) {
  980.         return NULL;
  981.     }

  982.     cln = ngx_pool_cleanup_add(cf->pool, 0);
  983.     if (cln == NULL) {
  984.         return NULL;
  985.     }

  986.     cln->handler = ngx_stream_geoip_cleanup;
  987.     cln->data = conf;

  988.     return conf;
  989. }


  990. static char *
  991. ‌ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  992. {
  993.     ngx_stream_geoip_conf_t  *gcf = conf;

  994.     ngx_str_t  *value;

  995. #if (NGX_HAVE_GEOIP)
  996.     if (gcf->country) {
  997.         return "is duplicate";
  998.     }
  999. #endif
  1000. #if (NGX_HAVE_MAXMINDDB)
  1001.     if (gcf->country_mmdb) {
  1002.         return "is duplicate";
  1003.     }
  1004. #endif

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

  1006. #if (NGX_HAVE_MAXMINDDB)

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

  1008.     case NGX_OK:
  1009.         return ngx_stream_geoip_charset(cf);

  1010.     case NGX_ERROR:
  1011.         return NGX_CONF_ERROR;

  1012.     default:
  1013.         break;
  1014.     }

  1015. #endif

  1016. #if (NGX_HAVE_GEOIP)

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

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

  1021.         return NGX_CONF_ERROR;
  1022.     }

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

  1026.         } else {
  1027.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1028.                                "invalid parameter \"%V\"", &value[2]);
  1029.             return NGX_CONF_ERROR;
  1030.         }
  1031.     }

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

  1033.     case GEOIP_COUNTRY_EDITION:

  1034.         return NGX_CONF_OK;

  1035. #if (NGX_HAVE_GEOIP_V6)
  1036.     case GEOIP_COUNTRY_EDITION_V6:

  1037.         gcf->country_v6 = 1;
  1038.         return NGX_CONF_OK;
  1039. #endif

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

  1046. #else

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

  1049.     return NGX_CONF_ERROR;

  1050. #endif
  1051. }


  1052. static char *
  1053. ‌ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1054. {
  1055.     ngx_stream_geoip_conf_t  *gcf = conf;

  1056.     ngx_str_t  *value;

  1057. #if (NGX_HAVE_GEOIP)
  1058.     if (gcf->org) {
  1059.         return "is duplicate";
  1060.     }
  1061. #endif
  1062. #if (NGX_HAVE_MAXMINDDB)
  1063.     if (gcf->org_mmdb) {
  1064.         return "is duplicate";
  1065.     }
  1066. #endif

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

  1068. #if (NGX_HAVE_MAXMINDDB)

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

  1070.     case NGX_OK:
  1071.         return ngx_stream_geoip_charset(cf);

  1072.     case NGX_ERROR:
  1073.         return NGX_CONF_ERROR;

  1074.     default:
  1075.         break;
  1076.     }

  1077. #endif

  1078. #if (NGX_HAVE_GEOIP)

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

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

  1083.         return NGX_CONF_ERROR;
  1084.     }

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

  1088.         } else {
  1089.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1090.                                "invalid parameter \"%V\"", &value[2]);
  1091.             return NGX_CONF_ERROR;
  1092.         }
  1093.     }

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

  1095.     case GEOIP_ISP_EDITION:
  1096.     case GEOIP_ORG_EDITION:
  1097.     case GEOIP_DOMAIN_EDITION:
  1098.     case GEOIP_ASNUM_EDITION:

  1099.         return NGX_CONF_OK;

  1100. #if (NGX_HAVE_GEOIP_V6)
  1101.     case GEOIP_ISP_EDITION_V6:
  1102.     case GEOIP_ORG_EDITION_V6:
  1103.     case GEOIP_DOMAIN_EDITION_V6:
  1104.     case GEOIP_ASNUM_EDITION_V6:

  1105.         gcf->org_v6 = 1;
  1106.         return NGX_CONF_OK;
  1107. #endif

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

  1114. #else

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

  1117.     return NGX_CONF_ERROR;

  1118. #endif
  1119. }


  1120. static char *
  1121. ‌ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1122. {
  1123.     ngx_stream_geoip_conf_t  *gcf = conf;

  1124.     ngx_str_t  *value;

  1125. #if (NGX_HAVE_GEOIP)
  1126.     if (gcf->city) {
  1127.         return "is duplicate";
  1128.     }
  1129. #endif
  1130. #if (NGX_HAVE_MAXMINDDB)
  1131.     if (gcf->city_mmdb) {
  1132.         return "is duplicate";
  1133.     }
  1134. #endif

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

  1136. #if (NGX_HAVE_MAXMINDDB)

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

  1138.     case NGX_OK:
  1139.         return ngx_stream_geoip_charset(cf);

  1140.     case NGX_ERROR:
  1141.         return NGX_CONF_ERROR;

  1142.     default:
  1143.         break;
  1144.     }

  1145. #endif

  1146. #if (NGX_HAVE_GEOIP)

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

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

  1151.         return NGX_CONF_ERROR;
  1152.     }

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

  1156.         } else {
  1157.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1158.                                "invalid parameter \"%V\"", &value[2]);
  1159.             return NGX_CONF_ERROR;
  1160.         }
  1161.     }

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

  1163.     case GEOIP_CITY_EDITION_REV0:
  1164.     case GEOIP_CITY_EDITION_REV1:

  1165.         return NGX_CONF_OK;

  1166. #if (NGX_HAVE_GEOIP_V6)
  1167.     case GEOIP_CITY_EDITION_REV0_V6:
  1168.     case GEOIP_CITY_EDITION_REV1_V6:

  1169.         gcf->city_v6 = 1;
  1170.         return NGX_CONF_OK;
  1171. #endif

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

  1178. #else

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

  1181.     return NGX_CONF_ERROR;

  1182. #endif
  1183. }


  1184. #if (NGX_HAVE_MAXMINDDB)

  1185. static ngx_int_t
  1186. ‌ngx_stream_geoip_open(ngx_conf_t *cf, ngx_str_t *name, MMDB_s **db)
  1187. {
  1188.     int               status;
  1189.     MMDB_s           *mmdb;
  1190.     ngx_file_info_t   fi;

  1191.     if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
  1192.         ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
  1193.                            ngx_file_info_n " \"%V\" failed", name);
  1194.         return NGX_ERROR;
  1195.     }

  1196.     mmdb = ngx_palloc(cf->pool, sizeof(MMDB_s));
  1197.     if (mmdb == NULL) {
  1198.         return NGX_ERROR;
  1199.     }

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

  1201.     if (status == MMDB_SUCCESS) {
  1202.         *db = mmdb;
  1203.         return NGX_OK;
  1204.     }

  1205.     /* presumably a legacy GeoIP database */

  1206.     if (status == MMDB_INVALID_METADATA_ERROR) {
  1207.         return NGX_DECLINED;
  1208.     }

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

  1211.     return NGX_ERROR;
  1212. }


  1213. static char *
  1214. ‌ngx_stream_geoip_charset(ngx_conf_t *cf)
  1215. {
  1216.     ngx_str_t  *value;

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

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

  1219.     if (cf->args->nelts == 3 && ngx_strcmp(value[2].data, "utf8") != 0) {
  1220.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1221.                            "invalid parameter \"%V\"", &value[2]);
  1222.         return NGX_CONF_ERROR;
  1223.     }

  1224.     return NGX_CONF_OK;
  1225. }

  1226. #endif


  1227. static void
  1228. ‌ngx_stream_geoip_cleanup(void *data)
  1229. {
  1230.     ngx_stream_geoip_conf_t  *gcf = data;

  1231. #if (NGX_HAVE_GEOIP)

  1232.     if (gcf->country) {
  1233.         GeoIP_delete(gcf->country);
  1234.     }

  1235.     if (gcf->org) {
  1236.         GeoIP_delete(gcf->org);
  1237.     }

  1238.     if (gcf->city) {
  1239.         GeoIP_delete(gcf->city);
  1240.     }

  1241. #endif

  1242. #if (NGX_HAVE_MAXMINDDB)

  1243.     if (gcf->country_mmdb) {
  1244.         MMDB_close(gcf->country_mmdb);
  1245.     }

  1246.     if (gcf->org_mmdb) {
  1247.         MMDB_close(gcf->org_mmdb);
  1248.     }

  1249.     if (gcf->city_mmdb) {
  1250.         MMDB_close(gcf->city_mmdb);
  1251.     }

  1252. #endif
  1253. }