#!/usr/bin/perl

my @Id    = split / /,'$Id: nodelist2dns.pl,v 1.1 2007/02/14 13:24:15 stas Exp $';
my $ver   = $Id[2];
my $banner= "nodelist2dns v.$ver - A perl script to convert nodelist to DNS (bind) zone file";
# (c) Copyleft Stas Degteff 2:5080/102@fidonet
# Distributes on GNU GPL v2, see http://www.gnu.org

# Features:
# - make "A" and "CNAME" records like "fNNNN.nMMMM.zZ" for nodes Z:MMMM/NNNN
# - make "A" and "CNAME" records like "f0.nMMMM.zZ" for host nodes Z:MMMM/0
# - IP address and domain name is taken from "BBS name" field of the nodelist
#   for nodes with IBN flag
# - parse INA flag
# - ignore "Down" and "Hold" nodes

# ToDo:
# - parse parameter of Ixx flags
# - parse flags "DOM", "DO4", "DO3", "DO2"

# What it's works.
# Scans nodelist:
# - skip any nodes without flag "IBN";
# - for lines begins with "Zone,", "Region,", "Host,", "Hub,", "Pvt,", "," check ",INA:"
#   and match address after this flag;
# - if "INA" not presented then math IP (numberic) address or fully-qualified
#   domain name in field 3 ("station/BBS name").

# Changelog.
# 1.0 
# * first public release
# 1.1 
# + parse "INA" flag
# ! parse "Region" lines (bugfix)
# ! fixed ip address detection (regexp bugfix)
# + display warnings if incorrect nodelist line is found
# + if domain name or IP address isn't specified then generate default domain
#   name (using template fXXX.nYYY.zZ.fidonet.net. "fidonet.net" is specified
#   in configuration section and may be changed)
# + multiple addresses: first taken from "INA" flag, second taken from BBS name
#   field

# configuration
#
my $TTL    =7200;                       # TTL for the DNS zone SOA record
my $server ="srv.grumbler.org";         # DNS host
my $person ="hostmaster.grumbler.org";  # responsible person (email address)
my $minttl =3600;                       # minimum zone's TTL
my $refresh=86400;                      # zone's refresh period
my $retry  =3600;                       # zone's retry period
my $expire =604800;                     # zone's expire time
my $default_root_domain="fidonet.net";  # domain for constucts hostname if
                                        # IP address/domain isn't specified in
                                        # nodelist line.
#
# configuration end
#
print "$banner\n";

if($#ARGV<0 || $#ARGV>1){

  print <<USAGE;

USAGE: nodelist2dns nodelist.file dns.zone.file

USAGE
  exit;
}

$person =~ s/@/./;  # if responsible person specified like name@email.domain

my ($nodelist,$zonefile)=@ARGV;

if( -f $nodelist && -r $nodelist){
  open NODELIST, $nodelist || die "Can't open $nodelist: $!\n";
}
open DNSZONE, ">$zonefile" || die "Can't create $zonefile: $!\n";

&process;

print "File '$zonefile' is generated\n";

close NODELIST;
close DNSZONE;
exit;

sub process{

$line = <NODELIST>;

# ;A FidoNet Nodelist for Friday, September 2, 2005 -- Day number 245 : 31474
# $sn="20052450";
if( $line =~ /;A FidoNet Nodelist for [\w\s,]+, (\d\d\d\d) -- Day number (\d+) /){
  my $year=$1;
  my $day=$2;
  $day="0".$day if($day<10);
  $day="0".$day if($day<100);
  $sn=$year.$day."0";
}else{
  die "Wrong nodelist!\n";
}

  print DNSZONE "
; FIDONET IP nodes DNS zone file
; Generated by $banner
;

\@	$TTL	IN	SOA	$server. $person. (
			$sn	; serial number: concantenate of year day version (0)
			$refresh		; refresh
			$retry		; retry
			$expire		; expire
			$minttl		; minimum TTL
			)

\@		IN	NS	$server.

";

  my $zone,$net,$node;
  while(<NODELIST>){
    next if(/^;/);
    if(/^[^\w,]/) { print "Warning! Illegal line:\n", $_;};

    if(/^Zone,(\d+),/){
      $zone=$1;
      $net=$1;
      $node=0;
    }
    elsif(/^(Region|Host),(\d+),/){ # net/independent number
      $net=$2;
      $node=0;
    }
    elsif(/^(Down|Hold),/){      # skip nodes with Down/Hold status.
      next
    }
    elsif(/^(Hub|Pvt|),(\d+),/){ # node number
      $node=$2;
    }
    else{
      next;
    }

    s/[\r\n]$//;
    if(!/,IBN(:.*)?(,.*)?$/){ # see binkp nodes only
      next
    }
    my @host;

    if( /,INA:([\w][\w\.]+\.\w{2,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(,.*)?$/){ # INA flag
      push @host, $1;
    }
    if(/^(\w+)?,\d+,([\w][\w\.]+\.\w{2,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}),/){ # station name field
      push @host, $2;
    }
    if($#host==-1){
      push @host, "f$node.n$net.z$zone.$default_root_domain";
    }

    foreach $host (@host) {
      if( $host =~ /^(\d+\.){3}\d+$/ ) {
        print DNSZONE "f$node.n$net.z$zone	IN	A	$host\n";
      } else {
        print DNSZONE "f$node.n$net.z$zone	IN	CNAME	$host.\n";
      }
    }
#    print DNSZONE "f$node.n$net.z$zone	IN	TXT	\"$zone:$net/$node\@fidonet\"\n\n";

  }

}

__END__
