我现在拥有的是通过UDP进行DNS查询并且它正常工作,但是如果消息被截断并且我需要通过TCP重新连接相同的查询我不能这样做,主要的问题是我确实有字节查询array和tcp发送字符。
我汇编了UDP查询:
String DNS_SERVER_ADDRESS = args[0];
String domain = args[1];
ipAddress = InetAddress.getByName(DNS_SERVER_ADDRESS);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
dos = new DataOutputStream(baos);
// *** Build a DNS Request Frame ****
// Identifier: A 16-bit identification field generated by the device that creates the DNS query.
// It is copied by the server into the response, so it can be used by that device to match that
// query to the corresponding reply received from a DNS server. This is used in a manner similar
// to how the Identifier field is used in many of the ICMP message types.
dos.writeShort(0x1234);
// Write Query Flags
dos.writeShort(0x0100);
// Question Count: Specifies the number of questions in the Question section of the message.
dos.writeShort(0x0001);
// Answer Record Count: Specifies the number of resource records in the Answer section of the message.
dos.writeShort(0x0000);
// Authority Record Count: Specifies the number of resource records in the Authority section of
// the message. (“NS” stands for “name server”)
dos.writeShort(0x0000);
// Additional Record Count: Specifies the number of resource records in the Additional section of the message.
dos.writeShort(0x0000);
// TODO: write query
String[] domainParts = domain.split("\\.");
System.out.println(domain + " has " + domainParts.length + " parts");
for (String domainPart : domainParts) {
System.out.println("Writing: " + domainPart);
byte[] domainBytes = domainPart.getBytes("UTF-8");
dos.writeByte(domainBytes.length);
dos.write(domainBytes);
}
// No more parts
dos.writeByte(0x00);
// QType 0x01 = A (Host Request)
if (args.length>2)
dos.writeShort(typeEncode(args[2]));
else
dos.writeShort(0x00ff); // "ANY" as default
// QClass 0x01 = IN
dos.writeShort(0x0001);
dnsFrame = baos.toByteArray();
System.out.println("Sending: " + dnsFrame.length + " bytes");
for (byte aDnsFrame : dnsFrame) {
System.out.print("0x" + String.format("%x", aDnsFrame) + " ");
}
// *** Send DNS Request Frame ***
DatagramSocket socket = new DatagramSocket();
DatagramPacket dnsReqPacket = new DatagramPacket(dnsFrame, dnsFrame.length, ipAddress, DNS_SERVER_PORT);
socket.send(dnsReqPacket);
return socket;
我如何尝试通过TCP发送它:
Socket echoSocket = null;
// strumień do zapisu do serwera
Writer out = null;
// strumień do odczytu z serwera
BufferedReader in = null;
// nazwa serwera
String hostname=args[0];
try {
System.out.println("próba utworzenia gniazda");
echoSocket = new Socket(ipAddress, DNS_SERVER_PORT);
System.out.println("próba utworzenia strumienia wyjściowego");
out = new PrintWriter(echoSocket.getOutputStream(), true);
OutputStream outputStream = echoSocket.getOutputStream();
System.out.println("próba utworzenia strumienia wejściowego");
in = new BufferedReader(new InputStreamReader(echoSocket.getInputStream()));
ObjectOutputStream os = new ObjectOutputStream(echoSocket.getOutputStream());
os.flush();
ObjectInputStream is = new ObjectInputStream(echoSocket.getInputStream());
os.writeObject(dnsFrame);
byte[] temp = (byte[]) is.readObject();
} catch (UnknownHostException e) {
System.err.println("Nieznany host: " + hostname + ".");
System.exit(1);
} catch (IOException e) {
System.err.println("Błąd połączenia z " + hostname + ".");
System.exit(1);
}
// zakończenie pracy - pozamykaj strumienie i gniazda
out.close();
in.close();
echoSocket.close();
我使用wireshark来读取我在TCP中实际发送的内容,它与UDP不同,所以我假设问题正在发送字节数组。
答案 0 :(得分:5)
DNS的TCP协议与其UDP协议相同,但有一点不同 - 通过TCP发送的消息以网络字节顺序的16位整数作为前缀,以指定消息字节长度。这不是UDP所必需的,因为消息长度由数据报的大小决定。
Per RFC 1035,"域名 - 实施和规范":
4.2。传输
DNS假定消息将作为数据报或在数据报中传输 虚拟电路承载的字节流。虽然虚拟电路可以 用于任何DNS活动,数据报是由于的查询首选 它们的开销更低,性能更好。区域刷新活动 必须使用虚拟电路,因为需要可靠的传输。
Internet支持在服务器上使用TCP [RFC-793]进行名称服务器访问 端口53(十进制)以及UDP上使用UDP [RFC-768]的数据报访问 53号端口(十进制)。
4.2.1。 UDP使用
使用UDP用户服务器端口53(十进制)发送的消息。
UDP承载的消息限制为512字节(不包括IP 或UDP标头)。截断更长的消息并设置TC位 标题。
UDP不适用于区域传输,但建议使用方法 用于Internet中的标准查询。使用UDP发送的查询可能是 丢失,因此需要重传策略。查询或他们的 响应可以由网络重新排序,也可以通过名称处理 服务器,因此解析器不应该依赖它们按顺序返回。
最佳UDP重传策略将随着性能而变化 互联网和客户的需求,但建议如下:
在重复查询服务器的特定地址之前,客户端应尝试其他服务器和服务器地址。
如果可能,重传间隔应基于先前的统计。过于激进的重传可能会轻易减慢整个社区的响应速度。根据客户端与预期服务器的连接情况,最小重传间隔应为2-5秒。
有关服务器选择和重传策略的更多建议可以 在本备忘录的解析器部分找到。
4.2.2。 TCP使用
通过TCP连接发送的消息使用服务器端口53(十进制)。的的 消息以两个字节长度字段为前缀,该字段给出消息长度,不包括两个字节长度字段。这个长度字段允许 在开始之前组装完整消息的低级处理 解析它。
建议使用多种连接管理策略:
服务器不应阻止等待TCP数据的其他活动。
服务器应支持多个连接。
服务器应该假设客户端将启动连接关闭,并且应该延迟关闭其连接结束,直到满足所有未完成的客户端请求。
如果服务器需要关闭休眠连接以回收资源,它应该等到连接空闲一段时间大约两分钟。特别是,服务器应该允许在单个连接上进行SOA和AXFR请求序列(开始刷新操作)。由于服务器无论如何都无法回答查询,因此可以使用单边关闭或重置而不是优雅关闭。
因此,您在TCP中所要做的就是:
将TCP套接字连接到DNS服务器
发送DNS查询时,在UDP中以完全相同的方式创建消息字节数组,然后将字节数组的长度作为16位整数发送发送字节数组本身。
读取DNS响应时,首先读取16位长度,然后读取它指定的字节数。
根据需要重复步骤2-3,无论您需要发送多少查询。
完成后关闭连接。
另见:
RFC 7766: DNS Transport over TCP - Implementation Requirements
简而言之,请勿使用基于字符串字符,对象等的I / O类,就像您目前正在尝试的那样。 DNS消息是二进制数据,并且在UDP和TCP中以完全相同的方式格式化。无论您使用哪种传输,生成和解析DNS消息的代码都应完全相同。唯一不同的是如何传输/接收原始消息字节:
在UDP中使用DatagramSocket
和DatagramPacket
。
在TCP中使用Socket
,OutputStream
和InputStream
。您可以使用DataOutputStream
和DataInputStream
来帮助您,其中: