add load/badip.pl
[spider.git] / perl / DXCIDR.pm
1 #
2 # IP Address block list / checker
3 #
4 # This is a DXSpider compatible, optional skin over Net::CIDR::Lite
5 # If Net::CIDR::Lite is not present, then a find will always returns 0
6 #
7
8 package DXCIDR;
9
10 use strict;
11 use warnings;
12 use 5.16.1;
13 use DXVars;
14 use DXDebug;
15 use DXUtil;
16 use DXLog;
17 use IO::File;
18 use File::Copy;
19 use Socket qw(AF_INET AF_INET6 inet_pton inet_ntop);
20
21 our $active = 0;
22 our $badipfn = "badip";
23 my $ipv4;
24 my $ipv6;
25 my $count4 = 0;
26 my $count6 = 0;
27
28 # load the badip file
29 sub load
30 {
31         if ($active) {
32                 $count4 = _load($ipv4, 4);
33                 $count6 = _load($ipv6, 6);
34         }
35         LogDbg('DXProt', "DXCIDR: loaded $count4 IPV4 addresses and $count6 IPV6 addresses");
36         return $count4 + $count6;
37 }
38
39 sub _fn
40 {
41         return localdata($badipfn) . ".$_[0]";
42 }
43
44 sub _load
45 {
46         my $list = shift;
47         my $sort = shift;
48         my $fn = _fn($sort);
49         my $fh = IO::File->new($fn);
50         my $count = 0;
51         
52         if ($fh) {
53                 while (<$fh>) {
54                         chomp;
55                         next if /^\s*\#/;
56                         next unless /[\.:]/;
57                         $list->add_any($_);
58                         ++$count;
59                 }
60                 $fh->close;
61                 $list->clean if $count;
62                 $list->prep_find;
63         } elsif (-r $fn) {
64                 LogDbg('err', "DXCIDR: $fn not found ($!)");
65         }
66         return $count;
67 }
68
69 sub _put
70 {
71         my $list = shift;
72         my $sort = shift;
73         my $fn = _fn($sort);
74         my $r = rand;
75         my $fh = IO::File->new (">$fn.$r");
76         if ($fh) {
77                 for ($list->list) {
78                         $fh->print("$_\n");
79                 }
80                 move "$fn.$r", $fn;
81         } else {
82                 LogDbg('err', "DXCIDR: cannot write $fn.$r $!");
83         }
84 }
85
86 sub add
87 {
88         my $count = 0;
89         
90         for my $ip (@_) {
91                 # protect against stupid or malicious
92                 next if /^127\./;
93                 next if /^::1$/;
94                 if (/\./) {
95                         if ($ipv4->find($ip)) {
96                                 LogDbg('DXProt', "DXCIDR: Ignoring existing IPV4 $ip");
97                                 next;
98                         } 
99                         $ipv4->add_any($ip);
100                         ++$count;
101                         ++$count4;
102                 } elsif (/:/) {
103                         if ($ipv6->find($ip)) {
104                                 LogDbg('DXProt', "DXCIDR: Ignoring existing IPV6 $ip");
105                                 next;
106                         } 
107                         $ipv6->add_any($ip);
108                         ++$count;
109                         ++$count6;
110                         LogDbg('DXProt', "DXCIDR: Added IPV6 $ip address");
111                 }
112         }
113         if ($ipv4 && $count4) {
114                 $ipv4->prep_find;
115                 _put($ipv4, 4);
116         }
117         if ($ipv6 && $count6) {
118                 $ipv6->prep_find;
119                 _put($ipv6, 6);
120         }
121         return $count;
122 }
123
124 sub save
125 {
126         return 0 unless $active;
127         _put($ipv4, 4) if $count4;
128         _put($ipv6, 6) if $count6;
129 }
130
131 sub _sort
132 {
133         my @in;
134         my @out;
135         for (@_) {
136                 push @in, [inet_pton(m|:|?AF_INET6:AF_INET, $_), split m|/|];
137         }
138         @out = sort {$a->[0] <=> $b->[0]} @in;
139         return map { "$_->[1]/$_->[2]"} @out;
140 }
141
142 sub list
143 {
144         my @out;
145         push @out, $ipv4->list if $count4;
146         push @out, $ipv6->list if $count6;
147         return _sort(@out);
148 }
149
150 sub find
151 {
152         return 0 unless $active;
153         return 0 unless $_[0];
154
155         if ($_[0] =~ /\./) {
156                 return $ipv4->find($_[0]) if $count4;
157         }
158         return $ipv6->find($_[0]) if $count6;
159 }
160
161 sub init
162 {
163         eval { require Net::CIDR::Lite };
164         if ($@) {
165                 LogDbg('DXProt', "DXCIDR: load (cpanm) the perl module Net::CIDR::Lite to check for bad IP addresses (or CIDR ranges)");
166                 return;
167         }
168
169         import Net::CIDR::Lite;
170
171         $ipv4 = Net::CIDR::Lite->new;
172         $ipv6 = Net::CIDR::Lite->new;
173
174         $active = 1;
175         load();
176 }
177
178
179
180 1;