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 }