Value-Domain用のDDNS更新スクリプト
Linux版DiCEの設定が今一不便なので、Perlの練習がてらに自作の更新スクリプトを書いてみた。Value-Domain専用。
/usr/local/bin/vdddns
use strict;
use LWP::UserAgent;
use File::Slurp;
use Sys::Syslog;
# settings
my $interval = 3000; # seconds
my $working_directory = "/var/vdddns";
my $ip_check_uri = "http://dyn2.value-domain.com/cgi-bin/dyn.fcg?ip";
my $update_uri_template = 'http://dyn2.value-domain.com/cgi-bin/dyn.fcg?d=@DOMAIN@&p=@PASSWORD@&h=@HOST@&i=@IP_ADDRESS@';
my $conf_file = "/etc/vdddns.conf";
my ($log_stdout, $log_syslog, $log_file) = (1, 1, 1);
# settings 2
my $save_file = $working_directory . "/ip";
my $log_file = $working_directory . "/log";
# command line params
my $mode = 0;
if ($ARGV[0] eq "-c") {$mode = 1};
if ($ARGV[0] eq "-f") {$mode = 2};
# load saved ip address
my $ip = read_file($save_file, err_mode => 'quiet');
unless ($ip) {
# the first running
$ip = "UNSET"; # dummy ip
}
# HTTP requset
my $ua = LWP::UserAgent->new;
my $ip_req = HTTP::Request->new(GET => $ip_check_uri);
# start observation
if ($mode eq 0) {
print_log("VDDDNS has been started. Saved IP address: $ip")
};
while (1) {
my $ip_res = $ua->request($ip_req);
if ($ip_res->is_success) {
my $new_ip = $ip_res->content;
if ($new_ip ne $ip || $mode eq 2) {
# IP address changed or force to update
if ($mode eq 2) {
print_log("IP address: $new_ip");
} else {
print_log("IP address changed: $ip -> $new_ip");
}
$ip = $new_ip;
unless (write_file($save_file, $ip)) { # save to the file
print_log( "Could not write the ip address: $save_file");
}
# load conf list
my @entries = ();
my $conf_list = read_file($conf_file, err_mode => 'quiet');
unless ($conf_list) {
print_log("Could not load the configration file: $conf_file");
} else {
my @lines = split /\n/, $conf_list;
foreach my $line (@lines) {
my @values = split /\s+/, $line;
if (@values ne 0) { # remove empty line
push(@entries, [@values]);
}
}
}
foreach my $entry (@entries) {
# make uri for updating
my ($account, $password, $domain, $host) = @{$entry};
my $update_uri = $update_uri_template;
$update_uri =~ s/\@ACCOUNT\@/$account/g;
$update_uri =~ s/\@PASSWORD\@/$password/g;
$update_uri =~ s/\@DOMAIN\@/$domain/g;
$update_uri =~ s/\@HOST\@/$host/g;
$update_uri =~ s/\@IP_ADDRESS\@/$ip/g;
# request to update
my $update_req = HTTP::Request->new(GET => $update_uri);
my $update_res = $ua->request($update_req);
if ($update_res->is_success) {
# parse result
my ($status_code_line, $status_message) = split /\n/, $update_res->content;
$status_code_line =~ /^status=(.*)$/;
my $status_code = $1;
# print result
if ($status_code eq "0") {
print_log("Updating success: $host.$domain");
} else {
print_log("Updating error: $host.$domain $status_message");
}
} else {
print_log("Updating HTTP error: $update_uri " . $update_res->status_line);
}
}
}
} else {
print_log ("IP adress lookup error: " . $ip_res->status_line);
}
if ($mode ne 0) { last; }
sleep $interval;
}
sub print_log {
my ($message) = @_;
my ($sec, $min, $hour, $mday, $mon, $year) = localtime(time);
$mon++;
$year += 1900;
my $full_message = sprintf "[%s-%s-%sT%s:%s:%s] %s\n", $year, $mon, $mday, $hour, $min, $sec, $message;
if ($log_stdout) {
print $full_message;
}
if ($log_file) {
write_file($log_file, {append => 1}, $full_message)
}
if ($log_syslog) {
openlog('VDDDNS', 'cons,pid', 'user');
syslog('info', $message);
closelog();
}
}
/etc/vdddns.conf
account foo example.com www
account foo example.com home
account2 bar example.jp www
使い方は次のような感じ。
# 引数無しだとデーモンとして起動する。
# プロセスが常駐して、指定時間毎にチェックを行い、
# IPが変更されていた場合は更新を行う。
perl /usr/local/bin/vdddns
# cronで起動する時は-cを付ける。
# 前回起動時のIPと比較して、変更があった場合のみ更新を行う。
perl /usr/local/bin/vddns -c
# 強制的に更新を行いたい場合は-fを付ける。
# 起動時のIPで更新を行う。
perl /usr/local/bin/vddns -f
設定ファイルは次のフォーマットで書く。各項目をタブか半角スペースで区切って並べる。空白文字の数は任意でかまわない。空行は無視される。
アカウント名 DDNS用パスワード ドメイン名 ホスト名
実はアカウント名は更新時には使わないのだが、気にしない方向で。
なお、IPアドレスを保持するファイルを/var/vdddnsに作るので、あらかじめmkdir -p /var/vdddns
しておく必要がある。
cronで定期的に起動する場合は適当な設定ファイル(crontab -e
で開く)に次の行を足しておくと良い。
0-59/10 * * * * perl /usr/local/bin/vdddns -c
この設定で10分ごとにIPが変更されていないかがチェックされる。確認の間隔を余り短くしすぎると、DDNSのサーバに負荷が掛かりすぎてしまうので注意。
デーモンとして起動する場合は適当にinit.dスクリプトを書けばいいのだろうが、調べるのが面倒なので今日の所はとりあえずここまでにしておく。それにしても、cron版と強制更新版を完成後に足したせいで、ソースが汚くなってしまった。最初から思いついていればもう少しまともな設計が出来たのだが。