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<String> 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<String> 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 servers.toArray(new String[servers.size()]); 164 } 165 166 /* 167 * Returns true if serversForUrls(urls) would make use of servers 168 * from the underlying platform. 169 */ 170 private static boolean platformServersUsed(DnsUrl[] urls) { 171 if (!platformServersAvailable()) { 172 return false; 173 } 174 for (int i = 0; i < urls.length; i++) { 175 if (urls[i].getHost() == null && 176 urls[i].getPort() < 0) { 177 return true; 178 } 179 } 180 return false; 181 } 182 183 /* 184 * Returns a value for the PROVIDER_URL property (space-separated URL 185 * Strings) that reflects the given domain and servers. 186 * Each server is of the form "server[:port]". 187 * There must be at least one server. 188 * IPv6 literal host names include delimiting brackets. 189 */ 190 private static String constructProviderUrl(String domain, 191 String[] servers) { 192 String path = ""; 193 if (!domain.equals(".")) { 194 try { 195 path = "/" + UrlUtil.encode(domain, "ISO-8859-1"); 196 } catch (java.io.UnsupportedEncodingException e) { 197 // assert false : "ISO-Latin-1 charset unavailable"; 198 } 199 } 200 201 StringBuffer buf = new StringBuffer(); 202 for (int i = 0; i < servers.length; i++) { 203 if (i > 0) { 204 buf.append(' '); 205 } 206 buf.append("dns://").append(servers[i]).append(path); 207 } 208 return buf.toString(); 209 } 210 211 /* 212 * Reads environment to find URL(s) of initial context. 213 * Default URL is "dns:". 214 */ 215 private static String getInitCtxUrl(Hashtable env) { 216 String url = (String) env.get(Context.PROVIDER_URL); 217 return ((url != null) ? url : DEFAULT_URL); 218 } 219 220 /** 221 * Removes any DNS server that's not permitted to access 222 * @param input the input server[:port] list, must not be null 223 * @param oneIsEnough return output once there exists one ok 224 * @return the filtered list, all non-permitted input removed 225 */ 226 private static List filterNameServers(List input, boolean oneIsEnough) { 227 SecurityManager security = System.getSecurityManager(); 228 if (security == null || input == null || input.isEmpty()) { 229 return input; 230 } else { 231 List output = new ArrayList(); 232 for (Object o: input) { 233 if (o instanceof String) { 234 String platformServer = (String)o; 235 int colon = platformServer.indexOf(':', 236 platformServer.indexOf(']') + 1); 237 238 int p = (colon < 0) 239 ? DEFAULT_PORT 240 : Integer.parseInt( 241 platformServer.substring(colon + 1)); 242 String s = (colon < 0) 243 ? platformServer 244 : platformServer.substring(0, colon); 245 try { 246 security.checkConnect(s, p); 247 output.add(platformServer); 248 if (oneIsEnough) { 249 return output; 250 } 251 } catch (SecurityException se) { 252 continue; 253 } 254 } 255 } 256 return output; 257 } 258 } 259 }