I would like to become self-sufficient, and therefore do without services such as DNSDynamic and DYNDNS. And I don't like paying for services that I could do myself.
So here's the scenario - I have my main website hosted with a hosting company. I also have a home server with my music, etc. on it. But the problem is that my ISP (BT) does not offer consumers Static IP addresses.
I would like to have a subdomain of my main domain (which points to my main host) point to my home IP. This is done with a simple A record - which I have done myself.
This boils down to me wanting to make a PHP script (run by a cron job on my home server) to update the A records in cPanel to the current IP of my home server.
Here's some sample code - of course the bit that's missing is the API Code to communicate with cPanel, and I'd be very thankful to anyone who could fill in the gaps for me.
<?php
$current_ip = file_get_contents("http://mydomain.com/getip.php");
$username = "CPANEL_USERNAME";
$password = "CPANEL_PASSWORD";
$domain = "CPANEL_DOMAIN";
$request = file_get_contents("http://someapipage?username=".$username."&pw=".$password."&domain=".$domain."&ip=".$current_ip);
?>
The code in http://mydomain.com/getip.php
is something along the lines of simply
<?php echo $_SERVER["REMOTE_ADDR"]; ?>
I already have the grasp of how to set up a cron job, as my home server is running Ubuntu, and I have read tutorials that call my cron.php
in my localhost directory using wget
.
I have tried this link but I couldn't fathom what he was doing. Thanks in advance.
I have just written this library for cPanel's JSON-API based on their documentation and the jordih.net links to that documentation. It's not so well documented, but the gist of it is:
Create a zone_records object by calling
$zones = new zone_records("cpaneluser", "pass", "website_to_login", "domain_of_records")
Note that the website to login is usually 127.0.0.1 if you are running this from the server you want to change the records on.
Once called, you can then access a member $zones->DNSrecords
. This contains an array of the DNS A records and CNAME records (both of which are DNSrecord objects). The others (except TXT) are irrelevant since you cannot edit them without extra additions (functions) to the classes.
Each DNSrecord has some members (e.g target, ttl, name, type) which, while private
, can be accessed via $record->ttl
because I have added the "magic" __get
method. The "magic" __set
method is implemented to change the ttl and target only (you cannot change other properties using this API function, and the object will raise an exception if you try to do so).
You can use
$zones->addrecord($type, $target, $name, $ttl)
to add a record, or
$zones->deleterecord($line)
to delete the record that is on line $line
in the zone file - you can find it via $record->line
.
If you want to make some of your own queries in the ZoneEdit module, you can call
$zones->doquery("function_from_API", array("parameters=>"here"), array("headers"=>"here"))
and it will return the cPanel response (as will the addrecord and deleterecord methods). Finally, I would advise you to use try {...} catch (Exception $e) {...}
since my objects throw exceptions if/when something goes wrong (you can edit them out of course).
This code is in the public domain - you can get it at https://github.com/ShadowLNC/cpanel_dns (classdns.php is the main file, and dns_update.php shows an example).
Edit: The link below is now broken. Here is the entire script for reference:
#!/usr/bin/perl
# -------------------------------------------------------------------------------
# neobitti_update_ip.pl
#
# Version 1.0 - 16.01.2012
#
# 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 Data::Dumper;
# --- 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 = "example.com";
my $user = "username";
my $pass = "password";
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="";
print $response->content . "\n";
print $zone . "\n";
print $zone->{'data'}->{'status'} . "\n";
print $zone->{'data'}->{'record'} . "\n";
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'};
print $name;
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);
}
# --- Main procedure ---------------------------------------------------------
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...\n";
my $result=setip ($param_domain,$line,$param_ip);
if ($result eq "1") {
print "Update successful!\n";
} else {
print "$result\n";
}
} else {
print "Error - check domain and hostname!\n";
}
You need to replace "username" and "password" with your own credentials.
Run the script and pass in the domain name, the subdomain name and the new IP of the subdomain:
./neobitti_update_ip.pl <domain> <subdomain> <ip>
Example:
./neobitti_update_ip.pl example.com subdomain.example.com 93.184.216.34
Below is my original answer with a link to the source:
There is a Perl-script doing the exact thing you are requesting here:
http://stefan.gofferje.net/it-stuff/scripts/50-dynamic-dns-update-via-cpanel-api
If it has to be PHP I suppose it should be fairly simple to translate from Perl to PHP (or any other language). The author of the script has posted some background information here.
There is a bash script for linux to solve the original question asked, namely how to automate the update of an web address to point to a changing IP. The well known and well working ddclient is the standard tool for many providers, but unfortunately not (yet) working for cPanel driven providers (I use serverprofis.de but there is a long list of others).
The bash script is found at github and I have just edited in the code the credentials etc. as found on cPanel and then added a cron job to execute it every few minutes. The script checks, like ddclient, whether the IP has changed and stops if no change is detected compared to the last update of the provider. If the IP has changed, it changes the record at the provider to point to the new IP.
It works well so far.