]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/contrib/cidrexpand
Update tzcode to 2023c.
[FreeBSD/FreeBSD.git] / contrib / sendmail / contrib / cidrexpand
1 #!/usr/bin/perl -w
2 #
3 # usage:
4 #  cidrexpand < /etc/mail/access | makemap -r hash /etc/mail/access
5 #
6 # v 1.1
7 #
8 # 17 July 2000 Derek J. Balling (dredd@megacity.org)
9 #
10 # Acts as a preparser on /etc/mail/access_db to allow you to use address/bit
11 # notation.
12 #
13 # If you have two overlapping CIDR blocks with conflicting actions
14 # e.g.   10.2.3.128/25 REJECT and 10.2.3.143 ACCEPT
15 # make sure that the exceptions to the more general block are specified
16 # later in the access_db.
17 #
18 # the -r flag to makemap will make it "do the right thing"
19 #
20 # Modifications
21 # -------------
22 # 26 Jul 2001 Derek Balling (dredd@megacity.org)
23 #     Now uses Net::CIDR because it makes life a lot easier.
24 #
25 #  5 Nov 2002 Richard Rognlie (richard@sendmail.com)
26 #     Added code to deal with the prefix tags that may now be included in
27 #     the access_db
28 #
29 #     Added clarification in the notes for what to do if you have
30 #     exceptions to a larger CIDR block.
31 #
32 #  26 Jul 2006 Richard Rognlie (richard@sendmail.com)
33 #     Added code to strip "comments" (anything after a non-escaped #)
34 #     # characters after a \ or within quotes (single and double) are
35 #     left intact.
36 #
37 #     e.g.
38 #       From:1.2.3.4    550 Die spammer # spammed us 2006.07.26
39 #     becomes
40 #       From:1.2.3.4    550 Die spammer
41 #
42 #  3 August 2006
43 #     Corrected a bug to have it handle the special case of "0.0.0.0/0"
44 #     since Net::CIDR doesn't handle it properly.
45 #
46 #  27 April 2016
47 #     Corrected IPv6 handling.  Note that UseCompressedIPv6Addresses must
48 #     be turned off for this to work; there are three reasons for this:
49 #       1) if the MTA uses compressed IPv6 addresses then CIDR 'cuts'
50 #          in the compressed range *cannot* be matched, as the MTA simply
51 #          won't look for them.  E.g., there's no way to accurately
52 #          match "IPv6:fe80::/64" when for the address "IPv6:fe80::54ad"
53 #          the MTA doesn't lookup up "IPv6:fe80:0:0:0"
54 #       2) cidrexpand only generates uncompressed addresses, so CIDR
55 #          'cuts' to the right of the compressed range won't be matched
56 #          either.  Why doesn't it generate compressed address output?
57 #          Oh, because:
58 #       3) compressed addresses are ambiguous when colon-groups are
59 #          chopped off!  You want an access map entry for
60 #               IPv6:fe80::0:5420
61 #          but not for
62 #               IPv6:fe80::5420:1234
63 #          ?  Sorry, the former is really
64 #               IPv6:fe80::5420
65 #          which will also match the latter!
66 #
67 #  25 July 2016
68 #     Since cidrexpand already requires UseCompressedIPv6Addresses to be
69 #     turned off, it can also canonicalize non-CIDR IPv6 addresses to the
70 #     format that sendmail looks up, expanding compressed addresses and
71 #     trimming superfluous leading zeros.
72 #
73 # Report bugs to: <dredd@megacity.org>
74 #
75
76 our $VERSION = '1.1';
77
78 use strict;
79 use Net::CIDR qw(cidr2octets cidrvalidate);
80 use Getopt::Std;
81 $Getopt::Std::STANDARD_HELP_VERSION = 1;
82
83 sub VERSION_MESSAGE;
84 sub HELP_MESSAGE;
85 sub print_expanded_v4network;
86 sub print_expanded_v6network;
87
88 our %opts;
89 getopts('cfhOSt:', \%opts);
90
91 if ($opts{h}) {
92     HELP_MESSAGE(\*STDOUT);
93     exit 0;
94 }
95
96 # Delimiter between the key and value
97 my $space_re = exists $opts{t} ? $opts{t} : '\s+';
98
99 # Regexp that matches IPv4 address literals
100 my $ipv4_re = qr"(?:\d+\.){3}\d+";
101
102 # Regexp that matches IPv6 address literals, plus a lot more.
103 # Further checks are required for verifying that it's really one
104 my $ipv6_re = qr"[0-9A-Fa-f:]{2,39}(?:\.\d+\.\d+\.\d+)?";
105
106 my %pending;
107 while (<>)
108 {
109     chomp;
110     my ($prefix, $network, $len, $right);
111
112     next if /^#/ && $opts{S};
113     if ( (/\#/) && $opts{c} )
114     {
115         # print "checking...\n";
116         my $i;
117         my $qtype='';
118         for ($i=0 ; $i<length($_) ; $i++)
119         {
120             my $ch = substr($_,$i,1);
121             if ($ch eq '\\')
122             {
123                 $i++;
124                 next;
125             }
126             elsif ($qtype eq '' && $ch eq '#')
127             {
128                 substr($_,$i) = '';
129                 last;
130             }
131             elsif ($qtype ne '' && $ch eq $qtype)
132             {
133                 $qtype = '';
134             }
135             elsif ($qtype eq '' && $ch =~ /[\'\"]/)
136             {
137                 $qtype = $ch;
138             }
139         }
140     }
141
142     if (($prefix, $network, $len, $right) =
143             m!^(|[^\s:]+:)(${ipv4_re})/(\d+)(${space_re}.*)$!)
144     {
145         print_expanded_v4network($network, $len, $prefix, $right);
146     }
147     elsif ((($prefix, $network, $len, $right) =
148             m!^((?:[^\s:]+:)?[Ii][Pp][Vv]6:)(${ipv6_re})(?:/(\d+))?(${space_re}.*)$!) &&
149             (!defined($len) || $len <= 128) &&
150             defined(cidrvalidate($network)))
151     {
152         print_expanded_v6network($network, $len // 128, $prefix, $right);
153     }
154     else
155     {
156         if (%pending && m!^(.+?)${space_re}!)
157         {
158             delete $pending{$opts{f} ? $1 : lc($1)};
159         }
160         print "$_\n";
161     }
162 }
163 print foreach values %pending;
164
165 sub print_expanded_v4network
166 {
167     my ($network, $len, $prefix, $suffix) = @_;
168     my $fp = $opts{f} ? $prefix : lc($prefix);
169
170     # cidr2octets() doesn't handle a prefix-length of zero, so do
171     # that ourselves
172     foreach my $nl ($len == 0 ? (0..255) : cidr2octets("$network/$len"))
173     {
174         my $val = "$prefix$nl$suffix\n";
175         if ($opts{O})
176         {
177             $pending{"$fp$nl"} = $val;
178             next;
179         }
180         print $val;
181     }
182 }
183
184 sub print_expanded_v6network
185 {
186     my ($network, $len, $prefix, $suffix) = @_;
187
188     # cidr2octets() doesn't handle a prefix-length of zero, so do
189     # that ourselves.  Easiest is to just recurse on bottom and top
190     # halves with a length of 1
191     if ($len == 0) {
192         print_expanded_v6network("::", 1, $prefix, $suffix);
193         print_expanded_v6network("8000::", 1, $prefix, $suffix);
194     }
195     else
196     {
197         my $fp = $opts{f} ? $prefix : lc($prefix);
198         foreach my $nl (cidr2octets("$network/$len"))
199         {
200             # trim leading zeros from each group
201             $nl =~ s/(^|:)0+(?=[^:])/$1/g;
202             my $val = "$prefix$nl$suffix\n";
203             if ($opts{O})
204             {
205                 $pending{"$fp$nl"} = $val;
206                 next;
207             }
208             print $val;
209         }
210     }
211 }
212
213 sub VERSION_MESSAGE
214 {
215     my ($fh) = @_;
216     print $fh "cidrexpand - Version $VERSION\n";
217 }
218
219 sub HELP_MESSAGE
220 {
221     my ($fh) = @_;
222     print $fh <<'EOF';
223 Usage: cidrexpand [-cfhOS] [-t regexp] files...
224
225 Expand CIDR format inside the keys of map entries for makemap.
226
227   -c    Truncate lines at the first unquoted '#'
228
229   -f    Treat keys as case-sensitive when doing override detection
230         for the -O option.  By default overlap detection is
231         case-insensitive.
232
233   -h    Print this usage
234
235   -O    When a CIDR expansion would generate a partial conflict
236         with a later entry, suppress the overlap from the earlier
237         expansion
238
239   -S    Skip lines that start with '#'
240
241   -t regexp
242         Use 'regexp' to match the delimiter between key and value,
243         defaulting to \s+
244
245 EOF
246 }