#!/usr/bin/perl
#
# tomz.pl-DDNS>Dynamic Domain Name Server Updater
#
# chkconfig: 2345 99 99
# description: Dynamic Domain Name Server Updater
#
### BEGIN INIT INFO
# Provides:          Dynamic Domain Name Server Updater
# Required-Start:    $local_fs
# Required-Stop:     $local_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Dynamic Domain Name Server Updater
# Description:       Dynamic Domain Name Server Updater
### END INIT INFO
use warnings;
use Switch;
use Proc::Daemon;
use Proc::PID::File;
use Cwd 'abs_path';
use File::Basename;
use Socket;
require 'sys/ioctl.ph';

# Define script path and name  
$scriptLocation = abs_path($0);   
$scriptName = basename($0);

# PID file - do not change
$pid_file = "/var/run/$scriptName.pid";

# Sleep Between first and next DDNS updates 
$sleepFor = 5;

# Define DDNS Records To be updated - FQDN !  
@DDNS_records = ('vpn.dozynkowa.tomz.pl.', 'dozynkowa.tomz.pl.', 'hosting.tomz.pl.', 'tomz.pl.');

# Define DDNS Servers - one DDNS record to one DNS Server   
@DDNS_servers = ('ns1.tomz.pl');

# Interface IP to update   
@DDNS_interface = ('ppp0', 'ppp1', 'ppp2', 'ppp2');

# DNS keys path  
@DDNS_keys = ('/srv/bind/keys/ddns/Kdozynkowa-ddns.+157+38053.private');

# Run in loop(1), or run once (0)
$loopRun = 1;

# Sleep between loop iterations
$loopSleep = 300;

# http://snipplr.com/view/46170/
sub get_interface_address
  {
  	my ($iface) = @_;
  	my $socket;
  	socket($socket, PF_INET, SOCK_STREAM, (getprotobyname('tcp'))[2]) || die "unable to create a socket: $!\n";
  	my $buf = pack('a256', $iface);
  	if (ioctl($socket, SIOCGIFADDR(), $buf) && (my @address = unpack('x20 C4', $buf)))
  	{
  		return join('.', @address);
  	}
  	return undef;
  }


# Daemonize
sub daemonize()
  {

    if ( -e "$pid_file" )
      {
        print "Process is already running\n";
        exit(0);
      }

    # Daemonize
    Proc::Daemon::Init();
    
    # If already running exit
    if (Proc::PID::File->running()) 
      { 
        exit(0); 
      }
  }
 
# Run Command
sub runCommand_Bash($)
   {
      local $cmd = $_[0];
      local @cmdResult = `$cmd`;
      
      if ( $? == 0 ) 
        {
          return @cmdResult, $?;
        } else {
                 return $?;
               }
    }

# Trim a String
sub trim($)
  {
    my $string = shift;
    $string =~ s/^\s+//;
    $string =~ s/\s+$//;
    return $string;
  }

# DNS to IP                     
sub returnIP($$) 
  {
     $domainName = $_[0];
     $dnsServer = $_[1];
     
     @result = runCommand_Bash("dig $domainName \@$dnsServer +short +authority");
                        
     if ( !$result[1] )
      {
          chomp($result[0]);
          return $result[0];
      }else{
              return 0;
            } 
  }

sub updateDDNS($$$$) 
  {
     $domainName = $_[0];
     $dnsServer = $_[1];
     $interfaceIP = $_[2];
     $key = $_[3];
       
     $cmd = ("echo \"
              server $dnsServer
              update delete $domainName A  
              update add $domainName 60 A $interfaceIP
              send\" | nsupdate -k $key
	      ");
              
    @result = runCommand_Bash("$cmd");
    
    if ( !$result[1] )
      {
        print "Success: $domainName \@$dnsServer $interfaceIP\n";  
      }else{
              return 0;
            }   
  }  

sub serve_Request($) 
  {
    my($serveCMD) = @_; 

    $continue=1;
    while ($continue)
      {
        $SIG{TERM} = sub { $continue = 0 };
        $SIG{QUIT} = sub { $continue = 0 };
        $SIG{HUP} = sub { $continue = 0 };
        $SIG{INT} = sub { $continue = 0 };
  
        $i = 0;
        foreach $record (@DDNS_records)
          {
              unless ($DDNS_servers[$i])
                {
		    $ii = $i;
		    while ( !$DDNS_servers[$ii] )
			{
				$ii--;
			}
                    $hostIP = returnIP($record,$DDNS_servers[$ii]);
                }else{
                        $hostIP = returnIP($record,$DDNS_servers[$i]);
                     }
             
              unless ($DDNS_interface[$i])
                {
		    $ii = $i;
		    while ( !$DDNS_interface[$ii] )
			{
				$ii--;
			}
                    $interfaceIP = get_interface_address("$DDNS_interface[$ii]"); 
                }else{
                        $interfaceIP = get_interface_address("$DDNS_interface[$i]");
                     }

              unless ($DDNS_keys[$i])
                {
		    $ii = $i;
		    while ( !$DDNS_keys[$ii] )
			{
				$ii--;
			}
                    $key = $DDNS_keys[$ii];   
                }else{
                         $key = $DDNS_keys[$i]; 
                     }
              
              if (!$hostIP)
                {
                  print "Error: Domain $domainName couldnt be authoritatively resolved by $dnsServer, or not exists\n";
                  $i++;
                  next;
                }

              if ( !$interfaceIP )
                {
                  print "Error: Coulnd not resolve hostIP or interface IP\n";
                  $i++;
                  next;
                }
             
              if ( "$hostIP" ne "$interfaceIP" )
                {
                  sleep($sleepFor); 
                  updateDDNS($record,$hostIP,$interfaceIP,$key);
                }else{
                        print "Record $record \@$hostIP already updated - SKIPPING\n";
                     } 
              $i++; 
          }
        if ( !$loopRun )
          {
             exit(0);
          } 
         sleep($loopSleep);
      }           
  }
        

## Check & serve
if ( exists($ARGV[0]) )
  {
      $switchMain=$ARGV[0];
      switch ($switchMain) 
                          {
                              case 'start' 
                                    { 
                                        daemonize();
                                        print "Starting DDNS updater in background \n";
                                        serve_Request('start');
                                    }
                              
                              case 'stop' 
                                    { 
                                        print "Stopping DDNS updater\n";
                                        if ( -e $pid_file )
                                          {
                                              open(PID,$pid_file);
                                              my $pid = <PID>;
		                                          close PID;
                                              runCommand_Bash("kill -HUP $pid");
		                                      }else{
                                                  print  "It is  NOT running, exitiing";
                                                  exit(0);
                                               }
                                    }
                              else 
                                   {
                                        print_usage();
                                   }
                          }
  }else 
        {
             print_usage();
        }
  
  
# Print usage help   
sub print_usage
   {
          print "Usage: ${scriptLocation} {start|stop|restart}\n";
          exit;
   }
