2010-07-21 20:46:37 +00:00
package xCAT_plugin::ddns ;
2010-03-17 14:03:21 +00:00
use strict ;
2010-03-12 22:06:59 +00:00
use Getopt::Long ;
use Net::DNS ;
2010-12-01 13:00:18 +00:00
use File::Path ;
2010-03-12 22:06:59 +00:00
use xCAT::Table ;
2010-03-15 21:11:51 +00:00
use Sys::Hostname ;
2011-01-26 16:07:01 +00:00
use xCAT::NetworkUtils qw/getipaddr/ ;
use Math::BigInt ;
2010-03-22 13:18:34 +00:00
use MIME::Base64 ;
2010-08-06 15:29:07 +00:00
use xCAT::SvrUtils ;
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 ;
2011-08-15 00:30:40 +00:00
my $ distro = xCAT::Utils - > osver ( ) ;
2010-12-01 13:00:18 +00:00
2011-08-15 00:30:40 +00:00
my $ service = "named" ;
# is this ubuntu ?
if ( $ distro =~ /ubuntu*/ ) {
$ service = "bind9" ;
}
2010-03-12 22:06:59 +00:00
2010-03-16 20:40:19 +00:00
sub handled_commands
{
2010-07-21 20:39:51 +00:00
return { "makedns" = > "site:dnshandler" } ;
2010-03-16 20:40:19 +00:00
}
2010-06-16 17:59:42 +00:00
sub getzonesfornet {
2011-02-15 20:54:40 +00:00
my $ netent = shift ;
my $ net = $ netent - > { net } ;
my $ mask = $ netent - > { mask } ;
2010-06-16 17:59:42 +00:00
my @ zones = ( ) ;
2011-02-15 20:54:40 +00:00
if ( $ netent - > { ddnsdomain } ) {
push @ zones , $ netent - > { ddnsdomain } ;
}
2011-01-26 18:19:45 +00:00
if ( $ net =~ /:/ ) { #ipv6, for now do the simple stuff under the assumption we won't have a mask indivisible by 4
$ net =~ s/\/(.*)// ;
my $ maskbits = $ 1 ;
if ( $ mask ) {
die "Not supporting having a mask like $mask on an ipv6 network like $net" ;
}
my $ netnum = getipaddr ( $ net , GetNumber = > 1 ) ;
2011-06-16 13:45:21 +00:00
unless ( $ netnum ) { return ( ) ; }
2011-01-26 18:19:45 +00:00
$ netnum - > brsft ( 128 - $ maskbits ) ;
my $ prefix = $ netnum - > as_hex ( ) ;
my $ nibbs = $ maskbits / 4 ;
$ prefix =~ s/^0x// ;
my $ rev ;
foreach ( reverse ( split // , $ prefix ) ) {
$ rev . = $ _ . "." ;
$ nibbs - - ;
}
while ( $ nibbs ) {
$ rev . = "0." ;
$ nibbs - - ;
}
$ rev . = "ip6.arpa." ;
2011-02-15 20:54:40 +00:00
push @ zones , $ rev ;
return @ zones ;
2011-01-26 18:19:45 +00:00
}
2010-06-16 17:59:42 +00:00
#return all in-addr reverse zones for a given mask and net
#for class a,b,c, the answer is easy
#for classless, identify the partial byte, do $netbyte | (0xff&~$maskbyte) to get the highest value
#return sequence from $net to value calculated above
#since old bind.pm only went as far as class c, we will carry that over for now (more people with smaller than class c complained
#and none hit the theoretical conflict. FYI, the 'official' method in RFC 2317 seems cumbersome, but maybe one day it makes sense
#since this is dhcpv4 for now, we'll use the inet_aton, ntop functions to generate the answers (dhcpv6 omapi would be nice...)
my $ netn = inet_aton ( $ net ) ;
my $ maskn = inet_aton ( $ mask ) ;
unless ( $ netn and $ mask ) { return ( ) ; }
my $ netnum = unpack ( 'N' , $ netn ) ;
my $ masknum = unpack ( 'N' , $ maskn ) ;
if ( $ masknum >= 0xffffff00 ) { #treat all netmasks higher than 255.255.255.0 as class C
$ netnum = $ netnum & 0xffffff00 ;
$ netn = pack ( 'N' , $ netnum ) ;
$ net = inet_ntoa ( $ netn ) ;
2010-06-16 18:23:30 +00:00
$ net =~ s/\.[^\.]*$// ;
2010-06-16 17:59:42 +00:00
return ( join ( '.' , reverse ( split ( '\.' , $ net ) ) ) . '.IN-ADDR.ARPA.' ) ;
2010-06-16 18:14:47 +00:00
} elsif ( $ masknum > 0xffff0000 ) { #(/17) to /23
2010-06-16 17:59:42 +00:00
my $ tempnumber = ( $ netnum >> 8 ) ;
$ masknum = $ masknum >> 8 ;
my $ highnet = $ tempnumber | ( 0xffffff & ~ $ masknum ) ;
foreach ( $ tempnumber .. $ highnet ) {
$ netnum = $ _ << 8 ;
$ net = inet_ntoa ( pack ( 'N' , $ netnum ) ) ;
2010-06-16 18:23:30 +00:00
$ net =~ s/\.[^\.]*$// ;
2010-06-16 17:59:42 +00:00
push @ zones , join ( '.' , reverse ( split ( '\.' , $ net ) ) ) . '.IN-ADDR.ARPA.' ;
}
return @ zones ;
2010-06-16 18:14:47 +00:00
} elsif ( $ masknum > 0xff000000 ) { # (/9) to class b /16, could have made it more flexible, for for only two cases, not worth in
2010-06-16 17:59:42 +00:00
my $ tempnumber = ( $ netnum >> 16 ) ; #the last two bytes are insignificant, shift them off to make math easier
$ masknum = $ masknum >> 16 ;
my $ highnet = $ tempnumber | ( 0xffff & ~ $ masknum ) ;
foreach ( $ tempnumber .. $ highnet ) {
$ netnum = $ _ << 16 ; #convert back to the real network value
$ net = inet_ntoa ( pack ( 'N' , $ netnum ) ) ;
2010-06-16 18:23:30 +00:00
$ net =~ s/\.[^\.]*$// ;
$ net =~ s/\.[^\.]*$// ;
2010-06-16 17:59:42 +00:00
push @ zones , join ( '.' , reverse ( split ( '\.' , $ net ) ) ) . '.IN-ADDR.ARPA.' ;
}
return @ zones ;
2010-06-16 18:14:47 +00:00
} else { #class a (theoretically larger, but those shouldn't exist)
my $ tempnumber = ( $ netnum >> 24 ) ; #the last two bytes are insignificant, shift them off to make math easier
$ masknum = $ masknum >> 24 ;
my $ highnet = $ tempnumber | ( 0xff & ~ $ masknum ) ;
foreach ( $ tempnumber .. $ highnet ) {
$ netnum = $ _ << 24 ; #convert back to the real network value
$ net = inet_ntoa ( pack ( 'N' , $ netnum ) ) ;
2010-06-16 18:23:30 +00:00
$ net =~ s/\.[^\.]*$// ;
$ net =~ s/\.[^\.]*$// ;
$ net =~ s/\.[^\.]*$// ;
2010-06-16 18:14:47 +00:00
push @ zones , join ( '.' , reverse ( split ( '\.' , $ net ) ) ) . '.IN-ADDR.ARPA.' ;
}
return @ zones ;
}
2010-06-16 17:59:42 +00:00
}
2011-01-26 16:07:01 +00:00
sub get_reverse_zones_for_entity {
2010-03-12 22:06:59 +00:00
my $ ctx = shift ;
my $ node = shift ;
my $ net ;
2011-04-12 11:22:43 +00:00
if ( ( $ node =~ /loopback/ ) || ( $ node =~ /localhost/ ) )
{
# do not use DNS to resolve localhsot
return ;
}
2010-03-12 22:06:59 +00:00
if ( $ ctx - > { hoststab } and $ ctx - > { hoststab } - > { $ node } and $ ctx - > { hoststab } - > { $ node } - > [ 0 ] - > { ip } ) {
$ node = $ ctx - > { hoststab } - > { $ node } - > [ 0 ] - > { ip } ;
}
2011-01-26 16:07:01 +00:00
my @ tvars = getipaddr ( $ node , GetNumber = > 1 , GetAllAddresses = > 1 ) ;
2010-03-12 22:06:59 +00:00
my $ tvar ;
2011-01-26 16:07:01 +00:00
my @ revs ;
foreach $ tvar ( @ tvars ) {
#if ($tvar = getipaddr($node,GetNumber=>1)) { #This is an assignment, we are testing and storing the value in one shot
2010-03-12 22:06:59 +00:00
foreach my $ net ( keys % { $ ctx - > { nets } } ) {
if ( $ ctx - > { nets } - > { $ net } - > { netn } == ( $ tvar & $ ctx - > { nets } - > { $ net } - > { mask } ) ) {
2011-01-26 16:07:01 +00:00
if ( $ net =~ /\./ ) { #IPv4/IN-ADDR.ARPA case.
my $ maskstr = unpack ( "B32" , pack ( "N" , $ ctx - > { nets } - > { $ net } - > { mask } ) ) ;
my $ maskcount = ( $ maskstr =~ tr /1/ / ) ;
2011-04-13 02:55:22 +00:00
if ( $ maskcount >= 24 )
{
$ maskcount -= ( $ maskcount % 8 ) ; #e.g. treat the 27bit netmask as 24bit
}
else
{
$ maskcount += ( ( 8 - ( $ maskcount % 8 ) ) % 8 ) ; #round to the next octet
}
2011-01-26 16:07:01 +00:00
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.' ;
push @ revs , $ rev ;
} elsif ( $ net =~ /:/ ) { #v6/ip6.arpa case
$ net =~ /\/(.*)/ ;
my $ maskbits = $ 1 ;
unless ( $ maskbits and ( ( $ maskbits % 4 ) == 0 ) ) {
die "Never expected this, $net should have had CIDR / notation... and the mask should be a factor of 4, if not, need work..."
}
my $ netnum = Math::BigInt - > new ( $ ctx - > { nets } - > { $ net } - > { netn } ) ;
$ netnum - > brsft ( 128 - $ maskbits ) ;
my $ prefix = $ netnum - > as_hex ( ) ;
my $ nibbs = $ maskbits / 4 ;
$ prefix =~ s/^0x// ;
my $ rev ;
foreach ( reverse ( split // , $ prefix ) ) {
$ rev . = $ _ . "." ;
$ nibbs - - ;
}
while ( $ nibbs ) {
$ rev . = "0." ;
$ nibbs - - ;
}
$ rev . = "ip6.arpa." ;
push @ revs , $ rev ;
2010-03-12 22:06:59 +00:00
}
}
}
}
2011-01-26 16:07:01 +00:00
return @ revs ;
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 ;
2011-04-14 02:38:36 +00:00
my $ help ;
2010-08-25 13:13:44 +00:00
my $ deletemode = 0 ;
2010-03-12 22:06:59 +00:00
if ( $ request - > { arg } ) {
$ hadargs = 1 ;
@ ARGV = @ { $ request - > { arg } } ;
2011-04-14 02:38:36 +00:00
Getopt::Long:: Configure ( "no_pass_through" ) ;
2011-05-11 06:48:21 +00:00
Getopt::Long:: Configure ( "bundling" ) ;
2010-03-12 22:06:59 +00:00
if ( ! GetOptions (
'a|all' = > \ $ allnodes ,
'n|new' = > \ $ zapfiles ,
2010-08-25 13:13:44 +00:00
'd|delete' = > \ $ deletemode ,
2011-04-14 02:38:36 +00:00
'h|help' = > \ $ help ,
2010-03-12 22:06:59 +00:00
) ) {
2011-04-14 02:38:36 +00:00
#xCAT::SvrUtils::sendmsg([1,"TODO: makedns Usage message"], $callback);
makedns_usage ( $ callback ) ;
2010-03-12 22:06:59 +00:00
return ;
}
}
2011-04-14 02:38:36 +00:00
if ( $ help )
{
makedns_usage ( $ callback ) ;
2011-06-22 03:33:51 +00:00
return ;
2011-04-14 02:38:36 +00:00
}
2011-06-28 09:07:53 +00:00
if ( $ deletemode && ( ! $ request - > { node } - > [ 0 ] ) )
{
makedns_usage ( $ callback ) ;
return ;
}
2011-04-14 02:38:36 +00:00
2010-08-25 13:13:44 +00:00
$ ctx - > { deletemode } = $ deletemode ;
2011-08-07 11:24:49 +00:00
# check for site.domain
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 } ) {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "domain not defined in site table" ] , $ callback ) ;
2010-03-16 21:38:40 +00:00
return ;
}
$ ctx - > { domain } = $ stab - > { value } ;
2011-08-07 11:24:49 +00:00
# get site.master
# check to see if /etc/resolv.conf has nameserver=site.master, if no
# put out a warning
my $ master = $ sitetab - > getAttribs ( { key = > 'master' } , [ 'value' ] ) ;
unless ( $ master and $ master - > { value } ) {
xCAT::SvrUtils:: sendmsg ( [ 1 , "master not defined in site table" ] , $ callback ) ;
return ;
}
my $ resolv = "/etc/resolv.conf" ;
my $ found = 0 ;
2011-08-09 16:39:47 +00:00
# check if nameserver site.master in /etc/resolv.conf
2011-08-07 11:24:49 +00:00
my $ nameserver = $ master - > { value } ;
my $ cmd = "grep $nameserver $resolv" ;
my @ output = xCAT::Utils - > runcmd ( $ cmd , 0 ) ;
if ( $ ::RUNCMD_RC != 0 )
{
$ found = 0 ;
} else { # if it is there check it is a nameserver
foreach my $ line ( @ output ) {
if ( $ line =~ /^nameserver/ ) { # line is a nameserver line
$ found = 1 ;
last ;
}
}
}
if ( $ found == 0 ) { # not nameserver master found
xCAT::SvrUtils:: sendmsg ( [ 0 , "Warning:The management node is not defined as a nameserver in /etc/resolv.conf. Add \"nameserver $nameserver\" to /etc/resolv.conf and run makedns again." ] , $ callback ) ;
}
2011-08-09 16:39:47 +00:00
2011-08-18 17:38:53 +00:00
# chk if search site.domain or domain site.domain on AIX in /etc/resolv.conf
2011-08-09 16:39:47 +00:00
$ found = 0 ;
my $ domain = $ ctx - > { domain } ;
my $ cmd = "grep $domain $resolv" ;
my @ output = xCAT::Utils - > runcmd ( $ cmd , 0 ) ;
if ( $ ::RUNCMD_RC != 0 )
{
$ found = 0 ;
} else { # if it is there check it is a search clause
foreach my $ line ( @ output ) {
if ( $ line =~ /^search/ ) { # line is a search line
$ found = 1 ;
last ;
}
2011-08-18 17:38:53 +00:00
# if AIX could be a domain line
if ( xCAT::Utils - > isAIX ( ) ) {
if ( $ line =~ /^domain/ ) {
$ found = 1 ;
last ;
}
}
2011-08-09 16:39:47 +00:00
}
}
if ( $ found == 0 ) { # no search site.domain found
2011-08-18 17:38:53 +00:00
xCAT::SvrUtils:: sendmsg ( [ 0 , "Warning:The domain is not defined in a search path or domain path for AIX (in /etc/resolv.conf. Add \"search $domain\" to /etc/resolv.conf and run makedns again." ] , $ callback ) ;
2011-08-09 16:39:47 +00:00
}
2011-08-19 11:55:15 +00:00
# check for selinux disabled
2011-08-22 12:02:08 +00:00
my $ rc = xCAT::Utils - > isSELINUX ( ) ;
if ( $ rc == 0 )
2011-08-19 11:55:15 +00:00
{
xCAT::SvrUtils:: sendmsg ( [ 0 , "Warning:SELINUX is not disabled. The makedns command will not be able to generate a complete DNS setup. Disable SELINUX and run the command again." ] , $ callback ) ;
2011-08-22 12:02:08 +00:00
}
2011-06-23 07:10:36 +00:00
my $ networkstab = xCAT::Table - > new ( 'networks' , - create = > 0 ) ;
unless ( $ networkstab ) { xCAT::SvrUtils:: sendmsg ( [ 1 , 'Unable to enumerate networks, try to run makenetworks' ] , $ callback ) ; }
my @ networks = $ networkstab - > getAllAttribs ( 'net' , 'mask' , 'ddnsdomain' ) ;
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 {
2011-06-29 17:57:25 +00:00
if ( $ deletemode ) {
#when this was permitted, it really ruined peoples' days
xCAT::SvrUtils:: sendmsg ( [ 1 , "makedns -d without noderange or -a is not supported" ] , $ callback ) ;
return ;
}
2010-03-15 21:11:51 +00:00
#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 ;
2011-02-15 20:54:40 +00:00
if ( $ addr !~ /^\d+\.\d+\.\d+\.\d+$/ and $ addr !~ /^[abcdef0123456789:]+$/ ) {
xCAT::SvrUtils:: sendmsg ( ":Ignoring line $_ in /etc/hosts, address seems malformed." , $ callback ) ;
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-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( ":Ignoring line $_ in /etc/hosts, names $names contain invalid characters (valid characters include a through z, numbers and the '-', but not '_'" , $ callback ) ;
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 ;
2011-07-20 06:45:30 +00:00
xCAT::SvrUtils:: sendmsg ( ":Handling $node in /etc/hosts." , $ callback ) ;
2010-03-16 21:38:40 +00:00
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
}
2011-06-23 07:10:36 +00:00
# exclude the nodes not belong to any nets defined in networks table
# because only the nets defined in networks table will be add zones later.
my $ found = 0 ;
foreach ( @ networks )
{
if ( xCAT::NetworkUtils - > ishostinsubnet ( $ addr , $ _ - > { mask } , $ _ - > { net } ) )
{
$ found = 1 ;
}
}
if ( $ found )
{
push @ nodes , $ node ;
$ ctx - > { nodeips } - > { $ node } - > { $ addr } = 1 ;
}
else
{
2011-06-23 07:39:43 +00:00
unless ( $ node =~ /localhost/ )
{
xCAT::SvrUtils:: sendmsg ( ":Ignoring host $node in /etc/hosts, it does not belong to any nets defined in networks table." , $ callback ) ;
}
2011-06-23 07:10:36 +00:00
}
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 ;
2011-06-23 07:10:36 +00:00
2010-03-12 22:06:59 +00:00
foreach ( @ networks ) {
2011-01-26 16:07:01 +00:00
my $ maskn ;
if ( $ _ - > { mask } ) { #better be IPv4, we only do CIDR for v6, use the v4/v6 agnostic just in case
$ maskn = getipaddr ( $ _ - > { mask } , GetNumber = > 1 ) ; #pack("N",inet_aton($_->{mask}));
} elsif ( $ _ - > { net } =~ /\/(.*)/ ) { #CIDR
my $ maskbits = $ 1 ;
my $ numbits ;
if ( $ _ - > { net } =~ /:/ ) { #v6
$ numbits = 128 ;
} elsif ( $ _ - > { net } =~ /\./ ) {
$ numbits = 32 ;
} else {
die "Network " . $ _ - > { net } . " appears to be malformed in networks table" ;
}
$ maskn = Math::BigInt - > new ( "0b" . ( "1" x $ maskbits ) . ( "0" x ( $ numbits - $ maskbits ) ) ) ;
}
2010-03-12 22:06:59 +00:00
$ ctx - > { nets } - > { $ _ - > { net } } - > { mask } = $ maskn ;
2011-01-26 16:07:01 +00:00
my $ net = $ _ - > { net } ;
$ net =~ s/\/.*// ;
$ ctx - > { nets } - > { $ _ - > { net } } - > { netn } = getipaddr ( $ net , GetNumber = > 1 ) ;
2010-06-16 17:59:42 +00:00
my $ currzone ;
2011-02-15 20:54:40 +00:00
foreach $ currzone ( getzonesfornet ( $ _ ) ) {
2010-06-16 17:59:42 +00:00
$ ctx - > { zonestotouch } - > { $ currzone } = 1 ;
}
2010-03-12 22:06:59 +00:00
}
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
2011-05-11 06:48:21 +00:00
2011-05-12 07:59:51 +00:00
$ stab = $ sitetab - > getAttribs ( { key = > 'forwarders' } , [ 'value' ] ) ;
if ( $ stab and $ stab - > { value } ) {
my @ forwarders = split /[ ,]/ , $ stab - > { value } ;
$ ctx - > { forwarders } = \ @ forwarders ;
2010-03-12 22:06:59 +00:00
}
2010-03-15 21:11:51 +00:00
$ ctx - > { zonestotouch } - > { $ ctx - > { domain } } = 1 ;
2011-06-23 07:10:36 +00:00
xCAT::SvrUtils:: sendmsg ( "Getting reverse zones, this may take several minutes in scaling cluster." , $ callback ) ;
2010-03-12 22:06:59 +00:00
foreach ( @ nodes ) {
2011-01-26 16:07:01 +00:00
my @ revzones = get_reverse_zones_for_entity ( $ ctx , $ _ ) ; ;
unless ( @ revzones ) { next ; }
$ ctx - > { revzones } - > { $ _ } = \ @ revzones ;
foreach ( @ revzones ) {
$ ctx - > { zonestotouch } - > { $ _ } = 1 ;
}
2010-03-12 22:06:59 +00:00
}
2011-06-23 07:10:36 +00:00
xCAT::SvrUtils:: sendmsg ( "Completed getting reverse zones." , $ callback ) ;
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
2011-04-13 08:08:32 +00:00
if ( xCAT::Utils - > isAIX ( ) )
{
system ( "/usr/bin/stopsrc -s $service" ) ;
}
else
{
2011-08-15 00:30:40 +00:00
system ( "service $service stop" ) ; #named may otherwise hold on to stale journal filehandles
2011-04-13 08:08:32 +00:00
}
2010-12-01 13:00:18 +00:00
my $ conf = get_conf ( ) ;
unlink $ conf ;
my $ DBDir = get_dbdir ( ) ;
foreach ( <$DBDir/db.*> ) {
2010-03-17 14:03:21 +00:00
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-12-01 13:00:18 +00:00
$ ctx - > { zonesdir } = get_zonesdir ( ) ;
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-12-01 13:00:18 +00:00
xCAT::SvrUtils:: sendmsg ( "Restarting $service" , $ callback ) ;
2011-04-12 11:22:43 +00:00
if ( xCAT::Utils - > isAIX ( ) )
{
system ( "/usr/bin/stopsrc -s $service" ) ;
system ( "/usr/bin/startsrc -s $service" ) ;
}
else
{
2011-08-15 00:30:40 +00:00
system ( "service $service stop" ) ;
system ( "service $service start" ) ;
2011-04-12 11:22:43 +00:00
}
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( "Restarting named complete" , $ callback ) ;
2010-03-15 21:11:51 +00:00
}
2010-03-12 22:06:59 +00:00
} else {
unless ( $ ctx - > { privkey } ) {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "Unable to update DNS due to lack of credentials in passwd to communicate with remote server" ] , $ callback ) ;
2010-03-12 22:06:59 +00:00
}
}
#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 ( ) ;
2010-08-25 13:13:44 +00:00
add_or_delete_records ( $ ctx ) ;
2011-06-23 07:10:36 +00:00
xCAT::SvrUtils:: sendmsg ( "DNS setup is completed" , $ callback ) ;
2010-03-12 22:06:59 +00:00
}
2010-12-01 13:00:18 +00:00
sub get_zonesdir {
my $ ZonesDir = get_dbdir ( ) ;
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 ) ;
}
if ( $ sitetab ) {
2010-12-08 11:43:56 +00:00
my ( $ ref ) = $ sitetab - > getAttribs ( { key = > 'bindzones' } , 'value' ) ;
2010-12-01 13:00:18 +00:00
if ( $ ref and $ ref - > { value } ) {
$ ZonesDir = $ ref - > { value } ;
}
}
return "$ZonesDir" ;
}
sub get_conf {
my $ conf = "/etc/named.conf" ;
2011-08-15 00:30:40 +00:00
# is this ubuntu ?
if ( $ distro =~ /ubuntu*/ ) {
$ conf = "/etc/bind/named.conf" ;
}
2010-12-01 13:00:18 +00:00
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 ) ;
}
if ( $ sitetab ) {
2010-12-08 11:43:56 +00:00
my ( $ ref ) = $ sitetab - > getAttribs ( { key = > 'bindconf' } , 'value' ) ;
2010-12-01 13:00:18 +00:00
if ( $ ref and $ ref - > { value } ) {
$ conf = $ ref - > { value } ;
}
}
return "$conf" ;
}
2010-03-15 21:11:51 +00:00
sub get_dbdir {
2010-12-01 13:00:18 +00:00
my $ DBDir ;
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 ) ;
}
if ( $ sitetab ) {
( my $ ref ) = $ sitetab - > getAttribs ( { key = > 'binddir' } , 'value' ) ;
if ( $ ref and $ ref - > { value } ) {
$ DBDir = $ ref - > { value } ;
}
}
if ( - d "$DBDir" ) {
return "$DBDir"
} elsif ( - d "/var/named" ) {
2010-03-15 21:11:51 +00:00
return "/var/named/" ;
} elsif ( - d "/var/lib/named" ) {
2011-07-19 06:26:52 +00:00
# Temp fix for bugzilla 73119
chown ( scalar ( getpwnam ( 'root' ) ) , scalar ( getgrnam ( 'named' ) ) , "/var/lib/named" ) ;
2010-03-15 21:11:51 +00:00
return "/var/lib/named/" ;
2011-08-15 00:30:40 +00:00
}
elsif ( - d "/var/lib/bind" ) {
return "/var/lib/bind/" ;
}
else {
2010-03-15 21:11:51 +00:00
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/" ;
}
}
2010-05-27 15:57:56 +00:00
sub isvalidip {
#inet_pton/ntop good for ensuring an ip looks like an ip? or do string compare manually?
#for now, do string analysis, one problem with pton/ntop is that 010.1.1.1 would look diff from 10.1.1.1)
my $ candidate = shift ;
if ( $ candidate =~ /^(\d+)\.(\d+)\.(\d+).(\d+)\z/ ) {
return (
$ 1 >= 0 and $ 1 <= 255 and
$ 2 >= 0 and $ 2 <= 255 and
$ 3 >= 0 and $ 3 <= 255 and
$ 4 >= 0 and $ 4 <= 255
) ;
}
}
2010-03-15 21:11:51 +00:00
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 ;
2011-06-23 07:10:36 +00:00
xCAT::SvrUtils:: sendmsg ( "Updating zones." , $ callback ) ;
2010-03-16 20:36:25 +00:00
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 } ;
2010-05-27 15:57:56 +00:00
unless ( isvalidip ( $ ip ) ) {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "The hosts table entry for $node indicates $ip as an ip address, which is not a valid address" ] , $ callback ) ;
2010-05-27 15:57:56 +00:00
next ;
}
2010-03-16 20:36:25 +00:00
} 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" ;
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "Unable to find an IP for $node in hosts table or via system lookup (i.e. /etc/hosts" ] , $ callback ) ;
2010-03-16 20:36:25 +00:00
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 ;
2010-06-08 19:14:48 +00:00
#If needed, the below, but it was a fairly painfully restricted paradigm for zonefile names...
#} elsif (not $zonefilename =~ /_/) {
# $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 ;
}
}
2011-06-23 07:10:36 +00:00
xCAT::SvrUtils:: sendmsg ( "Completed updating zones." , $ callback ) ;
2010-03-15 21:11:51 +00:00
}
2010-03-12 22:06:59 +00:00
sub update_namedconf {
my $ ctx = shift ;
2010-12-01 13:00:18 +00:00
my $ namedlocation = get_conf ( ) ;
2010-03-12 22:06:59 +00:00
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 {
2011-08-03 19:57:41 +00:00
#push @newnamed,"\t\t//listen-on-v6 { any; };\n";
2010-03-12 22:06:59 +00:00
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 ;
2010-06-09 18:53:46 +00:00
#$zfilename =~ s/\..*//;
2010-04-01 19:19:04 +00:00
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-12-01 13:00:18 +00:00
push @ newnamed , "options {\n" , "\tdirectory \"" . $ ctx - > { zonesdir } . "\";\n" ;
2011-07-21 14:37:54 +00:00
push @ newnamed , "\t\t//listen-on-v6 { any; };\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 ;
2010-06-09 18:53:46 +00:00
#$zfilename =~ s/\..*//;
2010-04-01 19:19:04 +00:00
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 ;
2010-06-09 18:53:46 +00:00
#$zfilename =~ s/\..*//;
2010-04-01 19:19:04 +00:00
push @ newnamed , "\t};\n" , "\tfile \"db.$zfilename\";\n" , "};\n\n" ;
2010-03-12 22:06:59 +00:00
}
2011-07-20 06:45:30 +00:00
# For AIX, add a hint zone
if ( xCAT::Utils - > isAIX ( ) )
{
unless ( grep ( /hint/ , @ newnamed ) )
{
push @ newnamed , "zone \"\.\" in {\n" , "\ttype hint;\n" , "\tfile \"db\.cache\";\n" , "};\n\n" ;
# Toutch the stub zone file
system ( "/usr/bin/touch $ctx->{dbdir}.'/db.cache'" ) ;
$ ctx - > { restartneeded } = 1 ;
}
}
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-08-25 13:13:44 +00:00
sub add_or_delete_records {
2010-03-15 21:11:51 +00:00
my $ ctx = shift ;
2011-06-23 07:10:36 +00:00
xCAT::SvrUtils:: sendmsg ( "Updating DNS records, this may take several minutes in scaling cluster." , $ callback ) ;
2010-03-15 21:11:51 +00:00
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 {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "Unable to find omapi key in passwd table" ] , $ callback ) ;
2010-03-15 21:11:51 +00:00
}
}
my $ node ;
2011-01-26 18:19:45 +00:00
my @ ips ;
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 } } ) {
2010-03-16 20:36:25 +00:00
my $ name = $ node ;
2011-04-12 11:22:43 +00:00
if ( ( $ name =~ /loopback/ ) || ( $ name =~ /localhost/ ) )
{
next ;
}
2010-03-16 20:36:25 +00:00
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 } ) {
2011-01-26 18:19:45 +00:00
@ ips = ( $ ctx - > { hoststab } - > { $ node } - > [ 0 ] - > { ip } ) ;
2010-03-15 21:11:51 +00:00
} else {
2011-01-26 18:19:45 +00:00
@ ips = getipaddr ( $ node , GetAllAddresses = > 1 ) ;
unless ( @ ips ) {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "Unable to find an IP for $node in hosts table or via system lookup (i.e. /etc/hosts" ] , $ callback ) ;
2010-03-15 21:11:51 +00:00
next ;
}
2011-01-26 18:19:45 +00:00
}
foreach my $ ip ( @ ips ) {
$ ctx - > { currip } = $ ip ;
#time to update, A and PTR records, IPv6 still TODO
if ( $ ip =~ /\./ ) { #v4
$ ip = join ( '.' , reverse ( split ( /\./ , $ ip ) ) ) ;
$ ip . = '.IN-ADDR.ARPA.' ;
} elsif ( $ ip =~ /:/ ) { #v6
$ ip = getipaddr ( $ ip , GetNumber = > 1 ) ;
$ ip = $ ip - > as_hex ( ) ;
$ ip =~ s/^0x// ;
$ ip = join ( '.' , reverse ( split ( // , $ ip ) ) ) ;
$ ip . = '.ip6.arpa.' ;
} else {
die "ddns did not understand $ip result of lookup" ;
}
#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 ;
2011-08-12 11:17:36 +00:00
my $ tmpdm ;
unless ( $ domain =~ /\.$/ ) { $ tmpdm = $ domain . '.' ; } #example.com becomes example.com.
2011-01-26 18:19:45 +00:00
find_nameserver_for_dns ( $ ctx , $ revzone ) ;
2011-08-12 11:17:36 +00:00
find_nameserver_for_dns ( $ ctx , $ tmpdm ) ;
2011-01-26 18:19:45 +00:00
}
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 } } ) {
2010-08-25 13:13:44 +00:00
if ( $ ctx - > { deletemode } ) {
$ update - > push ( update = > rr_del ( $ entry ) ) ;
} else {
$ 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' ) {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "Failure encountered updating $zone, error was " . $ reply - > header - > rcode ] , $ callback ) ;
2010-04-05 19:39:51 +00:00
}
$ 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 ) ;
2011-08-19 11:32:43 +00:00
if ( $ reply - > header - > rcode ne 'NOERROR' ) {
xCAT::SvrUtils:: sendmsg ( [ 1 , "Failure encountered updating $zone, error was " . $ reply - > header - > rcode ] , $ callback ) ;
}
2011-05-13 07:31:18 +00:00
# sometimes resolver does not work if the update zone request sent so quick
sleep 1 ;
2010-03-16 20:36:25 +00:00
}
}
2011-06-23 07:10:36 +00:00
xCAT::SvrUtils:: sendmsg ( "Completed updating DNS records." , $ callback ) ;
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 . = '.' }
2011-01-26 18:19:45 +00:00
my @ rrcontent ;
if ( $ ip =~ /:/ ) {
@ rrcontent = ( "$name IN AAAA $ip" ) ;
} else {
@ rrcontent = ( "$name IN A $ip" ) ;
}
2010-03-19 13:23:17 +00:00
foreach ( keys % { $ ctx - > { nodeips } - > { $ node } } ) {
unless ( $ _ eq $ ip ) {
2011-01-26 18:19:45 +00:00
if ( $ _ =~ /:/ ) {
push @ rrcontent , "$name IN AAAA $_" ;
} else {
push @ rrcontent , "$name IN A $_" ;
}
2010-03-19 13:23:17 +00:00
}
}
2010-09-27 20:21:39 +00:00
if ( $ ctx - > { deletemode } ) {
push @ rrcontent , "$name TXT" ;
push @ rrcontent , "$name A" ;
}
2011-01-26 18:19:45 +00:00
if ( $ zone =~ /IN-ADDR.ARPA/ or $ zone =~ /ip6.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
2011-07-12 20:18:36 +00:00
} elsif ( $ ::XCATSITEVALS { ddnsserver } ) {
$ ctx - > { nsmap } - > { $ zone } = $ ::XCATSITEVALS { ddnsserver } ;
2010-03-17 14:03:21 +00:00
} else {
my $ reply = $ ctx - > { resolver } - > query ( $ zone , 'NS' ) ;
if ( $ reply ) {
2011-08-19 11:32:43 +00:00
if ( $ reply - > header - > rcode ne 'NOERROR' ) {
xCAT::SvrUtils:: sendmsg ( [ 1 , "Failure encountered querying $zone, error was " . $ reply - > header - > rcode ] , $ callback ) ;
}
2010-03-17 14:03:21 +00:00
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 !~ /\./ ) {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "Unable to find reverse zone to hold $node" ] , $ callback , $ node ) ;
2010-03-17 14:03:21 +00:00
last ;
}
2010-03-16 20:36:25 +00:00
$ zone =~ s/^[^\.]*\.// ; #strip all up to and including first dot
unless ( $ zone ) {
2010-08-06 15:29:07 +00:00
xCAT::SvrUtils:: sendmsg ( [ 1 , "Unable to find zone to hold $node" ] , $ callback , $ node ) ;
2010-03-16 20:36:25 +00:00
last ;
}
}
}
}
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 ;
}
2011-04-14 02:38:36 +00:00
sub makedns_usage
{
my $ callback = shift ;
my $ rsp ;
push @ { $ rsp - > { data } } ,
"\n makedns - sets up domain name services (DNS)." ;
push @ { $ rsp - > { data } } , " Usage: " ;
push @ { $ rsp - > { data } } , "\tmakedns [-h|--help ]" ;
push @ { $ rsp - > { data } } , "\tmakedns [-n|--new ] [noderange]" ;
2011-06-28 09:07:53 +00:00
push @ { $ rsp - > { data } } , "\tmakedns [-d|--delete noderange]" ;
2011-04-14 02:38:36 +00:00
push @ { $ rsp - > { data } } , "\n" ;
xCAT::MsgUtils - > message ( "I" , $ rsp , $ callback ) ;
return 0 ;
}
2010-03-16 21:38:40 +00:00
1 ;