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

发布于 2025-01-18 20:46:33 字数 12016 浏览 1 评论 0原文

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

根据所述RFC,响应的答案部分应该如此:

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

我正在尝试解析name部分。

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

到目前为止,这是我的代码,该请求对a google> google.com使用canflare用作DNS的记录正常运行

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

我得到的结果似乎有效。

我的问题是,我似乎无法在答案部分中解析名称。它似乎是从指针毫无意义的指针开始。

显然,我在这里做错了什么,但我不能说什么。

我知道我要问的是模糊的,但是如果我知道问题是什么,我就不会在这里。

我还使用Wireshark来概念我为确保响应正确的请求。似乎确实是正确的(显然我已经将URL更改为stackoverflow.com)

https://i.sstatic.net/0ixy7.png“ alt =”在此处输入图像说明”>

I'm trying to parse a DNS response using java. I'm following RFC-1035 for guidelines on how to send requests and receieve responses, the format that is.

According to said RFC the answer section of a response should look like so:

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

I'm trying to parse the NAME section.

According to section 4.1.4. Message compression I should be able to get the first 2 bits and determine if the next byte is either a label or a pointer.

Here's my code so far, the request works just fine for a A record of google.com using CouldFlare as a DNS

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");
        }

    }
}

And the printed info

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

Comparing this with a command line DNS lookup tool

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

The result I get seems valid.

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.

Clearly I'm doing something wrong here but I can't tell what.

I understand that what I'm asking is vague but then again if I knew what the issue was I wouldn't be here.

I've also used Wireshark to incercept a request I did to make sure the response is correct. It seems to be correct indeed(I've obviously changed the url to stackoverflow.com)

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

怎樣才叫好 2025-01-25 20:46:33

我的问题是我似乎无法在答案中解析名称
部分。它似乎是从指针毫无意义的指针开始。

我可能比您更了解这一点,但想知道您为什么这么说? FirstByte告诉您有一个指针,以下值(0x0C)显示了为压缩目的(如果我正确的话)的名称的偏移。在相同的字节中没有其他位于firstByte中的其他位。

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.

I probably know at lot less about this than you but am wondering why you say that? firstByte is telling you there's a pointer and the following value (0x0c) shows you the offset of the name for compression purposes (if I've got that right). None of the other bits in the same byte as firstByte is set so that can be ignored from the point of view of the offset value

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文