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