• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.text.TextUtils;
22 
23 import com.android.internal.util.BitUtils;
24 
25 import java.nio.BufferUnderflowException;
26 import java.nio.ByteBuffer;
27 import java.text.DecimalFormat;
28 import java.text.FieldPosition;
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  * Defines basic data for DNS protocol based on RFC 1035.
34  * Subclasses create the specific format used in DNS packet.
35  *
36  * @hide
37  */
38 public abstract class DnsPacket {
39     public class DnsHeader {
40         private static final String TAG = "DnsHeader";
41         public final int id;
42         public final int flags;
43         public final int rcode;
44         private final int[] mRecordCount;
45 
46         /**
47          * Create a new DnsHeader from a positioned ByteBuffer.
48          *
49          * The ByteBuffer must be in network byte order (which is the default).
50          * Reads the passed ByteBuffer from its current position and decodes a DNS header.
51          * When this constructor returns, the reading position of the ByteBuffer has been
52          * advanced to the end of the DNS header record.
53          * This is meant to chain with other methods reading a DNS response in sequence.
54          */
DnsHeader(@onNull ByteBuffer buf)55         DnsHeader(@NonNull ByteBuffer buf) throws BufferUnderflowException {
56             id = BitUtils.uint16(buf.getShort());
57             flags = BitUtils.uint16(buf.getShort());
58             rcode = flags & 0xF;
59             mRecordCount = new int[NUM_SECTIONS];
60             for (int i = 0; i < NUM_SECTIONS; ++i) {
61                 mRecordCount[i] = BitUtils.uint16(buf.getShort());
62             }
63         }
64 
65         /**
66          * Get record count by type.
67          */
getRecordCount(int type)68         public int getRecordCount(int type) {
69             return mRecordCount[type];
70         }
71     }
72 
73     /**
74      * Superclass for DNS questions and DNS resource records.
75      *
76      * DNS questions (No TTL/RDATA)
77      * DNS resource records (With TTL/RDATA)
78      */
79     public class DnsRecord {
80         private static final int MAXNAMESIZE = 255;
81         private static final int MAXLABELSIZE = 63;
82         private static final int MAXLABELCOUNT = 128;
83         private static final int NAME_NORMAL = 0;
84         private static final int NAME_COMPRESSION = 0xC0;
85         private final DecimalFormat byteFormat = new DecimalFormat();
86         private final FieldPosition pos = new FieldPosition(0);
87 
88         private static final String TAG = "DnsRecord";
89 
90         public final String dName;
91         public final int nsType;
92         public final int nsClass;
93         public final long ttl;
94         private final byte[] mRdata;
95 
96         /**
97          * Create a new DnsRecord from a positioned ByteBuffer.
98          *
99          * Reads the passed ByteBuffer from its current position and decodes a DNS record.
100          * When this constructor returns, the reading position of the ByteBuffer has been
101          * advanced to the end of the DNS header record.
102          * This is meant to chain with other methods reading a DNS response in sequence.
103          *
104          * @param ByteBuffer input of record, must be in network byte order
105          *         (which is the default).
106          */
DnsRecord(int recordType, @NonNull ByteBuffer buf)107         DnsRecord(int recordType, @NonNull ByteBuffer buf)
108                 throws BufferUnderflowException, ParseException {
109             dName = parseName(buf, 0 /* Parse depth */);
110             if (dName.length() > MAXNAMESIZE) {
111                 throw new ParseException(
112                         "Parse name fail, name size is too long: " + dName.length());
113             }
114             nsType = BitUtils.uint16(buf.getShort());
115             nsClass = BitUtils.uint16(buf.getShort());
116 
117             if (recordType != QDSECTION) {
118                 ttl = BitUtils.uint32(buf.getInt());
119                 final int length = BitUtils.uint16(buf.getShort());
120                 mRdata = new byte[length];
121                 buf.get(mRdata);
122             } else {
123                 ttl = 0;
124                 mRdata = null;
125             }
126         }
127 
128         /**
129          * Get a copy of rdata.
130          */
131         @Nullable
getRR()132         public byte[] getRR() {
133             return (mRdata == null) ? null : mRdata.clone();
134         }
135 
136         /**
137          * Convert label from {@code byte[]} to {@code String}
138          *
139          * Follows the same conversion rules of the native code (ns_name.c in libc)
140          */
labelToString(@onNull byte[] label)141         private String labelToString(@NonNull byte[] label) {
142             final StringBuffer sb = new StringBuffer();
143             for (int i = 0; i < label.length; ++i) {
144                 int b = BitUtils.uint8(label[i]);
145                 // Control characters and non-ASCII characters.
146                 if (b <= 0x20 || b >= 0x7f) {
147                     // Append the byte as an escaped decimal number, e.g., "\19" for 0x13.
148                     sb.append('\\');
149                     byteFormat.format(b, sb, pos);
150                 } else if (b == '"' || b == '.' || b == ';' || b == '\\'
151                         || b == '(' || b == ')' || b == '@' || b == '$') {
152                     // Append the byte as an escaped character, e.g., "\:" for 0x3a.
153                     sb.append('\\');
154                     sb.append((char) b);
155                 } else {
156                     // Append the byte as a character, e.g., "a" for 0x61.
157                     sb.append((char) b);
158                 }
159             }
160             return sb.toString();
161         }
162 
parseName(@onNull ByteBuffer buf, int depth)163         private String parseName(@NonNull ByteBuffer buf, int depth) throws
164                 BufferUnderflowException, ParseException {
165             if (depth > MAXLABELCOUNT) {
166                 throw new ParseException("Failed to parse name, too many labels");
167             }
168             final int len = BitUtils.uint8(buf.get());
169             final int mask = len & NAME_COMPRESSION;
170             if (0 == len) {
171                 return "";
172             } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION) {
173                 throw new ParseException("Parse name fail, bad label type");
174             } else if (mask == NAME_COMPRESSION) {
175                 // Name compression based on RFC 1035 - 4.1.4 Message compression
176                 final int offset = ((len & ~NAME_COMPRESSION) << 8) + BitUtils.uint8(buf.get());
177                 final int oldPos = buf.position();
178                 if (offset >= oldPos - 2) {
179                     throw new ParseException("Parse compression name fail, invalid compression");
180                 }
181                 buf.position(offset);
182                 final String pointed = parseName(buf, depth + 1);
183                 buf.position(oldPos);
184                 return pointed;
185             } else {
186                 final byte[] label = new byte[len];
187                 buf.get(label);
188                 final String head = labelToString(label);
189                 if (head.length() > MAXLABELSIZE) {
190                     throw new ParseException("Parse name fail, invalid label length");
191                 }
192                 final String tail = parseName(buf, depth + 1);
193                 return TextUtils.isEmpty(tail) ? head : head + "." + tail;
194             }
195         }
196     }
197 
198     public static final int QDSECTION = 0;
199     public static final int ANSECTION = 1;
200     public static final int NSSECTION = 2;
201     public static final int ARSECTION = 3;
202     private static final int NUM_SECTIONS = ARSECTION + 1;
203 
204     private static final String TAG = DnsPacket.class.getSimpleName();
205 
206     protected final DnsHeader mHeader;
207     protected final List<DnsRecord>[] mRecords;
208 
DnsPacket(@onNull byte[] data)209     protected DnsPacket(@NonNull byte[] data) throws ParseException {
210         if (null == data) throw new ParseException("Parse header failed, null input data");
211         final ByteBuffer buffer;
212         try {
213             buffer = ByteBuffer.wrap(data);
214             mHeader = new DnsHeader(buffer);
215         } catch (BufferUnderflowException e) {
216             throw new ParseException("Parse Header fail, bad input data", e);
217         }
218 
219         mRecords = new ArrayList[NUM_SECTIONS];
220 
221         for (int i = 0; i < NUM_SECTIONS; ++i) {
222             final int count = mHeader.getRecordCount(i);
223             if (count > 0) {
224                 mRecords[i] = new ArrayList(count);
225             }
226             for (int j = 0; j < count; ++j) {
227                 try {
228                     mRecords[i].add(new DnsRecord(i, buffer));
229                 } catch (BufferUnderflowException e) {
230                     throw new ParseException("Parse record fail", e);
231                 }
232             }
233         }
234     }
235 }
236