解析 DNS 响应应答部分未给出预期结果
我正在尝试使用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)
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我可能比您更了解这一点,但想知道您为什么这么说?
FirstByte
告诉您有一个指针,以下值(0x0C)显示了为压缩目的(如果我正确的话)的名称的偏移。在相同的字节中没有其他位于firstByte
中的其他位。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 asfirstByte
is set so that can be ignored from the point of view of the offset value