やりたいこと
PHPから家サーバ・プロジェクト(ieserver.net)様提供のDynamicDNSにIPアドレスを登録したい。
Perl版更新スクリプトのサンプルが提供されているので、
それをPHPでリライトする感じでいきたい。
注意事項
グローバルIPアドレスの取得のために外部サービスを利用するので、頻繁に実行しないこと。
サービス提供者、他の利用者の迷惑になります。
動作確認環境、使用ライブラリ等
-
Fedora 24 (Server Edition) 64bit
-
PHP 5.6.27
-
外部IPアドレス表示サービス
-
ieserver.net IPアドレス確認画面
-
WTF is my IP?!?!??
-
My External IP address
Perl版コードの解析
- IPキャッシュの内容を取得
- IPアドレス確認URLへアクセスし、自身のグローバルIPを取得
- もし、グローバルIPアドレスがIPキャッシュの内容と同一か、オールゼロ(取得失敗)であれば、プログラムを終了
- 登録用スクリプトにアクセスして登録を試みる
- レスポンスの内容にグローバルIPアドレスが含まれていれば、登録成功したと見なす。
PHPに書き換え
<?php
# 戻り値の定義
define("DDNS_NOCHANGE", 0);
define("DDNS_SUCCESS", 1);
define("DDNS_FAILED", 2);
define("DDNS_IPCHK_FAIL", 3);
define("DDNS_UPDATE_FAIL", 4);
function ddns_update($account, $domain, $password, &$outside_ip){
$ip_cache = '/tmp/php.ip.cache';
# 一時ファイルがなければ作る。前回のグローバルIPアドレスが記録される
if(!file_exists($ip_cache)){ touch($ip_cache); }
# 現在のグローバルIPアドレスを取得する
$ip_urls = array(
"http://ieserver.net/ipcheck.shtml",
"https://wtfismyip.com/text",
"https://myexternalip.com/raw",
);
$outside_ip = "";
foreach($ip_urls as $ip_url)
{
$outside_ip = trim(file_get_contents($ip_url));
if($outside_ip != "" AND $outside_ip != "0.0.0.0") { break; }
}
if($outside_ip == "" OR $outside_ip == "0.0.0.0") { return DDNS_IPCHK_FAIL; }
# 前回のグローバルIPアドレスを取得する
$recent_ip = trim(file_get_contents($ip_cache));
# 現在のIPアドレスが前回のものと同じ場合は、変更なしとして関数を抜ける
if($outside_ip == $recent_ip) { return DDNS_NOCHANGE; }
# 登録用スクリプトにアクセスして更新を試みる
$result = file_get_contents(sprintf(
'https://ieserver.net/cgi-bin/dip.cgi?username=%1$s&domain=%2$s&password=%3$s&updatehost=1',
$account, $domain, $password
));
# 戻り値の取得に失敗した場合は、エラーとして関数を抜ける
if($result == false) { return DDNS_UPDATE_FAIL; }
# 戻り値に現在のグローバルIPアドレスが含まれていなければ、エラーとして関数を抜ける
if(strpos($result, $outside_ip) === false) { return DDNS_UPDATE_FAIL; }
# 一時ファイルに、現在のグローバルIPアドレスを書き込む
file_put_contents(DDNS_TEMP_FILE, trim($outside_ip), LOCK_EX);
# 成功として関数を抜ける
return DDNS_SUCCESS;
}
使い方
<?php
require 'ddns_update.php';
$result = ddns_update("example", "orz.hm", "password", $ip);
switch($result) {
case DDNS_NOCHANGE:
echo "IPアドレスは前回と同じです。\n"; break;
case DDNS_SUCCESS:
echo "IPアドレスを更新しました。IPアドレスは ${ip} です。\n"; break;
case DDNS_IPCHK_FAIL:
case DDNS_UPDATE_FAIL:
echo "IPアドレスの更新に失敗しました。\n"; break;
}
簡単な解説
サンプルのPerlスクリプトをPHPに起こして、ログ書き出しを省いてステータスを取得できるよう手を加えた。
新しいIPアドレスの取得も可能となり、他プログラムとの連携もしやすくなっていると思う。
ログが必要な場合は、関数側か使用する側のどちらかに手を加えればよい。
20161029追記
ieserver.netオフィシャルのIPアドレス表示が調子悪い時があるみたいなので
ieserver.netでの取得に失敗した場合は、順番に他サービスを参照することで、IPアドレス取得の信頼性の向上を図ることにした。
具体的な手法としては、配列に格納された各サービスのURLを順番に参照し、
最初に取得できたデータをIPアドレスとして用いる。
謝辞
DynamicDNSサービスを提供して頂いている家サーバ・プロジェクト様に深謝の意を表します。
また、グローバルIPアドレス取得サービスを提供して頂いている「wtfismyip.com」および「My External IP address」運営の皆様に感謝の意を表します。