Mojolicious-Plugin-ClientIP
view release on metacpan or search on metacpan
lib/Mojolicious/Plugin/ClientIP.pm view on Meta::CPAN
package Mojolicious::Plugin::ClientIP;
use Mojo::Base 'Mojolicious::Plugin';
our $VERSION = '0.02';
has 'ignore';
sub register {
my $self = shift;
my ($app, $conf) = @_;
$self->ignore([
@{$conf->{private} || [qw(127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16) ]},
@{$conf->{ignore} || []}
]);
$app->helper(client_ip => sub {
my ($c) = @_;
state $key = '__plugin_clientip_ip';
return $c->stash($key) if $c->stash($key);
my $xff = $c->req->headers->header('X-Forwarded-For') // '';
my @candidates = reverse grep { $_ } split /,\s*/, $xff;
my $ip = $self->_find(\@candidates) // $c->tx->remote_address;
$c->stash($key => $ip);
return $ip;
});
}
sub _find {
my $self = shift;
my ($candidates) = @_;
state $octet = '(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})';
state $ip4 = qr/\A$octet\.$octet\.$octet\.$octet\z/;
for (@$candidates) {
next unless /$ip4/;
next if _match($_, $self->ignore);
return $_;
}
return;
}
sub _match {
my ($ip, $ips) = @_;
my $ip_bit = _to_bit($ip);
for (@$ips) {
return 1 if $ip eq $_;
if (my ($net, $prefix) = m{^([\d\.]+)/(\d+)$}) {
my $match_ip_bit = _to_bit($1);
return 1 if substr($ip_bit, 0, $2) eq substr($match_ip_bit, 0, $2);
}
}
return;
}
sub _to_bit {
my ($ip) = @_;
join '', map { unpack('B8', pack('C', $_)) } split /\./, $ip;
}
1;
__END__
=encoding utf-8
=head1 NAME
Mojolicious::Plugin::ClientIP - Get client's IP address from X-Forwarded-For
=head1 SYNOPSIS
use Mojolicious::Lite;
plugin 'ClientIP';
get '/' => sub {
my $c = shift;
$c->render(text => $c->client_ip);
};
app->start;
=head1 DESCRIPTION
Mojolicious::Plugin::ClientIP is a Mojolicious plugin to get an IP address looks like client, not proxy, from X-Forwarded-For header.
=head1 METHODS
=head2 client_ip
Find a client IP address from X-Forwarded-For. Private network addresses in XFF are ignored by default. If the good IP address is not found, it returns Mojo::Transaction#remote_address.
=head1 OPTIONS
=head2 ignore
Specify IP list to be ignored with ArrayRef.
plugin 'ClientIP', ignore => [qw(192.0.2.1 192.0.2.16/28)];
=head2 private
Specify IP list which is handled as private IP address.
This is a optional parameter.
Default values are C<[qw(127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16)]> as defined in RFC 1918.
plugin 'ClientIP', ignore => [qw(192.0.2.16/28)], private => [qw(10.0.0.0/8)];
=head1 LICENSE
Copyright (C) Six Apart Ltd. E<lt>sixapart@cpan.orgE<gt>
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 AUTHOR
ziguzagu E<lt>ziguzagu@cpan.orgE<gt>
=cut
( run in 0.571 second using v1.01-cache-2.11-cpan-39bf76dae61 )