| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| package org.apache.directory.server.dns.store.jndi.operations; |
| |
| |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import javax.naming.CompoundName; |
| import javax.naming.Name; |
| import javax.naming.NamingEnumeration; |
| import javax.naming.NamingException; |
| import javax.naming.directory.Attribute; |
| import javax.naming.directory.Attributes; |
| import javax.naming.directory.DirContext; |
| import javax.naming.directory.SearchControls; |
| import javax.naming.directory.SearchResult; |
| |
| import org.apache.directory.server.dns.messages.QuestionRecord; |
| import org.apache.directory.server.dns.messages.RecordClass; |
| import org.apache.directory.server.dns.messages.RecordType; |
| import org.apache.directory.server.dns.messages.ResourceRecord; |
| import org.apache.directory.server.dns.messages.ResourceRecordModifier; |
| import org.apache.directory.server.dns.store.DnsAttribute; |
| import org.apache.directory.server.dns.store.jndi.DnsOperation; |
| import org.apache.directory.shared.ldap.constants.SchemaConstants; |
| |
| |
| /** |
| * A JNDI context operation for looking up Resource Records from an embedded JNDI provider. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| * @version $Rev$, $Date$ |
| */ |
| public class GetRecords implements DnsOperation |
| { |
| private static final long serialVersionUID = 1077580995617778894L; |
| |
| /** The name of the question to get. */ |
| private final QuestionRecord question; |
| |
| |
| /** |
| * Creates the action to be used against the embedded JNDI provider. |
| * |
| * @param question |
| */ |
| public GetRecords( QuestionRecord question ) |
| { |
| this.question = question; |
| } |
| |
| /** |
| * Mappings of type to objectClass. |
| */ |
| private static final Map<RecordType, String> TYPE_TO_OBJECTCLASS; |
| |
| static |
| { |
| Map<RecordType, String> typeToObjectClass = new HashMap<RecordType, String>(); |
| typeToObjectClass.put( RecordType.SOA, "apacheDnsStartOfAuthorityRecord" ); |
| typeToObjectClass.put( RecordType.A, "apacheDnsAddressRecord" ); |
| typeToObjectClass.put( RecordType.NS, "apacheDnsNameServerRecord" ); |
| typeToObjectClass.put( RecordType.CNAME, "apacheDnsCanonicalNameRecord" ); |
| typeToObjectClass.put( RecordType.PTR, "apacheDnsPointerRecord" ); |
| typeToObjectClass.put( RecordType.MX, "apacheDnsMailExchangeRecord" ); |
| typeToObjectClass.put( RecordType.SRV, "apacheDnsServiceRecord" ); |
| typeToObjectClass.put( RecordType.TXT, "apacheDnsTextRecord" ); |
| |
| TYPE_TO_OBJECTCLASS = Collections.unmodifiableMap( typeToObjectClass ); |
| } |
| |
| /** |
| * Mappings of type to objectClass. |
| */ |
| private static final Map<String, RecordType> OBJECTCLASS_TO_TYPE; |
| |
| static |
| { |
| Map<String, RecordType> objectClassToType = new HashMap<String, RecordType>(); |
| objectClassToType.put( "apacheDnsStartOfAuthorityRecord", RecordType.SOA ); |
| objectClassToType.put( "apacheDnsAddressRecord", RecordType.A ); |
| objectClassToType.put( "apacheDnsNameServerRecord", RecordType.NS ); |
| objectClassToType.put( "apacheDnsCanonicalNameRecord", RecordType.CNAME ); |
| objectClassToType.put( "apacheDnsPointerRecord", RecordType.PTR ); |
| objectClassToType.put( "apacheDnsMailExchangeRecord", RecordType.MX ); |
| objectClassToType.put( "apacheDnsServiceRecord", RecordType.SRV ); |
| objectClassToType.put( "apacheDnsTextRecord", RecordType.TXT ); |
| objectClassToType.put( "apacheDnsReferralNameServer", RecordType.NS ); |
| objectClassToType.put( "apacheDnsReferralAddress", RecordType.A ); |
| |
| OBJECTCLASS_TO_TYPE = Collections.unmodifiableMap( objectClassToType ); |
| } |
| |
| |
| /** |
| * Note that the base is a relative path from the exiting context. |
| * It is not a DN. |
| */ |
| public Set<ResourceRecord> execute( DirContext ctx, Name base ) throws Exception |
| { |
| if ( question == null ) |
| { |
| return null; |
| } |
| |
| String name = question.getDomainName(); |
| RecordType type = question.getRecordType(); |
| |
| SearchControls controls = new SearchControls(); |
| controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); |
| |
| String filter = "(objectClass=" + TYPE_TO_OBJECTCLASS.get( type ) + ")"; |
| |
| NamingEnumeration<SearchResult> list = ctx.search( transformDomainName( name ), filter, controls ); |
| |
| Set<ResourceRecord> set = new HashSet<ResourceRecord>(); |
| |
| while ( list.hasMore() ) |
| { |
| SearchResult result = list.next(); |
| Name relative = getRelativeName( ctx.getNameInNamespace(), result.getName() ); |
| |
| set.add( getRecord( result.getAttributes(), relative ) ); |
| } |
| |
| return set; |
| } |
| |
| |
| /** |
| * Marshals a RecordStoreEntry from an Attributes object. |
| * |
| * @param attrs the attributes of the DNS question |
| * @return the entry for the question |
| * @throws NamingException if there are any access problems |
| */ |
| private ResourceRecord getRecord( Attributes attrs, Name relative ) throws NamingException |
| { |
| String SOA_MINIMUM = "86400"; |
| String SOA_CLASS = "IN"; |
| |
| ResourceRecordModifier modifier = new ResourceRecordModifier(); |
| |
| Attribute attr; |
| |
| // if no name, transform rdn |
| attr = attrs.get( DnsAttribute.NAME ); |
| |
| if ( attr != null ) |
| { |
| modifier.setDnsName( ( String ) attr.get() ); |
| } |
| else |
| { |
| relative = getDomainComponents( relative ); |
| |
| String dnsName; |
| dnsName = transformDistinguishedName( relative.toString() ); |
| modifier.setDnsName( dnsName ); |
| } |
| |
| // type is implicit in objectclass |
| attr = attrs.get( DnsAttribute.TYPE ); |
| |
| if ( attr != null ) |
| { |
| modifier.setDnsType( RecordType.valueOf( ( String ) attr.get() ) ); |
| } |
| else |
| { |
| modifier.setDnsType( getType( attrs.get( SchemaConstants.OBJECT_CLASS_AT ) ) ); |
| } |
| |
| // class defaults to SOA CLASS |
| String dnsClass = ( attr = attrs.get( DnsAttribute.CLASS ) ) != null ? ( String ) attr.get() : SOA_CLASS; |
| modifier.setDnsClass( RecordClass.valueOf( dnsClass ) ); |
| |
| // ttl defaults to SOA MINIMUM |
| String dnsTtl = ( attr = attrs.get( DnsAttribute.TTL ) ) != null ? ( String ) attr.get() : SOA_MINIMUM; |
| modifier.setDnsTtl( Integer.parseInt( dnsTtl ) ); |
| |
| NamingEnumeration<String> ids = attrs.getIDs(); |
| |
| while ( ids.hasMore() ) |
| { |
| String id = ids.next(); |
| modifier.put( id, ( String ) attrs.get( id ).get() ); |
| } |
| |
| return modifier.getEntry(); |
| } |
| |
| |
| /** |
| * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a> |
| * to transform any Internet domain name into a distinguished name. |
| * |
| * @param domainName the domain name |
| * @return the distinguished name |
| */ |
| String transformDomainName( String domainName ) |
| { |
| if ( domainName == null || domainName.length() == 0 ) |
| { |
| return ""; |
| } |
| |
| StringBuffer buf = new StringBuffer( domainName.length() + 16 ); |
| |
| buf.append( "dc=" ); |
| buf.append( domainName.replaceAll( "\\.", ",dc=" ) ); |
| |
| return buf.toString(); |
| } |
| |
| |
| /** |
| * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a> |
| * to transform a distinguished name into an Internet domain name. |
| * |
| * @param distinguishedName the distinguished name |
| * @return the domain name |
| */ |
| String transformDistinguishedName( String distinguishedName ) |
| { |
| if ( distinguishedName == null || distinguishedName.length() == 0 ) |
| { |
| return ""; |
| } |
| |
| String domainName = distinguishedName.replaceFirst( "dc=", "" ); |
| domainName = domainName.replaceAll( ",dc=", "." ); |
| |
| return domainName; |
| } |
| |
| |
| private RecordType getType( Attribute objectClass ) throws NamingException |
| { |
| NamingEnumeration<?> list = objectClass.getAll(); |
| |
| while ( list.hasMore() ) |
| { |
| String value = ( String ) list.next(); |
| |
| if ( !value.equals( "apacheDnsAbstractRecord" ) ) |
| { |
| RecordType type = OBJECTCLASS_TO_TYPE.get( value ); |
| |
| if ( type == null ) |
| { |
| throw new RuntimeException( "Record type to objectClass mapping has not been set." ); |
| } |
| |
| return type; |
| } |
| } |
| |
| throw new NamingException( "ResourceRecord requires STRUCTURAL objectClass" ); |
| } |
| |
| |
| private Name getRelativeName( String nameInNamespace, String baseDn ) throws NamingException |
| { |
| Properties props = new Properties(); |
| props.setProperty( "jndi.syntax.direction", "right_to_left" ); |
| props.setProperty( "jndi.syntax.separator", "," ); |
| props.setProperty( "jndi.syntax.ignorecase", "true" ); |
| props.setProperty( "jndi.syntax.trimblanks", "true" ); |
| |
| Name searchBaseDn = null; |
| |
| Name ctxRoot = new CompoundName( nameInNamespace, props ); |
| searchBaseDn = new CompoundName( baseDn, props ); |
| |
| if ( !searchBaseDn.startsWith( ctxRoot ) ) |
| { |
| throw new NamingException( "Invalid search base " + baseDn ); |
| } |
| |
| for ( int ii = 0; ii < ctxRoot.size(); ii++ ) |
| { |
| searchBaseDn.remove( 0 ); |
| } |
| |
| return searchBaseDn; |
| } |
| |
| |
| private Name getDomainComponents( Name name ) throws NamingException |
| { |
| for ( int ii = 0; ii < name.size(); ii++ ) |
| { |
| if ( !name.get( ii ).startsWith( "dc=" ) ) |
| { |
| name.remove( ii ); |
| } |
| } |
| |
| return name; |
| } |
| } |