ホームページ制作/SEO対策なら株式会社サイバーブレーンへ

 03-5961-5681 平日10:00~19:00

 2019-08-16

DNS - 問い合わせ実験

PHPでDNS問い合わせをソケットを使って試す。

https://www.ietf.org/rfc/rfc1035.txt

とにもかくにも厄介なのが、PHPでバイナリデータを扱うのに四苦八苦する。
とりあえず、DNSでAレコードの問い合わせを簡易的に作成。
今回は、DNSの応答ヘッダのみ出力するところまでを作成。
もちろんヘッダのみなので、肝心のIPアドレスを表示するまでには至っていないけど、
ある程度参考にはなると思います。
例によってコードはダラダラ書きです。

また、ローカル環境では、大抵、ゲートウェイのルータがDNSサーバとして機能していると
思いますので、ルーターのIPアドレスをDNSとしてセットすると問題ないと思われます。
外部DNSサーバーを指定すると、ブロックされたり、或はルーティングを静的に設定しないと
正しく動作しないので注意してください。
グローバルネットワークの環境であれば、余程きついセキュリティ設定でもされていない
限りは、問題なく動作すると思います。

PHPソース




//ここは利用するDNSサーバーのIPアドレスをセットしてください。
$dns_server_ip = '192.168.1.1';
$port = 53;

$id = mt_rand(1, 65535);

$qdata = createQueryARecord($id, 'www.cyberbrain.co.jp');
$sendLen = strlen($qdata);

$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_sendto($sock, $qdata, $sendLen, 0, $dns_server_ip, $port);
$buf = '';

$wait = true;
while($wait){
socket_recvfrom($sock, $buf, 4096, 0, $dns_server_ip, $post);


$ret = unpack('nid/Cflag1/Cflag2/n4other', $buf);

echo 'id:' . $ret['id'] . "\n";
if ($ret['id'] != $id) {
echo 'Mismatch ID.';
break;
}


$QR = ($ret['flag1'] & 0x80) >> 7;
echo 'QR:' . $QR . "\n";
if (!$QR){
echo 'Invalid Value.';
break;
}

$OPCODE = $ret['flag1'] & 0x78;
echo 'query code: ' . $OPCODE . "\n";
// AAビットの取り出し
$AA = $ret['flag1'] & 0x04;
if (!$AA){
echo 'Non-authoritative' . "\n";
} else {
echo 'Authoritative' . "\n";
}
//TCビット
$TC = $ret['flag1'] & 0x02;
if ($TC){//切り落し-伝送チャンネル上に最大長よりメッセージが長くなったためこのメッセージが切り落とされたことを示します。
echo 'TrunCation On' . "\n";
//とりあえず、対応しないので、ブレークする。
break;
} else {
$wait = false;
}

//RD
$RD = $ret['flag1'] & 0x01;
if ($RD){// 再帰要望
echo 'Recursion Desired' . "\n";
}


$RA = $ret['flag2'] & 0x80;
if ($RA){// 再帰可能
echo 'Recursion Available' . "\n";
}
//将来のために予約されたエリアで常に0,0以外なら何かが定義されている
$Z = $ret['flag2'] & 0x70;
if ($Z){
echo 'Reserved for future use : ' . $Z . "\n";
}
// Response code.
$RCODE = $ret['flag2'] & 0x0f;
switch($RCODE){
case 0://No error condition
break;
case 1:
echo 'Format error - The name server was unable to interpret the query.' ."\n";
break;
case 2:
echo 'Server failure - The name server was unable to process this query due to a problem with the name server.' . "\n";
break;
case 3:
echo 'Name Error - Meaningful only for responses from an authoritative name server, this code signifies that the domain name referenced in the query does not exist.' . "\n";
break;
case 4:
echo 'Not Implemented - The name server does not support the requested kind of query.' . "\n";
break;
case 5:
echo 'Refused.' . "\n";
break;
default:
echo 'Reserved for future use.' . "\n";
}
// QDCOUNT
echo 'Question Count: ' . $ret['other1'] . "\n";
//ANCOUNT
echo 'Answer Count: ' . $ret['other2'] . "\n";
// NSCOUNT an unsigned 16 bit integer specifying the number of name
echo 'Server resource records: ' . $ret['other3'] . "\n";
// ARCOUNT an unsigned 16 bit integer specifying the number of
echo 'Resource records in the additional records: ' . $ret['other4'] . "\n";

//
}
socket_close($sock);
exit(0);

function createQueryARecord($id, $host){


//DNSヘッダフォーマット
// ID (16) DNSのトランザクションID。クエリ時に指定し、応答パケットにコピーされて戻る。
/*
次の16bitは、
QR (1) 問い合わせが0、応答が1
OPCODE (4) 問い合わせの種類を指定する。0が通常のクエリ、0 => 問い合わせ、1 => 逆問い合わせ、2 => サーバ状態要求
AA (1) 管理権限がある応答であることを示す
TC (1) パケット長制限などで応答が切り詰められていることを示す
RD (1) 名前解決を要求するビット。0は権威DNSサーバへの問い合わせで、1はフルサービスリゾルバへの問い合わせ

RA (1) 名前解決可能であることを示す
Z (1) 将来のために予約。常に0とする
AD (1) DNSSEC検証に成功したことを示す(応答)/応答のADビットを理解できることを示す(問い合わせ)
CD (1) DNSSEC検証の禁止
RCODE (4)
*/

// QDCOUNT (16) Questionセクションの数で、通常は1
// ANCOUNT (16) Answerセクションのリソースレコード(RR)数
// NSCOUNT (16) AuthorityセクションのRR数
// ARCOUNT (16) AdditionalセクションのRR数


// Aレコード問い合わせは
// IDは任意でよく、通常のクエリであるから、ヘッダは以下のようにすればよい。
// 名前解決を要求する場合は、RDビットを1にする。そのため、以下のようなHeaderセクションとなる。
// 0x00,0x00,0x01(RDビットを1に),0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00

$data = pack('n', $id);//ID 16bit
//フラグ16ビットのセット
$data .= pack('C*',0x01, 0x00);//RDを1にセットし、それ以外は0でフラグをセット

// QDCOUNT Questionセクションの数で、通常は1
$data .= pack('C*', 0x00, 0x01);

//ANCOUNT, NSCOUNT, ARCOUNT,
$data .= pack('C*', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);

$div = explode('.', $host);

foreach($div as $v){
$len = strlen($v);
if ($len<=0){
echo 'invalid host name: ' . $host . "\n";
exit(1);
}
$data .= pack('C*', $len) . $v;
}
$data .= pack('C', 0x00);//ホスト名の最後にnullセット

// A IN をセット
$data .= pack('C*', 0x00,0x01,0x00,0x01);

return $data;
}

~この記事の著者~

サイト制作に関するご相談はこちら

 03-5961-5681 平日10:00~19:00

メールフォーム

あなたが
サイトに
求める要素
あなたがサイトに求める0要素