去年までここのブログのドメイン管理はvalue-domainで行っていたのですが、つい最近同じGMOグループのお名前.comにドメイン移管しました。

ただ、value-domainではダイナミックDNS機能が標準で提供されており、自宅のIPアドレスをRaspberry Piからcurlコマンドで自動更新できていたのですが、お名前.comではその機能が提供されていないことに移管してから気づき、手動で更新する羽目に。。

よくよく調べると、WindowsであればDiCEなどで自動更新できるみたいですが、DNSレコード更新のためだけにWindows機を常時起動しておくわけにもいかず、できれば今まで通りRaspberry Piから自動更新できる方法を模索していました。
(DiCEはLinux版もあるみたいですが、Raspbery Piで動くものは無いようでした)

そこで今回は、ヘッドレスブラウザのPhantomJSを使った自動テストフレームワークであるCasperJSを使って、お名前.comの管理ページからDNSレコードを変更するまでの操作をスクリプト化し、無理やり自動更新するようにしてみました。

注意:本方法を使った場合、設定によっては先方のサーバに思わぬ負荷を与えてしまうことがあります。ご利用にあたっては自己責任でお願い致します。

  1. Raspberry Pi(raspbian)にPhantomJSをインストール
    apt-getではインストールできないため、こちらのビルド版を/usr/local/bin以下にコピーして利用
  2. 続けてCasperJSをインストール
    公式サイトにある1.1.0-beta3のzipファイルをダウンロードし、/usr/local/casperjsに展開後、PATH環境変数に/usr/local/casperjs/binを追加
  3. 以下スクリプトをonamaedns.jsの名前でコピー
var casper = require('casper').create({
  pageSettings: {
    userAgent: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)'
  }, 
  logLevel: 'info', 
  waitTimeout: 30000, 
  verbose: true
});
casper.on('remote.message', function(msg) {
  this.log(msg, 'info');
});
var new_ip = null;

function exit_usage() {
  casper.echo('usage: casperjs --ssl-protocol=any --ignore-ssl-errors=true onamaedns.js [options]');
  casper.echo('options:');
  casper.echo('  --userid    onamae.com id');
  casper.echo('  --passwd    onamae.com password');
  casper.echo('  --domain    update domain');
  casper.echo('  --host      update hostname');
  casper.exit();
}
var options = casper.cli.options;
if (!options['userid'] || !options['passwd'] || !options['domain'] || !options['host']) {
  exit_usage();
}

casper.start();

casper.thenOpen('http://whatismyip.akamai.com/', function() {
  new_ip = this.fetchText('body');
  this.log('new_ip=' + new_ip, 'info');
});

casper.thenOpen('https://www.onamae.com/domain/navi/domain.html', function() {
  this.evaluate(function(options) {
    document.querySelector('input[name="username"]').value = options['userid'];
    document.querySelector('input[name="password"]').value = options['passwd'];
    document.querySelector('input[name="login"]').click();
  }, options);
});

casper.waitForSelector('a[href*="domain/navi/menu/domain"]', function() {
  this.click('a[href*="domain/navi/menu/domain"]');
});

casper.waitForSelector('a[href*="domain/navi/dns_manage"]', function() {
  this.click('a[href*="domain/navi/dns_manage"]');
});

casper.waitForSelector('input[type="radio"][value="' + options['domain'] + '"]', function() {
  this.click('input[type="radio"][value="' + options['domain'] + '"]');
});

casper.waitForSelector('a.btn07.idSubmitexternal > span', function() {
  this.click('a.btn07.idSubmitexternal > span');
});

casper.waitForSelector('a[title*="domain/navi/dns_controll/input"]', function() {
  this.click('a[title*="domain/navi/dns_controll/input"]');
});

casper.waitForSelector('#dns_controll_inputForm', function() {
  var ret = this.evaluate(function(new_ip, options) {
    var num = document.querySelector('input[value="' + options['host'] + '.' + options['domain'] + '"]').getAttribute('id').replace('hostNameUsed', '');
    var old_ip = document.querySelector('#hdd_add_recvalue_a_used' + num).value;
    if (new_ip == old_ip) {
      return false
    }

    document.querySelector('#hdd_add_recvalue_a_used' + num).value = new_ip;
    document.querySelector('#idSubmit').click();
    return true;
  }, new_ip, options);

  if (ret === false) {
    this.die('same ip adress');
  }
});

casper.then(function() {
  this.click('#idSubmit');
});

casper.waitForSelector('a[href*="submitDnsconfirmForm"] > span', function() {
  this.click('a[href*="submitDnsconfirmForm()"]');
});

casper.waitForUrl(/domain\/navi\/dns_controll\/result/, function() {
  this.exit();
});

casper.run();
  1. お名前.comの管理ページにログインして、本スクリプトで更新するDNSレコードを登録しておく
  2. スクリプトを実行してみて動作テスト(問題なければcronに登録)
casperjs --ssl-protocol=any --ignore-ssl-errors=true onamaedns.js

ハマった所としては、

  • PhantomJSのバージョンが古いため、(お名前.comを含む)POODLE対策を行ったサーバにHTTPS接続できない
    →casperjs –ssl-protocol=any(またはtlsv1) –ignore-ssl-errors=trueをつける
  • たまにタイムアウトで失敗する
    →Raspberry Pi(特に初期のやつ)だと重い処理らしく、waitForSelector()で応答を待つようにした

なところで、一応1ヶ月前くらいから毎朝3時に無線LANルーターの再起動とともに実行するよう仕掛けていますが、無事に成功しているようです。
(その度にお名前.comから更新完了の通知メールが届くのが難点ですが。。)