src/stream/ngx_stream_geoip_module.c - nginx source code

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. #include <GeoIP.h>
  9. #include <GeoIPCity.h>


  10. #define NGX_GEOIP_COUNTRY_CODE   0
  11. #define NGX_GEOIP_COUNTRY_CODE3  1
  12. #define NGX_GEOIP_COUNTRY_NAME   2


  13. typedef struct {
  14.     GeoIP        *country;
  15.     GeoIP        *org;
  16.     GeoIP        *city;
  17. #if (NGX_HAVE_GEOIP_V6)
  18.     unsigned      country_v6:1;
  19.     unsigned      org_v6:1;
  20.     unsigned      city_v6:1;
  21. #endif
  22. } ngx_stream_geoip_conf_t;


  23. typedef struct {
  24.     ngx_str_t    *name;
  25.     uintptr_t     data;
  26. } ngx_stream_geoip_var_t;


  27. typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *,
  28.     u_long addr);


  29. ngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = {
  30.     GeoIP_country_code_by_ipnum,
  31.     GeoIP_country_code3_by_ipnum,
  32.     GeoIP_country_name_by_ipnum,
  33. };


  34. #if (NGX_HAVE_GEOIP_V6)

  35. typedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *,
  36.     geoipv6_t addr);


  37. ngx_stream_geoip_variable_handler_v6_pt
  38.     ngx_stream_geoip_country_v6_functions[] =
  39. {
  40.     GeoIP_country_code_by_ipnum_v6,
  41.     GeoIP_country_code3_by_ipnum_v6,
  42.     GeoIP_country_name_by_ipnum_v6,
  43. };

  44. #endif


  45. static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s,
  46.     ngx_stream_variable_value_t *v, uintptr_t data);
  47. static ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s,
  48.     ngx_stream_variable_value_t *v, uintptr_t data);
  49. static ngx_int_t ngx_stream_geoip_city_variable(ngx_stream_session_t *s,
  50.     ngx_stream_variable_value_t *v, uintptr_t data);
  51. static ngx_int_t ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,
  52.     ngx_stream_variable_value_t *v, uintptr_t data);
  53. static ngx_int_t ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,
  54.     ngx_stream_variable_value_t *v, uintptr_t data);
  55. static ngx_int_t ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
  56.     ngx_stream_variable_value_t *v, uintptr_t data);
  57. static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s);

  58. static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf);
  59. static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf);
  60. static char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
  61.     void *conf);
  62. static char *ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
  63.     void *conf);
  64. static char *ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
  65.     void *conf);
  66. static void ngx_stream_geoip_cleanup(void *data);


  67. static ngx_command_t  ngx_stream_geoip_commands[] = {

  68.     { ngx_string("geoip_country"),
  69.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
  70.       ngx_stream_geoip_country,
  71.       NGX_STREAM_MAIN_CONF_OFFSET,
  72.       0,
  73.       NULL },

  74.     { ngx_string("geoip_org"),
  75.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
  76.       ngx_stream_geoip_org,
  77.       NGX_STREAM_MAIN_CONF_OFFSET,
  78.       0,
  79.       NULL },

  80.     { ngx_string("geoip_city"),
  81.       NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
  82.       ngx_stream_geoip_city,
  83.       NGX_STREAM_MAIN_CONF_OFFSET,
  84.       0,
  85.       NULL },

  86.       ngx_null_command
  87. };


  88. static ngx_stream_module_t  ngx_stream_geoip_module_ctx = {
  89.     ngx_stream_geoip_add_variables,        /* preconfiguration */
  90.     NULL,                                  /* postconfiguration */

  91.     ngx_stream_geoip_create_conf,          /* create main configuration */
  92.     NULL,                                  /* init main configuration */

  93.     NULL,                                  /* create server configuration */
  94.     NULL                                   /* merge server configuration */
  95. };


  96. ngx_module_t  ngx_stream_geoip_module = {
  97.     NGX_MODULE_V1,
  98.     &ngx_stream_geoip_module_ctx,          /* module context */
  99.     ngx_stream_geoip_commands,             /* module directives */
  100.     NGX_STREAM_MODULE,                     /* module type */
  101.     NULL,                                  /* init master */
  102.     NULL,                                  /* init module */
  103.     NULL,                                  /* init process */
  104.     NULL,                                  /* init thread */
  105.     NULL,                                  /* exit thread */
  106.     NULL,                                  /* exit process */
  107.     NULL,                                  /* exit master */
  108.     NGX_MODULE_V1_PADDING
  109. };


  110. static ngx_stream_variable_t  ngx_stream_geoip_vars[] = {

  111.     { ngx_string("geoip_country_code"), NULL,
  112.       ngx_stream_geoip_country_variable,
  113.       NGX_GEOIP_COUNTRY_CODE, 0, 0 },

  114.     { ngx_string("geoip_country_code3"), NULL,
  115.       ngx_stream_geoip_country_variable,
  116.       NGX_GEOIP_COUNTRY_CODE3, 0, 0 },

  117.     { ngx_string("geoip_country_name"), NULL,
  118.       ngx_stream_geoip_country_variable,
  119.       NGX_GEOIP_COUNTRY_NAME, 0, 0 },

  120.     { ngx_string("geoip_org"), NULL,
  121.       ngx_stream_geoip_org_variable,
  122.       0, 0, 0 },

  123.     { ngx_string("geoip_city_continent_code"), NULL,
  124.       ngx_stream_geoip_city_variable,
  125.       offsetof(GeoIPRecord, continent_code), 0, 0 },

  126.     { ngx_string("geoip_city_country_code"), NULL,
  127.       ngx_stream_geoip_city_variable,
  128.       offsetof(GeoIPRecord, country_code), 0, 0 },

  129.     { ngx_string("geoip_city_country_code3"), NULL,
  130.       ngx_stream_geoip_city_variable,
  131.       offsetof(GeoIPRecord, country_code3), 0, 0 },

  132.     { ngx_string("geoip_city_country_name"), NULL,
  133.       ngx_stream_geoip_city_variable,
  134.       offsetof(GeoIPRecord, country_name), 0, 0 },

  135.     { ngx_string("geoip_region"), NULL,
  136.       ngx_stream_geoip_city_variable,
  137.       offsetof(GeoIPRecord, region), 0, 0 },

  138.     { ngx_string("geoip_region_name"), NULL,
  139.       ngx_stream_geoip_region_name_variable,
  140.       0, 0, 0 },

  141.     { ngx_string("geoip_city"), NULL,
  142.       ngx_stream_geoip_city_variable,
  143.       offsetof(GeoIPRecord, city), 0, 0 },

  144.     { ngx_string("geoip_postal_code"), NULL,
  145.       ngx_stream_geoip_city_variable,
  146.       offsetof(GeoIPRecord, postal_code), 0, 0 },

  147.     { ngx_string("geoip_latitude"), NULL,
  148.       ngx_stream_geoip_city_float_variable,
  149.       offsetof(GeoIPRecord, latitude), 0, 0 },

  150.     { ngx_string("geoip_longitude"), NULL,
  151.       ngx_stream_geoip_city_float_variable,
  152.       offsetof(GeoIPRecord, longitude), 0, 0 },

  153.     { ngx_string("geoip_dma_code"), NULL,
  154.       ngx_stream_geoip_city_int_variable,
  155.       offsetof(GeoIPRecord, dma_code), 0, 0 },

  156.     { ngx_string("geoip_area_code"), NULL,
  157.       ngx_stream_geoip_city_int_variable,
  158.       offsetof(GeoIPRecord, area_code), 0, 0 },

  159.       ngx_stream_null_variable
  160. };


  161. static u_long
  162. ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)
  163. {
  164.     ngx_addr_t           addr;
  165.     struct sockaddr_in  *sin;

  166.     addr.sockaddr = s->connection->sockaddr;
  167.     addr.socklen = s->connection->socklen;
  168.     /* addr.name = s->connection->addr_text; */

  169. #if (NGX_HAVE_INET6)

  170.     if (addr.sockaddr->sa_family == AF_INET6) {
  171.         u_char           *p;
  172.         in_addr_t         inaddr;
  173.         struct in6_addr  *inaddr6;

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

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

  177.             inaddr = (in_addr_t) p[12] << 24;
  178.             inaddr += p[13] << 16;
  179.             inaddr += p[14] << 8;
  180.             inaddr += p[15];

  181.             return inaddr;
  182.         }
  183.     }

  184. #endif

  185.     if (addr.sockaddr->sa_family != AF_INET) {
  186.         return INADDR_NONE;
  187.     }

  188.     sin = (struct sockaddr_in *) addr.sockaddr;
  189.     return ntohl(sin->sin_addr.s_addr);
  190. }


  191. #if (NGX_HAVE_GEOIP_V6)

  192. static geoipv6_t
  193. ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)
  194. {
  195.     ngx_addr_t            addr;
  196.     in_addr_t             addr4;
  197.     struct in6_addr       addr6;
  198.     struct sockaddr_in   *sin;
  199.     struct sockaddr_in6  *sin6;

  200.     addr.sockaddr = s->connection->sockaddr;
  201.     addr.socklen = s->connection->socklen;
  202.     /* addr.name = s->connection->addr_text; */

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

  204.     case AF_INET:
  205.         /* Produce IPv4-mapped IPv6 address. */
  206.         sin = (struct sockaddr_in *) addr.sockaddr;
  207.         addr4 = ntohl(sin->sin_addr.s_addr);

  208.         ngx_memzero(&addr6, sizeof(struct in6_addr));
  209.         addr6.s6_addr[10] = 0xff;
  210.         addr6.s6_addr[11] = 0xff;
  211.         addr6.s6_addr[12] = addr4 >> 24;
  212.         addr6.s6_addr[13] = addr4 >> 16;
  213.         addr6.s6_addr[14] = addr4 >> 8;
  214.         addr6.s6_addr[15] = addr4;
  215.         return addr6;

  216.     case AF_INET6:
  217.         sin6 = (struct sockaddr_in6 *) addr.sockaddr;
  218.         return sin6->sin6_addr;

  219.     default:
  220.         return in6addr_any;
  221.     }
  222. }

  223. #endif


  224. static ngx_int_t
  225. ngx_stream_geoip_country_variable(ngx_stream_session_t *s,
  226.     ngx_stream_variable_value_t *v, uintptr_t data)
  227. {
  228.     ngx_stream_geoip_variable_handler_pt     handler =
  229.         ngx_stream_geoip_country_functions[data];
  230. #if (NGX_HAVE_GEOIP_V6)
  231.     ngx_stream_geoip_variable_handler_v6_pt  handler_v6 =
  232.         ngx_stream_geoip_country_v6_functions[data];
  233. #endif

  234.     const char               *val;
  235.     ngx_stream_geoip_conf_t  *gcf;

  236.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  237.     if (gcf->country == NULL) {
  238.         goto not_found;
  239.     }

  240. #if (NGX_HAVE_GEOIP_V6)
  241.     val = gcf->country_v6
  242.               ? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf))
  243.               : handler(gcf->country, ngx_stream_geoip_addr(s, gcf));
  244. #else
  245.     val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf));
  246. #endif

  247.     if (val == NULL) {
  248.         goto not_found;
  249.     }

  250.     v->len = ngx_strlen(val);
  251.     v->valid = 1;
  252.     v->no_cacheable = 0;
  253.     v->not_found = 0;
  254.     v->data = (u_char *) val;

  255.     return NGX_OK;

  256. not_found:

  257.     v->not_found = 1;

  258.     return NGX_OK;
  259. }


  260. static ngx_int_t
  261. ngx_stream_geoip_org_variable(ngx_stream_session_t *s,
  262.     ngx_stream_variable_value_t *v, uintptr_t data)
  263. {
  264.     size_t                    len;
  265.     char                     *val;
  266.     ngx_stream_geoip_conf_t  *gcf;

  267.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  268.     if (gcf->org == NULL) {
  269.         goto not_found;
  270.     }

  271. #if (NGX_HAVE_GEOIP_V6)
  272.     val = gcf->org_v6
  273.               ? GeoIP_name_by_ipnum_v6(gcf->org,
  274.                                        ngx_stream_geoip_addr_v6(s, gcf))
  275.               : GeoIP_name_by_ipnum(gcf->org,
  276.                                     ngx_stream_geoip_addr(s, gcf));
  277. #else
  278.     val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf));
  279. #endif

  280.     if (val == NULL) {
  281.         goto not_found;
  282.     }

  283.     len = ngx_strlen(val);
  284.     v->data = ngx_pnalloc(s->connection->pool, len);
  285.     if (v->data == NULL) {
  286.         ngx_free(val);
  287.         return NGX_ERROR;
  288.     }

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

  290.     v->len = len;
  291.     v->valid = 1;
  292.     v->no_cacheable = 0;
  293.     v->not_found = 0;

  294.     ngx_free(val);

  295.     return NGX_OK;

  296. not_found:

  297.     v->not_found = 1;

  298.     return NGX_OK;
  299. }


  300. static ngx_int_t
  301. ngx_stream_geoip_city_variable(ngx_stream_session_t *s,
  302.     ngx_stream_variable_value_t *v, uintptr_t data)
  303. {
  304.     char         *val;
  305.     size_t        len;
  306.     GeoIPRecord  *gr;

  307.     gr = ngx_stream_geoip_get_city_record(s);
  308.     if (gr == NULL) {
  309.         goto not_found;
  310.     }

  311.     val = *(char **) ((char *) gr + data);
  312.     if (val == NULL) {
  313.         goto no_value;
  314.     }

  315.     len = ngx_strlen(val);
  316.     v->data = ngx_pnalloc(s->connection->pool, len);
  317.     if (v->data == NULL) {
  318.         GeoIPRecord_delete(gr);
  319.         return NGX_ERROR;
  320.     }

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

  322.     v->len = len;
  323.     v->valid = 1;
  324.     v->no_cacheable = 0;
  325.     v->not_found = 0;

  326.     GeoIPRecord_delete(gr);

  327.     return NGX_OK;

  328. no_value:

  329.     GeoIPRecord_delete(gr);

  330. not_found:

  331.     v->not_found = 1;

  332.     return NGX_OK;
  333. }


  334. static ngx_int_t
  335. ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,
  336.     ngx_stream_variable_value_t *v, uintptr_t data)
  337. {
  338.     size_t        len;
  339.     const char   *val;
  340.     GeoIPRecord  *gr;

  341.     gr = ngx_stream_geoip_get_city_record(s);
  342.     if (gr == NULL) {
  343.         goto not_found;
  344.     }

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

  346.     GeoIPRecord_delete(gr);

  347.     if (val == NULL) {
  348.         goto not_found;
  349.     }

  350.     len = ngx_strlen(val);
  351.     v->data = ngx_pnalloc(s->connection->pool, len);
  352.     if (v->data == NULL) {
  353.         return NGX_ERROR;
  354.     }

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

  356.     v->len = len;
  357.     v->valid = 1;
  358.     v->no_cacheable = 0;
  359.     v->not_found = 0;

  360.     return NGX_OK;

  361. not_found:

  362.     v->not_found = 1;

  363.     return NGX_OK;
  364. }


  365. static ngx_int_t
  366. ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,
  367.     ngx_stream_variable_value_t *v, uintptr_t data)
  368. {
  369.     float         val;
  370.     GeoIPRecord  *gr;

  371.     gr = ngx_stream_geoip_get_city_record(s);
  372.     if (gr == NULL) {
  373.         v->not_found = 1;
  374.         return NGX_OK;
  375.     }

  376.     v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5);
  377.     if (v->data == NULL) {
  378.         GeoIPRecord_delete(gr);
  379.         return NGX_ERROR;
  380.     }

  381.     val = *(float *) ((char *) gr + data);

  382.     v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
  383.     v->valid = 1;
  384.     v->no_cacheable = 0;
  385.     v->not_found = 0;

  386.     GeoIPRecord_delete(gr);

  387.     return NGX_OK;
  388. }


  389. static ngx_int_t
  390. ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
  391.     ngx_stream_variable_value_t *v, uintptr_t data)
  392. {
  393.     int           val;
  394.     GeoIPRecord  *gr;

  395.     gr = ngx_stream_geoip_get_city_record(s);
  396.     if (gr == NULL) {
  397.         v->not_found = 1;
  398.         return NGX_OK;
  399.     }

  400.     v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);
  401.     if (v->data == NULL) {
  402.         GeoIPRecord_delete(gr);
  403.         return NGX_ERROR;
  404.     }

  405.     val = *(int *) ((char *) gr + data);

  406.     v->len = ngx_sprintf(v->data, "%d", val) - v->data;
  407.     v->valid = 1;
  408.     v->no_cacheable = 0;
  409.     v->not_found = 0;

  410.     GeoIPRecord_delete(gr);

  411.     return NGX_OK;
  412. }


  413. static GeoIPRecord *
  414. ngx_stream_geoip_get_city_record(ngx_stream_session_t *s)
  415. {
  416.     ngx_stream_geoip_conf_t  *gcf;

  417.     gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);

  418.     if (gcf->city) {
  419. #if (NGX_HAVE_GEOIP_V6)
  420.         return gcf->city_v6
  421.                    ? GeoIP_record_by_ipnum_v6(gcf->city,
  422.                                               ngx_stream_geoip_addr_v6(s, gcf))
  423.                    : GeoIP_record_by_ipnum(gcf->city,
  424.                                            ngx_stream_geoip_addr(s, gcf));
  425. #else
  426.         return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf));
  427. #endif
  428.     }

  429.     return NULL;
  430. }


  431. static ngx_int_t
  432. ngx_stream_geoip_add_variables(ngx_conf_t *cf)
  433. {
  434.     ngx_stream_variable_t  *var, *v;

  435.     for (v = ngx_stream_geoip_vars; v->name.len; v++) {
  436.         var = ngx_stream_add_variable(cf, &v->name, v->flags);
  437.         if (var == NULL) {
  438.             return NGX_ERROR;
  439.         }

  440.         var->get_handler = v->get_handler;
  441.         var->data = v->data;
  442.     }

  443.     return NGX_OK;
  444. }


  445. static void *
  446. ngx_stream_geoip_create_conf(ngx_conf_t *cf)
  447. {
  448.     ngx_pool_cleanup_t       *cln;
  449.     ngx_stream_geoip_conf_t  *conf;

  450.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip_conf_t));
  451.     if (conf == NULL) {
  452.         return NULL;
  453.     }

  454.     cln = ngx_pool_cleanup_add(cf->pool, 0);
  455.     if (cln == NULL) {
  456.         return NULL;
  457.     }

  458.     cln->handler = ngx_stream_geoip_cleanup;
  459.     cln->data = conf;

  460.     return conf;
  461. }


  462. static char *
  463. ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  464. {
  465.     ngx_stream_geoip_conf_t  *gcf = conf;

  466.     ngx_str_t  *value;

  467.     if (gcf->country) {
  468.         return "is duplicate";
  469.     }

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

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

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

  475.         return NGX_CONF_ERROR;
  476.     }

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

  480.         } else {
  481.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  482.                                "invalid parameter \"%V\"", &value[2]);
  483.             return NGX_CONF_ERROR;
  484.         }
  485.     }

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

  487.     case GEOIP_COUNTRY_EDITION:

  488.         return NGX_CONF_OK;

  489. #if (NGX_HAVE_GEOIP_V6)
  490.     case GEOIP_COUNTRY_EDITION_V6:

  491.         gcf->country_v6 = 1;
  492.         return NGX_CONF_OK;
  493. #endif

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


  501. static char *
  502. ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  503. {
  504.     ngx_stream_geoip_conf_t  *gcf = conf;

  505.     ngx_str_t  *value;

  506.     if (gcf->org) {
  507.         return "is duplicate";
  508.     }

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

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

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

  514.         return NGX_CONF_ERROR;
  515.     }

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

  519.         } else {
  520.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  521.                                "invalid parameter \"%V\"", &value[2]);
  522.             return NGX_CONF_ERROR;
  523.         }
  524.     }

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

  526.     case GEOIP_ISP_EDITION:
  527.     case GEOIP_ORG_EDITION:
  528.     case GEOIP_DOMAIN_EDITION:
  529.     case GEOIP_ASNUM_EDITION:

  530.         return NGX_CONF_OK;

  531. #if (NGX_HAVE_GEOIP_V6)
  532.     case GEOIP_ISP_EDITION_V6:
  533.     case GEOIP_ORG_EDITION_V6:
  534.     case GEOIP_DOMAIN_EDITION_V6:
  535.     case GEOIP_ASNUM_EDITION_V6:

  536.         gcf->org_v6 = 1;
  537.         return NGX_CONF_OK;
  538. #endif

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


  546. static char *
  547. ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  548. {
  549.     ngx_stream_geoip_conf_t  *gcf = conf;

  550.     ngx_str_t  *value;

  551.     if (gcf->city) {
  552.         return "is duplicate";
  553.     }

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

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

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

  559.         return NGX_CONF_ERROR;
  560.     }

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

  564.         } else {
  565.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  566.                                "invalid parameter \"%V\"", &value[2]);
  567.             return NGX_CONF_ERROR;
  568.         }
  569.     }

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

  571.     case GEOIP_CITY_EDITION_REV0:
  572.     case GEOIP_CITY_EDITION_REV1:

  573.         return NGX_CONF_OK;

  574. #if (NGX_HAVE_GEOIP_V6)
  575.     case GEOIP_CITY_EDITION_REV0_V6:
  576.     case GEOIP_CITY_EDITION_REV1_V6:

  577.         gcf->city_v6 = 1;
  578.         return NGX_CONF_OK;
  579. #endif

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


  587. static void
  588. ngx_stream_geoip_cleanup(void *data)
  589. {
  590.     ngx_stream_geoip_conf_t  *gcf = data;

  591.     if (gcf->country) {
  592.         GeoIP_delete(gcf->country);
  593.     }

  594.     if (gcf->org) {
  595.         GeoIP_delete(gcf->org);
  596.     }

  597.     if (gcf->city) {
  598.         GeoIP_delete(gcf->city);
  599.     }
  600. }