1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.directory.server.dns.io.decoder;
22
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.EnumMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import org.apache.directory.server.dns.messages.DnsMessage;
32 import org.apache.directory.server.dns.messages.DnsMessageModifier;
33 import org.apache.directory.server.dns.messages.MessageType;
34 import org.apache.directory.server.dns.messages.OpCode;
35 import org.apache.directory.server.dns.messages.QuestionRecord;
36 import org.apache.directory.server.dns.messages.RecordClass;
37 import org.apache.directory.server.dns.messages.RecordType;
38 import org.apache.directory.server.dns.messages.ResourceRecord;
39 import org.apache.directory.server.dns.messages.ResourceRecordImpl;
40 import org.apache.directory.server.dns.messages.ResponseCode;
41 import org.apache.directory.server.i18n.I18n;
42 import org.apache.mina.core.buffer.IoBuffer;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46
47
48
49
50
51
52
53
54
55 public class DnsMessageDecoder
56 {
57
58 private final Logger logger = LoggerFactory.getLogger( DnsMessageDecoder.class );
59
60
61
62
63 private static final Map<RecordType, RecordDecoder> DEFAULT_DECODERS;
64
65 static
66 {
67 EnumMap<RecordType, RecordDecoder> map = new EnumMap<>( RecordType.class );
68
69 map.put( RecordType.A, new AddressRecordDecoder() );
70 map.put( RecordType.NS, new NameServerRecordDecoder() );
71 map.put( RecordType.MX, new MailExchangeRecordDecoder() );
72 map.put( RecordType.AAAA, new IPv6RecordDecoder() );
73
74 DEFAULT_DECODERS = Collections.unmodifiableMap( map );
75 }
76
77
78
79
80
81
82
83
84
85 public DnsMessage decode( IoBuffer in ) throws IOException
86 {
87 DnsMessageModifieressages/DnsMessageModifier.html#DnsMessageModifier">DnsMessageModifier modifier = new DnsMessageModifier();
88
89 modifier.setTransactionId( in.getUnsignedShort() );
90
91 byte header = in.get();
92 modifier.setMessageType( decodeMessageType( header ) );
93 modifier.setOpCode( decodeOpCode( header ) );
94 modifier.setAuthoritativeAnswer( decodeAuthoritativeAnswer( header ) );
95 modifier.setTruncated( decodeTruncated( header ) );
96 modifier.setRecursionDesired( decodeRecursionDesired( header ) );
97
98 header = in.get();
99 modifier.setRecursionAvailable( decodeRecursionAvailable( header ) );
100 modifier.setResponseCode( decodeResponseCode( header ) );
101
102 short questionCount = in.getShort();
103 short answerCount = in.getShort();
104 short authorityCount = in.getShort();
105 short additionalCount = in.getShort();
106
107 logger.debug( "decoding {} question records", questionCount );
108 modifier.setQuestionRecords( getQuestions( in, questionCount ) );
109
110 logger.debug( "decoding {} answer records", answerCount );
111 modifier.setAnswerRecords( getRecords( in, answerCount ) );
112
113 logger.debug( "decoding {} authority records", authorityCount );
114 modifier.setAuthorityRecords( getRecords( in, authorityCount ) );
115
116 logger.debug( "decoding {} additional records", additionalCount );
117 modifier.setAdditionalRecords( getRecords( in, additionalCount ) );
118
119 return modifier.getDnsMessage();
120 }
121
122
123 private List<ResourceRecord> getRecords( IoBuffer byteBuffer, short recordCount ) throws IOException
124 {
125 List<ResourceRecord> records = new ArrayList<>( recordCount );
126
127 for ( int ii = 0; ii < recordCount; ii++ )
128 {
129 String domainName = getDomainName( byteBuffer );
130 RecordType recordType = RecordType.convert( byteBuffer.getShort() );
131 RecordClass recordClass = RecordClass.convert( byteBuffer.getShort() );
132
133 int timeToLive = byteBuffer.getInt();
134 short dataLength = byteBuffer.getShort();
135
136 Map<String, Object> attributes = decode( byteBuffer, recordType, dataLength );
137 records.add( new ResourceRecordImpl( domainName, recordType, recordClass, timeToLive, attributes ) );
138 }
139
140 return records;
141 }
142
143
144 private Map<String, Object> decode( IoBuffer byteBuffer, RecordType type, short length ) throws IOException
145 {
146 RecordDecoder recordDecoder = DEFAULT_DECODERS.get( type );
147
148 if ( recordDecoder == null )
149 {
150 throw new IllegalArgumentException( I18n.err( I18n.ERR_600, type ) );
151 }
152
153 return recordDecoder.decode( byteBuffer, length );
154 }
155
156
157 private List<QuestionRecord> getQuestions( IoBuffer byteBuffer, short questionCount )
158 {
159 List<QuestionRecord> questions = new ArrayList<>( questionCount );
160
161 for ( int ii = 0; ii < questionCount; ii++ )
162 {
163 String domainName = getDomainName( byteBuffer );
164
165 RecordType recordType = RecordType.convert( byteBuffer.getShort() );
166 RecordClass recordClass = RecordClass.convert( byteBuffer.getShort() );
167
168 questions.add( new QuestionRecord( domainName, recordType, recordClass ) );
169 }
170
171 return questions;
172 }
173
174
175 static String getDomainName( IoBuffer byteBuffer )
176 {
177 StringBuilder domainName = new StringBuilder();
178 recurseDomainName( byteBuffer, domainName );
179
180 return domainName.toString();
181 }
182
183
184 static void recurseDomainName( IoBuffer byteBuffer, StringBuilder domainName )
185 {
186 int length = byteBuffer.getUnsigned();
187
188 if ( isOffset( length ) )
189 {
190 int position = byteBuffer.getUnsigned();
191 int offset = length & ~( 0xc0 ) << 8;
192 int originalPosition = byteBuffer.position();
193 byteBuffer.position( position + offset );
194
195 recurseDomainName( byteBuffer, domainName );
196
197 byteBuffer.position( originalPosition );
198 }
199 else if ( isLabel( length ) )
200 {
201 int labelLength = length;
202 getLabel( byteBuffer, domainName, labelLength );
203 recurseDomainName( byteBuffer, domainName );
204 }
205 }
206
207
208 static boolean isOffset( int length )
209 {
210 return ( ( length & 0xc0 ) == 0xc0 );
211 }
212
213
214 static boolean isLabel( int length )
215 {
216 return ( length != 0 && ( length & 0xc0 ) == 0 );
217 }
218
219
220 static void getLabel( IoBuffer byteBuffer, StringBuilder domainName, int labelLength )
221 {
222 for ( int jj = 0; jj < labelLength; jj++ )
223 {
224 char character = ( char ) byteBuffer.get();
225 domainName.append( character );
226 }
227
228 if ( byteBuffer.get( byteBuffer.position() ) != 0 )
229 {
230 domainName.append( "." );
231 }
232 }
233
234
235 private MessageType decodeMessageType( byte header )
236 {
237 return MessageType.convert( ( byte ) ( ( header & 0x80 ) >>> 7 ) );
238 }
239
240
241 private OpCode decodeOpCode( byte header )
242 {
243 return OpCode.convert( ( byte ) ( ( header & 0x78 ) >>> 3 ) );
244 }
245
246
247 private boolean decodeAuthoritativeAnswer( byte header )
248 {
249 return ( ( header & 0x04 ) >>> 2 ) == 1;
250 }
251
252
253 private boolean decodeTruncated( byte header )
254 {
255 return ( ( header & 0x02 ) >>> 1 ) == 1;
256 }
257
258
259 private boolean decodeRecursionDesired( byte header )
260 {
261 return ( header & 0x01 ) == 1;
262 }
263
264
265 private boolean decodeRecursionAvailable( byte header )
266 {
267 return ( ( header & 0x80 ) >>> 7 ) == 1;
268 }
269
270
271 private ResponseCode decodeResponseCode( byte header )
272 {
273 return ResponseCode.convert( ( byte ) ( header & 0x0F ) );
274 }
275 }