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