~ubuntu-branches/ubuntu/hardy/lighttpd/hardy

« back to all changes in this revision

Viewing changes to src/mod_extforward.c

  • Committer: Bazaar Package Importer
  • Author(s): Soren Hansen
  • Date: 2007-05-01 13:15:59 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20070501131559-y8jos9wp79uf3pp4
Tags: 1.4.15-1ubuntu1
* Merge from Debian unstable. Remaining Ubuntu changes:
  - Add fam/gamin stat cache engine support
  - Clean environment in init.d script
  - Replace Depends: on perl with Depends: on libterm-readline-perl-perl
  - Make sure that upgrades succeed, even if we can't restart lighttpd
  - DebianMaintainerField update

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <ctype.h>
 
2
#include <stdlib.h>
 
3
#include <string.h>
 
4
#include <stdio.h>
 
5
#include <netinet/in.h>
 
6
 
 
7
#include "base.h"
 
8
#include "log.h"
 
9
#include "buffer.h"
 
10
 
 
11
#include "plugin.h"
 
12
 
 
13
#include "inet_ntop_cache.h"
 
14
#ifdef HAVE_CONFIG_H
 
15
#include "config.h"
 
16
#endif
 
17
 
 
18
/**
 
19
 * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com
 
20
 *                  extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu
 
21
 *
 
22
 * Config example:
 
23
 *
 
24
 *       Trust proxy 10.0.0.232 and 10.0.0.232
 
25
 *       extforward.forwarder = ( "10.0.0.232" => "trust",
 
26
 *                                "10.0.0.233" => "trust" )
 
27
 *
 
28
 *       Trust all proxies  (NOT RECOMMENDED!)
 
29
 *       extforward.forwarder = ( "all" => "trust")
 
30
 *
 
31
 *       Note that "all" has precedence over specific entries,
 
32
 *       so "all except" setups will not work.
 
33
 *
 
34
 * Note: The effect of this module is variable on $HTTP["remotip"] directives and
 
35
 *       other module's remote ip dependent actions.
 
36
 *  Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP.
 
37
 *  Things done in between these two moments will match on the real client's IP.
 
38
 *  The moment things are done by a module depends on in which hook it does things and within the same hook
 
39
 *  on whether they are before/after us in the module loading order
 
40
 *  (order in the server.modules directive in the config file).
 
41
 *
 
42
 * Tested behaviours:
 
43
 *
 
44
 *  mod_access: Will match on the real client.
 
45
 *
 
46
 *  mod_accesslog:
 
47
 *   In order to see the "real" ip address in access log ,
 
48
 *   you'll have to load mod_extforward after mod_accesslog.
 
49
 *   like this:
 
50
 *
 
51
 *    server.modules  = (
 
52
 *       .....
 
53
 *       mod_accesslog,
 
54
 *       mod_extforward
 
55
 *    )
 
56
 *
 
57
 * Known issues:
 
58
 *      seems causing segfault with mod_ssl and $HTTP{"socket"} directives
 
59
 *      LEM 2006.05.26: Fixed segfault $SERVER["socket"] directive. Untested with SSL.
 
60
 *
 
61
 * ChangeLog:
 
62
 *     2005.12.19   Initial Version
 
63
 *     2005.12.19   fixed conflict with conditional directives
 
64
 *     2006.05.26   LEM: IPv6 support
 
65
 *     2006.05.26   LEM: Fix a segfault with $SERVER["socket"] directive.
 
66
 *     2006.05.26   LEM: Run at uri_raw time, as we don't need to see the URI
 
67
 *                       In this manner, we run before mod_access and $HTTP["remoteip"] directives work!
 
68
 *     2006.05.26   LEM: Clean config_cond cache of tests whose result we probably change.
 
69
 */
 
70
 
 
71
 
 
72
/* plugin config for all request/connections */
 
73
 
 
74
typedef struct {
 
75
        array *forwarder;
 
76
} plugin_config;
 
77
 
 
78
typedef struct {
 
79
        PLUGIN_DATA;
 
80
 
 
81
        plugin_config **config_storage;
 
82
 
 
83
        plugin_config conf;
 
84
} plugin_data;
 
85
 
 
86
 
 
87
/* context , used for restore remote ip */
 
88
 
 
89
typedef struct {
 
90
        sock_addr saved_remote_addr;
 
91
        buffer *saved_remote_addr_buf;
 
92
} handler_ctx;
 
93
 
 
94
 
 
95
static handler_ctx * handler_ctx_init(sock_addr oldaddr, buffer *oldaddr_buf) {
 
96
        handler_ctx * hctx;
 
97
        hctx = calloc(1, sizeof(*hctx));
 
98
        hctx->saved_remote_addr = oldaddr;
 
99
        hctx->saved_remote_addr_buf = oldaddr_buf;
 
100
        return hctx;
 
101
}
 
102
 
 
103
static void handler_ctx_free(handler_ctx *hctx) {
 
104
        free(hctx);
 
105
}
 
106
 
 
107
/* init the plugin data */
 
108
INIT_FUNC(mod_extforward_init) {
 
109
        plugin_data *p;
 
110
        p = calloc(1, sizeof(*p));
 
111
        return p;
 
112
}
 
113
 
 
114
/* destroy the plugin data */
 
115
FREE_FUNC(mod_extforward_free) {
 
116
        plugin_data *p = p_d;
 
117
 
 
118
        UNUSED(srv);
 
119
 
 
120
        if (!p) return HANDLER_GO_ON;
 
121
 
 
122
        if (p->config_storage) {
 
123
                size_t i;
 
124
 
 
125
                for (i = 0; i < srv->config_context->used; i++) {
 
126
                        plugin_config *s = p->config_storage[i];
 
127
 
 
128
                        if (!s) continue;
 
129
 
 
130
                        array_free(s->forwarder);
 
131
 
 
132
                        free(s);
 
133
                }
 
134
                free(p->config_storage);
 
135
        }
 
136
 
 
137
 
 
138
        free(p);
 
139
 
 
140
        return HANDLER_GO_ON;
 
141
}
 
142
 
 
143
/* handle plugin config and check values */
 
144
 
 
145
SETDEFAULTS_FUNC(mod_extforward_set_defaults) {
 
146
        plugin_data *p = p_d;
 
147
        size_t i = 0;
 
148
 
 
149
        config_values_t cv[] = {
 
150
                { "extforward.forwarder",             NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
 
151
                { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
 
152
        };
 
153
 
 
154
        if (!p) return HANDLER_ERROR;
 
155
 
 
156
        p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
 
157
 
 
158
        for (i = 0; i < srv->config_context->used; i++) {
 
159
                plugin_config *s;
 
160
 
 
161
                s = calloc(1, sizeof(plugin_config));
 
162
                s->forwarder    = array_init();
 
163
 
 
164
                cv[0].destination = s->forwarder;
 
165
 
 
166
                p->config_storage[i] = s;
 
167
 
 
168
                if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
 
169
                        return HANDLER_ERROR;
 
170
                }
 
171
        }
 
172
 
 
173
        return HANDLER_GO_ON;
 
174
}
 
175
 
 
176
#define PATCH(x) \
 
177
        p->conf.x = s->x;
 
178
static int mod_extforward_patch_connection(server *srv, connection *con, plugin_data *p) {
 
179
        size_t i, j;
 
180
        plugin_config *s = p->config_storage[0];
 
181
 
 
182
        PATCH(forwarder);
 
183
 
 
184
        /* LEM: The purpose of this seems to match extforward configuration
 
185
                stanzas that are not in the global context, but in some sub-context.
 
186
                I fear this will break contexts of the form HTTP['remote'] = .
 
187
                (in the form that they do not work with the real remote, but matching on
 
188
                the proxy instead).
 
189
 
 
190
                I'm not sure this this is all thread-safe. Is the p we are passed different
 
191
                for each connection or is it global?
 
192
 
 
193
                mod_fastcgi does the same, so it must be safe.
 
194
         */
 
195
        /* skip the first, the global context */
 
196
        for (i = 1; i < srv->config_context->used; i++) {
 
197
                data_config *dc = (data_config *)srv->config_context->data[i];
 
198
                s = p->config_storage[i];
 
199
 
 
200
                /* condition didn't match */
 
201
                if (!config_check_cond(srv, con, dc)) continue;
 
202
 
 
203
                /* merge config */
 
204
                for (j = 0; j < dc->value->used; j++) {
 
205
                        data_unset *du = dc->value->data[j];
 
206
 
 
207
                        if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.forwarder"))) {
 
208
                                PATCH(forwarder);
 
209
                        }
 
210
                }
 
211
        }
 
212
 
 
213
        return 0;
 
214
}
 
215
#undef PATCH
 
216
 
 
217
 
 
218
static void put_string_into_array_len(array *ary, const char *str, int len)
 
219
{
 
220
        data_string *tempdata;
 
221
        if (len == 0)
 
222
                return;
 
223
        tempdata = data_string_init();
 
224
        buffer_copy_string_len(tempdata->value,str,len);
 
225
        array_insert_unique(ary,(data_unset *)tempdata);
 
226
}
 
227
/*
 
228
   extract a forward array from the environment
 
229
*/
 
230
static array *extract_forward_array(buffer *pbuffer)
 
231
{
 
232
        array *result = array_init();
 
233
        if (pbuffer->used > 0) {
 
234
                char *base, *curr;
 
235
                /* state variable, 0 means not in string, 1 means in string */
 
236
                int in_str = 0;
 
237
                for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++)
 
238
                {
 
239
                        if (in_str) {
 
240
                                if ( (*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':' ) {
 
241
                                        /* found an separator , insert value into result array */
 
242
                                        put_string_into_array_len(result, base, curr-base);
 
243
                                        /* change state to not in string */
 
244
                                        in_str = 0;
 
245
                                }
 
246
                        } else {
 
247
                                if (*curr >= '0' && *curr <= '9')
 
248
                                {
 
249
                                        /* found leading char of an IP address, move base pointer and change state */
 
250
                                        base = curr;
 
251
                                        in_str = 1;
 
252
                                }
 
253
                        }
 
254
                }
 
255
                /* if breaking out while in str, we got to the end of string, so add it */
 
256
                if (in_str)
 
257
                {
 
258
                        put_string_into_array_len(result, base, curr-base);
 
259
                }
 
260
        }
 
261
        return result;
 
262
}
 
263
 
 
264
#define IP_TRUSTED 1
 
265
#define IP_UNTRUSTED 0
 
266
/*
 
267
   check whether ip is trusted, return 1 for trusted , 0 for untrusted
 
268
*/
 
269
static int is_proxy_trusted(const char *ipstr, plugin_data *p)
 
270
{
 
271
        data_string* allds = (data_string *) array_get_element(p->conf.forwarder,"all");
 
272
        if (allds) {
 
273
                if (strcasecmp(allds->value->ptr,"trust") == 0)
 
274
                        return IP_TRUSTED;
 
275
                else
 
276
                        return IP_UNTRUSTED;
 
277
        }
 
278
        return (data_string *)array_get_element(p->conf.forwarder,ipstr) ? IP_TRUSTED : IP_UNTRUSTED ;
 
279
}
 
280
 
 
281
struct addrinfo *ipstr_to_sockaddr(const char *host)
 
282
{
 
283
   struct addrinfo hints, *res0;
 
284
   int result;
 
285
 
 
286
   memset(&hints, 0, sizeof(hints));
 
287
#ifndef AI_NUMERICSERV
 
288
/** 
 
289
 * quoting $ man getaddrinfo
 
290
 *
 
291
 * NOTES
 
292
 *        AI_ADDRCONFIG, AI_ALL, and AI_V4MAPPED are available since glibc 2.3.3.  
 
293
 *        AI_NUMERICSERV is available since glibc 2.3.4.
 
294
 */
 
295
#define AI_NUMERICSERV 0
 
296
#endif
 
297
   hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
 
298
 
 
299
   result = getaddrinfo(host, NULL, &hints, &res0);
 
300
   if ( result != 0 )
 
301
   {
 
302
      fprintf(stderr,"could not resolve hostname %s because %s\n", host,gai_strerror(result));
 
303
      if (result == EAI_SYSTEM)
 
304
         perror("The system error is ");
 
305
      return NULL;
 
306
   }
 
307
   else
 
308
      if (res0==0)
 
309
         fprintf(stderr, "Problem in resolving hostname %s: succeeded, but no information returned\n", host);
 
310
 
 
311
   return res0;
 
312
}
 
313
 
 
314
 
 
315
static void clean_cond_cache(server *srv, connection *con)
 
316
{
 
317
        size_t i;
 
318
 
 
319
        for (i = 0; i < srv->config_context->used; i++) {
 
320
                data_config *dc = (data_config *)srv->config_context->data[i];
 
321
 
 
322
                if (dc->comp == COMP_HTTP_REMOTEIP)
 
323
                {
 
324
                        con->cond_cache[i].result = COND_RESULT_UNSET;
 
325
                        con->cond_cache[i].patterncount = 0;
 
326
                }
 
327
        }
 
328
}
 
329
 
 
330
URIHANDLER_FUNC(mod_extforward_uri_handler) {
 
331
        plugin_data *p = p_d;
 
332
        data_string *forwarded = NULL;
 
333
#ifdef HAVE_IPV6
 
334
        char b2[INET6_ADDRSTRLEN + 1];
 
335
        struct addrinfo *addrlist = NULL;
 
336
#endif
 
337
        const char *dst_addr_str = NULL;
 
338
        int i;
 
339
        array *forward_array = NULL;
 
340
        char *real_remote_addr = NULL;
 
341
#ifdef HAVE_IPV6
 
342
#endif
 
343
 
 
344
        if (!con->request.headers) return HANDLER_GO_ON;
 
345
 
 
346
        mod_extforward_patch_connection(srv, con, p);
 
347
 
 
348
        if (con->conf.log_request_handling) {
 
349
                log_error_write(srv, __FILE__, __LINE__, "s", 
 
350
                                "-- mod_extforward_uri_handler called");
 
351
        }
 
352
 
 
353
        if ((NULL == (forwarded = (data_string *) array_get_element(con->request.headers,"X-Forwarded-For")) &&
 
354
             NULL == (forwarded = (data_string *) array_get_element(con->request.headers,  "Forwarded-For")))) {
 
355
 
 
356
                if (con->conf.log_request_handling) {
 
357
                        log_error_write(srv, __FILE__, __LINE__, "s", 
 
358
                                        "no X-Forwarded-For|Forwarded-For: found, skipping");
 
359
                }
 
360
 
 
361
                return HANDLER_GO_ON;
 
362
        }
 
363
 
 
364
        /* if the remote ip itself is not trusted , then do nothing */
 
365
#ifdef HAVE_IPV6
 
366
        dst_addr_str = inet_ntop(con->dst_addr.plain.sa_family,
 
367
                      con->dst_addr.plain.sa_family == AF_INET6 ?
 
368
                       (struct sockaddr *)&(con->dst_addr.ipv6.sin6_addr) :
 
369
                       (struct sockaddr *)&(con->dst_addr.ipv4.sin_addr),
 
370
                      b2,
 
371
                      (sizeof b2) - 1);
 
372
#else
 
373
        dst_addr_str = inet_ntoa(con->dst_addr.ipv4.sin_addr);
 
374
#endif
 
375
        if (IP_UNTRUSTED == is_proxy_trusted (dst_addr_str, p) ) {
 
376
                if (con->conf.log_request_handling) {
 
377
                        log_error_write(srv, __FILE__, __LINE__, "s",
 
378
                                        "remote address is NOT a trusted proxy, skipping");
 
379
                }
 
380
 
 
381
                return HANDLER_GO_ON;
 
382
        }
 
383
 
 
384
        forward_array = extract_forward_array(forwarded->value);
 
385
 
 
386
        /* Testing shows that multiple headers and multiple values in one header
 
387
           come in _reverse_ order. So the first one we get is the last one in the request. */
 
388
        for (i = forward_array->used - 1; i >= 0; i--) {
 
389
                data_string *ds = (data_string *) forward_array->data[i];
 
390
                if (ds) {
 
391
                        real_remote_addr = ds->value->ptr;
 
392
                        break;
 
393
                } else {
 
394
                        /* bug ?  bailing out here */
 
395
                        break;
 
396
                }
 
397
        }
 
398
 
 
399
        if (real_remote_addr != NULL) { /* parsed */
 
400
                sock_addr sock;
 
401
 
 
402
                struct addrinfo *addrs_left;
 
403
 
 
404
                if (con->conf.log_request_handling) {
 
405
                        log_error_write(srv, __FILE__, __LINE__, "ss",
 
406
                                        "using address:", real_remote_addr);
 
407
                }
 
408
#ifdef HAVE_IPV6
 
409
                addrlist = ipstr_to_sockaddr(real_remote_addr);
 
410
                sock.plain.sa_family = AF_UNSPEC;
 
411
                for (addrs_left = addrlist; addrs_left != NULL;
 
412
                     addrs_left = addrs_left -> ai_next) {
 
413
                        sock.plain.sa_family = addrs_left->ai_family;
 
414
                        if ( sock.plain.sa_family == AF_INET ) {
 
415
                                sock.ipv4.sin_addr = ((struct sockaddr_in*)addrs_left->ai_addr)->sin_addr;
 
416
                                break;
 
417
                        } else if ( sock.plain.sa_family == AF_INET6 ) {
 
418
                                sock.ipv6.sin6_addr = ((struct sockaddr_in6*)addrs_left->ai_addr)->sin6_addr;
 
419
                                break;
 
420
                        }
 
421
                }
 
422
#else
 
423
                sock.ipv4.sin_addr.s_addr = inet_addr(real_remote_addr);
 
424
                sock.plain.sa_family = (sock.ipv4.sin_addr.s_addr == 0xFFFFFFFF) ? AF_UNSPEC : AF_INET;
 
425
#endif
 
426
                if (sock.plain.sa_family != AF_UNSPEC) {
 
427
                        /* we found the remote address, modify current connection and save the old address */
 
428
                        if (con->plugin_ctx[p->id]) {
 
429
                                log_error_write(srv, __FILE__, __LINE__, "s", 
 
430
                                                "patching an already patched connection!");
 
431
                                handler_ctx_free(con->plugin_ctx[p->id]);
 
432
                                con->plugin_ctx[p->id] = NULL;
 
433
                        }
 
434
                        /* save old address */
 
435
                        con->plugin_ctx[p->id] = handler_ctx_init(con->dst_addr, con->dst_addr_buf);
 
436
                        /* patch connection address */
 
437
                        con->dst_addr = sock;
 
438
                        con->dst_addr_buf = buffer_init();
 
439
                        buffer_copy_string(con->dst_addr_buf, real_remote_addr);
 
440
                
 
441
                        if (con->conf.log_request_handling) {
 
442
                                log_error_write(srv, __FILE__, __LINE__, "ss",
 
443
                                                "patching con->dst_addr_buf for the accesslog:", real_remote_addr);
 
444
                        }
 
445
                        /* Now, clean the conf_cond cache, because we may have changed the results of tests */
 
446
                        clean_cond_cache(srv, con);
 
447
                }
 
448
#ifdef HAVE_IPV6
 
449
                if (addrlist != NULL ) freeaddrinfo(addrlist);
 
450
#endif
 
451
        }
 
452
        array_free(forward_array);
 
453
 
 
454
        /* not found */
 
455
        return HANDLER_GO_ON;
 
456
}
 
457
 
 
458
CONNECTION_FUNC(mod_extforward_restore) {
 
459
        plugin_data *p = p_d;
 
460
        UNUSED(srv);
 
461
 
 
462
        /* LEM: This seems completely unuseful, as we are not using
 
463
                p->conf in this function. Furthermore, it brings a
 
464
                segfault if one of the conditional configuration
 
465
                blocks is "SERVER['socket'] == foo", because the
 
466
                socket is not known yet in the srv/con structure.
 
467
         */
 
468
        /* mod_extforward_patch_connection(srv, con, p); */
 
469
 
 
470
        /* restore this connection's remote ip */
 
471
        if (con->plugin_ctx[p->id]) {
 
472
                handler_ctx *hctx = con->plugin_ctx[p->id];
 
473
                con->dst_addr = hctx->saved_remote_addr;
 
474
                buffer_free(con->dst_addr_buf);
 
475
                con->dst_addr_buf = hctx->saved_remote_addr_buf;
 
476
/*              log_error_write(srv, __FILE__, __LINE__,"s","LEM: Reset dst_addr_buf"); */
 
477
                handler_ctx_free(hctx);
 
478
                con->plugin_ctx[p->id] = NULL;
 
479
                /* Now, clean the conf_cond cache, because we may have changed the results of tests */
 
480
                clean_cond_cache(srv, con);
 
481
        }
 
482
        return HANDLER_GO_ON;
 
483
}
 
484
 
 
485
 
 
486
/* this function is called at dlopen() time and inits the callbacks */
 
487
 
 
488
int mod_extforward_plugin_init(plugin *p) {
 
489
        p->version     = LIGHTTPD_VERSION_ID;
 
490
        p->name        = buffer_init_string("extforward");
 
491
 
 
492
        p->init        = mod_extforward_init;
 
493
        p->handle_uri_raw = mod_extforward_uri_handler;
 
494
        p->handle_request_done = mod_extforward_restore;
 
495
        p->connection_reset = mod_extforward_restore;
 
496
        p->set_defaults  = mod_extforward_set_defaults;
 
497
        p->cleanup     = mod_extforward_free;
 
498
 
 
499
        p->data        = NULL;
 
500
 
 
501
        return 0;
 
502
}
 
503