001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021 package org.apache.directory.server.dns.io.decoder; 022 023 024 import java.io.IOException; 025 import java.util.ArrayList; 026 import java.util.Collections; 027 import java.util.HashMap; 028 import java.util.List; 029 import java.util.Map; 030 031 import org.apache.directory.server.dns.messages.DnsMessage; 032 import org.apache.directory.server.dns.messages.DnsMessageModifier; 033 import org.apache.directory.server.dns.messages.MessageType; 034 import org.apache.directory.server.dns.messages.OpCode; 035 import org.apache.directory.server.dns.messages.QuestionRecord; 036 import org.apache.directory.server.dns.messages.RecordClass; 037 import org.apache.directory.server.dns.messages.RecordType; 038 import org.apache.directory.server.dns.messages.ResourceRecord; 039 import org.apache.directory.server.dns.messages.ResourceRecordImpl; 040 import org.apache.directory.server.dns.messages.ResponseCode; 041 import org.apache.directory.server.i18n.I18n; 042 import org.apache.mina.core.buffer.IoBuffer; 043 import org.slf4j.Logger; 044 import org.slf4j.LoggerFactory; 045 046 047 /** 048 * A decoder for DNS messages. The primary usage of the DnsMessageDecoder is by 049 * calling the <code>decode(ByteBuffer)</code> method which will read the 050 * message from the incoming ByteBuffer and build a <code>DnsMessage</code> 051 * from it according to the DnsMessage encoding in RFC-1035. 052 * 053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 054 * @version $Rev$, $Date$ 055 */ 056 public class DnsMessageDecoder 057 { 058 059 private final Logger logger = LoggerFactory.getLogger( DnsMessageDecoder.class ); 060 061 /** 062 * A Hashed Adapter mapping record types to their encoders. 063 */ 064 private static final Map<RecordType, RecordDecoder> DEFAULT_DECODERS; 065 066 static 067 { 068 Map<RecordType, RecordDecoder> map = new HashMap<RecordType, RecordDecoder>(); 069 070 map.put( RecordType.A, new AddressRecordDecoder() ); 071 map.put( RecordType.NS, new NameServerRecordDecoder() ); 072 map.put( RecordType.MX, new MailExchangeRecordDecoder() ); 073 map.put( RecordType.AAAA, new IPv6RecordDecoder() ); 074 075 DEFAULT_DECODERS = Collections.unmodifiableMap( map ); 076 } 077 078 079 /** 080 * Decode the {@link ByteBuffer} into a {@link DnsMessage}. 081 * 082 * @param in 083 * @return The {@link DnsMessage}. 084 * @throws IOException 085 */ 086 public DnsMessage decode( IoBuffer in ) throws IOException 087 { 088 DnsMessageModifier modifier = new DnsMessageModifier(); 089 090 modifier.setTransactionId( in.getUnsignedShort() ); 091 092 byte header = in.get(); 093 modifier.setMessageType( decodeMessageType( header ) ); 094 modifier.setOpCode( decodeOpCode( header ) ); 095 modifier.setAuthoritativeAnswer( decodeAuthoritativeAnswer( header ) ); 096 modifier.setTruncated( decodeTruncated( header ) ); 097 modifier.setRecursionDesired( decodeRecursionDesired( header ) ); 098 099 header = in.get(); 100 modifier.setRecursionAvailable( decodeRecursionAvailable( header ) ); 101 modifier.setResponseCode( decodeResponseCode( header ) ); 102 103 short questionCount = in.getShort(); 104 short answerCount = in.getShort(); 105 short authorityCount = in.getShort(); 106 short additionalCount = in.getShort(); 107 108 logger.debug( "decoding {} question records", questionCount ); 109 modifier.setQuestionRecords( getQuestions( in, questionCount ) ); 110 111 logger.debug( "decoding {} answer records", answerCount ); 112 modifier.setAnswerRecords( getRecords( in, answerCount ) ); 113 114 logger.debug( "decoding {} authority records", authorityCount ); 115 modifier.setAuthorityRecords( getRecords( in, authorityCount ) ); 116 117 logger.debug( "decoding {} additional records", additionalCount ); 118 modifier.setAdditionalRecords( getRecords( in, additionalCount ) ); 119 120 return modifier.getDnsMessage(); 121 } 122 123 124 private List<ResourceRecord> getRecords( IoBuffer byteBuffer, short recordCount ) throws IOException 125 { 126 List<ResourceRecord> records = new ArrayList<ResourceRecord>( recordCount ); 127 128 for ( int ii = 0; ii < recordCount; ii++ ) 129 { 130 String domainName = getDomainName( byteBuffer ); 131 RecordType recordType = RecordType.convert( byteBuffer.getShort() ); 132 RecordClass recordClass = RecordClass.convert( byteBuffer.getShort() ); 133 134 int timeToLive = byteBuffer.getInt(); 135 short dataLength = byteBuffer.getShort(); 136 137 Map<String, Object> attributes = decode( byteBuffer, recordType, dataLength ); 138 records.add( new ResourceRecordImpl( domainName, recordType, recordClass, timeToLive, attributes ) ); 139 } 140 141 return records; 142 } 143 144 145 private Map<String, Object> decode( IoBuffer byteBuffer, RecordType type, short length ) throws IOException 146 { 147 RecordDecoder recordDecoder = DEFAULT_DECODERS.get( type ); 148 149 if ( recordDecoder == null ) 150 { 151 throw new IllegalArgumentException( I18n.err(I18n.ERR_600, type ) ); 152 } 153 154 return recordDecoder.decode( byteBuffer, length ); 155 } 156 157 158 private List<QuestionRecord> getQuestions( IoBuffer byteBuffer, short questionCount ) 159 { 160 List<QuestionRecord> questions = new ArrayList<QuestionRecord>( questionCount ); 161 162 for ( int ii = 0; ii < questionCount; ii++ ) 163 { 164 String domainName = getDomainName( byteBuffer ); 165 166 RecordType recordType = RecordType.convert( byteBuffer.getShort() ); 167 RecordClass recordClass = RecordClass.convert( byteBuffer.getShort() ); 168 169 questions.add( new QuestionRecord( domainName, recordType, recordClass ) ); 170 } 171 172 return questions; 173 } 174 175 176 static String getDomainName( IoBuffer byteBuffer ) 177 { 178 StringBuffer domainName = new StringBuffer(); 179 recurseDomainName( byteBuffer, domainName ); 180 181 return domainName.toString(); 182 } 183 184 185 static void recurseDomainName( IoBuffer byteBuffer, StringBuffer domainName ) 186 { 187 int length = byteBuffer.getUnsigned(); 188 189 if ( isOffset( length ) ) 190 { 191 int position = byteBuffer.getUnsigned(); 192 int offset = length & ~( 0xc0 ) << 8; 193 int originalPosition = byteBuffer.position(); 194 byteBuffer.position( position + offset ); 195 196 recurseDomainName( byteBuffer, domainName ); 197 198 byteBuffer.position( originalPosition ); 199 } 200 else if ( isLabel( length ) ) 201 { 202 int labelLength = length; 203 getLabel( byteBuffer, domainName, labelLength ); 204 recurseDomainName( byteBuffer, domainName ); 205 } 206 } 207 208 209 static boolean isOffset( int length ) 210 { 211 return ( ( length & 0xc0 ) == 0xc0 ); 212 } 213 214 215 static boolean isLabel( int length ) 216 { 217 return ( length != 0 && ( length & 0xc0 ) == 0 ); 218 } 219 220 221 static void getLabel( IoBuffer byteBuffer, StringBuffer domainName, int labelLength ) 222 { 223 for ( int jj = 0; jj < labelLength; jj++ ) 224 { 225 char character = ( char ) byteBuffer.get(); 226 domainName.append( character ); 227 } 228 229 if ( byteBuffer.get( byteBuffer.position() ) != 0 ) 230 { 231 domainName.append( "." ); 232 } 233 } 234 235 236 private MessageType decodeMessageType( byte header ) 237 { 238 return MessageType.convert( ( byte ) ( ( header & 0x80 ) >>> 7 ) ); 239 } 240 241 242 private OpCode decodeOpCode( byte header ) 243 { 244 return OpCode.convert( ( byte ) ( ( header & 0x78 ) >>> 3 ) ); 245 } 246 247 248 private boolean decodeAuthoritativeAnswer( byte header ) 249 { 250 return ( ( header & 0x04 ) >>> 2 ) == 1; 251 } 252 253 254 private boolean decodeTruncated( byte header ) 255 { 256 return ( ( header & 0x02 ) >>> 1 ) == 1; 257 } 258 259 260 private boolean decodeRecursionDesired( byte header ) 261 { 262 return ( ( header & 0x01 ) ) == 1; 263 } 264 265 266 private boolean decodeRecursionAvailable( byte header ) 267 { 268 return ( ( header & 0x80 ) >>> 7 ) == 1; 269 } 270 271 272 private ResponseCode decodeResponseCode( byte header ) 273 { 274 return ResponseCode.convert( ( byte ) ( header & 0x0F ) ); 275 } 276 }