use strict;
our $VERSION = 0.04;
use Plack::Util::Accessor qw( IPFile );
sub _build_real_ip {
my ($env) = @_;
my @possible_forwarded_ips
= grep {
$_->iptype
!~ /^(?:LOOPBACK|LINK\-LOCAL|PRIVATE|UNIQUE\-LOCAL\-UNICAST|LINK\-LOCAL\-UNICAST|RESERVED)$/xo
}
grep {defined}
map { Net::IP::XS->new($_) }
grep {defined} (
$env->{'HTTP_X_REAL_IP'},
$env->{'HTTP_CLIENT_IP'},
split( /,\s*/xo, $env->{'HTTP_X_FORWARDED_FOR'} // '' ),
$env->{'HTTP_X_FORWARDED'},
$env->{'HTTP_X_CLUSTER_CLIENT_IP'},
$env->{'HTTP_FORWARDED_FOR'},
$env->{'HTTP_FORWARDED'},
);
return $possible_forwarded_ips[0]
// Net::IP::XS->new( $env->{'REMOTE_ADDR'} // '' );
}
sub prepare_app {
my $self = shift;
if (my $ipfiles = $self->IPFile) {
my @ipfiles = ref $ipfiles ? @{ $ipfiles } : ($ipfiles);
for my $ipfile (@ipfiles) {
my $match = Net::IP::Match::Trie->new();
open my $fh, "<", $ipfile or die "$!";
while (<$fh>) {
chomp;
my ($CIDRS, $lable) = split(/[,|\s]/, $_);
$match->add( $lable , [$CIDRS] );
}
push @{ $self->{IPMatcher} }, $match;
}
}
}
sub call {
my $self = shift;
my $env = shift;
my $ip;
if ($env->{QUERY_STRING} =~ /(?:^|&)ip=([^&]+)/) {
$ip = $1;
}
else {
$ip = _build_real_ip($env)->ip;
}
$env->{MATCH_IP} = $ip;
foreach my $matcher (@{ $self->{IPMatcher} }) {
my $label = $matcher->match_ip($ip);
$env->{IPMATCH_LABEL} = $label and last;
}
return $self->app->($env);
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Plack::Middleware::IPMatch - 查找指定 IP (CIDR) 所对应的标签 LABEL
=head1 SYNOPSIS
enable 'Plack::Middleware::IPMatch',
IPFile => [ '/path/to/CT.txt', '/path/to/CNC.txt' ];
=head1 DESCRIPTION
Plack::Middleware::IPMatch 这个是使用, Net::IP::Match::Trie 来实现的超级快的进行 CIDR 转换成指定的 LABEL 的模块.
因为是使用的前缀树实现, 所以有着超级快的查询速度.
=head1 CONFIGURATION
=head2 IPFile
IPFile => '/path/to/CT-IP.dat';
IPFile => [ '/path/to/CT-IP.dat', '/path/to/CNC-IP.dat' ];
这个需要本身有自己整理过的 IP 数据库, 然后给整个数据库存成文本格式
=head2 IPFile 格式
格式需要自己来收集 IP 数据, 存成如下格式的文本
112.122.128.0/21,CNC-AH-AH
112.122.136.0/23,CNC-AH-AH
112.122.138.0/25,CNC-AH-AH
112.122.138.128/29,CNC-AH-AH
112.122.138.144/28,CNC-AH-AH
112.122.138.160/27,CNC-AH-AH
112.122.138.192/26,CNC-AH-AH
=head1 Header
=head2 IPMATCH_LABEL
默认会在 $env 的哈希中增加 C<IPMATCH_LABEL> 的字段的 Header, 这就是查询的结果
可以使用如下的方式来访问
$env->{IPMATCH_LABEL}
=head2 MATCH_IP
进行 ip 转换时候所使用的 IP 地址, 默认会存到这个 header 中传给应用
=head1 AUTHOR
扶凯 E<lt>iakuf@163.comE<gt>
=head1 LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 SEE ALSO
=cut