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 
19 typedef 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 
28 typedef 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 
36 static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
37 static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,
38     ngx_addr_t *addr);
39 static void ngx_http_realip_cleanup(void *data);
40 static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
41     void *conf);
42 static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
43 static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);
44 static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,
45     void *parent, void *child);
46 static ngx_int_t ngx_http_realip_add_variables(ngx_conf_t *cf);
47 static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);
48 static ngx_http_realip_ctx_t *ngx_http_realip_get_module_ctx(
49     ngx_http_request_t *r);
50 
51 
52 static ngx_int_t ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
53     ngx_http_variable_value_t *v, uintptr_t data);
54 static 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 
58 static 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 
86 static 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 
101 ngx_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 
117 static 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_http_null_variable
126 };
127 
128 
129 static ngx_int_t
ngx_http_realip_handler(ngx_http_request_t * r)130 ngx_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         if (r->connection->proxy_protocol == NULL) {
184             return NGX_DECLINED;
185         }
186 
187         value = &r->connection->proxy_protocol->src_addr;
188         xfwd = NULL;
189 
190         break;
191 
192     default: /* NGX_HTTP_REALIP_HEADER */
193 
194         part = &r->headers_in.headers.part;
195         header = part->elts;
196 
197         hash = rlcf->hash;
198         len = rlcf->header.len;
199         p = rlcf->header.data;
200 
201         for (i = 0; /* void */ ; i++) {
202 
203             if (i >= part->nelts) {
204                 if (part->next == NULL) {
205                     break;
206                 }
207 
208                 part = part->next;
209                 header = part->elts;
210                 i = 0;
211             }
212 
213             if (hash == header[i].hash
214                 && len == header[i].key.len
215                 && ngx_strncmp(p, header[i].lowcase_key, len) == 0)
216             {
217                 value = &header[i].value;
218                 xfwd = NULL;
219 
220                 goto found;
221             }
222         }
223 
224         return NGX_DECLINED;
225     }
226 
227 found:
228 
229     c = r->connection;
230 
231     addr.sockaddr = c->sockaddr;
232     addr.socklen = c->socklen;
233     /* addr.name = c->addr_text; */
234 
235     if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
236                                     rlcf->recursive)
237         != NGX_DECLINED)
238     {
239         if (rlcf->type == NGX_HTTP_REALIP_PROXY) {
240             ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port);
241         }
242 
243         return ngx_http_realip_set_addr(r, &addr);
244     }
245 
246     return NGX_DECLINED;
247 }
248 
249 
250 static ngx_int_t
ngx_http_realip_set_addr(ngx_http_request_t * r,ngx_addr_t * addr)251 ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)
252 {
253     size_t                  len;
254     u_char                 *p;
255     u_char                  text[NGX_SOCKADDR_STRLEN];
256     ngx_connection_t       *c;
257     ngx_pool_cleanup_t     *cln;
258     ngx_http_realip_ctx_t  *ctx;
259 
260     cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));
261     if (cln == NULL) {
262         return NGX_HTTP_INTERNAL_SERVER_ERROR;
263     }
264 
265     ctx = cln->data;
266 
267     c = r->connection;
268 
269     len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
270                         NGX_SOCKADDR_STRLEN, 0);
271     if (len == 0) {
272         return NGX_HTTP_INTERNAL_SERVER_ERROR;
273     }
274 
275     p = ngx_pnalloc(c->pool, len);
276     if (p == NULL) {
277         return NGX_HTTP_INTERNAL_SERVER_ERROR;
278     }
279 
280     ngx_memcpy(p, text, len);
281 
282     cln->handler = ngx_http_realip_cleanup;
283     ngx_http_set_ctx(r, ctx, ngx_http_realip_module);
284 
285     ctx->connection = c;
286     ctx->sockaddr = c->sockaddr;
287     ctx->socklen = c->socklen;
288     ctx->addr_text = c->addr_text;
289 
290     c->sockaddr = addr->sockaddr;
291     c->socklen = addr->socklen;
292     c->addr_text.len = len;
293     c->addr_text.data = p;
294 
295     return NGX_DECLINED;
296 }
297 
298 
299 static void
ngx_http_realip_cleanup(void * data)300 ngx_http_realip_cleanup(void *data)
301 {
302     ngx_http_realip_ctx_t *ctx = data;
303 
304     ngx_connection_t  *c;
305 
306     c = ctx->connection;
307 
308     c->sockaddr = ctx->sockaddr;
309     c->socklen = ctx->socklen;
310     c->addr_text = ctx->addr_text;
311 }
312 
313 
314 static char *
ngx_http_realip_from(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)315 ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
316 {
317     ngx_http_realip_loc_conf_t *rlcf = conf;
318 
319     ngx_int_t             rc;
320     ngx_str_t            *value;
321     ngx_url_t             u;
322     ngx_cidr_t            c, *cidr;
323     ngx_uint_t            i;
324     struct sockaddr_in   *sin;
325 #if (NGX_HAVE_INET6)
326     struct sockaddr_in6  *sin6;
327 #endif
328 
329     value = cf->args->elts;
330 
331     if (rlcf->from == NULL) {
332         rlcf->from = ngx_array_create(cf->pool, 2,
333                                       sizeof(ngx_cidr_t));
334         if (rlcf->from == NULL) {
335             return NGX_CONF_ERROR;
336         }
337     }
338 
339 #if (NGX_HAVE_UNIX_DOMAIN)
340 
341     if (ngx_strcmp(value[1].data, "unix:") == 0) {
342         cidr = ngx_array_push(rlcf->from);
343         if (cidr == NULL) {
344             return NGX_CONF_ERROR;
345         }
346 
347         cidr->family = AF_UNIX;
348         return NGX_CONF_OK;
349     }
350 
351 #endif
352 
353     rc = ngx_ptocidr(&value[1], &c);
354 
355     if (rc != NGX_ERROR) {
356         if (rc == NGX_DONE) {
357             ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
358                                "low address bits of %V are meaningless",
359                                &value[1]);
360         }
361 
362         cidr = ngx_array_push(rlcf->from);
363         if (cidr == NULL) {
364             return NGX_CONF_ERROR;
365         }
366 
367         *cidr = c;
368 
369         return NGX_CONF_OK;
370     }
371 
372     ngx_memzero(&u, sizeof(ngx_url_t));
373     u.host = value[1];
374 
375     if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
376         if (u.err) {
377             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
378                                "%s in set_real_ip_from \"%V\"",
379                                u.err, &u.host);
380         }
381 
382         return NGX_CONF_ERROR;
383     }
384 
385     cidr = ngx_array_push_n(rlcf->from, u.naddrs);
386     if (cidr == NULL) {
387         return NGX_CONF_ERROR;
388     }
389 
390     ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));
391 
392     for (i = 0; i < u.naddrs; i++) {
393         cidr[i].family = u.addrs[i].sockaddr->sa_family;
394 
395         switch (cidr[i].family) {
396 
397 #if (NGX_HAVE_INET6)
398         case AF_INET6:
399             sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
400             cidr[i].u.in6.addr = sin6->sin6_addr;
401             ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
402             break;
403 #endif
404 
405         default: /* AF_INET */
406             sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
407             cidr[i].u.in.addr = sin->sin_addr.s_addr;
408             cidr[i].u.in.mask = 0xffffffff;
409             break;
410         }
411     }
412 
413     return NGX_CONF_OK;
414 }
415 
416 
417 static char *
ngx_http_realip(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)418 ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
419 {
420     ngx_http_realip_loc_conf_t *rlcf = conf;
421 
422     ngx_str_t  *value;
423 
424     if (rlcf->type != NGX_CONF_UNSET_UINT) {
425         return "is duplicate";
426     }
427 
428     value = cf->args->elts;
429 
430     if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
431         rlcf->type = NGX_HTTP_REALIP_XREALIP;
432         return NGX_CONF_OK;
433     }
434 
435     if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
436         rlcf->type = NGX_HTTP_REALIP_XFWD;
437         return NGX_CONF_OK;
438     }
439 
440     if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
441         rlcf->type = NGX_HTTP_REALIP_PROXY;
442         return NGX_CONF_OK;
443     }
444 
445     rlcf->type = NGX_HTTP_REALIP_HEADER;
446     rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
447     rlcf->header = value[1];
448 
449     return NGX_CONF_OK;
450 }
451 
452 
453 static void *
ngx_http_realip_create_loc_conf(ngx_conf_t * cf)454 ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
455 {
456     ngx_http_realip_loc_conf_t  *conf;
457 
458     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
459     if (conf == NULL) {
460         return NULL;
461     }
462 
463     /*
464      * set by ngx_pcalloc():
465      *
466      *     conf->from = NULL;
467      *     conf->hash = 0;
468      *     conf->header = { 0, NULL };
469      */
470 
471     conf->type = NGX_CONF_UNSET_UINT;
472     conf->recursive = NGX_CONF_UNSET;
473 
474     return conf;
475 }
476 
477 
478 static char *
ngx_http_realip_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)479 ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
480 {
481     ngx_http_realip_loc_conf_t  *prev = parent;
482     ngx_http_realip_loc_conf_t  *conf = child;
483 
484     if (conf->from == NULL) {
485         conf->from = prev->from;
486     }
487 
488     ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
489     ngx_conf_merge_value(conf->recursive, prev->recursive, 0);
490 
491     if (conf->header.len == 0) {
492         conf->hash = prev->hash;
493         conf->header = prev->header;
494     }
495 
496     return NGX_CONF_OK;
497 }
498 
499 
500 static ngx_int_t
ngx_http_realip_add_variables(ngx_conf_t * cf)501 ngx_http_realip_add_variables(ngx_conf_t *cf)
502 {
503     ngx_http_variable_t  *var, *v;
504 
505     for (v = ngx_http_realip_vars; v->name.len; v++) {
506         var = ngx_http_add_variable(cf, &v->name, v->flags);
507         if (var == NULL) {
508             return NGX_ERROR;
509         }
510 
511         var->get_handler = v->get_handler;
512         var->data = v->data;
513     }
514 
515     return NGX_OK;
516 }
517 
518 
519 static ngx_int_t
ngx_http_realip_init(ngx_conf_t * cf)520 ngx_http_realip_init(ngx_conf_t *cf)
521 {
522     ngx_http_handler_pt        *h;
523     ngx_http_core_main_conf_t  *cmcf;
524 
525     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
526 
527     h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
528     if (h == NULL) {
529         return NGX_ERROR;
530     }
531 
532     *h = ngx_http_realip_handler;
533 
534     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
535     if (h == NULL) {
536         return NGX_ERROR;
537     }
538 
539     *h = ngx_http_realip_handler;
540 
541     return NGX_OK;
542 }
543 
544 
545 static ngx_http_realip_ctx_t *
ngx_http_realip_get_module_ctx(ngx_http_request_t * r)546 ngx_http_realip_get_module_ctx(ngx_http_request_t *r)
547 {
548     ngx_pool_cleanup_t     *cln;
549     ngx_http_realip_ctx_t  *ctx;
550 
551     ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
552 
553     if (ctx == NULL && (r->internal || r->filter_finalize)) {
554 
555         /*
556          * if module context was reset, the original address
557          * can still be found in the cleanup handler
558          */
559 
560         for (cln = r->pool->cleanup; cln; cln = cln->next) {
561             if (cln->handler == ngx_http_realip_cleanup) {
562                 ctx = cln->data;
563                 break;
564             }
565         }
566     }
567 
568     return ctx;
569 }
570 
571 
572 static ngx_int_t
ngx_http_realip_remote_addr_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)573 ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
574     ngx_http_variable_value_t *v, uintptr_t data)
575 {
576     ngx_str_t              *addr_text;
577     ngx_http_realip_ctx_t  *ctx;
578 
579     ctx = ngx_http_realip_get_module_ctx(r);
580 
581     addr_text = ctx ? &ctx->addr_text : &r->connection->addr_text;
582 
583     v->len = addr_text->len;
584     v->valid = 1;
585     v->no_cacheable = 0;
586     v->not_found = 0;
587     v->data = addr_text->data;
588 
589     return NGX_OK;
590 }
591 
592 
593 static ngx_int_t
ngx_http_realip_remote_port_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)594 ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
595     ngx_http_variable_value_t *v, uintptr_t data)
596 {
597     ngx_uint_t              port;
598     struct sockaddr        *sa;
599     ngx_http_realip_ctx_t  *ctx;
600 
601     ctx = ngx_http_realip_get_module_ctx(r);
602 
603     sa = ctx ? ctx->sockaddr : r->connection->sockaddr;
604 
605     v->len = 0;
606     v->valid = 1;
607     v->no_cacheable = 0;
608     v->not_found = 0;
609 
610     v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
611     if (v->data == NULL) {
612         return NGX_ERROR;
613     }
614 
615     port = ngx_inet_get_port(sa);
616 
617     if (port > 0 && port < 65536) {
618         v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
619     }
620 
621     return NGX_OK;
622 }
623