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 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
228 found:
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
251 static ngx_int_t
ngx_http_realip_set_addr(ngx_http_request_t * r,ngx_addr_t * addr)252 ngx_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
300 static void
ngx_http_realip_cleanup(void * data)301 ngx_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
315 static char *
ngx_http_realip_from(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)316 ngx_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_url_t u;
323 ngx_cidr_t c, *cidr;
324 ngx_uint_t i;
325 struct sockaddr_in *sin;
326 #if (NGX_HAVE_INET6)
327 struct sockaddr_in6 *sin6;
328 #endif
329
330 value = cf->args->elts;
331
332 if (rlcf->from == NULL) {
333 rlcf->from = ngx_array_create(cf->pool, 2,
334 sizeof(ngx_cidr_t));
335 if (rlcf->from == NULL) {
336 return NGX_CONF_ERROR;
337 }
338 }
339
340 #if (NGX_HAVE_UNIX_DOMAIN)
341
342 if (ngx_strcmp(value[1].data, "unix:") == 0) {
343 cidr = ngx_array_push(rlcf->from);
344 if (cidr == NULL) {
345 return NGX_CONF_ERROR;
346 }
347
348 cidr->family = AF_UNIX;
349 return NGX_CONF_OK;
350 }
351
352 #endif
353
354 rc = ngx_ptocidr(&value[1], &c);
355
356 if (rc != NGX_ERROR) {
357 if (rc == NGX_DONE) {
358 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
359 "low address bits of %V are meaningless",
360 &value[1]);
361 }
362
363 cidr = ngx_array_push(rlcf->from);
364 if (cidr == NULL) {
365 return NGX_CONF_ERROR;
366 }
367
368 *cidr = c;
369
370 return NGX_CONF_OK;
371 }
372
373 ngx_memzero(&u, sizeof(ngx_url_t));
374 u.host = value[1];
375
376 if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
377 if (u.err) {
378 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
379 "%s in set_real_ip_from \"%V\"",
380 u.err, &u.host);
381 }
382
383 return NGX_CONF_ERROR;
384 }
385
386 cidr = ngx_array_push_n(rlcf->from, u.naddrs);
387 if (cidr == NULL) {
388 return NGX_CONF_ERROR;
389 }
390
391 ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));
392
393 for (i = 0; i < u.naddrs; i++) {
394 cidr[i].family = u.addrs[i].sockaddr->sa_family;
395
396 switch (cidr[i].family) {
397
398 #if (NGX_HAVE_INET6)
399 case AF_INET6:
400 sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
401 cidr[i].u.in6.addr = sin6->sin6_addr;
402 ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
403 break;
404 #endif
405
406 default: /* AF_INET */
407 sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
408 cidr[i].u.in.addr = sin->sin_addr.s_addr;
409 cidr[i].u.in.mask = 0xffffffff;
410 break;
411 }
412 }
413
414 return NGX_CONF_OK;
415 }
416
417
418 static char *
ngx_http_realip(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)419 ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
420 {
421 ngx_http_realip_loc_conf_t *rlcf = conf;
422
423 ngx_str_t *value;
424
425 if (rlcf->type != NGX_CONF_UNSET_UINT) {
426 return "is duplicate";
427 }
428
429 value = cf->args->elts;
430
431 if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
432 rlcf->type = NGX_HTTP_REALIP_XREALIP;
433 return NGX_CONF_OK;
434 }
435
436 if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
437 rlcf->type = NGX_HTTP_REALIP_XFWD;
438 return NGX_CONF_OK;
439 }
440
441 if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
442 rlcf->type = NGX_HTTP_REALIP_PROXY;
443 return NGX_CONF_OK;
444 }
445
446 rlcf->type = NGX_HTTP_REALIP_HEADER;
447 rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
448 rlcf->header = value[1];
449
450 return NGX_CONF_OK;
451 }
452
453
454 static void *
ngx_http_realip_create_loc_conf(ngx_conf_t * cf)455 ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
456 {
457 ngx_http_realip_loc_conf_t *conf;
458
459 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
460 if (conf == NULL) {
461 return NULL;
462 }
463
464 /*
465 * set by ngx_pcalloc():
466 *
467 * conf->from = NULL;
468 * conf->hash = 0;
469 * conf->header = { 0, NULL };
470 */
471
472 conf->type = NGX_CONF_UNSET_UINT;
473 conf->recursive = NGX_CONF_UNSET;
474
475 return conf;
476 }
477
478
479 static char *
ngx_http_realip_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)480 ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
481 {
482 ngx_http_realip_loc_conf_t *prev = parent;
483 ngx_http_realip_loc_conf_t *conf = child;
484
485 if (conf->from == NULL) {
486 conf->from = prev->from;
487 }
488
489 ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
490 ngx_conf_merge_value(conf->recursive, prev->recursive, 0);
491
492 if (conf->header.len == 0) {
493 conf->hash = prev->hash;
494 conf->header = prev->header;
495 }
496
497 return NGX_CONF_OK;
498 }
499
500
501 static ngx_int_t
ngx_http_realip_add_variables(ngx_conf_t * cf)502 ngx_http_realip_add_variables(ngx_conf_t *cf)
503 {
504 ngx_http_variable_t *var, *v;
505
506 for (v = ngx_http_realip_vars; v->name.len; v++) {
507 var = ngx_http_add_variable(cf, &v->name, v->flags);
508 if (var == NULL) {
509 return NGX_ERROR;
510 }
511
512 var->get_handler = v->get_handler;
513 var->data = v->data;
514 }
515
516 return NGX_OK;
517 }
518
519
520 static ngx_int_t
ngx_http_realip_init(ngx_conf_t * cf)521 ngx_http_realip_init(ngx_conf_t *cf)
522 {
523 ngx_http_handler_pt *h;
524 ngx_http_core_main_conf_t *cmcf;
525
526 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
527
528 h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
529 if (h == NULL) {
530 return NGX_ERROR;
531 }
532
533 *h = ngx_http_realip_handler;
534
535 h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
536 if (h == NULL) {
537 return NGX_ERROR;
538 }
539
540 *h = ngx_http_realip_handler;
541
542 return NGX_OK;
543 }
544
545
546 static ngx_http_realip_ctx_t *
ngx_http_realip_get_module_ctx(ngx_http_request_t * r)547 ngx_http_realip_get_module_ctx(ngx_http_request_t *r)
548 {
549 ngx_pool_cleanup_t *cln;
550 ngx_http_realip_ctx_t *ctx;
551
552 ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
553
554 if (ctx == NULL && (r->internal || r->filter_finalize)) {
555
556 /*
557 * if module context was reset, the original address
558 * can still be found in the cleanup handler
559 */
560
561 for (cln = r->pool->cleanup; cln; cln = cln->next) {
562 if (cln->handler == ngx_http_realip_cleanup) {
563 ctx = cln->data;
564 break;
565 }
566 }
567 }
568
569 return ctx;
570 }
571
572
573 static ngx_int_t
ngx_http_realip_remote_addr_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)574 ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,
575 ngx_http_variable_value_t *v, uintptr_t data)
576 {
577 ngx_str_t *addr_text;
578 ngx_http_realip_ctx_t *ctx;
579
580 ctx = ngx_http_realip_get_module_ctx(r);
581
582 addr_text = ctx ? &ctx->addr_text : &r->connection->addr_text;
583
584 v->len = addr_text->len;
585 v->valid = 1;
586 v->no_cacheable = 0;
587 v->not_found = 0;
588 v->data = addr_text->data;
589
590 return NGX_OK;
591 }
592
593
594 static ngx_int_t
ngx_http_realip_remote_port_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)595 ngx_http_realip_remote_port_variable(ngx_http_request_t *r,
596 ngx_http_variable_value_t *v, uintptr_t data)
597 {
598 ngx_uint_t port;
599 struct sockaddr *sa;
600 ngx_http_realip_ctx_t *ctx;
601
602 ctx = ngx_http_realip_get_module_ctx(r);
603
604 sa = ctx ? ctx->sockaddr : r->connection->sockaddr;
605
606 v->len = 0;
607 v->valid = 1;
608 v->no_cacheable = 0;
609 v->not_found = 0;
610
611 v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
612 if (v->data == NULL) {
613 return NGX_ERROR;
614 }
615
616 port = ngx_inet_get_port(sa);
617
618 if (port > 0 && port < 65536) {
619 v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
620 }
621
622 return NGX_OK;
623 }
624