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 */ 020 021package org.apache.directory.server.dns.store.jndi.operations; 022 023 024import java.util.Collections; 025import java.util.EnumMap; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.Map; 029import java.util.Properties; 030import java.util.Set; 031 032import javax.naming.CompoundName; 033import javax.naming.Name; 034import javax.naming.NamingEnumeration; 035import javax.naming.NamingException; 036import javax.naming.directory.Attribute; 037import javax.naming.directory.Attributes; 038import javax.naming.directory.DirContext; 039import javax.naming.directory.SearchControls; 040import javax.naming.directory.SearchResult; 041 042import org.apache.directory.api.ldap.model.constants.SchemaConstants; 043import org.apache.directory.server.dns.messages.QuestionRecord; 044import org.apache.directory.server.dns.messages.RecordClass; 045import org.apache.directory.server.dns.messages.RecordType; 046import org.apache.directory.server.dns.messages.ResourceRecord; 047import org.apache.directory.server.dns.messages.ResourceRecordModifier; 048import org.apache.directory.server.dns.store.DnsAttribute; 049import org.apache.directory.server.dns.store.jndi.DnsOperation; 050import org.apache.directory.server.i18n.I18n; 051 052 053/** 054 * A JNDI context operation for looking up Resource Records from an embedded JNDI provider. 055 * 056 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 057 */ 058public class GetRecords implements DnsOperation 059{ 060 /** The name of the question to get. */ 061 private final QuestionRecord question; 062 063 064 /** 065 * Creates the action to be used against the embedded JNDI provider. 066 * 067 * @param question 068 */ 069 public GetRecords( QuestionRecord question ) 070 { 071 this.question = question; 072 } 073 074 /** 075 * Mappings of type to objectClass. 076 */ 077 private static final Map<RecordType, String> TYPE_TO_OBJECTCLASS; 078 079 static 080 { 081 EnumMap<RecordType, String> typeToObjectClass = new EnumMap<>( RecordType.class ); 082 typeToObjectClass.put( RecordType.SOA, "apacheDnsStartOfAuthorityRecord" ); 083 typeToObjectClass.put( RecordType.A, "apacheDnsAddressRecord" ); 084 typeToObjectClass.put( RecordType.NS, "apacheDnsNameServerRecord" ); 085 typeToObjectClass.put( RecordType.CNAME, "apacheDnsCanonicalNameRecord" ); 086 typeToObjectClass.put( RecordType.PTR, "apacheDnsPointerRecord" ); 087 typeToObjectClass.put( RecordType.MX, "apacheDnsMailExchangeRecord" ); 088 typeToObjectClass.put( RecordType.SRV, "apacheDnsServiceRecord" ); 089 typeToObjectClass.put( RecordType.TXT, "apacheDnsTextRecord" ); 090 091 TYPE_TO_OBJECTCLASS = Collections.unmodifiableMap( typeToObjectClass ); 092 } 093 094 /** 095 * Mappings of type to objectClass. 096 */ 097 private static final Map<String, RecordType> OBJECTCLASS_TO_TYPE; 098 099 static 100 { 101 Map<String, RecordType> objectClassToType = new HashMap<>(); 102 objectClassToType.put( "apacheDnsStartOfAuthorityRecord", RecordType.SOA ); 103 objectClassToType.put( "apacheDnsAddressRecord", RecordType.A ); 104 objectClassToType.put( "apacheDnsNameServerRecord", RecordType.NS ); 105 objectClassToType.put( "apacheDnsCanonicalNameRecord", RecordType.CNAME ); 106 objectClassToType.put( "apacheDnsPointerRecord", RecordType.PTR ); 107 objectClassToType.put( "apacheDnsMailExchangeRecord", RecordType.MX ); 108 objectClassToType.put( "apacheDnsServiceRecord", RecordType.SRV ); 109 objectClassToType.put( "apacheDnsTextRecord", RecordType.TXT ); 110 objectClassToType.put( "apacheDnsReferralNameServer", RecordType.NS ); 111 objectClassToType.put( "apacheDnsReferralAddress", RecordType.A ); 112 113 OBJECTCLASS_TO_TYPE = Collections.unmodifiableMap( objectClassToType ); 114 } 115 116 117 /** 118 * Note that the base is a relative path from the exiting context. 119 * It is not a Dn. 120 */ 121 public Set<ResourceRecord> execute( DirContext ctx, Name base ) throws Exception 122 { 123 if ( question == null ) 124 { 125 return null; 126 } 127 128 String name = question.getDomainName(); 129 RecordType type = question.getRecordType(); 130 131 SearchControls controls = new SearchControls(); 132 controls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 133 134 String filter = "(objectClass=" + TYPE_TO_OBJECTCLASS.get( type ) + ")"; 135 136 NamingEnumeration<SearchResult> list = ctx.search( transformDomainName( name ), filter, controls ); 137 138 Set<ResourceRecord> set = new HashSet<>(); 139 140 while ( list.hasMore() ) 141 { 142 SearchResult result = list.next(); 143 Name relative = getRelativeName( ctx.getNameInNamespace(), result.getName() ); 144 145 set.add( getRecord( result.getAttributes(), relative ) ); 146 } 147 148 return set; 149 } 150 151 152 /** 153 * Marshals a RecordStoreEntry from an Attributes object. 154 * 155 * @param attrs the attributes of the DNS question 156 * @return the entry for the question 157 * @throws NamingException if there are any access problems 158 */ 159 private ResourceRecord getRecord( Attributes attrs, Name relative ) throws NamingException 160 { 161 String soaMinimum = "86400"; 162 String soaClass = "IN"; 163 164 ResourceRecordModifier modifier = new ResourceRecordModifier(); 165 166 Attribute attr; 167 168 // if no name, transform rdn 169 attr = attrs.get( DnsAttribute.NAME ); 170 171 if ( attr != null ) 172 { 173 modifier.setDnsName( ( String ) attr.get() ); 174 } 175 else 176 { 177 relative = getDomainComponents( relative ); 178 179 String dnsName; 180 dnsName = transformDistinguishedName( relative.toString() ); 181 modifier.setDnsName( dnsName ); 182 } 183 184 // type is implicit in objectclass 185 attr = attrs.get( DnsAttribute.TYPE ); 186 187 if ( attr != null ) 188 { 189 modifier.setDnsType( RecordType.valueOf( ( String ) attr.get() ) ); 190 } 191 else 192 { 193 modifier.setDnsType( getType( attrs.get( SchemaConstants.OBJECT_CLASS_AT ) ) ); 194 } 195 196 // class defaults to SOA CLASS 197 attr = attrs.get( DnsAttribute.CLASS ); 198 String dnsClass = attr != null ? ( String ) attr.get() : soaClass; 199 modifier.setDnsClass( RecordClass.valueOf( dnsClass ) ); 200 201 // ttl defaults to SOA MINIMUM 202 attr = attrs.get( DnsAttribute.TTL ); 203 String dnsTtl = attr != null ? ( String ) attr.get() : soaMinimum; 204 modifier.setDnsTtl( Integer.parseInt( dnsTtl ) ); 205 206 NamingEnumeration<String> ids = attrs.getIDs(); 207 208 while ( ids.hasMore() ) 209 { 210 String id = ids.next(); 211 modifier.put( id, ( String ) attrs.get( id ).get() ); 212 } 213 214 return modifier.getEntry(); 215 } 216 217 218 /** 219 * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a> 220 * to transform any Internet domain name into a distinguished name. 221 * 222 * @param domainName the domain name 223 * @return the distinguished name 224 */ 225 String transformDomainName( String domainName ) 226 { 227 if ( domainName == null || domainName.length() == 0 ) 228 { 229 return ""; 230 } 231 232 StringBuilder buf = new StringBuilder( domainName.length() + 16 ); 233 234 buf.append( "dc=" ); 235 buf.append( domainName.replaceAll( "\\.", ",dc=" ) ); 236 237 return buf.toString(); 238 } 239 240 241 /** 242 * Uses the algorithm in <a href="http://www.faqs.org/rfcs/rfc2247.html">RFC 2247</a> 243 * to transform a distinguished name into an Internet domain name. 244 * 245 * @param distinguishedName the distinguished name 246 * @return the domain name 247 */ 248 String transformDistinguishedName( String distinguishedName ) 249 { 250 if ( distinguishedName == null || distinguishedName.length() == 0 ) 251 { 252 return ""; 253 } 254 255 String domainName = distinguishedName.replaceFirst( "dc=", "" ); 256 domainName = domainName.replaceAll( ",dc=", "." ); 257 258 return domainName; 259 } 260 261 262 private RecordType getType( Attribute objectClass ) throws NamingException 263 { 264 NamingEnumeration<?> list = objectClass.getAll(); 265 266 while ( list.hasMore() ) 267 { 268 String value = ( String ) list.next(); 269 270 if ( !value.equals( "apacheDnsAbstractRecord" ) ) 271 { 272 RecordType type = OBJECTCLASS_TO_TYPE.get( value ); 273 274 if ( type == null ) 275 { 276 throw new RuntimeException( I18n.err( I18n.ERR_646 ) ); 277 } 278 279 return type; 280 } 281 } 282 283 throw new NamingException( I18n.err( I18n.ERR_647 ) ); 284 } 285 286 287 private Name getRelativeName( String nameInNamespace, String baseDn ) throws NamingException 288 { 289 Properties props = new Properties(); 290 props.setProperty( "jndi.syntax.direction", "right_to_left" ); 291 props.setProperty( "jndi.syntax.separator", "," ); 292 props.setProperty( "jndi.syntax.ignorecase", "true" ); 293 props.setProperty( "jndi.syntax.trimblanks", "true" ); 294 295 Name searchBaseDn = null; 296 297 Name ctxRoot = new CompoundName( nameInNamespace, props ); 298 searchBaseDn = new CompoundName( baseDn, props ); 299 300 if ( !searchBaseDn.startsWith( ctxRoot ) ) 301 { 302 throw new NamingException( I18n.err( I18n.ERR_648, baseDn ) ); 303 } 304 305 for ( int ii = 0; ii < ctxRoot.size(); ii++ ) 306 { 307 searchBaseDn.remove( 0 ); 308 } 309 310 return searchBaseDn; 311 } 312 313 314 private Name getDomainComponents( Name name ) throws NamingException 315 { 316 for ( int ii = 0; ii < name.size(); ii++ ) 317 { 318 if ( !name.get( ii ).startsWith( "dc=" ) ) 319 { 320 name.remove( ii ); 321 } 322 } 323 324 return name; 325 } 326}