in reply to Re^3: mod-perl2 re-using $ENV{REMOTE_ADDR}
in thread mod-perl2 re-using $ENV{REMOTE_ADDR}

I'm glad you have something that appears to be working for you. However I find the logic somewhat confusing, and somewhat perverse.

I think the intention is to find a valid external IP, or failing that to fall back to any valid internal IP. If that is indeed the intention, I think it could be expressed more clearly with something like:

my($valid_ip, $valid_external); foreach my $check (@IPs) { # clean it up $check =~ s/^\s*(.*?)\s*$/\1/; # skip if not a valid IP next unless is_ip($check); # overwrite with the last IP we saw that's valid $valid_ip = $check; # skip unless it's an external IP next if (not is_public_ip($check)) or (is_loopback_ip($check)) or ($check eq "41.79.21.90"); # overwrite with the last valid IP that is external $valid_external = $check; } # choose last external IP if we found one, else last valid IP my $ip = $valid_external // $valid_ip;

Your current code favours the last external IP rather than the first one, and the last internal IP rather than the first one, but I cannot tell if either of those choices are intentional. My proposed code honours that preference, but if you actually want the _first_ external IP from the list you could add last; at the end of the loop to short-circuit it; if you want the first internal IP from the list, you can replace $valid_ip = $check with $valid_ip //= $check.

Side-note: the //g option on the cleanup regexp serves no useful purpose, so I removed it.

Hope this helps.

Replies are listed 'Best First'.
Re^5: mod-perl2 re-using $ENV{REMOTE_ADDR}
by bbs2web (Acolyte) on May 03, 2023 at 19:32 UTC
    This is really great, many many thanks!

    So ideally:
    I would like to order the IPs by those present in the HTTP_X_FORWARDED_FOR header, failing which it should use REMOTE_ADDR.
    I would like to prefer the first public IP, alternatively the last valid private IP (closest to the client).

    Your loop is much cleaner and easier to understand, avoids the double negative that I was doing (unshifting REMOTE_ADDR to the beginning of the array and then selecting the last entry). The problem that you high-lighted with my loop also explains the odd case where some small WISPs were transparently natting HTTP behind fake public IPs in their network.

    use Data::Validate::IP qw(is_ip is_public_ip is_loopback_ip); my($valid_ip, $valid_public); my @IPs; if (defined $ENV{HTTP_X_FORWARDED_FOR}) { @IPs=split /,/, $ENV{HTTP_X_FORWARDED_FOR}; }; push(@IPs, $ENV{REMOTE_ADDR}); foreach my $check (@IPs) { # trim whitespaces $check =~ s/\s//g; # skip if not a valid IP next unless is_ip($check); # overwrite with last valid IP #$valid_ip //= $check; # select first valid ip $valid_ip = $check; # skip unless it's a public, loopback or proxy IP next if (not is_public_ip($check)) or (is_loopback_ip($check)) or ($check eq "192.0.2.90"); # overwrite with the last valid public IP $valid_public = $check; # select first public IP last; } # select public IP, if we found one, else last valid IP my $ip = $valid_public // $valid_ip; print "Content-type: text/html\nCache-Control: max-age=0,no-cache,no-s +tore,post-check=0,pre-check=0\n\n<html><head><title>Current IP Check< +/title></head><body>Current IP Address: $ip</body></html>";