add -s and -c flags, add ipv6 default zones to list of ignored zones, add nameservice...
[freeside.git] / bin / bind.import
1 #!/usr/bin/perl -w
2 #
3 # -s: import slave zones as master.  useful if you need to recreate your
4 #     primary nameserver from a secondary
5 # -c chroot_dir: import data from chrooted bind (corrects the path for
6 #                downloading zone files
7 #
8 # need to manually put header in
9 #  /usr/local/etc/freeside/export.<datasrc./bind/<machine>/named.conf.HEADER
10
11 use strict;
12 use vars qw( %d_part_svc );
13 use Getopt::Std;
14 use Term::Query qw(query);
15 #use BIND::Conf_Parser;
16 #use DNS::ZoneParse 0.81;
17
18 #use Net::SCP qw(iscp);
19 use Net::SCP qw(scp);
20 use FS::UID qw(adminsuidsetup datasrc);
21 use FS::Record qw(qsearch); #qsearchs);
22 #use FS::svc_acct_sm;
23 use FS::svc_domain;
24 use FS::domain_record;
25 #use FS::svc_acct;
26 #use FS::part_svc;
27
28 use vars qw($opt_s $opt_c);
29 getopts("sc:");
30
31 my $user = shift or die &usage;
32 adminsuidsetup $user;
33
34 $FS::svc_Common::noexport_hack = 1;
35 $FS::domain_record::noserial_hack = 1;
36
37 use vars qw($spooldir);
38 $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/bind";
39 mkdir $spooldir unless -d $spooldir;
40
41 %d_part_svc =
42   map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_domain'});
43
44 print "\n\n",
45       ( join "\n", map "$_: ".$d_part_svc{$_}->svc, sort keys %d_part_svc ),
46       "\n\n";
47 use vars qw($domain_svcpart);
48 $^W=0; #Term::Query isn't -w-safe
49 $domain_svcpart =
50   query "Enter part number for domains: ", 'irk', [ keys %d_part_svc ];
51 $^W=1;
52
53 print "\n\n", <<END;
54 Enter the location and name of your primary named.conf file, for example
55 "ns.isp.com:/var/named/named.conf"
56 END
57 my($named_conf)=&getvalue(":");
58   
59 use vars qw($named_machine $prefix);
60 $named_machine = (split(/:/, $named_conf))[0];
61 $prefix = "$spooldir/$named_machine";
62 mkdir $prefix unless -d $prefix;
63
64 #iscp("root\@$named_conf","$prefix/named.conf.import");
65 scp("root\@$named_conf","$prefix/named.conf.import");
66
67
68 sub getvalue {
69   my $prompt = shift;
70   $^W=0; # Term::Query isn't -w-safe
71   my $return = query $prompt, '';
72   $^W=1;
73   $return;
74 }
75
76 print "\n\n";
77
78 ##
79
80 $FS::svc_domain::whois_hack=1;
81
82 my $p = Parser->new;
83 $p->parse_file("$prefix/named.conf.import");
84
85 print "\nBIND import completed.\n";
86
87 ##
88
89 sub usage {
90   die "Usage:\n\n  bind.import user\n";
91 }
92
93 ########
94 BEGIN {
95   
96   package Parser;
97   use BIND::Conf_Parser;
98   use vars qw(@ISA $named_dir);
99   @ISA = qw(BIND::Conf_Parser);
100   
101   sub handle_option {
102     my($self, $option, $argument) = @_;
103     return unless $option eq "directory";
104     $named_dir = $argument;
105   }
106   
107   sub handle_zone {
108     my($self, $name, $class, $type, $options) = @_;
109     return unless $class eq 'in';
110     return if grep { $name eq $_ } (qw(
111       . localhost 127.in-addr.arpa 0.in-addr.arpa 255.in-addr.arpa
112       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
113       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
114     ));
115
116     use FS::Record qw(qsearchs);
117     use FS::svc_domain;
118
119     my $domain =
120       qsearchs('svc_domain', { 'domain' => $name } )
121       || new FS::svc_domain( {
122                                svcpart => $main::domain_svcpart,
123                                domain  => $name,
124                                action  => 'N',
125                            } );
126     unless ( $domain->svcnum ) {
127       my $error = $domain->insert;
128       die $error if $error;
129     }
130
131     if ( $type eq 'slave' && !$main::opt_s ) {
132
133       #use Data::Dumper;
134       #print Dumper($options);
135       #exit;
136
137       foreach my $master ( @{ $options->{masters} } ) {
138         my $domain_record = new FS::domain_record( {
139           'svcnum'  => $domain->svcnum,
140           'reczone' => '@',
141           'recaf'   => 'IN',
142           'rectype' => '_mstr',
143           'recdata' => $master,
144         } );
145         my $error = $domain_record->insert;
146         die $error if $error;
147       }
148
149     } elsif ( $type eq 'master' || ( $type eq 'slave' && $main::opt_s ) ) {
150
151       my $file = $options->{file};
152   
153       use File::Basename;
154       my $basefile = basename($file);
155       my $sourcefile = $file;
156       $sourcefile = "$named_dir/$sourcefile" unless $file =~ /^\//;
157       $sourcefile = "$main::opt_c/$sourcefile" if $main::opt_c;
158
159       use Net::SCP qw(iscp scp);
160       scp("root\@$main::named_machine:$sourcefile",
161            "$main::prefix/$basefile.import");
162     
163       use DNS::ZoneParse 0.84;
164       my $zone = DNS::ZoneParse->new("$main::prefix/$basefile.import");
165     
166       my $dump = $zone->dump;
167   
168       #use Data::Dumper;
169       #print "$name: ". Dumper($dump);
170       #exit;
171     
172       foreach my $rectype ( keys %$dump ) {
173         if ( $rectype =~ /^SOA$/i ) {
174           my $rec = $dump->{$rectype};
175           my $domain_record = new FS::domain_record( {
176             'svcnum'  => $domain->svcnum,
177             'reczone' => $rec->{origin},
178             'recaf'   => 'IN',
179             'rectype' => $rectype,
180             'recdata' =>
181               $rec->{primary}. ' '. $rec->{email}. ' ( '.
182              join(' ', map $rec->{$_},
183                            qw( serial refresh retry expire minimumTTL ) ).
184              ' )',
185           } );
186           my $error = $domain_record->insert;
187           die $error if $error;
188        } else {
189           #die $dump->{$rectype};
190           foreach my $rec ( @{ $dump->{$rectype} } ) {
191             my $domain_record = new FS::domain_record( {
192               'svcnum'  => $domain->svcnum,
193               'reczone' => $rec->{name},
194               'recaf'   => $rec->{class},
195               'rectype' => $rectype,
196               'recdata' => ( $rectype =~ /^MX$/i
197                                ? $rec->{priority}. ' '. $rec->{host}
198                                : $rec->{host}                      ),
199             } );
200             my $error = $domain_record->insert;
201             die $error if $error;
202           }
203         }
204       }
205
206     #} else {
207     #  die "unrecognized type $type\n";
208     }
209     
210   }
211
212 }
213 #########
214