Ticket #1528: 1528.3.patch
| File 1528.3.patch, 9.7 kB (added by glen, 4 months ago) |
|---|
-
src/mod_extforward.c
old new 20 20 /** 21 21 * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com 22 22 * extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu 23 * support chained proxies by glen@delfi.ee, #1528 23 24 * 24 25 * Config example: 25 26 * … … 33 34 * Note that "all" has precedence over specific entries, 34 35 * so "all except" setups will not work. 35 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 * 36 41 * Note: The effect of this module is variable on $HTTP["remotip"] directives and 37 42 * other module's remote ip dependent actions. 38 43 * Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. … … 225 230 char *base, *curr; 226 231 /* state variable, 0 means not in string, 1 means in string */ 227 232 int in_str = 0; 228 for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) 229 { 233 for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) { 230 234 if (in_str) { 231 if ( (*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':') {235 if ((*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':') { 232 236 /* found an separator , insert value into result array */ 233 put_string_into_array_len(result, base, curr -base);237 put_string_into_array_len(result, base, curr - base); 234 238 /* change state to not in string */ 235 239 in_str = 0; 236 240 } 237 241 } else { 238 if (*curr >= '0' && *curr <= '9') 239 { 242 if (*curr >= '0' && *curr <= '9') { 240 243 /* found leading char of an IP address, move base pointer and change state */ 241 244 base = curr; 242 245 in_str = 1; … … 244 247 } 245 248 } 246 249 /* if breaking out while in str, we got to the end of string, so add it */ 247 if (in_str) 248 { 249 put_string_into_array_len(result, base, curr-base); 250 if (in_str) { 251 put_string_into_array_len(result, base, curr - base); 250 252 } 251 253 } 252 254 return result; … … 255 257 #define IP_TRUSTED 1 256 258 #define IP_UNTRUSTED 0 257 259 /* 258 check whether ip is trusted, return 1 for trusted , 0 for untrusted259 */260 * check whether ip is trusted, return 1 for trusted , 0 for untrusted 261 */ 260 262 static int is_proxy_trusted(const char *ipstr, plugin_data *p) 261 263 { 262 data_string* allds = (data_string *) array_get_element(p->conf.forwarder,"all"); 264 data_string* allds = (data_string *)array_get_element(p->conf.forwarder, "all"); 265 263 266 if (allds) { 264 if (strcasecmp(allds->value->ptr, "trust") == 0)267 if (strcasecmp(allds->value->ptr, "trust") == 0) { 265 268 return IP_TRUSTED; 266 else269 } else { 267 270 return IP_UNTRUSTED; 271 } 268 272 } 269 return (data_string *)array_get_element(p->conf.forwarder,ipstr) ? IP_TRUSTED : IP_UNTRUSTED ; 273 274 return (data_string *)array_get_element(p->conf.forwarder,ipstr) ? IP_TRUSTED : IP_UNTRUSTED; 270 275 } 271 276 277 /* 278 * Return char *ip of last address of proxy that is not trusted. 279 * Do not accept "all" keyword here. 280 */ 281 static const char *last_not_in_array(array *a, plugin_data *p) 282 { 283 for (int i = a->used - 1; i >= 0; i--) { 284 data_string *ds = (data_string *)a->data[i]; 285 const char *ip = ds->value->ptr; 286 287 if (IP_UNTRUSTED == is_proxy_trusted(ip, p)) { 288 return ip; 289 } 290 } 291 return NULL; 292 } 293 272 294 struct addrinfo *ipstr_to_sockaddr(const char *host) 273 295 { 274 296 struct addrinfo hints, *res0; … … 316 338 struct addrinfo *addrlist = NULL; 317 339 #endif 318 340 const char *dst_addr_str = NULL; 319 int i;320 341 array *forward_array = NULL; 321 c har *real_remote_addr = NULL;342 const char *real_remote_addr = NULL; 322 343 #ifdef HAVE_IPV6 323 344 #endif 324 345 … … 342 363 return HANDLER_GO_ON; 343 364 } 344 365 345 /* if the remote ip itself is not trusted , then do nothing */346 366 #ifdef HAVE_IPV6 347 367 dst_addr_str = inet_ntop(con->dst_addr.plain.sa_family, 348 368 con->dst_addr.plain.sa_family == AF_INET6 ? … … 353 373 #else 354 374 dst_addr_str = inet_ntoa(con->dst_addr.ipv4.sin_addr); 355 375 #endif 356 if (IP_UNTRUSTED == is_proxy_trusted (dst_addr_str, p) ) { 376 377 /* if the remote ip itself is not trusted, then do nothing */ 378 if (IP_UNTRUSTED == is_proxy_trusted(dst_addr_str, p)) { 357 379 if (con->conf.log_request_handling) { 358 380 log_error_write(srv, __FILE__, __LINE__, "s", 359 381 "remote address is NOT a trusted proxy, skipping"); … … 362 384 return HANDLER_GO_ON; 363 385 } 364 386 387 /* build forward_array from forwarded data_string */ 365 388 forward_array = extract_forward_array(forwarded->value); 389 real_remote_addr = last_not_in_array(forward_array, p); 390 array_free(forward_array); 366 391 367 /* Testing shows that multiple headers and multiple values in one header368 come in _reverse_ order. So the first one we get is the last one in the request. */369 for (i = forward_array->used - 1; i >= 0; i--) {370 data_string *ds = (data_string *) forward_array->data[i];371 if (ds) {372 real_remote_addr = ds->value->ptr;373 break;374 } else {375 /* bug ? bailing out here */376 break;377 }378 }379 380 392 if (real_remote_addr != NULL) { /* parsed */ 381 393 sock_addr sock; 382 394 struct addrinfo *addrs_left; 383 395 server_socket *srv_sock = con->srv_socket; 384 data_string *forwarded_proto = (data_string *) array_get_element(con->request.headers,"X-Forwarded-Proto");396 data_string *forwarded_proto = (data_string *)array_get_element(con->request.headers, "X-Forwarded-Proto"); 385 397 386 if (forwarded_proto && !strcmp(forwarded_proto->value->ptr, "https")) 398 if (forwarded_proto && !strcmp(forwarded_proto->value->ptr, "https")) { 387 399 srv_sock->is_proxy_ssl = 1; 388 else400 } else { 389 401 srv_sock->is_proxy_ssl = 0; 402 } 390 403 391 404 if (con->conf.log_request_handling) { 392 log_error_write(srv, __FILE__, __LINE__, "ss", 393 "using address:", real_remote_addr); 405 log_error_write(srv, __FILE__, __LINE__, "ss", "using address:", real_remote_addr); 394 406 } 395 407 #ifdef HAVE_IPV6 396 408 addrlist = ipstr_to_sockaddr(real_remote_addr); 397 409 sock.plain.sa_family = AF_UNSPEC; 398 for (addrs_left = addrlist; addrs_left != NULL; 399 addrs_left = addrs_left -> ai_next) { 410 for (addrs_left = addrlist; addrs_left != NULL; addrs_left = addrs_left -> ai_next) { 400 411 sock.plain.sa_family = addrs_left->ai_family; 401 if ( sock.plain.sa_family == AF_INET) {412 if (sock.plain.sa_family == AF_INET) { 402 413 sock.ipv4.sin_addr = ((struct sockaddr_in*)addrs_left->ai_addr)->sin_addr; 403 414 break; 404 } else if ( sock.plain.sa_family == AF_INET6) {415 } else if (sock.plain.sa_family == AF_INET6) { 405 416 sock.ipv6.sin6_addr = ((struct sockaddr_in6*)addrs_left->ai_addr)->sin6_addr; 406 417 break; 407 418 } … … 436 447 if (addrlist != NULL ) freeaddrinfo(addrlist); 437 448 #endif 438 449 } 439 array_free(forward_array);440 450 441 451 /* not found */ 442 452 return HANDLER_GO_ON; -
tests/mod-extforward.conf
old new 1 1 debug.log-request-handling = "enable" 2 debug.log-response-header = " enable"3 debug.log-request-header = " enable"2 debug.log-response-header = "disable" 3 debug.log-request-header = "disable" 4 4 5 5 server.document-root = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/" 6 6 server.pid-file = env.SRCDIR + "/tmp/lighttpd/lighttpd.pid" … … 27 27 28 28 extforward.forwarder = ( 29 29 "127.0.0.1" => "trust", 30 "127.0.30.1" => "trust", 30 31 ) -
tests/mod-extforward.t
old new 8 8 9 9 use strict; 10 10 use IO::Socket; 11 use Test::More tests => 2;11 use Test::More tests => 5; 12 12 use LightyTest; 13 13 14 14 my $tf = LightyTest->new(); … … 18 18 19 19 ok($tf->start_proc == 0, "Starting lighttpd") or die(); 20 20 21 ## check if If-Modified-Since, If-None-Match works22 23 21 $t->{REQUEST} = ( <<EOF 24 22 GET /ip.pl HTTP/1.0 25 23 Host: www.example.org … … 27 25 EOF 28 26 ); 29 27 $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.10.1' } ]; 30 ok($tf->handle_http($t) == 0, 'expect 127.0.10.1 ');28 ok($tf->handle_http($t) == 0, 'expect 127.0.10.1, from single ip'); 31 29 32 30 $t->{REQUEST} = ( <<EOF 33 31 GET /ip.pl HTTP/1.0 … … 36 34 EOF 37 35 ); 38 36 $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.20.1' } ]; 39 ok($tf->handle_http($t) == 0, 'expect 127.0.20.1 ');37 ok($tf->handle_http($t) == 0, 'expect 127.0.20.1, from two ips'); 40 38 39 $t->{REQUEST} = ( <<EOF 40 GET /ip.pl HTTP/1.0 41 Host: www.example.org 42 X-Forwarded-For: 127.0.10.1, 127.0.20.1, 127.0.30.1 43 EOF 44 ); 45 $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.20.1' } ]; 46 ok($tf->handle_http($t) == 0, 'expect 127.0.20.1, from chained proxies'); 47 41 48 ok($tf->stop_proc == 0, "Stopping lighttpd"); -
tests/docroot/www/ip.pl
old new 2 2 print "Content-Type: text/html\r\n\r\n"; 3 3 print $ENV{'REMOTE_ADDR'}; 4 4 5 if ($ENV{'QUERY_STRING'} eq 'info') { 6 print "\nF:",$ENV{'HTTP_X_FORWARDED_FOR'},"\n"; 7 8 while (my($key, $value) = each %ENV) { 9 printf "%s => %s\n", $key, $value; 10 } 11 } 12 5 13 0; -
NEWS
old new 20 20 * HTTPS env var should be "on" when using mod_extforward and the X-Forwarded-Proto header is set. (#1499) 21 21 * generate ETag and Last-Modified headers for mod_ssi based on newest modified include (#1491) 22 22 * support letterhomes in mod_userdir (#1473) 23 * support chained proxies in mod_extforward (#1528) 23 24 24 25 - 1.4.18 - 2007-09-09 25 26

