Edit File: swapip
#!/usr/local/cpanel/3rdparty/bin/perl package scripts::swapip; # cpanel - scripts/swapip Copyright 2017 cPanel, Inc. # All Rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited # use strict; use warnings; use Cpanel::BinCheck::Lite (); use Cpanel::LoadModule (); use Cpanel::JSON (); use Cpanel::NameserverCfg (); use Cpanel::DnsUtils::AskDnsAdmin (); use Cpanel::ZoneFile (); use Cpanel::Encoder::URI (); use Cpanel::Hostname (); use Cpanel::ResellerFunctions (); use Cpanel::DnsUtils::Stream (); use Cpanel::Proxy::Tiny (); use Cpanel::NAT (); use Cpanel::IP::Loopback (); Cpanel::BinCheck::Lite::check_argv(); ## if invoked as a script, there is nothing in the call stack my $invoked_as_script = !caller(); __PACKAGE__->script(@ARGV) if ($invoked_as_script); sub script { my ( $package, @args ) = @_; my ( $sourceip, $targetip, @DOMAINS, $zoneref, $showmsgs, $skipreload, $ftpip, $replaceip ); my $input_ref; if ( $args[0] eq '--json' ) { $input_ref = Cpanel::JSON::LoadFileNoSetUTF8( \*STDIN ); } elsif ( $args[0] eq '--storable' ) { Cpanel::LoadModule::load_perl_module('Cpanel::SafeStorable'); $input_ref = Cpanel::SafeStorable::fd_retrieve( \*STDIN ); } elsif ( ref $args[0] ) { $input_ref = $args[0]; } else { $sourceip = shift(@args); $targetip = shift(@args); $ftpip = shift(@args); @DOMAINS = @args; } if ($input_ref) { $sourceip = $input_ref->{'sourceip'}; $targetip = ( $input_ref->{'destip'} || $input_ref->{'targetip'} ); @DOMAINS = @{ $input_ref->{'domainref'} }; $zoneref = $input_ref->{'zoneref'}; $showmsgs = $input_ref->{'showmsgs'}; $skipreload = $input_ref->{'skipreload'}; $replaceip = $input_ref->{'replaceip'}; } if ( ref $sourceip eq 'ARRAY' ) { foreach my $ip (@$sourceip) { $ip = Cpanel::NAT::get_public_ip($ip); } } else { $sourceip = Cpanel::NAT::get_public_ip($sourceip); } $targetip = Cpanel::NAT::get_public_ip($targetip); $ftpip = Cpanel::NAT::get_public_ip($ftpip); if ( !$zoneref || !ref $zoneref ) { $zoneref = {}; } my %STATICDOMAINS; foreach my $res ( Cpanel::ResellerFunctions::getresellerslist() ) { foreach my $ns ( Cpanel::NameserverCfg::fetch($res) ) { if ($ns) { $STATICDOMAINS{ lc($ns) } = 1; } } } $STATICDOMAINS{ Cpanel::Hostname::gethostname() } = 1; if ( !@DOMAINS ) { print "Error: domains missing\n"; die_usage(); } if ( !$sourceip ) { print "Error: sourceip missing\n"; die_usage(); } if ( !$targetip ) { print "Error: targetip missing\n"; die_usage(); } return _changezones( \%STATICDOMAINS, \@DOMAINS, $sourceip, $targetip, $ftpip, $zoneref, $showmsgs, $skipreload, $replaceip ); } sub _changezones { ## no critic qw(Subroutines::ProhibitExcessComplexity Subroutines::ProhibitManyArgs) my $hr_STATICDOMAINS = shift; my $domainref = shift; my $sourceip = shift; ## TODO: rename? why is this called $destip here but $targetip above? my $destip = shift; my $ftpip = shift; my $zoneref = shift; my $showmsgs = shift || 0; my $skipreload = shift || 0; my $replaceip = shift || 'all'; my %sourceips; if ($sourceip) { if ( ref $sourceip ) { %sourceips = map { $_ => 1 } @{$sourceip}; } elsif ( $sourceip != -1 ) { $sourceips{$sourceip} = 1; } } my @fetchlist; foreach my $domain (@$domainref) { if ( !$zoneref->{$domain} ) { push @fetchlist, $domain; } } if (@fetchlist) { my $encoded_zones = Cpanel::DnsUtils::AskDnsAdmin::askdnsadmin( 'GETZONES', 0, join( ',', @fetchlist ) ); foreach my $zonedata ( split( /\&/, $encoded_zones ) ) { my ( $name, $value ) = split( /=/, $zonedata, 2 ); next if ( !$name ); $name =~ s/^cpdnszone-//g; my @ZONE = split( /\n/, Cpanel::Encoder::URI::uri_decode_str($value) ); $zoneref->{ Cpanel::Encoder::URI::uri_decode_str($name) } = \@ZONE; } } foreach my $zone ( keys %$zoneref ) { my $zf = Cpanel::ZoneFile->new( 'text' => $zoneref->{$zone}, 'domain' => $zone ); if ( $zf->{'status'} ) { } if ( !( scalar keys %sourceips ) ) { my @main_a_records = $zf->find_records( 'type' => 'A', 'name' => $zone . '.' ); if (@main_a_records) { next if Cpanel::IP::Loopback::is_loopback( $main_a_records[0]->{'address'} ); $sourceips{ $main_a_records[0]->{'address'} } = 1; } } delete $sourceips{''}; #safety my @arecords = $zf->find_records( 'type' => 'A' ); if ( !( scalar keys %sourceips ) ) { next if Cpanel::IP::Loopback::is_loopback( $arecords[0]->{'address'} ); $sourceips{ $arecords[0]->{'address'} } = 1; } for ( my $i = 0; $i <= $#arecords; $i++ ) { my $dnsname = $arecords[$i]->{'name'}; if ( $dnsname !~ /\.$/ ) { $dnsname .= '.' . $zone; } else { $dnsname =~ s/\.$//g; } if ( $ftpip && $dnsname =~ /^ftp\./ ) { $arecords[$i]->{'address'} = $ftpip; next; } elsif ( exists $hr_STATICDOMAINS->{ lc($dnsname) } ) { next; } # only replace if it is the zone name or a known proxy subdomain and currently has a source IP if ( _should_replace( $dnsname, $zone, $replaceip ) && exists $sourceips{ $arecords[$i]->{'address'} } ) { $arecords[$i]->{'address'} = $destip; } } $zf->replace_records( \@arecords ); my $zref = $zf->serialize(); $zoneref->{$zone} = $zref; } my $zdata; my @RELOADLIST; foreach my $zone ( keys %$zoneref ) { if ( !$zoneref->{$zone} ) { next(); } my $zonedata = join( "\n", @{ $zoneref->{$zone} } ); $zonedata =~ s/\n{4}/\n/g; if ( $zonedata eq '' ) { next(); } #we should just edit the soa? $zonedata = Cpanel::DnsUtils::Stream::upsrnumstream($zonedata); #increase serial number if ($showmsgs) { print "Changed $replaceip instances of [" . join( ',', keys %sourceips ) . "] -> [$destip] in $zone\n"; } push @RELOADLIST, $zone; $zdata .= 'cpdnszone-' . Cpanel::Encoder::URI::uri_encode_str($zone) . '=' . Cpanel::Encoder::URI::uri_encode_str($zonedata) . '&'; # CPANEL-10588: SAVEZONE is required in order to propagate changes for MyDNS DB. Cpanel::DnsUtils::AskDnsAdmin::askdnsadmin( 'SAVEZONE', 0, $zone, $zonedata ); } Cpanel::DnsUtils::AskDnsAdmin::askdnsadmin( 'SYNCZONES', 0, '', '', '', $zdata ); if ( !$skipreload ) { Cpanel::DnsUtils::AskDnsAdmin::askdnsadmin( 'RELOADZONES', 0, join( ',', @RELOADLIST ) ); } return; } my $cpzones; sub _should_replace { my ( $dnsname, $zone, $replaceip ) = @_; if ( $replaceip eq 'all' ) { return 1; } elsif ( $replaceip eq 'basic' ) { my @pieces = split( /\./, $dnsname ); if ( !defined $cpzones ) { my $proxies = Cpanel::Proxy::Tiny::get_known_proxy_subdomains( { force_autodiscover_support => 1 } ); # we also need to check for the mail and ftp record which can be A records $cpzones = { map ( { $_ => 1 } keys %$proxies ), 'mail' => 1, 'ftp' => 1 }; } # $dnsname and $zone both lack trailing dot, so don't add it when comparing return 1 if $dnsname eq $zone || $dnsname eq '@' || scalar @pieces > 2 && $cpzones->{ $pieces[0] }; return; } print "Invalid replaceip value ($replaceip); assuming 'all' ...\n"; return 1; } sub die_usage { die "Usage: $0 sourceip destip ftpip domains...\n"; } 1;