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