LoginSignup
0
0

More than 1 year has passed since last update.

Dart で プログラマブルなメールサービスを作ろう (2) DNS 用のQuery を組んでみる

Last updated at Posted at 2021-12-26

Dart で プログラマブルなメールサービスを作ろう の連載記事です。

Dart で DNS リクエストする機能を組んでいきます。メールを送信する際に、メールアドレスから、そのメールを管理しているメールプロバイダーを探して、
そのプロバイダーにメールを送信します。

  • 1. DNS に 依頼して、MXレコードを取得する。
  • 2. MXレコードから、Aレコードを取得する。 といった流れを組みます。

例えば、tetorica.net の場合だと、

スクリーンショット 2021-12-26 17.10.56.png

118.27.109.25 の IPアドレスを取得する必要があります。

DNS の仕様は REF1035 に書かれています。

Dart で Aレコードをリクエストしてみる。

Google の Public DNS を利用してみます。

https://dns.google/dns-query?dns=${base64化したDNS Message}
にGet リクエスを飛ばすことで、DNSから Aレコード MXレコード を取得する事ができます。

メッセージのFormat

DNSの Format は以下の通り、Header、Question、Answer、Authority、Additional
と5つのSectionからなっています。

4. MESSAGES

4.1. Format

    +---------------------+
    |        Header       |
    +---------------------+
    |       Question      | the question for the name server
    +---------------------+
    |        Answer       | RRs answering the question
    +---------------------+
    |      Authority      | RRs pointing toward an authority
    +---------------------+
    |      Additional     | RRs holding additional information
    +---------------------+

ただし、リクエストする場合は、 Answer、Authority、Additional のデーターが0バイトになります。

リクエスする場合のHeader

Header の SPECは以下の通り
```

4.1.1. Header section format

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

IDはなんでも良いです。
RDは、1になります。
QDCOUNTは、1になります。
それ以外は、0でOKです。

リクエスする場合のHeader をDartで書く

request_header.dart
var buffer = DNSBuffer(12);

void setInt16AtBE(Uint8List _buffer, int index, int value) {
  _buffer[index + 0] = (value >> 8) & 0xFF;
  _buffer[index + 1] = (value >> 0) & 0xFF;
}

void main() {
  var buffer = Uint8List(12);
  for (var i = 0; i < 12; i++) {
    buffer[i] = 0;
  }
  setInt16AtBE(buffer, 0, 0x1234);
  buffer[2] = 0x01;
  setInt16AtBE(buffer, 5, 0x01);
  print(toHex(buffer)); // 123401000001000000000000
}

リクエスする場合のQuestion

                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Question の Section の Format は以上のような感じで、QNAMEに関しては、
A RECORDを取得する場合は、

QTYPE は 1
QCLASS は 1

を設定します、
QNAME には、ドメイン名を設定しますが、github.comのような文字列ではないです・

QNAME

       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    12 |           6           |           g           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    14 |           i           |           t           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    16 |           h           |           u           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    26 |           b           |           3           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    28 |           c           |           o           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    30 |           m           |           0           |
       +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

のような、1バイトの数字、アスキー文字列1バイトの数字、アスキー文字列.Null文字(数字の0)が してされます。

リクエスする場合のQuestion をDartで書いてみる

request_question.dart

void main() {
  var host = 'github.com';
  var splitHost = host.split('.');
  // Calc Buffer Size
  var length = splitHost.length;
  splitHost.forEach((e) {
    length += e.length;
  });
  length += 1; // NULL CHAR
  length += 4; // CLASS  AND TYPE

  // Set Value
  var buffer = Uint8List(length);
  for (var i = 0; i < 12; i++) {
    buffer[i] = 0;
  }

  var index = 0;
  splitHost.forEach((e) {
    buffer[index++] = e.length;
    for (var i = 0; i < e.length; i++) {
      buffer[index++] = ascii.encode(e.substring(i, i + 1))[0];
    }
  });

  setInt16AtBE(buffer, length - 4, 0x01);
  setInt16AtBE(buffer, length - 2, 0x01);
  print(toHex(buffer)); // 0667697468756203636f6d0000010001
}

ブラウザーからRequestしてみる。

これらの、Header と Question を合わせると、
1234010000010000000000000667697468756203636f6d0000010001 の バイトデータができます、これを Base64に変換すると、

request_base64.dart
import 'dart:typed_data' show Uint8List;
import 'dart:convert' show base64;

Uint8List fromHexString(String hexSrc) {
  var _buffer = Uint8List(hexSrc.length ~/ 2);
  for (var i = 0, j = 0; i < hexSrc.length; i += 2, j++) {
    var v = int.parse(hexSrc.substring(i, i + 2), radix: 16);
    _buffer[j] = v & 0xFF;
  }
  return _buffer;
}

void main() {
  var buffer = fromHexString('1234010000010000000000000667697468756203636f6d0000010001');
  print(base64.encode(buffer)); // EjQBAAABAAAAAAAABmdpdGh1YgNjb20AAAEAAQ==
}

EjQBAAABAAAAAAAABmdpdGh1YgNjb20AAAEAAQ==になります。
これから、 == を取り除いて。

Google Public DNS のアドレスに当てはめると、以下のようになります。

https://dns.google/dns-query?dns=EjQBAAABAAAAAAAABmdpdGh1YgNjb20AAAEAAQ

すると、DNSから結果を取得できます。

1234818000010001000000000667697468756203636f6d0000010001c00c000100010000003c000434c04859

次回

今回受け取った、値をParseしていきます。

REF

今回のコードなどは以下を参照してください。
https://github.com/kyorohiro/dart2.dns

0
0
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
0
0