#!/usr/bin/perl
# $Header: /u/cvsroot/env/b/nic-to-tcprules,v 1.4 2003/07/23 19:30:58 mayoff Exp $

use strict;
use vars qw( %ranges );

foreach my $cc (@ARGV) {
    $cc =~ y/A-Z/a-z/;
    $ranges{$cc} = [];
}

@ARGV = ();

while (<>) {
    my ($nic, $cc, $type, $n, $size, $date, $status) = split(/\|/);
    $cc =~ y/A-Z/a-z/;
    if ($type eq 'ipv4' && exists($ranges{$cc}) && $size > 0) {
	if ($size == 1) {
	    push(@{$ranges{$cc}}, $n);
	} else {
	    push(@{$ranges{$cc}}, get_ranges($n, $size));
	}
    }
}

foreach my $cc (sort keys %ranges) {
    next if (length(@{$ranges{$cc}}) == 0);

    print "# $cc\n";
    foreach my $range (@{$ranges{$cc}}) {
	print "$range:deny\n";
    }
    print "\n";
}

exit(0);

sub get_ranges {
    my ($dotted_start, $length) = @_;

    my $start = undot($dotted_start);
    my $end = $start + $length - 1;

    my @start_octets = get_octets($start);
    my @end_octets = get_octets($end);

    my $i;
    for ($i = 0; $i < 4; $i++) {
	last if ($start_octets[$i] != $end_octets[$i]);
    }
    my $matchlength = $i;
    $i++;
    for ( ; $i < 4; $i++) {
	if ($start_octets[$i] != 0 || $end_octets[$i] != 255) {
	    warn "cannot handle range $dotted_start, $length";
	    return;
	}
    }

    my $range;
    for ($i = 0; $i < $matchlength; $i++) {
	$range .= $start_octets[$i];
	$range .= '.';
    }

    $range .= $start_octets[$i];
    $range .= '-';
    $range .= $end_octets[$i];

    $range .= '.' if ($i < 3);

    return ($range);
}

sub undot {
    my ($dotted) = @_;

    my @octets = split(/\./, $dotted);
    my $n = 0;
    foreach my $octet (@octets) {
	$n = ($n << 8) + $octet;
    }

    return $n;
}

sub get_octets {
    my ($ip) = @_;

    return (
	($ip >> 24) & 0xff,
	($ip >> 16) & 0xff,
	($ip >> 8) & 0xff,
	$ip & 0xff
    );
}

__END__

=head1 NAME

nic-to-tcprules

=head1 SYNOPSIS

I<nic-to-tcprules> I<cc> [I<cc> ...] I<E<lt>> I<nic-assignment-file>

=head1 DESCRIPTION

I<nic-to-tcprules> takes a list of two-letter country codes as
command-line arguments, and reads a series of address assignments on
standard input. For each IPv4 address block assignment, if the block
is assigned to one of the country codes given on the command line, the
program prints a I<tcprules>-style "deny" line for that
address block. The output lines are grouped by country.

Address assignments for various regions, in the format needed by this
program, can be found at these URLs:

=over 4

=item E<lt>http://ftp.apnic.net/stats/apnic/E<gt>

=item E<lt>ftp://ftp.arin.net/pub/stats/arin/E<gt>

=item E<lt>ftp://ftp.ripe.net/ripe/stats/E<gt>

=back

=head1 MOTIVATION

I get a lot of unwanted e-mail from IP addresses assigned to
countries like Korea and China. Since I don't know anyone
in those countries, I'm willing to risk blocking all e-mail
from those countries. My incoming SMTP server runs under
I<tcpserver> (E<lt>http://cr.yp.to/ucspi-tcp/tcpserver.htmlE<gt>), so I
need to convert the APNIC data to the format used by I<tcprules>
(E<lt>http://cr.yp.to/ucspi-tcp/tcprules.htmlE<gt>).

=head1 BUGS

This version of I<nic-to-tcprules> does not support arbitrary address
ranges. An input range that can be expressed as a single I<tcprules>
range will be converted; any other range will produce a warning on
standard error and nothing on standard output. As of 2002/06/16, all
RIPE and APNIC blocks are convertable, but some ARIN blocks are not.
This doesn't bother me because the only countries I currently want to
block are handled by APNIC.

=head1 AUTHOR

Rob Mayoff <mayoff@dqd.com>

=head1 VERSION

$Id: nic-to-tcprules,v 1.4 2003/07/23 19:30:58 mayoff Exp $

=cut
