#!/usr/bin/perl # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html #This is ported forward from xCAT 1.3 #TODO: A lot of stuff was handled by the script portion of makedns, notably: # db.cache # forwarders # chroot # dnsallowq # mucking with sysconfig package xCAT_plugin::bind; use strict; no strict "refs"; #This code is as yet still very broken... #use warnings; use Sys::Hostname; use Cwd; use Getopt::Long; use xCAT::Table; use xCAT::MsgUtils; use Data::Dumper; sub handled_commands { return {"makedns" => "bind"}; } #NAME # # h2n - Translate host table to name server file format # $Date: 1999/08/08 17:17:56 $ $Revision: 8.2 $ # #SYNOPSIS # # h2n -d DOMAIN -n NET [options] # Various defaults my $Host; my $doaliases = 1; my $domx = 1; my $dowks = 0; my $dotxt = 0; my $dontdodomains = 0; my $Bootfile = "/etc/named.conf"; my $DBDir = "/var/named/"; my $Domain = ""; my $Hostfile = "/etc/hosts"; my $Commentfile = ""; my $Commentfileread = 0; my $User = "root"; my $RespHost = ""; my $RespUser = ""; my $DefSerial = 1; my $DefRefresh = 10800; my $DefRetry = 3600; my $DefExpire = 604800; my $DefTtl = 86400; my $UseDefSOAValues = 0; my $DefMxWeight = 10; my $Defsubnetmask = ""; my $ForceSerial = -1; my $UseDateInSerial = 1; my $DateSerial = 0; my $Version = 8; my $request; my $callback; my @forwarders; #Declarations to alleviate use strict, since the code doesn't seem to be structured well enough to avoid it for these cases my $Bootsecsaveaddr; my $Bootsecaddr; my @Networks; my @bootmsgs_v4; my @bootmsgs_v8; my @elimpats; my @cpats; my @makesoa; my $Domainfile; my %cpatrel; my @Servers; my $Serial; my $Refresh; my @Mx; my $Expire; my %Hosts; my %Comments; my $Domainpattern; my @Netpatterns; my $Ttl; my $Retry; my %Cnames; my %CommentRRs; my $soa_warned; my %Aliases; my %Netfiles; sub process_request { $request = shift; $callback = shift; %Netfiles = (); %Aliases = (); $soa_warned = 0; my $canonical; my $aliases; %Comments = (); %CommentRRs = (); %Cnames = (); %Hosts = (); @Netpatterns = (); $DBDir = "/var/named/"; unless (-d $DBDir) { $DBDir = "/var/lib/named/"; } # if both do not exist, make /var/named unless (-d $DBDir) { $DBDir = "/var/named/"; my $cmd = "/bin/mkdir $DBDir"; my $outref = xCAT::Utils->runcmd("$cmd", 0); if ($::RUNCMD_RC != 0) { my $rsp = {}; $rsp->{data}->[0] = "Could not create $DBDir.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } } $Host = hostname; $Host =~ s/\..*//; my $sitetab = xCAT::Table->new('site'); unless ($sitetab) { my $rsp = {}; $rsp->{data}->[0] = "No site table found.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); return; } my @args = (); if ($request->{arg}) { @args = @{$request->{arg}}; @ARGV = @{$request->{arg}}; } &checkusageandversion($callback); (my $fent) = $sitetab->getAttribs({key => 'forwarders'}, 'value'); if ($fent and defined $fent->{value}) { @forwarders = split /[,:;]/, $fent->{value}; } unless (grep /^-d$/, @args) { (my $dent) = $sitetab->getAttribs({key => 'domain'}, 'value'); if ($dent and $dent->{value}) { push @args, "-d"; $dent->{value} =~ s/\.$//; push @args, $dent->{value}; } } unless (grep /^-s$/, @args) { push @args, "-s"; push @args, $Host; } unless (grep /^-n$/, @args) { my $nettab = xCAT::Table->new('networks'); unless ($nettab) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open networks table, has makenetworks been run?.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); return; } foreach (@{$nettab->getAllEntries()}) { if ($_->{net} =~ /:/) { my $rsp = {}; $rsp->{data}->[0] = "Ignoring IPv6 network, not supported in bind.pm (site.dnshandler=ddns if you want to do IPv6 DNS records"; xCAT::MsgeUtils->message("W",$rsp,$callback,1); next; } push @args, "-n"; push @args, $_->{net} . ":" . $_->{mask}; } } push(@bootmsgs_v4, "primary\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"); push(@bootmsgs_v8, qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;\n\tfile "db.127.0.0";\n\tnotify no;\n};\n\n| ); &PARSEARGS($callback, @args); &FIXUP($callback); unless (open(HOSTS, $Hostfile)) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open $Hostfile\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } my $data; my $comment; my $addr; my $names; LINE: while () { next if /^[ \t]*#/; # skip comment lines next if /^$/; # skip empty lines chop; # remove the trailing newline tr/A-Z/a-z/; # translate to lower case ($data, $comment) = split('#', $_, 2); ($addr, $names) = split(/[ ]+/, $data, 2); if ($names =~ /^[ \t]*$/) { #$callback->({data=>["Bad line in hosts file ignored '$_'"]}); next LINE; } $addr =~ s/^[ ]*//; $addr =~ s/[ ]*$//; if ($addr !~ /^\d+\.\d+\.\d+\.\d+$/) { my $rsp = {}; $rsp->{data}->[0] = "Ignoring $addr (not a valid IPv4 address).\n"; xCAT::MsgUtils->message("I", $rsp, $callback); next LINE; } # Match -e args my $netpat; foreach $netpat (@elimpats) { next LINE if (/[.\s]$netpat/); } # Process -c args foreach $netpat (@cpats) { if (/\.$netpat/) { ($canonical, $aliases) = split(' ', $names, 2); $canonical =~ s/\.$netpat//; #If we feed in names with, say, underscores, bind complains usually due to default check-names behavior. Later, we may should support putting check-names to ignore, but for now let them know it won't work and skip it unless ($canonical =~ /^[a-z0-9-]+$/i) { xCAT::MsgUtils->message("E", {errorcode=>[1],error=>["$canonical contains invalid characters, skipping entry"]}, $callback, 1); next LINE; } if ($Cnames{$canonical} != 1) { printf DOMAIN "%-20s IN CNAME %s.%s.\n", $canonical, $canonical, $cpatrel{$netpat}; $Cnames{$canonical} = 1; } next LINE; } } # Check that the address is in the address list. my $match = 'none'; foreach $netpat (@Netpatterns) { $match = $netpat, last if ($addr =~ /^$netpat\./ or $addr =~ /^$netpat$/); } next if ($match eq 'none'); ($canonical, $aliases) = split(' ', $names, 2); # separate out aliases next if ($dontdodomains && $canonical =~ /\./); # skip domain names $canonical =~ s/$Domainpattern//; # strip off domain if there is one unless ($canonical =~ /^[a-z0-9-]+$/i) { xCAT::MsgUtils->message("E", {errorcode=>[1],error=>["$canonical contains invalid characters, skipping entry"]}, $callback, 1); next; } $Hosts{$canonical} .= $addr . " "; # index addresses by canonical name $Aliases{$addr} .= $aliases . " "; # index aliases by address $Comments{"$canonical-$addr"} = $comment; # Print PTR records my $file = $Netfiles{$match}; printf $file "%-30s\tIN PTR %s.%s.\n", &REVERSE($addr), $canonical, $Domain; } # # Go through the list of canonical names. # If there is more than 1 address associated with the # name, it is a multi-homed host. For each address # look up the aliases since the aliases are associated # with the address, not the canonical name. # foreach $canonical (keys %Hosts) { my @addrs = split(' ', $Hosts{$canonical}); my $numaddrs = $#addrs + 1; foreach my $addr (@addrs) { # # Print address record for canonical name. # if ($Cnames{$canonical} != 1) { printf DOMAIN "%-20s IN A %s\n", $canonical, $addr; } else { my $rsp = {}; $rsp->{data}->[0] = "$canonical - cannot create A record because CNAME exists for name.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } # # Print cname or address records for each alias. # If this is a multi-homed host, print an address # record for each alias. If this is a single address # host, print a cname record. # my $alias; if ($doaliases) { my @aliases = split(' ', $Aliases{$addr}); foreach $alias (@aliases) { # # Skip over the alias if the alias and canonical # name only differ in that one of them has the # domain appended to it. # next if ($dontdodomains && $alias =~ /\./); # skip domain names $alias =~ s/$Domainpattern//; if ($alias eq $canonical) { next; } unless ($alias =~ /^[a-z0-9-]+$/i) { xCAT::MsgUtils->message("E", {errorcode=>[1],error=>["$canonical alias $alias contains invalid characters, skipping entry"]}, $callback, 1); next; } my $aliasforallnames = 0; if ($numaddrs > 1) { # # If alias exists for *all* addresses of this host, we # can use a CNAME instead of an address record. # my $aliasforallnames = 1; my $xalias = $alias . " "; # every alias ends with blank my @xaddrs = split(' ', $Hosts{$canonical}); foreach my $xaddr (@xaddrs) { if (!($Aliases{$xaddr} =~ /\b$xalias/)) { $aliasforallnames = 0; } } } if (($numaddrs > 1) && !$aliasforallnames) { printf DOMAIN "%-20s IN A %s\n", $alias, $addr; } else { # # Flag aliases that have already been used # in CNAME records or have A records. # if (($Cnames{$alias} != 1) && (!$Hosts{$alias})) { printf DOMAIN "%-20s IN CNAME %s.%s.\n", $alias, $canonical, $Domain; $Cnames{$alias} = 1; } else { my $rsp = {}; $rsp->{data}->[0] = "$alias - CNAME or A exists already. alias ignored.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } } if ($aliasforallnames) { # # Since a CNAME record was created, remove this # name from the alias list so we don't encounter # it again for the next address of this host. # my $xalias = $alias . " "; # every alias ends with blank my @xaddrs = split(' ', $Hosts{$canonical}); my $xaddr; foreach $xaddr (@xaddrs) { $Aliases{$xaddr} =~ s/\b$xalias//; } } } } } if ($domx) { &MX($callback, $canonical, @addrs); } if ($dotxt) { &TXT($canonical, @addrs); } if ($Commentfile ne "") { &DO_COMMENTS($callback, $canonical, @addrs); } } # Deal with spcl's if (-r "spcl.$Domainfile") { print DOMAIN "\$INCLUDE spcl.$Domainfile\n"; } my $file; my $n; foreach $n (@Networks) { if (-r "spcl.$n") { $file = "DB.$n"; print $file "\$INCLUDE spcl.$n\n"; } } # generate boot.* files &GEN_BOOT($callback); my $rsp = {}; $rsp->{data}->[0] = "Setup of DNS complete."; xCAT::MsgUtils->message("I", $rsp, $callback); exit 0; } # # Generate resource record data for # strings from the commment field that # are found in the comment file (-C). # sub DO_COMMENTS { my ($callback, $canonical, @addrs) = @_; my (@c, $c, $a, $comments); if (!$Commentfileread) { unless (open(F, $Commentfile)) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open file $Commentfile: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } $Commentfileread++; while () { chop; my ($key, $c) = split(':', $_, 2); $CommentRRs{$key} = $c; } close(F); } my $key; foreach $a (@addrs) { $key = "$canonical-$a"; $comments .= " $Comments{$key}"; } @c = split(' ', $comments); foreach $c (@c) { if ($CommentRRs{$c}) { printf DOMAIN "%-20s %s\n", $canonical, $CommentRRs{$c}; } } } # # Generate MX record data # sub MX { my ($callback, $canonical, @addrs) = @_; my ($first, $a, $key, $comments); if ($Cnames{$canonical}) { my $rsp = {}; $rsp->{data}->[0] = "$canonical - cannot create MX record because CNAME exists for name.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); return; } $first = 1; foreach $a (@addrs) { $key = "$canonical-$a"; $comments .= " $Comments{$key}"; } if ($comments !~ /\[no smtp\]/) { # Add WKS if requested if ($dowks) { foreach $a (@addrs) { printf DOMAIN "%-20s IN WKS %s TCP SMTP\n", $canonical, $a; } } printf DOMAIN "%-20s IN MX %s %s.%s.\n", $canonical, $DefMxWeight, $canonical, $Domain; $first = 0; } if ($#Mx >= 0) { foreach $a (@Mx) { if ($first) { printf DOMAIN "%-20s IN MX %s\n", $canonical, $a; $first = 0; } else { printf DOMAIN "%-20s IN MX %s\n", "", $a; } } } } # # Generate TXT record data # sub TXT { my ($canonical, @addrs) = @_; my ($a, $key, $comments); foreach $a (@addrs) { $key = "$canonical-$a"; $comments .= " $Comments{$key}"; } $comments =~ s/\[no smtp\]//g; $comments =~ s/^\s*//; $comments =~ s/\s*$//; if ($comments ne "") { printf DOMAIN "%s IN TXT \"%s\"\n", $canonical, $comments; } } # # Create the SOA record at the beginning of the file # sub MAKE_SOA { my ($callback, $fname, $file) = @_; my ($s); if (-s $fname) { unless (open($file, "$fname")) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open $fname: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } $_ = <$file>; chop; if (/\($/) { my $junk; if (!$soa_warned) { my $rsp = {}; $rsp->{data}->[0] = "Converting SOA format to new style.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); $soa_warned++; } if ($ForceSerial > 0) { $Serial = $ForceSerial; } else { ($Serial, $junk) = split(' ', <$file>, 2); $Serial++; if ($UseDateInSerial && ($DateSerial > $Serial)) { $Serial = $DateSerial; } } ($Refresh, $junk) = split(' ', <$file>, 2); ($Retry, $junk) = split(' ', <$file>, 2); ($Expire, $junk) = split(' ', <$file>, 2); ($Ttl, $junk) = split(' ', <$file>, 2); } else { if (/TTL/) { $_ = <$file>; } split(' '); if ($#_ == 11) { if ($ForceSerial > 0) { $Serial = $ForceSerial; } else { $Serial = ++$_[6]; if ($UseDateInSerial && ($DateSerial > $Serial)) { $Serial = $DateSerial; } } $Refresh = $_[7]; $Retry = $_[8]; $Expire = $_[9]; $Ttl = $_[10]; } else { my $rsp = {}; $rsp->{data}->[0] = "Improper format SOA in $fname.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } } if ($UseDefSOAValues) { $Refresh = $DefRefresh; $Retry = $DefRetry; $Expire = $DefExpire; $Ttl = $DefTtl; } close($file); } else { if ($ForceSerial > 0) { $Serial = $ForceSerial; } else { $Serial = $DefSerial; if ($UseDateInSerial && ($DateSerial > $Serial)) { $Serial = $DateSerial; } } $Refresh = $DefRefresh; $Retry = $DefRetry; $Expire = $DefExpire; $Ttl = $DefTtl; close($file); } unless (open($file, "> $fname")) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open $fname: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } print $file '$TTL 86400' . "\n"; print $file "\@ IN SOA $RespHost $RespUser "; print $file "( $Serial $Refresh $Retry $Expire $Ttl )\n"; foreach $s (@Servers) { print $file " IN NS $s\n"; } print $file "\n"; } # # Reverse the octets of an IP address and append # in-addr.arpa. # sub REVERSE { join('.', reverse(split('\.', $_[0]))) . '.IN-ADDR.ARPA.'; } # # Establish what we will be using for SOA records # sub FIXUP { my $callback = shift; my ($s); if ($Host =~ /\./) { $RespHost = "$Host."; } else { $RespHost = "$Host.$Domain."; } $RespHost =~ s/\.\././g; if ($User =~ /@/) { # -u user@... if ($User =~ /\./) { $RespUser = "$User."; # -u user@terminator.movie.edu } else { $RespUser = "$User.$Domain."; # -u user@terminator } $RespUser =~ s/@/./; } elsif ($User =~ /\./) { $RespUser = "$User."; # -u user.terminator.movie.edu } else { $RespUser = "$User.$RespHost"; # -u user } $RespUser =~ s/\.\././g; # Strip any ".."'s to "." # Clean up nameservers if (!@Servers) { my $rsp = {}; $rsp->{data}->[0] = "No -s option specified. Assuming \"-s $Host.$Domain\".\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); push(@Servers, "$Host.$Domain."); } else { foreach $s (@Servers) { if ($s !~ /\./) { $s .= ".$Domain"; } if ($s !~ /\.$/) { $s .= "."; } } } # Clean up MX hosts foreach $s (@Mx) { $s =~ s/:/ /; if ($s !~ /\./) { $s .= ".$Domain"; } if ($s !~ /\.$/) { $s .= "."; } } # Now open boot file (named.conf) and print saved data unless (open(BOOT, "> $Bootfile")) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open $Bootfile.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } # # Write either the version 4 boot file directives or the # version 8 boot file directives. # if ($Version == 4) { print BOOT "\ndirectory $DBDir\n"; foreach my $line (@bootmsgs_v4) { print BOOT $line; } print BOOT "cache\t. db.cache\n"; if (-r "spcl.boot") { print BOOT "include\tspcl.boot\n"; } } else { print BOOT qq|\noptions {\n\tdirectory "$DBDir";\n|; if (@forwarders) { print BOOT qq|\tforwarders {\n|; foreach (@forwarders) { print BOOT qq|\t\t$_;\n|; } print BOOT qq|\t};\n|; } if (-r "spcl.options") { print BOOT "\t# These options came from the file spcl.options\n"; # # Copy the options in since "include" cannot be used # within a statement. # unless (open(OPTIONS, "{data}->[0] = "Unable to open spcl.options.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } while () { print BOOT; } close(OPTIONS); } print BOOT qq|};\n\n|; foreach my $line (@bootmsgs_v8) { print BOOT $line; } # hint zone is also needed for DNS forwarders on AIX if (xCAT::Utils->isAIX()) { print BOOT qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; } else { unless (@forwarders) { print BOOT qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; } } if (-r "spcl.boot") { print BOOT qq|include "spcl.boot";\n\n|; } } close(BOOT); # Go ahead and start creating files and making SOA's my $x1; my $x2; foreach my $i (@makesoa) { ($x1, $x2) = split(' ', $i); &MAKE_SOA($callback, $x1, $x2); } printf DOMAIN "%-20s IN A 127.0.0.1\n", "localhost"; my $file = "DB.127.0.0.1"; &MAKE_SOA($callback, $DBDir . "db.127.0.0", $file); if (xCAT::Utils->isAIX()) { # if forwarders is set, we need to create the hint file for root name servers. if (@forwarders) { my $tmpfile = $DBDir . "db.cache"; my $cmd = qq~dig @"$forwarders[0]" . ns >> $tmpfile~; my $outref = xCAT::Utils->runcmd("$cmd", 0); if ($::RUNCMD_RC != 0) { my $rsp = {}; $rsp->{data}->[0] = "Could not run command: $cmd.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } } else { my $nothing; open($nothing, ">>", $DBDir . "db.cache"); close($nothing); } } else { my $nothing; open($nothing, ">>", $DBDir . "db.cache"); close($nothing); } printf $file "%-30s\tIN PTR localhost.\n", &REVERSE("127.0.0.1"); close($file); } sub PARSEARGS { my ($callback, @args) = @_; my ($i, $net, $subnetmask, $option, $tmp1); my ($file, @newargs, @targs); my ($sec, $min, $hour, $mday, $mon, $year, $rest); ($sec, $min, $hour, $mday, $mon, $year, $rest) = localtime(time); $DateSerial = ($mday * 100) + (($mon + 1) * 10000) + (($year + 1900) * 1000000); $i = 0; while ($i <= $#args) { $option = $args[$i]; if ($option eq "-d") { if ($Domain ne "") { my $rsp = {}; $rsp->{data}->[0] = "Only one -d option allowed.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } $Domain = $args[++$i]; $Domainpattern = "." . $Domain; $Domainpattern =~ s/\./\\./g; # for stripping off domain # Add entry to the boot file. $Domainfile = $Domain; $Domainfile =~ s/\..*//; push(@makesoa, $DBDir . "db.$Domainfile DOMAIN"); push(@bootmsgs_v4, "primary\t$Domain db.$Domainfile\n"); push(@bootmsgs_v8, qq|zone "$Domain" in {\n\ttype master;\n\tfile "db.$Domainfile";\n};\n\n| ); } elsif ($option eq "-f") { $file = $args[++$i]; unless (open(F, $file)) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open args file $file: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } while () { next if (/^#/); next if (/^$/); chop; @targs = split(' '); push(@newargs, @targs); } close(F); &PARSEARGS($callback, @newargs); } elsif ($option eq "-z") { $Bootsecsaveaddr = $args[++$i]; if (!defined($Bootsecaddr)) { $Bootsecaddr = $Bootsecsaveaddr; } } elsif ($option eq "-Z") { $Bootsecaddr = $args[++$i]; if (!defined($Bootsecsaveaddr)) { $Bootsecsaveaddr = $Bootsecaddr; } } elsif ($option eq "-b") { $Bootfile = $args[++$i]; } elsif ($option eq "-A") { $doaliases = 0; } elsif ($option eq "-M") { $domx = 0; } elsif ($option eq "-w") { $dowks = 1; } elsif ($option eq "-D") { $dontdodomains = 1; } elsif ($option eq "-t") { $dotxt = 1; } elsif ($option eq "-u") { $User = $args[++$i]; } elsif ($option eq "-s") { while ($args[++$i] !~ /^-/ && $i <= $#args) { push(@Servers, $args[$i]); } $i--; } elsif ($option eq "-m") { if ($args[++$i] !~ /:/) { my $rsp = {}; $rsp->{data}->[0] = "Improper format for -m option ignored ($args[$i]).\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } push(@Mx, $args[$i]); } elsif ($option eq "-c") { my $tmp1 = $args[++$i]; if ($tmp1 !~ /\./) { $tmp1 .= ".$Domain"; } if ($Domain eq $tmp1) { my $rsp = {}; $rsp->{data}->[0] = "Domain for -c option must not match domain for -d option.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } my $tmp2 = $tmp1; $tmp2 =~ s/\./\\./g; $cpatrel{$tmp2} = $tmp1; push(@cpats, $tmp2); } elsif ($option eq "-e") { $tmp1 = $args[++$i]; if ($tmp1 !~ /\./) { $tmp1 .= ".$Domain"; } $tmp1 =~ s/\./\\./g; push(@elimpats, $tmp1); } elsif ($option eq "-h") { $Host = $args[++$i]; } elsif ($option eq "-o") { if ($args[++$i] !~ /^[:\d]*$/ || split(':', $args[$i]) != 4) { my $rsp = {}; $rsp->{data}->[0] = "Improper format for -o ($args[$i]).\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } ($DefRefresh, $DefRetry, $DefExpire, $DefTtl) = split(':', $args[$i]); $UseDefSOAValues = 1; } elsif ($option eq "-i") { $ForceSerial = $args[++$i]; } elsif ($option eq "-H") { $Hostfile = $args[++$i]; if (!-r $Hostfile || -z $Hostfile) { my $rsp = {}; $rsp->{data}->[0] = "Invalid file specified for -H ($Hostfile).\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } } elsif ($option eq "-C") { $Commentfile = $args[++$i]; if (!-r $Commentfile || -z $Commentfile) { my $rsp = {}; $rsp->{data}->[0] = "Invalid file specified for -C ($Commentfile).\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } } elsif ($option eq "-N") { $Defsubnetmask = $args[++$i]; if ($Defsubnetmask !~ /^[.\d]*$/ || split('\.', $Defsubnetmask) != 4) { my $rsp = {}; $rsp->{data}->[0] = "Improper subnet mask ($Defsubnetmask).\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } if ($#Networks >= 0) { my $rsp = {}; $rsp->{data}->[0] = "-N option should be specified before any -n options.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } } elsif ($option eq "-n") { (my $tnet, $subnetmask) = split(':', $args[++$i]); $net = ""; my @netm = split(/\./, $subnetmask); my @tnets = split(/\./, $tnet); foreach (0 .. 3) { my $res = ($tnets[$_] + 0) & ($netm[$_] + 0); if ($netm[$_]) { $net .= $res . '.'; } } $net =~ s/\.$//; if (not $net) { my $rsp = {}; $rsp->{data}->[0] = "Empty network found in networks table (i.e. ,,),This is almost certainly going to cause a problem....\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } if ($subnetmask eq "") { my $rsp = {}; $rsp->{data}->[0] = "$net has no defined netmask in the networks table, the result will probably be wrong.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); foreach $tmp1 (&SUBNETS($net, $Defsubnetmask)) { &BUILDNET($tmp1); } } else { if ($subnetmask !~ /^[.\d]*$/ || split('\.', $subnetmask) != 4) { my $rsp = {}; $rsp->{data}->[0] = "Improper subnet mask ($subnetmask).\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } foreach $tmp1 (&SUBNETS($net, $subnetmask)) { &BUILDNET($tmp1); } } } else { if ($option =~ /^-.*/) { my $rsp = {}; $rsp->{data}->[0] = "Unknown option: $option ... ignored.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); } } $i++; } if (!@Networks || $Domain eq "") { my $rsp = {}; $rsp->{data}->[0] = "Must specify one -d and at least one -n.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit(1); } } sub BUILDNET { my ($net) = @_; push(@Networks, $net); # # Create pattern to match against. # The dots must be changed to \. so they # aren't used as wildcards. # my $netpat = $net; $netpat =~ s/\./\\./g; push(@Netpatterns, $netpat); # # Create db files for PTR records. # Save the file names in an array for future use. # my $netfile = "DB.$net"; $Netfiles{$netpat} = $netfile; push(@makesoa, $DBDir . "db.$net $netfile"); # Add entry to the boot file. my $revaddr = &REVERSE($net); chop($revaddr); # remove trailing dot push(@bootmsgs_v4, "primary $revaddr db.$net\n"); push(@bootmsgs_v8, qq|zone "$revaddr" in {\n\ttype master;\n\tfile "db.$net";\n};\n\n|); } # # Calculate all the subnets from a network number and mask. # This was originally written for awk, not perl. # sub SUBNETS { my ($network, $mask) = @_; my (@ans, @net, @mask, $buf, $number, $i, $j, $howmany); @net = split(/\./, $network); @mask = split(/\./, $mask); $number = ''; # # Only expand bytes 1, 2, or 3 # for DNS purposes # for ($i = 0 ; $i < 4 ; $i++) { if ($mask[$i] == 255) { $number = $number . $net[$i] . '.'; } elsif (($mask[$i] == 0) || $mask[$i] eq '') { push(@ans, $network); last; } else { # # This should be done as a bit-wise or # but awk does not have an or symbol # $howmany = 255 - $mask[$i]; for ($j = 0 ; $j <= $howmany ; $j++) { if ($net[$i] + $j <= 255) { $buf = sprintf("%s%d", $number, $net[$i] + $j); push(@ans, $buf); } } last; } } if ($#ans == -1) { push(@ans, $network); } @ans; } sub GEN_BOOT { $callback = shift; my ($revaddr, $n); if (0) { #! -e "boot.cacheonly") { DISABLE THIS PART # # Create a boot file for a cache-only server # unless (open(F, ">boot.cacheonly")) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open boot.cacheonly: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } if ($Version == 4) { print F "directory\t$DBDir\n"; print F "primary\t\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"; print F "cache\t\t. db.cache\n"; if (-r "spcl.cacheonly") { printf F "include\t\tspcl.cacheonly\n"; } close(F); } else { print F qq|\noptions {\n\tdirectory "$DBDir";\n|; if (@forwarders) { print F qq|\tforwarders {\n|; foreach (@forwarders) { print F qq|\t\t$_;\n|; } print F qq|\t};\n|; } if (-r "spcl.options") { print F "\t# These options came from the file spcl.options\n"; # # Copy the options in since "include" cannot be used # within a statement. # unless (open(OPTIONS, "{data}->[0] = "Unable to open boot.cacheonly: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } while () { print F; } close(OPTIONS); } print F qq|};\n\n|; print F qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;|; print F qq|\n\tfile "db.127.0.0";|; print F qq|\n\tnotify no;\n};\n\n|; #print F qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; if (-r "spcl.cacheonly") { print F qq|include "spcl.cacheonly";\n\n|; } } } # # Create a 2 boot files for a secondary (slave) servers. # One boot file doesn't save the zone data in a file. The # other boot file does save the zone data in a file. # if (defined($Bootsecaddr)) { unless (open(F, ">boot.sec")) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open boot.sec: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } if ($Version == 4) { print F "directory\t$DBDir\n"; print F "primary\t\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"; printf F "secondary\t%-23s $Bootsecaddr\n", $Domain; foreach $n (@Networks) { $revaddr = &REVERSE($n); chop($revaddr); printf F "secondary\t%-23s $Bootsecaddr\n", $revaddr; } print F "cache\t\t. db.cache\n"; if (-r "spcl.boot") { printf F "include\t\tspcl.boot\n"; } } else { print F qq|\noptions {\n\tdirectory "$DBDir";\n|; if (-r "spcl.options") { print F "\t# These options came from the file spcl.options\n"; # # Copy the options in since "include" cannot be used # within a statement. # unless (open(OPTIONS, "{data}->[0] = "Unable to open spcl.options.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } while () { print F; } close(OPTIONS); } print F qq|};\n\n|; print F qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;|; print F qq|\n\tfile "db.127.0.0";|; print F qq|\n\tnotify no;\n};\n\n|; print F qq|zone "$Domain" in {\n\ttype slave;\n\tmasters {|; print F qq| $Bootsecaddr; };\n};\n\n|; foreach $n (@Networks) { $revaddr = &REVERSE($n); chop($revaddr); print F qq|zone "$revaddr" in {\n\ttype slave;\n\tmasters {|; print F qq| $Bootsecaddr; };\n};\n\n|; } #print F qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; if (-r "spcl.boot") { print F qq|include "spcl.boot";\n\n|; } } close(F); unless (open(F, ">boot.sec.save")) { my $rsp = {}; $rsp->{data}->[0] = "Unable to open boot.sec.save: $!.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } if ($Version == 4) { print F "directory\t$DBDir\n"; print F "primary\t\t0.0.127.IN-ADDR.ARPA db.127.0.0\n"; printf F "secondary\t%-23s $Bootsecsaveaddr db.%s\n", $Domain, $Domainfile; foreach $n (@Networks) { $revaddr = &REVERSE($n); chop($revaddr); printf F "secondary\t%-23s $Bootsecsaveaddr db.%s\n", $revaddr, $n; } print F "cache\t\t. db.cache\n"; if (-r "spcl.boot") { printf F "include\t\tspcl.boot\n"; } } else { print F qq|\noptions {\n\tdirectory "$DBDir";\n|; if (-r "spcl.options") { print F "\t# These options came from the file spcl.options\n"; # # Copy the options in since "include" cannot be used # within a statement. # unless (open(OPTIONS, "{data}->[0] = "Can't open spcl.options.\n"; xCAT::MsgUtils->message("E", $rsp, $callback, 1); exit 1; } while () { print F; } close(OPTIONS); } print F qq|};\n\n|; print F qq|zone "0.0.127.IN-ADDR.ARPA" in {\n\ttype master;|; print F qq|\n\tfile "db.127.0.0";|; print F qq|\n\tnotify no;\n};\n\n|; print F qq|zone "$Domain" in {\n\ttype slave;\n\tfile "db.$Domainfile";|; print F qq|\n\tmasters { $Bootsecsaveaddr; };\n};\n\n|; foreach $n (@Networks) { $revaddr = &REVERSE($n); chop($revaddr); print F qq|zone "$revaddr" in {\n\ttype slave;\n\tfile "db.$n";\n\tmasters {|; print F qq| $Bootsecsaveaddr; };\n};\n\n|; } #print F qq|zone "." in {\n\ttype hint;\n\tfile "db.cache";\n};\n\n|; if (-r "spcl.boot") { print F qq|include "spcl.boot";\n\n|; } } close(F); } } # subroutine to display the usage sub checkusageandversion { my $callback = shift; # parse the options if ( GetOptions('h|help' => \$::HELP, 'v|version' => \$::VERSION) ) { if ($::HELP) { my $rsp = {}; $rsp->{data}->[0] = "Usage:"; $rsp->{data}->[1] = " makedns "; $rsp->{data}->[2] = " makedns [-h|--help|-v|--version]"; $rsp->{data}->[3] = " See man makedns"; xCAT::MsgUtils->message("I", $rsp, $callback); exit 0; } if ($::VERSION) { my $version = xCAT::Utils->Version(); my $rsp = {}; $rsp->{data}->[0] = "$version"; xCAT::MsgUtils->message("I", $rsp, $callback); exit 0; } } return; } 1;