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.store.jndi.operations;
22  
23  
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Map;
28  import java.util.Properties;
29  import java.util.Set;
30  
31  import javax.naming.CompoundName;
32  import javax.naming.Name;
33  import javax.naming.NamingEnumeration;
34  import javax.naming.NamingException;
35  import javax.naming.directory.Attribute;
36  import javax.naming.directory.Attributes;
37  import javax.naming.directory.DirContext;
38  import javax.naming.directory.SearchControls;
39  import javax.naming.directory.SearchResult;
40  
41  import org.apache.directory.server.dns.messages.QuestionRecord;
42  import org.apache.directory.server.dns.messages.RecordClass;
43  import org.apache.directory.server.dns.messages.RecordType;
44  import org.apache.directory.server.dns.messages.ResourceRecord;
45  import org.apache.directory.server.dns.messages.ResourceRecordModifier;
46  import org.apache.directory.server.dns.store.DnsAttribute;
47  import org.apache.directory.server.dns.store.jndi.DnsOperation;
48  import org.apache.directory.shared.ldap.constants.SchemaConstants;
49  
50  
51  /**
52   * A JNDI context operation for looking up Resource Records from an embedded JNDI provider.
53   *
54   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
55   * @version $Rev$, $Date$
56   */
57  public class GetRecords implements DnsOperation
58  {
59      private static final long serialVersionUID = 1077580995617778894L;
60  
61      /** The name of the question to get. */
62      private final QuestionRecord question;
63  
64  
65      /**
66       * Creates the action to be used against the embedded JNDI provider.
67       * 
68       * @param question 
69       */
70      public GetRecords( QuestionRecord question )
71      {
72          this.question = question;
73      }
74  
75      /**
76       * Mappings of type to objectClass.
77       */
78      private static final Map<RecordType, String> TYPE_TO_OBJECTCLASS;
79  
80      static
81      {
82          Map<RecordType, String> typeToObjectClass = new HashMap<RecordType, String>();
83          typeToObjectClass.put( RecordType.SOA, "apacheDnsStartOfAuthorityRecord" );
84          typeToObjectClass.put( RecordType.A, "apacheDnsAddressRecord" );
85          typeToObjectClass.put( RecordType.NS, "apacheDnsNameServerRecord" );
86          typeToObjectClass.put( RecordType.CNAME, "apacheDnsCanonicalNameRecord" );
87          typeToObjectClass.put( RecordType.PTR, "apacheDnsPointerRecord" );
88          typeToObjectClass.put( RecordType.MX, "apacheDnsMailExchangeRecord" );
89          typeToObjectClass.put( RecordType.SRV, "apacheDnsServiceRecord" );
90          typeToObjectClass.put( RecordType.TXT, "apacheDnsTextRecord" );
91  
92          TYPE_TO_OBJECTCLASS = Collections.unmodifiableMap( typeToObjectClass );
93      }
94  
95      /**
96       * Mappings of type to objectClass.
97       */
98      private static final Map<String, RecordType> OBJECTCLASS_TO_TYPE;
99  
100     static
101     {
102         Map<String, RecordType> objectClassToType = new HashMap<String, RecordType>();
103         objectClassToType.put( "apacheDnsStartOfAuthorityRecord", RecordType.SOA );
104         objectClassToType.put( "apacheDnsAddressRecord", RecordType.A );
105         objectClassToType.put( "apacheDnsNameServerRecord", RecordType.NS );
106         objectClassToType.put( "apacheDnsCanonicalNameRecord", RecordType.CNAME );
107         objectClassToType.put( "apacheDnsPointerRecord", RecordType.PTR );
108         objectClassToType.put( "apacheDnsMailExchangeRecord", RecordType.MX );
109         objectClassToType.put( "apacheDnsServiceRecord", RecordType.SRV );
110         objectClassToType.put( "apacheDnsTextRecord", RecordType.TXT );
111         objectClassToType.put( "apacheDnsReferralNameServer", RecordType.NS );
112         objectClassToType.put( "apacheDnsReferralAddress", RecordType.A );
113 
114         OBJECTCLASS_TO_TYPE = Collections.unmodifiableMap( objectClassToType );
115     }
116 
117 
118     /**
119      * Note that the base is a relative path from the exiting context.
120      * It is not a DN.
121      */
122     public Set<ResourceRecord> execute( DirContext ctx, Name base ) throws Exception
123     {
124         if ( question == null )
125         {
126             return null;
127         }
128 
129         String name = question.getDomainName();
130         RecordType type = question.getRecordType();
131 
132         SearchControls controls = new SearchControls();
133         controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
134 
135         String filter = "(objectClass=" + TYPE_TO_OBJECTCLASS.get( type ) + ")";
136 
137         NamingEnumeration<SearchResult> list = ctx.search( transformDomainName( name ), filter, controls );
138 
139         Set<ResourceRecord> set = new HashSet<ResourceRecord>();
140 
141         while ( list.hasMore() )
142         {
143             SearchResult result = list.next();
144             Name relative = getRelativeName( ctx.getNameInNamespace(), result.getName() );
145 
146             set.add( getRecord( result.getAttributes(), relative ) );
147         }
148 
149         return set;
150     }
151 
152 
153     /**
154      * Marshals a RecordStoreEntry from an Attributes object.
155      *
156      * @param attrs the attributes of the DNS question
157      * @return the entry for the question
158      * @throws NamingException if there are any access problems
159      */
160     private ResourceRecord getRecord( Attributes attrs, Name relative ) throws NamingException
161     {
162         String SOA_MINIMUM = "86400";
163         String SOA_CLASS = "IN";
164 
165         ResourceRecordModifier modifier = new ResourceRecordModifier();
166 
167         Attribute attr;
168 
169         // if no name, transform rdn
170         attr = attrs.get( DnsAttribute.NAME );
171 
172         if ( attr != null )
173         {
174             modifier.setDnsName( ( String ) attr.get() );
175         }
176         else
177         {
178             relative = getDomainComponents( relative );
179 
180             String dnsName;
181             dnsName = transformDistinguishedName( relative.toString() );
182             modifier.setDnsName( dnsName );
183         }
184 
185         // type is implicit in objectclass
186         attr = attrs.get( DnsAttribute.TYPE );
187 
188         if ( attr != null )
189         {
190             modifier.setDnsType( RecordType.valueOf( ( String ) attr.get() ) );
191         }
192         else
193         {
194             modifier.setDnsType( getType( attrs.get( SchemaConstants.OBJECT_CLASS_AT ) ) );
195         }
196 
197         // class defaults to SOA CLASS
198         String dnsClass = ( attr = attrs.get( DnsAttribute.CLASS ) ) != null ? ( String ) attr.get() : SOA_CLASS;
199         modifier.setDnsClass( RecordClass.valueOf( dnsClass ) );
200 
201         // ttl defaults to SOA MINIMUM
202         String dnsTtl = ( attr = attrs.get( DnsAttribute.TTL ) ) != null ? ( String ) attr.get() : SOA_MINIMUM;
203         modifier.setDnsTtl( Integer.parseInt( dnsTtl ) );
204 
205         NamingEnumeration<String> ids = attrs.getIDs();
206 
207         while ( ids.hasMore() )
208         {
209             String id = ids.next();
210             modifier.put( id, ( String ) attrs.get( id ).get() );
211         }
212 
213         return modifier.getEntry();
214     }
215 
216 
217     /**
218      * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a>
219      * to transform any Internet domain name into a distinguished name.
220      *
221      * @param domainName the domain name
222      * @return the distinguished name
223      */
224     String transformDomainName( String domainName )
225     {
226         if ( domainName == null || domainName.length() == 0 )
227         {
228             return "";
229         }
230 
231         StringBuffer buf = new StringBuffer( domainName.length() + 16 );
232 
233         buf.append( "dc=" );
234         buf.append( domainName.replaceAll( "\\.", ",dc=" ) );
235 
236         return buf.toString();
237     }
238 
239 
240     /**
241      * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a>
242      * to transform a distinguished name into an Internet domain name.
243      *
244      * @param distinguishedName the distinguished name
245      * @return the domain name
246      */
247     String transformDistinguishedName( String distinguishedName )
248     {
249         if ( distinguishedName == null || distinguishedName.length() == 0 )
250         {
251             return "";
252         }
253 
254         String domainName = distinguishedName.replaceFirst( "dc=", "" );
255         domainName = domainName.replaceAll( ",dc=", "." );
256 
257         return domainName;
258     }
259 
260 
261     private RecordType getType( Attribute objectClass ) throws NamingException
262     {
263         NamingEnumeration<?> list = objectClass.getAll();
264 
265         while ( list.hasMore() )
266         {
267             String value = ( String ) list.next();
268 
269             if ( !value.equals( "apacheDnsAbstractRecord" ) )
270             {
271                 RecordType type = OBJECTCLASS_TO_TYPE.get( value );
272 
273                 if ( type == null )
274                 {
275                     throw new RuntimeException( "Record type to objectClass mapping has not been set." );
276                 }
277 
278                 return type;
279             }
280         }
281 
282         throw new NamingException( "ResourceRecord requires STRUCTURAL objectClass" );
283     }
284 
285 
286     private Name getRelativeName( String nameInNamespace, String baseDn ) throws NamingException
287     {
288         Properties props = new Properties();
289         props.setProperty( "jndi.syntax.direction", "right_to_left" );
290         props.setProperty( "jndi.syntax.separator", "," );
291         props.setProperty( "jndi.syntax.ignorecase", "true" );
292         props.setProperty( "jndi.syntax.trimblanks", "true" );
293 
294         Name searchBaseDn = null;
295 
296         Name ctxRoot = new CompoundName( nameInNamespace, props );
297         searchBaseDn = new CompoundName( baseDn, props );
298 
299         if ( !searchBaseDn.startsWith( ctxRoot ) )
300         {
301             throw new NamingException( "Invalid search base " + baseDn );
302         }
303 
304         for ( int ii = 0; ii < ctxRoot.size(); ii++ )
305         {
306             searchBaseDn.remove( 0 );
307         }
308 
309         return searchBaseDn;
310     }
311 
312 
313     private Name getDomainComponents( Name name ) throws NamingException
314     {
315         for ( int ii = 0; ii < name.size(); ii++ )
316         {
317             if ( !name.get( ii ).startsWith( "dc=" ) )
318             {
319                 name.remove( ii );
320             }
321         }
322 
323         return name;
324     }
325 }