8278972: Improve URL supports
Reviewed-by: skoivu, rhalade, alanb
This commit is contained in:
parent
395bb5b7f9
commit
9e051d5396
src
java.naming/share/classes/com/sun/jndi
jdk.naming.dns/share/classes/com/sun/jndi/dns
jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,6 +28,10 @@ package com.sun.jndi.ldap;
|
||||
import javax.naming.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
import com.sun.jndi.toolkit.url.Uri;
|
||||
import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
@ -64,6 +68,25 @@ import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
|
||||
public final class LdapURL extends Uri {
|
||||
|
||||
private static final String PARSE_MODE_PROP = "com.sun.jndi.ldapURLParsing";
|
||||
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
|
||||
|
||||
public static final ParseMode PARSE_MODE;
|
||||
static {
|
||||
PrivilegedAction<String> action = () ->
|
||||
System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
|
||||
ParseMode parseMode = DEFAULT_PARSE_MODE;
|
||||
try {
|
||||
@SuppressWarnings("removal")
|
||||
String mode = AccessController.doPrivileged(action);
|
||||
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
|
||||
} catch (Throwable t) {
|
||||
parseMode = DEFAULT_PARSE_MODE;
|
||||
} finally {
|
||||
PARSE_MODE = parseMode;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean useSsl = false;
|
||||
private String DN = null;
|
||||
private String attributes = null;
|
||||
@ -83,7 +106,7 @@ public final class LdapURL extends Uri {
|
||||
useSsl = scheme.equalsIgnoreCase("ldaps");
|
||||
|
||||
if (! (scheme.equalsIgnoreCase("ldap") || useSsl)) {
|
||||
throw new MalformedURLException("Not an LDAP URL: " + url);
|
||||
throw newInvalidURISchemeException(url);
|
||||
}
|
||||
|
||||
parsePathAndQuery(); // DN, attributes, scope, filter, extensions
|
||||
@ -99,6 +122,21 @@ public final class LdapURL extends Uri {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MalformedURLException newInvalidURISchemeException(String uri) {
|
||||
return new MalformedURLException("Not an LDAP URL: " + uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSchemeOnly(String uri) {
|
||||
return isLdapSchemeOnly(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ParseMode parseMode() {
|
||||
return PARSE_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the URL is an LDAPS URL.
|
||||
*/
|
||||
@ -151,13 +189,33 @@ public final class LdapURL extends Uri {
|
||||
StringTokenizer st = new StringTokenizer(urlList, " ");
|
||||
|
||||
while (st.hasMoreTokens()) {
|
||||
urls[i++] = st.nextToken();
|
||||
// we don't accept scheme-only URLs here
|
||||
urls[i++] = validateURI(st.nextToken());
|
||||
}
|
||||
String[] trimmed = new String[i];
|
||||
System.arraycopy(urls, 0, trimmed, 0, i);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
public static boolean isLdapSchemeOnly(String uri) {
|
||||
return "ldap:".equals(uri) || "ldaps:".equals(uri);
|
||||
}
|
||||
|
||||
public static String validateURI(String uri) {
|
||||
// no validation in legacy mode parsing
|
||||
if (PARSE_MODE == ParseMode.LEGACY) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
// special case of scheme-only URIs
|
||||
if (isLdapSchemeOnly(uri)) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
// use java.net.URI to validate the uri syntax
|
||||
return URI.create(uri).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an LDAP URL has query components.
|
||||
*/
|
||||
@ -181,7 +239,8 @@ public final class LdapURL extends Uri {
|
||||
String p = (port != -1) ? (":" + port) : "";
|
||||
String d = (dn != null) ? ("/" + UrlUtil.encode(dn, "UTF8")) : "";
|
||||
|
||||
return useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d;
|
||||
String uri = useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d;
|
||||
return validateURI(uri);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// UTF8 should always be supported
|
||||
throw new IllegalStateException("UTF-8 encoding unavailable");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,6 +32,8 @@ import javax.naming.spi.NamingManager;
|
||||
import java.util.Hashtable;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import com.sun.jndi.toolkit.url.Uri.ParseMode;
|
||||
|
||||
/**
|
||||
* This abstract class is a generic URL context that accepts as the
|
||||
* name argument either a string URL or a Name whose first component
|
||||
@ -48,6 +50,7 @@ import java.net.MalformedURLException;
|
||||
* @author Rosanna Lee
|
||||
*/
|
||||
public abstract class GenericURLContext implements Context {
|
||||
|
||||
protected Hashtable<String, Object> myEnv = null;
|
||||
|
||||
@SuppressWarnings("unchecked") // Expect Hashtable<String, Object>
|
||||
@ -161,8 +164,18 @@ public abstract class GenericURLContext implements Context {
|
||||
if (url.startsWith("//", start)) {
|
||||
start += 2; // skip double slash
|
||||
|
||||
// find last slash
|
||||
int posn = url.indexOf('/', start);
|
||||
// find where the authority component ends
|
||||
// and the rest of the URL starts
|
||||
int slash = url.indexOf('/', start);
|
||||
int qmark = url.indexOf('?', start);
|
||||
int fmark = url.indexOf('#', start);
|
||||
if (fmark > -1 && qmark > fmark) qmark = -1;
|
||||
if (fmark > -1 && slash > fmark) slash = -1;
|
||||
if (qmark > -1 && slash > qmark) slash = -1;
|
||||
int posn = slash > -1 ? slash
|
||||
: (qmark > -1 ? qmark
|
||||
: (fmark > -1 ? fmark
|
||||
: url.length()));
|
||||
if (posn >= 0) {
|
||||
start = posn;
|
||||
} else {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,6 +27,8 @@ package com.sun.jndi.toolkit.url;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
|
||||
/**
|
||||
@ -36,15 +38,17 @@ import java.net.MalformedURLException;
|
||||
*
|
||||
* <p> The java.net.URL class cannot be used to parse URIs since it
|
||||
* requires the installation of URL stream handlers that may not be
|
||||
* available. The hack of getting around this by temporarily
|
||||
* replacing the scheme part of a URI is not appropriate here: JNDI
|
||||
* service providers must work on older Java platforms, and we want
|
||||
* new features and bug fixes that are not available in old versions
|
||||
* of the URL class.
|
||||
* available.
|
||||
*
|
||||
* <p> It may be appropriate to drop this code in favor of the
|
||||
* java.net.URI class. The changes would need to be written so as to
|
||||
* still run on pre-1.4 platforms not containing that class.
|
||||
* <p> The {@linkplain ParseMode#STRICT strict} parsing mode uses
|
||||
* the java.net.URI class to syntactically validate URI strings.
|
||||
* The {@linkplain ParseMode#COMPAT compat} mode validate the
|
||||
* URI authority and rejects URI fragments, but doesn't perform any
|
||||
* additional validation on path and query, other than that
|
||||
* which may be implemented in the concrete the Uri subclasses.
|
||||
* The {@linkplain ParseMode#LEGACY legacy} mode should not be
|
||||
* used unless the application is capable of validating all URI
|
||||
* strings before any constructors of this class is invoked.
|
||||
*
|
||||
* <p> The format of an absolute URI (see the RFCs mentioned above) is:
|
||||
* <blockquote><pre>{@code
|
||||
@ -105,6 +109,28 @@ import java.net.MalformedURLException;
|
||||
|
||||
public class Uri {
|
||||
|
||||
// three parsing modes
|
||||
public enum ParseMode {
|
||||
/**
|
||||
* Strict validation mode.
|
||||
* Validate the URI syntactically using {@link java.net.URI}.
|
||||
* Rejects URI fragments unless explicitly supported by the
|
||||
* subclass.
|
||||
*/
|
||||
STRICT,
|
||||
/**
|
||||
* Compatibility mode. The URI authority is syntactically validated.
|
||||
* Rejects URI fragments unless explicitly supported by the
|
||||
* subclass.
|
||||
* This is the default.
|
||||
*/
|
||||
COMPAT,
|
||||
/**
|
||||
* Legacy mode. In this mode, no validation is performed.
|
||||
*/
|
||||
LEGACY
|
||||
}
|
||||
|
||||
protected String uri;
|
||||
protected String scheme;
|
||||
protected String host = null;
|
||||
@ -112,6 +138,7 @@ public class Uri {
|
||||
protected boolean hasAuthority;
|
||||
protected String path;
|
||||
protected String query = null;
|
||||
protected String fragment;
|
||||
|
||||
|
||||
/**
|
||||
@ -128,6 +155,15 @@ public class Uri {
|
||||
protected Uri() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The parse mode for parsing this URI.
|
||||
* The default is {@link ParseMode#COMPAT}.
|
||||
* @return the parse mode for parsing this URI.
|
||||
*/
|
||||
protected ParseMode parseMode() {
|
||||
return ParseMode.COMPAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a Uri object given a URI string.
|
||||
* This method must be called exactly once, and before any other Uri
|
||||
@ -135,7 +171,7 @@ public class Uri {
|
||||
*/
|
||||
protected void init(String uri) throws MalformedURLException {
|
||||
this.uri = uri;
|
||||
parse(uri);
|
||||
parse(uri, parseMode());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,10 +224,229 @@ public class Uri {
|
||||
return uri;
|
||||
}
|
||||
|
||||
private void parse(String uri, ParseMode mode) throws MalformedURLException {
|
||||
switch (mode) {
|
||||
case STRICT -> parseStrict(uri);
|
||||
case COMPAT -> parseCompat(uri);
|
||||
case LEGACY -> parseLegacy(uri);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a URI string and sets this object's fields accordingly.
|
||||
* Use java.net.URI to validate the uri string syntax
|
||||
*/
|
||||
private void parse(String uri) throws MalformedURLException {
|
||||
private void parseStrict(String uri) throws MalformedURLException {
|
||||
try {
|
||||
if (!isSchemeOnly(uri)) {
|
||||
URI u = new URI(uri);
|
||||
scheme = u.getScheme();
|
||||
if (scheme == null) throw new MalformedURLException("Invalid URI: " + uri);
|
||||
var auth = u.getRawAuthority();
|
||||
hasAuthority = auth != null;
|
||||
if (hasAuthority) {
|
||||
var host = u.getHost();
|
||||
var port = u.getPort();
|
||||
if (host != null) this.host = host;
|
||||
if (port != -1) this.port = port;
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1 ? "" : (":" + port));
|
||||
if (!hostport.equals(auth)) {
|
||||
// throw if we have user info or regname
|
||||
throw new MalformedURLException("unsupported authority: " + auth);
|
||||
}
|
||||
}
|
||||
path = u.getRawPath();
|
||||
if (u.getRawQuery() != null) {
|
||||
query = "?" + u.getRawQuery();
|
||||
}
|
||||
if (u.getRawFragment() != null) {
|
||||
if (!acceptsFragment()) {
|
||||
throw new MalformedURLException("URI fragments not supported: " + uri);
|
||||
}
|
||||
fragment = "#" + u.getRawFragment();
|
||||
}
|
||||
} else {
|
||||
// scheme-only URIs are not supported by java.net.URI
|
||||
// validate the URI by appending "/" to the uri string.
|
||||
var s = uri.substring(0, uri.indexOf(':'));
|
||||
URI u = new URI(uri + "/");
|
||||
if (!s.equals(u.getScheme())
|
||||
|| !checkSchemeOnly(uri, u.getScheme())) {
|
||||
throw newInvalidURISchemeException(uri);
|
||||
}
|
||||
scheme = s;
|
||||
path = "";
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
var mue = new MalformedURLException(e.getMessage());
|
||||
mue.initCause(e);
|
||||
throw mue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parses a URI string and sets this object's fields accordingly.
|
||||
* Compatibility mode. Use java.net.URI to validate the syntax of
|
||||
* the uri string authority.
|
||||
*/
|
||||
private void parseCompat(String uri) throws MalformedURLException {
|
||||
int i; // index into URI
|
||||
|
||||
i = uri.indexOf(':'); // parse scheme
|
||||
int slash = uri.indexOf('/');
|
||||
int qmark = uri.indexOf('?');
|
||||
int fmark = uri.indexOf('#');
|
||||
if (i < 0 || slash > 0 && i > slash || qmark > 0 && i > qmark || fmark > 0 && i > fmark) {
|
||||
throw new MalformedURLException("Invalid URI: " + uri);
|
||||
}
|
||||
if (fmark > -1) {
|
||||
if (!acceptsFragment()) {
|
||||
throw new MalformedURLException("URI fragments not supported: " + uri);
|
||||
}
|
||||
}
|
||||
if (i == uri.length() - 1) {
|
||||
if (!isSchemeOnly(uri)) {
|
||||
throw newInvalidURISchemeException(uri);
|
||||
}
|
||||
}
|
||||
scheme = uri.substring(0, i);
|
||||
i++; // skip past ":"
|
||||
|
||||
hasAuthority = uri.startsWith("//", i);
|
||||
if (fmark > -1 && qmark > fmark) qmark = -1;
|
||||
int endp = qmark > -1 ? qmark : fmark > -1 ? fmark : uri.length();
|
||||
if (hasAuthority) { // parse "//host:port"
|
||||
i += 2; // skip past "//"
|
||||
int starta = i;
|
||||
// authority ends at the first appearance of /, ?, or #
|
||||
int enda = uri.indexOf('/', i);
|
||||
if (enda == -1 || qmark > -1 && qmark < enda) enda = qmark;
|
||||
if (enda == -1 || fmark > -1 && fmark < enda) enda = fmark;
|
||||
if (enda < 0) {
|
||||
enda = uri.length();
|
||||
}
|
||||
if (uri.startsWith(":", i)) {
|
||||
// LdapURL supports empty host.
|
||||
i++;
|
||||
host = "";
|
||||
if (enda > i) {
|
||||
port = Integer.parseInt(uri.substring(i, enda));
|
||||
}
|
||||
} else {
|
||||
// Use URI to parse authority
|
||||
try {
|
||||
// URI requires at least one char after authority:
|
||||
// we use "/" and expect that the resulting URI path
|
||||
// will be exactly "/".
|
||||
URI u = new URI(uri.substring(0, enda) + "/");
|
||||
String auth = uri.substring(starta, enda);
|
||||
host = u.getHost();
|
||||
port = u.getPort();
|
||||
String p = u.getRawPath();
|
||||
String q = u.getRawQuery();
|
||||
String f = u.getRawFragment();
|
||||
String ui = u.getRawUserInfo();
|
||||
if (ui != null) {
|
||||
throw new MalformedURLException("user info not supported in authority: " + ui);
|
||||
}
|
||||
if (!"/".equals(p)) {
|
||||
throw new MalformedURLException("invalid authority: " + auth);
|
||||
}
|
||||
if (q != null) {
|
||||
throw new MalformedURLException("invalid trailing characters in authority: ?" + q);
|
||||
}
|
||||
if (f != null) {
|
||||
throw new MalformedURLException("invalid trailing characters in authority: #" + f);
|
||||
}
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1?"":(":" + port));
|
||||
if (!auth.equals(hostport)) {
|
||||
// throw if we have user info or regname
|
||||
throw new MalformedURLException("unsupported authority: " + auth);
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
var mue = new MalformedURLException(e.getMessage());
|
||||
mue.initCause(e);
|
||||
throw mue;
|
||||
}
|
||||
}
|
||||
i = enda;
|
||||
}
|
||||
path = uri.substring(i, endp);
|
||||
// look for query
|
||||
if (qmark > -1) {
|
||||
if (fmark > -1) {
|
||||
query = uri.substring(qmark, fmark);
|
||||
} else {
|
||||
query = uri.substring(qmark);
|
||||
}
|
||||
}
|
||||
if (fmark > -1) {
|
||||
fragment = uri.substring(fmark);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass of {@code Uri} that supports scheme only
|
||||
* URIs can override this method and return true in the
|
||||
* case where the URI string is a scheme-only URI that
|
||||
* the subclass supports.
|
||||
* @implSpec
|
||||
* The default implementation of this method returns false,
|
||||
* always.
|
||||
* @param uri An URI string
|
||||
* @return if this is a scheme-only URI supported by the subclass
|
||||
*/
|
||||
protected boolean isSchemeOnly(String uri) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given uri string should be considered
|
||||
* as a scheme-only URI. For some protocols - e.g. DNS, we
|
||||
* might accept "dns://" as a valid URL denoting default DNS.
|
||||
* For others - we might only accept "scheme:".
|
||||
* @implSpec
|
||||
* The default implementation of this method returns true if
|
||||
* the URI is of the form {@code "<scheme>:"} with nothing
|
||||
* after the scheme delimiter.
|
||||
* @param uri the URI
|
||||
* @param scheme the scheme
|
||||
* @return true if the URI should be considered as a scheme-only
|
||||
* URI supported by this URI scheme.
|
||||
*/
|
||||
protected boolean checkSchemeOnly(String uri, String scheme) {
|
||||
return uri.equals(scheme + ":");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code MalformedURLException} to be thrown when the
|
||||
* URI scheme is not supported.
|
||||
*
|
||||
* @param uri the URI string
|
||||
* @return a {@link MalformedURLException}
|
||||
*/
|
||||
protected MalformedURLException newInvalidURISchemeException(String uri) {
|
||||
return new MalformedURLException("Invalid URI scheme: " + uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether fragments are supported.
|
||||
* @implSpec
|
||||
* The default implementation of this method retturns false, always.
|
||||
* @return true if fragments are supported.
|
||||
*/
|
||||
protected boolean acceptsFragment() {
|
||||
return parseMode() == ParseMode.LEGACY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a URI string and sets this object's fields accordingly.
|
||||
* Legacy parsing mode.
|
||||
*/
|
||||
private void parseLegacy(String uri) throws MalformedURLException {
|
||||
int i; // index into URI
|
||||
|
||||
i = uri.indexOf(':'); // parse scheme
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,7 +27,11 @@ package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Hashtable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import com.sun.jndi.toolkit.url.Uri;
|
||||
@ -56,6 +60,24 @@ import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
|
||||
public class DnsUrl extends Uri {
|
||||
|
||||
private static final String PARSE_MODE_PROP = "com.sun.jndi.dnsURLParsing";
|
||||
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
|
||||
|
||||
public static final ParseMode PARSE_MODE;
|
||||
static {
|
||||
PrivilegedAction<String> action = () ->
|
||||
System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
|
||||
ParseMode parseMode = DEFAULT_PARSE_MODE;
|
||||
try {
|
||||
@SuppressWarnings("removal")
|
||||
String mode = AccessController.doPrivileged(action);
|
||||
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
|
||||
} catch (Throwable t) {
|
||||
parseMode = DEFAULT_PARSE_MODE;
|
||||
} finally {
|
||||
PARSE_MODE = parseMode;
|
||||
}
|
||||
}
|
||||
private String domain; // domain name of the context
|
||||
|
||||
|
||||
@ -71,19 +93,58 @@ public class DnsUrl extends Uri {
|
||||
StringTokenizer st = new StringTokenizer(urlList, " ");
|
||||
|
||||
while (st.hasMoreTokens()) {
|
||||
urls[i++] = new DnsUrl(st.nextToken());
|
||||
try {
|
||||
urls[i++] = new DnsUrl(validateURI(st.nextToken()));
|
||||
} catch (URISyntaxException e) {
|
||||
MalformedURLException mue = new MalformedURLException(e.getMessage());
|
||||
mue.initCause(e);
|
||||
throw mue;
|
||||
}
|
||||
}
|
||||
DnsUrl[] trimmed = new DnsUrl[i];
|
||||
System.arraycopy(urls, 0, trimmed, 0, i);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ParseMode parseMode() {
|
||||
return PARSE_MODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean isSchemeOnly(String uri) {
|
||||
return isDnsSchemeOnly(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkSchemeOnly(String uri, String scheme) {
|
||||
return uri.equals(scheme + ":") || uri.equals(scheme + "://");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final MalformedURLException newInvalidURISchemeException(String uri) {
|
||||
return new MalformedURLException(
|
||||
uri + " is not a valid DNS pseudo-URL");
|
||||
}
|
||||
|
||||
private static boolean isDnsSchemeOnly(String uri) {
|
||||
return "dns:".equals(uri) || "dns://".equals(uri);
|
||||
}
|
||||
|
||||
private static String validateURI(String uri) throws URISyntaxException {
|
||||
// no validation in legacy parsing mode
|
||||
if (PARSE_MODE == ParseMode.LEGACY) return uri;
|
||||
// special case of scheme-only URIs
|
||||
if (isDnsSchemeOnly(uri)) return uri;
|
||||
// use java.net.URI to validate the uri syntax
|
||||
return new URI(uri).toString();
|
||||
}
|
||||
|
||||
public DnsUrl(String url) throws MalformedURLException {
|
||||
super(url);
|
||||
|
||||
if (!scheme.equals("dns")) {
|
||||
throw new MalformedURLException(
|
||||
url + " is not a valid DNS pseudo-URL");
|
||||
throw newInvalidURISchemeException(url);
|
||||
}
|
||||
|
||||
domain = path.startsWith("/")
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,12 +25,17 @@
|
||||
|
||||
package com.sun.jndi.url.rmi;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.spi.ResolveResult;
|
||||
import com.sun.jndi.toolkit.url.GenericURLContext;
|
||||
import com.sun.jndi.rmi.registry.RegistryContext;
|
||||
import com.sun.jndi.toolkit.url.Uri.ParseMode;
|
||||
|
||||
|
||||
/**
|
||||
@ -47,10 +52,244 @@ import com.sun.jndi.rmi.registry.RegistryContext;
|
||||
*/
|
||||
public class rmiURLContext extends GenericURLContext {
|
||||
|
||||
private static final String PARSE_MODE_PROP = "com.sun.jndi.rmiURLParsing";
|
||||
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
|
||||
|
||||
public static final ParseMode PARSE_MODE;
|
||||
static {
|
||||
PrivilegedAction<String> action = () ->
|
||||
System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
|
||||
ParseMode parseMode = DEFAULT_PARSE_MODE;
|
||||
try {
|
||||
@SuppressWarnings("removal")
|
||||
String mode = AccessController.doPrivileged(action);
|
||||
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
|
||||
} catch (Throwable t) {
|
||||
parseMode = DEFAULT_PARSE_MODE;
|
||||
} finally {
|
||||
PARSE_MODE = parseMode;
|
||||
}
|
||||
}
|
||||
|
||||
public rmiURLContext(Hashtable<?,?> env) {
|
||||
super(env);
|
||||
}
|
||||
|
||||
public static class Parser {
|
||||
final String url;
|
||||
final ParseMode mode;
|
||||
String host = null;
|
||||
int port = -1;
|
||||
String objName = null;
|
||||
public Parser(String url) {
|
||||
this(url, PARSE_MODE);
|
||||
}
|
||||
public Parser(String url, ParseMode mode) {
|
||||
this.url = url;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public String url() {return url;}
|
||||
public String host() {return host;}
|
||||
public int port() {return port;}
|
||||
public String objName() {return objName;}
|
||||
public ParseMode mode() {return mode;}
|
||||
|
||||
public void parse() throws NamingException {
|
||||
if (!url.startsWith("rmi:")) {
|
||||
throw (new IllegalArgumentException(
|
||||
"rmiURLContext: name is not an RMI URL: " + url));
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case STRICT -> parseStrict();
|
||||
case COMPAT -> parseCompat();
|
||||
case LEGACY -> parseLegacy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void parseStrict() throws NamingException {
|
||||
assert url.startsWith("rmi:");
|
||||
|
||||
if (url.equals("rmi:") || url.equals("rmi://")) return;
|
||||
|
||||
// index into url, following the "rmi:"
|
||||
int i = 4;
|
||||
|
||||
if (url.startsWith("//", i)) {
|
||||
i += 2;
|
||||
try {
|
||||
URI uri = URI.create(url);
|
||||
host = uri.getHost();
|
||||
port = uri.getPort();
|
||||
String auth = uri.getRawAuthority();
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1 ? "" : ":" + port);
|
||||
if (!hostport.equals(auth)) {
|
||||
boolean failed = true;
|
||||
if (hostport.equals("") && auth.startsWith(":")) {
|
||||
// supports missing host
|
||||
try {
|
||||
port = Integer.parseInt(auth.substring(1));
|
||||
failed = false;
|
||||
} catch (NumberFormatException x) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
throw newNamingException(new IllegalArgumentException("invalid authority: "
|
||||
+ auth));
|
||||
}
|
||||
}
|
||||
i += auth.length();
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw newNamingException(iae);
|
||||
}
|
||||
}
|
||||
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseCompat() throws NamingException {
|
||||
assert url.startsWith("rmi:");
|
||||
|
||||
int i = 4; // index into url, following the "rmi:"
|
||||
boolean hasAuthority = url.startsWith("//", i);
|
||||
if (hasAuthority) i += 2; // skip past "//"
|
||||
int slash = url.indexOf('/', i);
|
||||
int qmark = url.indexOf('?', i);
|
||||
int fmark = url.indexOf('#', i);
|
||||
if (fmark > -1 && qmark > fmark) qmark = -1;
|
||||
if (fmark > -1 && slash > fmark) slash = -1;
|
||||
if (qmark > -1 && slash > qmark) slash = -1;
|
||||
|
||||
// The end of the authority component is either the
|
||||
// slash (slash will be -1 if it doesn't come before
|
||||
// query or fragment), or the question mark (qmark will
|
||||
// be -1 if it doesn't come before the fragment), or
|
||||
// the fragment separator, or the end of the URI
|
||||
// string if there is no path, no query, and no fragment.
|
||||
int enda = slash > -1 ? slash
|
||||
: (qmark > -1 ? qmark
|
||||
: (fmark > -1 ? fmark
|
||||
: url.length()));
|
||||
if (fmark > -1) {
|
||||
if (!acceptsFragment()) {
|
||||
throw newNamingException(new IllegalArgumentException("URI fragments not supported: " + url));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAuthority && enda > i) { // parse "//host:port"
|
||||
if (url.startsWith(":", i)) {
|
||||
// LdapURL supports empty host.
|
||||
i++;
|
||||
host = "";
|
||||
if (enda > i) {
|
||||
port = Integer.parseInt(url.substring(i, enda));
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
URI uri = URI.create(url.substring(0, enda));
|
||||
host = uri.getHost();
|
||||
port = uri.getPort();
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1 ? "" : ":" + port);
|
||||
if (!hostport.equals(uri.getRawAuthority())) {
|
||||
throw newNamingException(new IllegalArgumentException("invalid authority: "
|
||||
+ uri.getRawAuthority()));
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw newNamingException(iae);
|
||||
}
|
||||
}
|
||||
i = enda;
|
||||
}
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The legacy parsing used to only throw IllegalArgumentException
|
||||
// and continues to do so
|
||||
private void parseLegacy() {
|
||||
assert url.startsWith("rmi:");
|
||||
|
||||
// Parse the URL.
|
||||
int i = 4; // index into url, following the "rmi:"
|
||||
|
||||
if (url.startsWith("//", i)) { // parse "//host:port"
|
||||
i += 2; // skip past "//"
|
||||
int slash = url.indexOf('/', i);
|
||||
if (slash < 0) {
|
||||
slash = url.length();
|
||||
}
|
||||
if (url.startsWith("[", i)) { // at IPv6 literal
|
||||
int brac = url.indexOf(']', i + 1);
|
||||
if (brac < 0 || brac > slash) {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
host = url.substring(i, brac + 1); // include brackets
|
||||
i = brac + 1; // skip past "[...]"
|
||||
} else { // at host name or IPv4
|
||||
int colon = url.indexOf(':', i);
|
||||
int hostEnd = (colon < 0 || colon > slash)
|
||||
? slash
|
||||
: colon;
|
||||
if (i < hostEnd) {
|
||||
host = url.substring(i, hostEnd);
|
||||
}
|
||||
i = hostEnd; // skip past host
|
||||
}
|
||||
if ((i + 1 < slash)) {
|
||||
if ( url.startsWith(":", i)) { // parse port
|
||||
i++; // skip past ":"
|
||||
port = Integer.parseInt(url.substring(i, slash));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
}
|
||||
i = slash;
|
||||
}
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
}
|
||||
|
||||
NamingException newNamingException(Throwable cause) {
|
||||
NamingException ne = new NamingException(cause.getMessage());
|
||||
ne.initCause(cause);
|
||||
return ne;
|
||||
}
|
||||
|
||||
boolean acceptsFragment() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the registry portion of "url" to the corresponding
|
||||
* RMI registry, and returns the atomic object name as the
|
||||
@ -59,63 +298,11 @@ public class rmiURLContext extends GenericURLContext {
|
||||
protected ResolveResult getRootURLContext(String url, Hashtable<?,?> env)
|
||||
throws NamingException
|
||||
{
|
||||
if (!url.startsWith("rmi:")) {
|
||||
throw (new IllegalArgumentException(
|
||||
"rmiURLContext: name is not an RMI URL: " + url));
|
||||
}
|
||||
|
||||
// Parse the URL.
|
||||
|
||||
String host = null;
|
||||
int port = -1;
|
||||
String objName = null;
|
||||
|
||||
int i = 4; // index into url, following the "rmi:"
|
||||
|
||||
if (url.startsWith("//", i)) { // parse "//host:port"
|
||||
i += 2; // skip past "//"
|
||||
int slash = url.indexOf('/', i);
|
||||
if (slash < 0) {
|
||||
slash = url.length();
|
||||
}
|
||||
if (url.startsWith("[", i)) { // at IPv6 literal
|
||||
int brac = url.indexOf(']', i + 1);
|
||||
if (brac < 0 || brac > slash) {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
host = url.substring(i, brac + 1); // include brackets
|
||||
i = brac + 1; // skip past "[...]"
|
||||
} else { // at host name or IPv4
|
||||
int colon = url.indexOf(':', i);
|
||||
int hostEnd = (colon < 0 || colon > slash)
|
||||
? slash
|
||||
: colon;
|
||||
if (i < hostEnd) {
|
||||
host = url.substring(i, hostEnd);
|
||||
}
|
||||
i = hostEnd; // skip past host
|
||||
}
|
||||
if ((i + 1 < slash)) {
|
||||
if ( url.startsWith(":", i)) { // parse port
|
||||
i++; // skip past ":"
|
||||
port = Integer.parseInt(url.substring(i, slash));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
}
|
||||
i = slash;
|
||||
}
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
Parser parser = new Parser(url);
|
||||
parser.parse();
|
||||
String host = parser.host;
|
||||
int port = parser.port;
|
||||
String objName = parser.objName;
|
||||
|
||||
// Represent object name as empty or single-component composite name.
|
||||
CompositeName remaining = new CompositeName();
|
||||
|
Loading…
x
Reference in New Issue
Block a user