View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  
21  package org.apache.directory.server.dns.io.encoder;
22  
23  
24  import java.io.IOException;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Iterator;
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.MessageType;
33  import org.apache.directory.server.dns.messages.OpCode;
34  import org.apache.directory.server.dns.messages.QuestionRecord;
35  import org.apache.directory.server.dns.messages.RecordType;
36  import org.apache.directory.server.dns.messages.ResourceRecord;
37  import org.apache.directory.server.dns.messages.ResponseCode;
38  import org.apache.mina.common.ByteBuffer;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  
43  /**
44   * An encoder for DNS messages.  The primary usage of the DnsMessageEncoder is 
45   * to call the <code>encode(ByteBuffer, DnsMessage)</code> method which will 
46   * write the message to the outgoing ByteBuffer according to the DnsMessage 
47   * encoding in RFC-1035.
48   * 
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   * @version $Rev$, $Date$
51   */
52  public class DnsMessageEncoder
53  {
54      /** the log for this class */
55      private static final Logger log = LoggerFactory.getLogger( DnsMessageEncoder.class );
56  
57      /**
58       * A Hashed Adapter mapping record types to their encoders.
59       */
60      private static final Map<RecordType, RecordEncoder> DEFAULT_ENCODERS;
61  
62      static
63      {
64          Map<RecordType, RecordEncoder> map = new HashMap<RecordType, RecordEncoder>();
65  
66          map.put( RecordType.SOA, new StartOfAuthorityRecordEncoder() );
67          map.put( RecordType.A, new AddressRecordEncoder() );
68          map.put( RecordType.NS, new NameServerRecordEncoder() );
69          map.put( RecordType.CNAME, new CanonicalNameRecordEncoder() );
70          map.put( RecordType.PTR, new PointerRecordEncoder() );
71          map.put( RecordType.MX, new MailExchangeRecordEncoder() );
72          map.put( RecordType.SRV, new ServerSelectionRecordEncoder() );
73          map.put( RecordType.TXT, new TextRecordEncoder() );
74  
75          DEFAULT_ENCODERS = Collections.unmodifiableMap( map );
76      }
77  
78  
79      /**
80       * Encodes the {@link DnsMessage} into the {@link ByteBuffer}.
81       *
82       * @param byteBuffer
83       * @param message
84       */
85      public void encode( ByteBuffer byteBuffer, DnsMessage message )
86      {
87          byteBuffer.putShort( ( short ) message.getTransactionId() );
88  
89          byte header = ( byte ) 0x00;
90          header |= encodeMessageType( message.getMessageType() );
91          header |= encodeOpCode( message.getOpCode() );
92          header |= encodeAuthoritativeAnswer( message.isAuthoritativeAnswer() );
93          header |= encodeTruncated( message.isTruncated() );
94          header |= encodeRecursionDesired( message.isRecursionDesired() );
95          byteBuffer.put( header );
96  
97          header = ( byte ) 0x00;
98          header |= encodeRecursionAvailable( message.isRecursionAvailable() );
99          header |= encodeResponseCode( message.getResponseCode() );
100         byteBuffer.put( header );
101 
102         byteBuffer
103             .putShort( ( short ) ( message.getQuestionRecords() != null ? message.getQuestionRecords().size() : 0 ) );
104         byteBuffer.putShort( ( short ) ( message.getAnswerRecords() != null ? message.getAnswerRecords().size() : 0 ) );
105         byteBuffer.putShort( ( short ) ( message.getAuthorityRecords() != null ? message.getAuthorityRecords().size()
106             : 0 ) );
107         byteBuffer.putShort( ( short ) ( message.getAdditionalRecords() != null ? message.getAdditionalRecords().size()
108             : 0 ) );
109 
110         putQuestionRecords( byteBuffer, message.getQuestionRecords() );
111         putResourceRecords( byteBuffer, message.getAnswerRecords() );
112         putResourceRecords( byteBuffer, message.getAuthorityRecords() );
113         putResourceRecords( byteBuffer, message.getAdditionalRecords() );
114     }
115 
116 
117     private void putQuestionRecords( ByteBuffer byteBuffer, List<QuestionRecord> questions )
118     {
119         if ( questions == null )
120         {
121             return;
122         }
123 
124         QuestionRecordEncoder encoder = new QuestionRecordEncoder();
125 
126         Iterator<QuestionRecord> it = questions.iterator();
127 
128         while ( it.hasNext() )
129         {
130             QuestionRecord question = it.next();
131             encoder.put( byteBuffer, question );
132         }
133     }
134 
135 
136     private void putResourceRecords( ByteBuffer byteBuffer, List<ResourceRecord> records )
137     {
138         if ( records == null )
139         {
140             return;
141         }
142 
143         Iterator<ResourceRecord> it = records.iterator();
144 
145         while ( it.hasNext() )
146         {
147             ResourceRecord record = it.next();
148 
149             try
150             {
151                 put( byteBuffer, record );
152             }
153             catch ( IOException ioe )
154             {
155                 log.error( ioe.getMessage(), ioe );
156             }
157         }
158     }
159 
160 
161     private void put( ByteBuffer byteBuffer, ResourceRecord record ) throws IOException
162     {
163         RecordType type = record.getRecordType();
164 
165         RecordEncoder encoder = DEFAULT_ENCODERS.get( type );
166 
167         if ( encoder == null )
168         {
169             throw new IOException( "Encoder unavailable for " + type );
170         }
171 
172         encoder.put( byteBuffer, record );
173     }
174 
175 
176     private byte encodeMessageType( MessageType messageType )
177     {
178         byte oneBit = ( byte ) ( messageType.convert() & 0x01 );
179         return ( byte ) ( oneBit << 7 );
180     }
181 
182 
183     private byte encodeOpCode( OpCode opCode )
184     {
185         byte fourBits = ( byte ) ( opCode.convert() & 0x0F );
186         return ( byte ) ( fourBits << 3 );
187     }
188 
189 
190     private byte encodeAuthoritativeAnswer( boolean authoritative )
191     {
192         if ( authoritative )
193         {
194             return ( byte ) ( ( byte ) 0x01 << 2 );
195         }
196         return ( byte ) 0;
197     }
198 
199 
200     private byte encodeTruncated( boolean truncated )
201     {
202         if ( truncated )
203         {
204             return ( byte ) ( ( byte ) 0x01 << 1 );
205         }
206         return 0;
207     }
208 
209 
210     private byte encodeRecursionDesired( boolean recursionDesired )
211     {
212         if ( recursionDesired )
213         {
214             return ( byte ) 0x01;
215         }
216         return 0;
217     }
218 
219 
220     private byte encodeRecursionAvailable( boolean recursionAvailable )
221     {
222         if ( recursionAvailable )
223         {
224             return ( byte ) ( ( byte ) 0x01 << 7 );
225         }
226         return 0;
227     }
228 
229 
230     private byte encodeResponseCode( ResponseCode responseCode )
231     {
232         return ( byte ) ( responseCode.convert() & 0x0F );
233     }
234 }