DNS

DNS [Domain Name System] is a service on the Internet. As a distributed database that maps domain names and IP addresses to each other, it makes it easier for people to access the Internet. DNS uses TCP and UDP port 53.

Vernacular version

Is the client (for example: browser) incoming website domain name, to the DNS list to find the corresponding IP back to the client, and then the client can find the corresponding server according to the IP, you can send a request to the server.

The purpose of DNS is to give the corresponding server IP address to the client. Finally, the client communicates with the server and there is no DNS problem.

DNS Message Format

The FORMAT of DNS packets is the same for both request packets and reply packets returned by the DNS server.

  • HeaderMessage header
  • QuestionQuery questions
  • AnswerThe reply
  • AuthorityAuthorization response
  • AdditionalAdditional information
  DNS format

  +--+--+--+--+--+--+--+
  |        Header      |
  +--+--+--+--+--+--+--+
  |      Question      |
  +--+--+--+--+--+--+--+
  |      Answer        |
  +--+--+--+--+--+--+--+
  |      Authority     |
  +--+--+--+--+--+--+--+
  |      Additional    |
  +--+--+--+--+--+--+--+
Copy the code

The Header packet Header

  • ID: 2Bytes (16bit). The client parses the DNS reply packet returned by the serverIDThe value is set with the request packetIDIf they are the same, the DNS session is considered to be the same.
  • FLAGS: 2Bytes (16bit). Contains the following attributes:
    • QR: 0Indicates the query message,1Indicates a response message;
    • opcode: Usually, the value is0(standard query), other values are1(Reverse query) and2(Server status request),[3]Retention values;
    • AAThe authoritative answer bit is meaningful only in the authoritative answer, indicating that the authoritative answer server is the authoritative server that queries the domain name.
    • TC: truncated: indicates that a packet is longer than the allowed length and therefore truncated.
    • RDThe: Recursion Desired – this bit is set by the request and the same value is returned when the request is returned. If RD is configured, recursive resolution is recommended for DNS servers. Recursive query is optional.
    • RA: indicates Recursion Available — this bit is set or cancelled in the reply and is used to indicate whether the server supports recursive queries.
    • Z: Reserved value, not currently used;
    • RCODE: Response Code – The four bits are set in the Response packet and represent the following meanings:
      • 0: No errors.
      • 1: Format error – The server cannot understand the requested message.
      • 2: Server failure – the request cannot be processed because of Server failure
      • 3: Name Error – Indicates that the resolved domain Name does not exist.
      • 4Implemented – DNS does Not support query types;
      • 5: Refused – The server Refused to respond due to the configured policy. For example, the server does not want to answer certain requesters, or the server does not want to perform certain operations (such as zone transfer);
      • [6]: Reserved value, not used currently.
  • QDCOUNT: no symbols16bitThe integer indicates the number of problem records in the request segment.
  • ANCOUNT: no symbols16bitThe integer indicates the number of reply records in the reply segment.
  • NSCOUNT: no symbols16bitAn integer indicates the number of authorization records in the authorization segment.
  • ARCOUNT: no symbols16bitThe integer indicates the number of additional records in the additional segment of the message.
  Header format

    0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                      ID                       |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |QR|  opcode   |AA|TC|RD|RA|   Z    |   RCODE   |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    QDCOUNT                    |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    ANCOUNT                    |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    NSCOUNT                    |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    ARCOUNT                    |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Copy the code

Question query field

  • QNAMEunsigned8bitThe unit length is unlimited and indicates the query name (widely known as: domain name).
  • QTYPEunsigned16bitThe integer indicates the protocol type to be queried.
  • QCLASSunsigned16bitIntegers represent the class of the query, for example,INOn behalf of the Internet.
Question format 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ... | | QNAME | | ... | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS  | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+Copy the code

Answer/Authority/Additional

The format of these three fields is the same.

  • NAMEThe domain name contained in the resource record.
  • TYPEsaidDNSProtocol type.
  • CLASSClass that represents RDATA.
  • TTLA 4-byte unsigned integer represents the amount of time a resource record can be cached. 0 means it can only be transmitted, but not cached.
  • RDLENGTHA 2-byte unsigned integer represents the length of RDATA
  • RDATAAn indefinite string representing a record. The format root TYPE is CLASS dependent. For example, if TYPE is A and CLASS is IN, then RDATA is A 4-byte ARPA network address.
  Answer/Authority/Additional format

    0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    NAME                       |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    TYPE                       |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    CLASS                      |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    TTL                        |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    RDLENGTH                   |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  |                    RDATA                      |
  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Copy the code

DNS request packet parsing

All talk, no action. So how do YOU parse DNS request messages? Let’s take a look at a DNS request:

  6dca 0100 0001 0000 0000 0000 0377 7777
  0561 7070 6c65 0363 6f6d 0000 0100 01 
Copy the code

This is an example of a Buffer. Don’t be nervous. First, see what console.log looks like after parsing.

The following is a DNS request packet to query the IP address of www.apple.com.

  //Header
  ID:  <Buffer 6d ca>
  FLAG:  QR:  0 opcode:  0 AA:  0 TC:  0 RD:  1
  RA:  0 zero:  0 recode:  0
  QDCOUNT:  <Buffer 00 01> ANCOUNT:  <Buffer 00 00> NSCOUNT:  <Buffer 00 00> ARCOUNT:  <Buffer 00 00>
  
  //QUESTION
  QNAME:  <Buffer 03 77 77 77 05 61 70 70 6c 65 03 63 6f 6d 00> QTYPE:  <Buffer 00 01> QCLASS:  <Buffer 00 01>
  
  QUESTION STRING:  www.apple.com 
Copy the code

Request packet parsing is divided into two pieces:

  • HeaderHeader resolution
  • QUESTIONQuery problem resolution

Header Header parsing

Parse the Header part.

Determine the size of each field:

ID: 2 bytes QR: 1bit Opcode: 4bit AA: 1bit TC: 1bit RD: 1bit RA: 1bit Z: 3bit RCODE: 4bit QDCOUNT: 2 bytes ANCOUNT: 2 bytes NSCOUNT: 2 bytes ARCOUNT: 2 bytesCopy the code

A total of 12 bytes.

It’s easy to parse the header if we discard the [3,4] bytes, but bits require bitwise manipulation of the buffer instance’s value.

So the values of the following parameters can be obtained directly from buffer:

  var header = {};

  header.id = buf.slice(0.2);
  header.qdcount = buf.slice(4.6);
  header.ancount = buf.slice(6.8);
  header.nscount = buf.slice(8.10);
  header.arcount = buf.slice(10.12);
Copy the code

The difficulty is how to obtain the value of [3,4]. First, the bytes corresponding to the buffer instance need to be converted into a binary string and then into a numeric value, and then calculate the final result according to the length of the parameter.

First, convert the buffer to a binary string and then to a numeric value (assuming the DNS packet is BUF) :

  // The third byte is converted to a base string of '2' and then converted to a numeric value
  var b = buf.slice(2.3).toString('binary'.0.1).charCodeAt(0);
Copy the code

Step 2: Data cutting:

The first thing to understand about this function is that it takes bits of length from offset, converts them to Integer bits, and returns them.

To put it bluntly, you convert the binary data you need to an Integer and return it.

  var bitSlice = function(b, offset, length) {
      return (b >>> (7-(offset+length- 1& ~ ()))0xff << length);
  };
Copy the code

Note that only one byte is considered === 8bit, so it can be written as (7-(offset+length-1)) and 0xff << length. If it is not a byte, you may need to change the values of the digits 7 and 0xff inside.

The demo start:

  'use strict';

  var buf = Buffer.from([0x2d]);
  var b = buf.toString('binary' , 0.1).charCodeAt(0);

  console.log(bitSlice(b , 0.1));/ / 0
  console.log(bitSlice(b , 1.1));/ / 0
  console.log(bitSlice(b , 2.1));/ / 1
  console.log(bitSlice(b , 3.1));/ / 0
  console.log(bitSlice(b , 4.1));/ / 1
  console.log(bitSlice(b , 5.1));/ / 1
  console.log(bitSlice(b , 6.1));/ / 0
  console.log(bitSlice(b , 7.1));/ / 1
  console.log(bitSlice(b , 5.3));/ / 5 = = = 0000, 0101

  /** * Hex: 0x2d * 10 base: 45 * 2 base: 0010 1101 ** (45,0,1) : 45>>>7 & ~(0xff<<1) * 45>>>7 = 0000 0000 * (0xff<<1) = 0000 0000 0000 0000 0000 0001 1111 1110 510 * ~(0xff<<1) = 1111 1111 1111 1111 1111 1110 0000 0001 -511 = -((0xff<<1)+1) * * 0000 0000 0000 0000 0000 0000 0000 0000 === 45>>>7 * & 1111  1111 1111 1111 1111 1110 0000 0001 === ~(0xff<<1) * ---------------------------------------- * 0000 0000 0000 0000 0000 0000 0000 = 0 * * (1,2,1) : 45>>>5 & ~(0xff<<1) * 45>>>5 = 0000 0001 * (0xff<<1) = 0000 0000 0000 0000 0000 0001 1111 1110 510 * ~(0xff<<1) = 1111 1111 1111 1111 1111 1110 0000 0001 -511 = -((0xff<<1)+1) * * 0000 0000 0000 0000 0000 0000 0000 0001 === 45>>>5 * & 1111  1111 1111 1111 1111 1110 0000 0001 === ~(0xff<<1) * ---------------------------------------- * 0000 0000 0000 0000 0000 0000 0000 0001 = 1 */
Copy the code

Once you understand what the above function does, you can actually use it to fetch the value in the [3,4] byte of the DNS packet Header.

Off the bat:

  // the third byte
  var b = buf.slice(2.3).toString('binary'.0.1).charCodeAt(0);
  header.qr = bitSlice(b,0.1);
  header.opcode = bitSlice(b,1.4);
  header.aa = bitSlice(b,5.1);
  header.tc = bitSlice(b,6.1);
  header.rd = bitSlice(b,7.1);
  
  // the fourth byte
  b = buf.slice(3.4).toString('binary'.0.1).charCodeAt(0);
  header.ra = bitSlice(b,0.1);
  header.z = bitSlice(b,1.3);
  header.rcode = bitSlice(b,4.4);
Copy the code

QUESTION Query field resolution

It mainly includes domain name query, protocol type and category.

The three parameters QTYPE and QCLASS are fixed 2 bytes, and QNAME is not fixed.

So be careful when fetching data, since the QUESTION message follows the Header, it is taken from the 12th byte back:

var question = {};
  question.qname = buf.slice(12, buf.length4 -);
  question.qtype = buf.slice(buf.length4 -, buf.length2 -);
  question.qclass = buf.slice(buf.length2 -, buf.length);
Copy the code

Qname uses a mixture of len+data encoding, ending in 0x00. Each string starts with a length, followed by content. The qname length must be 8 bytes.

For example www.apple.com(note: the middle. A buffer instance of this object is represented as:

03 77 77 77 05 61 70 70 6C 65 03 63 6F 6D 00Copy the code

That is, the first digit represents the length, followed by data of the same length, and so on.

  var domainify = function(qname) {
    var parts = [];

    for (var i = 0; i < qname.length && qname[i];) {
      var len = qname[i] , offset = i+1;// Get the length of each domain

      parts.push(qname.slice(offset,offset+len).toString());// Get each domain name

      i = offset+len;
    }

    return parts.join('. ');// put together a complete domain name
  };
Copy the code

Qtype Protocol type. Check the details

List of protocol types:

value Protocol type describe
1 A IPv4 address
2 NS Name server
5 CNAME The canonical name defines an alias for the official name of the host
6 SOA Start authorization marks the start of an area
11 WKS Be familiar with the network services provided by service definition hosts
12 PTR Pointers convert IP addresses to domain names
13 HINFO Host information This section describes the hardware and operating system used by a host
15 MX Mail exchange routes mail changes to the mail server
28 AAAA IPv6 address
252 AXFR Transmit requests for the entire district
255 ANY Requests for all records

Qclass is usually 1 and refers to Internet data.

Application scenario – DNS request proxy

Save the following code as a.js file and execute it using Node.js, using a machine on the same LAN to configure DNS to that machine.

The following code is for reference only:

  'use strict';

  const dgram = require('dgram');
  const dns = require('dns');
  const fs = require('fs');
  const server = dgram.createSocket('udp4');

  var bitSlice = function(b, offset, length) {
      return (b >>> (7-(offset+length- 1& ~ ()))0xff << length);
  };

  var domainify = function(qname) {
      var parts = [];

      for (var i = 0; i < qname.length && qname[i];) {
          var length = qname[i];
          var offset = i+1;

          parts.push(qname.slice(offset,offset+length).toString());

          i = offset+length;
      }

      return parts.join('. ');
  };

  var parse = function(buf) {
      var header = {};
      var question = {};
      var b = buf.slice(2.3).toString('binary'.0.1).charCodeAt(0);
      console.log('b:',b,buf.slice(2.3));
      header.id = buf.slice(0.2);
      header.qr = bitSlice(b,0.1);
      header.opcode = bitSlice(b,1.4);
      header.aa = bitSlice(b,5.1);
      header.tc = bitSlice(b,6.1);
      header.rd = bitSlice(b,7.1);

      b = buf.slice(3.4).toString('binary'.0.1).charCodeAt(0);

      header.ra = bitSlice(b,0.1);
      header.z = bitSlice(b,1.3);
      header.rcode = bitSlice(b,4.4);

      header.qdcount = buf.slice(4.6);
      header.ancount = buf.slice(6.8);
      header.nscount = buf.slice(8.10);
      header.arcount = buf.slice(10.12);

      question.qname = buf.slice(12, buf.length4 -);
      question.qtype = buf.slice(buf.length4 -, buf.length2 -);
      question.qclass = buf.slice(buf.length2 -, buf.length);

      return {header:header, question:question};
  };

  server.on('error' , (err)=>{
      console.log(`server error: ${err.stack}`);
  });

  server.on('message' , (msg , rinfo)=>{
      //fs.writeFile('dns.json' ,msg, {flag:'w',endcoding:'utf-8'} ,(err)=>{
      // console.log(err);
      / /});
      var query = parse(msg);
      console.log('ID:' ,query.header.id);
      console.log('FLAG:' , 'QR: ',query.header.qr , 'opcode: ',query.header.opcode , 'AA: ',query.header.aa , 'TC: ',query.header.tc,'RD: ',query.header.rd);
      
      console.log('RA: ',query.header.ra , 'zero: ',query.header.z , 'recode: ',query.header.rcode);

      console.log('QDCOUNT: ',query.header.qdcount , 'ANCOUNT: ' , query.header.ancount, 'NSCOUNT: ' , query.header.nscount,'ARCOUNT: ',query.header.arcount);
          
      console.log('QNAME: ',query.question.qname , 'QTYPE: ', query.question.qtype ,'QCLASS: ' , query.question.qclass);

      console.log('QUESTION STRING: ' ,domainify(query.question.qname));

      server.close();
  });

  server.on('listening', () = > {var address = server.address();
      console.log(`server listening ${address.address}:${address.port}`);
  });

  server.bind({port:53.address:'8.8.8.8'});//address needs to specify the IP address of the machine you want to use for proxying

Copy the code

The resources

Docstore. Mike. Ua/orelly/netw…

www.comptechdoc.org/independent…

www.iprotocolsec.com/2012/01/13/…