#!/usr/bin/perl -w # # version 0.4 # Copyright (C) 2004 Mandrakesoft # Author: Antoine Ginies # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package MDK::Wizard::Bind; use lib qw(/usr/lib/libDrakX); use strict; use services; use common; use MDK::Wizard::Varspaceval; use MDK::Wizard::Wizcommon; use network::network; my $wiz = new MDK::Wizard::Wizcommon; my $in = interactive->vnew; my $SERIAL = chomp_(`date +20%y%m%d`); my $HOSTNAME = $wiz->{net}->network_get("HOSTNAME"); my $BIND_CHROOT = "/var/lib/named"; my $IPSERVER = $wiz->{net}->itf_get("IPADDR"); my $sys_wizard_dns = "/etc/sysconfig/drak_dns_wiz"; if (-f $sys_wizard_dns) { our ($interface) = cat_($sys_wizard_dns) =~ /INTERFACE=(.*)/; ($IPSERVER) = `/sbin/ip addr show dev $interface` =~ /^\s*inet\s+(\d+\.\d+\.\d+\.\d+)/m; } else { our $interface = $wiz->{net}->default_itf; ($IPSERVER) = `/sbin/ip addr show dev $interface` =~ /^\s*inet\s+(\d+\.\d+\.\d+\.\d+)/m; } my $DOMAINNAME = chomp_(`dnsdomainname`); my $CLIENTIP = get_spe_ip("ipnor", $IPSERVER) . "."; my $WDIR = "/tmp/dnstest"; my $NAMED_DIR = $BIND_CHROOT . "/var/named"; my $DNSKEY = ""; my $SHORTHOSTNAME = chomp_(`hostname -s`); my $TEXTINFO = "dns Wizard"; my $REP_SAVE = "/tmp/bck"; my $ZONE_DIR = $NAMED_DIR . "/zone"; my $DATE = `date +%d-%m-20%y`; my $WIZARD_SCRIPTS = "/usr/share/wizards/dns_wizard/scripts"; my $o = { name => 'DNS Configuration Wizard', var => { IPOFFORWARDER => '', ADDSEARCH => '', DOMAINNAME => $DOMAINNAME, SHORTHOSTNAME => $SHORTHOSTNAME, IPMASTER => '', CLIENTNAME => '', CLIENTIP => $CLIENTIP, }, init => sub { my ($err, $msg) = test_host_domain($SHORTHOSTNAME, $DOMAINNAME); if (!$err) { $in->ask_warn(N("Error"), $msg); die 'wizcancel'; } ($err, $msg); }, needed_rpm => [ 'bind' ], defaultimage => "/usr/share/mcc/themes/default/dns_server-mdk.png", }; my %level = ( 1 => N("Master DNS server"), 2 => N("Slave DNS server"), 3 => N("Add host in DNS"), 4 => N("Remove host in DNS"), ); my @list_hosts; sub list_hosts { my $iprev = get_spe_ip('iprev', $IPSERVER); my $db = "$ZONE_DIR/db.$iprev.hosts"; #my $ipnor = get_spe_ip('iprev', $IPSERVER); # push @list_hosts, ""; foreach (cat_($db)) { my ($h) = /\d{1,3}\tIN\tPTR\t(.*)/; !$h or push @list_hosts,$h; } @list_hosts; } $o->{pages} = { welcome => { name => N("DNS Master configuration wizard") . "\n\n" . N("DNS (Domain Name Server) is the service that maps an IP address of a machine with an internet host name.") . "\n\n" . N("This wizard will help you configuring the DNS services of your server. This configuration will provide a local DNS service for local computers names, with non-local requests forwarded to an outside DNS."), no_back => 1, pre => sub { $o->{var}{wiz_level} ||= 1; }, post => sub { if ($o->{var}{wiz_level} == 2) { return 'slave'; } elsif ($o->{var}{wiz_level} == 1) { return 'interface'; } elsif ($o->{var}{wiz_level} == 3) { return -f $sys_wizard_dns ? 'addhost' : 'error_notmaster'; } elsif ($o->{var}{wiz_level} == 4) { return -f $sys_wizard_dns ? 'removehost' : 'error_notmaster'; } }, data => [ { label => '', val => \$o->{var}{wiz_level}, type => 'list', list => [ sort keys %level ], format => sub { $level{$_[0]} } }, ], next => 'interface', }, interface => { name => N("DNS server Interface"), data => [ { list => [ keys %{$wiz->{net}{itf}} ], val => \$o->{var}{interface} }, ], no_back => 1, next => 'ipforward' }, addhost => { name => N("Client identification:") . "\n\n" . N("Your client on the network will be identified by name, as in clientname.company.net. Every machine on the network must have a (unique) IP address, in the usual dotted syntax.") . "\n\n" . N("(You don't need to add the domain after the name)") . "\n\n" . N("Note that the given IP address and client name should be unique in the network."), data => [ { label => N("Server:"), val_ref => \$o->{var}{SHORTHOSTNAME} }, { label => N("DNS Domainname:"), val_ref => \$o->{var}{DOMAINNAME} }, { label => N("Name of the machine:"), val => \$o->{var}{CLIENTNAME} }, { label => N("IP address of the machine:"), val => \$o->{var}{CLIENTIP} }, ], complete => sub { if ($o->{var}{CLIENTIP}) { if (!is_ip($o->{var}{CLIENTIP})) { #$in->ask_warn(N("Error"), N("This is not a valid IP address.")); return 1; } else { return 0; } } }, no_back => 1, next => 'summaryadd', no_back => 1, }, removehost => { name => N("Remove host:") . "\n\n" . N("Remove a host in existing DNS configuration.") . "\n\n" . N("Choose the host you want to remove in the following list."), data => [ { label => N("Computer Name:"), val => \$o->{var}{CLIENTNAME}, list_ref => \@list_hosts }, ], post => \&list_hosts, next => 'summaryremove', no_back => 1, }, slave => { name => N("Slave DNS server") . "\n\n" . N("A slave name server will take some of the burden away from your primary name server, and will also function as a backup server, in case your master server is unreachable."), data => [ { label => N("IP Address of the master DNS server:"), val => \$o->{var}{IPMASTER} }, ], complete => sub { if ($o->{var}{IPMASTER}) { if (!is_ip($o->{var}{IPMASTER})) { #$in->ask_warn(N("Error"), N("This is not a valid IP address.")); return 1; } else { return 0; } } }, no_back => 1, next => 'summaryslave', }, ipforward => { name => N("IP of your forwarder") . "\n\n" . N("Forwarding occurs on only those queries for which the server is not authoritative and does not have the answer in its cache.") . "\n\n" . N("If you need it and know your IP forwarder enter IP address of it, if you dont know leave it blank"), pre => sub { ($IPSERVER) = `/sbin/ip addr show dev $o->{var}{interface}` =~ /^\s*inet\s+(\d+\.\d+\.\d+\.\d+)/m; output($sys_wizard_dns, "INTERFACE=$o->{var}{interface}\n"); }, data => [ { label => N("External DNS:"), val => \$o->{var}{IPOFFORWARDER} }, ], complete => sub { if ($o->{var}{IPOFFORWARDER}) { if (!is_ip($o->{var}{IPOFFORWARDER})) { #$in->ask_warn(N("Error"), N("This is not a valid IP address for your forwarder.")); return 1; } else { return 0; } } }, no_back => 1, next => 'addsearch', }, addsearch => { name => N("Add search domain") . "\n\n" . N("Search list for host-name lookup. The search list is normally determined from the local domain name; by default, it contains only the local domain name. This may be changed by listing the desired domain search path following the search keyword") . "\n\n" . N("Domainname of this server is automatically added, and you dont need to add it here."), data => [ { label => N("Default domain name to search:"), val => \$o->{var}{ADDSEARCH} }, ], next => 'summary', }, error_ipf => { name => N("This is not a valid IP address for your forwarder... press next to continue"), ignore => 1, next => 'ipforward', }, error_ipm => { name => N("This is not a valid Master DNS IP address... press next to continue"), ignore => 1, next => 'slave', }, error_iph => { name => N("This is not a valid IP address... press next to continue"), ignore => 1, next => 'addhost', }, dhcp_warning => { name => N("Warning") . "\n\n" . N("You are in dhcp, server may not work with your configuration."), ignore => 1, next => 'client_id' }, error_add => { name => N("Error.") . "\n\n" . N("It seems that host is already in your DNS configuration... press next to continue"), ignore => 1, next => 'addhost', }, error_remove => { name => N("Error:") . "\n\n" . N("It seems that this is not present in your DNS configuration... press next to continue"), ignore => 1, next => 'removehost', }, error_nosrv => { name => N("It seems that no DNS server has been set through wizard. Please run DNS wizard: Master DNS server."), end => 1, }, error_notmaster => { name => N("It seems that you are not a master DNS server, so I can't add/remove host."), end => 1, }, summaryslave => { name => N("Wizard will Now build your DNS slave configuration") . "\n\n" . N("with this configuration:"), data => [ { label => N("IP Address of the master DNS server:"), val_ref => \$o->{var}{IPMASTER} }, ], post => \&do_it_slave, next => 'end', }, summaryadd => { name => N("Client with this identification will be added to your DNS"), data => [ { label => N("Server:"), val_ref => \$o->{var}{SHORTHOSTNAME} }, { label => N("DNS Domainname:"), val_ref => \$o->{var}{DOMAINNAME} }, { label => N("Computer name:"), val_ref => \$o->{var}{CLIENTNAME} }, { label => N("Computer IP address:"), val_ref => \$o->{var}{CLIENTIP} }, ], post => \&do_it_add, }, summaryremove => { name => N("Client with this identification will be removed from your DNS"), data => [ { label => N("Computer name:"), val => \$o->{var}{CLIENTNAME}, list_ref => \@list_hosts }, ], post => \&do_it_remove, next => 'endremove', }, summary => { name => N("The DNS server is about to be configured with the following configuration"), data => [ { label => N("Server Hostname:"), val_ref => \$o->{var}{SHORTHOSTNAME} }, { label => N("Domainname:"), val_ref => \$o->{var}{DOMAINNAME} }, { label => N("External DNS:"), val_ref => \$o->{var}{IPOFFORWARDER} }, { label => N("Default domain name to search:"), val_ref => \$o->{var}{ADDSEARCH} }, ], post => \&do_it_master, next => 'end', }, endadd => { name => N("Congratulations"), data => [ { label => N("The wizard successfully added the host in your DNS.") } ], no_back => 1, end => 1, }, endremove => { name => N("Congratulations"), data => [ { label => N("The wizard successfully removed the host from your DNS.") } ], no_back => 1, end => 1, }, end => { name => N("Congratulations"), data => [ { label => N("The wizard successfully configured the DNS service of your server.") } ], no_back => 1, end => 1, }, error_end => { name => N("Failed"), data => [ { label => N("Please Relaunch drakwizard, and try to change some parameters.") } ], no_back => 1, end => 1, }, }; sub test_srv { my $dir = $BIND_CHROOT . "/var/named/zone"; -d $dir or return 'error_nosrv'; } sub interface_to_ip { my ($interface) = @_; my ($ip) = `/sbin/ip addr show dev $interface` =~ /^\s*inet\s+(\d+\.\d+\.\d+\.\d+)/m; $ip; } sub crea_wdir { if (-e $WDIR) { system("rm -rf $WDIR") } mkdir_p($WDIR); } sub resolv_ip { my ($ip) = @_; gethostbyaddr(Socket::inet_aton($ip), Socket::AF_INET()); } sub resolv_name { my ($name) = @_; join(".", unpack "C4", (gethostbyname $name)[4]); } sub get_spe_ip { # waiting iprev, ipnorm or ipend my ($att, $ip) = @_; my @o = split(/\./, $ip); if ($att =~ /iprev/) { my $iprev = $o[2] . "." . $o[1] . "." . $o[0]; return $iprev; } elsif ($att =~ /ipnor/) { my $ipnor = $o[0] . "." . $o[1] . "." . $o[2]; return $ipnor; } elsif ($att =~ /ipend/) { my $ipend = $o[3]; return $ipend; } } sub increment_serial { my ($iprev) = @_; my ($SERIAL) = cat_("$ZONE_DIR/db.$DOMAINNAME.hosts") =~ m/\s+(.*?)\s+;\s+Serial/; $SERIAL = chomp_($SERIAL+1); substInFile { s/\s+\d+\s+;\s+Serial/ $SERIAL ; Serial/; } "$ZONE_DIR/db.$DOMAINNAME.hosts"; substInFile { s/\s+\d+\s+;\s+Serial/ $SERIAL ; Serial/; } "$ZONE_DIR/db.$iprev.hosts"; } sub crea_db_local { output($WDIR . "/db.localhost", <{var}{IPOFFORWARDER} or append_to_file($WDIR . "/named.conf", "\tforwarders { $o->{var}{IPOFFORWARDER}; };\n"); append_to_file($WDIR . "/named.conf", <> DiG 8.1 <<>> \@A.ROOT-SERVERS.NET. ; (1 server found) ;; res options: init recurs defnam dnsrch ;; got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10 ;; flags: qr aa rd; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 13 ;; QUERY SECTION: ;; ., type = NS, class = IN ;; ANSWER SECTION: . 6D IN NS G.ROOT-SERVERS.NET. . 6D IN NS J.ROOT-SERVERS.NET. . 6D IN NS K.ROOT-SERVERS.NET. . 6D IN NS L.ROOT-SERVERS.NET. . 6D IN NS M.ROOT-SERVERS.NET. . 6D IN NS A.ROOT-SERVERS.NET. . 6D IN NS H.ROOT-SERVERS.NET. . 6D IN NS B.ROOT-SERVERS.NET. . 6D IN NS C.ROOT-SERVERS.NET. . 6D IN NS D.ROOT-SERVERS.NET. . 6D IN NS E.ROOT-SERVERS.NET. . 6D IN NS I.ROOT-SERVERS.NET. . 6D IN NS F.ROOT-SERVERS.NET. ;; ADDITIONAL SECTION: G.ROOT-SERVERS.NET. 5w6d16h IN A 192.112.36.4 J.ROOT-SERVERS.NET. 5w6d16h IN A 198.41.0.10 K.ROOT-SERVERS.NET. 5w6d16h IN A 193.0.14.129 L.ROOT-SERVERS.NET. 5w6d16h IN A 198.32.64.12 M.ROOT-SERVERS.NET. 5w6d16h IN A 202.12.27.33 A.ROOT-SERVERS.NET. 5w6d16h IN A 198.41.0.4 H.ROOT-SERVERS.NET. 5w6d16h IN A 128.63.2.53 B.ROOT-SERVERS.NET. 5w6d16h IN A 128.9.0.107 C.ROOT-SERVERS.NET. 5w6d16h IN A 192.33.4.12 D.ROOT-SERVERS.NET. 5w6d16h IN A 128.8.10.90 E.ROOT-SERVERS.NET. 5w6d16h IN A 192.203.230.10 I.ROOT-SERVERS.NET. 5w6d16h IN A 192.36.148.17 F.ROOT-SERVERS.NET. 5w6d16h IN A 192.5.5.241 ;; Total query time: 215 msec ;; FROM: roke.uio.no to SERVER: A.ROOT-SERVERS.NET. 198.41.0.4 ;; WHEN: Sun Feb 15 01:22:51 1998 ;; MSG SIZE sent: 17 rcvd: 436 EOF } # end roots.hints # create ipreverse sub crea_iprev { my ($ip, $d) = @_; my $iprev = get_spe_ip('iprev', $ip); my $ipend = get_spe_ip('ipend', $ip); output($WDIR . "/db." . $iprev . ".hosts", <{var}{IPMASTER} or return append_to_file($WDIR . "/resolv.conf", "nameserver $o->{var}{IPMASTER}\n"); !$o->{var}{ADDSEARCH} or return append_to_file($WDIR . "/resolv.conf", "search $o->{var}{ADDSEARCH}\n"); !$o->{var}{IPOFFORWARDER} or return append_to_file($WDIR . "/resolv.conf", "nameserver $o->{var}{IPOFFORWARDER}\n"); } # end set resolv.conf # set /etc/hosts sub set_hosts { my ($ip, $h) = @_; if (!any { /$ip\s* $h/ } cat_($WDIR . "/hosts")) { append_to_file($WDIR . "/hosts", <{var}{IPMASTER}; $ip or return 'slave'; my @fields = $ip =~ $ip_regexp or return 'error_ipm'; every { 0 <= $_ && $_ <= 255 } @fields or return 'error_ipm'; } sub check_ipf { my $ip = $o->{var}{IPOFFORWARDER}; $ip or return 'addsearch'; my @fields = $ip =~ $ip_regexp or return 'error_ipf'; every { 0 <= $_ && $_ <= 255 } @fields or return 'error_ipf'; } sub check_iph { my $ip = $o->{var}{CLIENTIP}; my @fields = $ip =~ $ip_regexp or return 'error_iph'; every { 0 <= $_ && $_ <= 255 } @fields or return 'error_iph'; } sub do_it { $::testing and return; foreach (qw(named.ca master reverse)) { cp_af("$WIZARD_SCRIPTS/$_", "$NAMED_DIR/$_") if !-e "$NAMED_DIR/$_"; } #my ($st) = @_; crea_wdir($WDIR); # create files crea_db_local(); crea_127(); crea_named_common(); # set host configuration if (-f $WDIR . '/hosts') { rm_rf($WDIR . '/hosts') } set_hosts('127.0.0.1', 'localhost.localdomain localhost'); set_hosts($IPSERVER, $HOSTNAME); } sub end_it { crea_hints(); crea_rndc(); # set configuration files on server set_resolv(); # check generated config file are good check_config(); # create backup save_old_config(); # copy in correct place copy_good(); reload_or_restart('named'); } sub do_it_master { return if $::testing; my $in = 'interactive'->vnew('su', 'dns'); check_starts_on_boot($in, 'named'); my $w = $in->wait_message(N("Master DNS server"), N("Configuring your system as Master DNS server ...")); output($sys_wizard_dns, "INTERFACE=$o->{var}{interface}\n"); do_it(); crea_iprev($IPSERVER, $DOMAINNAME); crea_ipnorm($IPSERVER, $DOMAINNAME); crea_named_master($IPSERVER, $DOMAINNAME); end_it(); undef $w; check_started('named'); } sub do_it_slave { return if $::testing; my $in = 'interactive'->vnew('su', 'dns'); check_starts_on_boot($in, 'named'); my $w = $in->wait_message(N("Slave DNS server"), N("Configuring your system as Slave DNS server ...")); if (-f $sys_wizard_dns) { unlink $sys_wizard_dns } do_it(); rm_rf(glob("$NAMED_DIR/bak*")); crea_named_slave($IPSERVER, $DOMAINNAME, $o->{var}{IPMASTER}); end_it(); undef $w; check_started('named'); } sub get_shortname { # sure someone can find a better method to do that my ($name) = @_; my @DT = split(/\./, $DOMAINNAME); my $NB = $#DT; if (any { /$DOMAINNAME$/x } $name) { my @shortname = split(/\./, $name); splice(@shortname, -$NB); my $shortn; foreach (@shortname) { $shortn or return $shortn = $_; !$shortn or return $shortn . "." . $_; } } else { return $name } } sub do_it_add { return if $::testing; test_srv(); my $iprev = get_spe_ip('iprev', $IPSERVER); my $ipend = get_spe_ip('ipend', $o->{var}{CLIENTIP}); my $SNAME = get_shortname($o->{var}{CLIENTNAME}); if (any { /$ipend\tIN/ } cat_("$ZONE_DIR/db.$iprev.hosts")) { return 'error_add'; } elsif (any { /$SNAME.$DOMAINNAME.$/ } cat_("$ZONE_DIR/db.$iprev.hosts")) { return 'error_add'; } else { append_to_file("$ZONE_DIR/db.$DOMAINNAME.hosts", "$SNAME.$DOMAINNAME.\tIN\tA\t$o->{var}{CLIENTIP}\n"); append_to_file("$ZONE_DIR/db.$iprev.hosts", "$ipend\tIN\tPTR\t$SNAME.$DOMAINNAME.\n"); } increment_serial($iprev); system("service named reload"); return 'endadd'; } sub do_it_remove { return if $::testing; test_srv(); my $iprev = get_spe_ip('iprev', $IPSERVER); my $NAME = $o->{var}{CLIENTNAME}; substInFile { s/^\b$NAME.\b.*//; s/^\s*$//; } "$ZONE_DIR/db.$DOMAINNAME.hosts"; substInFile { s/^\d+\tIN\tPTR\t$NAME.*//; s/^\s*$//; } "$ZONE_DIR/db.$iprev.hosts"; increment_serial($iprev); system("service named reload"); } #34 IN PTR xp2400.guibland.com. sub do_it_list { return if $::testing; my $iprev = get_spe_ip('iprev', $IPSERVER); my $db = "$ZONE_DIR/db.$iprev.hosts"; #my $ipnor = get_spe_ip('ipnor', $IPSERVER); my @hosts; my @ip; foreach (cat_($db)) { my ($ipend, $h) = /(\d{1,3})\tIN\tPTR\t(.*)/; if (!$h) { push @hosts, $h; push @ip, $ipend } } } sub new { my ($class) = @_; bless $o, $class; } 1;