#!/usr/bin/perl -w # # REQUIRED: # -p: part number for domains # # -n: named.conf file (or an include file with zones you want to import), # for example root@ns.isp.com:/var/named/named.conf # # OPTIONAL: # -d: dry-run, debug: don't insert any records, just dump debugging output # -e: use existing domains records in Freeside # -s: import slave zones as master. useful if you need to recreate your # primary nameserver from a secondary # -c dir: override patch for downloading zone files (for example, when # downloading zone files from chrooted bind) # # need to manually put header in # /usr/local/etc/freeside/export./named.conf.HEADER # (or, nowadays, better just to include the file freeside exports) use strict; use vars qw($domain_svcpart); use Getopt::Std; use Data::Dumper; #use BIND::Conf_Parser; #use DNS::ZoneParse 0.81; use Net::SCP qw(scp iscp); use FS::UID qw(adminsuidsetup datasrc); use FS::Record qw(qsearch); #qsearchs); #use FS::svc_acct_sm; use FS::svc_domain; use FS::domain_record; #use FS::svc_acct; #use FS::part_svc; use vars qw($opt_p $opt_n $opt_s $opt_c $opt_d $opt_e); getopts("p:n:sc:de"); my $user = shift or die &usage; adminsuidsetup $user; $FS::svc_Common::noexport_hack = 1; $FS::domain_record::noserial_hack = 1; use vars qw($spooldir); $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/bind"; mkdir $spooldir unless -d $spooldir; $domain_svcpart = $opt_p; my $named_conf = $opt_n; use vars qw($named_machine $prefix); $named_machine = (split(/:/, $named_conf))[0]; my $pnamed_machine = $named_machine; $pnamed_machine =~ s/^[\w\-]+\@//; $prefix = "$spooldir/$pnamed_machine"; mkdir $prefix unless -d $prefix; #iscp("$named_conf","$prefix/named.conf.import"); scp("$named_conf","$prefix/named.conf.import"); ## $FS::svc_domain::whois_hack=1; my $p = Parser->new; $p->parse_file("$prefix/named.conf.import"); print "\nBIND import completed.\n"; ## sub usage { die "Usage:\n\n bind.import -p partnum -n \"user\@machine:/path/to/named.conf\" [ -s ] [ -c chroot_dir ] [ -d ] [ -e ] user\n"; } ######## BEGIN { package Parser; use BIND::Conf_Parser; use vars qw(@ISA $named_dir); @ISA = qw(BIND::Conf_Parser); $named_dir = 'COULD_NOT_FIND_NAMED_DIRECTORY_TRY_SETTING_-C_OPTION'; sub handle_option { my($self, $option, $argument) = @_; return unless $option eq "directory"; $named_dir = $argument; #warn "found named dir: $named_dir\n"; } sub handle_zone { my($self, $name, $class, $type, $options) = @_; return unless $class eq 'in'; return if grep { $name eq $_ } (qw( . localhost 127.in-addr.arpa 0.in-addr.arpa 255.in-addr.arpa 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.int )); use FS::Record qw(qsearchs); use FS::svc_domain; my $domain = qsearchs('svc_domain', { 'domain' => $name } ) || new FS::svc_domain( { svcpart => $main::domain_svcpart, domain => $name, action => 'N', } ); unless ( $domain->svcnum ) { my $error = $domain->insert; die $error if $error; } if ( $type eq 'slave' && !$main::opt_s ) { if ( $main::opt_d ) { use Data::Dumper; print "$name: ". Dumper($options); } else { foreach my $master ( @{ $options->{masters} } ) { my $domain_record = new FS::domain_record( { 'svcnum' => $domain->svcnum, 'reczone' => '@', 'recaf' => 'IN', 'rectype' => '_mstr', 'recdata' => $master, } ); my $error = $domain_record->insert; die $error if $error; } } } elsif ( $type eq 'master' || ( $type eq 'slave' && $main::opt_s ) ) { my $file = $options->{file}; use File::Basename; my $basefile = basename($file); my $sourcefile = $file; if ( $main::opt_c ) { $sourcefile = "$main::opt_c/$sourcefile" if $main::opt_c; } else { $sourcefile = "$named_dir/$sourcefile" unless $file =~ /^\//; } use Net::SCP qw(iscp scp); #iscp("$main::named_machine:$sourcefile", # "$main::prefix/$basefile.import"); scp("$main::named_machine:$sourcefile", "$main::prefix/$basefile.import"); use DNS::ZoneParse 0.84; my $zone = DNS::ZoneParse->new("$main::prefix/$basefile.import"); my $dump = $zone->dump; if ( $main::opt_d ) { use Data::Dumper; print "$name: ". Dumper($dump); } else { foreach my $rectype ( keys %$dump ) { if ( $rectype =~ /^SOA$/i ) { my $rec = $dump->{$rectype}; $rec->{email} =~ s/\@/\./; my $domain_record = new FS::domain_record( { 'svcnum' => $domain->svcnum, 'reczone' => $rec->{origin}, 'recaf' => 'IN', 'rectype' => $rectype, 'recdata' => $rec->{primary}. ' '. $rec->{email}. ' ( '. join(' ', map $rec->{$_}, qw( serial refresh retry expire minimumTTL ) ). ' )', } ); my $error = $domain_record->insert; die $error if $error; } else { #die $dump->{$rectype}; my $datasub; if ( $rectype =~ /^MX$/i ) { $datasub = sub { $_[0]->{priority}. ' '. $_[0]->{host}; }; } elsif ( $rectype =~ /^TXT$/i ) { $datasub = sub { $_[0]->{text}; }; } else { $datasub = sub { $_[0]->{host}; }; } foreach my $rec ( @{ $dump->{$rectype} } ) { my $domain_record = new FS::domain_record( { 'svcnum' => $domain->svcnum, 'reczone' => $rec->{name}, 'recaf' => $rec->{class} || 'IN', 'rectype' => $rectype, 'recdata' => &{$datasub}($rec), } ); my $error = $domain_record->insert; if ( $error ) { warn "$error inserting ". $rec->{name}. ' . '. $domain->domain. "\n"; warn Dumper($rec); #system('cat',"$main::prefix/$basefile.import"); die; } } } } } #} else { # die "unrecognized type $type\n"; } } } #########