Dart で プログラマブルなメールサービスを作ろう の連載記事です。
Dart で DNS リクエストする機能を組んでいきます。メールを送信する際に、メールアドレスから、そのメールを管理しているメールプロバイダーを探して、
そのプロバイダーにメールを送信します。
-
- DNS に 依頼して、MXレコードを取得する。
-
- MXレコードから、Aレコードを取得する。
といった流れを組みます。
- MXレコードから、Aレコードを取得する。
例えば、tetorica.net の場合だと、
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で書く
```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で書いてみる
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に変換すると、
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