1 /* 2 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jndi.ldap; 27 28 import java.util.*; 29 30 import javax.naming.*; 31 import javax.naming.directory.*; 32 import javax.naming.spi.ldap.LdapDnsProviderResult; 33 import javax.naming.spi.ObjectFactory; 34 import javax.naming.spi.InitialContextFactory; 35 import javax.naming.ldap.Control; 36 37 import com.sun.jndi.url.ldap.ldapURLContextFactory; 38 39 final public class LdapCtxFactory implements ObjectFactory, InitialContextFactory { 40 /** 41 * The type of each address in an LDAP reference. 42 */ 43 public final static String ADDRESS_TYPE = "URL"; 44 45 // ----------------- ObjectFactory interface -------------------- 46 47 public Object getObjectInstance(Object ref, Name name, Context nameCtx, 48 Hashtable<?,?> env) throws Exception { 49 50 if (!isLdapRef(ref)) { 51 return null; 52 } 53 ObjectFactory factory = new ldapURLContextFactory(); 54 String[] urls = getURLs((Reference)ref); 55 return factory.getObjectInstance(urls, name, nameCtx, env); 56 } 57 58 // ----------------- InitialContext interface -------------------- 59 60 public Context getInitialContext(Hashtable<?,?> envprops) 61 throws NamingException { 62 63 try { 64 String providerUrl = (envprops != null) ? 65 (String)envprops.get(Context.PROVIDER_URL) : null; 66 67 // If URL not in environment, use defaults 68 if (providerUrl == null) { 69 return new LdapCtx("", LdapCtx.DEFAULT_HOST, 70 LdapCtx.DEFAULT_PORT, envprops, false); 71 } 72 73 // Extract URL(s) 74 String[] urls = LdapURL.fromList(providerUrl); 75 76 if (urls.length == 0) { 77 throw new ConfigurationException(Context.PROVIDER_URL + 78 " property does not contain a URL"); 79 } 80 81 // Generate an LDAP context 82 return getLdapCtxInstance(urls, envprops); 83 84 } catch (LdapReferralException e) { 85 86 if (envprops != null && 87 "throw".equals(envprops.get(Context.REFERRAL))) { 88 throw e; 89 } 90 91 Control[] bindCtls = (envprops != null)? 92 (Control[])envprops.get(LdapCtx.BIND_CONTROLS) : null; 93 94 return (LdapCtx)e.getReferralContext(envprops, bindCtls); 95 } 96 } 97 98 /** 99 * Returns true if argument is an LDAP reference. 100 */ 101 private static boolean isLdapRef(Object obj) { 102 103 if (!(obj instanceof Reference)) { 104 return false; 105 } 106 String thisClassName = LdapCtxFactory.class.getName(); 107 Reference ref = (Reference)obj; 108 109 return thisClassName.equals(ref.getFactoryClassName()); 110 } 111 112 /** 113 * Returns the URLs contained within an LDAP reference. 114 */ 115 private static String[] getURLs(Reference ref) throws NamingException { 116 117 int size = 0; // number of URLs 118 String[] urls = new String[ref.size()]; 119 120 Enumeration<RefAddr> addrs = ref.getAll(); 121 while (addrs.hasMoreElements()) { 122 RefAddr addr = addrs.nextElement(); 123 124 if ((addr instanceof StringRefAddr) && 125 addr.getType().equals(ADDRESS_TYPE)) { 126 127 urls[size++] = (String)addr.getContent(); 128 } 129 } 130 if (size == 0) { 131 throw (new ConfigurationException( 132 "Reference contains no valid addresses")); 133 } 134 135 // Trim URL array down to size. 136 if (size == ref.size()) { 137 return urls; 138 } 139 String[] urls2 = new String[size]; 140 System.arraycopy(urls, 0, urls2, 0, size); 141 return urls2; 142 } 143 144 // ------------ Utilities used by other classes ---------------- 145 146 public static DirContext getLdapCtxInstance(Object urlInfo, Hashtable<?,?> env) 147 throws NamingException { 148 149 if (urlInfo instanceof String) { 150 return getUsingURL((String)urlInfo, env); 151 } else if (urlInfo instanceof String[]) { 152 return getUsingURLs((String[])urlInfo, env); 153 } else { 154 throw new IllegalArgumentException( 155 "argument must be an LDAP URL String or array of them"); 156 } 157 } 158 159 private static DirContext getUsingURL(String url, Hashtable<?,?> env) 160 throws NamingException 161 { 162 try { 163 LdapDnsProviderResult r = 164 LdapDnsProviderService.getInstance().lookupEndpoints(url, env); 165 LdapCtx ctx = null; 166 NamingException lastException = null; 167 168 /* 169 * Prior to this change we had been assuming that the url.getDN() 170 * should be converted to a domain name via 171 * ServiceLocator.mapDnToDomainName(url.getDN()) 172 * 173 * However this is incorrect as we can't assume that the supplied 174 * url.getDN() is the same as the dns domain for the directory 175 * server. 176 * 177 * This means that we depend on the dnsProvider to return both 178 * the list of urls of individual hosts from which we attempt to 179 * create an LdapCtx from *AND* the domain name that they serve 180 * 181 * In order to do this the dnsProvider must return an 182 * {@link LdapDnsProviderResult}. 183 * 184 */ 185 for (String u : r.getEndpoints()) { 186 try { 187 ctx = getLdapCtxFromUrl( 188 r.getDomainName(), new LdapURL(u), env); 189 } catch (NamingException e) { 190 // try the next element 191 lastException = e; 192 } 193 } 194 195 if (lastException != null) { 196 throw lastException; 197 } 198 199 if (ctx == null) { 200 // we have resolved them, but they are not valid 201 throw new NamingException("Could not resolve a valid ldap host"); 202 } 203 204 // Record the URL that created the context 205 ctx.setProviderUrl(url); 206 return ctx; 207 } catch (NamingException e) { 208 // getDnsUrls(url, env) may throw a NamingException, which there is 209 // no need to wrap. 210 throw e; 211 } catch (Exception e) { 212 NamingException ex = new NamingException(); 213 ex.setRootCause(e); 214 throw ex; 215 } 216 } 217 218 private static LdapCtx getLdapCtxFromUrl(String domain, 219 LdapURL url, 220 Hashtable<?,?> env) 221 throws NamingException 222 { 223 String dn = url.getDN(); 224 String host = url.getHost(); 225 int port = url.getPort(); 226 LdapCtx ctx = new LdapCtx(dn, host, port, env, url.useSsl()); 227 ctx.setDomainName(domain); 228 return ctx; 229 } 230 231 /* 232 * Try each URL until one of them succeeds. 233 * If all URLs fail, throw one of the exceptions arbitrarily. 234 * Not pretty, but potentially more informative than returning null. 235 */ 236 private static DirContext getUsingURLs(String[] urls, Hashtable<?,?> env) 237 throws NamingException 238 { 239 NamingException ex = null; 240 for (String u : urls) { 241 try { 242 return getUsingURL(u, env); 243 } catch (NamingException e) { 244 ex = e; 245 } 246 } 247 throw ex; 248 } 249 250 /** 251 * Used by Obj and obj/RemoteToAttrs too so must be public 252 */ 253 public static Attribute createTypeNameAttr(Class<?> cl) { 254 Vector<String> v = new Vector<>(10); 255 String[] types = getTypeNames(cl, v); 256 if (types.length > 0) { 257 BasicAttribute tAttr = 258 new BasicAttribute(Obj.JAVA_ATTRIBUTES[Obj.TYPENAME]); 259 for (int i = 0; i < types.length; i++) { 260 tAttr.add(types[i]); 261 } 262 return tAttr; 263 } 264 return null; 265 } 266 267 private static String[] getTypeNames(Class<?> currentClass, Vector<String> v) { 268 269 getClassesAux(currentClass, v); 270 Class<?>[] members = currentClass.getInterfaces(); 271 for (int i = 0; i < members.length; i++) { 272 getClassesAux(members[i], v); 273 } 274 String[] ret = new String[v.size()]; 275 int i = 0; 276 277 for (String name : v) { 278 ret[i++] = name; 279 } 280 return ret; 281 } 282 283 private static void getClassesAux(Class<?> currentClass, Vector<String> v) { 284 if (!v.contains(currentClass.getName())) { 285 v.addElement(currentClass.getName()); 286 } 287 currentClass = currentClass.getSuperclass(); 288 289 while (currentClass != null) { 290 getTypeNames(currentClass, v); 291 currentClass = currentClass.getSuperclass(); 292 } 293 } 294 }