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 = buffer.readUnsignedShort();
59              final int answerCount = buffer.readUnsignedShort();
60              final int authorityRecordCount = buffer.readUnsignedShort();
61              final int additionalRecordCount = buffer.readUnsignedShort();
62  
63              decodeQuestions(response, buffer, questionCount);
64              if (!decodeRecords(response, DnsSection.ANSWER, buffer, answerCount)) {
65                  success = true;
66                  return response;
67              }
68              if (!decodeRecords(response, DnsSection.AUTHORITY, buffer, authorityRecordCount)) {
69                  success = true;
70                  return response;
71              }
72  
73              decodeRecords(response, DnsSection.ADDITIONAL, buffer, additionalRecordCount);
74              success = true;
75              return response;
76          } finally {
77              if (!success) {
78                  response.release();
79              }
80          }
81      }
82  
83      protected abstract DnsResponse newResponse(A sender, A recipient, int id,
84                                                 DnsOpCode opCode, DnsResponseCode responseCode) throws Exception;
85  
86      private void decodeQuestions(DnsResponse response, ByteBuf buf, int questionCount) throws Exception {
87          for (int i = questionCount; i > 0; i --) {
88              response.addRecord(DnsSection.QUESTION, recordDecoder.decodeQuestion(buf));
89          }
90      }
91  
92      private boolean decodeRecords(
93              DnsResponse response, DnsSection section, ByteBuf buf, int count) throws Exception {
94          for (int i = count; i > 0; i --) {
95              final DnsRecord r = recordDecoder.decodeRecord(buf);
96              if (r == null) {
97                  // Truncated response
98                  return false;
99              }
100 
101             response.addRecord(section, r);
102         }
103         return true;
104     }
105 }