#!/usr/local/cpanel/3rdparty/bin/perl

#                                      Copyright 2026 WebPros International, LLC
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited.

package scripts::fix_listen_on_localhost;

use cPstrict;

use Cpanel::Usage                      ();
use Cpanel::Update::Logger             ();
use Cpanel::NameServer::Utils::BIND    ();
use Cpanel::Transaction::File::Raw     ();
use Cpanel::DnsUtils::RNDCQueue::Adder ();
use Cpanel::ServerTasks                ();

exit( run(@ARGV) // 0 ) unless caller;

sub run (@argv) {

    # not tidy
    my $force   = 0;
    my $verbose = 0;
    my $debug   = 0;

    # default value comes from Cpanel::NameServer::Utils::BIND
    my $file = Cpanel::NameServer::Utils::BIND::find_namedconf();

    my %opts = (
        'force'   => \$force,
        'verbose' => \$verbose,
        'debug'   => \$debug,
        'file'    => \$file,
    );

    # ==== init process options

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

    # set logger ( default level to fatal )
    my $level = $verbose ? 'info' : 'fatal';
    $level = 'debug' if $debug;
    my $logger = Cpanel::Update::Logger->new( { 'stdout' => 1, 'log_level' => $level } );

    # ==== main code start here

    my @files = ($file);

    # add extra file if chroot is enabled
    my ($chrootdir) = Cpanel::NameServer::Utils::BIND::find_chrootbinddir();
    push @files, $chrootdir . $file
      if $chrootdir && $chrootdir ne '';

    # fix for each file
    my $need_to_reload;
    foreach my $f (@files) {
        $need_to_reload ||= fix_file( $logger, file => $f, force => $force );
    }

    # reload service if possible
    if ($need_to_reload) {
        Cpanel::DnsUtils::RNDCQueue::Adder->add("reload");
        Cpanel::ServerTasks::schedule_task( ['BINDTasks'], 1, 'rndc_queue' );
    }

    return 0;
}

### helpers

sub fix_file ( $logger, %opts ) {
    $logger->info("file candidate : $opts{file}");

    eval { _check_prerequire( $logger, \%opts ); };
    return if $@;
    $logger->info("file $opts{file} will be fixed");

    my $tx           = Cpanel::Transaction::File::Raw->new( path => $opts{file}, restore_original_permissions => 1 );
    my $contents_ref = $tx->get_data();
    $$contents_ref =~ s{^\s*listen-on[^;]*\{\s*127\.0\.0\.1;\s*\};\s*$}{    listen-on { any; }; /*      updated by cPanel*/\n}mg;
    $tx->save_and_close_or_die();

    return 1;
}

sub _check_prerequire ( $logger, $opts ) {
    my $file = $opts->{file};

    # do not apply force on these set of prerequires
    _die_and_next( $logger, "No file defined" )            unless defined $file;
    _die_and_next( $logger, "File '$file' doesn't exist" ) unless -e $file;

    # these ones can be disabled using force option
    return if $opts->{force};

    # basic prerequire to avoid to touch file if not needed
    system("grep listen-on $file | grep -v '//' | grep 127.0.0.1 >/dev/null");
    _die_and_next( $logger, "cannot find listen-on 127.0.0.1" ) unless $? == 0;

    return;
}

sub _die_and_next ( $logger, $msg = 'Unknown error' ) {
    $logger->error($msg);
    die($msg);
}

sub usage {
    my $prog = $0;
    $prog =~ s{^.+/(.+)$}{$1};
    print <<EOF;
$prog [options] [ -f FILE ]

This script will convert the default listen-on entry in named.conf from
    listen-on { 127.0.0.1; };
to a more widely entry
    listen-on { any; };

Only works on IPv4.

Parameters:
    --file=FILE, -f FILE - you can specify a FILE default one will be /etc/namedb/named.conf

Modifiers Flags:

    --force - will update the file without checking any conditions
    --verbose - display some friendly verbose messages
    --debug - display more messages
    --help - dislay this help message and exit
EOF
    exit;    ## no critic (Cpanel::NoExitsFromSubroutines) - usage() terminates the script after printing help
}

1;
