View Javadoc
1   /*
2    * Copyright 2019 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.dns;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.handler.codec.CorruptedFrameException;
20  
21  import java.net.SocketAddress;
22  
23  import static io.netty.util.internal.ObjectUtil.checkNotNull;
24  
25  abstract class DnsResponseDecoder<A extends SocketAddress> {
26  
27      private final DnsRecordDecoder recordDecoder;
28  
29      /**
30       * Creates a new decoder with the specified {@code recordDecoder}.
31       */
32      DnsResponseDecoder(DnsRecordDecoder recordDecoder) {
33          this.recordDecoder = checkNotNull(recordDecoder, "recordDecoder");
34      }
35  
36      final DnsResponse decode(A sender, A recipient, ByteBuf buffer) throws Exception {
37          final int id = buffer.readUnsignedShort();
38  
39          final int flags = buffer.readUnsignedShort();
40          if (flags >> 15 == 0) {
41              throw new CorruptedFrameException("not a response");
42          }
43  
44          final DnsResponse response = newResponse(
45                  sender,
46                  recipient,
47                  id,
48                  DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)), DnsResponseCode.valueOf((byte) (flags & 0xf)));
49  
50          response.setRecursionDesired((flags >> 8 & 1) == 1);
51          response.setAuthoritativeAnswer((flags >> 10 & 1) == 1);
52          response.setTruncated((flags >> 9 & 1) == 1);
53          response.setRecursionAvailable((flags >> 7 & 1) == 1);
54          response.setZ(flags >> 4 & 0x7);
55  
56          boolean success = false;
57          try {
58              final int questionCount = DnsCodecUtil.checkRecordCount(DnsSection.QUESTION, buffer.readUnsignedShort());
59              final int answerCount = DnsCodecUtil.checkRecordCount(DnsSection.ANSWER, buffer.readUnsignedShort());
60              final int authorityRecordCount =
61                      DnsCodecUtil.checkRecordCount(DnsSection.AUTHORITY, buffer.readUnsignedShort());
62              final int additionalRecordCount =
63                      DnsCodecUtil.checkRecordCount(DnsSection.ADDITIONAL, buffer.readUnsignedShort());
64  
65              decodeQuestions(response, buffer, questionCount);
66              if (!decodeRecords(response, DnsSection.ANSWER, buffer, answerCount)) {
67                  success = true;
68                  return response;
69              }
70              if (!decodeRecords(response, DnsSection.AUTHORITY, buffer, authorityRecordCount)) {
71                  success = true;
72                  return response;
73              }
74  
75              decodeRecords(response, DnsSection.ADDITIONAL, buffer, additionalRecordCount);
76              success = true;
77              return response;
78          } finally {
79              if (!success) {
80                  response.release();
81              }
82          }
83      }
84  
85      protected abstract DnsResponse newResponse(A sender, A recipient, int id,
86                                                 DnsOpCode opCode, DnsResponseCode responseCode) throws Exception;
87  
88      private void decodeQuestions(DnsResponse response, ByteBuf buf, int questionCount) throws Exception {
89          for (int i = questionCount; i > 0; i --) {
90              response.addRecord(DnsSection.QUESTION, recordDecoder.decodeQuestion(buf));
91          }
92      }
93  
94      private boolean decodeRecords(
95              DnsResponse response, DnsSection section, ByteBuf buf, int count) throws Exception {
96          for (int i = count; i > 0; i --) {
97              final DnsRecord r = recordDecoder.decodeRecord(buf);
98              if (r == null) {
99                  // Truncated response
100                 return false;
101             }
102 
103             response.addRecord(section, r);
104         }
105         return true;
106     }
107 }