1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.codec.dns;
17
18 import io.netty5.buffer.api.Buffer;
19 import io.netty5.channel.socket.SocketProtocolFamily;
20 import io.netty5.handler.codec.UnsupportedMessageTypeException;
21 import io.netty5.util.internal.StringUtil;
22 import io.netty5.util.internal.UnstableApi;
23
24 import static io.netty5.handler.codec.dns.DnsCodecUtil.addressNumber;
25
26
27
28
29
30
31 @UnstableApi
32 public class DefaultDnsRecordEncoder implements DnsRecordEncoder {
33 private static final int PREFIX_MASK = Byte.SIZE - 1;
34
35
36
37
38 protected DefaultDnsRecordEncoder() { }
39
40 @Override
41 public final void encodeQuestion(DnsQuestion question, Buffer out) throws Exception {
42 encodeName(question.name(), out);
43 out.ensureWritable(4);
44 out.writeShort((short) question.type().intValue());
45 out.writeShort((short) question.dnsClass());
46 }
47
48 @Override
49 public void encodeRecord(DnsRecord record, Buffer out) throws Exception {
50 if (record instanceof DnsQuestion) {
51 encodeQuestion((DnsQuestion) record, out);
52 } else if (record instanceof DnsPtrRecord) {
53 encodePtrRecord((DnsPtrRecord) record, out);
54 } else if (record instanceof DnsOptEcsRecord) {
55 encodeOptEcsRecord((DnsOptEcsRecord) record, out);
56 } else if (record instanceof DnsOptPseudoRecord) {
57 encodeOptPseudoRecord((DnsOptPseudoRecord) record, out);
58 } else if (record instanceof DnsRawRecord) {
59 encodeRawRecord((DnsRawRecord) record, out);
60 } else {
61 throw new UnsupportedMessageTypeException(StringUtil.simpleClassName(record));
62 }
63 }
64
65 private void encodeRecord0(DnsRecord record, Buffer out) throws Exception {
66 encodeName(record.name(), out);
67 out.ensureWritable(8);
68 out.writeShort((short) record.type().intValue());
69 out.writeShort((short) record.dnsClass());
70 out.writeInt((int) record.timeToLive());
71 }
72
73 private void encodePtrRecord(DnsPtrRecord record, Buffer out) throws Exception {
74 encodeRecord0(record, out);
75 encodeName(record.hostname(), out);
76 }
77
78 private void encodeOptPseudoRecord(DnsOptPseudoRecord record, Buffer out) throws Exception {
79 encodeRecord0(record, out);
80 out.ensureWritable(2);
81 out.writeShort((short) 0);
82 }
83
84 private void encodeOptEcsRecord(DnsOptEcsRecord record, Buffer out) throws Exception {
85 encodeRecord0(record, out);
86
87 int sourcePrefixLength = record.sourcePrefixLength();
88 int scopePrefixLength = record.scopePrefixLength();
89 int lowOrderBitsToPreserve = sourcePrefixLength & PREFIX_MASK;
90
91 byte[] bytes = record.address();
92 int addressBits = bytes.length << 3;
93 if (addressBits < sourcePrefixLength || sourcePrefixLength < 0) {
94 throw new IllegalArgumentException(sourcePrefixLength + ": " +
95 sourcePrefixLength + " (expected: 0 >= " + addressBits + ')');
96 }
97
98
99 final short addressNumber = (short) addressNumber(bytes.length == 4 ?
100 SocketProtocolFamily.INET : SocketProtocolFamily.INET6);
101 int payloadLength = calculateEcsAddressLength(sourcePrefixLength, lowOrderBitsToPreserve);
102
103 int fullPayloadLength = 2 +
104 2 +
105 2 +
106 1 +
107 1 +
108 payloadLength;
109
110 out.ensureWritable(fullPayloadLength);
111 out.writeShort((short) fullPayloadLength);
112 out.writeShort((short) 8);
113
114 out.writeShort((short) (fullPayloadLength - 4));
115 out.writeShort(addressNumber);
116 out.writeByte((byte) sourcePrefixLength);
117 out.writeByte((byte) scopePrefixLength);
118
119 if (lowOrderBitsToPreserve > 0) {
120 int bytesLength = payloadLength - 1;
121 out.writeBytes(bytes, 0, bytesLength);
122
123
124 out.writeByte(padWithZeros(bytes[bytesLength], lowOrderBitsToPreserve));
125 } else {
126
127 out.writeBytes(bytes, 0, payloadLength);
128 }
129 }
130
131
132 static int calculateEcsAddressLength(int sourcePrefixLength, int lowOrderBitsToPreserve) {
133 return (sourcePrefixLength >>> 3) + (lowOrderBitsToPreserve != 0 ? 1 : 0);
134 }
135
136 private void encodeRawRecord(DnsRawRecord record, Buffer out) throws Exception {
137 encodeRecord0(record, out);
138
139 Buffer content = record.content();
140 int contentLen = content.readableBytes();
141
142 out.ensureWritable(2 + contentLen);
143 out.writeShort((short) contentLen);
144 content.copyInto(content.readerOffset(), out, out.writerOffset(), contentLen);
145 out.skipWritableBytes(contentLen);
146 }
147
148 protected void encodeName(String name, Buffer buf) throws Exception {
149 DnsCodecUtil.encodeDomainName(name, buf);
150 }
151
152 private static byte padWithZeros(byte b, int lowOrderBitsToPreserve) {
153 switch (lowOrderBitsToPreserve) {
154 case 0:
155 return 0;
156 case 1:
157 return (byte) (0x80 & b);
158 case 2:
159 return (byte) (0xC0 & b);
160 case 3:
161 return (byte) (0xE0 & b);
162 case 4:
163 return (byte) (0xF0 & b);
164 case 5:
165 return (byte) (0xF8 & b);
166 case 6:
167 return (byte) (0xFC & b);
168 case 7:
169 return (byte) (0xFE & b);
170 case 8:
171 return b;
172 default:
173 throw new IllegalArgumentException("lowOrderBitsToPreserve: " + lowOrderBitsToPreserve);
174 }
175 }
176 }