1
2/*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
10#include <ngx_http.h>
11
12
13#define NGX_HTTP_REALIP_XREALIP  0
14#define NGX_HTTP_REALIP_XFWD     1
15#define NGX_HTTP_REALIP_HEADER   2
16#define NGX_HTTP_REALIP_PROXY    3
17
18
19typedef struct {
20    ngx_array_t       *from;     /* array of ngx_cidr_t */
21    ngx_uint_t         type;
22    ngx_uint_t         hash;
23    ngx_str_t          header;
24    ngx_flag_t         recursive;
25} ngx_http_realip_loc_conf_t;
26
27
28typedef struct {
29    ngx_connection_t  *connection;
30    struct sockaddr   *sockaddr;
31    socklen_t          socklen;
32    ngx_str_t          addr_text;
33} ngx_http_realip_ctx_t;
34
35
36static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
37static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,
38    ngx_addr_t *addr);
39static void ngx_http_realip_cleanup(void *data);
40static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
41    void *conf);
42static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
43static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);
44static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,
45    void *parent, void *child);
46static ngx_int_t ngx_http_realip_add_variables(ngx_conf_t *cf);
47static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);
48static ngx_http_realip_ctx_t *ngx_http_realip_get_module_ctx(
49    ngx_http_request_t *r);
50
51
52static ngx_int_t ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
53    ngx_http_variable_value_t *v, uintptr_t data);
54static ngx_int_t ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
55    ngx_http_variable_value_t *v, uintptr_t data);
56
57
58static ngx_command_t  ngx_http_realip_commands[] = {
59
60    { ngx_string("set_real_ip_from"),
61      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
62      ngx_http_realip_from,
63      NGX_HTTP_LOC_CONF_OFFSET,
64      0,
65      NULL },
66
67    { ngx_string("real_ip_header"),
68      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
69      ngx_http_realip,
70      NGX_HTTP_LOC_CONF_OFFSET,
71      0,
72      NULL },
73
74    { ngx_string("real_ip_recursive"),
75      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
76      ngx_conf_set_flag_slot,
77      NGX_HTTP_LOC_CONF_OFFSET,
78      offsetof(ngx_http_realip_loc_conf_t, recursive),
79      NULL },
80
81      ngx_null_command
82};
83
84
85
86static ngx_http_module_t  ngx_http_realip_module_ctx = {
87    ngx_http_realip_add_variables,         /* preconfiguration */
88    ngx_http_realip_init,                  /* postconfiguration */
89
90    NULL,                                  /* create main configuration */
91    NULL,                                  /* init main configuration */
92
93    NULL,                                  /* create server configuration */
94    NULL,                                  /* merge server configuration */
95
96    ngx_http_realip_create_loc_conf,       /* create location configuration */
97    ngx_http_realip_merge_loc_conf         /* merge location configuration */
98};
99
100
101ngx_module_t  ngx_http_realip_module = {
102    NGX_MODULE_V1,
103    &ngx_http_realip_module_ctx,           /* module context */
104    ngx_http_realip_commands,              /* module directives */
105    NGX_HTTP_MODULE,                       /* module type */
106    NULL,                                  /* init master */
107    NULL,                                  /* init module */
108    NULL,                                  /* init process */
109    NULL,                                  /* init thread */
110    NULL,                                  /* exit thread */
111    NULL,                                  /* exit process */
112    NULL,                                  /* exit master */
113    NGX_MODULE_V1_PADDING
114};
115
116
117static ngx_http_variable_t  ngx_http_realip_vars[] = {
118
119    { ngx_string("realip_remote_addr"), NULL,
120      ngx_http_realip_remote_addr_variable, 0, 0, 0 },
121
122    { ngx_string("realip_remote_port"), NULL,
123      ngx_http_realip_remote_port_variable, 0, 0, 0 },
124
125    { ngx_null_string, NULL, NULL, 0, 0, 0 }
126};
127
128
129static ngx_int_t
130ngx_http_realip_handler(ngx_http_request_t *r)
131{
132    u_char                      *p;
133    size_t                       len;
134    ngx_str_t                   *value;
135    ngx_uint_t                   i, hash;
136    ngx_addr_t                   addr;
137    ngx_array_t                 *xfwd;
138    ngx_list_part_t             *part;
139    ngx_table_elt_t             *header;
140    ngx_connection_t            *c;
141    ngx_http_realip_ctx_t       *ctx;
142    ngx_http_realip_loc_conf_t  *rlcf;
143
144    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);
145
146    if (rlcf->from == NULL) {
147        return NGX_DECLINED;
148    }
149
150    ctx = ngx_http_realip_get_module_ctx(r);
151
152    if (ctx) {
153        return NGX_DECLINED;
154    }
155
156    switch (rlcf->type) {
157
158    case NGX_HTTP_REALIP_XREALIP:
159
160        if (r->headers_in.x_real_ip == NULL) {
161            return NGX_DECLINED;
162        }
163
164        value = &r->headers_in.x_real_ip->value;
165        xfwd = NULL;
166
167        break;
168
169    case NGX_HTTP_REALIP_XFWD:
170
171        xfwd = &r->headers_in.x_forwarded_for;
172
173        if (xfwd->elts == NULL) {
174            return NGX_DECLINED;
175        }
176
177        value = NULL;
178
179        break;
180
181    case NGX_HTTP_REALIP_PROXY:
182
183        value = &r->connection->proxy_protocol_addr;
184
185        if (value->len == 0) {
186            return NGX_DECLINED;
187        }
188
189        xfwd = NULL;
190
191        break;
192
193    default: /* NGX_HTTP_REALIP_HEADER */
194
195        part = &r->headers_in.headers.part;
196        header = part->elts;
197
198        hash = rlcf->hash;
199        len = rlcf->header.len;
200        p = rlcf->header.data;
201
202        for (i = 0; /* void */ ; i++) {
203
204            if (i >= part->nelts) {
205                if (part->next == NULL) {
206                    break;
207                }
208
209                part = part->next;
210                header = part->elts;
211                i = 0;
212            }
213
214            if (hash == header[i].hash
215                && len == header[i].key.len
216                && ngx_strncmp(p, header[i].lowcase_key, len) == 0)
217            {
218                value = &header[i].value;
219                xfwd = NULL;
220
221                goto found;
222            }
223        }
224
225        return NGX_DECLINED;
226    }
227
228found:
229
230    c = r->connection;
231
232    addr.sockaddr = c->sockaddr;
233    addr.socklen = c->socklen;
234    /* addr.name = c->addr_text; */
235
236    if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
237                                    rlcf->recursive)
238        != NGX_DECLINED)
239    {
240        if (rlcf->type == NGX_HTTP_REALIP_PROXY) {
241            ngx_inet_set_port(addr.sockaddr, c->proxy_protocol_port);
242        }
243
244        return ngx_http_realip_set_addr(r, &addr);
245    }
246
247    return NGX_DECLINED;
248}
249
250
251static ngx_int_t
252ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)
253{
254    size_t                  len;
255    u_char                 *p;
256    u_char                  text[NGX_SOCKADDR_STRLEN];
257    ngx_connection_t       *c;
258    ngx_pool_cleanup_t     *cln;
259    ngx_http_realip_ctx_t  *ctx;
260
261    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));
262    if (cln == NULL) {
263        return NGX_HTTP_INTERNAL_SERVER_ERROR;
264    }
265
266    ctx = cln->data;
267
268    c = r->connection;
269
270    len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
271                        NGX_SOCKADDR_STRLEN, 0);
272    if (len == 0) {
273        return NGX_HTTP_INTERNAL_SERVER_ERROR;
274    }
275
276    p = ngx_pnalloc(c->pool, len);
277    if (p == NULL) {
278        return NGX_HTTP_INTERNAL_SERVER_ERROR;
279    }
280
281    ngx_memcpy(p, text, len);
282
283    cln->handler = ngx_http_realip_cleanup;
284    ngx_http_set_ctx(r, ctx, ngx_http_realip_module);
285
286    ctx->connection = c;
287    ctx->sockaddr = c->sockaddr;
288    ctx->socklen = c->socklen;
289    ctx->addr_text = c->addr_text;
290
291    c->sockaddr = addr->sockaddr;
292    c->socklen = addr->socklen;
293    c->addr_text.len = len;
294    c->addr_text.data = p;
295
296    return NGX_DECLINED;
297}
298
299
300static void
301ngx_http_realip_cleanup(void *data)
302{
303    ngx_http_realip_ctx_t *ctx = data;
304
305    ngx_connection_t  *c;
306
307    c = ctx->connection;
308
309    c->sockaddr = ctx->sockaddr;
310    c->socklen = ctx->socklen;
311    c->addr_text = ctx->addr_text;
312}
313
314
315static char *
316ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
317{
318    ngx_http_realip_loc_conf_t *rlcf = conf;
319
320    ngx_int_t                rc;
321    ngx_str_t               *value;
322    ngx_cidr_t              *cidr;
323
324    value = cf->args->elts;
325
326    if (rlcf->from == NULL) {
327        rlcf->from = ngx_array_create(cf->pool, 2,
328                                      sizeof(ngx_cidr_t));
329        if (rlcf->from == NULL) {
330            return NGX_CONF_ERROR;
331        }
332    }
333
334    cidr = ngx_array_push(rlcf->from);
335    if (cidr == NULL) {
336        return NGX_CONF_ERROR;
337    }
338
339#if (NGX_HAVE_UNIX_DOMAIN)
340
341    if (ngx_strcmp(value[1].data, "unix:") == 0) {
342        cidr->family = AF_UNIX;
343        return NGX_CONF_OK;
344    }
345
346#endif
347
348    rc = ngx_ptocidr(&value[1], cidr);
349
350    if (rc == NGX_ERROR) {
351        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
352                           &value[1]);
353        return NGX_CONF_ERROR;
354    }
355
356    if (rc == NGX_DONE) {
357        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
358                           "low address bits of %V are meaningless", &value[1]);
359    }
360
361    return NGX_CONF_OK;
362}
363
364
365static char *
366ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
367{
368    ngx_http_realip_loc_conf_t *rlcf = conf;
369
370    ngx_str_t  *value;
371
372    if (rlcf->type != NGX_CONF_UNSET_UINT) {
373        return "is duplicate";
374    }
375
376    value = cf->args->elts;
377
378    if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
379        rlcf->type = NGX_HTTP_REALIP_XREALIP;
380        return NGX_CONF_OK;
381    }
382
383    if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
384        rlcf->type = NGX_HTTP_REALIP_XFWD;
385        return NGX_CONF_OK;
386    }
387
388    if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
389        rlcf->type = NGX_HTTP_REALIP_PROXY;
390        return NGX_CONF_OK;
391    }
392
393    rlcf->type = NGX_HTTP_REALIP_HEADER;
394    rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
395    rlcf->header = value[1];
396
397    return NGX_CONF_OK;
398}
399
400
401static void *
402ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
403{
404    ngx_http_realip_loc_conf_t  *conf;
405
406    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
407    if (conf == NULL) {
408        return NULL;
409    }
410
411    /*
412     * set by ngx_pcalloc():
413     *
414     *     conf->from = NULL;
415     *     conf->hash = 0;
416     *     conf->header = { 0, NULL };
417     */
418
419    conf->type = NGX_CONF_UNSET_UINT;
420    conf->recursive = NGX_CONF_UNSET;
421
422    return conf;
423}
424
425
426static char *
427ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
428{
429    ngx_http_realip_loc_conf_t  *prev = parent;
430    ngx_http_realip_loc_conf_t  *conf = child;
431
432    if (conf->from == NULL) {
433        conf->from = prev->from;
434    }
435
436    ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
437    ngx_conf_merge_value(conf->recursive, prev->recursive, 0);
438
439    if (conf->header.len == 0) {
440        conf->hash = prev->hash;
441        conf->header = prev->header;
442    }
443
444    return NGX_CONF_OK;
445}
446
447
448static ngx_int_t
449ngx_http_realip_add_variables(ngx_conf_t *cf)
450{
451    ngx_http_variable_t  *var, *v;
452
453    for (v = ngx_http_realip_vars; v->name.len; v++) {
454        var = ngx_http_add_variable(cf, &v->name, v->flags);
455        if (var == NULL) {
456            return NGX_ERROR;
457        }
458
459        var->get_handler = v->get_handler;
460        var->data = v->data;
461    }
462
463    return NGX_OK;
464}
465
466
467static ngx_int_t
468ngx_http_realip_init(ngx_conf_t *cf)
469{
470    ngx_http_handler_pt        *h;
471    ngx_http_core_main_conf_t  *cmcf;
472
473    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
474
475    h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
476    if (h == NULL) {
477        return NGX_ERROR;
478    }
479
480    *h = ngx_http_realip_handler;
481
482    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
483    if (h == NULL) {
484        return NGX_ERROR;
485    }
486
487    *h = ngx_http_realip_handler;
488
489    return NGX_OK;
490}
491
492
493static ngx_http_realip_ctx_t *
494ngx_http_realip_get_module_ctx(ngx_http_request_t *r)
495{
496    ngx_pool_cleanup_t     *cln;
497    ngx_http_realip_ctx_t  *ctx;
498
499    ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
500
501    if (ctx == NULL && (r->internal || r->filter_finalize)) {
502
503        /*
504         * if module context was reset, the original address
505         * can still be found in the cleanup handler
506         */
507
508        for (cln = r->pool->cleanup; cln; cln = cln->next) {
509            if (cln->handler == ngx_http_realip_cleanup) {
510                ctx = cln->data;
511                break;
512            }
513        }
514    }
515
516    return ctx;
517}
518
519
520static ngx_int_t
521ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
522    ngx_http_variable_value_t *v, uintptr_t data)
523{
524    ngx_str_t              *addr_text;
525    ngx_http_realip_ctx_t  *ctx;
526
527    ctx = ngx_http_realip_get_module_ctx(r);
528
529    addr_text = ctx ? &ctx->addr_text : &r->connection->addr_text;
530
531    v->len = addr_text->len;
532    v->valid = 1;
533    v->no_cacheable = 0;
534    v->not_found = 0;
535    v->data = addr_text->data;
536
537    return NGX_OK;
538}
539
540
541static ngx_int_t
542ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
543    ngx_http_variable_value_t *v, uintptr_t data)
544{
545    ngx_uint_t              port;
546    struct sockaddr        *sa;
547    ngx_http_realip_ctx_t  *ctx;
548
549    ctx = ngx_http_realip_get_module_ctx(r);
550
551    sa = ctx ? ctx->sockaddr : r->connection->sockaddr;
552
553    v->len = 0;
554    v->valid = 1;
555    v->no_cacheable = 0;
556    v->not_found = 0;
557
558    v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
559    if (v->data == NULL) {
560        return NGX_ERROR;
561    }
562
563    port = ngx_inet_get_port(sa);
564
565    if (port > 0 && port < 65536) {
566        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
567    }
568
569    return NGX_OK;
570}
571