Ticket #1528: 1528.3.patch

File 1528.3.patch, 9.7 kB (added by glen, 4 months ago)

test3

  • src/mod_extforward.c

    old new  
    2020/** 
    2121 * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com 
    2222 *                  extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu 
     23 *                  support chained proxies by glen@delfi.ee, #1528 
    2324 * 
    2425 * Config example: 
    2526 * 
     
    3334 *       Note that "all" has precedence over specific entries, 
    3435 *       so "all except" setups will not work. 
    3536 * 
     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 * 
    3641 * Note: The effect of this module is variable on $HTTP["remotip"] directives and 
    3742 *       other module's remote ip dependent actions. 
    3843 *  Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. 
     
    225230                char *base, *curr; 
    226231                /* state variable, 0 means not in string, 1 means in string */ 
    227232                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++) { 
    230234                        if (in_str) { 
    231                                 if ( (*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':' ) { 
     235                                if ((*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':') { 
    232236                                        /* 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); 
    234238                                        /* change state to not in string */ 
    235239                                        in_str = 0; 
    236240                                } 
    237241                        } else { 
    238                                 if (*curr >= '0' && *curr <= '9') 
    239                                 { 
     242                                if (*curr >= '0' && *curr <= '9') { 
    240243                                        /* found leading char of an IP address, move base pointer and change state */ 
    241244                                        base = curr; 
    242245                                        in_str = 1; 
     
    244247                        } 
    245248                } 
    246249                /* 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); 
    250252                } 
    251253        } 
    252254        return result; 
     
    255257#define IP_TRUSTED 1 
    256258#define IP_UNTRUSTED 0 
    257259/* 
    258    check whether ip is trusted, return 1 for trusted , 0 for untrusted 
    259 */ 
     260 * check whether ip is trusted, return 1 for trusted , 0 for untrusted 
     261 */ 
    260262static int is_proxy_trusted(const char *ipstr, plugin_data *p) 
    261263{ 
    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 
    263266        if (allds) { 
    264                 if (strcasecmp(allds->value->ptr,"trust") == 0) 
     267                if (strcasecmp(allds->value->ptr, "trust") == 0) { 
    265268                        return IP_TRUSTED; 
    266                 else 
     269                } else { 
    267270                        return IP_UNTRUSTED; 
     271                } 
    268272        } 
    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; 
    270275} 
    271276 
     277/* 
     278 * Return char *ip of last address of proxy that is not trusted. 
     279 * Do not accept "all" keyword here. 
     280 */ 
     281static 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 
    272294struct addrinfo *ipstr_to_sockaddr(const char *host) 
    273295{ 
    274296   struct addrinfo hints, *res0; 
     
    316338        struct addrinfo *addrlist = NULL; 
    317339#endif 
    318340        const char *dst_addr_str = NULL; 
    319         int i; 
    320341        array *forward_array = NULL; 
    321         char *real_remote_addr = NULL; 
     342        const char *real_remote_addr = NULL; 
    322343#ifdef HAVE_IPV6 
    323344#endif 
    324345 
     
    342363                return HANDLER_GO_ON; 
    343364        } 
    344365 
    345         /* if the remote ip itself is not trusted , then do nothing */ 
    346366#ifdef HAVE_IPV6 
    347367        dst_addr_str = inet_ntop(con->dst_addr.plain.sa_family, 
    348368                      con->dst_addr.plain.sa_family == AF_INET6 ? 
     
    353373#else 
    354374        dst_addr_str = inet_ntoa(con->dst_addr.ipv4.sin_addr); 
    355375#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)) { 
    357379                if (con->conf.log_request_handling) { 
    358380                        log_error_write(srv, __FILE__, __LINE__, "s", 
    359381                                        "remote address is NOT a trusted proxy, skipping"); 
     
    362384                return HANDLER_GO_ON; 
    363385        } 
    364386 
     387        /* build forward_array from forwarded data_string */ 
    365388        forward_array = extract_forward_array(forwarded->value); 
     389        real_remote_addr = last_not_in_array(forward_array, p); 
     390        array_free(forward_array); 
    366391 
    367         /* Testing shows that multiple headers and multiple values in one header 
    368            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  
    380392        if (real_remote_addr != NULL) { /* parsed */ 
    381393                sock_addr sock; 
    382394                struct addrinfo *addrs_left; 
    383395                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"); 
    385397 
    386                 if (forwarded_proto && !strcmp(forwarded_proto->value->ptr, "https")) 
     398                if (forwarded_proto && !strcmp(forwarded_proto->value->ptr, "https")) { 
    387399                        srv_sock->is_proxy_ssl = 1; 
    388                 else 
     400                } else { 
    389401                        srv_sock->is_proxy_ssl = 0; 
     402                } 
    390403 
    391404                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); 
    394406                } 
    395407#ifdef HAVE_IPV6 
    396408                addrlist = ipstr_to_sockaddr(real_remote_addr); 
    397409                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) { 
    400411                        sock.plain.sa_family = addrs_left->ai_family; 
    401                         if ( sock.plain.sa_family == AF_INET ) { 
     412                        if (sock.plain.sa_family == AF_INET) { 
    402413                                sock.ipv4.sin_addr = ((struct sockaddr_in*)addrs_left->ai_addr)->sin_addr; 
    403414                                break; 
    404                         } else if ( sock.plain.sa_family == AF_INET6 ) { 
     415                        } else if (sock.plain.sa_family == AF_INET6) { 
    405416                                sock.ipv6.sin6_addr = ((struct sockaddr_in6*)addrs_left->ai_addr)->sin6_addr; 
    406417                                break; 
    407418                        } 
     
    436447                if (addrlist != NULL ) freeaddrinfo(addrlist); 
    437448#endif 
    438449        } 
    439         array_free(forward_array); 
    440450 
    441451        /* not found */ 
    442452        return HANDLER_GO_ON; 
  • tests/mod-extforward.conf

    old new  
    11debug.log-request-handling   = "enable" 
    2 debug.log-response-header   = "enable" 
    3 debug.log-request-header   = "enable" 
     2debug.log-response-header   = "disable" 
     3debug.log-request-header   = "disable" 
    44 
    55server.document-root         = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/" 
    66server.pid-file              = env.SRCDIR + "/tmp/lighttpd/lighttpd.pid" 
     
    2727 
    2828extforward.forwarder = ( 
    2929        "127.0.0.1" => "trust", 
     30        "127.0.30.1" => "trust", 
    3031) 
  • tests/mod-extforward.t

    old new  
    88 
    99use strict; 
    1010use IO::Socket; 
    11 use Test::More tests => 2
     11use Test::More tests => 5
    1212use LightyTest; 
    1313 
    1414my $tf = LightyTest->new(); 
     
    1818 
    1919ok($tf->start_proc == 0, "Starting lighttpd") or die(); 
    2020 
    21 ## check if If-Modified-Since, If-None-Match works 
    22  
    2321$t->{REQUEST} = ( <<EOF 
    2422GET /ip.pl HTTP/1.0 
    2523Host: www.example.org 
     
    2725EOF 
    2826); 
    2927$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'); 
     28ok($tf->handle_http($t) == 0, 'expect 127.0.10.1, from single ip'); 
    3129 
    3230$t->{REQUEST} = ( <<EOF 
    3331GET /ip.pl HTTP/1.0 
     
    3634EOF 
    3735); 
    3836$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'); 
     37ok($tf->handle_http($t) == 0, 'expect 127.0.20.1, from two ips'); 
    4038 
     39$t->{REQUEST} = ( <<EOF 
     40GET /ip.pl HTTP/1.0 
     41Host: www.example.org 
     42X-Forwarded-For: 127.0.10.1, 127.0.20.1, 127.0.30.1 
     43EOF 
     44); 
     45$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.20.1' } ]; 
     46ok($tf->handle_http($t) == 0, 'expect 127.0.20.1, from chained proxies'); 
     47 
    4148ok($tf->stop_proc == 0, "Stopping lighttpd"); 
  • tests/docroot/www/ip.pl

    old new  
    22print "Content-Type: text/html\r\n\r\n"; 
    33print $ENV{'REMOTE_ADDR'}; 
    44 
     5if ($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 
    5130; 
  • NEWS

    old new  
    2020  * HTTPS env var should be "on" when using mod_extforward and the X-Forwarded-Proto header is set. (#1499) 
    2121  * generate ETag and Last-Modified headers for mod_ssi based on newest modified include (#1491) 
    2222  * support letterhomes in mod_userdir (#1473) 
     23  * support chained proxies in mod_extforward (#1528) 
    2324 
    2425- 1.4.18 - 2007-09-09 
    2526