easydnsupdate:
#!/usr/bin/perl -w
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Script to update a dynamic IP with easydns service on any Unix based system with Perl.
# (BSD, Mac OS X, Linux and others). Tested on OS X, OpenBSD 3.1 and GNU/Linux 2.4.
# You need to configure the username, password, domain in /usr/local/etc/easydnsupdate.conf
# to make it work for you. Make sure, the file is not world-readable!
#
# You might need to install some Perl modules. The best way to do it is to use the CPAN
# module:
# perl -MCPAN -e shell
#
# and type install Bundle::libnet (for LWP) and install Crypt::SSLeay (for SSL support)
# if you want to use secure connections.
#
# Mac OS X users: be careful when installing LWP (libnet) because it installs a HEAD program
# that could overwrite the head program in /usr/bin/. Make sure your configuration
# is such that HEAD will get installed in /usr/local/bin/. The HFS file system
# is not case sensitive. If you overwrite head by mistake, take a fresh one from the install CD.
#
# OpenBSD users: you need to install p5-Crypt-SSLeay and p5-libwww, both found as prepared
# packages.
#
# Typically, you would copy this script to /usr/local/bin/easydnsupdate
# and add these lines to /etc/ppp/ip-up :
#
# -- cut here
# # Parameters are passed to ip-up (see man pppd).
# # We get the ip address
# ip=$4
#
# logger -i -t $0 "Updating IP with EasyDNS.com"
# /usr/local/bin/easydnsupdate $ip
# -- cut here
#
# If ip-up does not exist (it is a file that gets called automatically when your connection
# comes up) then create it. It must be executable and start with #!/bin/sh
#
# There is "some" protection to avoid updating the address if it has not changed
# since last time it was updated. It is safe to call easydnsupdate from the command-line
# as long you you pass the interface and ip address. If you don't pass the IP address,
# it will try to get it.
#
# If you have problems, everything is logged into /var/log/system.log (may differ, depending on
# your syslog configuration)
#
# Contact: Daniel Cote mailto:dccote@novajo.ca
# http://www.novajo.ca/easydns.html
# Feel free to use and abuse. Just leave my name somewhere in the header.
#
# Modifications for 1.2 done by Steffen Beyer, cpunk@reactor.de
# Principle of operation
#
# 1) Process options and arguments
# 2) Read configuration file and keep an array of hash references
# of all domains with their relevant information
# 3) Check if update required
# 4) Go through all domains form configuration file
# 4.1) Build URL for each specific service
# 4.2) "Call" URL
# 4.3) Check result based on service
# 4.4) Log meaningful message through system facilities
# History:
# Version Date Changes
# -------------------------------------------------------------------------------------------
# 1.0 05/20/2002 initial release
# 1.1 06/22/2002 user-agent is beeing transmitted as required by the DynDNS
# specification. / if the IP is specified on the command line,
# the interface can be left out (wasn't used anyway). / bugfix:
# no update is tried if an unknown interface is specified. /
# added simple HTTP error handling. / minor changes.
# (Steffen Beyer, cpunk@reactor.de)
# 1.2 06/24/2002 bugfix: script died without libwww installed. / ifconfig
# based IP detection now works under GNU/Linux. / bugfix:
# negative response from server did not prevent ipaddr file
# from beeing updated. / minor changes.
# (Steffen Beyer, cpunk@reactor.de)
# 1.3 08/04/2003 the preferred fetching method is saved
# in a file ~/easydnsudpate
use strict;
use Sys::Syslog qw(:DEFAULT setlogsock);
use vars qw ($opt_f $opt_c);
use Getopt::Std;
# Global variable that could be changed
# this file (which is not critical)
# keeps the IP address when the last
# update was performed to avoid abuse
my $ipfile = "/tmp/easydns_ipaddr";
# set to one of the following to avoid a directory scan on each run:
# "LWP" - use perl's libwww interface
# "curl" - use 'curl' software found in path
# "wget" - use 'wget' software found in path
my $fetchURLmethod = "";
my $myname = "easydnsupdate";
my $version = "1.3";
#
# Read parameters passed via command-line
#
my $ip;
my $configfile = "/usr/local/etc/easydnsupdate.conf";
my $forceupdate;
my $usage = "Usage: $myname [-f] [-c configfile] interface|ip_address\n"
."-f forces the update no matter what\n"
."-c provides a configuration file other than $configfile\n"
."interface is the network interface (ppp0, en0, tun0, etc...)\n"
."ip_address is the ip address you want to use for the update. If not provided, the script will try to guess using 'ifconfig interface'\n";
# Perl standard function to process options passed through command-line
# They are removed from @ARGV
if (! getopts('fc:')) {
die "$usage";
}
if ($opt_c) {
# Overrides configuration file
$configfile = $opt_c;
}
if ($opt_f) {
# Force update even if IP has not changed
$forceupdate = 1;
}
if (scalar @ARGV == 1) {
my $reg_decbyte = '([01]?\d{1,2}|2[0-4]\d|25[0-5])';
if ($ARGV[0] =~ /^$reg_decbyte\.$reg_decbyte\.$reg_decbyte\.$reg_decbyte$/) {
# argument is a v4 IP (thanks to Alan Derk for the regex)
$ip = $ARGV[0];
} else {
# else assume interface device
$ip = `/sbin/ifconfig $ARGV[0] 2> /dev/null | grep inet | head -1 | awk '{ print \$2 }'` || die "ifconfig returned error.\n";
# GNU/Linux specific
if ($ip =~ /:/) { $ip = $' };
chomp($ip);
}
} elsif (scalar @ARGV == 2) {
# old argument format
$ip = $ARGV[1];
} else {
die "$usage";
}
if (-e "$ENV{HOME}/.easydnsudpate") {
$fetchURLmethod = `cat $ENV{HOME}/.easydnsudpate`;
}
# Read configuration file
my @domainlist;
unless (open CONFIGFILE, $configfile) {
die "Cannot open config file. Make sure you have read permission on the file $configfile.\n";
}
while () {
if (m|^#|) {
next;
} elsif (m|(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S*)\s+(\S*)\s+(\S*)\s+(\S*)|i) {
my $entryref = {"service" => $1,
"username" => $2,
"password" => $3,
"domain" => $4,
"mx" => $5,
"backmx" => $6,
"wildcard" => $7,
"secure" => $8,
};
if ( ! ($entryref->{mx} =~ m|\S+\.\S+|i) ) {
$entryref->{mx} = 0;
}
if ($entryref->{backmx} =~ /y(es)?|t(rue)?|on|1/i) {
$entryref->{backmx} = 1;
} else {
$entryref->{backmx} = 0;
}
if ($entryref->{wildcard} =~ /y(es)?|t(rue)?|on|1/i) {
$entryref->{wildcard} = 1;
} else {
$entryref->{wildcard} = 0;
}
if ($entryref->{secure} =~ /y(es)?|t(rue)?|on|1/i) {
$entryref->{secure} = 1;
} else {
$entryref->{secure} = 0;
}
push @domainlist, $entryref;
}
}
close CONFIGFILE;
# Check if IP address has changed
if (! $forceupdate) {
my $ipatlastupdate = "";
if (-e "$ipfile") {
$ipatlastupdate = `cat $ipfile`;
chomp($ipatlastupdate);
} else {
`touch $ipfile;chmod oug+r $ipfile`;
}
if ($ip eq $ipatlastupdate) {
Logger("No update needed since IP has not changed.");
exit;
}
}
# Update domains
my $entryref;
foreach $entryref (@domainlist) {
my $url = URLForService($entryref);
if ($url ne "invalid service") {
my $response = FetchURL($url);
AnalyzeResult($response, $entryref);
`echo -n "$ip" > "$ipfile"`;
}
}
# We are done. Below are subroutines used for clarity.
sub URLForService
{
my ($domainref) = @_;
my $service = $domainref->{service};
my $username = $domainref->{username};
my $password = $domainref->{password};
my $domain = $domainref->{domain};
my $secure = $domainref->{secure};
my $url;
# If you want to add a new service, do that here. Email me your changes at dccote@novajo.ca
# Also modify AnalyzeResult() for returned text;
if ($service =~m|easydns|i) {
$url = "$username:$password\@members.easydns.com/dyn/dyndns.php?hostname=$domain&myip=$ip";
$url .= "&mx=".$domainref->{mx} if ($domainref->{mx} != 0);
$url .= "&backmx=YES" if ($domainref->{backmx} != 0);
$url .= "&wildcard=ON" if ($domainref->{wildcard} != 0);
} elsif ($service =~m|customdyndns|i) {
$url = "$username:$password\@members.dyndns.org/nic/update?system=custom&hostname=$domain&myip=$ip";
$url .= "&mx=".$domainref->{mx} if ($domainref->{mx} != 0);
$url .= "&backmx=YES" if ($domainref->{backmx} != 0);
$url .= "&wildcard=ON" if ($domainref->{wildcard} != 0);
} elsif ($service =~m|statdyndns|i) {
$url = "$username:$password\@members.dyndns.org/nic/update?system=statdns&hostname=$domain&myip=$ip";
$url .= "&mx=".$domainref->{mx} if ($domainref->{mx} != 0);
$url .= "&backmx=YES" if ($domainref->{backmx} != 0);
$url .= "&wildcard=ON" if ($domainref->{wildcard} != 0);
} elsif ($service =~m|dyndns|i) {
$url = "$username:$password\@members.dyndns.org/nic/update?system=dyndns&hostname=$domain&myip=$ip";
$url .= "&mx=".$domainref->{mx} if ($domainref->{mx} != 0);
$url .= "&backmx=YES" if ($domainref->{backmx} != 0);
$url .= "&wildcard=ON" if ($domainref->{wildcard} != 0);
} else {
$url = "invalid service";
Logger("Invalid service: $service. Check config file $configfile.","err");
}
if ($secure) {
$url = "https://".$url;
} else {
$url = "http://".$url;
}
return $url;
}
sub AnalyzeResult {
my ($text, $entryref) = @_;
my $service = $entryref->{service};
my $message = "Domain: ".$entryref->{domain}." at $service";
my $success = 0;
# If you want to add a new service, add error management here. Email me your changes at dccote@novajo.ca
# Also modify URLForService() to build url;
if ($service =~m|easydns|i) {
if ($text =~ m|NOERROR|i) {
$message .= " updated to point to $ip";
$success = 1;
} elsif ($text =~ m|NOACCESS|i) {
$message .= " authentication failed. Check username and password.";
} elsif ($text =~ m|ILLEGAL INPUT|i) {
$message .= " error in client. Contact dccote\@novajo.ca.";
} elsif ($text =~ m|NOSERVICE|i) {
$message .= " dynamic DNS is not turned on for this domain. Go to main easydns web site to activate.";
} elsif ($text =~ m|TOOSOON|i) {
$message .= " cannot be updated right now (not enough time since last update).";
} else {
$message .= " generated this unknown error: $text. Please contact dccote\@novajo.ca.";
}
} elsif ($service =~m|dyndns|i) {
if ($text =~ m|badauth|i) {
$message .= " authentication failed. Check username and password.";
} elsif ($text =~ m|badsys|i) {
$message .= " error in client. Contact dccote\@novajo.ca.";
} elsif ($text =~ m|badagent|i) {
$message .= " updating agent has been blocked. Contact dccote\@novajo.ca.";
} elsif ($text =~ m|good|i) {
$message .= " updated to point to $ip";
$success = 1;
} elsif ($text =~ m|nochg|i) {
$message .= " unnecessarily updated to point to $ip. Avoid using the -f option.";
$success = 1;
} elsif ($text =~ m|notfqdn|i) {
$message .= ". A fully qualified domain was not provided.";
} elsif ($text =~ m|nohost|i) {
$message .= " does not refer to an existing hostname";
} elsif ($text =~ m|!donator|i) {
$message .= " cannot be set offline since your are not a donator";
} elsif ($text =~ m|!yours|i) {
$message .= " is not yours and was not updated.";
} elsif ($text =~ m|!active|i) {
$message .= " is not active. Go to main dydns.org web site to activate.";
} elsif ($text =~ m|abuse|i) {
$message .= " has been blocked for abuse. Contact DynDNS.org to be unblocked.";
} elsif ($text =~ m|numhost|i) {
$message .= " has generated an error at dydns. You should contact support\@dyndns.org.";
} elsif ($text =~ m|dnserr|i) {
$message .= " has generated an error at dydns. You should contact support\@dyndns.org.";
} elsif ($text =~ m|w(\d+)h|i) {
$message .= " cannot be updated for another $1 hour(s) and will not be updated now.";
} elsif ($text =~ m|w(\d+)m|i) {
$message .= " cannot be updated for another $1 minute(s) and will not be updated now.";
} elsif ($text =~ m|w(\d+)s|i) {
my $sleeptime = $1;
sleep($sleeptime);
$message .= " had to wait $sleeptime second(s) before being updated to point to $ip";
} elsif ($text =~ /911|999/i) {
$message .= ". Things are not working well at dyndns.org right now: $text";
} else {
$message .= " generated this unknown error: $text. Please contact dccote\@novajo.ca.";
}
}
Logger($message);
exit 1 if (!$success);
}
sub FetchURL {
my ($url) = @_;
# The first time, we will have to determine how to fetch the URL
if (! $fetchURLmethod) {
my @PATH = split /:/, $ENV{PATH};
my $hasLWP = `find @INC -name 'LWP.pm' -print 2>/dev/null`;
my $hasWget = `find @PATH -name 'wget' -print 2>/dev/null`;
my $hasCurl = `find @PATH -name 'curl' -print 2>/dev/null`;
if ($hasLWP) {
$fetchURLmethod = "LWP";
} elsif ($hasCurl) {
$fetchURLmethod = "curl";
} elsif ($hasWget) {
$fetchURLmethod = "wget";
} else {
die "Unable to fetch URL: this system does not have one of the following: LWP, curl or wget. You must install one.\n";
}
`echo -n $fetchURLmethod >> $ENV{HOME}/.easydnsudpate`;
}
my $returnedtext;
my $failure = "HTTP request to server failed.";
my $fetchURLcommand;
if ($fetchURLmethod =~ /curl/i) { $fetchURLcommand = "curl -s -A $myname/$version-curl"; }
if ($fetchURLmethod =~ /wget/i) { $fetchURLcommand = "wget -qnv -O- -U $myname/$version-wget"; }
if ($fetchURLmethod =~ /LWP/i) {
require LWP;
my $ua = LWP::UserAgent->new(agent => "$myname/$version-LWP");
my $req = HTTP::Request->new(GET => "$url");
my $res = $ua->request($req);
if ($res->is_error()) {
Logger($failure,"err");
exit 1;
}
$returnedtext = $res->content();
} else {
$returnedtext = `$fetchURLcommand "$url"`;
if ($?) {
Logger($failure,"err");
exit 1;
}
}
return $returnedtext;
}
sub Logger {
my ($message, $level) = @_;
$level = 'notice' unless $level;
setlogsock('unix');
openlog("$myname",'','user');
syslog($level,$message);
closelog();
}
|