Dart 언어로 DNS 쿼리 만들기

15764 단어 dartdnsdartlang
이 섹션에서는 A 레코드를 가져오기 위한 DNS 쿼리를 만드는 방법을 설명합니다.

그리고 이 섹션에서는 Google의 Public DNS를 사용하려고 합니다.
Get 요청을 보내면
https://dns.google/dns-query?dns=${base64ized DNS Message}
Get 요청을 보내 DNS에서 레코드를 검색할 수 있습니다.

DNS 메시지 형식



DNS 형식은 Header , Question , Answer , Authority , Additional 의 5개 섹션으로 구성됩니다.

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

from https://datatracker.ietf.org/doc/html/rfc1035


요청 헤더



헤더 섹션은 다음과 같은 형식입니다.

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이 될 수 있습니다.

  • 이것을 다트에 써봅시다.

    
    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
    }
    
    


    요청에 대한 질문




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


    질문 섹션의 형식은 위와 같으며 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           |
           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    
    

    위는 단일 바이트 숫자, ASCII 문자열, 단일 바이트 숫자 및 ASCII 문자열입니다.

    Null 문자는 끝에 추가됩니다.

    이것을 다트에 써봅시다.

    
    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
    }
    
    


    브라우저에서 요청



    헤더와 질문을 결합하면 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
    


    참조



    https://github.com/kyorohiro/dart2.dns

    https://datatracker.ietf.org/doc/html/rfc1035

    좋은 웹페이지 즐겨찾기