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