Release-1.4.10-patches: lighttpd-1.4.10-mod_extforward.c

File lighttpd-1.4.10-mod_extforward.c, 8.4 kB (added by jan, 3 years ago)

extract host from X-forwarded-for

Line 
1#include <ctype.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "base.h"
6#include "log.h"
7#include "buffer.h"
8
9#include "plugin.h"
10
11#include "inet_ntop_cache.h"
12#ifdef HAVE_CONFIG_H
13#include "config.h"
14#endif
15
16/**
17 * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com
18 * FIXME: only support ipv4
19 *
20 * Config example:
21 *
22 *       Trust proxy 10.0.0.232 and 10.0.0.232
23 *       extforward.forwarder = ( "10.0.0.232" => "trust",
24 *                                "10.0.0.233" => "trust" )
25 *
26 *       Trust all proxies  (NOT RECOMMENDED!)
27 *       extforward.forwarder = ( "all" => "trust")
28 *
29 * Note: In order to see the "real" ip address in access log ,
30 *       you'll have to load mod_extforward after mod_accesslog.
31 *       like this:
32 *
33 *       server.modules  = (
34 *              .....
35 *              mod_accesslog,
36 *              mod_extforward
37 *       )         
38 *
39 * Known issues:
40 *      seems causing segfault with mod_ssl and $HTTP{"socket"} directives
41 *
42 * ChangeLog:
43 *     2005.12.19   Initial Version
44 *     2005.12.19   fixed conflict with conditional directives
45 */
46
47
48/* plugin config for all request/connections */
49
50typedef struct {
51        array *forwarder;
52} plugin_config;
53
54typedef struct {
55        PLUGIN_DATA;
56       
57        plugin_config **config_storage;
58       
59        plugin_config conf; 
60} plugin_data;
61
62
63/* context , used for restore remote ip */
64
65typedef struct {
66        sock_addr saved_remote_addr;
67} handler_ctx;
68
69
70static handler_ctx * handler_ctx_init(sock_addr oldaddr) {
71        handler_ctx * hctx;
72        hctx = calloc(1, sizeof(*hctx));
73        hctx->saved_remote_addr = oldaddr;
74        return hctx;
75}
76
77static void handler_ctx_free(handler_ctx *hctx) {
78        free(hctx);
79}
80
81/* init the plugin data */
82INIT_FUNC(mod_extforward_init) {
83        plugin_data *p;
84        p = calloc(1, sizeof(*p));
85        return p;
86}
87
88/* detroy the plugin data */
89FREE_FUNC(mod_extforward_free) {
90        plugin_data *p = p_d;
91       
92        UNUSED(srv);
93
94        if (!p) return HANDLER_GO_ON;
95       
96        if (p->config_storage) {
97                size_t i;
98
99                for (i = 0; i < srv->config_context->used; i++) {
100                        plugin_config *s = p->config_storage[i];
101
102                        if (!s) continue;
103                       
104                        array_free(s->forwarder);
105                       
106                        free(s);
107                }
108                free(p->config_storage);
109        }
110       
111       
112        free(p);
113       
114        return HANDLER_GO_ON;
115}
116
117/* handle plugin config and check values */
118
119SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
120        plugin_data *p = p_d;
121        size_t i = 0;
122       
123        config_values_t cv[] = { 
124                { "extforward.forwarder",             NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
125                { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
126        };
127       
128        if (!p) return HANDLER_ERROR;
129       
130        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
131       
132        for (i = 0; i < srv->config_context->used; i++) {
133                plugin_config *s;
134               
135                s = calloc(1, sizeof(plugin_config));
136                s->forwarder    = array_init();
137               
138                cv[0].destination = s->forwarder;
139               
140                p->config_storage[i] = s;
141       
142                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
143                        return HANDLER_ERROR;
144                }
145        }
146       
147        return HANDLER_GO_ON;
148}
149
150#define PATCH(x) \
151        p->conf.x = s->x;
152static int mod_extforward_patch_connection(server *srv, connection *con, plugin_data *p) {
153        size_t i, j;
154        plugin_config *s = p->config_storage[0];
155       
156        PATCH(forwarder);
157       
158        /* skip the first, the global context */
159        for (i = 1; i < srv->config_context->used; i++) {
160                data_config *dc = (data_config *)srv->config_context->data[i];
161                s = p->config_storage[i];
162               
163                /* condition didn't match */
164                if (!config_check_cond(srv, con, dc)) continue;
165               
166                /* merge config */
167                for (j = 0; j < dc->value->used; j++) {
168                        data_unset *du = dc->value->data[j];
169                       
170                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.forwarder"))) {
171                                PATCH(forwarder);
172                        }
173                }
174        }
175       
176        return 0;
177}
178#undef PATCH
179
180
181static void put_string_into_array_len(array *ary, const char *str, int len)
182{
183        data_string *tempdata;
184        if (len == 0)
185                return;
186        tempdata = data_string_init();
187        buffer_copy_string_len(tempdata->value,str,len);
188        array_insert_unique(ary,(data_unset *)tempdata);
189}
190/*
191   extract an forward array from the environment
192*/
193static array *extract_forward_array(buffer *pbuffer)
194{
195        array *result = array_init();
196        if (pbuffer->used > 0) {
197                char *base, *curr;
198                /* state variable, 0 means not in string, 1 means in string */
199                int in_str = 0;
200                for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++)
201                {
202                        if (in_str) {
203                                if (*curr > '9' || ( *curr < '0'  && *curr != '.' ) ) {
204                                        /* found an separator , insert value into result array */
205                                        put_string_into_array_len(result, base, curr-base);
206                                        /* change state to not in string */                                     
207                                        in_str = 0;
208                                }
209                        } else {
210                                if (*curr >= '0' && *curr <= '9')
211                                {
212                                        /* found leading char of an IP address, move base pointer change states */
213                                        base = curr;
214                                        in_str = 1;
215                                }
216                        }
217                }
218                /* if breaking out while in str, we get to the end of string, so add it */
219                if (in_str) 
220                {
221                        put_string_into_array_len(result, base, curr-base);
222                }
223        }
224        return result;
225}
226
227#define IP_TRUSTED 1
228#define IP_UNTRUSTED 0
229/*
230   check whether ip is trusted, return 1 for trusted , 0 for untrusted
231*/
232static int is_proxy_trusted(const char *ipstr, plugin_data *p)
233{
234        data_string* allds = (data_string *) array_get_element(p->conf.forwarder,"all");
235        if (allds) {
236                if (strcasecmp(allds->value->ptr,"trust") == 0)
237                        return IP_TRUSTED;
238                else
239                        return IP_UNTRUSTED;
240        }
241        return (data_string *)array_get_element(p->conf.forwarder,ipstr) ? IP_TRUSTED : IP_UNTRUSTED ;
242}
243
244
245URIHANDLER_FUNC(mod_extforward_uri_handler) {
246        plugin_data *p = p_d;
247        data_string *forwarded = NULL;
248        UNUSED(srv);
249        mod_extforward_patch_connection(srv, con, p);
250       
251        /* if the remote ip itself is not trusted , then do nothing */
252        if (IP_UNTRUSTED == is_proxy_trusted (inet_ntoa(con->dst_addr.ipv4.sin_addr) ,p) )
253                return HANDLER_GO_ON;
254       
255        /* log_error_write(srv, __FILE__, __LINE__,"s","remote address is trusted proxy, go on\n");*/
256        if (con->request.headers && (
257           (forwarded = (data_string *)array_get_element(con->request.headers,"X-Forwarded-For"))  ||
258           (forwarded = (data_string *) array_get_element(con->request.headers,"Forwarded-For"))  ) 
259           )
260        {
261                /* log_error_write(srv, __FILE__, __LINE__,"s","found forwarded header\n");*/
262                /* found forwarded for header */
263                int i;
264                array *forward_array = extract_forward_array(forwarded->value);
265                unsigned int real_remote_addr = 0xFFFFFFFF;
266                for (i = forward_array->used -1 ;i>=0; i--)
267                {
268                        data_string *ds = (data_string *) forward_array->data[i];
269                        if (ds) {
270                                /* log_error_write(srv, __FILE__, __LINE__,"ss","forward",ds->value->ptr);*/
271                                real_remote_addr = inet_addr(ds->value->ptr);
272                                /* check whether it is trusted */
273                                if (IP_UNTRUSTED == is_proxy_trusted(ds->value->ptr,p) )
274                                        break;
275                                /* log_error_write(srv, __FILE__, __LINE__,"ss",ds->value->ptr," is trusted");  */
276                        }
277                        else {
278                                /* bug ?  bailing out here */
279                                break;
280                        }
281                }
282                if (real_remote_addr != 0xFFFFFFFF) /* parsed and inet_addr not returning -1 */
283                {
284                        /* log_error_write(srv, __FILE__, __LINE__,"ss","use forward",inet_ntoa(*((struct in_addr *)&real_remote_addr)));*/
285                        /* we found the remote address, modify current connection and save the old address */
286                        if (con->plugin_ctx[p->id]) {
287                                log_error_write(srv, __FILE__, __LINE__,"patching an already patched connection!");
288                                handler_ctx_free(con->plugin_ctx[p->id]);
289                                con->plugin_ctx[p->id] = NULL;
290                        }
291                        /* save old address */
292                        con->plugin_ctx[p->id] = handler_ctx_init(con->dst_addr);
293                        /* patch connection address */
294                        con->dst_addr.ipv4.sin_addr.s_addr = real_remote_addr;
295                }
296                array_free(forward_array);
297        }
298       
299        /* not found */
300        return HANDLER_GO_ON;
301}
302
303CONNECTION_FUNC(mod_extforward_restore) {
304        plugin_data *p = p_d;
305        UNUSED(srv);
306        mod_extforward_patch_connection(srv, con, p);
307        /* restore this connection 's remote ip */
308        if (con->plugin_ctx[p->id]) {
309                handler_ctx *hctx = con->plugin_ctx[p->id];
310                con->dst_addr = hctx->saved_remote_addr;
311                handler_ctx_free(hctx);
312                con->plugin_ctx[p->id] = NULL;
313        }
314        return HANDLER_GO_ON;
315}
316
317
318/* this function is called at dlopen() time and inits the callbacks */
319
320int mod_extforward_plugin_init(plugin *p) {
321        p->version     = LIGHTTPD_VERSION_ID;
322        p->name        = buffer_init_string("extforward");
323       
324        p->init        = mod_extforward_init;
325        p->handle_uri_clean = mod_extforward_uri_handler;
326        p->handle_request_done = mod_extforward_restore;
327        p->connection_reset = mod_extforward_restore;
328        p->set_defaults  = mod_extforward_set_defaults;
329        p->cleanup     = mod_extforward_free;
330       
331        p->data        = NULL;
332       
333        return 0;
334}