2010-03-12 22:06:59 +00:00
package xCAT_plugin::dns ;
2010-03-17 14:03:21 +00:00
use strict ;
2010-03-12 22:06:59 +00:00
use Getopt::Long ;
use Net::DNS ;
use xCAT::Table ;
2010-03-15 21:11:51 +00:00
use Sys::Hostname ;
2010-03-22 13:18:34 +00:00
use MIME::Base64 ;
2010-03-12 22:06:59 +00:00
use Socket ;
2010-03-17 14:03:21 +00:00
use Fcntl qw/:flock/ ;
2010-03-12 22:06:59 +00:00
#This is a rewrite of DNS management using nsupdate rather than direct zone mangling
my $ callback ;
2010-03-16 20:40:19 +00:00
sub handled_commands
{
return { "makedns" = > "dns" } ;
}
2010-03-12 22:06:59 +00:00
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 ) ;
2010-03-15 21:11:51 +00:00
$ rev . = '.IN-ADDR.ARPA' ;
2010-03-12 22:06:59 +00:00
return $ rev ;
}
}
}
2010-03-17 14:03:21 +00:00
return undef ;
2010-03-12 22:06:59 +00:00
}
sub process_request {
my $ request = shift ;
2010-03-16 21:38:40 +00:00
$ callback = shift ;
2010-03-17 14:03:21 +00:00
umask 0007 ;
2010-03-12 22:06:59 +00:00
my $ ctx = { } ;
2010-03-16 21:38:40 +00:00
my @ nodes = ( ) ;
2010-03-12 22:06:59 +00:00
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 ;
}
}
2010-03-16 21:38:40 +00:00
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 } ;
2010-03-12 22:06:59 +00:00
if ( $ request - > { node } ) { #we have a noderange to process
@ nodes = @ { $ request - > { node } } ;
} elsif ( $ allnodes ) {
#read all nodelist specified nodes
2010-03-15 21:11:51 +00:00
} else {
#legacy behavior, read from /etc/hosts
my $ hostsfile ;
open ( $ hostsfile , "<" , "/etc/hosts" ) ;
2010-03-17 14:03:21 +00:00
flock ( $ hostsfile , LOCK_SH ) ;
2010-03-15 21:11:51 +00:00
my @ contents = <$hostsfile> ;
2010-03-17 14:03:21 +00:00
flock ( $ hostsfile , LOCK_UN ) ;
2010-03-15 21:11:51 +00:00
close ( $ hostsfile ) ;
2010-03-16 21:38:40 +00:00
my $ domain = $ ctx - > { domain } ;
unless ( $ domain =~ /^\./ ) { $ domain = '.' . $ domain ; }
2010-03-15 21:11:51 +00:00
my $ addr ;
my $ name ;
my $ canonical ;
my $ aliasstr ;
my @ aliases ;
my $ names ;
foreach ( @ contents ) {
2010-03-16 21:38:40 +00:00
chomp ; #no newline
2010-03-15 21:11:51 +00:00
s/#.*// ; #strip comments;
2010-03-16 21:38:40 +00:00
s/^[ \t\n]*// ; #remove leading whitespace
2010-03-15 21:11:51 +00:00
next unless ( $ _ ) ; #skip empty lines
( $ addr , $ names ) = split /[ \t]+/ , $ _ , 2 ;
if ( $ addr !~ /^\d+\.\d+\.\d+\.\d+$/ ) {
2010-03-17 14:03:21 +00:00
sendmsg ( ":Ignoring line $_ in /etc/hosts, only IPv4 format entries are supported currently" ) ;
2010-03-15 21:11:51 +00:00
next ;
}
2010-03-16 21:38:40 +00:00
unless ( $ names =~ /^[a-z0-9\. \t\n-]+$/i ) {
2010-03-17 14:03:21 +00:00
sendmsg ( ":Ignoring line $_ in /etc/hosts, names $names contain invalid characters (valid characters include a through z, numbers and the '-', but not '_'" ) ;
2010-03-15 21:11:51 +00:00
next ;
}
( $ canonical , $ aliasstr ) = split /[ \t]+/ , $ names , 2 ;
if ( $ aliasstr ) {
@ aliases = split /[ \t]+/ , $ aliasstr ;
} else {
@ aliases = ( ) ;
}
2010-03-16 21:38:40 +00:00
my % names = ( ) ;
my $ node = $ canonical ;
unless ( $ canonical =~ /$domain/ ) {
$ canonical . = $ domain ;
}
unless ( $ canonical =~ /\.\z/ ) { $ canonical . = '.' } #for only the sake of comparison, ensure consistant dot suffix
foreach my $ alias ( @ aliases ) {
unless ( $ alias =~ /$domain/ ) {
$ alias . = $ domain ;
}
unless ( $ alias =~ /\.\z/ ) {
$ alias . = '.' ;
}
if ( $ alias eq $ canonical ) {
next ;
}
$ ctx - > { aliases } - > { $ node } - > { $ alias } = 1 ; #remember alias for CNAM records later
}
push @ nodes , $ node ;
2010-03-19 13:23:17 +00:00
$ ctx - > { nodeips } - > { $ node } - > { $ addr } = 1 ;
2010-03-15 21:11:51 +00:00
}
2010-03-12 22:06:59 +00:00
}
my $ hoststab = xCAT::Table - > new ( 'hosts' , - create = > 0 ) ;
if ( $ hoststab ) {
$ ctx - > { hoststab } = $ hoststab - > getNodesAttribs ( \ @ nodes , [ 'ip' ] ) ;
}
2010-03-15 21:11:51 +00:00
$ ctx - > { nodes } = \ @ nodes ;
2010-03-12 22:06:59 +00:00
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
$ stab = $ sitetab - > getAttribs ( { key = > 'forwarders' } , [ 'value' ] ) ;
if ( $ stab and $ stab - > { value } ) {
my @ forwarders = split /[ ,]/ , $ stab - > { value } ;
$ ctx - > { forwarders } = \ @ forwarders ;
}
2010-03-15 21:11:51 +00:00
$ ctx - > { zonestotouch } - > { $ ctx - > { domain } } = 1 ;
2010-03-12 22:06:59 +00:00
foreach ( @ nodes ) {
2010-03-16 21:38:40 +00:00
my $ revzone = get_reverse_zone_for_entity ( $ ctx , $ _ ) ; ;
unless ( $ revzone ) { next ; }
$ ctx - > { revzones } - > { $ _ } = $ revzone ;
$ ctx - > { zonestotouch } - > { $ ctx - > { revzones } - > { $ _ } } = 1 ;
2010-03-12 22:06:59 +00:00
}
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 ;
2010-03-15 21:11:51 +00:00
$ ctx - > { adzones } = {
"_msdcs." . $ ctx - > { domain } = > 1 ,
"_sites." . $ ctx - > { domain } = > 1 ,
"_tcp." . $ ctx - > { domain } = > 1 ,
"_udp." . $ ctx - > { domain } = > 1 ,
} ;
2010-03-12 22:06:59 +00:00
}
}
$ 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
2010-04-05 20:12:16 +00:00
system ( "/sbin/service named stop" ) ; #named may otherwise hold on to stale journal filehandles
2010-03-17 14:03:21 +00:00
unlink "/etc/named.conf" ;
foreach ( </var/named/db.*> ) {
unlink $ _ ;
}
foreach ( </var/lib/named/db.*> ) {
unlink $ _ ;
}
2010-03-12 22:06:59 +00:00
}
#We manipulate local namedconf
2010-03-15 21:11:51 +00:00
$ ctx - > { dbdir } = get_dbdir ( ) ;
2010-04-01 19:19:04 +00:00
chmod 0775 , $ ctx - > { dbdir } ; # assure dynamic dns can actually execute against the directory
2010-03-16 20:36:25 +00:00
update_namedconf ( $ ctx ) ;
2010-03-15 21:11:51 +00:00
update_zones ( $ ctx ) ;
if ( $ ctx - > { restartneeded } ) {
2010-03-17 14:03:21 +00:00
sendmsg ( "Restarting named" ) ;
2010-03-15 21:11:51 +00:00
system ( "/sbin/service named start" ) ;
system ( "/sbin/service named reload" ) ;
2010-03-17 14:03:21 +00:00
sendmsg ( "Restarting named complete" ) ;
2010-03-15 21:11:51 +00:00
}
2010-03-12 22:06:59 +00:00
} 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....
2010-03-16 20:36:25 +00:00
$ ctx - > { resolver } = Net::DNS::Resolver - > new ( ) ;
add_records ( $ ctx ) ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
sub get_dbdir {
if ( - d "/var/named" ) {
return "/var/named/" ;
} elsif ( - d "/var/lib/named" ) {
return "/var/lib/named/" ;
} else {
use File::Path ;
mkpath "/var/named/" ;
2010-03-17 14:03:21 +00:00
chown ( scalar ( getpwnam ( 'named' ) ) , scalar ( getgrnam ( 'named' ) ) , "/var/named" ) ;
2010-03-15 21:11:51 +00:00
return "/var/named/" ;
}
}
sub update_zones {
my $ ctx = shift ;
my $ currzone ;
my $ dbdir = $ ctx - > { dbdir } ;
my $ domain = $ ctx - > { domain } ;
my $ name = hostname ;
2010-03-16 20:36:25 +00:00
my $ node = $ name ;
unless ( $ domain =~ /^\./ ) {
$ domain = '.' . $ domain ;
}
2010-03-15 21:11:51 +00:00
unless ( $ name =~ /\./ ) {
$ name . = $ domain ;
}
unless ( $ name =~ /\.\z/ ) {
$ name . = '.' ;
}
2010-03-16 20:36:25 +00:00
my $ ip = $ node ;
if ( $ ctx - > { hoststab } and $ ctx - > { hoststab } - > { $ node } and $ ctx - > { hoststab } - > { $ node } - > [ 0 ] - > { ip } ) {
$ ip = $ ctx - > { hoststab } - > { $ node } - > [ 0 ] - > { ip } ;
} else {
unless ( $ ip = inet_aton ( $ ip ) ) {
print "Unable to find an IP for $node in hosts table or via system lookup (i.e. /etc/hosts" ;
sendmsg ( [ 1 , "Unable to find an IP for $node in hosts table or via system lookup (i.e. /etc/hosts" ] ) ;
next ;
}
$ ip = inet_ntoa ( $ ip ) ;
}
2010-03-15 21:11:51 +00:00
my @ neededzones = keys % { $ ctx - > { zonestotouch } } ;
push @ neededzones , keys % { $ ctx - > { adzones } } ;
my ( $ sec , $ min , $ hour , $ mday , $ mon , $ year , $ rest ) = localtime ( time ) ;
my $ serial = ( $ mday * 100 ) + ( ( $ mon + 1 ) * 10000 ) + ( ( $ year + 1900 ) * 1000000 ) ;
foreach $ currzone ( @ neededzones ) {
2010-04-01 19:19:04 +00:00
my $ zonefilename = $ currzone ;
2010-03-15 21:11:51 +00:00
if ( $ currzone =~ /IN-ADDR\.ARPA/ ) {
$ currzone =~ s/\.IN-ADDR\.ARPA.*// ;
my @ octets = split /\./ , $ currzone ;
$ currzone = join ( '.' , reverse ( @ octets ) ) ;
2010-04-02 18:09:58 +00:00
$ zonefilename = $ currzone ;
} else {
$ zonefilename =~ s/\..*// ; #compatible with bind.pm
2010-03-15 21:11:51 +00:00
}
2010-04-01 19:19:04 +00:00
unless ( - f $ dbdir . "/db.$zonefilename" ) {
2010-03-15 21:11:51 +00:00
my $ zonehdl ;
2010-04-01 19:19:04 +00:00
open ( $ zonehdl , ">>" , $ dbdir . "/db.$zonefilename" ) ;
2010-03-17 14:03:21 +00:00
flock ( $ zonehdl , LOCK_EX ) ;
seek ( $ zonehdl , 0 , 0 ) ;
truncate ( $ zonehdl , 0 ) ;
2010-03-15 21:11:51 +00:00
print $ zonehdl '$TTL 86400' . "\n" ;
print $ zonehdl '@ IN SOA ' . $ name . " root.$name ( $serial 10800 3600 604800 86400 )\n" ;
print $ zonehdl " IN NS $name\n" ;
2010-03-16 20:36:25 +00:00
if ( $ name =~ /$currzone/ ) { #Must guarantee an A record for the DNS server
print $ zonehdl "$name IN A $ip\n" ;
}
2010-03-17 14:03:21 +00:00
flock ( $ zonehdl , LOCK_UN ) ;
2010-03-15 21:11:51 +00:00
close ( $ zonehdl ) ;
2010-04-01 19:19:04 +00:00
chown ( scalar ( getpwnam ( 'named' ) ) , scalar ( getgrnam ( 'named' ) ) , $ dbdir . "/db.$zonefilename" ) ;
2010-03-15 21:11:51 +00:00
$ ctx - > { restartneeded } = 1 ;
}
}
}
2010-03-12 22:06:59 +00:00
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 ) ;
2010-03-17 14:03:21 +00:00
flock ( $ nameconf , LOCK_SH ) ;
2010-03-12 22:06:59 +00:00
@ currnamed = <$nameconf> ;
2010-03-17 14:03:21 +00:00
flock ( $ nameconf , LOCK_UN ) ;
2010-03-12 22:06:59 +00:00
close ( $ nameconf ) ;
my $ i = 0 ;
for ( $ i = 0 ; $ i < scalar ( @ currnamed ) ; $ i + + ) {
my $ line = $ currnamed [ $ i ] ;
if ( $ line =~ /^options +\{/ ) {
$ gotoptions = 1 ;
my $ skip = 0 ;
do {
if ( $ ctx - > { forwarders } and $ line =~ /forwarders {/ ) {
2010-03-15 21:11:51 +00:00
push @ newnamed , "\tforwarders \{\n" ;
2010-03-12 22:06:59 +00:00
$ skip = 1 ;
foreach ( @ { $ ctx - > { forwarders } } ) {
2010-03-15 21:11:51 +00:00
push @ newnamed , "\t\t" . $ _ . ";\n" ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
push @ newnamed , "\t};\n" ;
2010-03-12 22:06:59 +00:00
} elsif ( $ skip ) {
2010-03-15 21:11:51 +00:00
if ( $ line =~ /};/ ) {
2010-03-12 22:06:59 +00:00
$ skip = 0 ;
}
} else {
push @ newnamed , $ line ;
}
$ i + + ;
$ line = $ currnamed [ $ i ] ;
} while ( $ line !~ /^\};/ ) ;
2010-03-15 21:11:51 +00:00
push @ newnamed , $ line ;
2010-03-12 22:06:59 +00:00
} elsif ( $ line =~ /^zone "([^"]*)" in \{/ ) {
my $ currzone = $ 1 ;
if ( $ ctx - > { zonestotouch } - > { $ currzone } or $ ctx - > { adzones } - > { $ currzone } ) {
$ didzones { $ currzone } = 1 ;
2010-03-15 21:11:51 +00:00
my @ candidate = ( $ line ) ;
my $ needreplace = 1 ;
do {
$ i + + ;
$ line = $ currnamed [ $ i ] ;
push @ candidate , $ line ;
if ( $ line =~ /key xcat_key/ ) {
$ needreplace = 0 ;
}
} while ( $ line !~ /^\};/ ) ; #skip the old file zone
unless ( $ needreplace ) {
push @ newnamed , @ candidate ;
next ;
}
$ ctx - > { restartneeded } = 1 ;
2010-03-12 22:06:59 +00:00
push @ newnamed , "zone \"$currzone\" in {\n" , "\ttype master;\n" , "\tallow-update {\n" , "\t\tkey xcat_key;\n" ;
my @ list ;
2010-03-15 21:11:51 +00:00
if ( not $ ctx - > { adzones } - > { $ currzone } ) {
if ( $ ctx - > { dnsupdaters } ) {
@ list = @ { $ ctx - > { dnsupdaters } } ;
}
2010-03-12 22:06:59 +00:00
} else {
2010-03-15 21:11:51 +00:00
if ( $ ctx - > { adservers } ) {
@ list = @ { $ ctx - > { adservers } } ;
}
2010-03-12 22:06:59 +00:00
}
foreach ( @ list ) {
2010-03-15 21:11:51 +00:00
push @ newnamed , "\t\t$_;\n" ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
if ( $ currzone =~ /IN-ADDR\.ARPA/ ) {
my $ net = $ currzone ;
$ net =~ s/.IN-ADDR\.ARPA.*// ;
my @ octets = split /\./ , $ net ;
$ net = join ( '.' , reverse ( @ octets ) ) ;
push @ newnamed , "\t};\n" , "\tfile \"db.$net\";\n" , "};\n" ;
} else {
2010-04-01 19:19:04 +00:00
my $ zfilename = $ currzone ;
$ zfilename =~ s/\..*// ;
push @ newnamed , "\t};\n" , "\tfile \"db.$zfilename\";\n" , "};\n" ;
2010-03-12 22:06:59 +00:00
}
} else {
push @ newnamed , $ line ;
2010-03-15 21:11:51 +00:00
do {
$ i + + ;
$ line = $ currnamed [ $ i ] ;
push @ newnamed , $ line ;
} while ( $ line !~ /^\};/ ) ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
2010-03-12 22:06:59 +00:00
} elsif ( $ line =~ /^key xcat_key/ ) {
$ gotkey = 1 ;
if ( $ ctx - > { privkey } ) {
2010-03-15 21:11:51 +00:00
#for now, assume the field is correct
#push @newnamed,"key xcat_key {\n","\talgorithm hmac-md5;\n","\tsecret \"".$ctx->{privkey}."\";\n","};\n\n";
push @ newnamed , $ line ;
do {
$ i + + ;
$ line = $ currnamed [ $ i ] ;
push @ newnamed , $ line ;
} while ( $ line !~ /^\};/ ) ;
2010-03-12 22:06:59 +00:00
} else {
2010-03-15 21:11:51 +00:00
push @ newnamed , $ line ;
2010-03-12 22:06:59 +00:00
while ( $ line !~ /^\};/ ) { #skip the old file zone
if ( $ line =~ /secret \"([^"]*)\"/ ) {
my $ passtab = xCAT::Table - > new ( "passwd" , - create = > 1 ) ;
2010-04-01 14:09:42 +00:00
$ passtab - > setAttribs ( { key = > "omapi" , username = > "xcat_key" } , { password = > $ 1 } ) ;
2010-03-12 22:06:59 +00:00
}
$ i + + ;
$ line = $ currnamed [ $ i ] ;
2010-03-15 21:11:51 +00:00
push @ newnamed , $ line ;
2010-03-12 22:06:59 +00:00
}
}
2010-03-15 21:11:51 +00:00
} else {
push @ newnamed , $ line ;
2010-03-12 22:06:59 +00:00
}
}
}
unless ( $ gotoptions ) {
2010-03-15 21:11:51 +00:00
push @ newnamed , "options {\n" , "\tdirectory \"" . $ ctx - > { dbdir } . "\";\n" ;
2010-03-12 22:06:59 +00:00
if ( $ ctx - > { forwarders } ) {
push @ newnamed , "\tforwarders {\n" ;
foreach ( @ { $ ctx - > { forwarders } } ) {
push @ newnamed , "\t\t$_;\n" ;
}
push @ newnamed , "\t};\n" ;
}
2010-03-15 21:11:51 +00:00
push @ newnamed , "};\n\n" ;
2010-03-12 22:06:59 +00:00
}
unless ( $ gotkey ) {
unless ( $ ctx - > { privkey } ) { #need to generate one
$ ctx - > { privkey } = encode_base64 ( genpassword ( 32 ) ) ;
2010-03-22 13:18:34 +00:00
chomp ( $ ctx - > { privkey } ) ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
push @ newnamed , "key xcat_key {\n" , "\talgorithm hmac-md5;\n" , "\tsecret \"" . $ ctx - > { privkey } . "\";\n" , "};\n\n" ;
$ ctx - > { restartneeded } = 1 ;
2010-03-12 22:06:59 +00:00
}
my $ zone ;
foreach $ zone ( keys % { $ ctx - > { zonestotouch } } ) {
if ( $ didzones { $ zone } ) { next ; }
2010-03-15 21:11:51 +00:00
$ ctx - > { restartneeded } = 1 ; #have to add a zone, a restart will be needed
push @ newnamed , "zone \"$zone\" in {\n" , "\ttype master;\n" , "\tallow-update {\n" , "\t\tkey xcat_key;\n" ;
2010-03-12 22:06:59 +00:00
foreach ( @ { $ ctx - > { dnsupdaters } } ) {
2010-03-15 21:11:51 +00:00
push @ newnamed , "\t\t$_;\n" ;
}
if ( $ zone =~ /IN-ADDR\.ARPA/ ) {
my $ net = $ zone ;
$ net =~ s/.IN-ADDR\.ARPA.*// ;
my @ octets = split /\./ , $ net ;
$ net = join ( '.' , reverse ( @ octets ) ) ;
push @ newnamed , "\t};\n" , "\tfile \"db.$net\";\n" , "};\n" ;
} else {
2010-04-01 19:19:04 +00:00
my $ zfilename = $ zone ;
$ zfilename =~ s/\..*// ;
push @ newnamed , "\t};\n" , "\tfile \"db.$zfilename\";\n" , "};\n" ;
2010-03-12 22:06:59 +00:00
}
}
foreach $ zone ( keys % { $ ctx - > { adzones } } ) {
if ( $ didzones { $ zone } ) { next ; }
2010-03-15 21:11:51 +00:00
$ ctx - > { restartneeded } = 1 ; #have to add a zone, a restart will be needed
push @ newnamed , "zone \"$zone\" in {\n" , "\ttype master;\n" , "\tallow-update {\n" , "\t\tkey xcat_key;\n" ;
2010-03-12 22:06:59 +00:00
foreach ( @ { $ ctx - > { adservers } } ) {
2010-03-15 21:11:51 +00:00
push @ newnamed , "\t\t$_;\n" ;
2010-03-12 22:06:59 +00:00
}
2010-04-01 19:19:04 +00:00
my $ zfilename = $ zone ;
$ zfilename =~ s/\..*// ;
push @ newnamed , "\t};\n" , "\tfile \"db.$zfilename\";\n" , "};\n\n" ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
my $ newnameconf ;
2010-03-17 14:03:21 +00:00
open ( $ newnameconf , ">>" , $ namedlocation ) ;
flock ( $ newnameconf , LOCK_EX ) ;
seek ( $ newnameconf , 0 , 0 ) ;
truncate ( $ newnameconf , 0 ) ;
2010-03-15 21:11:51 +00:00
for my $ l ( @ newnamed ) { print $ newnameconf $ l ; }
2010-03-17 14:03:21 +00:00
flock ( $ newnameconf , LOCK_UN ) ;
2010-03-15 21:11:51 +00:00
close ( $ newnameconf ) ;
2010-03-17 14:03:21 +00:00
chown ( scalar ( getpwnam ( 'root' ) ) , scalar ( getgrnam ( 'named' ) ) , $ namedlocation ) ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
sub add_records {
my $ ctx = shift ;
unless ( $ ctx - > { privkey } ) {
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 } ;
} else {
sendmsg ( [ 1 , "Unable to find omapi key in passwd table" ] ) ;
}
}
my $ node ;
my $ ip ;
2010-03-16 20:36:25 +00:00
my $ domain = $ ctx - > { domain } ; # store off for lazy typing and possible local mangling
unless ( $ domain =~ /^\./ ) { $ domain = '.' . $ domain ; } #example.com becomes .example.com for consistency
$ ctx - > { nsmap } = { } ; #will store a map to known NS records to avoid needless redundant queries to sort nodes into domains
$ ctx - > { updatesbyzone } = { } ; #sort all updates into their respective zones for bulk update for fewer DNS transactions
2010-03-15 21:11:51 +00:00
foreach $ node ( @ { $ ctx - > { nodes } } ) {
$ ip = $ node ;
2010-03-16 20:36:25 +00:00
my $ name = $ node ;
unless ( $ name =~ /$domain/ ) { $ name . = $ domain } # $name needs to represent fqdn, but must preserve $node as a nodename for cfg lookup
#if (domaintab->{$node}->[0]->{domain) { $domain = domaintab->{$node}->[0]->{domain) }
#above is TODO draft of how multi-domain support could come into play
2010-03-15 21:11:51 +00:00
if ( $ ctx - > { hoststab } and $ ctx - > { hoststab } - > { $ node } and $ ctx - > { hoststab } - > { $ node } - > [ 0 ] - > { ip } ) {
$ ip = $ ctx - > { hoststab } - > { $ node } - > [ 0 ] - > { ip } ;
} else {
unless ( $ ip = inet_aton ( $ ip ) ) {
sendmsg ( [ 1 , "Unable to find an IP for $node in hosts table or via system lookup (i.e. /etc/hosts" ] ) ;
next ;
}
$ ip = inet_ntoa ( $ ip ) ;
}
2010-03-16 20:36:25 +00:00
$ ctx - > { currip } = $ ip ;
2010-03-15 21:11:51 +00:00
#time to update, A and PTR records, IPv6 still TODO
2010-03-16 20:36:25 +00:00
$ ip = join ( '.' , reverse ( split ( /\./ , $ ip ) ) ) ;
2010-03-15 21:11:51 +00:00
$ ip . = '.IN-ADDR.ARPA.' ;
2010-03-16 20:36:25 +00:00
#ok, now it is time to identify which zones should actually hold the forward (A) and reverse (PTR) records and a nameserver to handle the request
my $ revzone = $ ip ;
$ ctx - > { currnode } = $ node ;
$ ctx - > { currname } = $ name ;
$ ctx - > { currrevname } = $ ip ;
find_nameserver_for_dns ( $ ctx , $ revzone ) ;
find_nameserver_for_dns ( $ ctx , $ domain ) ;
2010-03-15 21:11:51 +00:00
}
2010-03-16 20:36:25 +00:00
my $ zone ;
foreach $ zone ( keys % { $ ctx - > { updatesbyzone } } ) {
my $ resolver = Net::DNS::Resolver - > new ( nameservers = > [ $ ctx - > { nsmap } - > { $ zone } ] ) ;
2010-03-17 14:03:21 +00:00
my $ entry ;
2010-04-05 19:39:51 +00:00
my $ numreqs = 300 ; # limit to 300 updates in a payload, something broke at 644 on a certain sample, choosing 300 for now
2010-03-16 20:36:25 +00:00
my $ update = Net::DNS::Update - > new ( $ zone ) ;
2010-03-17 14:03:21 +00:00
foreach $ entry ( @ { $ ctx - > { updatesbyzone } - > { $ zone } } ) {
$ update - > push ( update = > rr_add ( $ entry ) ) ;
2010-04-05 19:39:51 +00:00
$ numreqs -= 1 ;
if ( $ numreqs == 0 ) {
$ update - > sign_tsig ( "xcat_key" , $ ctx - > { privkey } ) ;
$ numreqs = 300 ;
my $ reply = $ resolver - > send ( $ update ) ;
if ( $ reply - > header - > rcode ne 'NOERROR' ) {
sendmsg ( [ 1 , "Failure encountered updating $zone, error was " . $ reply - > header - > rcode ] ) ;
}
$ update = Net::DNS::Update - > new ( $ zone ) ; #new empty request
}
}
2010-04-05 20:12:16 +00:00
if ( $ numreqs != 300 ) { #either no entries at all to begin with or a perfect multiple of 300
2010-04-05 19:39:51 +00:00
$ update - > sign_tsig ( "xcat_key" , $ ctx - > { privkey } ) ;
my $ reply = $ resolver - > send ( $ update ) ;
2010-03-16 20:36:25 +00:00
}
}
}
sub find_nameserver_for_dns {
my $ ctx = shift ;
my $ zone = shift ;
my $ node = $ ctx - > { currnode } ;
my $ ip = $ ctx - > { currip } ;
my $ rname = $ ctx - > { currrevname } ;
my $ name = $ ctx - > { currname } ;
unless ( $ name =~ /\.\z/ ) { $ name . = '.' }
2010-03-19 13:23:17 +00:00
my @ rrcontent = ( "$name IN A $ip" ) ;
foreach ( keys % { $ ctx - > { nodeips } - > { $ node } } ) {
unless ( $ _ eq $ ip ) {
push @ rrcontent , "$name IN A $_" ;
}
}
2010-03-16 20:36:25 +00:00
if ( $ zone =~ /IN-ADDR.ARPA/ ) { #reverse style
2010-03-19 13:23:17 +00:00
@ rrcontent = ( "$rname IN PTR $name" ) ;
2010-03-16 20:36:25 +00:00
}
while ( $ zone ) {
unless ( defined $ ctx - > { nsmap } - > { $ zone } ) { #ok, we already thought about this zone and made a decision
2010-03-17 14:03:21 +00:00
if ( $ zone =~ /^\.*192.IN-ADDR.ARPA\.*/ or $ zone =~ /^\.*172.IN-ADDR.ARPA\.*/ or $ zone =~ /127.IN-ADDR.ARPA\.*/ or $ zone =~ /^\.*IN-ADDR.ARPA\.*/ or $ zone =~ /^\.*ARPA\.*/ ) {
$ ctx - > { nsmap } - > { $ zone } = 0 ; #ignore zones that are likely to appear, but probably not ours
} else {
my $ reply = $ ctx - > { resolver } - > query ( $ zone , 'NS' ) ;
if ( $ reply ) {
foreach my $ record ( $ reply - > answer ) {
if ( $ record - > nsdname =~ /blackhole.*\.iana\.org/ ) {
$ ctx - > { nsmap } - > { $ zone } = 0 ;
} else {
$ ctx - > { nsmap } - > { $ zone } = $ record - > nsdname ;
}
}
} else {
$ ctx - > { nsmap } - > { $ zone } = 0 ;
}
2010-03-16 20:36:25 +00:00
}
}
if ( $ ctx - > { nsmap } - > { $ zone } ) { #we have a nameserver for this zone, therefore this zone is one to update
2010-03-19 13:23:17 +00:00
push @ { $ ctx - > { updatesbyzone } - > { $ zone } } , @ rrcontent ;
2010-03-16 20:36:25 +00:00
last ;
} else { #we have it defined, but zero, means search higher domains. Possible to shortcut further by pointing to the right domain, maybe later
2010-03-17 14:03:21 +00:00
if ( $ zone !~ /\./ ) {
sendmsg ( [ 1 , "Unable to find reverse zone to hold $node" ] , $ node ) ;
last ;
}
2010-03-16 20:36:25 +00:00
$ zone =~ s/^[^\.]*\.// ; #strip all up to and including first dot
unless ( $ zone ) {
2010-03-17 14:03:21 +00:00
sendmsg ( [ 1 , "Unable to find zone to hold $node" ] , $ node ) ;
2010-03-16 20:36:25 +00:00
last ;
}
}
}
}
sub sendmsg {
2010-03-16 20:40:19 +00:00
# my $callback = $output_handler;
my $ text = shift ;
my $ node = shift ;
my $ descr ;
my $ rc ;
if ( ref $ text eq 'HASH' ) {
die "not right now" ;
} elsif ( ref $ text eq 'ARRAY' ) {
$ rc = $ text - > [ 0 ] ;
$ text = $ text - > [ 1 ] ;
}
if ( $ text =~ /:/ ) {
( $ descr , $ text ) = split /:/ , $ text , 2 ;
}
$ text =~ s/^ *// ;
$ text =~ s/ *$// ;
my $ msg ;
my $ curptr ;
if ( $ node ) {
$ msg - > { node } = [ { name = > [ $ node ] } ] ;
$ curptr = $ msg - > { node } - > [ 0 ] ;
} else {
$ msg = { } ;
$ curptr = $ msg ;
}
if ( $ rc ) {
$ curptr - > { errorcode } = [ $ rc ] ;
$ curptr - > { error } = [ $ text ] ;
$ curptr = $ curptr - > { error } - > [ 0 ] ;
} else {
$ curptr - > { data } = [ { contents = > [ $ text ] } ] ;
$ curptr = $ curptr - > { data } - > [ 0 ] ;
if ( $ descr ) { $ curptr - > { desc } = [ $ descr ] ; }
}
# print $outfd freeze([$msg]);
# print $outfd "\nENDOFFREEZE6sK4ci\n";
# yield;
# waitforack($outfd);
$ callback - > ( $ msg ) ;
2010-03-15 21:11:51 +00:00
}
2010-03-18 14:02:12 +00:00
sub genpassword
{
#Generate a pseudo-random password of specified length
my $ length = shift ;
my $ password = '' ;
my $ characters =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890' ;
srand ; #have to reseed, rand is not rand otherwise
while ( length ( $ password ) < $ length )
{
$ password . = substr ( $ characters , int ( rand 63 ) , 1 ) ;
}
return $ password ;
}
2010-03-16 21:38:40 +00:00
1 ;