src/http/modules/ngx_http_realip_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_http.h>


  8. #define NGX_HTTP_REALIP_XREALIP  0
  9. #define NGX_HTTP_REALIP_XFWD     1
  10. #define NGX_HTTP_REALIP_HEADER   2
  11. #define NGX_HTTP_REALIP_PROXY    3


  12. typedef struct {
  13.     ngx_array_t       *from;     /* array of ngx_cidr_t */
  14.     ngx_uint_t         type;
  15.     ngx_uint_t         hash;
  16.     ngx_str_t          header;
  17.     ngx_flag_t         recursive;
  18. } ngx_http_realip_loc_conf_t;


  19. typedef struct {
  20.     ngx_connection_t  *connection;
  21.     struct sockaddr   *sockaddr;
  22.     socklen_t          socklen;
  23.     ngx_str_t          addr_text;
  24. } ngx_http_realip_ctx_t;


  25. static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
  26. static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,
  27.     ngx_addr_t *addr);
  28. static void ngx_http_realip_cleanup(void *data);
  29. static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
  30.     void *conf);
  31. static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  32. static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);
  33. static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,
  34.     void *parent, void *child);
  35. static ngx_int_t ngx_http_realip_add_variables(ngx_conf_t *cf);
  36. static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);
  37. static ngx_http_realip_ctx_t *ngx_http_realip_get_module_ctx(
  38.     ngx_http_request_t *r);


  39. static ngx_int_t ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
  40.     ngx_http_variable_value_t *v, uintptr_t data);
  41. static ngx_int_t ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
  42.     ngx_http_variable_value_t *v, uintptr_t data);


  43. static ngx_command_t  ngx_http_realip_commands[] = {

  44.     { ngx_string("set_real_ip_from"),
  45.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  46.       ngx_http_realip_from,
  47.       NGX_HTTP_LOC_CONF_OFFSET,
  48.       0,
  49.       NULL },

  50.     { ngx_string("real_ip_header"),
  51.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  52.       ngx_http_realip,
  53.       NGX_HTTP_LOC_CONF_OFFSET,
  54.       0,
  55.       NULL },

  56.     { ngx_string("real_ip_recursive"),
  57.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
  58.       ngx_conf_set_flag_slot,
  59.       NGX_HTTP_LOC_CONF_OFFSET,
  60.       offsetof(ngx_http_realip_loc_conf_t, recursive),
  61.       NULL },

  62.       ngx_null_command
  63. };



  64. static ngx_http_module_t  ngx_http_realip_module_ctx = {
  65.     ngx_http_realip_add_variables,         /* preconfiguration */
  66.     ngx_http_realip_init,                  /* postconfiguration */

  67.     NULL,                                  /* create main configuration */
  68.     NULL,                                  /* init main configuration */

  69.     NULL,                                  /* create server configuration */
  70.     NULL,                                  /* merge server configuration */

  71.     ngx_http_realip_create_loc_conf,       /* create location configuration */
  72.     ngx_http_realip_merge_loc_conf         /* merge location configuration */
  73. };


  74. ngx_module_t  ngx_http_realip_module = {
  75.     NGX_MODULE_V1,
  76.     &ngx_http_realip_module_ctx,           /* module context */
  77.     ngx_http_realip_commands,              /* module directives */
  78.     NGX_HTTP_MODULE,                       /* module type */
  79.     NULL,                                  /* init master */
  80.     NULL,                                  /* init module */
  81.     NULL,                                  /* init process */
  82.     NULL,                                  /* init thread */
  83.     NULL,                                  /* exit thread */
  84.     NULL,                                  /* exit process */
  85.     NULL,                                  /* exit master */
  86.     NGX_MODULE_V1_PADDING
  87. };


  88. static ngx_http_variable_t  ngx_http_realip_vars[] = {

  89.     { ngx_string("realip_remote_addr"), NULL,
  90.       ngx_http_realip_remote_addr_variable, 0, 0, 0 },

  91.     { ngx_string("realip_remote_port"), NULL,
  92.       ngx_http_realip_remote_port_variable, 0, 0, 0 },

  93.       ngx_http_null_variable
  94. };


  95. static ngx_int_t
  96. ngx_http_realip_handler(ngx_http_request_t *r)
  97. {
  98.     u_char                      *p;
  99.     size_t                       len;
  100.     ngx_str_t                   *value;
  101.     ngx_uint_t                   i, hash;
  102.     ngx_addr_t                   addr;
  103.     ngx_list_part_t             *part;
  104.     ngx_table_elt_t             *header, *xfwd;
  105.     ngx_connection_t            *c;
  106.     ngx_http_realip_ctx_t       *ctx;
  107.     ngx_http_realip_loc_conf_t  *rlcf;

  108.     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);

  109.     if (rlcf->from == NULL) {
  110.         return NGX_DECLINED;
  111.     }

  112.     ctx = ngx_http_realip_get_module_ctx(r);

  113.     if (ctx) {
  114.         return NGX_DECLINED;
  115.     }

  116.     switch (rlcf->type) {

  117.     case NGX_HTTP_REALIP_XREALIP:

  118.         if (r->headers_in.x_real_ip == NULL) {
  119.             return NGX_DECLINED;
  120.         }

  121.         value = &r->headers_in.x_real_ip->value;
  122.         xfwd = NULL;

  123.         break;

  124.     case NGX_HTTP_REALIP_XFWD:

  125.         xfwd = r->headers_in.x_forwarded_for;

  126.         if (xfwd == NULL) {
  127.             return NGX_DECLINED;
  128.         }

  129.         value = NULL;

  130.         break;

  131.     case NGX_HTTP_REALIP_PROXY:

  132.         if (r->connection->proxy_protocol == NULL) {
  133.             return NGX_DECLINED;
  134.         }

  135.         value = &r->connection->proxy_protocol->src_addr;
  136.         xfwd = NULL;

  137.         break;

  138.     default: /* NGX_HTTP_REALIP_HEADER */

  139.         part = &r->headers_in.headers.part;
  140.         header = part->elts;

  141.         hash = rlcf->hash;
  142.         len = rlcf->header.len;
  143.         p = rlcf->header.data;

  144.         for (i = 0; /* void */ ; i++) {

  145.             if (i >= part->nelts) {
  146.                 if (part->next == NULL) {
  147.                     break;
  148.                 }

  149.                 part = part->next;
  150.                 header = part->elts;
  151.                 i = 0;
  152.             }

  153.             if (hash == header[i].hash
  154.                 && len == header[i].key.len
  155.                 && ngx_strncmp(p, header[i].lowcase_key, len) == 0)
  156.             {
  157.                 value = &header[i].value;
  158.                 xfwd = NULL;

  159.                 goto found;
  160.             }
  161.         }

  162.         return NGX_DECLINED;
  163.     }

  164. found:

  165.     c = r->connection;

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

  169.     if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
  170.                                     rlcf->recursive)
  171.         != NGX_DECLINED)
  172.     {
  173.         if (rlcf->type == NGX_HTTP_REALIP_PROXY) {
  174.             ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port);
  175.         }

  176.         return ngx_http_realip_set_addr(r, &addr);
  177.     }

  178.     return NGX_DECLINED;
  179. }


  180. static ngx_int_t
  181. ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)
  182. {
  183.     size_t                  len;
  184.     u_char                 *p;
  185.     u_char                  text[NGX_SOCKADDR_STRLEN];
  186.     ngx_connection_t       *c;
  187.     ngx_pool_cleanup_t     *cln;
  188.     ngx_http_realip_ctx_t  *ctx;

  189.     cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));
  190.     if (cln == NULL) {
  191.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  192.     }

  193.     ctx = cln->data;

  194.     c = r->connection;

  195.     len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
  196.                         NGX_SOCKADDR_STRLEN, 0);
  197.     if (len == 0) {
  198.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  199.     }

  200.     p = ngx_pnalloc(c->pool, len);
  201.     if (p == NULL) {
  202.         return NGX_HTTP_INTERNAL_SERVER_ERROR;
  203.     }

  204.     ngx_memcpy(p, text, len);

  205.     cln->handler = ngx_http_realip_cleanup;
  206.     ngx_http_set_ctx(r, ctx, ngx_http_realip_module);

  207.     ctx->connection = c;
  208.     ctx->sockaddr = c->sockaddr;
  209.     ctx->socklen = c->socklen;
  210.     ctx->addr_text = c->addr_text;

  211.     c->sockaddr = addr->sockaddr;
  212.     c->socklen = addr->socklen;
  213.     c->addr_text.len = len;
  214.     c->addr_text.data = p;

  215.     return NGX_DECLINED;
  216. }


  217. static void
  218. ngx_http_realip_cleanup(void *data)
  219. {
  220.     ngx_http_realip_ctx_t *ctx = data;

  221.     ngx_connection_t  *c;

  222.     c = ctx->connection;

  223.     c->sockaddr = ctx->sockaddr;
  224.     c->socklen = ctx->socklen;
  225.     c->addr_text = ctx->addr_text;
  226. }


  227. static char *
  228. ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  229. {
  230.     ngx_http_realip_loc_conf_t *rlcf = conf;

  231.     ngx_int_t             rc;
  232.     ngx_str_t            *value;
  233.     ngx_url_t             u;
  234.     ngx_cidr_t            c, *cidr;
  235.     ngx_uint_t            i;
  236.     struct sockaddr_in   *sin;
  237. #if (NGX_HAVE_INET6)
  238.     struct sockaddr_in6  *sin6;
  239. #endif

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

  241.     if (rlcf->from == NULL) {
  242.         rlcf->from = ngx_array_create(cf->pool, 2,
  243.                                       sizeof(ngx_cidr_t));
  244.         if (rlcf->from == NULL) {
  245.             return NGX_CONF_ERROR;
  246.         }
  247.     }

  248. #if (NGX_HAVE_UNIX_DOMAIN)

  249.     if (ngx_strcmp(value[1].data, "unix:") == 0) {
  250.         cidr = ngx_array_push(rlcf->from);
  251.         if (cidr == NULL) {
  252.             return NGX_CONF_ERROR;
  253.         }

  254.         cidr->family = AF_UNIX;
  255.         return NGX_CONF_OK;
  256.     }

  257. #endif

  258.     rc = ngx_ptocidr(&value[1], &c);

  259.     if (rc != NGX_ERROR) {
  260.         if (rc == NGX_DONE) {
  261.             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
  262.                                "low address bits of %V are meaningless",
  263.                                &value[1]);
  264.         }

  265.         cidr = ngx_array_push(rlcf->from);
  266.         if (cidr == NULL) {
  267.             return NGX_CONF_ERROR;
  268.         }

  269.         *cidr = c;

  270.         return NGX_CONF_OK;
  271.     }

  272.     ngx_memzero(&u, sizeof(ngx_url_t));
  273.     u.host = value[1];

  274.     if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
  275.         if (u.err) {
  276.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  277.                                "%s in set_real_ip_from \"%V\"",
  278.                                u.err, &u.host);
  279.         }

  280.         return NGX_CONF_ERROR;
  281.     }

  282.     cidr = ngx_array_push_n(rlcf->from, u.naddrs);
  283.     if (cidr == NULL) {
  284.         return NGX_CONF_ERROR;
  285.     }

  286.     ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));

  287.     for (i = 0; i < u.naddrs; i++) {
  288.         cidr[i].family = u.addrs[i].sockaddr->sa_family;

  289.         switch (cidr[i].family) {

  290. #if (NGX_HAVE_INET6)
  291.         case AF_INET6:
  292.             sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
  293.             cidr[i].u.in6.addr = sin6->sin6_addr;
  294.             ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
  295.             break;
  296. #endif

  297.         default: /* AF_INET */
  298.             sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
  299.             cidr[i].u.in.addr = sin->sin_addr.s_addr;
  300.             cidr[i].u.in.mask = 0xffffffff;
  301.             break;
  302.         }
  303.     }

  304.     return NGX_CONF_OK;
  305. }


  306. static char *
  307. ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  308. {
  309.     ngx_http_realip_loc_conf_t *rlcf = conf;

  310.     ngx_str_t  *value;

  311.     if (rlcf->type != NGX_CONF_UNSET_UINT) {
  312.         return "is duplicate";
  313.     }

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

  315.     if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
  316.         rlcf->type = NGX_HTTP_REALIP_XREALIP;
  317.         return NGX_CONF_OK;
  318.     }

  319.     if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
  320.         rlcf->type = NGX_HTTP_REALIP_XFWD;
  321.         return NGX_CONF_OK;
  322.     }

  323.     if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
  324.         rlcf->type = NGX_HTTP_REALIP_PROXY;
  325.         return NGX_CONF_OK;
  326.     }

  327.     rlcf->type = NGX_HTTP_REALIP_HEADER;
  328.     rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
  329.     rlcf->header = value[1];

  330.     return NGX_CONF_OK;
  331. }


  332. static void *
  333. ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
  334. {
  335.     ngx_http_realip_loc_conf_t  *conf;

  336.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
  337.     if (conf == NULL) {
  338.         return NULL;
  339.     }

  340.     /*
  341.      * set by ngx_pcalloc():
  342.      *
  343.      *     conf->from = NULL;
  344.      *     conf->hash = 0;
  345.      *     conf->header = { 0, NULL };
  346.      */

  347.     conf->type = NGX_CONF_UNSET_UINT;
  348.     conf->recursive = NGX_CONF_UNSET;

  349.     return conf;
  350. }


  351. static char *
  352. ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  353. {
  354.     ngx_http_realip_loc_conf_t  *prev = parent;
  355.     ngx_http_realip_loc_conf_t  *conf = child;

  356.     if (conf->from == NULL) {
  357.         conf->from = prev->from;
  358.     }

  359.     ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
  360.     ngx_conf_merge_value(conf->recursive, prev->recursive, 0);

  361.     if (conf->header.len == 0) {
  362.         conf->hash = prev->hash;
  363.         conf->header = prev->header;
  364.     }

  365.     return NGX_CONF_OK;
  366. }


  367. static ngx_int_t
  368. ngx_http_realip_add_variables(ngx_conf_t *cf)
  369. {
  370.     ngx_http_variable_t  *var, *v;

  371.     for (v = ngx_http_realip_vars; v->name.len; v++) {
  372.         var = ngx_http_add_variable(cf, &v->name, v->flags);
  373.         if (var == NULL) {
  374.             return NGX_ERROR;
  375.         }

  376.         var->get_handler = v->get_handler;
  377.         var->data = v->data;
  378.     }

  379.     return NGX_OK;
  380. }


  381. static ngx_int_t
  382. ngx_http_realip_init(ngx_conf_t *cf)
  383. {
  384.     ngx_http_handler_pt        *h;
  385.     ngx_http_core_main_conf_t  *cmcf;

  386.     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

  387.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
  388.     if (h == NULL) {
  389.         return NGX_ERROR;
  390.     }

  391.     *h = ngx_http_realip_handler;

  392.     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
  393.     if (h == NULL) {
  394.         return NGX_ERROR;
  395.     }

  396.     *h = ngx_http_realip_handler;

  397.     return NGX_OK;
  398. }


  399. static ngx_http_realip_ctx_t *
  400. ngx_http_realip_get_module_ctx(ngx_http_request_t *r)
  401. {
  402.     ngx_pool_cleanup_t     *cln;
  403.     ngx_http_realip_ctx_t  *ctx;

  404.     ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);

  405.     if (ctx == NULL && (r->internal || r->filter_finalize)) {

  406.         /*
  407.          * if module context was reset, the original address
  408.          * can still be found in the cleanup handler
  409.          */

  410.         for (cln = r->pool->cleanup; cln; cln = cln->next) {
  411.             if (cln->handler == ngx_http_realip_cleanup) {
  412.                 ctx = cln->data;
  413.                 break;
  414.             }
  415.         }
  416.     }

  417.     return ctx;
  418. }


  419. static ngx_int_t
  420. ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
  421.     ngx_http_variable_value_t *v, uintptr_t data)
  422. {
  423.     ngx_str_t              *addr_text;
  424.     ngx_http_realip_ctx_t  *ctx;

  425.     ctx = ngx_http_realip_get_module_ctx(r);

  426.     addr_text = ctx ? &ctx->addr_text : &r->connection->addr_text;

  427.     v->len = addr_text->len;
  428.     v->valid = 1;
  429.     v->no_cacheable = 0;
  430.     v->not_found = 0;
  431.     v->data = addr_text->data;

  432.     return NGX_OK;
  433. }


  434. static ngx_int_t
  435. ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
  436.     ngx_http_variable_value_t *v, uintptr_t data)
  437. {
  438.     ngx_uint_t              port;
  439.     struct sockaddr        *sa;
  440.     ngx_http_realip_ctx_t  *ctx;

  441.     ctx = ngx_http_realip_get_module_ctx(r);

  442.     sa = ctx ? ctx->sockaddr : r->connection->sockaddr;

  443.     v->len = 0;
  444.     v->valid = 1;
  445.     v->no_cacheable = 0;
  446.     v->not_found = 0;

  447.     v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
  448.     if (v->data == NULL) {
  449.         return NGX_ERROR;
  450.     }

  451.     port = ngx_inet_get_port(sa);

  452.     if (port > 0 && port < 65536) {
  453.         v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
  454.     }

  455.     return NGX_OK;
  456. }