our
@ISA
=
qw()
;
my
@geoip_default_path
=
qw(
/usr/local/share/GeoIP
/usr/share/GeoIP
/var/lib/GeoIP
/opt/share/GeoIP
)
;
my
@geoip_types
=
qw( city country isp asn )
;
my
%geoip_default_files
= (
'city'
=> [
'GeoIPCity.dat'
,
'GeoLiteCity.dat'
],
'country'
=> [
'GeoIP.dat'
],
'isp'
=> [
'GeoIPISP.dat'
],
'asn'
=> [
'GeoIPASNum.dat'
],
);
my
%geoip2_default_files
= (
'city'
=> [
'GeoIP2-City.mmdb'
,
'GeoLite2-City.mmdb'
,
'dbip-city.mmdb'
,
'dbip-city-lite.mmdb'
],
'country'
=> [
'GeoIP2-Country.mmdb'
,
'GeoLite2-Country.mmdb'
,
'dbip-country.mmdb'
,
'dbip-country-lite.mmdb'
],
'isp'
=> [
'GeoIP2-ISP.mmdb'
,
'GeoLite2-ISP.mmdb'
],
'asn'
=> [
'GeoIP2-ASN.mmdb'
,
'GeoLite2-ASN.mmdb'
],
);
my
%country_to_continent
= (
'AP'
=>
'AS'
,
'EU'
=>
'EU'
,
'AD'
=>
'EU'
,
'AE'
=>
'AS'
,
'AF'
=>
'AS'
,
'AG'
=>
'NA'
,
'AI'
=>
'NA'
,
'AL'
=>
'EU'
,
'AM'
=>
'AS'
,
'CW'
=>
'NA'
,
'AO'
=>
'AF'
,
'AQ'
=>
'AN'
,
'AR'
=>
'SA'
,
'AS'
=>
'OC'
,
'AT'
=>
'EU'
,
'AU'
=>
'OC'
,
'AW'
=>
'NA'
,
'AZ'
=>
'AS'
,
'BA'
=>
'EU'
,
'BB'
=>
'NA'
,
'BD'
=>
'AS'
,
'BE'
=>
'EU'
,
'BF'
=>
'AF'
,
'BG'
=>
'EU'
,
'BH'
=>
'AS'
,
'BI'
=>
'AF'
,
'BJ'
=>
'AF'
,
'BM'
=>
'NA'
,
'BN'
=>
'AS'
,
'BO'
=>
'SA'
,
'BR'
=>
'SA'
,
'BS'
=>
'NA'
,
'BT'
=>
'AS'
,
'BV'
=>
'AN'
,
'BW'
=>
'AF'
,
'BY'
=>
'EU'
,
'BZ'
=>
'NA'
,
'CA'
=>
'NA'
,
'CC'
=>
'AS'
,
'CD'
=>
'AF'
,
'CF'
=>
'AF'
,
'CG'
=>
'AF'
,
'CH'
=>
'EU'
,
'CI'
=>
'AF'
,
'CK'
=>
'OC'
,
'CL'
=>
'SA'
,
'CM'
=>
'AF'
,
'CN'
=>
'AS'
,
'CO'
=>
'SA'
,
'CR'
=>
'NA'
,
'CU'
=>
'NA'
,
'CV'
=>
'AF'
,
'CX'
=>
'AS'
,
'CY'
=>
'AS'
,
'CZ'
=>
'EU'
,
'DE'
=>
'EU'
,
'DJ'
=>
'AF'
,
'DK'
=>
'EU'
,
'DM'
=>
'NA'
,
'DO'
=>
'NA'
,
'DZ'
=>
'AF'
,
'EC'
=>
'SA'
,
'EE'
=>
'EU'
,
'EG'
=>
'AF'
,
'EH'
=>
'AF'
,
'ER'
=>
'AF'
,
'ES'
=>
'EU'
,
'ET'
=>
'AF'
,
'FI'
=>
'EU'
,
'FJ'
=>
'OC'
,
'FK'
=>
'SA'
,
'FM'
=>
'OC'
,
'FO'
=>
'EU'
,
'FR'
=>
'EU'
,
'FX'
=>
'EU'
,
'GA'
=>
'AF'
,
'GB'
=>
'EU'
,
'GD'
=>
'NA'
,
'GE'
=>
'AS'
,
'GF'
=>
'SA'
,
'GH'
=>
'AF'
,
'GI'
=>
'EU'
,
'GL'
=>
'NA'
,
'GM'
=>
'AF'
,
'GN'
=>
'AF'
,
'GP'
=>
'NA'
,
'GQ'
=>
'AF'
,
'GR'
=>
'EU'
,
'GS'
=>
'AN'
,
'GT'
=>
'NA'
,
'GU'
=>
'OC'
,
'GW'
=>
'AF'
,
'GY'
=>
'SA'
,
'HK'
=>
'AS'
,
'HM'
=>
'AN'
,
'HN'
=>
'NA'
,
'HR'
=>
'EU'
,
'HT'
=>
'NA'
,
'HU'
=>
'EU'
,
'ID'
=>
'AS'
,
'IE'
=>
'EU'
,
'IL'
=>
'AS'
,
'IN'
=>
'AS'
,
'IO'
=>
'AS'
,
'IQ'
=>
'AS'
,
'IR'
=>
'AS'
,
'IS'
=>
'EU'
,
'IT'
=>
'EU'
,
'JM'
=>
'NA'
,
'JO'
=>
'AS'
,
'JP'
=>
'AS'
,
'KE'
=>
'AF'
,
'KG'
=>
'AS'
,
'KH'
=>
'AS'
,
'KI'
=>
'OC'
,
'KM'
=>
'AF'
,
'KN'
=>
'NA'
,
'KP'
=>
'AS'
,
'KR'
=>
'AS'
,
'KW'
=>
'AS'
,
'KY'
=>
'NA'
,
'KZ'
=>
'AS'
,
'LA'
=>
'AS'
,
'LB'
=>
'AS'
,
'LC'
=>
'NA'
,
'LI'
=>
'EU'
,
'LK'
=>
'AS'
,
'LR'
=>
'AF'
,
'LS'
=>
'AF'
,
'LT'
=>
'EU'
,
'LU'
=>
'EU'
,
'LV'
=>
'EU'
,
'LY'
=>
'AF'
,
'MA'
=>
'AF'
,
'MC'
=>
'EU'
,
'MD'
=>
'EU'
,
'MG'
=>
'AF'
,
'MH'
=>
'OC'
,
'MK'
=>
'EU'
,
'ML'
=>
'AF'
,
'MM'
=>
'AS'
,
'MN'
=>
'AS'
,
'MO'
=>
'AS'
,
'MP'
=>
'OC'
,
'MQ'
=>
'NA'
,
'MR'
=>
'AF'
,
'MS'
=>
'NA'
,
'MT'
=>
'EU'
,
'MU'
=>
'AF'
,
'MV'
=>
'AS'
,
'MW'
=>
'AF'
,
'MX'
=>
'NA'
,
'MY'
=>
'AS'
,
'MZ'
=>
'AF'
,
'NA'
=>
'AF'
,
'NC'
=>
'OC'
,
'NE'
=>
'AF'
,
'NF'
=>
'OC'
,
'NG'
=>
'AF'
,
'NI'
=>
'NA'
,
'NL'
=>
'EU'
,
'NO'
=>
'EU'
,
'NP'
=>
'AS'
,
'NR'
=>
'OC'
,
'NU'
=>
'OC'
,
'NZ'
=>
'OC'
,
'OM'
=>
'AS'
,
'PA'
=>
'NA'
,
'PE'
=>
'SA'
,
'PF'
=>
'OC'
,
'PG'
=>
'OC'
,
'PH'
=>
'AS'
,
'PK'
=>
'AS'
,
'PL'
=>
'EU'
,
'PM'
=>
'NA'
,
'PN'
=>
'OC'
,
'PR'
=>
'NA'
,
'PS'
=>
'AS'
,
'PT'
=>
'EU'
,
'PW'
=>
'OC'
,
'PY'
=>
'SA'
,
'QA'
=>
'AS'
,
'RE'
=>
'AF'
,
'RO'
=>
'EU'
,
'RU'
=>
'EU'
,
'RW'
=>
'AF'
,
'SA'
=>
'AS'
,
'SB'
=>
'OC'
,
'SC'
=>
'AF'
,
'SD'
=>
'AF'
,
'SE'
=>
'EU'
,
'SG'
=>
'AS'
,
'SH'
=>
'AF'
,
'SI'
=>
'EU'
,
'SJ'
=>
'EU'
,
'SK'
=>
'EU'
,
'SL'
=>
'AF'
,
'SM'
=>
'EU'
,
'SN'
=>
'AF'
,
'SO'
=>
'AF'
,
'SR'
=>
'SA'
,
'ST'
=>
'AF'
,
'SV'
=>
'NA'
,
'SY'
=>
'AS'
,
'SZ'
=>
'AF'
,
'TC'
=>
'NA'
,
'TD'
=>
'AF'
,
'TF'
=>
'AN'
,
'TG'
=>
'AF'
,
'TH'
=>
'AS'
,
'TJ'
=>
'AS'
,
'TK'
=>
'OC'
,
'TM'
=>
'AS'
,
'TN'
=>
'AF'
,
'TO'
=>
'OC'
,
'TL'
=>
'AS'
,
'TR'
=>
'EU'
,
'TT'
=>
'NA'
,
'TV'
=>
'OC'
,
'TW'
=>
'AS'
,
'TZ'
=>
'AF'
,
'UA'
=>
'EU'
,
'UG'
=>
'AF'
,
'UM'
=>
'OC'
,
'US'
=>
'NA'
,
'UY'
=>
'SA'
,
'UZ'
=>
'AS'
,
'VA'
=>
'EU'
,
'VC'
=>
'NA'
,
'VE'
=>
'SA'
,
'VG'
=>
'NA'
,
'VI'
=>
'NA'
,
'VN'
=>
'AS'
,
'VU'
=>
'OC'
,
'WF'
=>
'OC'
,
'WS'
=>
'OC'
,
'YE'
=>
'AS'
,
'YT'
=>
'AF'
,
'RS'
=>
'EU'
,
'ZA'
=>
'AF'
,
'ZM'
=>
'AF'
,
'ME'
=>
'EU'
,
'ZW'
=>
'AF'
,
'AX'
=>
'EU'
,
'GG'
=>
'EU'
,
'IM'
=>
'EU'
,
'JE'
=>
'EU'
,
'BL'
=>
'NA'
,
'MF'
=>
'NA'
,
'BQ'
=>
'NA'
,
'SS'
=>
'AF'
,
'**'
=>
'**'
,
);
sub
new {
my
(
$class
,
$conf
) =
@_
;
$class
=
ref
(
$class
) ||
$class
;
my
$self
= {};
bless
(
$self
,
$class
);
$self
->{cache} = ();
$self
->init_database(
$conf
|| {});
$self
;
}
sub
init_database {
my
(
$self
,
$opts
) =
@_
;
$opts
->{wanted}->{city} = 1
if
$opts
->{wanted}->{country};
$opts
->{wanted}->{isp} = 1
if
$opts
->{wanted}->{asn};
my
$geodb_opts
= {
'module'
=>
$opts
->{conf}->{module} ||
undef
,
'dbs'
=>
$opts
->{conf}->{options} ||
undef
,
'wanted'
=>
$opts
->{wanted} ||
undef
,
'search_path'
=>
defined
$opts
->{conf}->{geodb_search_path} ?
$opts
->{conf}->{geodb_search_path} : \
@geoip_default_path
,
};
my
(
$db
,
$dbapi
,
$loaded
);
if
(!
$db
&& (!
$geodb_opts
->{module} ||
$geodb_opts
->{module} eq
'geoip2'
)) {
(
$db
,
$dbapi
) =
$self
->load_geoip2(
$geodb_opts
);
$loaded
=
'geoip2'
if
$db
;
}
if
(!
$db
&& (!
$geodb_opts
->{module} ||
$geodb_opts
->{module} eq
'geoip'
)) {
(
$db
,
$dbapi
) =
$self
->load_geoip(
$geodb_opts
);
$loaded
=
'geoip'
if
$db
;
}
if
(!
$db
&&
$geodb_opts
->{module} &&
$geodb_opts
->{module} eq
'dbfile'
) {
(
$db
,
$dbapi
) =
$self
->load_dbfile(
$geodb_opts
);
$loaded
=
'dbfile'
if
$db
;
}
if
(!
$db
&& (!
$geodb_opts
->{module} ||
$geodb_opts
->{module} eq
'fast'
)) {
(
$db
,
$dbapi
) =
$self
->load_fast(
$geodb_opts
);
$loaded
=
'fast'
if
$db
;
}
if
(!
$db
) {
dbg(
"geodb: No supported database could be loaded"
);
die
(
"No supported GeoDB database could be loaded\n"
);
}
if
(!
$dbapi
->{country} &&
$dbapi
->{city}) {
$dbapi
->{country} =
$dbapi
->{city};
}
if
(!
$dbapi
->{country_v6} &&
$dbapi
->{city_v6}) {
$dbapi
->{country_v6} =
$dbapi
->{city_v6}
}
if
(
$loaded
eq
'geoip2'
) {
if
(!
$dbapi
->{asn} &&
$dbapi
->{isp}) {
$dbapi
->{asn} =
$dbapi
->{isp};
}
if
(!
$dbapi
->{asn_v6} &&
$dbapi
->{isp_v6}) {
$dbapi
->{asn_v6} =
$dbapi
->{isp_v6}
}
}
$self
->{db} =
$db
;
$self
->{dbapi} =
$dbapi
;
foreach
(@{
$self
->get_dbinfo()}) {
dbg(
"geodb: database info: "
.
$_
);
}
return
1;
}
sub
load_geoip2 {
my
(
$self
,
$geodb_opts
) =
@_
;
my
(
$db
,
$dbapi
,
$ok
);
my
$errwarn
= (
$geodb_opts
->{module}||
''
) eq
'geoip2'
;
eval
{
} or
do
{
my
$err
= $@;
$err
=~ s/ at .*//s;
$err
=
"geodb: MaxMind::DB::Reader (GeoIP2) module load failed: $err"
;
$errwarn
?
warn
(
"$err\n"
) : dbg(
$err
);
return
(
undef
,
undef
);
};
my
%path
;
foreach
my
$dbtype
(
@geoip_types
) {
next
if
$dbtype
eq
'country'
&&
$db
->{city};
next
if
$dbtype
eq
'asn'
&&
$db
->{isp};
next
if
$geodb_opts
->{wanted} && !
$geodb_opts
->{wanted}->{
$dbtype
};
if
(!
defined
$geodb_opts
->{dbs}->{
$dbtype
}) {
PATHS_GEOIP2:
foreach
my
$p
(@{
$geodb_opts
->{search_path}}) {
foreach
my
$f
(@{
$geoip2_default_files
{
$dbtype
}}) {
if
(-f
"$p/$f"
) {
$path
{
$dbtype
} =
"$p/$f"
;
dbg(
"geodb: GeoIP2: search found $dbtype $p/$f"
);
last
PATHS_GEOIP2;
}
}
}
}
else
{
if
(!-f
$geodb_opts
->{dbs}->{
$dbtype
}) {
dbg(
"geodb: GeoIP2: $dbtype database requested, but not found: "
.
$geodb_opts
->{dbs}->{
$dbtype
});
next
;
}
$path
{
$dbtype
} =
$geodb_opts
->{dbs}->{
$dbtype
};
}
if
(
defined
$path
{
$dbtype
}) {
eval
{
$db
->{
$dbtype
} = MaxMind::DB::Reader->new(
file
=>
$path
{
$dbtype
},
);
die
"unknown error"
unless
$db
->{
$dbtype
};
1;
};
if
($@ || !
$db
->{
$dbtype
}) {
my
$err
= $@;
$err
=~ s/\s+Trace begun.*//s;
$err
=~ s/ at .*//s;
dbg(
"geodb: GeoIP2: $dbtype load failed: $err"
);
}
else
{
dbg(
"geodb: GeoIP2: loaded $dbtype from $path{$dbtype}"
);
$ok
= 1;
}
}
else
{
my
$from
=
defined
$geodb_opts
->{dbs}->{
$dbtype
} ?
$geodb_opts
->{dbs}->{
$dbtype
} :
"default locations"
;
dbg(
"geodb: GeoIP2: $dbtype database not found from $from"
);
}
}
if
(!
$ok
) {
warn
(
"geodb: GeoIP2 requested, but no databases could be loaded\n"
)
if
$errwarn
;
return
(
undef
,
undef
)
}
$db
->{city} and
$dbapi
->{dbinfo_city} =
sub
{
my
$m
=
$_
[0]->{db}->{city}->metadata();
return
"GeoIP2 city: "
.
$m
->description()->{en}.
" / "
.
localtime
(
$m
->build_epoch());
};
$db
->{country} and
$dbapi
->{dbinfo_country} =
sub
{
my
$m
=
$_
[0]->{db}->{country}->metadata();
return
"GeoIP2 country: "
.
$m
->description()->{en}.
" / "
.
localtime
(
$m
->build_epoch());
};
$db
->{isp} and
$dbapi
->{dbinfo_isp} =
sub
{
my
$m
=
$_
[0]->{db}->{isp}->metadata();
return
"GeoIP2 isp: "
.
$m
->description()->{en}.
" / "
.
localtime
(
$m
->build_epoch());
};
$db
->{asn} and
$dbapi
->{dbinfo_asn} =
sub
{
my
$m
=
$_
[0]->{db}->{asn}->metadata();
return
"GeoIP2 asn: "
.
$m
->description()->{en}.
" / "
.
localtime
(
$m
->build_epoch());
};
$db
->{city} and
$dbapi
->{city} =
$dbapi
->{city_v6} =
sub
{
my
$res
= {};
my
$city
;
eval
{
$city
=
$_
[0]->{db}->{city}->record_for_address(
$_
[1]);
1;
} or
do
{
$@ =~ s/\s+Trace begun.*//s;
dbg(
"geodb: GeoIP2 city query failed for $_[1]: $@"
);
return
$res
;
};
eval
{
$res
->{city_name} =
$city
->{city}->{names}->{en};
$res
->{country} =
$city
->{country}->{iso_code};
$res
->{country_name} =
$city
->{country}->{names}->{en};
$res
->{continent} =
$city
->{continent}->{code};
$res
->{continent_name} =
$city
->{continent}->{names}->{en};
1;
};
return
$res
;
};
$db
->{country} and
$dbapi
->{country} =
$dbapi
->{country_v6} =
sub
{
my
$res
= {};
my
$country
;
eval
{
$country
=
$_
[0]->{db}->{country}->record_for_address(
$_
[1]);
1;
} or
do
{
$@ =~ s/\s+Trace begun.*//s;
dbg(
"geodb: GeoIP2 country query failed for $_[1]: $@"
);
return
$res
;
};
eval
{
$res
->{country} =
$country
->{country}->{iso_code};
$res
->{country_name} =
$country
->{country}->{names}->{en};
$res
->{continent} =
$country
->{continent}->{code};
$res
->{continent_name} =
$country
->{continent}->{names}->{en};
1;
};
return
$res
;
};
$db
->{isp} and
$dbapi
->{isp} =
$dbapi
->{isp_v6} =
sub
{
my
$res
= {};
my
$isp
;
eval
{
$isp
=
$_
[0]->{db}->{isp}->record_for_address(
$_
[1]);
1;
} or
do
{
$@ =~ s/\s+Trace begun.*//s;
dbg(
"geodb: GeoIP2 isp query failed for $_[1]: $@"
);
return
$res
;
};
eval
{
$res
->{asn} =
$isp
->{autonomous_system_number};
$res
->{asn_organization} =
$isp
->{autonomous_system_organization};
$res
->{isp} =
$isp
->{isp};
$res
->{organization} =
$isp
->{organization};
1;
};
return
$res
;
};
$db
->{asn} and
$dbapi
->{asn} =
$dbapi
->{asn_v6} =
sub
{
my
$res
= {};
my
$asn
;
eval
{
$asn
=
$_
[0]->{db}->{asn}->record_for_address(
$_
[1]);
1;
} or
do
{
$@ =~ s/\s+Trace begun.*//s;
dbg(
"geodb: GeoIP2 asn query failed for $_[1]: $@"
);
return
$res
;
};
eval
{
$res
->{asn} =
$asn
->{autonomous_system_number};
$res
->{asn_organization} =
$asn
->{autonomous_system_organization};
1;
};
return
$res
;
};
return
(
$db
,
$dbapi
);
}
sub
load_geoip {
my
(
$self
,
$geodb_opts
) =
@_
;
my
(
$db
,
$dbapi
,
$ok
);
my
(
$gic_wanted
,
$gic_have
,
$gip_wanted
,
$gip_have
);
my
(
$flags
,
$fix_stderr
,
$can_ipv6
);
my
$errwarn
= (
$geodb_opts
->{module}||
''
) eq
'geoip'
;
eval
{
$gip_wanted
= version->parse(
'v1.4.4'
);
$gip_have
= version->parse(Geo::IP->VERSION);
$gic_wanted
= version->parse(
'v1.6.3'
);
eval
{
$gic_have
= version->parse(Geo::IP->lib_version()); };
$gic_have
=
'none'
if
!
defined
$gic_have
;
dbg(
"geodb: GeoIP: versions: Geo::IP $gip_have, C library $gic_have"
);
$flags
= 0;
$fix_stderr
= 0;
if
(
ref
(
$gic_have
) eq
'version'
) {
eval
'$flags = Geo::IP::GEOIP_SILENCE'
if
$gip_wanted
>=
$gip_have
;
$fix_stderr
=
$flags
&&
$gic_wanted
>=
$gic_have
;
}
$can_ipv6
= Geo::IP->VERSION >= 1.39 && Geo::IP->api eq
'CAPI'
;
1;
} or
do
{
my
$err
= $@;
$err
=~ s/ at .*//s;
$err
=
"geodb: Geo::IP module load failed: $err"
;
$errwarn
?
warn
(
"$err\n"
) : dbg(
$err
);
return
(
undef
,
undef
);
};
my
%path
;
foreach
my
$dbtype
(
@geoip_types
) {
next
if
$dbtype
eq
'country'
&&
$db
->{city};
next
if
$dbtype
eq
'asn'
&&
$db
->{isp};
next
if
$geodb_opts
->{wanted} && !
$geodb_opts
->{wanted}->{
$dbtype
};
if
(!
defined
$geodb_opts
->{dbs}->{
$dbtype
}) {
PATHS_GEOIP:
foreach
my
$p
(@{
$geodb_opts
->{search_path}}) {
foreach
my
$f
(@{
$geoip_default_files
{
$dbtype
}}) {
if
(-f
"$p/$f"
) {
$path
{
$dbtype
} =
"$p/$f"
;
dbg(
"geodb: GeoIP: search found $dbtype $p/$f"
);
if
(
$can_ipv6
&&
$f
=~ s/\.(dat)$/v6.$1/i) {
if
(-f
"$p/$f"
) {
$path
{
$dbtype
.
"_v6"
} =
"$p/$f"
;
dbg(
"geodb: GeoIP: search found $dbtype $p/$f"
);
}
}
last
PATHS_GEOIP;
}
}
}
}
else
{
if
(!-f
$geodb_opts
->{dbs}->{
$dbtype
}) {
dbg(
"geodb: GeoIP: $dbtype database requested, but not found: "
.
$geodb_opts
->{dbs}->{
$dbtype
});
next
;
}
$path
{
$dbtype
} =
$geodb_opts
->{dbs}->{
$dbtype
};
}
}
if
(!
$can_ipv6
) {
dbg(
"geodb: GeoIP: IPv6 support not enabled, versions Geo::IP 1.39, GeoIP C API 1.4.7 required"
);
}
if
(
$fix_stderr
) {
open
(OLDERR,
">&STDERR"
);
open
(STDERR,
">/dev/null"
);
}
foreach
my
$dbtype
(
@geoip_types
) {
next
unless
defined
$path
{
$dbtype
};
eval
{
$db
->{
$dbtype
} = Geo::IP->
open
(
$path
{
$dbtype
}, Geo::IP->GEOIP_STANDARD |
$flags
);
if
(
$can_ipv6
&&
defined
$path
{
$dbtype
.
"_v6"
}) {
$db
->{
$dbtype
.
"_v6"
} = Geo::IP->
open
(
$path
{
$dbtype
.
"_v6"
}, Geo::IP->GEOIP_STANDARD |
$flags
);
}
};
if
($@ || !
$db
->{
$dbtype
}) {
my
$err
= $@;
$err
=~ s/ at .*//s;
dbg(
"geodb: GeoIP: database $path{$dbtype} load failed: $err"
);
}
else
{
dbg(
"geodb: GeoIP: loaded $dbtype from $path{$dbtype}"
);
$ok
= 1;
}
}
if
(
$fix_stderr
) {
open
(STDERR,
">&OLDERR"
);
close
(OLDERR);
}
if
(!
$ok
) {
warn
(
"geodb: GeoIP requested, but no databases could be loaded\n"
)
if
$errwarn
;
return
(
undef
,
undef
)
}
$db
->{city} and
$dbapi
->{dbinfo_city} =
sub
{
return
"Geo::IP IPv4 city: "
. (
$_
[0]->{db}->{city}->database_info ||
'?'
).
" / IPv6: "
.
(
$_
[0]->{db}->{city_v6} ?
$_
[0]->{db}->{city_v6}->database_info ||
'?'
:
'no'
)
};
$db
->{country} and
$dbapi
->{dbinfo_country} =
sub
{
return
"Geo::IP IPv4 country: "
. (
$_
[0]->{db}->{country}->database_info ||
'?'
).
" / IPv6: "
.
(
$_
[0]->{db}->{country_v6} ?
$_
[0]->{db}->{country_v6}->database_info ||
'?'
:
'no'
)
};
$db
->{isp} and
$dbapi
->{dbinfo_isp} =
sub
{
return
"Geo::IP IPv4 isp: "
. (
$_
[0]->{db}->{isp}->database_info ||
'?'
).
" / IPv6: "
.
(
$_
[0]->{db}->{isp_v6} ?
$_
[0]->{db}->{isp_v6}->database_info ||
'?'
:
'no'
)
};
$db
->{asn} and
$dbapi
->{dbinfo_asn} =
sub
{
return
"Geo::IP IPv4 asn: "
. (
$_
[0]->{db}->{asn}->database_info ||
'?'
).
" / IPv6: "
.
(
$_
[0]->{db}->{asn_v6} ?
$_
[0]->{db}->{asn_v6}->database_info ||
'?'
:
'no'
)
};
$db
->{city} and
$dbapi
->{city} =
sub
{
my
$res
= {};
my
$city
;
if
(
$_
[1] =~ IS_IPV4_ADDRESS) {
$city
=
$_
[0]->{db}->{city}->record_by_addr(
$_
[1]);
}
elsif
(
$_
[0]->{db}->{city_v6}) {
$city
=
$_
[0]->{db}->{city_v6}->record_by_addr_v6(
$_
[1]);
}
if
(!
defined
$city
) {
dbg(
"geodb: GeoIP city query failed for $_[1]"
);
return
$res
;
}
$res
->{city_name} =
$city
->city;
$res
->{country} =
$city
->country_code;
$res
->{country_name} =
$city
->country_name;
$res
->{continent} =
$city
->continent_code;
return
$res
;
};
$dbapi
->{city_v6} =
$dbapi
->{city}
if
$db
->{city_v6};
$db
->{country} and
$dbapi
->{country} =
sub
{
my
$res
= {};
my
$country
;
eval
{
if
(
$_
[1] =~ IS_IPV4_ADDRESS) {
$country
=
$_
[0]->{db}->{country}->country_code_by_addr(
$_
[1]);
}
elsif
(
$_
[0]->{db}->{country_v6}) {
$country
=
$_
[0]->{db}->{country_v6}->country_code_by_addr_v6(
$_
[1]);
}
1;
};
if
(!
defined
$country
) {
dbg(
"geodb: GeoIP country query failed for $_[1]"
);
return
$res
;
};
$res
->{country} =
$country
||
'XX'
;
$res
->{continent} =
$country_to_continent
{
$country
} ||
'XX'
;
return
$res
;
};
$dbapi
->{country_v6} =
$dbapi
->{country}
if
$db
->{country_v6};
$db
->{isp} and
$dbapi
->{isp} =
sub
{
my
$res
= {};
my
$isp
;
eval
{
if
(
$_
[1] =~ IS_IPV4_ADDRESS) {
$isp
=
$_
[0]->{db}->{isp}->isp_by_addr(
$_
[1]);
}
else
{
return
$res
;
}
1;
};
if
(!
defined
$isp
) {
dbg(
"geodb: GeoIP isp query failed for $_[1]"
);
return
$res
;
};
$res
->{isp} =
$isp
;
return
$res
;
};
$db
->{asn} and
$dbapi
->{asn} =
sub
{
my
$res
= {};
my
$asn
;
eval
{
if
(
$_
[1] =~ IS_IPV4_ADDRESS) {
$asn
=
$_
[0]->{db}->{asn}->isp_by_addr(
$_
[1]);
}
else
{
return
$res
;
}
1;
};
if
(!
defined
$asn
||
$asn
!~ /^((?:AS)?\d+)(?:\s+(.+))?/) {
dbg(
"geodb: GeoIP asn query failed for $_[1]"
);
return
$res
;
};
$res
->{asn} = $1;
$res
->{asn_organization} = $2
if
defined
$2;
return
$res
;
};
return
(
$db
,
$dbapi
);
}
sub
load_dbfile {
my
(
$self
,
$geodb_opts
) =
@_
;
my
(
$db
,
$dbapi
);
my
$errwarn
= (
$geodb_opts
->{module}||
''
) eq
'dbfile'
;
if
(!
defined
$geodb_opts
->{dbs}->{country}) {
my
$err
=
"geodb: IP::Country::DB_File requires geodb_options country:/path/to/ipcc.db"
;
$errwarn
?
warn
(
"$err\n"
) : dbg(
$err
);
return
(
undef
,
undef
);
}
if
(!-f
$geodb_opts
->{dbs}->{country}) {
my
$err
=
"geodb: IP::Country::DB_File database not found: "
.
$geodb_opts
->{dbs}->{country};
$errwarn
?
warn
(
"$err\n"
) : dbg(
$err
);
return
(
undef
,
undef
);
}
eval
{
$db
->{country} = IP::Country::DB_File->new(
$geodb_opts
->{dbs}->{country});
1;
};
if
($@ || !
$db
->{country}) {
my
$err
= $@;
$err
=~ s/ at .*//s;
$err
=
"geodb: IP::Country::DB_File country load failed: $err"
;
$errwarn
?
warn
(
"$err\n"
) : dbg(
$err
);
return
(
undef
,
undef
);
}
else
{
dbg(
"geodb: IP::Country::DB_File loaded country from "
.
$geodb_opts
->{dbs}->{country});
}
$db
->{country} and
$dbapi
->{dbinfo_country} =
sub
{
return
"IP::Country::DB_File country: "
.
localtime
(
$_
[0]->{db}->{country}->db_time());
};
$db
->{country} and
$dbapi
->{country} =
$dbapi
->{country_v6} =
sub
{
my
$res
= {};
my
$country
;
if
(
$_
[1] =~ IS_IPV4_ADDRESS) {
$country
=
$_
[0]->{db}->{country}->inet_atocc(
$_
[1]);
}
else
{
$country
=
$_
[0]->{db}->{country}->inet6_atocc(
$_
[1]);
}
if
(!
defined
$country
) {
dbg(
"geodb: IP::Country::DB_File country query failed for $_[1]"
);
return
$res
;
};
$res
->{country} =
$country
||
'XX'
;
$res
->{continent} =
$country_to_continent
{
$country
} ||
'XX'
;
return
$res
;
};
return
(
$db
,
$dbapi
);
}
sub
load_fast {
my
(
$self
,
$geodb_opts
) =
@_
;
my
(
$db
,
$dbapi
);
my
$errwarn
= (
$geodb_opts
->{module}||
''
) eq
'fast'
;
eval
{
$db
->{country} = IP::Country::Fast->new();
1;
};
if
($@ || !
$db
->{country}) {
my
$err
= $@;
$err
=~ s/ at .*//s;
$err
=
"geodb: IP::Country::Fast load failed: $err"
;
$errwarn
?
warn
(
"$err\n"
) : dbg(
$err
);
return
(
undef
,
undef
);
}
$db
->{country} and
$dbapi
->{dbinfo_country} =
sub
{
return
"IP::Country::Fast country: "
.
localtime
(
$_
[0]->{db}->{country}->db_time());
};
$db
->{country} and
$dbapi
->{country} =
sub
{
my
$res
= {};
my
$country
;
if
(
$_
[1] =~ IS_IPV4_ADDRESS) {
$country
=
$_
[0]->{db}->{country}->inet_atocc(
$_
[1]);
}
else
{
return
$res
;
}
if
(!
defined
$country
) {
dbg(
"geodb: IP::Country::Fast country query failed for $_[1]"
);
return
$res
;
};
$res
->{country} =
$country
||
'XX'
;
$res
->{continent} =
$country_to_continent
{
$country
} ||
'XX'
;
return
$res
;
};
return
(
$db
,
$dbapi
);
}
sub
get_dbinfo {
my
(
$self
,
$db
) =
@_
;
my
@lines
;
foreach
(
@geoip_types
) {
if
(
exists
$self
->{dbapi}->{
"dbinfo_"
.
$_
}) {
push
@lines
,
$self
->{dbapi}->{
"dbinfo_"
.
$_
}->(
$self
) ||
"$_ failed"
;
}
}
return
\
@lines
;
}
sub
get_country {
my
(
$self
,
$ip
) =
@_
;
return
undef
if
!
defined
$ip
||
$ip
!~ /\S/;
if
(
$ip
=~ IS_IP_PRIVATE) {
return
'**'
;
}
if
(
$ip
!~ IS_IP_ADDRESS) {
$ip
= name_to_ip(
$ip
);
return
'XX'
if
!
defined
$ip
;
}
if
(
$self
->{dbapi}->{city}) {
return
$self
->_get(
'city'
,
$ip
)->{country} ||
'XX'
;
}
elsif
(
$self
->{dbapi}->{country}) {
return
$self
->_get(
'country'
,
$ip
)->{country} ||
'XX'
;
}
else
{
return
undef
;
}
}
sub
get_continent {
my
(
$self
,
$ip
) =
@_
;
return
undef
if
!
defined
$ip
||
$ip
!~ /\S/;
if
(
length
(
$ip
) == 2) {
return
$country_to_continent
{
uc
(
$ip
)} ||
'XX'
;
}
if
(
$self
->{dbapi}->{city}) {
return
$self
->_get(
'city'
,
$ip
)->{continent} ||
'XX'
;
}
elsif
(
$self
->{dbapi}->{country}) {
return
$self
->_get(
'country'
,
$ip
)->{continent} ||
'XX'
;
}
else
{
return
undef
;
}
}
sub
get_isp {
my
(
$self
,
$ip
) =
@_
;
return
undef
if
!
defined
$ip
||
$ip
!~ /\S/;
if
(
$self
->{dbapi}->{isp}) {
return
$self
->_get(
'isp'
,
$ip
)->{isp};
}
else
{
return
undef
;
}
}
sub
get_isp_org {
my
(
$self
,
$ip
) =
@_
;
return
undef
if
!
defined
$ip
||
$ip
!~ /\S/;
if
(
$self
->{dbapi}->{isp}) {
return
$self
->_get(
'isp'
,
$ip
)->{organization};
}
else
{
return
undef
;
}
}
sub
get_asn {
my
(
$self
,
$ip
) =
@_
;
return
undef
if
!
defined
$ip
||
$ip
!~ /\S/;
if
(
$self
->{dbapi}->{asn}) {
return
$self
->_get(
'asn'
,
$ip
)->{asn};
}
elsif
(
$self
->{dbapi}->{isp}) {
return
$self
->_get(
'isp'
,
$ip
)->{asn};
}
else
{
return
undef
;
}
}
sub
get_asn_org {
my
(
$self
,
$ip
) =
@_
;
return
undef
if
!
defined
$ip
||
$ip
!~ /\S/;
if
(
$self
->{dbapi}->{asn}) {
return
$self
->_get(
'asn'
,
$ip
)->{asn_organization};
}
elsif
(
$self
->{dbapi}->{isp}) {
return
$self
->_get(
'isp'
,
$ip
)->{asn_organization};
}
else
{
return
undef
;
}
}
sub
get_all {
my
(
$self
,
$ip
) =
@_
;
return
undef
if
!
defined
$ip
||
$ip
!~ /\S/;
my
$all
= {};
if
(
$ip
=~ IS_IP_PRIVATE) {
return
{
'country'
=>
'**'
};
}
if
(
$ip
!~ IS_IP_ADDRESS) {
$ip
= name_to_ip(
$ip
);
if
(!
defined
$ip
) {
return
{
'country'
=>
'XX'
};
}
}
if
(
$self
->{dbapi}->{city}) {
my
$res
=
$self
->_get(
'city'
,
$ip
);
$all
->{
$_
} =
$res
->{
$_
}
foreach
(
keys
%$res
);
}
elsif
(
$self
->{dbapi}->{country}) {
my
$res
=
$self
->_get(
'country'
,
$ip
);
$all
->{
$_
} =
$res
->{
$_
}
foreach
(
keys
%$res
);
}
if
(
$self
->{dbapi}->{isp}) {
my
$res
=
$self
->_get(
'isp'
,
$ip
);
$all
->{
$_
} =
$res
->{
$_
}
foreach
(
keys
%$res
);
}
if
(
$self
->{dbapi}->{asn}) {
my
$res
=
$self
->_get(
'asn'
,
$ip
);
$all
->{
$_
} =
$res
->{
$_
}
foreach
(
keys
%$res
);
}
return
$all
;
}
sub
can {
my
(
$self
,
$check
) =
@_
;
return
defined
$self
->{dbapi}->{
$check
};
}
sub
name_to_ip {
my
$name
=
shift
;
if
(
my
$ip
= inet_aton(
$name
)) {
$ip
= inet_ntoa(
$ip
);
dbg(
"geodb: resolved internally $name: $ip"
);
return
$ip
;
}
dbg(
"geodb: failed to internally resolve $name"
);
return
undef
;
}
sub
_get {
my
(
$self
,
$type
,
$ip
) =
@_
;
if
(
scalar
keys
%{
$self
->{cache}} >= 100) {
$self
->{cache} = ();
}
if
(!
exists
$self
->{cache}{
$ip
}{
$type
}) {
if
(
$self
->{dbapi}->{
$type
}) {
$self
->{cache}{
$ip
}{
$type
} =
$self
->{dbapi}->{
$type
}->(
$self
,
$ip
);
}
else
{
return
undef
;
}
}
return
$self
->{cache}{
$ip
}{
$type
};
}
1;