LoginSignup
8
8

More than 5 years have passed since last update.

DNS サーバのステータスを確認する試み

Last updated at Posted at 2016-03-30

まとめ

  • DNS サーバを監視する必要があり、スクリプトを作成することにしました
  • dig など、既存のコマンドを使うこともできましたが、DNS の勉強を兼ねて自分で実装することにしました
  • Ruby の勉強も兼ねて、プログラムは Ruby で作成することにしました

  • RFC 1035 によると、DNS リクエスト・ヘッダの OPCODE フィールドに 2 を指定すると DNS サーバのステータスが確認できるようです

  • これを利用して DNS サーバのステータス・モニタリングができるか試してみました

  • スクリプトは問題なく完成しましたが、狙っていた結果(DNS サーバのステータスの確認)は得られませんでした

  • DNS サーバ側で OPCODE=2 のリクエストへの応答が実装されていない感じでした(応答ヘッダの RCODE が 4 (= "Not Implemented") でした)

  • DNS サーバの監視は、ダミーのリクエストを投げて、その応答の中の RCODE を確認するのが良いのかもしれません(いずれ試してみたいと思います)

  • 以下は今回作成したスクリプトと実装メモです

  • 内容に間違いがあったらごめんなさい

参考情報

ソースコード

  • 以下が作成したスクリプトです
  • 手続的なコードですがご容赦ください
dnsping.rb
#!/usr/bin/ruby

require 'socket'

if ARGV.size == 0
  puts "Usage: ./dnsping.rb <DNS Server IP Address>"
  exit 1
end

SERV = ARGV[0]           # DNS Server Address
PORT = 53                # DNS Port
LEN  = 512               # maxlen bytes

# Generate DNS Request
# for a server status request, we only need a header data
# header format is described in RFC 1035
def dns_request()

  id      = 1

  qr      = 0 << 15   # qr = 0      => query (not response)
  opcode  = 2 << 11   # opcode = 2  => server status request
  aa      = 0 << 10
  tc      = 0 << 9
  rd      = 0 << 8
  ra      = 0 << 7
  z       = 0 << 4
  rcode   = 0 << 0

  qdcount = 0
  ancount = 0
  nscount = 0
  arcount = 0

  f1 = id
  f2 = (qr + opcode + aa + tc + rd + ra + z + rcode)
  f3 = qdcount
  f4 = ancount
  f5 = nscount
  f6 = arcount

  # 'n*' converts 16bit unsigned array to byte array in network byte order
  header = [f1, f2, f3, f5, f5, f6].pack('n*')

  return header

end

def main()

  # create DNS request message
  req = dns_request()

  # display DNS request message
  print " Request:  "
  p req

  # create socket
  sock = UDPSocket.new
  sock.connect(SERV, PORT)

  # send the query
  sock.send req, 0

  # receive response
  data = sock.recvfrom(LEN)

  # display received data
  # data[0] => received data
  print " Response: "
  p data[0]

  # display response code
  # lower 4 bytes of the 4th octet in received data is the response code
  print " RCODE:    "
  p (data[0].unpack('C*')[3] & 0b1111)

end

main()

ソースコードの説明

#!/usr/bin/ruby

require 'socket'

if ARGV.size == 0
  puts "Usage: ./dnsping.rb <DNS Server IP Address>"
  exit 1
end

  • サーバのアドレスはスクリプトの第一引数で指定します
  • DNS サーバのポートは 53 番です

  • LEN は DNS サーバからの応答パケットの最大サイズです

    • 実際の応答パケットのサイズは 96byte でした(DNS のヘッダサイズが 96byte)

SERV = ARGV[0]           # DNS Server Address
PORT = 53                # DNS Port
LEN  = 512               # maxlen bytes

  • DNS のリクエスト・メッセージを作成します
  • リクエストのフォーマットは RFC 1035 に定義されています

  • 図解すると、こんな感じのバイト列を作成します

フィールド名 サイズ(バイト数) 設定値 説明
ID 16 1 任意の値
QR 1 0 問い合わせ
OPCODE 4 2 ステータス確認
AA 1 0 応答専用フィールド
TC 1 0 トランケートされていない
RD 1 0 再帰希望しない
RA 1 0 再帰不可能
Z 3 0 予約フィールド
RCODE 4 0 応答専用フィールド
QDCOUNT 16 0 0 => 全ての Zone, 1 => Question Section で指定した Zone
ANCOUNT 16 0 -
NSCOUNT 16 0 -
ARCOUNT 16 0 -

# Generate DNS Request
# for a server status request, we only need a header data
# header format is described in RFC 1035
def dns_request()

  • ID は任意の値を設定します

  id      = 1

  • DNS の問い合わせの際は QR = 0 に設定します
  • OPCODE = 2 が DNS サーバのステータス確認です
  • その他のフィールドは 0 に設定します
  • 各フィールドの意味と設定すべき値は RFC 1035 で解説されています

  qr      = 0 << 15   # qr = 0      => query (not response)
  opcode  = 2 << 11   # opcode = 2  => server status request
  aa      = 0 << 10
  tc      = 0 << 9
  rd      = 0 << 8
  ra      = 0 << 7
  z       = 0 << 4
  rcode   = 0 << 0

  qdcount = 0
  ancount = 0
  nscount = 0
  arcount = 0

  f1 = id
  f2 = (qr + opcode + aa + tc + rd + ra + z + rcode)
  f3 = qdcount
  f4 = ancount
  f5 = nscount
  f6 = arcount

  • Array#pack の書式指定に 'n*' を指定すると、16bit unsigned の配列をバイト配列に変換することができます
  • バイト・オーダーはネットワーク・バイト・オーダーになります

  # 'n*' converts 16bit unsigned array to byte array in network byte order
  header = [f1, f2, f3, f5, f5, f6].pack('n*')

  return header

end

def main()

  # create DNS request message
  req = dns_request()

  # display DNS request message
  print " Request:  "
  p req

  • 監視スクリプトなので DNS パケットは UDP で送信することにします

  # create socket
  sock = UDPSocket.new
  sock.connect(SERV, PORT)

  # send the query
  sock.send req, 0

  # receive response
  data = sock.recvfrom(LEN)

  # display received data
  # data[0] => received data
  print " Response: "
  p data[0]

  • DNS サーバから返ってきたパケットの 4 オクテット目の下位 4byte が応答コードです
  • この値が 0 ならサーバは正常に稼働しています
  • ここで返される応答コードの意味は以下の通りです
応答コード 意味
0 No Error
1 Format Error
2 Server Failure
3 Name Error
4 Not Implemented
5 Refused

  # display response code
  # lower 4 bytes of the 4th octet in received data is the response code
  print " RCODE:    "
  p (data[0].unpack('C*')[3] & 0b1111)

end

main()

実行結果

  • 手元の DNS サーバで試してみたところ、RCODE = 4 (= "Not Implemented") が返って来ました(残念!)
$ ./dnsping.rb x.x.x.x
 Request:  "\x00\x01\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00"
 Response: "\x00\x01\x90\x04\x00\x00\x00\x00\x00\x00\x00\x00"
 RCODE:    4

おしまい

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8