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.encoder; 022 023 024 import java.io.IOException; 025 import java.util.Collections; 026 import java.util.HashMap; 027 import java.util.Iterator; 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.MessageType; 033 import org.apache.directory.server.dns.messages.OpCode; 034 import org.apache.directory.server.dns.messages.QuestionRecord; 035 import org.apache.directory.server.dns.messages.RecordType; 036 import org.apache.directory.server.dns.messages.ResourceRecord; 037 import org.apache.directory.server.dns.messages.ResponseCode; 038 import org.apache.directory.server.i18n.I18n; 039 import org.apache.mina.core.buffer.IoBuffer; 040 import org.slf4j.Logger; 041 import org.slf4j.LoggerFactory; 042 043 044 /** 045 * An encoder for DNS messages. The primary usage of the DnsMessageEncoder is 046 * to call the <code>encode(ByteBuffer, DnsMessage)</code> method which will 047 * write the message to the outgoing ByteBuffer according to the DnsMessage 048 * encoding in RFC-1035. 049 * 050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 051 * @version $Rev$, $Date$ 052 */ 053 public class DnsMessageEncoder 054 { 055 /** the log for this class */ 056 private static final Logger log = LoggerFactory.getLogger( DnsMessageEncoder.class ); 057 058 /** 059 * A Hashed Adapter mapping record types to their encoders. 060 */ 061 private static final Map<RecordType, RecordEncoder> DEFAULT_ENCODERS; 062 063 static 064 { 065 Map<RecordType, RecordEncoder> map = new HashMap<RecordType, RecordEncoder>(); 066 067 map.put( RecordType.SOA, new StartOfAuthorityRecordEncoder() ); 068 map.put( RecordType.A, new AddressRecordEncoder() ); 069 map.put( RecordType.NS, new NameServerRecordEncoder() ); 070 map.put( RecordType.CNAME, new CanonicalNameRecordEncoder() ); 071 map.put( RecordType.PTR, new PointerRecordEncoder() ); 072 map.put( RecordType.MX, new MailExchangeRecordEncoder() ); 073 map.put( RecordType.SRV, new ServerSelectionRecordEncoder() ); 074 map.put( RecordType.TXT, new TextRecordEncoder() ); 075 076 DEFAULT_ENCODERS = Collections.unmodifiableMap( map ); 077 } 078 079 080 /** 081 * Encodes the {@link DnsMessage} into the {@link ByteBuffer}. 082 * 083 * @param byteBuffer 084 * @param message 085 */ 086 public void encode( IoBuffer byteBuffer, DnsMessage message ) 087 { 088 byteBuffer.putShort( ( short ) message.getTransactionId() ); 089 090 byte header = ( byte ) 0x00; 091 header |= encodeMessageType( message.getMessageType() ); 092 header |= encodeOpCode( message.getOpCode() ); 093 header |= encodeAuthoritativeAnswer( message.isAuthoritativeAnswer() ); 094 header |= encodeTruncated( message.isTruncated() ); 095 header |= encodeRecursionDesired( message.isRecursionDesired() ); 096 byteBuffer.put( header ); 097 098 header = ( byte ) 0x00; 099 header |= encodeRecursionAvailable( message.isRecursionAvailable() ); 100 header |= encodeResponseCode( message.getResponseCode() ); 101 byteBuffer.put( header ); 102 103 byteBuffer 104 .putShort( ( short ) ( message.getQuestionRecords() != null ? message.getQuestionRecords().size() : 0 ) ); 105 byteBuffer.putShort( ( short ) ( message.getAnswerRecords() != null ? message.getAnswerRecords().size() : 0 ) ); 106 byteBuffer.putShort( ( short ) ( message.getAuthorityRecords() != null ? message.getAuthorityRecords().size() 107 : 0 ) ); 108 byteBuffer.putShort( ( short ) ( message.getAdditionalRecords() != null ? message.getAdditionalRecords().size() 109 : 0 ) ); 110 111 putQuestionRecords( byteBuffer, message.getQuestionRecords() ); 112 putResourceRecords( byteBuffer, message.getAnswerRecords() ); 113 putResourceRecords( byteBuffer, message.getAuthorityRecords() ); 114 putResourceRecords( byteBuffer, message.getAdditionalRecords() ); 115 } 116 117 118 private void putQuestionRecords( IoBuffer byteBuffer, List<QuestionRecord> questions ) 119 { 120 if ( questions == null ) 121 { 122 return; 123 } 124 125 QuestionRecordEncoder encoder = new QuestionRecordEncoder(); 126 127 Iterator<QuestionRecord> it = questions.iterator(); 128 129 while ( it.hasNext() ) 130 { 131 QuestionRecord question = it.next(); 132 encoder.put( byteBuffer, question ); 133 } 134 } 135 136 137 private void putResourceRecords( IoBuffer byteBuffer, List<ResourceRecord> records ) 138 { 139 if ( records == null ) 140 { 141 return; 142 } 143 144 Iterator<ResourceRecord> it = records.iterator(); 145 146 while ( it.hasNext() ) 147 { 148 ResourceRecord record = it.next(); 149 150 try 151 { 152 put( byteBuffer, record ); 153 } 154 catch ( IOException ioe ) 155 { 156 log.error( ioe.getLocalizedMessage(), ioe ); 157 } 158 } 159 } 160 161 162 private void put( IoBuffer byteBuffer, ResourceRecord record ) throws IOException 163 { 164 RecordType type = record.getRecordType(); 165 166 RecordEncoder encoder = DEFAULT_ENCODERS.get( type ); 167 168 if ( encoder == null ) 169 { 170 throw new IOException( I18n.err( I18n.ERR_597, type ) ); 171 } 172 173 encoder.put( byteBuffer, record ); 174 } 175 176 177 private byte encodeMessageType( MessageType messageType ) 178 { 179 byte oneBit = ( byte ) ( messageType.convert() & 0x01 ); 180 return ( byte ) ( oneBit << 7 ); 181 } 182 183 184 private byte encodeOpCode( OpCode opCode ) 185 { 186 byte fourBits = ( byte ) ( opCode.convert() & 0x0F ); 187 return ( byte ) ( fourBits << 3 ); 188 } 189 190 191 private byte encodeAuthoritativeAnswer( boolean authoritative ) 192 { 193 if ( authoritative ) 194 { 195 return ( byte ) ( ( byte ) 0x01 << 2 ); 196 } 197 return ( byte ) 0; 198 } 199 200 201 private byte encodeTruncated( boolean truncated ) 202 { 203 if ( truncated ) 204 { 205 return ( byte ) ( ( byte ) 0x01 << 1 ); 206 } 207 return 0; 208 } 209 210 211 private byte encodeRecursionDesired( boolean recursionDesired ) 212 { 213 if ( recursionDesired ) 214 { 215 return ( byte ) 0x01; 216 } 217 return 0; 218 } 219 220 221 private byte encodeRecursionAvailable( boolean recursionAvailable ) 222 { 223 if ( recursionAvailable ) 224 { 225 return ( byte ) ( ( byte ) 0x01 << 7 ); 226 } 227 return 0; 228 } 229 230 231 private byte encodeResponseCode( ResponseCode responseCode ) 232 { 233 return ( byte ) ( responseCode.convert() & 0x0F ); 234 } 235 }