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 }