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