\!/ KyuuKazami \!/

Path : /scripts/
Upload :
Current File : //scripts/setupnameserver

#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - scripts/setupnameserver                   Copyright 2019 cPanel, L.L.C.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use strict;
use Cpanel::Config::LoadCpConf         ();
use Cpanel::Config::CpConfGuard        ();
use Cpanel::ServerTasks                ();
use Cpanel::Chkservd::Manage           ();
use Cpanel::Chkservd::Tiny             ();
use Cpanel::RPM                        ();
use Cpanel::RPM::Versions::File        ();
use Cpanel::RPM::Versions::Directory   ();
use Cpanel::Services::Enabled          ();
use Cpanel::Services::Restart          ();
use Cpanel::Init                       ();
use Cpanel::NameServer::Utils::Enabled ();
use Cpanel::Usage                      ();
use Cpanel::PID                        ();
use Cpanel::Encoder::Tiny              ();

my $force   = 0;
my $current = 0;
my $html    = 0;

delete $ENV{'cp_security_token'};
delete $ENV{'HTTP_REFERER'};

# Argument processing
my %opts = (
    'force'   => \$force,
    'current' => \$current,
    'html'    => \$html,
);

Cpanel::Usage::wrap_options( \@ARGV, \&usage, \%opts );

@ARGV = ( grep( !/^--/, @ARGV ) );

my $dnstype = shift;
usage() unless ( $dnstype || $current );

if ( $> != 0 ) {
    die "Conversion process must be performed as root";
}

my $cpconf_ref = Cpanel::Config::LoadCpConf::loadcpconf_not_copy();

$cpconf_ref->{'local_nameserver_type'} ||= 'powerdns';
my $current_nameserver = Cpanel::Services::Enabled::is_enabled('dns') ? $cpconf_ref->{'local_nameserver_type'} : 'disabled';

if ($current) {
    print "Current nameserver type: $current_nameserver\n";
    exit 0;
}

if ( !$force && $current_nameserver eq $dnstype ) {
    print "Already configured.\n";
    exit 0;
}

my $valid_dnstypes = { map { $_ => 1 } qw(bind nsd mydns powerdns disabled) };
unless ( $valid_dnstypes->{$dnstype} ) {
    print "Unknown nameserver type specified.\nTry $0 --help\n";
    exit 1;
}

if ( !$force && $current_nameserver eq 'powerdns' && $dnstype ne 'powerdns' && -t STDIN ) {
    print "WARNING: If you switch your nameserver away from PowerDNS, your DNS server will no longer serve DNSSEC records.\n";
    print "You must ensure that the domains do not have DS records configured at their domain registrar.\n";
    print "Failure to do so will cause DNS resolution issues.\n\n";
    my $msg = qq{Are you sure you want to switch to "$dnstype" [y/n]? };
    print $msg;
    my $count = 0;
    while ( my $read = <STDIN> ) {
        $count++;
        exit if ( $count >= 5 );
        exit if ( $read =~ m/^n/i );
        last if ( $read =~ m/^y/i );
        print $msg;
    }
}

# Check if the target service is currently unmanaged
# WARNING: GLOBAL VARIABLE

if ( !$force && $dnstype =~ m/^(nsd|mydns|powerdns)$/ ) {
    my $dir = Cpanel::RPM::Versions::Directory->new( { 'dont_set_legacy' => 1 } );
    my $target_state = $dir->fetch( { 'section' => 'target_settings', 'key' => $dnstype } );
    if ( $target_state && $target_state =~ m/^(unmanaged|uninstalled)$/ ) {
        print "WARNING: You are attempting to switch to $dnstype, but it has explicitly been set to $target_state in /var/cpanel/rpm.versions.d/\n";

        print "This means its RPMs have been flagged to be left alone by cPanel. "   if ( $target_state eq 'unmanaged' );
        print "This means its RPMs have been explicitly blocked from installation. " if ( $target_state eq 'uninstalled' );

        print "If this was unintentional, you may be able to remove\nthis flag by running the following command and then re-running setupnameserver\n";
        print "\n    /usr/local/cpanel/scripts/update_local_rpm_versions --del target_settings.$dnstype\n\n";

        print "If you meant to do this, please re-run setupnameserver with --force\n\n";
        exit 2;
    }
}

my ( $valid, $reason ) = Cpanel::NameServer::Utils::Enabled::valid_nameserver_type($dnstype);
unless ($valid) {
    print "The specified nameserver type is not available for your system:\n";
    print $reason, "\n";
    exit 1;
}

my $pid_obj = Cpanel::PID->new( { 'pid_file' => '/var/run/setupnameserver.pid' } );
unless ( $pid_obj->create_pid_file() > 0 ) {
    print "Setupnameserver appears to be running already.\n";
    print "Please wait for the conversion to finish before attempting another.\n";
    exit 1;
}

# check bind rpm before any actions, as we are not going to autofix it, but le the admin take care of it
check_bind() if $dnstype eq 'bind';

{
    my $cpconf_guard = Cpanel::Config::CpConfGuard->new();
    print "Setting name server to $dnstype in /var/cpanel/cpanel.config\n";
    $cpconf_ref->{'local_nameserver_type'} = $cpconf_guard->{'data'}->{'local_nameserver_type'} = $dnstype;
    $cpconf_guard->save();
}

my $init = Cpanel::Init->new();

suspend_chksrvd_monitoring();

#branch to uninsall/install functions
if ( $dnstype eq 'nsd' ) {
    disable_none();
    disable_bind();
    disable_mydns();
    disable_powerdns();
    enable_nsd();
    enable_chksrvd_monitoring();
}
elsif ( $dnstype eq 'bind' ) {
    disable_none();
    disable_nsd();
    disable_mydns();
    disable_powerdns();
    enable_bind();
    enable_chksrvd_monitoring();
}
elsif ( $dnstype eq 'mydns' ) {
    disable_none();
    disable_nsd();
    disable_bind();
    disable_powerdns();
    enable_mydns();
    enable_chksrvd_monitoring();
}
elsif ( $dnstype eq 'powerdns' ) {
    disable_none();
    disable_nsd();
    disable_bind();
    disable_mydns();
    enable_powerdns();
    enable_chksrvd_monitoring();
}
else {
    disable_chksrvd_monitoring();
    enable_none();
    disable_bind();
    disable_nsd();
    disable_mydns();
    disable_powerdns();
    install_cpanel_rpms();
}

if ( $force && $dnstype && $dnstype ne 'disabled' && $dnstype ne 'bind' && !$ENV{'CPANEL_BASE_INSTALL'} ) {
    print "\nChecking status of installed RPMs for $dnstype\n";
    system( '/usr/local/cpanel/scripts/check_cpanel_rpms', '--fix', '--long-list', '--targets', $dnstype );
}

restart_dnsadmin();

# Rebuild global cache so that the 'is_dnssec_supported' value is updated
system '/usr/local/cpanel/bin/build_global_cache';

$pid_obj->remove_pid_file();

print "\nNameserver conversion complete\n";

exit 0;

sub disable_bind {
    print "\nHalting BIND\n";
    my $output = eval { $init->run_command( 'named', 'stop' ) };
    print "\nDisabling BIND in init system\n";
    $output = $init->run_command_for_one( 'disable', 'named' );
}

sub disable_nsd {
    return unless Cpanel::NameServer::Utils::Enabled::valid_nameserver_type('nsd');
    print "\nHalting NSD\n";

    # TODO: We should not fail
    my $output = eval { $init->run_command( 'nsd', 'stop' ) };

    print "\nDisabling NSD in init system\n";
    $output = $init->run_command_for_one( 'disable', 'nsd' );
    unlink('/var/cpanel/usensd') if ( -e '/var/cpanel/usensd' );
}

sub disable_mydns {
    return unless Cpanel::NameServer::Utils::Enabled::valid_nameserver_type('mydns');
    print "\nHalting MyDNS\n";
    my $output = eval { $init->run_command( 'mydns', 'stop' ) };
    print "\nDisabling MyDNS in init system\n";
    $output = $init->run_command_for_one( 'disable', 'mydns' );
    unlink('/var/cpanel/usemydns') if ( -e '/var/cpanel/usemydns' );
}

sub disable_powerdns {
    print "\nHalting PowerDNS\n";
    my $output = eval { $init->run_command( 'pdns', 'stop' ) };
    print "\nDisabling PowerDNS in init system\n";
    $output = $init->run_command_for_one( 'disable', 'pdns' );
    unlink('/var/cpanel/usepowerdns') if ( -e '/var/cpanel/usepowerdns' );
}

sub disable_none {
    unlink('/etc/nameddisable')    if ( -e '/etc/nameddisable' );
    unlink('/etc/binddisable')     if ( -e '/etc/binddisable' );
    unlink('/etc/nsddisable')      if ( -e '/etc/nsddisable' );
    unlink('/etc/mydnsdisable')    if ( -e '/etc/mydnsdisable' );
    unlink('/etc/powerdnsdisable') if ( -e '/etc/powerdnsdisable' );
}

# Strictly speaking, touching /etc/nameddisable will cause chksrvd to skip over
# restarting BIND or NSD, but this will stop it from even polling for these processes
sub disable_chksrvd_monitoring {
    my %monitored_services = Cpanel::Chkservd::Manage::getmonitored();
    return unless ( $monitored_services{'named'} );
    Cpanel::Chkservd::Manage::disable('named');
    local $@;
    eval { Cpanel::ServerTasks::queue_task( ['CpServicesTasks'], "restartsrv tailwatchd" ); };
    warn if $@;
    return;
}

sub check_bind {
    print "Checking that BIND is installed\n";

    my $rpm = Cpanel::RPM->new();
    my $q   = $rpm->qa('bind');
    if ( !$q || !$q->{'bind'} ) {
        print STDERR qq{Error: The 'bind' RPM is not installed on your system.\n} . qq{Run the 'yum install bind' command to install it before you run this command again.\n};
        exit 1;
    }

    return;
}

# dummy helper to use or not html
sub restartservice {
    my $service = shift or die;

    # We do not taskqueue here since we want bind up right away on fresh installs
    my @args = $html ? ( 1, 0, \&Cpanel::Encoder::Tiny::safe_html_encode_str ) : ();
    return Cpanel::Services::Restart::restartservice( $service, @args );
}

sub enable_bind {
    print "\nUninstalling unused nameservers\n";
    install_cpanel_rpms($force);

    print "\nEnabling the BIND service...\n";
    my $output = $init->run_command_for_one( 'enable', 'named' );

    # Setup rndc
    print "\nSetting up rndc configuration\n";
    my @args = $html ? ('--html') : ();
    system( '/usr/local/cpanel/scripts/fixrndc', '-f', '-v', @args );

    print "\nStarting BIND\n";
    $output = restartservice('named');
    return;
}

sub enable_nsd {
    print "\nChecking that NSD is installed\n";
    install_cpanel_rpms($force);

    system( 'touch', '/var/cpanel/usensd' );

    print "\nEnabling NSD in init system\n";
    my $output = $init->run_command_for_one( 'enable', 'nsd' );
    print "\nStarting NSD\n";
    $output = restartservice('named');
    return;
}

sub enable_mydns {
    print "\nChecking that MyDNS is installed\n";
    install_cpanel_rpms($force);

    system( 'touch', '/var/cpanel/usemydns' );

    print "\nEnabling MyDNS in init system\n";
    my $output = $init->run_command_for_one( 'enable', 'mydns' );

    print "\nStarting MyDNS\n";
    $output = restartservice('named');

    print "\nImporting zones into MyDNS\n";
    system( '/usr/local/cpanel/bin/servers_queue', '--plugin=MydnsTasks', 'queue', 'importmydnsdb' );
    print "A process has been started to import existing DNS zones into MyDNS database.\n";
    print "This may take several hours depending on the number of zones that you have.\n";
    print "An email notification will be sent when the import completes.\n";
    return;
}

sub enable_powerdns {
    print "\nChecking that PowerDNS is installed\n";
    install_cpanel_rpms($force);

    system( 'touch', '/var/cpanel/usepowerdns' );

    print "\nEnabling PowerDNS in init system\n";
    my $output = $init->run_command_for_one( 'enable', 'pdns' );

    print "\nStarting PowerDNS\n";
    $output = restartservice('named');
    return;
}

sub enable_none {
    system( 'touch', '/etc/nameddisable' );
    return;
}

# Both BIND and NSD are monitored by chksrvd using restartsrv_named
sub enable_chksrvd_monitoring {
    my %monitored_services = Cpanel::Chkservd::Manage::getmonitored();
    return if ( $monitored_services{'named'} );
    Cpanel::Chkservd::Manage::enable('named');
    local $@;
    eval { Cpanel::ServerTasks::queue_task( ['CpServicesTasks'], "restartsrv tailwatchd" ); };
    warn if $@;
    unsuspend_chksrvd_monitoring();
    return;
}

sub unsuspend_chksrvd_monitoring {
    return Cpanel::Chkservd::Tiny::resume_service('named');
}

sub suspend_chksrvd_monitoring {
    return Cpanel::Chkservd::Tiny::suspend_service( 'named', 600 );
}

sub install_cpanel_rpms {

    # This object should always be re-instantiated here changing targets means Cpanel::RPM::Versions::Directory is invalid prior to now
    my $versions = Cpanel::RPM::Versions::File->new( { 'only_targets' => [qw/nsd mydns powerdns/] } );

    print "\nCalling RPM installer object\n";
    $versions->stage();
    $versions->commit_changes();
    return;
}

sub restart_dnsadmin {
    if ( Cpanel::Services::Enabled::is_enabled('dnsadmin') ) {
        local $@;
        eval { Cpanel::ServerTasks::queue_task( ['CpServicesTasks'], "restartsrv dnsadmin" ); };
        warn if $@;
    }

    return;
}

sub usage {
    print <<EO_USAGE;
setupnameserver [options] [nameserver type]

    Options:
      --help       Brief help message
      --force      Rerun configuration routines even if the selected
                   nameserver type is already configured
      --current    Show the currently selected nameserver type


    Nameserver Types:
      powerdns     Suggested. High performance. Supports DNSSEC. Functions
                   only as authoritative nameserver.
      bind         Functions as both authoritative and caching nameserver.
      nsd          DEPRECATED. Lower memory. Functions only as authoritative
                   nameserver.
      mydns        DEPRECATED. Uses MySQL as backend. Functions only as
                   authoritative nameserver.
      disabled     Disable the local nameserver.
EO_USAGE
    exit 0;
}

@KyuuKazami