static u_long
ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
{
    ngx_addr_t           addr;
    ngx_array_t         *xfwd;
    struct sockaddr_in  *sin;

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

    xfwd = &r->headers_in.x_forwarded_for;

    if (xfwd->nelts > 0 && gcf->proxies != NULL) {
        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
                                           gcf->proxies, gcf->proxy_recursive);
    }

#if (NGX_HAVE_INET6)

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

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

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

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

            return inaddr;
        }
    }

#endif

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

    sin = (struct sockaddr_in *) addr.sockaddr;
    return ntohl(sin->sin_addr.s_addr);
}
static geoipv6_t
ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
{
    ngx_addr_t            addr;
    ngx_array_t          *xfwd;
    in_addr_t             addr4;
    struct in6_addr       addr6;
    struct sockaddr_in   *sin;
    struct sockaddr_in6  *sin6;

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

    xfwd = &r->headers_in.x_forwarded_for;

    if (xfwd->nelts > 0 && gcf->proxies != NULL) {
        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
                                           gcf->proxies, gcf->proxy_recursive);
    }

    switch (addr.sockaddr->sa_family) {

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

        ngx_memzero(&addr6, sizeof(struct in6_addr));
        addr6.s6_addr[10] = 0xff;
        addr6.s6_addr[11] = 0xff;
        addr6.s6_addr[12] = addr4 >> 24;
        addr6.s6_addr[13] = addr4 >> 16;
        addr6.s6_addr[14] = addr4 >> 8;
        addr6.s6_addr[15] = addr4;
        return addr6;

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

    default:
        return in6addr_any;
    }
}
static ngx_http_ip2location_ctx_t *
ngx_http_ip2location_create_ctx(ngx_http_request_t *r)
{
    ngx_array_t                *xfwd;
    ngx_http_ip2location_ctx_t *ctx;
    ngx_pool_cleanup_t         *cln;
    ngx_http_ip2location_main_conf_t  *imcf;
    ngx_addr_t                  addr;
    u_char                      address[NGX_INET6_ADDRSTRLEN + 1];
    size_t                      size;

    ctx = ngx_http_get_module_ctx(r, ngx_http_ip2location_module);

    if (ctx) {
        return ctx;
    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ip2location_ctx_t));

    if (ctx == NULL) {
        return NULL;
    }

    ngx_http_set_ctx(r, ctx, ngx_http_ip2location_module);

    imcf = ngx_http_get_module_main_conf(r, ngx_http_ip2location_module);
    addr.sockaddr = r->connection->sockaddr;
    addr.socklen = r->connection->socklen;

    xfwd = &r->headers_in.x_forwarded_for;

    if (xfwd->nelts > 0 && imcf->proxies != NULL) {
        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL, imcf->proxies, imcf->proxy_recursive);
    }

#if defined(nginx_version) && (nginx_version) >= 1005003
    size = ngx_sock_ntop(addr.sockaddr, addr.socklen, address, NGX_INET6_ADDRSTRLEN, 0);
#else
    size = ngx_sock_ntop(addr.sockaddr, address, NGX_INET6_ADDRSTRLEN, 0);
#endif
    address[size] = '\0';

    ctx->record = IP2Location_get_all(imcf->database, (char *)address);

    if (ctx->record == NULL) {
        ctx->not_found = 1;
        return ctx;
    }


    cln = ngx_pool_cleanup_add(r->pool, 0);
    if (cln == NULL) {
        ngx_http_set_ctx(r, NULL, ngx_http_ip2location_module);
        IP2Location_free_record(ctx->record);
        return NULL;
    }


    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
                  "http ip2location record (%s):\n"
                  "       country short: %s\n"
                  "        country long: %s\n"
                  "              region: %s\n"
                  "                city: %s\n"
                  "                 isp: %s\n"
                  "            latitude: %f\n"
                  "           longitude: %f\n"
                  "              domain: %s\n"
                  "             zipcode: %s\n"
                  "            timezone: %s\n"
                  "            netspeed: %s\n"
                  "             iddcode: %s\n"
                  "            areacode: %s\n"
                  "  weatherstationcode: %s\n"
                  "  weatherstationname: %s\n"
                  "                 mcc: %s\n"
                  "                 mnc: %s\n"
                  "         mobilebrand: %s\n"
                  "           elevation: %f\n"
                  "           usagetype: %s\n",
                  address,
                  ctx->record->country_short,
                  ctx->record->country_long,
                  ctx->record->region,
                  ctx->record->city,
                  ctx->record->isp,
                  ctx->record->latitude,
                  ctx->record->longitude,
                  ctx->record->domain,
                  ctx->record->zipcode,
                  ctx->record->timezone,
                  ctx->record->netspeed,
                  ctx->record->iddcode,
                  ctx->record->areacode,
                  ctx->record->weatherstationcode,
                  ctx->record->weatherstationname,
                  ctx->record->mcc,
                  ctx->record->mnc,
                  ctx->record->mobilebrand,
                  ctx->record->elevation,
                  ctx->record->usagetype);

    cln->data = ctx->record;
    cln->handler = (ngx_pool_cleanup_pt) IP2Location_free_record;

    return ctx;
}
static ngx_int_t
ngx_http_realip_handler(ngx_http_request_t *r)
{
    u_char                      *p;
    size_t                       len;
    ngx_str_t                   *value;
    ngx_uint_t                   i, hash;
    ngx_addr_t                   addr;
    ngx_array_t                 *xfwd;
    ngx_list_part_t             *part;
    ngx_table_elt_t             *header;
    ngx_connection_t            *c;
    ngx_http_realip_ctx_t       *ctx;
    ngx_http_realip_loc_conf_t  *rlcf;
    ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
    if (ctx)
    {
        return NGX_DECLINED;
    }
    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);
    if (rlcf->from == NULL)
    {
        return NGX_DECLINED;
    }
    switch (rlcf->type)
    {
    case NGX_HTTP_REALIP_XREALIP:
        if (r->headers_in.x_real_ip == NULL)
        {
            return NGX_DECLINED;
        }
        value = &r->headers_in.x_real_ip->value;
        xfwd = NULL;
        break;
    case NGX_HTTP_REALIP_XFWD:
        xfwd = &r->headers_in.x_forwarded_for;
        if (xfwd->elts == NULL)
        {
            return NGX_DECLINED;
        }
        value = NULL;
        break;
    case NGX_HTTP_REALIP_PROXY:
        value = &r->connection->proxy_protocol_addr;
        if (value->len == 0)
        {
            return NGX_DECLINED;
        }
        xfwd = NULL;
        break;
    default: /* NGX_HTTP_REALIP_HEADER */
        part = &r->headers_in.headers.part;
        header = part->elts;
        hash = rlcf->hash;
        len = rlcf->header.len;
        p = rlcf->header.data;
        for (i = 0; /* void */ ; i++)
        {
            if (i >= part->nelts)
            {
                if (part->next == NULL)
                {
                    break;
                }
                part = part->next;
                header = part->elts;
                i = 0;
            }
            if (hash == header[i].hash
                    && len == header[i].key.len
                    && ngx_strncmp(p, header[i].lowcase_key, len) == 0)
            {
                value = &header[i].value;
                xfwd = NULL;
                goto found;
            }
        }
        return NGX_DECLINED;
    }
found:
    c = r->connection;
    addr.sockaddr = c->sockaddr;
    addr.socklen = c->socklen;
    /* addr.name = c->addr_text; */
    if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
                                    rlcf->recursive)
            != NGX_DECLINED)
    {
        return ngx_http_realip_set_addr(r, &addr);
    }
    return NGX_DECLINED;
}
static ngx_int_t
ngx_http_geoip2_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
    uintptr_t data)
{
    ngx_http_geoip2_ctx_t   *geoip2 = (ngx_http_geoip2_ctx_t *) data;
    ngx_http_geoip2_db_t    *database = geoip2->database;
    int                     mmdb_error;
    MMDB_entry_data_s       entry_data;
    ngx_http_geoip2_conf_t  *gcf;
    ngx_addr_t              addr;
    ngx_array_t             *xfwd;
    u_char                  *p;

#if (NGX_HAVE_INET6)
    uint8_t address[16], *addressp = address;
#else
    unsigned long address;
#endif

    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip2_module);
    addr.sockaddr = r->connection->sockaddr;
    addr.socklen = r->connection->socklen;

    xfwd = &r->headers_in.x_forwarded_for;

    if (xfwd->nelts > 0 && gcf->proxies != NULL) {
        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
                                           gcf->proxies, gcf->proxy_recursive);
    }

    switch (addr.sockaddr->sa_family) {
        case AF_INET:
#if (NGX_HAVE_INET6)
            ngx_memset(addressp, 0, 12);
            ngx_memcpy(addressp + 12, &((struct sockaddr_in *)
                                        addr.sockaddr)->sin_addr.s_addr, 4);
            break;

        case AF_INET6:
            ngx_memcpy(addressp, &((struct sockaddr_in6 *)
                                   addr.sockaddr)->sin6_addr.s6_addr, 16);
#else
            address = ((struct sockaddr_in *)addr.sockaddr)->sin_addr.s_addr;
#endif
            break;

        default:
            goto not_found;
    }

#if (NGX_HAVE_INET6)
    if (ngx_memcmp(&address, &database->address, sizeof(address))
        != 0) {
#else
    if (address != database->address) {
#endif
        memcpy(&database->address, &address, sizeof(address));
        database->result = MMDB_lookup_sockaddr(&database->mmdb,
                                           addr.sockaddr, &mmdb_error);

        if (mmdb_error != MMDB_SUCCESS) {
            goto not_found;
        }
    }

    if (!database->result.found_entry
            || MMDB_aget_value(&database->result.entry, &entry_data,
                               geoip2->lookup) != MMDB_SUCCESS) {
        goto not_found;
    }

    if (!entry_data.has_data) {
        goto not_found;
    }

    switch (entry_data.type) {
        case MMDB_DATA_TYPE_UTF8_STRING:
            v->data = (u_char *) entry_data.utf8_string;
            v->len = entry_data.data_size;
            break;
        case MMDB_DATA_TYPE_UINT32:
            p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
            if (p == NULL) {
                return NGX_ERROR;
            }
            v->len = ngx_sprintf(p, "%O", entry_data.uint32) - p;
            v->data = p;
            break;
        default:
            goto not_found;
    }

    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;

    return NGX_OK;

not_found:
    if (geoip2->default_value.len > 0) {
        v->data = geoip2->default_value.data;
        v->len = geoip2->default_value.len;

        v->valid = 1;
        v->no_cacheable = 0;
        v->not_found = 0;
    } else {
        v->not_found = 1;
    }

    return NGX_OK;
}


static void *
ngx_http_geoip2_create_conf(ngx_conf_t *cf)
{
    ngx_pool_cleanup_t      *cln;
    ngx_http_geoip2_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip2_conf_t));
    if (conf == NULL) {
        return NULL;
    }

    conf->proxy_recursive = NGX_CONF_UNSET;

    cln = ngx_pool_cleanup_add(cf->pool, 0);
    if (cln == NULL) {
        return NULL;
    }

    cln->handler = ngx_http_geoip2_cleanup;
    cln->data = conf;

    return conf;
}