b069a1ebaef2879ea56a13da2ebbb8a6d144ff5e
[nginx.git] / nginx / src / http / modules / ngx_http_realip_module.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 #define NGX_HTTP_REALIP_XREALIP  0
13 #define NGX_HTTP_REALIP_XFWD     1
14 #define NGX_HTTP_REALIP_HEADER   2
15
16
17 typedef struct {
18     in_addr_t          mask;
19     in_addr_t          addr;
20 } ngx_http_realip_from_t;
21
22
23 typedef struct {
24     ngx_array_t       *from;     /* array of ngx_http_realip_from_t */
25     ngx_uint_t         type;
26     ngx_uint_t         hash;
27     ngx_str_t          header;
28 } ngx_http_realip_loc_conf_t;
29
30
31 typedef struct {
32     ngx_connection_t  *connection;
33     in_addr_t          addr;
34     ngx_str_t          addr_text;
35 } ngx_http_realip_ctx_t;
36
37
38 static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
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_init(ngx_conf_t *cf);
47
48
49 static ngx_command_t  ngx_http_realip_commands[] = {
50
51     { ngx_string("set_real_ip_from"),
52       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
53       ngx_http_realip_from,
54       NGX_HTTP_LOC_CONF_OFFSET,
55       0,
56       NULL },
57
58     { ngx_string("real_ip_header"),
59       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
60       ngx_http_realip,
61       NGX_HTTP_LOC_CONF_OFFSET,
62       0,
63       NULL },
64
65       ngx_null_command
66 };
67
68
69
70 static ngx_http_module_t  ngx_http_realip_module_ctx = {
71     NULL,                                  /* preconfiguration */
72     ngx_http_realip_init,                  /* postconfiguration */
73
74     NULL,                                  /* create main configuration */
75     NULL,                                  /* init main configuration */
76
77     NULL,                                  /* create server configuration */
78     NULL,                                  /* merge server configuration */
79
80     ngx_http_realip_create_loc_conf,       /* create location configuration */
81     ngx_http_realip_merge_loc_conf         /* merge location configuration */
82 };
83
84
85 ngx_module_t  ngx_http_realip_module = {
86     NGX_MODULE_V1,
87     &ngx_http_realip_module_ctx,           /* module context */
88     ngx_http_realip_commands,              /* module directives */
89     NGX_HTTP_MODULE,                       /* module type */
90     NULL,                                  /* init master */
91     NULL,                                  /* init module */
92     NULL,                                  /* init process */
93     NULL,                                  /* init thread */
94     NULL,                                  /* exit thread */
95     NULL,                                  /* exit process */
96     NULL,                                  /* exit master */
97     NGX_MODULE_V1_PADDING
98 };
99
100
101 static ngx_int_t
102 ngx_http_realip_handler(ngx_http_request_t *r)
103 {
104     u_char                      *ip, *p;
105     size_t                       len;
106     in_addr_t                    addr;
107     ngx_uint_t                   i, hash;
108     ngx_list_part_t             *part;
109     ngx_table_elt_t             *header;
110     struct sockaddr_in          *sin;
111     ngx_connection_t            *c;
112     ngx_pool_cleanup_t          *cln;
113     ngx_http_realip_ctx_t       *ctx;
114     ngx_http_realip_from_t      *from;
115     ngx_http_realip_loc_conf_t  *rlcf;
116
117     ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
118
119     if (ctx) {
120         return NGX_DECLINED;
121     }
122
123     cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));
124     if (cln == NULL) {
125         return NGX_HTTP_INTERNAL_SERVER_ERROR;
126     }
127
128     rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);
129
130     if (rlcf->from == NULL) {
131         return NGX_DECLINED;
132     }
133
134     switch (rlcf->type) {
135
136     case NGX_HTTP_REALIP_XREALIP:
137
138         if (r->headers_in.x_real_ip == NULL) {
139             return NGX_DECLINED;
140         }
141
142         len = r->headers_in.x_real_ip->value.len;
143         ip = r->headers_in.x_real_ip->value.data;
144
145         break;
146
147     case NGX_HTTP_REALIP_XFWD:
148
149         if (r->headers_in.x_forwarded_for == NULL) {
150             return NGX_DECLINED;
151         }
152
153         len = r->headers_in.x_forwarded_for->value.len;
154         ip = r->headers_in.x_forwarded_for->value.data;
155
156         for (p = ip + len - 1; p > ip; p--) {
157             if (*p == ' ' || *p == ',') {
158                 p++;
159                 len -= p - ip;
160                 ip = p;
161                 break;
162             }
163         }
164
165         break;
166
167     default: /* NGX_HTTP_REALIP_HEADER */
168
169         part = &r->headers_in.headers.part;
170         header = part->elts;
171
172         hash = rlcf->hash;
173         len = rlcf->header.len;
174         p = rlcf->header.data;
175
176         for (i = 0; /* void */ ; i++) {
177
178             if (i >= part->nelts) {
179                 if (part->next == NULL) {
180                     break;
181                 }
182
183                 part = part->next;
184                 header = part->elts;
185                 i = 0;
186             }
187
188             if (hash == header[i].hash
189                 && len == header[i].key.len
190                 && ngx_strncmp(p, header[i].lowcase_key, len) == 0)
191             {
192                 len = header[i].value.len;
193                 ip = header[i].value.data;
194
195                 goto found;
196             }
197         }
198
199         return NGX_DECLINED;
200     }
201
202 found:
203
204     c = r->connection;
205
206     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "realip: \"%s\"", ip);
207
208     /* AF_INET only */
209
210     if (r->connection->sockaddr->sa_family != AF_INET) {
211         return NGX_DECLINED;
212     }
213
214     sin = (struct sockaddr_in *) c->sockaddr;
215
216     from = rlcf->from->elts;
217     for (i = 0; i < rlcf->from->nelts; i++) {
218
219         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
220                        "realip: %08XD %08XD %08XD",
221                        sin->sin_addr.s_addr, from[i].mask, from[i].addr);
222
223         if ((sin->sin_addr.s_addr & from[i].mask) == from[i].addr) {
224
225             ctx = cln->data;
226
227             ngx_http_set_ctx(r, ctx, ngx_http_realip_module);
228
229             addr = inet_addr((char *) ip);
230
231             if (addr == INADDR_NONE) {
232                 return NGX_DECLINED;
233             }
234
235             p = ngx_pnalloc(c->pool, len);
236             if (p == NULL) {
237                 return NGX_HTTP_INTERNAL_SERVER_ERROR;
238             }
239
240             ngx_memcpy(p, ip, len);
241
242             cln->handler = ngx_http_realip_cleanup;
243
244             ctx->connection = c;
245             ctx->addr = sin->sin_addr.s_addr;
246             ctx->addr_text = c->addr_text;
247
248             sin->sin_addr.s_addr = addr;
249
250             c->addr_text.len = len;
251             c->addr_text.data = p;
252
253             return NGX_DECLINED;
254         }
255     }
256
257     return NGX_DECLINED;
258 }
259
260
261 static void
262 ngx_http_realip_cleanup(void *data)
263 {
264     ngx_http_realip_ctx_t *ctx = data;
265
266     ngx_connection_t    *c;
267     struct sockaddr_in  *sin;
268
269     c = ctx->connection;
270
271     sin = (struct sockaddr_in *) c->sockaddr;
272     sin->sin_addr.s_addr = ctx->addr;
273
274     c->addr_text = ctx->addr_text;
275 }
276
277
278 static char *
279 ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
280 {
281     ngx_http_realip_loc_conf_t *rlcf = conf;
282
283     ngx_int_t                rc;
284     ngx_str_t               *value;
285     ngx_cidr_t               cidr;
286     ngx_http_realip_from_t  *from;
287
288     if (rlcf->from == NULL) {
289         rlcf->from = ngx_array_create(cf->pool, 2,
290                                       sizeof(ngx_http_realip_from_t));
291         if (rlcf->from == NULL) {
292             return NGX_CONF_ERROR;
293         }
294     }
295
296     from = ngx_array_push(rlcf->from);
297     if (from == NULL) {
298         return NGX_CONF_ERROR;
299     }
300
301     value = cf->args->elts;
302
303     rc = ngx_ptocidr(&value[1], &cidr);
304
305     if (rc == NGX_ERROR) {
306         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
307                            &value[1]);
308         return NGX_CONF_ERROR;
309     }
310
311     if (cidr.family != AF_INET) {
312         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
313                            "\"realip_from\" supports IPv4 only");
314         return NGX_CONF_ERROR;
315     }
316
317     if (rc == NGX_DONE) {
318         ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
319                            "low address bits of %V are meaningless", &value[1]);
320     }
321
322     from->mask = cidr.u.in.mask;
323     from->addr = cidr.u.in.addr;
324
325     return NGX_CONF_OK;
326 }
327
328
329 static char *
330 ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
331 {
332     ngx_http_realip_loc_conf_t *rlcf = conf;
333
334     ngx_str_t  *value;
335
336     value = cf->args->elts;
337
338     if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
339         rlcf->type = NGX_HTTP_REALIP_XREALIP;
340         return NGX_CONF_OK;
341     }
342
343     if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
344         rlcf->type = NGX_HTTP_REALIP_XFWD;
345         return NGX_CONF_OK;
346     }
347
348     rlcf->type = NGX_HTTP_REALIP_HEADER;
349     rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
350     rlcf->header = value[1];
351
352     return NGX_CONF_OK;
353 }
354
355
356 static void *
357 ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
358 {
359     ngx_http_realip_loc_conf_t  *conf;
360
361     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
362     if (conf == NULL) {
363         return NGX_CONF_ERROR;
364     }
365
366     /*
367      * set by ngx_pcalloc():
368      *
369      *     conf->from = NULL;
370      *     conf->hash = 0;
371      *     conf->header = { 0, NULL };
372      */
373
374     conf->type = NGX_CONF_UNSET_UINT;
375
376     return conf;
377 }
378
379
380 static char *
381 ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
382 {
383     ngx_http_realip_loc_conf_t  *prev = parent;
384     ngx_http_realip_loc_conf_t  *conf = child;
385
386     if (conf->from == NULL) {
387         conf->from = prev->from;
388     }
389
390     ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
391
392     if (conf->header.len == 0) {
393         conf->hash = prev->hash;
394         conf->header = prev->header;
395     }
396
397     return NGX_CONF_OK;
398 }
399
400
401 static ngx_int_t
402 ngx_http_realip_init(ngx_conf_t *cf)
403 {
404     ngx_http_handler_pt        *h;
405     ngx_http_core_main_conf_t  *cmcf;
406
407     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
408
409     h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
410     if (h == NULL) {
411         return NGX_ERROR;
412     }
413
414     *h = ngx_http_realip_handler;
415
416     h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
417     if (h == NULL) {
418         return NGX_ERROR;
419     }
420
421     *h = ngx_http_realip_handler;
422
423     return NGX_OK;
424 }