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 */
020package org.apache.directory.server.dns.service;
021
022
023import java.util.ArrayList;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Set;
027
028import org.apache.directory.server.dns.DnsException;
029import org.apache.directory.server.dns.messages.DnsMessage;
030import org.apache.directory.server.dns.messages.DnsMessageModifier;
031import org.apache.directory.server.dns.messages.MessageType;
032import org.apache.directory.server.dns.messages.OpCode;
033import org.apache.directory.server.dns.messages.QuestionRecord;
034import org.apache.directory.server.dns.messages.ResourceRecord;
035import org.apache.directory.server.dns.messages.ResponseCode;
036import org.apache.directory.server.dns.store.RecordStore;
037import org.apache.directory.server.i18n.I18n;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041
042/**
043 * Domain Name Service (DNS) Protocol (RFC 1034, 1035)
044 * 
045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
046 */
047public final class DomainNameService
048{
049    /** the log for this class */
050    private static final Logger LOG = LoggerFactory.getLogger( DomainNameService.class );
051
052
053    private DomainNameService()
054    {
055    }
056
057
058    /**
059     * Creates a new instance of DomainNameService.
060     */
061    public static void execute( DnsContext dnsContext, DnsMessage request ) throws Exception
062    {
063        if ( LOG.isDebugEnabled() )
064        {
065            monitorRequest( request );
066        }
067
068        getResourceRecords( dnsContext, request );
069
070        if ( LOG.isDebugEnabled() )
071        {
072            monitorContext( dnsContext );
073        }
074
075        buildReply( dnsContext, request );
076
077        if ( LOG.isDebugEnabled() )
078        {
079            monitorReply( dnsContext );
080        }
081    }
082
083
084    private static void monitorRequest( DnsMessage request )
085    {
086        try
087        {
088            if ( LOG.isDebugEnabled() )
089            {
090                LOG.debug( monitorMessage( request, "request" ) );
091            }
092        }
093        catch ( Exception e )
094        {
095            // This is a monitor.  No exceptions should bubble up.
096            LOG.error( I18n.err( I18n.ERR_153 ), e );
097        }
098    }
099
100
101    private static void getResourceRecords( DnsContext dnsContext, DnsMessage request ) throws DnsException
102    {
103        RecordStore store = dnsContext.getStore();
104
105        List<QuestionRecord> questions = request.getQuestionRecords();
106
107        Iterator<QuestionRecord> it = questions.iterator();
108
109        while ( it.hasNext() )
110        {
111            dnsContext.addResourceRecords( getEntry( store, it.next() ) );
112        }
113    }
114
115
116    /**
117     * Returns a set of {@link ResourceRecord}s from a {@link RecordStore}, given a DNS {@link QuestionRecord}.
118     *
119     * @param store
120     * @param question
121     * @return The set of {@link ResourceRecord}s.
122     * @throws DNSException
123     */
124    private static Set<ResourceRecord> getEntry( RecordStore store, QuestionRecord question ) throws DnsException
125    {
126        Set<ResourceRecord> records = null;
127
128        records = store.getRecords( question );
129
130        if ( records == null || records.isEmpty() )
131        {
132            LOG.debug( "The domain name referenced in the query does not exist." );
133
134            throw new DnsException( ResponseCode.NAME_ERROR );
135        }
136
137        return records;
138    }
139
140
141    private static void monitorContext( DnsContext dnsContext )
142    {
143        try
144        {
145            RecordStore store = dnsContext.getStore();
146            List<ResourceRecord> records = dnsContext.getResourceRecords();
147
148            StringBuilder sb = new StringBuilder();
149            sb.append( "Monitoring context:" );
150            sb.append( "\n\t" + "store:                     " + store );
151            sb.append( "\n\t" + "records:                   " + records );
152
153            LOG.debug( sb.toString() );
154        }
155        catch ( Exception e )
156        {
157            // This is a monitor.  No exceptions should bubble up.
158            LOG.error( I18n.err( I18n.ERR_154 ), e );
159        }
160    }
161
162
163    private static void buildReply( DnsContext dnsContext, DnsMessage request ) 
164    {
165        List<ResourceRecord> records = dnsContext.getResourceRecords();
166
167        DnsMessageModifier modifier = new DnsMessageModifier();
168
169        modifier.setTransactionId( request.getTransactionId() );
170        modifier.setMessageType( MessageType.RESPONSE );
171        modifier.setOpCode( OpCode.QUERY );
172        modifier.setAuthoritativeAnswer( false );
173        modifier.setTruncated( false );
174        modifier.setRecursionDesired( request.isRecursionDesired() );
175        modifier.setRecursionAvailable( false );
176        modifier.setReserved( false );
177        modifier.setAcceptNonAuthenticatedData( false );
178        modifier.setResponseCode( ResponseCode.NO_ERROR );
179        modifier.setQuestionRecords( request.getQuestionRecords() );
180
181        modifier.setAnswerRecords( records );
182        modifier.setAuthorityRecords( new ArrayList<ResourceRecord>() );
183        modifier.setAdditionalRecords( new ArrayList<ResourceRecord>() );
184
185        dnsContext.setReply( modifier.getDnsMessage() );
186    }
187
188
189    private static void monitorReply( DnsContext dnsContext )
190    {
191        try
192        {
193            DnsMessage reply = dnsContext.getReply();
194
195            if ( LOG.isDebugEnabled() )
196            {
197                LOG.debug( monitorMessage( reply, "reply" ) );
198            }
199        }
200        catch ( Exception e )
201        {
202            // This is a monitor.  No exceptions should bubble up.
203            LOG.error( I18n.err( I18n.ERR_155 ), e );
204        }
205    }
206
207
208    private static String monitorMessage( DnsMessage message, String direction )
209    {
210        MessageType messageType = message.getMessageType();
211        OpCode opCode = message.getOpCode();
212        ResponseCode responseCode = message.getResponseCode();
213        int transactionId = message.getTransactionId();
214
215        StringBuilder sb = new StringBuilder();
216        sb.append( "Monitoring " + direction + ":" );
217        sb.append( "\n\t" + "messageType                " + messageType );
218        sb.append( "\n\t" + "opCode                     " + opCode );
219        sb.append( "\n\t" + "responseCode               " + responseCode );
220        sb.append( "\n\t" + "transactionId              " + transactionId );
221
222        sb.append( "\n\t" + "authoritativeAnswer        " + message.isAuthoritativeAnswer() );
223        sb.append( "\n\t" + "truncated                  " + message.isTruncated() );
224        sb.append( "\n\t" + "recursionDesired           " + message.isRecursionDesired() );
225        sb.append( "\n\t" + "recursionAvailable         " + message.isRecursionAvailable() );
226        sb.append( "\n\t" + "reserved                   " + message.isReserved() );
227        sb.append( "\n\t" + "acceptNonAuthenticatedData " + message.isAcceptNonAuthenticatedData() );
228
229        List<QuestionRecord> questions = message.getQuestionRecords();
230
231        sb.append( "\n\t" + "questions:                 " + questions );
232
233        return sb.toString();
234    }
235}