fix DXCIDR inet_pton calls
[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         for my $ip (@_) {
89                 # protect against stupid or malicious
90                 next if /^127\./;
91                 next if /^::1$/;
92                 if (/\./) {
93                         if ($ipv4->find($ip)) {
94                                 LogDbg('DXProt', "DXCIDR: Ignoring existing IPV4 $ip");
95                                 next;
96                         } 
97                         $ipv4->add_any($ip);
98                         ++$count4;
99                 } elsif (/:/) {
100                         if ($ipv6->find($ip)) {
101                                 LogDbg('DXProt', "DXCIDR: Ignoring existing IPV6 $ip");
102                                 next;
103                         } 
104                         $ipv6->add_any($ip);
105                         ++$count6;
106                         LogDbg('DXProt', "DXCIDR: Added IPV6 $ip address");
107                 }
108         }
109         if ($ipv4 && $count4) {
110                 $ipv4->prep_find;
111                 _put($ipv4, 4);
112         }
113         if ($ipv6 && $count6) {
114                 $ipv6->prep_find;
115                 _put($ipv6, 6);
116         }
117 }
118
119 sub save
120 {
121         return 0 unless $active;
122         _put($ipv4, 4) if $count4;
123         _put($ipv6, 6) if $count6;
124 }
125
126 sub _sort
127 {
128         my @in;
129         my @out;
130         for (@_) {
131                 push @in, [inet_pton(m|:|?AF_INET6:AF_INET, $_), split m|/|];
132         }
133         @out = sort {$a->[0] <=> $b->[0]} @in;
134         return map { "$_->[1]/$_->[2]"} @out;
135 }
136
137 sub list
138 {
139         my @out;
140         push @out, $ipv4->list if $count4;
141         push @out, $ipv6->list if $count6;
142         return _sort(@out);
143 }
144
145 sub find
146 {
147         return 0 unless $active;
148         return 0 unless $_[0];
149
150         if ($_[0] =~ /\./) {
151                 return $ipv4->find($_[0]) if $count4;
152         }
153         return $ipv6->find($_[0]) if $count6;
154 }
155
156 sub init
157 {
158         eval { require Net::CIDR::Lite };
159         if ($@) {
160                 LogDbg('DXProt', "DXCIDR: load (cpanm) the perl module Net::CIDR::Lite to check for bad IP addresses (or CIDR ranges)");
161                 return;
162         }
163
164         import Net::CIDR::Lite;
165
166         $ipv4 = Net::CIDR::Lite->new;
167         $ipv6 = Net::CIDR::Lite->new;
168
169         $active = 1;
170         load();
171 }
172
173
174
175 1;