diff --git a/xCAT-server/lib/xcat/plugins/dns.pm.exp b/xCAT-server/lib/xcat/plugins/dns.pm.exp new file mode 100644 index 000000000..cfc37a68d --- /dev/null +++ b/xCAT-server/lib/xcat/plugins/dns.pm.exp @@ -0,0 +1,261 @@ +package xCAT_plugin::dns; +use Getopt::Long; +use Net::DNS; +use xCAT::Table; +use Socket; +use strict; +#This is a rewrite of DNS management using nsupdate rather than direct zone mangling + +my $callback; + +sub update_named_conf { + my $ctx = shift; + +} +sub get_reverse_zone_for_entity { + my $ctx = shift; + my $node = shift; + my $net; + if ($ctx->{hoststab} and $ctx->{hoststab}->{$node} and $ctx->{hoststab}->{$node}->[0]->{ip}) { + $node = $ctx->{hoststab}->{$node}->[0]->{ip}; + } + my $tvar; + if ($tvar = inet_aton($node)) { #This is an assignment, we are testing and storing the value in one shot + $tvar = unpack("N",$tvar); + foreach my $net (keys %{$ctx->{nets}}) { + if ($ctx->{nets}->{$net}->{netn} == ($tvar & $ctx->{nets}->{$net}->{mask})) { + my $maskstr = unpack("B32",pack("N",$ctx->{nets}->{$net}->{mask})); + my $maskcount = ($maskstr =~ tr/1//); + $maskcount+=((8-($maskcount%8))%8); #round to the next octet + my $newmask = 2**$maskcount -1 << (32 - $maskcount); + my $rev = inet_ntoa(pack("N",($tvar & $newmask))); + my @zone; + my @orig=split /\./,$rev; + while ($maskcount) { + $maskcount-=8; + unshift(@zone,(shift @orig)); + } + $rev = join('.',@zone); + $rev .= '.IN_ADDR.ARPA'; + return $rev; + } + } + } +} + +sub process_request { + my $request = shift; + my $callback = shift; + my $ctx = {}; + my @nodes; + my $hadargs=0; + my $allnodes; + my $zapfiles; + if ($request->{arg}) { + $hadargs=1; + @ARGV=@{$request->{arg}}; + if (!GetOptions( + 'a|all' => \$allnodes, + 'n|new' => \$zapfiles, + )) { + sendmsg([1,"TODO: makedns Usage message"]); + return; + } + } + + if ($request->{node}) { #we have a noderange to process + @nodes = @{$request->{node}}; + } elsif ($allnodes) { + #read all nodelist specified nodes + } else { #legacy behavior, read from /etc/hosts + sendmsg([1,"TODO: reading from /etc/hosts like old makedns"]); + } + my $hoststab = xCAT::Table->new('hosts',-create=>0); + if ($hoststab) { + $ctx->{hoststab} = $hoststab->getNodesAttribs(\@nodes,['ip']); + } + my $networkstab = xCAT::Table->new('networks',-create=>0); + unless ($networkstab) { sendmsg([1,'Unable to enumerate networks, try to run makenetworks']); } + my @networks = $networkstab->getAllAttribs('net','mask'); + foreach (@networks) { + my $maskn = unpack("N",inet_aton($_->{mask})); + $ctx->{nets}->{$_->{net}}->{mask} = $maskn; + $ctx->{nets}->{$_->{net}}->{netn} = unpack("N",inet_aton($_->{net})); + } + my $passtab = xCAT::Table->new('passwd'); + my $pent = $passtab->getAttribs({key=>'omapi',username=>'xcat_key'},['password']); + if ($pent and $pent->{password}) { + $ctx->{privkey} = $pent->{password}; + } #do not warn/error here yet, if we can't generate or extract, we'll know later + my $sitetab = xCAT::Table->new('site'); + my $stab = $sitetab->getAttribs({key=>'domain'},['value']); + unless ($stab and $stab->{value}) { + sendmsg([1,"domain not defined in site table"]); + return; + } + $ctx->{domain} = $stab->{value}; + $stab = $sitetab->getAttribs({key=>'forwarders'},['value']); + if ($stab and $stab->{value}) { + my @forwarders = split /[ ,]/,$stab->{value}; + $ctx->{forwarders}=\@forwarders; + } + $ctx->{domainstotouch}->{$ctx->{domain}}=1; + foreach (@nodes) { + $ctx->{revzones}->{$_} = get_reverse_zone_for_entity($ctx,$_); + $ctx->{domainstotouch}->{$ctx->{revzones}->{$_}}=1; + } + use Data::Dumper; + if (1) { #TODO: function to detect and return 1 if the master server is DNS SOA for all the zones we care about + #here, we are examining local files to assure that our key is in named.conf, the zones we care about are there, and that if + #active directory is in use, allow the domain controllers to update specific zones + $stab =$sitetab->getAttribs({key=>'directoryprovider'},['value']); + if ($stab and $stab->{value} and $stab->{value} eq 'activedirectory') { + $stab =$sitetab->getAttribs({key=>'directoryservers'},['value']); + if ($stab and $stab->{value} and $stab->{value}) { + my @dservers = split /[ ,]/,$stab->{value}; + $ctx->{adservers} = \@dservers; + } + } + $stab =$sitetab->getAttribs({key=>'dnsupdaters'},['value']); #allow unsecure updates from these + if ($stab and $stab->{value} and $stab->{value}) { + my @nservers = split /[ ,]/,$stab->{value}; + $ctx->{dnsupdaters} = \@nservers; + } + if ($zapfiles) { #here, we unlink all the existing files to start fresh + } + #We manipulate local namedconf + update_namedconf($ctx); + } else { + unless ($ctx->{privkey}) { + sendmsg([1,"Unable to update DNS due to lack of credentials in passwd to communicate with remote server"]); + } + } + #now we stick to Net::DNS style updates, with TSIG if possible. TODO: kerberized (i.e. Windows) DNS server support, maybe needing to use nsupdate -g.... +} + +sub update_namedconf { + my $ctx = shift; + my $namedlocation = '/etc/named.conf'; + my $nameconf; + my @newnamed; + my $gotoptions=0; + my $gotkey=0; + my %didzones; + if (-r $namedlocation) { + my @currnamed=(); + open($nameconf,"<",$namedlocation); + @currnamed=<$nameconf>; + close($nameconf); + my $i = 0; + for ($i=0;$i{forwarders} and $line =~ /forwarders {/) { + push @newnamed,"\tforwarders \{"; + $skip=1; + foreach (@{$ctx->{forwarders}}) { + push @newnamed,"\t\t".$_."\n"; + } + } elsif ($skip) { + if ($line =~ / };/) { + push @newnamed,"\t};"; + $skip = 0; + } + } else { + push @newnamed,$line; + } + $i++; + $line = $currnamed[$i]; + } while ($line !~ /^\};/); + } elsif ($line =~ /^zone "([^"]*)" in \{/) { + my $currzone = $1; + if ($ctx->{zonestotouch}->{$currzone} or $ctx->{adzones}->{$currzone}) { + $didzones{$currzone}=1; + push @newnamed,"zone \"$currzone\" in {\n","\ttype master;\n","\tallow-update {\n","\t\tkey xcat_key;\n"; + my @list; + if ($ctx->{adzones}->{$currzone}) { + @list = @{$ctx->{dnsupdaters}}; + } else { + @list = @{$ctx->{adservers}}; + } + foreach (@list) { + push @newnamed,"\t\t$_\n"; + } + push @newnamed,"\t};\n","\tfile \"db.$currzone\";\n","};\n"; + while ($line !~ /^\};/) { #skip the old file zone + $i++; + $line = $currnamed[$i]; + } + $i++; + } else { + push @newnamed,$line; + } + } elsif ($line =~ /^key xcat_key/) { + $gotkey=1; + if ($ctx->{privkey}) { + push @newnamed,"key xcat_key {\n","\talgorithm hmac-md5;\n","\tsecret \"".$ctx->{privkey}."\";\n","};\n"; + } else { + while ($line !~ /^\};/) { #skip the old file zone + if ($line =~ /secret \"([^"]*)\"/) { + my $passtab = xCAT::Table->new("passwd",-create=>1); + $passtab->setAttribs({key=>"omapi",user=>"xcat_key"},{password=>$1}); + } + $i++; + $line = $currnamed[$i]; + } + } + } + } + } + unless ($gotoptions) { + push @newnamed,"options {\n","\tdirectory \"/var/named/\";\n"; + if ($ctx->{forwarders}) { + push @newnamed,"\tforwarders {\n"; + foreach (@{$ctx->{forwarders}}) { + push @newnamed,"\t\t$_;\n"; + } + push @newnamed,"\t};\n"; + } + push @newnamed,"};\n"; + open($nameconf,"<",$namedlocation); + } + unless ($gotkey) { + unless ($ctx->{privkey}) { #need to generate one + $ctx->{privkey} = encode_base64(genpassword(32)); + } + push @newnamed,"key xcat_key {\n","\talgorithm hmac-md5;\n","\tsecret \"".$ctx->{privkey}."\";\n","};\n"; + } + my $zone; + foreach $zone (keys %{$ctx->{zonestotouch}}) { + if ($didzones{$zone}) { next; } + push @newnamed,"zone \"$zone\" in {\n","\ttype master;\n","allow-update {\n","\t\tkey xcat_key;\n"; + foreach (@{$ctx->{dnsupdaters}}) { + push @newnamed,"\t\t$_\n"; + } + push @newnamed,"\t};","\tfile \"db.$zone\";\n","};\n"; + } + foreach $zone (keys %{$ctx->{adzones}}) { + if ($didzones{$zone}) { next; } + push @newnamed,"zone \"$zone\" in {\n","\ttype master;\n","allow-update {\n","\t\tkey xcat_key;\n"; + foreach (@{$ctx->{adservers}}) { + push @newnamed,"\t\t$_\n"; + } + push @newnamed,"\t};","\tfile \"db.$zone\";\n","};\n"; + } + use Data::Dumper; + print Dumper(@newnamed); +} +my $resolver = Net::DNS::Resolver->new; + +my $key_name = 'xcat_key'; +my $key = 'UlpPekE5Zmg0VzBsbTA2alZSRkxWMGRWTDZRckhJaHM='; +my $update = Net::DNS::Update->new('xcat.e1350'); +$update->push(update => rr_add('foo.xcat.e1350 A 172.16.0.22')); +$update->sign_tsig($key_name,$key); +my $reply = $resolver->send($update); + +use Data::Dumper; +process_request({node=>['bmc1','n1','switch1']});