Automating Cpanel DNS Updates

Written by:

Automating Cpanel DNS Updates
  • 0.00 / 5 5
0 votes, 0.00 avg. rating (0% score)

I was using DNS services like DynDNS until one day the domain I was using was no longer offered. I realized that instead of using someone elses DNS services and domain names I should be using one of my hosted Cpanel domains.

I found this perl script written by Stefan Gofferje that can allow you to dynamically update a Cpanel DNS record. Good work Stefan. That script is below but I had made a few additions to it to suit my needs. My home network consists of cable internet which goes to a juniper firewall then goes to a linux gateway that provides my internal network with NATing and other services. The changes made allowed the script to find out what my current IP address is and use that to update my DNS record.

I cron the below script on my linux box to run once an hour so if my cable provider changes my DHCP assigned address, this script will automagically update my Cpanel DNS record. This is awesome so that when I am out of town I can always find and ssh into my home network.

Here is the code:

#!/usr/bin/perl
# -------------------------------------------------------------------------------
# cpanel-dns-update.pl
# original name: neobitti_update_ip.pl
#
# Version 1.0 - 16.01.2012
# Version 1.1 - 14.01.2014
#
# PERL script to dynamically update the IP of a host via the cPanel-API. This
# script was written to work with the Finnish hoster Neobitti but it might work
# with other hosters which use cPanel too.
#
# Copyright (C) 2012 Stefan Gofferje - http://stefan.gofferje.net/
#
# 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 of the
# License, 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
# -------------------------------------------------------------------------------
use strict;
use LWP::UserAgent;
use MIME::Base64;
use XML::Simple;
use LWP::Simple;
use Data::Dumper;
use Socket;
# --- Command line parameters ------------------------------------------------
my $param_domain=$ARGV[0];
my $param_host=$ARGV[1];
my $param_ip=$ARGV[2];
# --- cPanel information -----------------------------------------------------
# Storing passwords in clear text is ugly!
my $cpanel_domain = "yourdomain.com";
my $user = "yourcpaneluser";
my $pass = "yourcpanelpassword";
my $auth = "Basic " . MIME::Base64::encode( $user . ":" . $pass );
# --- Deactivate SSL certificate validation ----------------------------------
# This is ugly but neccessary because Neobitti uses self-signed SSL
# certificates which will fail validation
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
# --- Find out the linenumber for the A-record we want to change -------------
sub getlinenumber_a {
  my $domain=$_[0];
  my $hostname=$_[1].".";
  my $xml = new XML::Simple;
  my $request = HTTP::Request->new( GET => "https://$cpanel_domain:2083/xml-api/cpanel?cpanel_xmlapi_module=ZoneEdit&cpanel_xmlapi_func=fetchzone&domain=$domain" );
  $request->header( Authorization => $auth );
  my $response = $ua->request($request);

  my $zone = $xml->XMLin($response->content);
  my $linenumber="";
  if ($zone->{'data'}->{'status'} eq "1") {
    my $count = @{$zone->{'data'}->{'record'}};
    my $oldip="";
    for (my $item=0;$item<=$count;$item++) {         my $name=$zone->{'data'}->{'record'}[$item]->{'name'};
        my $type=$zone->{'data'}->{'record'}[$item]->{'type'};

        if ( ($name eq $hostname) && ($type eq "A") ) {
          $linenumber=$zone->{'data'}->{'record'}[$item]->{'Line'};
          $oldip=$zone->{'data'}->{'record'}[$item]->{'record'};
          print "Found $hostname in line $linenumber with IP $oldip.\n"; # DEBUG
        }
    }
  } else {
    $linenumber="0";
    print $zone->{'event'}->{'data'}->{'statusmsg;'}
  }
  return($linenumber);
}
# --- Change the IP address record for a certain linenumber ------------------
sub setip {

  my $domain=$_[0];
  my $linenumber=$_[1];
  my $newip=$_[2];
  my $result="";
  my $xml = new XML::Simple;
  my $request = HTTP::Request->new( GET => "https://$cpanel_domain:2083/xml-api/cpanel?cpanel_xmlapi_module=ZoneEdit&cpanel_xmlapi_func=edit_zone_record&domain=$domain&line=$linenumber&address=$newip" );
  $request->header( Authorization => $auth );
  my $response = $ua->request($request);

  my $reply = $xml->XMLin($response->content);
  if ($reply->{'data'}->{'status'} eq "1") {
    $result="1";
  } else {
    $result=$reply->{'data'}->{'statusmsg'};
  }
  return($result);
}

sub usage() {
	print "Usage: $0  <target_domain> [target_IP]\n";
	exit(1);
}

# --- Main procedure ---------------------------------------------------------
if ($#ARGV < 1) {
	usage();
}

my $rmturl = "http://ifconfig.me/ip";
if ($param_ip eq "") {
	# no ip passed in so update using our current internet IP address
	$param_ip = get($rmturl);
	chomp($param_ip);
}
if ($param_ip eq "") {
 print "No target IP address defined.";
 exit(1);
}

# Get the current IP of our target host
my $packed = gethostbyname($param_host) or die "Couldn't resolve address for $param_host: $!\n";
my $address = inet_ntoa($packed);
if ($param_ip eq $address) {
	print "No Change: Success!\n";
	exit(0);
}

print "Trying to find the linenumber for $param_host in $param_domain...\n";
my $line=getlinenumber_a($param_domain,$param_host);
if ( ($line ne "0") && ($line ne "") ) {
  print "Trying to update IP to $param_ip ...\n";
  my $result=setip ($param_domain,$line,$param_ip);
  if ($result eq "1") {
    print "Update successful!\n";
	exit(0);
  } else {
    print "$result\n";
  }
} else {
  print "Error - check domain and hostname!\n";
}
exit(1);

Leave a Reply