1e18a033bSKonstantin Ananyev
2e18a033bSKonstantin Ananyev/*
3e18a033bSKonstantin Ananyev * Copyright (C) Igor Sysoev
4e18a033bSKonstantin Ananyev * Copyright (C) Nginx, Inc.
5e18a033bSKonstantin Ananyev */
6e18a033bSKonstantin Ananyev
7e18a033bSKonstantin Ananyev
8e18a033bSKonstantin Ananyev#include <ngx_config.h>
9e18a033bSKonstantin Ananyev#include <ngx_core.h>
10e18a033bSKonstantin Ananyev#include <ngx_stream.h>
11e18a033bSKonstantin Ananyev
12e18a033bSKonstantin Ananyev
13e18a033bSKonstantin Ananyevtypedef struct {
14e18a033bSKonstantin Ananyev    ngx_array_t       *from;     /* array of ngx_cidr_t */
15e18a033bSKonstantin Ananyev} ngx_stream_realip_srv_conf_t;
16e18a033bSKonstantin Ananyev
17e18a033bSKonstantin Ananyev
18e18a033bSKonstantin Ananyevtypedef struct {
19e18a033bSKonstantin Ananyev    struct sockaddr   *sockaddr;
20e18a033bSKonstantin Ananyev    socklen_t          socklen;
21e18a033bSKonstantin Ananyev    ngx_str_t          addr_text;
22e18a033bSKonstantin Ananyev} ngx_stream_realip_ctx_t;
23e18a033bSKonstantin Ananyev
24e18a033bSKonstantin Ananyev
25e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_stream_realip_handler(ngx_stream_session_t *s);
26e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_stream_realip_set_addr(ngx_stream_session_t *s,
27e18a033bSKonstantin Ananyev    ngx_addr_t *addr);
28e18a033bSKonstantin Ananyevstatic char *ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
29e18a033bSKonstantin Ananyev    void *conf);
30e18a033bSKonstantin Ananyevstatic void *ngx_stream_realip_create_srv_conf(ngx_conf_t *cf);
31e18a033bSKonstantin Ananyevstatic char *ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent,
32e18a033bSKonstantin Ananyev    void *child);
33e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_stream_realip_add_variables(ngx_conf_t *cf);
34e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_stream_realip_init(ngx_conf_t *cf);
35e18a033bSKonstantin Ananyev
36e18a033bSKonstantin Ananyev
37e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s,
38e18a033bSKonstantin Ananyev    ngx_stream_variable_value_t *v, uintptr_t data);
39e18a033bSKonstantin Ananyevstatic ngx_int_t ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s,
40e18a033bSKonstantin Ananyev    ngx_stream_variable_value_t *v, uintptr_t data);
41e18a033bSKonstantin Ananyev
42e18a033bSKonstantin Ananyev
43e18a033bSKonstantin Ananyevstatic ngx_command_t  ngx_stream_realip_commands[] = {
44e18a033bSKonstantin Ananyev
45e18a033bSKonstantin Ananyev    { ngx_string("set_real_ip_from"),
46e18a033bSKonstantin Ananyev      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
47e18a033bSKonstantin Ananyev      ngx_stream_realip_from,
48e18a033bSKonstantin Ananyev      NGX_STREAM_SRV_CONF_OFFSET,
49e18a033bSKonstantin Ananyev      0,
50e18a033bSKonstantin Ananyev      NULL },
51e18a033bSKonstantin Ananyev
52e18a033bSKonstantin Ananyev      ngx_null_command
53e18a033bSKonstantin Ananyev};
54e18a033bSKonstantin Ananyev
55e18a033bSKonstantin Ananyev
56e18a033bSKonstantin Ananyevstatic ngx_stream_module_t  ngx_stream_realip_module_ctx = {
57e18a033bSKonstantin Ananyev    ngx_stream_realip_add_variables,       /* preconfiguration */
58e18a033bSKonstantin Ananyev    ngx_stream_realip_init,                /* postconfiguration */
59e18a033bSKonstantin Ananyev
60e18a033bSKonstantin Ananyev    NULL,                                  /* create main configuration */
61e18a033bSKonstantin Ananyev    NULL,                                  /* init main configuration */
62e18a033bSKonstantin Ananyev
63e18a033bSKonstantin Ananyev    ngx_stream_realip_create_srv_conf,     /* create server configuration */
64e18a033bSKonstantin Ananyev    ngx_stream_realip_merge_srv_conf       /* merge server configuration */
65e18a033bSKonstantin Ananyev};
66e18a033bSKonstantin Ananyev
67e18a033bSKonstantin Ananyev
68e18a033bSKonstantin Ananyevngx_module_t  ngx_stream_realip_module = {
69e18a033bSKonstantin Ananyev    NGX_MODULE_V1,
70e18a033bSKonstantin Ananyev    &ngx_stream_realip_module_ctx,         /* module context */
71e18a033bSKonstantin Ananyev    ngx_stream_realip_commands,            /* module directives */
72e18a033bSKonstantin Ananyev    NGX_STREAM_MODULE,                     /* module type */
73e18a033bSKonstantin Ananyev    NULL,                                  /* init master */
74e18a033bSKonstantin Ananyev    NULL,                                  /* init module */
75e18a033bSKonstantin Ananyev    NULL,                                  /* init process */
76e18a033bSKonstantin Ananyev    NULL,                                  /* init thread */
77e18a033bSKonstantin Ananyev    NULL,                                  /* exit thread */
78e18a033bSKonstantin Ananyev    NULL,                                  /* exit process */
79e18a033bSKonstantin Ananyev    NULL,                                  /* exit master */
80e18a033bSKonstantin Ananyev    NGX_MODULE_V1_PADDING
81e18a033bSKonstantin Ananyev};
82e18a033bSKonstantin Ananyev
83e18a033bSKonstantin Ananyev
84e18a033bSKonstantin Ananyevstatic ngx_stream_variable_t  ngx_stream_realip_vars[] = {
85e18a033bSKonstantin Ananyev
86e18a033bSKonstantin Ananyev    { ngx_string("realip_remote_addr"), NULL,
87e18a033bSKonstantin Ananyev      ngx_stream_realip_remote_addr_variable, 0, 0, 0 },
88e18a033bSKonstantin Ananyev
89e18a033bSKonstantin Ananyev    { ngx_string("realip_remote_port"), NULL,
90e18a033bSKonstantin Ananyev      ngx_stream_realip_remote_port_variable, 0, 0, 0 },
91e18a033bSKonstantin Ananyev
92e18a033bSKonstantin Ananyev    { ngx_null_string, NULL, NULL, 0, 0, 0 }
93e18a033bSKonstantin Ananyev};
94e18a033bSKonstantin Ananyev
95e18a033bSKonstantin Ananyev
96e18a033bSKonstantin Ananyevstatic ngx_int_t
97e18a033bSKonstantin Ananyevngx_stream_realip_handler(ngx_stream_session_t *s)
98e18a033bSKonstantin Ananyev{
99e18a033bSKonstantin Ananyev    ngx_addr_t                     addr;
100e18a033bSKonstantin Ananyev    ngx_connection_t              *c;
101e18a033bSKonstantin Ananyev    ngx_stream_realip_srv_conf_t  *rscf;
102e18a033bSKonstantin Ananyev
103e18a033bSKonstantin Ananyev    rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_realip_module);
104e18a033bSKonstantin Ananyev
105e18a033bSKonstantin Ananyev    if (rscf->from == NULL) {
106e18a033bSKonstantin Ananyev        return NGX_DECLINED;
107e18a033bSKonstantin Ananyev    }
108e18a033bSKonstantin Ananyev
109e18a033bSKonstantin Ananyev    c = s->connection;
110e18a033bSKonstantin Ananyev
111e18a033bSKonstantin Ananyev    if (c->proxy_protocol_addr.len == 0) {
112e18a033bSKonstantin Ananyev        return NGX_DECLINED;
113e18a033bSKonstantin Ananyev    }
114e18a033bSKonstantin Ananyev
115e18a033bSKonstantin Ananyev    if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) {
116e18a033bSKonstantin Ananyev        return NGX_DECLINED;
117e18a033bSKonstantin Ananyev    }
118e18a033bSKonstantin Ananyev
119e18a033bSKonstantin Ananyev    if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol_addr.data,
120e18a033bSKonstantin Ananyev                       c->proxy_protocol_addr.len)
121e18a033bSKonstantin Ananyev        != NGX_OK)
122e18a033bSKonstantin Ananyev    {
123e18a033bSKonstantin Ananyev        return NGX_DECLINED;
124e18a033bSKonstantin Ananyev    }
125e18a033bSKonstantin Ananyev
126e18a033bSKonstantin Ananyev    ngx_inet_set_port(addr.sockaddr, c->proxy_protocol_port);
127e18a033bSKonstantin Ananyev
128e18a033bSKonstantin Ananyev    return ngx_stream_realip_set_addr(s, &addr);
129e18a033bSKonstantin Ananyev}
130e18a033bSKonstantin Ananyev
131e18a033bSKonstantin Ananyev
132e18a033bSKonstantin Ananyevstatic ngx_int_t
133e18a033bSKonstantin Ananyevngx_stream_realip_set_addr(ngx_stream_session_t *s, ngx_addr_t *addr)
134e18a033bSKonstantin Ananyev{
135e18a033bSKonstantin Ananyev    size_t                    len;
136e18a033bSKonstantin Ananyev    u_char                   *p;
137e18a033bSKonstantin Ananyev    u_char                    text[NGX_SOCKADDR_STRLEN];
138e18a033bSKonstantin Ananyev    ngx_connection_t         *c;
139e18a033bSKonstantin Ananyev    ngx_stream_realip_ctx_t  *ctx;
140e18a033bSKonstantin Ananyev
141e18a033bSKonstantin Ananyev    c = s->connection;
142e18a033bSKonstantin Ananyev
143e18a033bSKonstantin Ananyev    ctx = ngx_palloc(c->pool, sizeof(ngx_stream_realip_ctx_t));
144e18a033bSKonstantin Ananyev    if (ctx == NULL) {
145e18a033bSKonstantin Ananyev        return NGX_ERROR;
146e18a033bSKonstantin Ananyev    }
147e18a033bSKonstantin Ananyev
148e18a033bSKonstantin Ananyev    len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
149e18a033bSKonstantin Ananyev                        NGX_SOCKADDR_STRLEN, 0);
150e18a033bSKonstantin Ananyev    if (len == 0) {
151e18a033bSKonstantin Ananyev        return NGX_ERROR;
152e18a033bSKonstantin Ananyev    }
153e18a033bSKonstantin Ananyev
154e18a033bSKonstantin Ananyev    p = ngx_pnalloc(c->pool, len);
155e18a033bSKonstantin Ananyev    if (p == NULL) {
156e18a033bSKonstantin Ananyev        return NGX_ERROR;
157e18a033bSKonstantin Ananyev    }
158e18a033bSKonstantin Ananyev
159e18a033bSKonstantin Ananyev    ngx_memcpy(p, text, len);
160e18a033bSKonstantin Ananyev
161e18a033bSKonstantin Ananyev    ngx_stream_set_ctx(s, ctx, ngx_stream_realip_module);
162e18a033bSKonstantin Ananyev
163e18a033bSKonstantin Ananyev    ctx->sockaddr = c->sockaddr;
164e18a033bSKonstantin Ananyev    ctx->socklen = c->socklen;
165e18a033bSKonstantin Ananyev    ctx->addr_text = c->addr_text;
166e18a033bSKonstantin Ananyev
167e18a033bSKonstantin Ananyev    c->sockaddr = addr->sockaddr;
168e18a033bSKonstantin Ananyev    c->socklen = addr->socklen;
169e18a033bSKonstantin Ananyev    c->addr_text.len = len;
170e18a033bSKonstantin Ananyev    c->addr_text.data = p;
171e18a033bSKonstantin Ananyev
172e18a033bSKonstantin Ananyev    return NGX_DECLINED;
173e18a033bSKonstantin Ananyev}
174e18a033bSKonstantin Ananyev
175e18a033bSKonstantin Ananyev
176e18a033bSKonstantin Ananyevstatic char *
177e18a033bSKonstantin Ananyevngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
178e18a033bSKonstantin Ananyev{
179e18a033bSKonstantin Ananyev    ngx_stream_realip_srv_conf_t *rscf = conf;
180e18a033bSKonstantin Ananyev
181e18a033bSKonstantin Ananyev    ngx_int_t                rc;
182e18a033bSKonstantin Ananyev    ngx_str_t               *value;
183e18a033bSKonstantin Ananyev    ngx_cidr_t              *cidr;
184e18a033bSKonstantin Ananyev
185e18a033bSKonstantin Ananyev    value = cf->args->elts;
186e18a033bSKonstantin Ananyev
187e18a033bSKonstantin Ananyev    if (rscf->from == NULL) {
188e18a033bSKonstantin Ananyev        rscf->from = ngx_array_create(cf->pool, 2,
189e18a033bSKonstantin Ananyev                                      sizeof(ngx_cidr_t));
190e18a033bSKonstantin Ananyev        if (rscf->from == NULL) {
191e18a033bSKonstantin Ananyev            return NGX_CONF_ERROR;
192e18a033bSKonstantin Ananyev        }
193e18a033bSKonstantin Ananyev    }
194e18a033bSKonstantin Ananyev
195e18a033bSKonstantin Ananyev    cidr = ngx_array_push(rscf->from);
196e18a033bSKonstantin Ananyev    if (cidr == NULL) {
197e18a033bSKonstantin Ananyev        return NGX_CONF_ERROR;
198e18a033bSKonstantin Ananyev    }
199e18a033bSKonstantin Ananyev
200e18a033bSKonstantin Ananyev#if (NGX_HAVE_UNIX_DOMAIN)
201e18a033bSKonstantin Ananyev
202e18a033bSKonstantin Ananyev    if (ngx_strcmp(value[1].data, "unix:") == 0) {
203e18a033bSKonstantin Ananyev        cidr->family = AF_UNIX;
204e18a033bSKonstantin Ananyev        return NGX_CONF_OK;
205e18a033bSKonstantin Ananyev    }
206e18a033bSKonstantin Ananyev
207e18a033bSKonstantin Ananyev#endif
208e18a033bSKonstantin Ananyev
209e18a033bSKonstantin Ananyev    rc = ngx_ptocidr(&value[1], cidr);
210e18a033bSKonstantin Ananyev
211e18a033bSKonstantin Ananyev    if (rc == NGX_ERROR) {
212e18a033bSKonstantin Ananyev        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
213e18a033bSKonstantin Ananyev                           &value[1]);
214e18a033bSKonstantin Ananyev        return NGX_CONF_ERROR;
215e18a033bSKonstantin Ananyev    }
216e18a033bSKonstantin Ananyev
217e18a033bSKonstantin Ananyev    if (rc == NGX_DONE) {
218e18a033bSKonstantin Ananyev        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
219e18a033bSKonstantin Ananyev                           "low address bits of %V are meaningless", &value[1]);
220e18a033bSKonstantin Ananyev    }
221e18a033bSKonstantin Ananyev
222e18a033bSKonstantin Ananyev    return NGX_CONF_OK;
223e18a033bSKonstantin Ananyev}
224e18a033bSKonstantin Ananyev
225e18a033bSKonstantin Ananyev
226e18a033bSKonstantin Ananyevstatic void *
227e18a033bSKonstantin Ananyevngx_stream_realip_create_srv_conf(ngx_conf_t *cf)
228e18a033bSKonstantin Ananyev{
229e18a033bSKonstantin Ananyev    ngx_stream_realip_srv_conf_t  *conf;
230e18a033bSKonstantin Ananyev
231e18a033bSKonstantin Ananyev    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_realip_srv_conf_t));
232e18a033bSKonstantin Ananyev    if (conf == NULL) {
233e18a033bSKonstantin Ananyev        return NULL;
234e18a033bSKonstantin Ananyev    }
235e18a033bSKonstantin Ananyev
236e18a033bSKonstantin Ananyev    /*
237e18a033bSKonstantin Ananyev     * set by ngx_pcalloc():
238e18a033bSKonstantin Ananyev     *
239e18a033bSKonstantin Ananyev     *     conf->from = NULL;
240e18a033bSKonstantin Ananyev     */
241e18a033bSKonstantin Ananyev
242e18a033bSKonstantin Ananyev    return conf;
243e18a033bSKonstantin Ananyev}
244e18a033bSKonstantin Ananyev
245e18a033bSKonstantin Ananyev
246e18a033bSKonstantin Ananyevstatic char *
247e18a033bSKonstantin Ananyevngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
248e18a033bSKonstantin Ananyev{
249e18a033bSKonstantin Ananyev    ngx_stream_realip_srv_conf_t *prev = parent;
250e18a033bSKonstantin Ananyev    ngx_stream_realip_srv_conf_t *conf = child;
251e18a033bSKonstantin Ananyev
252e18a033bSKonstantin Ananyev    if (conf->from == NULL) {
253e18a033bSKonstantin Ananyev        conf->from = prev->from;
254e18a033bSKonstantin Ananyev    }
255e18a033bSKonstantin Ananyev
256e18a033bSKonstantin Ananyev    return NGX_CONF_OK;
257e18a033bSKonstantin Ananyev}
258e18a033bSKonstantin Ananyev
259e18a033bSKonstantin Ananyev
260e18a033bSKonstantin Ananyevstatic ngx_int_t
261e18a033bSKonstantin Ananyevngx_stream_realip_add_variables(ngx_conf_t *cf)
262e18a033bSKonstantin Ananyev{
263e18a033bSKonstantin Ananyev    ngx_stream_variable_t  *var, *v;
264e18a033bSKonstantin Ananyev
265e18a033bSKonstantin Ananyev    for (v = ngx_stream_realip_vars; v->name.len; v++) {
266e18a033bSKonstantin Ananyev        var = ngx_stream_add_variable(cf, &v->name, v->flags);
267e18a033bSKonstantin Ananyev        if (var == NULL) {
268e18a033bSKonstantin Ananyev            return NGX_ERROR;
269e18a033bSKonstantin Ananyev        }
270e18a033bSKonstantin Ananyev
271e18a033bSKonstantin Ananyev        var->get_handler = v->get_handler;
272e18a033bSKonstantin Ananyev        var->data = v->data;
273e18a033bSKonstantin Ananyev    }
274e18a033bSKonstantin Ananyev
275e18a033bSKonstantin Ananyev    return NGX_OK;
276e18a033bSKonstantin Ananyev}
277e18a033bSKonstantin Ananyev
278e18a033bSKonstantin Ananyev
279e18a033bSKonstantin Ananyevstatic ngx_int_t
280e18a033bSKonstantin Ananyevngx_stream_realip_init(ngx_conf_t *cf)
281e18a033bSKonstantin Ananyev{
282e18a033bSKonstantin Ananyev    ngx_stream_handler_pt        *h;
283e18a033bSKonstantin Ananyev    ngx_stream_core_main_conf_t  *cmcf;
284e18a033bSKonstantin Ananyev
285e18a033bSKonstantin Ananyev    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
286e18a033bSKonstantin Ananyev
287e18a033bSKonstantin Ananyev    h = ngx_array_push(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers);
288e18a033bSKonstantin Ananyev    if (h == NULL) {
289e18a033bSKonstantin Ananyev        return NGX_ERROR;
290e18a033bSKonstantin Ananyev    }
291e18a033bSKonstantin Ananyev
292e18a033bSKonstantin Ananyev    *h = ngx_stream_realip_handler;
293e18a033bSKonstantin Ananyev
294e18a033bSKonstantin Ananyev    return NGX_OK;
295e18a033bSKonstantin Ananyev}
296e18a033bSKonstantin Ananyev
297e18a033bSKonstantin Ananyev
298e18a033bSKonstantin Ananyevstatic ngx_int_t
299e18a033bSKonstantin Ananyevngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s,
300e18a033bSKonstantin Ananyev    ngx_stream_variable_value_t *v, uintptr_t data)
301e18a033bSKonstantin Ananyev{
302e18a033bSKonstantin Ananyev    ngx_str_t                *addr_text;
303e18a033bSKonstantin Ananyev    ngx_stream_realip_ctx_t  *ctx;
304e18a033bSKonstantin Ananyev
305e18a033bSKonstantin Ananyev    ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module);
306e18a033bSKonstantin Ananyev
307e18a033bSKonstantin Ananyev    addr_text = ctx ? &ctx->addr_text : &s->connection->addr_text;
308e18a033bSKonstantin Ananyev
309e18a033bSKonstantin Ananyev    v->len = addr_text->len;
310e18a033bSKonstantin Ananyev    v->valid = 1;
311e18a033bSKonstantin Ananyev    v->no_cacheable = 0;
312e18a033bSKonstantin Ananyev    v->not_found = 0;
313e18a033bSKonstantin Ananyev    v->data = addr_text->data;
314e18a033bSKonstantin Ananyev
315e18a033bSKonstantin Ananyev    return NGX_OK;
316e18a033bSKonstantin Ananyev}
317e18a033bSKonstantin Ananyev
318e18a033bSKonstantin Ananyev
319e18a033bSKonstantin Ananyevstatic ngx_int_t
320e18a033bSKonstantin Ananyevngx_stream_realip_remote_port_variable(ngx_stream_session_t *s,
321e18a033bSKonstantin Ananyev    ngx_stream_variable_value_t *v, uintptr_t data)
322e18a033bSKonstantin Ananyev{
323e18a033bSKonstantin Ananyev    ngx_uint_t                port;
324e18a033bSKonstantin Ananyev    struct sockaddr          *sa;
325e18a033bSKonstantin Ananyev    ngx_stream_realip_ctx_t  *ctx;
326e18a033bSKonstantin Ananyev
327e18a033bSKonstantin Ananyev    ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module);
328e18a033bSKonstantin Ananyev
329e18a033bSKonstantin Ananyev    sa = ctx ? ctx->sockaddr : s->connection->sockaddr;
330e18a033bSKonstantin Ananyev
331e18a033bSKonstantin Ananyev    v->len = 0;
332e18a033bSKonstantin Ananyev    v->valid = 1;
333e18a033bSKonstantin Ananyev    v->no_cacheable = 0;
334e18a033bSKonstantin Ananyev    v->not_found = 0;
335e18a033bSKonstantin Ananyev
336e18a033bSKonstantin Ananyev    v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1);
337e18a033bSKonstantin Ananyev    if (v->data == NULL) {
338e18a033bSKonstantin Ananyev        return NGX_ERROR;
339e18a033bSKonstantin Ananyev    }
340e18a033bSKonstantin Ananyev
341e18a033bSKonstantin Ananyev    port = ngx_inet_get_port(sa);
342e18a033bSKonstantin Ananyev
343e18a033bSKonstantin Ananyev    if (port > 0 && port < 65536) {
344e18a033bSKonstantin Ananyev        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
345e18a033bSKonstantin Ananyev    }
346e18a033bSKonstantin Ananyev
347e18a033bSKonstantin Ananyev    return NGX_OK;
348e18a033bSKonstantin Ananyev}
349