1#!/usr/local/bin/perl 2# extended geoip.pm by Sven Strickroth <email@cs-ware.de> 3#----------------------------------------------------------------------------- 4# GeoIP Maxmind AWStats plugin with IPv6 support 5# This plugin allow you to get country report with countries detected 6# from a Geographical database (GeoIP internal database) instead of domain 7# hostname suffix. 8# This works with IPv4 and also IPv6 9# Needs the IPv6 country database from Maxmind (free). 10#----------------------------------------------------------------------------- 11# Perl Required Modules: Geo::IP (Geo::IP::PurePerl does not support IPv6 yet) 12#----------------------------------------------------------------------------- 13 14# <----- 15# ENTER HERE THE USE COMMAND FOR ALL REQUIRED PERL MODULES 16use vars qw/ $type /; 17$type='geoip'; 18if (!eval ('require "Geo/IP.pm";')) { 19 $error1=$@; 20# $type='geoippureperl'; 21# if (!eval ('require "Geo/IP/PurePerl.pm";')) { 22# $error2=$@; 23# $ret=($error1||$error2)?"Error:\n$error1$error2":""; 24 $ret.="Error: Need Perl module Geo::IP"; 25 return $ret; 26# } 27} else { 28 Geo::IP->VERSION >= 1.40 || die("Requires Geo/IP.pm >= 1.40"); 29} 30# -----> 31#use strict; 32no strict "refs"; 33 34 35 36#----------------------------------------------------------------------------- 37# PLUGIN VARIABLES 38#----------------------------------------------------------------------------- 39# <----- 40# ENTER HERE THE MINIMUM AWSTATS VERSION REQUIRED BY YOUR PLUGIN 41# AND THE NAME OF ALL FUNCTIONS THE PLUGIN MANAGE. 42my $PluginNeedAWStatsVersion="5.4"; 43my $PluginHooksFunctions="GetCountryCodeByAddr GetCountryCodeByName ShowInfoHost"; 44my $PluginName = "geoip6"; 45my $LoadedOverride=0; 46my $OverrideFile=""; 47my %TmpDomainLookup; 48# -----> 49 50# <----- 51# IF YOUR PLUGIN NEED GLOBAL VARIABLES, THEY MUST BE DECLARED HERE. 52use vars qw/ 53$gi 54/; 55# -----> 56 57 58#----------------------------------------------------------------------------- 59# PLUGIN FUNCTION: Init_pluginname 60#----------------------------------------------------------------------------- 61sub Init_geoip6 { 62 my $InitParams=shift; 63 my $checkversion=&Check_Plugin_Version($PluginNeedAWStatsVersion); 64 65 # <----- 66 # ENTER HERE CODE TO DO INIT PLUGIN ACTIONS 67 debug(" Plugin $PluginName: InitParams=$InitParams",1); 68 my ($mode,$tmpdatafile)=split(/\s+/,$InitParams,2); 69 my ($datafile,$override)=split(/\+/,$tmpdatafile,2); 70 if (! $datafile) { $datafile="$PluginName.dat"; } 71 else { $datafile =~ s/%20/ /g; } 72 if ($type eq 'geoippureperl') { 73 if ($mode eq '' || $mode eq 'GEOIP_MEMORY_CACHE') { $mode=Geo::IP::PurePerl::GEOIP_MEMORY_CACHE(); } 74 else { $mode=Geo::IP::PurePerl::GEOIP_STANDARD(); } 75 } else { 76 if ($mode eq '' || $mode eq 'GEOIP_MEMORY_CACHE') { $mode=Geo::IP::GEOIP_MEMORY_CACHE(); } 77 else { $mode=Geo::IP::GEOIP_STANDARD(); } 78 } 79 if ($override){$OverrideFile=$override;} 80 %TmpDomainLookup=(); 81 debug(" Plugin $PluginName: GeoIP initialized type=$type mode=$mode override=$override",1); 82 if ($type eq 'geoippureperl') { 83 $gi = Geo::IP::PurePerl->open($datafile, $mode); 84 } else { 85 $gi = Geo::IP->open($datafile, $mode); 86 } 87 88# Fails on some GeoIP version 89# debug(" Plugin geoip6: GeoIP initialized database_info=".$gi->database_info()); 90 # -----> 91 92 return ($checkversion?$checkversion:"$PluginHooksFunctions"); 93} 94 95 96#----------------------------------------------------------------------------- 97# PLUGIN FUNCTION: GetCountryCodeByAddr_pluginname 98# UNIQUE: YES (Only one plugin using this function can be loaded) 99# GetCountryCodeByAddr is called to translate an ip into a country code in lower case. 100#----------------------------------------------------------------------------- 101sub GetCountryCodeByAddr_geoip6 { 102 my $param="$_[0]"; 103 # <----- 104 if (! $param) { return ''; } 105 my $searchkey; 106 if ($param =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { # IPv4 address 107 $searchkey = '::ffff:'.$param; 108 } else { 109 $searchkey = $param; 110 } 111 my $res= TmpLookup_geoip6($param); 112 if (! $res) { 113 $res=lc($gi->country_code_by_addr_v6($searchkey)) || 'unknown'; 114 $TmpDomainLookup{$param}=$res; 115 if ($Debug) { debug(" Plugin $PluginName: GetCountryCodeByAddr for $searchkey: [$res]",5); } 116 } 117 elsif ($Debug) { debug(" Plugin $PluginName: GetCountryCodeByAddr for $param: Already resolved to [$res]",5); } 118 # -----> 119 return $res; 120} 121 122 123#----------------------------------------------------------------------------- 124# PLUGIN FUNCTION: GetCountryCodeByName_pluginname 125# UNIQUE: YES (Only one plugin using this function can be loaded) 126# GetCountryCodeByName is called to translate a host name into a country code in lower case. 127#----------------------------------------------------------------------------- 128sub GetCountryCodeByName_geoip6 { 129 my $param="$_[0]"; 130 # <----- 131 if (! $param) { return ''; } 132 my $res = TmpLookup_geoip6($param); 133 if (! $res) { 134 $res=lc($gi->country_code_by_name_v6($param)) || 'unknown'; 135 $TmpDomainLookup{$param}=$res; 136 if ($Debug) { debug(" Plugin $PluginName: GetCountryCodeByName for $param: [$res]",5); } 137 } 138 elsif ($Debug) { debug(" Plugin $PluginName: GetCountryCodeByName for $param: Already resolved to [$res]",5); } 139 # -----> 140 return $res; 141} 142 143 144#----------------------------------------------------------------------------- 145# PLUGIN FUNCTION: ShowInfoHost_pluginname 146# UNIQUE: NO (Several plugins using this function can be loaded) 147# Function called to add additionnal columns to the Hosts report. 148# This function is called when building rows of the report (One call for each 149# row). So it allows you to add a column in report, for example with code : 150# print "<TD>This is a new cell for $param</TD>"; 151# Parameters: Host name or ip 152#----------------------------------------------------------------------------- 153sub ShowInfoHost_geoip6 { 154 my $param="$_[0]"; 155 # <----- 156 if ($param eq '__title__') { 157 my $NewLinkParams=${QueryString}; 158 $NewLinkParams =~ s/(^|&)update(=\w*|$)//i; 159 $NewLinkParams =~ s/(^|&)output(=\w*|$)//i; 160 $NewLinkParams =~ s/(^|&)staticlinks(=\w*|$)//i; 161 $NewLinkParams =~ s/(^|&)framename=[^&]*//i; 162 my $NewLinkTarget=''; 163 if ($DetailedReportsOnNewWindows) { $NewLinkTarget=" target=\"awstatsbis\""; } 164 if (($FrameName eq 'mainleft' || $FrameName eq 'mainright') && $DetailedReportsOnNewWindows < 2) { 165 $NewLinkParams.="&framename=mainright"; 166 $NewLinkTarget=" target=\"mainright\""; 167 } 168 $NewLinkParams =~ tr/&/&/s; $NewLinkParams =~ s/^&//; $NewLinkParams =~ s/&$//; 169 if ($NewLinkParams) { $NewLinkParams="${NewLinkParams}&"; } 170 171 print "<th width=\"80\">"; 172 print "<a href=\"#countries\">GeoIP<br />Country</a>"; 173 print "</th>"; 174 } 175 elsif ($param) { 176 my $ip=0; 177 my $key; 178 if ($param =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { # IPv4 address 179 $ip=4; 180 $key='::ffff:'.$param; 181 } 182 elsif ($param =~ /^[0-9A-F]*:/i) { # IPv6 address 183 $ip=6; 184 $key=$param; 185 } 186 print "<td>"; 187 if ($key) { 188 my $res = TmpLookup_geoip6($param); 189 if (!$res && $gi) { 190 $res=lc($gi->country_code_by_addr_v6($key)); 191 } 192 if ($Debug) { debug(" Plugin $PluginName: GetCountryByIp for $key: [$res]",5); } 193 if ($res) { print $DomainsHashIDLib{$res}?$DomainsHashIDLib{$res}:"<span style=\"color: #$color_other\">$Message[0]</span>"; } 194 else { print "<span style=\"color: #$color_other\">$Message[0]</span>"; } 195 } 196 else { 197 my $res = TmpLookup_geoip6($param); 198 if (!$res){$res=lc($gi->country_code_by_name_v6($param)) if $gi;} 199 if ($Debug) { debug(" Plugin $PluginName: GetCountryByHostname for $param: [$res]",5); } 200 if ($res) { print $DomainsHashIDLib{$res}?$DomainsHashIDLib{$res}:"<span style=\"color: #$color_other\">$Message[0]</span>"; } 201 else { print "<span style=\"color: #$color_other\">$Message[0]</span>"; } 202 } 203 print "</td>"; 204 } 205 else { 206 print "<td> </td>"; 207 } 208 return 1; 209 # -----> 210} 211 212#----------------------------------------------------------------------------- 213# PLUGIN FUNCTION: LoadOverrideFile 214# Attempts to load a comma delimited file that will override the GeoIP database 215# Useful for Intranet records 216# CSV format: IP,2-char Country code 217#----------------------------------------------------------------------------- 218sub LoadOverrideFile_geoip6{ 219 my $filetoload=""; 220 if ($OverrideFile){ 221 if (!open(GEOIPFILE, $OverrideFile)){ 222 debug("Plugin $PluginName: Unable to open override file: $OverrideFile"); 223 $LoadedOverride = 1; 224 return; 225 } 226 }else{ 227 my $conf = (exists(&Get_Config_Name) ? Get_Config_Name() : $SiteConfig); 228 if ($conf && open(GEOIPFILE,"$DirData/$PluginName.$conf.txt")) { $filetoload="$DirData/$PluginName.$conf.txt"; } 229 elsif (open(GEOIPFILE,"$DirData/$PluginName.txt")) { $filetoload="$DirData/$PluginName.txt"; } 230 else { debug("No override file \"$DirData/$PluginName.txt\": $!"); } 231 } 232 if ($filetoload) 233 { 234 # This is the fastest way to load with regexp that I know 235 while (<GEOIPFILE>){ 236 chomp $_; 237 s/\r//; 238 my @record = split(",", $_); 239 # replace quotes if they were used in the file 240 foreach (@record){ $_ =~ s/"//g; } 241 # store in hash 242 $TmpDomainLookup{$record[0]} = $record[1]; 243 } 244 close GEOIPFILE; 245 debug(" Plugin $PluginName: Overload file loaded: ".(scalar keys %TmpDomainLookup)." entries found."); 246 } 247 $LoadedOverride = 1; 248 return; 249} 250 251#----------------------------------------------------------------------------- 252# PLUGIN FUNCTION: TmpLookup 253# Searches the temporary hash for the parameter value and returns the corresponding 254# GEOIP entry 255#----------------------------------------------------------------------------- 256sub TmpLookup_geoip6(){ 257 $param = shift; 258 if (!$LoadedOverride){&LoadOverrideFile_geoip6();} 259 #my $val; 260 #if ($gi && 261 #(($type eq 'geoip' && $gi->VERSION >= 1.30) || 262 # $type eq 'geoippureperl' && $gi->VERSION >= 1.17)){ 263 # $val = $TmpDomainLookup{$gi->get_ip_address($param)}; 264 #} 265 #else {$val = $TmpDomainLookup{$param};} 266 #return $val || ''; 267 return $TmpDomainLookup{$param}||''; 268} 269 2701; # Do not remove this line 271