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