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