1 /* 2 * Copyright (c) 2000, 2010, 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.dns; 27 28 29 import java.net.MalformedURLException; 30 import java.util.ArrayList; 31 import java.util.Hashtable; 32 import java.util.List; 33 34 import javax.naming.*; 35 import javax.naming.spi.*; 36 37 import com.sun.jndi.toolkit.url.UrlUtil; 38 import sun.net.dns.ResolverConfiguration; // available since 1.4.1 39 40 41 /** 42 * A DnsContextFactory serves as the initial context factory for DNS. 43 * 44 * <p> When an initial context is being created, the environment 45 * property "java.naming.provider.url" should contain a DNS pseudo-URL 46 * (see DnsUrl) or a space-separated list of them. Multiple URLs must 47 * all have the same domain value. 48 * If the property is not set, the default "dns:" is used. 49 * 50 * @author Scott Seligman 51 */ 52 53 54 public class DnsContextFactory implements InitialContextFactory { 55 56 private static final String DEFAULT_URL = "dns:"; 57 private static final int DEFAULT_PORT = 53; 58 59 60 public Context getInitialContext(Hashtable<?,?> env) throws NamingException { 61 if (env == null) { 62 env = new Hashtable(5); 63 } 64 return urlToContext(getInitCtxUrl(env), env); 65 } 66 67 public static DnsContext getContext(String domain, 68 String[] servers, Hashtable<?,?> env) 69 throws NamingException { 70 return new DnsContext(domain, servers, env); 71 } 72 73 /* 74 * "urls" are used to determine the servers, but any domain 75 * components are overridden by "domain". 76 */ 77 public static DnsContext getContext(String domain, 78 DnsUrl[] urls, Hashtable env) 79 throws NamingException { 80 81 String[] servers = serversForUrls(urls); 82 DnsContext ctx = getContext(domain, servers, env); 83 if (platformServersUsed(urls)) { 84 ctx.setProviderUrl(constructProviderUrl(domain, servers)); 85 } 86 return ctx; 87 } 88 89 /* 90 * Public for use by product test suite. 91 */ 92 public static boolean platformServersAvailable() { 93 return !filterNameServers( 94 ResolverConfiguration.open().nameservers(), true 95 ).isEmpty(); 96 } 97 98 private static Context urlToContext(String url, Hashtable env) 99 throws NamingException { 100 101 DnsUrl[] urls; 102 try { 103 urls = DnsUrl.fromList(url); 104 } catch (MalformedURLException e) { 105 throw new ConfigurationException(e.getMessage()); 106 } 107 if (urls.length == 0) { 108 throw new ConfigurationException( 109 "Invalid DNS pseudo-URL(s): " + url); 110 } 111 String domain = urls[0].getDomain(); 112 113 // If multiple urls, all must have the same domain. 114 for (int i = 1; i < urls.length; i++) { 115 if (!domain.equalsIgnoreCase(urls[i].getDomain())) { 116 throw new ConfigurationException( 117 "Conflicting domains: " + url); 118 } 119 } 120 return getContext(domain, urls, env); 121 } 122 123 /* 124 * Returns all the servers specified in a set of URLs. 125 * If a URL has no host (or port), the servers configured on the 126 * underlying platform are used if possible. If no configured 127 * servers can be found, then fall back to the old behavior of 128 * using "localhost". 129 * There must be at least one URL. 130 */ 131 private static String[] serversForUrls(DnsUrl[] urls) 132 throws NamingException { 133 134 if (urls.length == 0) { 135 throw new ConfigurationException("DNS pseudo-URL required"); 136 } 137 138 List servers = new ArrayList(); 139 140 for (int i = 0; i < urls.length; i++) { 141 String server = urls[i].getHost(); 142 int port = urls[i].getPort(); 143 144 if (server == null && port < 0) { 145 // No server or port given, so look to underlying platform. 146 // ResolverConfiguration does some limited caching, so the 147 // following is reasonably efficient even if called rapid-fire. 148 List platformServers = filterNameServers( 149 ResolverConfiguration.open().nameservers(), false); 150 if (!platformServers.isEmpty()) { 151 servers.addAll(platformServers); 152 continue; // on to next URL (if any, which is unlikely) 153 } 154 } 155 156 if (server == null) { 157 server = "localhost"; 158 } 159 servers.add((port < 0) 160 ? server 161 : server + ":" + port); 162 } 163 return (String[]) servers.toArray( 164 new String[servers.size()]); 165 } 166 167 /* 168 * Returns true if serversForUrls(urls) would make use of servers 169 * from the underlying platform. 170 */ 171 private static boolean platformServersUsed(DnsUrl[] urls) { 172 if (!platformServersAvailable()) { 173 return false; 174 } 175 for (int i = 0; i < urls.length; i++) { 176 if (urls[i].getHost() == null && 177 urls[i].getPort() < 0) { 178 return true; 179 } 180 } 181 return false; 182 } 183 184 /* 185 * Returns a value for the PROVIDER_URL property (space-separated URL 186 * Strings) that reflects the given domain and servers. 187 * Each server is of the form "server[:port]". 188 * There must be at least one server. 189 * IPv6 literal host names include delimiting brackets. 190 */ 191 private static String constructProviderUrl(String domain, 192 String[] servers) { 193 String path = ""; 194 if (!domain.equals(".")) { 195 try { 196 path = "/" + UrlUtil.encode(domain, "ISO-8859-1"); 197 } catch (java.io.UnsupportedEncodingException e) { 198 // assert false : "ISO-Latin-1 charset unavailable"; 199 } 200 } 201 202 StringBuffer buf = new StringBuffer(); 203 for (int i = 0; i < servers.length; i++) { 204 if (i > 0) { 205 buf.append(' '); 206 } 207 buf.append("dns://").append(servers[i]).append(path); 208 } 209 return buf.toString(); 210 } 211 212 /* 213 * Reads environment to find URL(s) of initial context. 214 * Default URL is "dns:". 215 */ 216 private static String getInitCtxUrl(Hashtable env) { 217 String url = (String) env.get(Context.PROVIDER_URL); 218 return ((url != null) ? url : DEFAULT_URL); 219 } 220 221 /** 222 * Removes any DNS server that's not permitted to access 223 * @param input the input server[:port] list, must not be null 224 * @param oneIsEnough return output once there exists one ok 225 * @return the filtered list, all non-permitted input removed 226 */ 227 private static List filterNameServers(List input, boolean oneIsEnough) { 228 SecurityManager security = System.getSecurityManager(); 229 if (security == null || input == null || input.isEmpty()) { 230 return input; 231 } else { 232 List output = new ArrayList(); 233 for (Object o: input) { 234 if (o instanceof String) { 235 String platformServer = (String)o; 236 int colon = platformServer.indexOf(':', 237 platformServer.indexOf(']') + 1); 238 239 int p = (colon < 0) 240 ? DEFAULT_PORT 241 : Integer.parseInt( 242 platformServer.substring(colon + 1)); 243 String s = (colon < 0) 244 ? platformServer 245 : platformServer.substring(0, colon); 246 try { 247 security.checkConnect(s, p); 248 output.add(platformServer); 249 if (oneIsEnough) { 250 return output; 251 } 252 } catch (SecurityException se) { 253 continue; 254 } 255 } 256 } 257 return output; 258 } 259 } 260 }