• このエントリーをはてなブックマークに追加
ss-2009-11-26-01 on Flickr

はじめに

以前,VALUE DOMAINのダイナミックDNSを更新するスクリプト書いたことを書きました.このスクリプトは,自宅サーバのcronで実際に動かしているのですが,ログを見てみると,結構500系のエラーが多いことが多いこと.

そんなわけで,失敗した際には成功するまで,またはある上限回数までリトライを繰り返す,といった処理を追加してみました.

また,前回の更新からIPアドレスが変化している場合,im.kayac.com経由でメッセージを投げる処理もついでに追加してみました.

ソースコード

変数のスコープがグチャグチャなのは仕様です ><

なお,wp-syntaxの都合上,ヒアドキュメント内の「’」を全角にしています.

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use LWP::UserAgent;
use FindBin;
 
#
# 設定
#
my $domain      = 'yourdomain';
my $passwd      = 'yourpassword';
my $host        = '*';
my $retry       = 10;
my $interval    = 20;
my $username_im = 'yourimusername';
#
# 設定 ここまで
#
 
my $logfile    = sprintf '/var/log/ddns/%s.log', $domain;
my $ipaddrfile = sprintf '%s/.ddns-ipaddr-%s', $FindBin::Bin, $domain;
my $ua         = LWP::UserAgent->new;
my $res;
 
 
my $ipaddr      = '';
my $ipaddr_prev = '';
 
#
# 前回更新時のIPアドレスの取得
#
if (-f $ipaddrfile) {
  local $/;
  open my $fh, '<', $ipaddrfile;
  $ipaddr_prev = <$fh>;
  close $fh;
}
 
#
# IPアドレスの取得
#
if($ipaddr eq '') {
  my $url_get_ip = 'http://dyn.value-domain.com/cgi-bin/dyn.fcg?ip';
  $res = $ua->get($url_get_ip);
  chomp($ipaddr = $res->content);
}
 
#
# DNS更新リクエスト
#
my $url_update = sprintf('http://dyn.value-domain.com/cgi-bin/dyn.fcg'
                           . '?d=%s&p=%s&h=%s&i=%s',
                         $domain,
                         $passwd,
                         $host,
                         $ipaddr,
                         );
 
for( my $i = 0; $i < $retry; $i++ ) {
  $res = $ua->get($url_update);
  chomp(my $content = $res->content);
  $content =~ s{(\x0d\x0a?|\x0a)}{ // }g;
 
  my @matched = $content =~ /status=(\d+)/im;
  my $status = defined $matched[0]  ?  int($matched[0])  :  9;
 
  my $code = $res->code;
 
  # 成功
 if( $code eq '200'  &&  $status == 0 ) {
    print_log($ipaddr, 1, $code, $status, $content);
 
    if ($ipaddr ne $ipaddr_prev) {
      post_im($ipaddr, $ipaddr_prev);
    }
    save_ipaddr($ipaddr);
    last;
  }
  # 失敗
  else {
    print_log($ipaddr, 0, $code, $status, $content);
    sleep($interval);
    next;
  }
}
 
 
# post im
sub post_im {
  my ($ipaddr, $ipaddr_prev) = @_;
 
  my $url_im = sprintf 'http://im.kayac.com/api/post/%s', $username_im;
  my $message = << "...";
$domain\’s IP address has changed:
  $ipaddr_prev => $ipaddr
...
 
  $ua->post($url_im, {message => $message});
}
 
# save ip address
sub save_ipaddr {
  my ($ipaddr) = @_;
 
  open my $fh, '>', $ipaddrfile  or  die $!;
  print $fh $ipaddr;
  close $fh;
}
 
# print log
sub print_log {
  my ($ipaddr, $result, $code, $status, $content) = @_;
 
  my $datetime;
  my ($ss, $mm, $hh, $d, $m, $y) = localtime time;
  $y+=1900; $m++;
  $datetime = sprintf '%04d-%02d-%02d %02d:%02d:%02d', $y, $m, $d, $hh, $mm, $ss;
 
  open(my $fh, '>>', $logfile)  or  die $!;
  printf $fh ("[%s] Request: %s => %s / Code: %s, Status: %d, Result: %s\n",
              $datetime,
              $ipaddr,
              $result ? 'o' : 'x',
              $code,
              $status,
              $content,
              );
  close $fh;
}
 
__END__

ログの一部

更新に失敗した場合,20秒後にリトライ,これを最大10回繰り返しています.

...
 
[2009-11-26 00:00:23] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 00:10:03] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 00:20:01] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 00:30:05] Request: ***.***.***.*** => x / Code: 503, Status: 9, Result: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> // <HTML><HEAD> // <TITLE>503 Service Temporarily Unavailable</TITLE> // </HEAD><BODY> // <H1>Service Temporarily Unavailable</H1> // The server is temporarily unable to service your // request due to maintenance downtime or capacity // problems. Please try again later. // </BODY></HTML>
[2009-11-26 00:30:26] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 00:40:02] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 00:50:01] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 01:00:02] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 01:10:04] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 01:20:02] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 01:30:02] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 01:40:01] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 01:50:02] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 02:00:15] Request: 500 Server closed connection without sending any data back => x / Code: 503, Status: 9, Result: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> // <HTML><HEAD> // <TITLE>503 Service Temporarily Unavailable</TITLE> // </HEAD><BODY> // <H1>Service Temporarily Unavailable</H1> // The server is temporarily unable to service your // request due to maintenance downtime or capacity // problems. Please try again later. // </BODY></HTML>
[2009-11-26 02:00:47] Request: 500 Server closed connection without sending any data back => x / Code: 500, Status: 9, Result: 500 Server closed connection without sending any data back
[2009-11-26 02:01:39] Request: 500 Server closed connection without sending any data back => x / Code: 500, Status: 9, Result: 500 Server closed connection without sending any data back
[2009-11-26 02:01:59] Request: 500 Server closed connection without sending any data back => x / Code: 500, Status: 9, Result: 500 Internal Server Error
[2009-11-26 02:02:19] Request: 500 Server closed connection without sending any data back => x / Code: 503, Status: 9, Result: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> // <HTML><HEAD> // <TITLE>503 Service Temporarily Unavailable</TITLE> // </HEAD><BODY> // <H1>Service Temporarily Unavailable</H1> // The server is temporarily unable to service your // request due to maintenance downtime or  capacity // problems. Please try again later. // </BODY></HTML>
[2009-11-26 02:02:40] Request: 500 Server closed connection without sending any data back => x / Code: 500, Status: 9, Result: 500 Internal Server Error
[2009-11-26 02:03:00] Request: 500 Server closed connection without sending any data back => x / Code: 200, Status: 9, Result: status=9 // Unknow Error.
[2009-11-26 02:03:21] Request: 500 Server closed connection without sending any data back => x / Code: 200, Status: 9, Result: status=9 // Unknow Error.
[2009-11-26 02:03:42] Request: 500 Server closed connection without sending any data back => x / Code: 200, Status: 9, Result: status=9 // Unknow Error.
[2009-11-26 02:04:03] Request: 500 Server closed connection without sending any data back => x / Code: 200, Status: 9, Result: status=9 // Unknow Error.
[2009-11-26 02:10:01] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK[2009-11-26 02:20:01] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 02:26:52] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 02:30:01] Request: ***.***.***.*** => x / Code: 503, Status: 9, Result: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> // <HTML><HEAD> // <TITLE>503 Service Temporarily Unavailable</TITLE> // </HEAD><BODY> // <H1>Service Temporarily Unavailable</H1> // The server is temporarily unable to service your // request due to maintenance downtime or capacity // problems. Please try again later. // </BODY></HTML>
[2009-11-26 02:30:21] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 02:33:17] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 02:40:02] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
[2009-11-26 02:45:02] Request: ***.***.***.*** => o / Code: 200, Status: 0, Result: status=0 // OK
 
...

おわりに

というわけで,自宅サーバのダイナミックDNS更新スクリプトは,こんな感じのものが動いていますよ,と.

ちなみに,im.kayac.comのメッセージをiPhoneで受け取るためには,iPhoneアプリ「im.kayac.com」をインストールしておく必要があります.(以下のリンクはiTunesが起動します.)