blob: ffc0265d13b7ad8f8195728f8e8491ffed658e90 [file] [log] [blame]
/*
* 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;
}
}