mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 12:07:51 +02:00
154 lines
5.5 KiB
Java
154 lines
5.5 KiB
Java
// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package google.registry.rdap;
|
|
|
|
import google.registry.request.HttpException.UnprocessableEntityException;
|
|
import javax.annotation.Nullable;
|
|
|
|
/**
|
|
* Object containing the results of parsing an RDAP partial match search pattern. Search patterns
|
|
* are of the form XXXX, XXXX* or XXXX*.YYYY. There can be at most one wildcard character, and it
|
|
* must be at the end, except for a possible suffix string on the end to restrict the search to a
|
|
* particular TLD (for domains) or domain (for nameservers).
|
|
*
|
|
* @see <a href="http://www.ietf.org/rfc/rfc7482.txt">
|
|
* RFC 7482: Registration Data Access Protocol (RDAP) Query Format</a>
|
|
*/
|
|
public final class RdapSearchPattern {
|
|
|
|
/** String before the wildcard character. */
|
|
private final String initialString;
|
|
|
|
/** If false, initialString contains the entire search string. */
|
|
private final boolean hasWildcard;
|
|
|
|
/**
|
|
* Terminating suffix after the wildcard, or null if none was specified; for domains, it should be
|
|
* a TLD, for nameservers, a domain. RFC 7482 requires only that it be a sequence of domain
|
|
* labels, but this definition is stricter for efficiency purposes.
|
|
*/
|
|
@Nullable
|
|
private final String suffix;
|
|
|
|
private RdapSearchPattern(
|
|
final String initialString, final boolean hasWildcard, @Nullable final String suffix) {
|
|
this.initialString = initialString;
|
|
this.hasWildcard = hasWildcard;
|
|
this.suffix = suffix;
|
|
}
|
|
|
|
public String getInitialString() {
|
|
return initialString;
|
|
}
|
|
|
|
public boolean getHasWildcard() {
|
|
return hasWildcard;
|
|
}
|
|
|
|
@Nullable
|
|
public String getSuffix() {
|
|
return suffix;
|
|
}
|
|
|
|
/**
|
|
* Attempts to return the next string in sort order after {@link #getInitialString()
|
|
* initialString}. This can be used to convert a wildcard query into a range query, by looking for
|
|
* strings greater than or equal to {@link #getInitialString() initialString} and less than {@link
|
|
* #getNextInitialString() nextInitialString}.
|
|
*/
|
|
public String getNextInitialString() {
|
|
return initialString.substring(0, initialString.length() - 1)
|
|
+ (char) (initialString.charAt(initialString.length() - 1) + 1);
|
|
}
|
|
|
|
/**
|
|
* Creates a SearchPattern using the provided search pattern string.
|
|
*
|
|
* @param pattern the string containing the partial match pattern
|
|
* @param allowSuffix true if a suffix is allowed after the wildcard
|
|
*
|
|
* @throws UnprocessableEntityException if {@code pattern} does not meet the requirements of RFC
|
|
* 7482
|
|
*/
|
|
public static RdapSearchPattern create(
|
|
String pattern, boolean allowSuffix) throws UnprocessableEntityException {
|
|
String initialString;
|
|
boolean hasWildcard;
|
|
String suffix;
|
|
// If there's no wildcard character, just lump everything into the initial string.
|
|
int wildcardPos = pattern.indexOf('*');
|
|
if (wildcardPos < 0) {
|
|
initialString = pattern;
|
|
hasWildcard = false;
|
|
suffix = null;
|
|
} else if (pattern.indexOf('*', wildcardPos + 1) >= 0) {
|
|
throw new UnprocessableEntityException("Only one wildcard allowed");
|
|
} else {
|
|
hasWildcard = true;
|
|
// Check for a suffix (e.g. exam*.com or ns*.example.com).
|
|
if (pattern.length() > wildcardPos + 1) {
|
|
if (!allowSuffix) {
|
|
throw new UnprocessableEntityException("Suffix not allowed after wildcard");
|
|
}
|
|
if ((pattern.length() == wildcardPos + 2) || (pattern.charAt(wildcardPos + 1) != '.')) {
|
|
throw new UnprocessableEntityException(
|
|
"Suffix after wildcard must be one or more domain"
|
|
+ " name labels, e.g. exam*.tld, ns*.example.tld");
|
|
}
|
|
suffix = pattern.substring(wildcardPos + 2);
|
|
} else {
|
|
suffix = null;
|
|
}
|
|
initialString = pattern.substring(0, wildcardPos);
|
|
}
|
|
if (initialString.length() < 2) {
|
|
throw new UnprocessableEntityException("At least two characters must be specified");
|
|
}
|
|
if (initialString.startsWith("xn--")
|
|
&& (initialString.length() < 7)) {
|
|
throw new UnprocessableEntityException(
|
|
"At least seven characters must be specified for punycode domain searches");
|
|
}
|
|
return new RdapSearchPattern(initialString, hasWildcard, suffix);
|
|
}
|
|
|
|
/**
|
|
* Checks a string to make sure that it matches the search pattern.
|
|
*
|
|
* @param string the string to be matched
|
|
* @return true if the pattern matches the string
|
|
*/
|
|
public boolean matches(@Nullable String string) {
|
|
if (string == null) {
|
|
return false;
|
|
}
|
|
int lengthAccountedFor = 0;
|
|
if (initialString != null) {
|
|
if (!string.startsWith(initialString)) {
|
|
return false;
|
|
}
|
|
lengthAccountedFor += initialString.length();
|
|
}
|
|
if (suffix != null) {
|
|
if (!string.endsWith(suffix)) {
|
|
return false;
|
|
}
|
|
lengthAccountedFor += suffix.length();
|
|
}
|
|
return hasWildcard
|
|
? (lengthAccountedFor <= string.length())
|
|
: (lengthAccountedFor == string.length());
|
|
}
|
|
}
|