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