解析 DNS 响应答案部分未给出预期结果

Parsing DNS response Answer section doesn't give expected results

我正在尝试使用 java 解析 DNS 响应。我正在关注 RFC-1035 有关如何发送请求和接收响应的指南,即格式。

根据所述 RFC,响应的答案部分应如下所示:

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                                               |
/                                               /
/                      NAME                     /
|                                               |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      TYPE                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     CLASS                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      TTL                      |
|                                               |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                   RDLENGTH                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/                     RDATA                     /
/                                               /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

我正在尝试解析 NAME 部分。

根据第 4.1.4 节。消息压缩我应该能够获取前 2 位并确定下一个字节是标签还是指针。

到目前为止,这是我的代码,使用 CouldFlare 作为 DNS

,对于 google.comA 记录,请求工作正常
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Random;

public class Stuff {
    private static final String DNS_SERVER_ADDRESS = "1.1.1.1";
    private static final int DNS_SERVER_PORT = 53;

    public static void main(String[] args) throws IOException {

        String domain = "google.com";
        InetAddress ipAddress = InetAddress.getByName(DNS_SERVER_ADDRESS);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        //Whenever an octet represents a numeric quantity, the left most bit in
        //the diagram is the high order or most significant bit.
        //this goes for requests as well as responses


       short requestFlags = Short.parseShort("0000000100000000", 2);
        ByteBuffer bytes = ByteBuffer.allocate(2).putShort(requestFlags);
        byte[] array = bytes.array();
        dos.writeShort(1234); // randomly chosen id
        dos.write(array);
        dos.writeShort(1); // QDCOUNT
        dos.writeShort(0); // ANCOUNT
        dos.writeShort(0); // NSCOUNT
        dos.writeShort(0); // ARCOUNT

        String[] domainParts = domain.split("\.");
        System.out.println(domain + " has " + domainParts.length + " parts");

        for (int i = 0; i < domainParts.length; i++) {
            System.out.println("Writing: " + domainParts[i]);
            byte[] domainBytes = domainParts[i].getBytes(StandardCharsets.UTF_8);
            dos.writeByte(domainBytes.length);
            dos.write(domainBytes);
        }

        // No more parts
        dos.writeByte(0);
        // Type 0x01 = A (Host Request)
        dos.writeShort(1);
        // Class 0x01 = IN
        dos.writeShort(1);

        byte[] dnsFrame = baos.toByteArray();

        System.out.println("Sending: " + dnsFrame.length + " bytes");
        for (int i = 0; i < dnsFrame.length; i++) {
            System.out.print(String.format("%s", dnsFrame[i]) + " ");
        }

        DatagramSocket socket = new DatagramSocket();
        DatagramPacket dnsReqPacket = new DatagramPacket(dnsFrame, dnsFrame.length, ipAddress, DNS_SERVER_PORT);
        socket.send(dnsReqPacket);

        // Await response from DNS server
        byte[] buf = new byte[512];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);

        System.out.println("\n\nReceived: " + packet.getLength() + " bytes");

        for (int i = 0; i < packet.getLength(); i++) {
            System.out.print(String.format("%s", buf[i]) + " ");
        }
        System.out.println("\n");
        //All communications inside of the domain protocol are carried in a single
        //format called a message.  The top level format of message is divided
        //into 5 sections (some of which are empty in certain cases) shown below:
        //    +---------------------+
        //    |        Header       |
        //    +---------------------+
        //    |       Question      | the question for the name server
        //    +---------------------+
        //    |        Answer       | RRs answering the question
        //    +---------------------+
        //    |      Authority      | RRs pointing toward an authority
        //    +---------------------+
        //    |      Additional     | RRs holding additional information
        //    +---------------------+

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

        DataInputStream din = new DataInputStream(new ByteArrayInputStream(buf));
        System.out.println("\n\nStart response decode");
        System.out.println("Transaction ID: " + din.readShort()); // ID
        short flags = din.readByte();
        int QR = (flags & 0b10000000) >>> 7; //QR
        int opCode = ( flags & 0b01111000) >>> 3; //Opcode
        int AA = ( flags & 0b00000100) >>> 2; //AA
        int TC = ( flags & 0b00000010) >>> 1; //TC
        int RD =  flags & 0b00000001;//RD
        System.out.println("QR "+QR);
        System.out.println("Opcode "+opCode);
        System.out.println("AA "+AA);
        System.out.println("TC "+TC);
        System.out.println("RD "+RD);
        flags = din.readByte();
        int RA = (flags & 0b10000000) >>> 7;//RA
        int Z = ( flags & 0b01110000) >>> 4;//Z
        int RCODE =  flags & 0b00001111;//RCODE
        System.out.println("RA "+RA);
        System.out.println("Z "+ Z);
        System.out.println("RCODE " +RCODE);

        System.out.println("Questions: " + String.format("%s", din.readShort())); //QDCOUNT
        System.out.println("Answers RRs: " + String.format("%s", din.readShort())); //ANCOUNT
        System.out.println("Authority RRs: " + String.format("%s", din.readShort())); //NSCOUNT
        System.out.println("Additional RRs: " + String.format("%s", din.readShort())); //ARCOUNT

        // Question
        //      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                                               |
        //    /                     QNAME                     /
        //    /                                               /
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                     QTYPE                     |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                     QCLASS                    |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        int recLen;
        while ((recLen = din.readByte()) > 0) {
            byte[] record = new byte[recLen];
            for (int i = 0; i < recLen; i++) {
                record[i] = din.readByte();
            }
            System.out.println("Record: " + new String(record, StandardCharsets.UTF_8)); //QNAME
        }
        System.out.println("Record Type: " + String.format("%s", din.readShort()));//QTYPE
        System.out.println("QCLASS - Class: " + String.format("%s", din.readShort())); // QCLASS
        System.out.println("\n\nstart answer, authority, and additional sections\n");

        //The answer, authority, and additional
        //      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                                               |
        //    /                                               /
        //    /                      NAME                     /
        //    |                                               |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                      TYPE                     |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                     CLASS                     |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                      TTL                      |
        //    |                                               |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    |                   RDLENGTH                    |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
        //    /                     RDATA                     /
        //    /                                               /
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        byte firstBytes = din.readByte();
        int firstTwoBits = (firstBytes & 0b11000000) >>> 6;
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        //    | 1  1|                OFFSET                   |
        //    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
        // 11 for offset
        //(The 10 and 01 combinations
        //are reserved for future use.)
        // 00 for label
        //label must begin with two zero bits because
        //labels are restricted to 63 octets or less
        System.out.println(firstTwoBits);
        if(firstTwoBits == 3) {
            System.out.println("It's a pointer");
        }else if(firstTwoBits == 0){
            System.out.println("It's a label");
        }

    }
}

以及打印的信息

google.com has 2 parts
Writing: google
Writing: com
Sending: 28 bytes
4 -46 1 0 0 1 0 0 0 0 0 0 6 103 111 111 103 108 101 3 99 111 109 0 0 1 0 1 

Received: 44 bytes
4 -46 -127 -128 0 1 0 1 0 0 0 0 6 103 111 111 103 108 101 3 99 111 109 0 0 1 0 1 -64 12 0 1 0 1 0 0 1 27 0 4 -114 -5 37 110 



Start response decode
Transaction ID: 1234
QR 1
Opcode 0
AA 0
TC 0
RD 1
RA 1
Z 0
RCODE 0
Questions: 1
Answers RRs: 1
Authority RRs: 0
Additional RRs: 0
Record: google
Record: com
Record Type: 1
QCLASS - Class: 1


start answer, authority, and additional sections

3
It's a pointer

Process finished with exit code 0

将其与命令行 DNS 查找工具进行比较

id 1329, opcode QUERY, rcode NOERROR, flags QR RD RA
;QUESTION
google.com. IN A
;ANSWER
google.com. 257 IN A 142.250.81.206
;AUTHORITY
;ADDITIONAL

我得到的结果似乎有效。

我的问题是我似乎无法解析答案部分的 NAME。它似乎以一个毫无意义的指针开始。

显然我在这里做错了什么,但我不知道是什么。

我知道我问的是含糊不清的问题,但话又说回来,如果我知道问题出在哪里,我就不会来这里了。

我还使用 Wireshark 来拦截我所做的请求以确保响应正确。它似乎确实是正确的(我显然已经将 url 更改为 whosebug.com)

My problem is that I can't seem to parse the NAME in the answer section. It seems to start with a pointer which makes no sense.

我对这方面的了解可能比你少得多,但我想知道你为什么这么说? firstByte 告诉你有一个指针,下面的值 (0x0c) 显示了用于压缩目的的名称偏移量(如果我没记错的话)。 None 与 firstByte 相同字节中的其他位被设置为从偏移值的角度可以忽略