xref: /freebsd/contrib/sendmail/contrib/cidrexpand (revision b0b1dbdd)
1#!/usr/bin/perl -w
2
3# $Id: cidrexpand,v 8.8 2006-08-07 17:18:37 ca Exp $
4#
5# v 0.4
6#
7# 17 July 2000 Derek J. Balling (dredd@megacity.org)
8#
9# Acts as a preparser on /etc/mail/access_db to allow you to use address/bit
10# notation.
11#
12# If you have two overlapping CIDR blocks with conflicting actions
13# e.g.   10.2.3.128/25 REJECT and 10.2.3.143 ACCEPT
14# make sure that the exceptions to the more general block are specified
15# later in the access_db.
16#
17# the -r flag to makemap will make it "do the right thing"
18#
19# Modifications
20# -------------
21# 26 Jul 2001 Derek Balling (dredd@megacity.org)
22#     Now uses Net::CIDR because it makes life a lot easier.
23#
24#  5 Nov 2002 Richard Rognlie (richard@sendmail.com)
25#     Added code to deal with the prefix tags that may now be included in
26#     the access_db
27#
28#     Added clarification in the notes for what to do if you have
29#     exceptions to a larger CIDR block.
30#
31#  26 Jul 2006 Richard Rognlie (richard@sendmail.com>
32#     Added code to strip "comments" (anything after a non-escaped #)
33#     # characters after a \ or within quotes (single and double) are
34#     left intact.
35#
36#     e.g.
37#	From:1.2.3.4	550 Die spammer # spammed us 2006.07.26
38#     becomes
39#	From:1.2.3.4	550 Die spammer
40#
41#  3 August 2006
42#
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# usage:
47#  cidrexpand < /etc/mail/access | makemap -r hash /etc/mail/access
48#
49#
50# Report bugs to: <dredd@megacity.org>
51#
52
53
54use strict;
55use Net::CIDR;
56use Getopt::Std;
57
58our ($opt_c,$opt_t);
59getopts('ct:');
60
61my $spaceregex = '\s+';
62if ($opt_t)
63{
64    $spaceregex = $opt_t;
65}
66
67while (<>)
68{
69    chomp;
70    my ($prefix,$left,$right,$space);
71
72    if ( (/\#/) && $opt_c )
73    {
74	# print "checking...\n";
75	my $i;
76	my $qtype='';
77	for ($i=0 ; $i<length($_) ; $i++)
78	{
79	    my $ch = substr($_,$i,1);
80	    if ($ch eq '\\')
81	    {
82		$i++;
83		next;
84	    }
85	    elsif ($qtype eq '' && $ch eq '#')
86	    {
87		substr($_,$i) = '';
88		last;
89	    }
90	    elsif ($qtype ne '' && $ch eq $qtype)
91	    {
92		$qtype = '';
93	    }
94	    elsif ($qtype eq '' && $ch =~ /[\'\"]/)
95	    {
96		$qtype = $ch;
97	    }
98	}
99    }
100
101    if (! /^(|\S\S*:)(\d+\.){3}\d+\/\d\d?$spaceregex.*/ )
102    {
103	print "$_\n";
104    }
105    else
106    {
107	($prefix,$left,$space,$right) =
108	    /^(|\S\S*:)((?:\d+\.){3}\d+\/\d\d?)($spaceregex)(.*)$/;
109
110	my @new_lefts = expand_network($left);
111	foreach my $nl (@new_lefts)
112	{
113	    print "$prefix$nl$space$right\n";
114	}
115    }
116}
117
118sub expand_network
119{
120    my $left_input = shift;
121    my @rc = ($left_input);
122    my ($network,$mask) = split /\//, $left_input;
123    if (defined $mask)
124    {
125	return (0..255)	if $mask == 0;
126
127	my @parts = split /\./, $network;
128	while ($#parts < 3)
129	{
130	    push @parts, "0";
131	}
132	my $clean_input = join '.', @parts;
133	$clean_input .= "/$mask";
134	my @octets = Net::CIDR::cidr2octets($clean_input);
135	@rc = @octets;
136    }
137    return @rc;
138}
139