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