From 99b1e93cc4516c4c39bff2e9ed66b700600d4931 Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Wed, 6 Apr 2022 18:35:38 -0400 Subject: [PATCH] Canonicalize domain/host names in nomulus tool commands (#1583) * Canonicalize domain/host names in nomulus tool commands This helps prevent some common user errors. --- .../registry/beam/initsql/Transforms.java | 6 +-- .../google/registry/flows/CheckApiAction.java | 4 +- .../registry/model/domain/DomainBase.java | 2 +- .../registry/model/domain/DomainContent.java | 4 +- .../registry/model/domain/DomainHistory.java | 2 +- .../google/registry/model/host/HostBase.java | 4 +- .../registry/model/host/HostHistory.java | 2 +- .../registry/model/host/HostResource.java | 2 +- .../model/tld/label/DomainLabelEntry.java | 4 +- .../google/registry/rdap/RdapActionBase.java | 4 +- .../java/google/registry/rde/RdeReporter.java | 4 +- .../tools/CanonicalizeLabelsCommand.java | 4 +- .../registry/tools/ConvertIdnCommand.java | 4 +- .../registry/tools/CreateHostCommand.java | 3 +- .../tools/CreateOrUpdateRegistrarCommand.java | 6 +-- .../tools/CreateOrUpdateTldCommand.java | 6 +-- .../registry/tools/DeleteHostCommand.java | 11 ++++-- .../tools/EncryptEscrowDepositCommand.java | 4 +- .../tools/EnqueuePollMessageCommand.java | 2 + .../google/registry/tools/EppToolCommand.java | 6 ++- .../GenerateAllocationTokensCommand.java | 15 +++++--- .../registry/tools/GetDomainCommand.java | 6 ++- .../google/registry/tools/GetHostCommand.java | 7 +++- .../registry/tools/LockDomainCommand.java | 4 +- .../tools/UniformRapidSuspensionCommand.java | 7 +++- .../registry/tools/UnlockDomainCommand.java | 5 ++- .../params/InternetDomainNameParameter.java | 3 +- .../ui/server/RegistrarFormFields.java | 4 +- .../google/registry/whois/WhoisReader.java | 10 ++--- .../tools/CheckDomainClaimsCommandTest.java | 6 +++ .../registry/tools/CreateHostCommandTest.java | 2 +- .../tools/EnqueuePollMessageCommandTest.java | 3 +- .../GenerateAllocationTokensCommandTest.java | 12 +++--- .../registry/tools/GetDomainCommandTest.java | 9 +++++ .../google/registry/util/DomainNameUtils.java | 4 +- .../registry/util/DomainNameUtilsTest.java | 37 ++++++++++--------- 36 files changed, 132 insertions(+), 86 deletions(-) diff --git a/core/src/main/java/google/registry/beam/initsql/Transforms.java b/core/src/main/java/google/registry/beam/initsql/Transforms.java index 414bdd597..fa969592b 100644 --- a/core/src/main/java/google/registry/beam/initsql/Transforms.java +++ b/core/src/main/java/google/registry/beam/initsql/Transforms.java @@ -22,7 +22,7 @@ import static google.registry.beam.initsql.BackupPaths.getExportFilePatterns; import static google.registry.model.ofy.ObjectifyService.auditedOfy; import static google.registry.util.DateTimeUtils.START_OF_TIME; import static google.registry.util.DateTimeUtils.isBeforeOrAt; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static java.util.Comparator.comparing; import static org.apache.beam.sdk.values.TypeDescriptors.kvs; import static org.apache.beam.sdk.values.TypeDescriptors.strings; @@ -343,11 +343,11 @@ public final class Transforms { // Canonicalize old domain/host names from 2016 and earlier before we were enforcing this. entity.setIndexedProperty( "fullyQualifiedDomainName", - canonicalizeDomainName((String) entity.getProperty("fullyQualifiedDomainName"))); + canonicalizeHostname((String) entity.getProperty("fullyQualifiedDomainName"))); } else if (entity.getKind().equals("HostResource")) { entity.setIndexedProperty( "fullyQualifiedHostName", - canonicalizeDomainName((String) entity.getProperty("fullyQualifiedHostName"))); + canonicalizeHostname((String) entity.getProperty("fullyQualifiedHostName"))); } return entity; } diff --git a/core/src/main/java/google/registry/flows/CheckApiAction.java b/core/src/main/java/google/registry/flows/CheckApiAction.java index 2dab9049c..6d255a604 100644 --- a/core/src/main/java/google/registry/flows/CheckApiAction.java +++ b/core/src/main/java/google/registry/flows/CheckApiAction.java @@ -31,7 +31,7 @@ import static google.registry.monitoring.whitebox.CheckApiMetric.Status.UNKNOWN_ import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.PREMIUM; import static google.registry.monitoring.whitebox.CheckApiMetric.Tier.STANDARD; import static google.registry.pricing.PricingEngineProxy.isDomainPremium; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static org.json.simple.JSONValue.toJSONString; import com.google.common.collect.ImmutableList; @@ -105,7 +105,7 @@ public class CheckApiAction implements Runnable { String domainString; InternetDomainName domainName; try { - domainString = canonicalizeDomainName(nullToEmpty(domain)); + domainString = canonicalizeHostname(nullToEmpty(domain)); domainName = validateDomainName(domainString); } catch (IllegalArgumentException | EppException e) { metricBuilder.status(INVALID_NAME); diff --git a/core/src/main/java/google/registry/model/domain/DomainBase.java b/core/src/main/java/google/registry/model/domain/DomainBase.java index 79cb89b80..8f20e7335 100644 --- a/core/src/main/java/google/registry/model/domain/DomainBase.java +++ b/core/src/main/java/google/registry/model/domain/DomainBase.java @@ -175,7 +175,7 @@ public class DomainBase extends DomainContent @Override public void beforeSqlSaveOnReplay() { - fullyQualifiedDomainName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedDomainName); + fullyQualifiedDomainName = DomainNameUtils.canonicalizeHostname(fullyQualifiedDomainName); dsData = dsData.stream() .filter(datum -> datum.getDigest() != null && datum.getDigest().length > 0) diff --git a/core/src/main/java/google/registry/model/domain/DomainContent.java b/core/src/main/java/google/registry/model/domain/DomainContent.java index e40d54046..3ef82f18a 100644 --- a/core/src/main/java/google/registry/model/domain/DomainContent.java +++ b/core/src/main/java/google/registry/model/domain/DomainContent.java @@ -34,7 +34,7 @@ import static google.registry.util.DateTimeUtils.END_OF_TIME; import static google.registry.util.DateTimeUtils.earliestOf; import static google.registry.util.DateTimeUtils.isBeforeOrAt; import static google.registry.util.DateTimeUtils.leapSafeAddYears; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.DomainNameUtils.getTldFromDomainName; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; @@ -890,7 +890,7 @@ public class DomainContent extends EppResource public B setDomainName(String domainName) { checkArgument( - domainName.equals(canonicalizeDomainName(domainName)), + domainName.equals(canonicalizeHostname(domainName)), "Domain name %s not in puny-coded, lower-case form", domainName); getInstance().fullyQualifiedDomainName = domainName; diff --git a/core/src/main/java/google/registry/model/domain/DomainHistory.java b/core/src/main/java/google/registry/model/domain/DomainHistory.java index ad7b2e02f..aacf4c8ea 100644 --- a/core/src/main/java/google/registry/model/domain/DomainHistory.java +++ b/core/src/main/java/google/registry/model/domain/DomainHistory.java @@ -309,7 +309,7 @@ public class DomainHistory extends HistoryEntry implements SqlEntity { if (domainContent == null) { domainContent = jpaTm().getEntityManager().find(DomainBase.class, getDomainRepoId()); domainContent.fullyQualifiedDomainName = - DomainNameUtils.canonicalizeDomainName(domainContent.fullyQualifiedDomainName); + DomainNameUtils.canonicalizeHostname(domainContent.fullyQualifiedDomainName); fillAuxiliaryFieldsFromDomain(this); } } diff --git a/core/src/main/java/google/registry/model/host/HostBase.java b/core/src/main/java/google/registry/model/host/HostBase.java index d607b88cb..f1826c4b5 100644 --- a/core/src/main/java/google/registry/model/host/HostBase.java +++ b/core/src/main/java/google/registry/model/host/HostBase.java @@ -19,7 +19,7 @@ import static com.google.common.collect.Sets.difference; import static com.google.common.collect.Sets.union; import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy; import static google.registry.util.DateTimeUtils.START_OF_TIME; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import com.google.common.collect.ImmutableSet; import com.googlecode.objectify.annotation.IgnoreSave; @@ -195,7 +195,7 @@ public class HostBase extends EppResource { public B setHostName(String hostName) { checkArgument( - hostName.equals(canonicalizeDomainName(hostName)), + hostName.equals(canonicalizeHostname(hostName)), "Host name %s not in puny-coded, lower-case form", hostName); getInstance().fullyQualifiedHostName = hostName; diff --git a/core/src/main/java/google/registry/model/host/HostHistory.java b/core/src/main/java/google/registry/model/host/HostHistory.java index 2f54dc24a..2e6577b1a 100644 --- a/core/src/main/java/google/registry/model/host/HostHistory.java +++ b/core/src/main/java/google/registry/model/host/HostHistory.java @@ -143,7 +143,7 @@ public class HostHistory extends HistoryEntry implements SqlEntity, UnsafeSerial if (hostBase == null) { hostBase = jpaTm().getEntityManager().find(HostResource.class, getHostRepoId()); hostBase.fullyQualifiedHostName = - DomainNameUtils.canonicalizeDomainName(hostBase.fullyQualifiedHostName); + DomainNameUtils.canonicalizeHostname(hostBase.fullyQualifiedHostName); } } diff --git a/core/src/main/java/google/registry/model/host/HostResource.java b/core/src/main/java/google/registry/model/host/HostResource.java index a6dbee862..a83682f35 100644 --- a/core/src/main/java/google/registry/model/host/HostResource.java +++ b/core/src/main/java/google/registry/model/host/HostResource.java @@ -73,7 +73,7 @@ public class HostResource extends HostBase @Override public void beforeSqlSaveOnReplay() { - fullyQualifiedHostName = DomainNameUtils.canonicalizeDomainName(fullyQualifiedHostName); + fullyQualifiedHostName = DomainNameUtils.canonicalizeHostname(fullyQualifiedHostName); } @Override diff --git a/core/src/main/java/google/registry/model/tld/label/DomainLabelEntry.java b/core/src/main/java/google/registry/model/tld/label/DomainLabelEntry.java index 58dd2fe5b..2fef50d48 100644 --- a/core/src/main/java/google/registry/model/tld/label/DomainLabelEntry.java +++ b/core/src/main/java/google/registry/model/tld/label/DomainLabelEntry.java @@ -16,7 +16,7 @@ package google.registry.model.tld.label; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.emptyToNull; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; import com.google.common.net.InternetDomainName; @@ -77,7 +77,7 @@ public abstract class DomainLabelEntry, D extends Domain public T build() { checkArgumentNotNull(emptyToNull(getInstance().domainLabel), "Label must be specified"); checkArgument( - getInstance().domainLabel.equals(canonicalizeDomainName(getInstance().domainLabel)), + getInstance().domainLabel.equals(canonicalizeHostname(getInstance().domainLabel)), "Label '%s' must be in puny-coded, lower-case form", getInstance().domainLabel); checkArgumentNotNull(getInstance().getValue(), "Value must be specified"); diff --git a/core/src/main/java/google/registry/rdap/RdapActionBase.java b/core/src/main/java/google/registry/rdap/RdapActionBase.java index 6f041283f..6ce71cdea 100644 --- a/core/src/main/java/google/registry/rdap/RdapActionBase.java +++ b/core/src/main/java/google/registry/rdap/RdapActionBase.java @@ -18,7 +18,7 @@ import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.net.HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN; import static google.registry.request.Actions.getPathForAction; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; import static javax.servlet.http.HttpServletResponse.SC_OK; @@ -245,7 +245,7 @@ public abstract class RdapActionBase implements Runnable { } String canonicalizeName(String name) { - name = canonicalizeDomainName(name); + name = canonicalizeHostname(name); if (name.endsWith(".")) { name = name.substring(0, name.length() - 1); } diff --git a/core/src/main/java/google/registry/rde/RdeReporter.java b/core/src/main/java/google/registry/rde/RdeReporter.java index 7fc5d647c..b74868000 100644 --- a/core/src/main/java/google/registry/rde/RdeReporter.java +++ b/core/src/main/java/google/registry/rde/RdeReporter.java @@ -19,7 +19,7 @@ import static com.google.appengine.api.urlfetch.HTTPMethod.PUT; import static com.google.common.io.BaseEncoding.base64; import static com.google.common.net.HttpHeaders.AUTHORIZATION; import static com.google.common.net.HttpHeaders.CONTENT_TYPE; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static java.nio.charset.StandardCharsets.UTF_8; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_OK; @@ -123,7 +123,7 @@ public class RdeReporter { private URL makeReportUrl(String tld, String id) { try { - return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeDomainName(tld), id)); + return new URL(String.format("%s/%s/%s", reportUrlPrefix, canonicalizeHostname(tld), id)); } catch (MalformedURLException e) { throw new RuntimeException(e); } diff --git a/core/src/main/java/google/registry/tools/CanonicalizeLabelsCommand.java b/core/src/main/java/google/registry/tools/CanonicalizeLabelsCommand.java index 87637df34..fad700ab6 100644 --- a/core/src/main/java/google/registry/tools/CanonicalizeLabelsCommand.java +++ b/core/src/main/java/google/registry/tools/CanonicalizeLabelsCommand.java @@ -14,7 +14,7 @@ package google.registry.tools; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static java.nio.charset.StandardCharsets.UTF_8; import com.beust.jcommander.Parameter; @@ -80,7 +80,7 @@ final class CanonicalizeLabelsCommand implements Command { private String canonicalize(String rawLabel) { try { - return canonicalizeDomainName(rawLabel.replaceAll(" ", "")); + return canonicalizeHostname(rawLabel.replaceAll(" ", "")); } catch (Exception e) { System.err.printf("Error canonicalizing %s: %s\n", rawLabel, e.getMessage()); return ""; diff --git a/core/src/main/java/google/registry/tools/ConvertIdnCommand.java b/core/src/main/java/google/registry/tools/ConvertIdnCommand.java index 6de9adefb..e13e036df 100644 --- a/core/src/main/java/google/registry/tools/ConvertIdnCommand.java +++ b/core/src/main/java/google/registry/tools/ConvertIdnCommand.java @@ -15,7 +15,7 @@ package google.registry.tools; import static google.registry.util.DomainNameUtils.ACE_PREFIX; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -38,7 +38,7 @@ final class ConvertIdnCommand implements Command { if (label.startsWith(ACE_PREFIX)) { System.out.println(Idn.toUnicode(Ascii.toLowerCase(label))); } else { - System.out.println(canonicalizeDomainName(label)); + System.out.println(canonicalizeHostname(label)); } } } diff --git a/core/src/main/java/google/registry/tools/CreateHostCommand.java b/core/src/main/java/google/registry/tools/CreateHostCommand.java index 264e9ac9b..55b129b8f 100644 --- a/core/src/main/java/google/registry/tools/CreateHostCommand.java +++ b/core/src/main/java/google/registry/tools/CreateHostCommand.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.net.InetAddresses; import com.google.template.soy.data.SoyMapData; import google.registry.tools.soy.HostCreateSoyInfo; +import google.registry.util.DomainNameUtils; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -68,7 +69,7 @@ final class CreateHostCommand extends MutatingEppToolCommand { addSoyRecord( clientId, new SoyMapData( - "hostname", hostName, + "hostname", DomainNameUtils.canonicalizeHostname(hostName), "ipv4addresses", ipv4Addresses.build(), "ipv6addresses", ipv6Addresses.build())); } diff --git a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java index 9a2f617fa..65863fbce 100644 --- a/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java +++ b/core/src/main/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Predicates.isNull; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.RegistrarUtils.normalizeRegistrarName; import static java.nio.charset.StandardCharsets.US_ASCII; import static org.joda.time.DateTimeZone.UTC; @@ -319,7 +319,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand { addAllowedTlds.isEmpty(), "Can't specify both --allowedTlds and --addAllowedTlds"); ImmutableSet.Builder allowedTldsBuilder = new ImmutableSet.Builder<>(); for (String allowedTld : allowedTlds) { - allowedTldsBuilder.add(canonicalizeDomainName(allowedTld)); + allowedTldsBuilder.add(canonicalizeHostname(allowedTld)); } builder.setAllowedTlds(allowedTldsBuilder.build()); } @@ -329,7 +329,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand { allowedTldsBuilder.addAll(oldRegistrar.getAllowedTlds()); } for (String allowedTld : addAllowedTlds) { - allowedTldsBuilder.add(canonicalizeDomainName(allowedTld)); + allowedTldsBuilder.add(canonicalizeHostname(allowedTld)); } builder.setAllowedTlds(allowedTldsBuilder.build()); } diff --git a/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java b/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java index 4484aca5d..b94e8a506 100644 --- a/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java +++ b/core/src/main/java/google/registry/tools/CreateOrUpdateTldCommand.java @@ -16,7 +16,7 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; import static google.registry.util.CollectionUtils.findDuplicates; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import com.beust.jcommander.Parameter; import com.google.common.base.Joiner; @@ -263,10 +263,10 @@ abstract class CreateOrUpdateTldCommand extends MutatingCommand { "Can't update roid suffixes on multiple TLDs simultaneously"); for (String tld : tlds) { checkArgument( - tld.equals(canonicalizeDomainName(tld)), + tld.equals(canonicalizeHostname(tld)), "TLD '%s' should be given in the canonical form '%s'", tld, - canonicalizeDomainName(tld)); + canonicalizeHostname(tld)); checkArgument( !Character.isDigit(tld.charAt(0)), "TLDs cannot begin with a number"); diff --git a/core/src/main/java/google/registry/tools/DeleteHostCommand.java b/core/src/main/java/google/registry/tools/DeleteHostCommand.java index 4e521bc77..129e68318 100644 --- a/core/src/main/java/google/registry/tools/DeleteHostCommand.java +++ b/core/src/main/java/google/registry/tools/DeleteHostCommand.java @@ -18,6 +18,7 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import com.google.template.soy.data.SoyMapData; import google.registry.tools.soy.HostDeleteSoyInfo; +import google.registry.util.DomainNameUtils; /** A command to delete a host via EPP. */ @Parameters(separators = " =", commandDescription = "Delete host") @@ -50,9 +51,11 @@ final class DeleteHostCommand extends MutatingEppToolCommand { @Override protected void initMutatingEppToolCommand() { setSoyTemplate(HostDeleteSoyInfo.getInstance(), HostDeleteSoyInfo.DELETEHOST); - addSoyRecord(clientId, new SoyMapData( - "hostName", hostName, - "reason", reason, - "requestedByRegistrar", requestedByRegistrar)); + addSoyRecord( + clientId, + new SoyMapData( + "hostName", DomainNameUtils.canonicalizeHostname(hostName), + "reason", reason, + "requestedByRegistrar", requestedByRegistrar)); } } diff --git a/core/src/main/java/google/registry/tools/EncryptEscrowDepositCommand.java b/core/src/main/java/google/registry/tools/EncryptEscrowDepositCommand.java index 38eee3a30..73af5efd8 100644 --- a/core/src/main/java/google/registry/tools/EncryptEscrowDepositCommand.java +++ b/core/src/main/java/google/registry/tools/EncryptEscrowDepositCommand.java @@ -14,7 +14,7 @@ package google.registry.tools; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; @@ -61,6 +61,6 @@ class EncryptEscrowDepositCommand implements CommandWithRemoteApi { @Override public final void run() throws Exception { - encryptor.encrypt(mode, canonicalizeDomainName(tld), revision, input, outdir); + encryptor.encrypt(mode, canonicalizeHostname(tld), revision, input, outdir); } } diff --git a/core/src/main/java/google/registry/tools/EnqueuePollMessageCommand.java b/core/src/main/java/google/registry/tools/EnqueuePollMessageCommand.java index f92139838..907cdd3c6 100644 --- a/core/src/main/java/google/registry/tools/EnqueuePollMessageCommand.java +++ b/core/src/main/java/google/registry/tools/EnqueuePollMessageCommand.java @@ -30,6 +30,7 @@ import google.registry.model.domain.DomainHistory; import google.registry.model.poll.PollMessage; import google.registry.model.registrar.Registrar; import google.registry.model.reporting.HistoryEntry; +import google.registry.util.DomainNameUtils; import java.util.List; import java.util.Optional; import javax.inject.Inject; @@ -79,6 +80,7 @@ class EnqueuePollMessageCommand extends MutatingCommand { @Override protected final void init() { + domainName = DomainNameUtils.canonicalizeHostname(domainName); checkArgument( !sendToAll || isNullOrEmpty(clientIds), "Cannot specify both --all and --clients"); tm().transact( diff --git a/core/src/main/java/google/registry/tools/EppToolCommand.java b/core/src/main/java/google/registry/tools/EppToolCommand.java index 388df64c4..078c63831 100644 --- a/core/src/main/java/google/registry/tools/EppToolCommand.java +++ b/core/src/main/java/google/registry/tools/EppToolCommand.java @@ -20,6 +20,7 @@ import static com.google.common.collect.Maps.filterValues; import static com.google.common.io.Resources.getResource; import static google.registry.model.tld.Registries.findTldForNameOrThrow; import static google.registry.tools.CommandUtilities.addHeader; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.PreconditionsUtils.checkArgumentPresent; import static google.registry.xml.XmlTransformer.prettyPrint; import static java.nio.charset.StandardCharsets.UTF_8; @@ -83,8 +84,9 @@ abstract class EppToolCommand extends ConfirmingCommand protected static Multimap validateAndGroupDomainNamesByTld(List names) { ImmutableMultimap.Builder builder = new ImmutableMultimap.Builder<>(); for (String name : names) { - InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(name)); - builder.put(tld.toString(), name); + String canonicalDomain = canonicalizeHostname(name); + InternetDomainName tld = findTldForNameOrThrow(InternetDomainName.from(canonicalDomain)); + builder.put(tld.toString(), canonicalDomain); } return builder.build(); } diff --git a/core/src/main/java/google/registry/tools/GenerateAllocationTokensCommand.java b/core/src/main/java/google/registry/tools/GenerateAllocationTokensCommand.java index 0a1e6acab..16137fddc 100644 --- a/core/src/main/java/google/registry/tools/GenerateAllocationTokensCommand.java +++ b/core/src/main/java/google/registry/tools/GenerateAllocationTokensCommand.java @@ -16,7 +16,6 @@ package google.registry.tools; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static com.google.common.collect.Queues.newArrayDeque; import static com.google.common.collect.Sets.difference; import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE; import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE; @@ -44,15 +43,18 @@ import google.registry.model.domain.token.AllocationToken.TokenType; import google.registry.persistence.VKey; import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions; import google.registry.util.CollectionUtils; +import google.registry.util.DomainNameUtils; import google.registry.util.NonFinalForTesting; import google.registry.util.Retrier; import google.registry.util.StringGenerator; import java.io.File; import java.io.IOException; +import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Named; @@ -164,11 +166,12 @@ class GenerateAllocationTokensCommand implements CommandWithRemoteApi { domainNames = null; } else { domainNames = - newArrayDeque( - Splitter.on('\n') - .omitEmptyStrings() - .trimResults() - .split(Files.asCharSource(new File(domainNamesFile), UTF_8).read())); + Splitter.on('\n') + .omitEmptyStrings() + .trimResults() + .splitToStream(Files.asCharSource(new File(domainNamesFile), UTF_8).read()) + .map(DomainNameUtils::canonicalizeHostname) + .collect(Collectors.toCollection(ArrayDeque::new)); numTokens = domainNames.size(); } diff --git a/core/src/main/java/google/registry/tools/GetDomainCommand.java b/core/src/main/java/google/registry/tools/GetDomainCommand.java index 5802c6dda..e442cc215 100644 --- a/core/src/main/java/google/registry/tools/GetDomainCommand.java +++ b/core/src/main/java/google/registry/tools/GetDomainCommand.java @@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import google.registry.model.domain.DomainBase; +import google.registry.util.DomainNameUtils; import java.util.List; /** Command to show a domain resource. */ @@ -33,8 +34,11 @@ final class GetDomainCommand extends GetEppResourceCommand { @Override public void runAndPrint() { for (String domainName : mainParameters) { + String canonicalDomain = DomainNameUtils.canonicalizeHostname(domainName); printResource( - "Domain", domainName, loadByForeignKey(DomainBase.class, domainName, readTimestamp)); + "Domain", + canonicalDomain, + loadByForeignKey(DomainBase.class, canonicalDomain, readTimestamp)); } } } diff --git a/core/src/main/java/google/registry/tools/GetHostCommand.java b/core/src/main/java/google/registry/tools/GetHostCommand.java index dc1f8a195..5776c9299 100644 --- a/core/src/main/java/google/registry/tools/GetHostCommand.java +++ b/core/src/main/java/google/registry/tools/GetHostCommand.java @@ -19,6 +19,7 @@ import static google.registry.model.EppResourceUtils.loadByForeignKey; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; import google.registry.model.host.HostResource; +import google.registry.util.DomainNameUtils; import java.util.List; /** Command to show one or more host resources. */ @@ -32,7 +33,9 @@ final class GetHostCommand extends GetEppResourceCommand { @Override public void runAndPrint() { - mainParameters.forEach( - h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp))); + mainParameters.stream() + .map(DomainNameUtils::canonicalizeHostname) + .forEach( + h -> printResource("Host", h, loadByForeignKey(HostResource.class, h, readTimestamp))); } } diff --git a/core/src/main/java/google/registry/tools/LockDomainCommand.java b/core/src/main/java/google/registry/tools/LockDomainCommand.java index 4382cce63..f9edfaf8b 100644 --- a/core/src/main/java/google/registry/tools/LockDomainCommand.java +++ b/core/src/main/java/google/registry/tools/LockDomainCommand.java @@ -14,6 +14,8 @@ package google.registry.tools; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; + import com.beust.jcommander.Parameters; /** @@ -26,6 +28,6 @@ public class LockDomainCommand extends LockOrUnlockDomainCommand { @Override protected void createAndApplyRequest(String domain) { - domainLockUtils.administrativelyApplyLock(domain, clientId, null, true); + domainLockUtils.administrativelyApplyLock(canonicalizeHostname(domain), clientId, null, true); } } diff --git a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java index 8b965b0c0..cc9f7f82c 100644 --- a/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java +++ b/core/src/main/java/google/registry/tools/UniformRapidSuspensionCommand.java @@ -38,6 +38,7 @@ import google.registry.model.eppcommon.StatusValue; import google.registry.model.host.HostResource; import google.registry.tools.soy.DomainRenewSoyInfo; import google.registry.tools.soy.UniformRapidSuspensionSoyInfo; +import google.registry.util.DomainNameUtils; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -122,12 +123,14 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand { protected void initMutatingEppToolCommand() { superuser = true; DateTime now = DateTime.now(UTC); - ImmutableSet newHostsSet = ImmutableSet.copyOf(newHosts); + ImmutableList newCanonicalHosts = + newHosts.stream().map(DomainNameUtils::canonicalizeHostname).collect(toImmutableList()); + ImmutableSet newHostsSet = ImmutableSet.copyOf(newCanonicalHosts); Optional domainOpt = loadByForeignKey(DomainBase.class, domainName, now); checkArgumentPresent(domainOpt, "Domain '%s' does not exist or is deleted", domainName); DomainBase domain = domainOpt.get(); Set missingHosts = - difference(newHostsSet, checkResourcesExist(HostResource.class, newHosts, now)); + difference(newHostsSet, checkResourcesExist(HostResource.class, newCanonicalHosts, now)); checkArgument(missingHosts.isEmpty(), "Hosts do not exist: %s", missingHosts); checkArgument( locksToPreserve.isEmpty() || undo, diff --git a/core/src/main/java/google/registry/tools/UnlockDomainCommand.java b/core/src/main/java/google/registry/tools/UnlockDomainCommand.java index be9b89704..03ccb6791 100644 --- a/core/src/main/java/google/registry/tools/UnlockDomainCommand.java +++ b/core/src/main/java/google/registry/tools/UnlockDomainCommand.java @@ -14,6 +14,8 @@ package google.registry.tools; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; + import com.beust.jcommander.Parameters; import java.util.Optional; @@ -27,6 +29,7 @@ public class UnlockDomainCommand extends LockOrUnlockDomainCommand { @Override protected void createAndApplyRequest(String domain) { - domainLockUtils.administrativelyApplyUnlock(domain, clientId, true, Optional.empty()); + domainLockUtils.administrativelyApplyUnlock( + canonicalizeHostname(domain), clientId, true, Optional.empty()); } } diff --git a/core/src/main/java/google/registry/tools/params/InternetDomainNameParameter.java b/core/src/main/java/google/registry/tools/params/InternetDomainNameParameter.java index 6544267f8..8da7fcb7a 100644 --- a/core/src/main/java/google/registry/tools/params/InternetDomainNameParameter.java +++ b/core/src/main/java/google/registry/tools/params/InternetDomainNameParameter.java @@ -15,6 +15,7 @@ package google.registry.tools.params; import com.google.common.net.InternetDomainName; +import google.registry.util.DomainNameUtils; /** InternetDomainName CLI parameter converter/validator. */ public final class InternetDomainNameParameter @@ -26,6 +27,6 @@ public final class InternetDomainNameParameter @Override public InternetDomainName convert(String value) { - return InternetDomainName.from(value); + return InternetDomainName.from(DomainNameUtils.canonicalizeHostname(value)); } } diff --git a/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java b/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java index 25476155f..c80a18609 100644 --- a/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java +++ b/core/src/main/java/google/registry/ui/server/RegistrarFormFields.java @@ -17,7 +17,7 @@ package google.registry.ui.server; import static com.google.common.collect.Range.atLeast; import static com.google.common.collect.Range.atMost; import static com.google.common.collect.Range.closed; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import com.google.common.base.Ascii; import com.google.common.base.Splitter; @@ -334,7 +334,7 @@ public final class RegistrarFormFields { if (!InternetDomainName.isValid(input)) { throw new FormFieldException("Not a valid hostname."); } - return canonicalizeDomainName(input); + return canonicalizeHostname(input); } public static @Nullable DateTime parseDateTime(@Nullable String input) { diff --git a/core/src/main/java/google/registry/whois/WhoisReader.java b/core/src/main/java/google/registry/whois/WhoisReader.java index b819f9f87..095a9d72f 100644 --- a/core/src/main/java/google/registry/whois/WhoisReader.java +++ b/core/src/main/java/google/registry/whois/WhoisReader.java @@ -17,7 +17,7 @@ package google.registry.whois; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.isNullOrEmpty; import static google.registry.model.tld.Registries.findTldForName; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import com.google.common.base.Joiner; @@ -122,7 +122,7 @@ class WhoisReader { logger.atInfo().log( "Attempting domain lookup command using domain name '%s'.", tokens.get(1)); return commandFactory.domainLookup( - InternetDomainName.from(canonicalizeDomainName(tokens.get(1))), + InternetDomainName.from(canonicalizeHostname(tokens.get(1))), fullOutput, whoisRedactedEmailText); } catch (IllegalArgumentException iae) { @@ -152,8 +152,8 @@ class WhoisReader { try { logger.atInfo().log( "Attempting nameserver lookup command using %s as a hostname.", tokens.get(1)); - return commandFactory.nameserverLookupByHost(InternetDomainName.from( - canonicalizeDomainName(tokens.get(1)))); + return commandFactory.nameserverLookupByHost( + InternetDomainName.from(canonicalizeHostname(tokens.get(1)))); } catch (IllegalArgumentException iae) { // Silently ignore this exception. } @@ -187,7 +187,7 @@ class WhoisReader { // Try to parse it as a domain name or host name. try { - final InternetDomainName targetName = InternetDomainName.from(canonicalizeDomainName(arg1)); + final InternetDomainName targetName = InternetDomainName.from(canonicalizeHostname(arg1)); // We don't know at this point whether we have a domain name or a host name. We have to // search through our configured TLDs to see if there's one that prefixes the name. diff --git a/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java b/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java index 352725f66..00d680457 100644 --- a/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java +++ b/core/src/test/java/google/registry/tools/CheckDomainClaimsCommandTest.java @@ -37,6 +37,12 @@ class CheckDomainClaimsCommandTest extends EppToolCommandTestCase { createTld("tld"); runCommandForced( "--client=NewRegistrar", - "--host=example.tld", + "--host=example.TLD", "--addresses=162.100.102.99,2001:0db8:85a3:0000:0000:8a2e:0370:7334,4.5.6.7"); eppVerifier.verifySent("host_create_complete.xml"); } diff --git a/core/src/test/java/google/registry/tools/EnqueuePollMessageCommandTest.java b/core/src/test/java/google/registry/tools/EnqueuePollMessageCommandTest.java index 47e3a6c97..2de17b461 100644 --- a/core/src/test/java/google/registry/tools/EnqueuePollMessageCommandTest.java +++ b/core/src/test/java/google/registry/tools/EnqueuePollMessageCommandTest.java @@ -55,7 +55,8 @@ class EnqueuePollMessageCommandTest extends CommandTestCase { assertNotInStdout("LiveRef"); } + @Test + void testSuccess_canonicalizeDomainName() throws Exception { + createTld("xn--q9jyb4c"); + persistActiveDomain("xn--aualito-txac.xn--q9jyb4c"); + runCommand("çauçalito.みんな", "--expand"); + assertInStdout("fullyQualifiedDomainName=xn--aualito-txac.xn--q9jyb4c"); + assertInStdout("contactId=contact1234"); + } + @Test void testSuccess_multipleArguments() throws Exception { persistActiveDomain("example.tld"); diff --git a/util/src/main/java/google/registry/util/DomainNameUtils.java b/util/src/main/java/google/registry/util/DomainNameUtils.java index 846636ad8..1c2fffdbe 100644 --- a/util/src/main/java/google/registry/util/DomainNameUtils.java +++ b/util/src/main/java/google/registry/util/DomainNameUtils.java @@ -39,8 +39,8 @@ public final class DomainNameUtils { .equals(potentialParent.parts()); } - /** Canonicalizes a domain name by lowercasing and converting unicode to punycode. */ - public static String canonicalizeDomainName(String label) { + /** Canonicalizes a hostname/domain name by lowercasing and converting unicode to punycode. */ + public static String canonicalizeHostname(String label) { String labelLowercased = Ascii.toLowerCase(label); try { return Idn.toASCII(labelLowercased); diff --git a/util/src/test/java/google/registry/util/DomainNameUtilsTest.java b/util/src/test/java/google/registry/util/DomainNameUtilsTest.java index e51bab700..36e4bc252 100644 --- a/util/src/test/java/google/registry/util/DomainNameUtilsTest.java +++ b/util/src/test/java/google/registry/util/DomainNameUtilsTest.java @@ -15,7 +15,7 @@ package google.registry.util; import static com.google.common.truth.Truth.assertThat; -import static google.registry.util.DomainNameUtils.canonicalizeDomainName; +import static google.registry.util.DomainNameUtils.canonicalizeHostname; import static google.registry.util.DomainNameUtils.getSecondLevelDomain; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -25,37 +25,38 @@ import org.junit.jupiter.api.Test; class DomainNameUtilsTest { @Test - void testCanonicalizeDomainName_succeeds() { - assertThat(canonicalizeDomainName("foo")).isEqualTo("foo"); - assertThat(canonicalizeDomainName("FOO")).isEqualTo("foo"); - assertThat(canonicalizeDomainName("foo.tld")).isEqualTo("foo.tld"); - assertThat(canonicalizeDomainName("xn--q9jyb4c")).isEqualTo("xn--q9jyb4c"); - assertThat(canonicalizeDomainName("XN--Q9JYB4C")).isEqualTo("xn--q9jyb4c"); - assertThat(canonicalizeDomainName("みんな")).isEqualTo("xn--q9jyb4c"); - assertThat(canonicalizeDomainName("みんな.みんな")).isEqualTo("xn--q9jyb4c.xn--q9jyb4c"); - assertThat(canonicalizeDomainName("みんな.foo")).isEqualTo("xn--q9jyb4c.foo"); - assertThat(canonicalizeDomainName("foo.みんな")).isEqualTo("foo.xn--q9jyb4c"); - assertThat(canonicalizeDomainName("ħ")).isEqualTo("xn--1ea"); + void testCanonicalizeHostname_succeeds() { + assertThat(canonicalizeHostname("foo")).isEqualTo("foo"); + assertThat(canonicalizeHostname("FOO")).isEqualTo("foo"); + assertThat(canonicalizeHostname("foo.tld")).isEqualTo("foo.tld"); + assertThat(canonicalizeHostname("xn--q9jyb4c")).isEqualTo("xn--q9jyb4c"); + assertThat(canonicalizeHostname("XN--Q9JYB4C")).isEqualTo("xn--q9jyb4c"); + assertThat(canonicalizeHostname("みんな")).isEqualTo("xn--q9jyb4c"); + assertThat(canonicalizeHostname("みんな.みんな")).isEqualTo("xn--q9jyb4c.xn--q9jyb4c"); + assertThat(canonicalizeHostname("みんな.foo")).isEqualTo("xn--q9jyb4c.foo"); + assertThat(canonicalizeHostname("foo.みんな")).isEqualTo("foo.xn--q9jyb4c"); + assertThat(canonicalizeHostname("BAR.foo.みんな")).isEqualTo("bar.foo.xn--q9jyb4c"); + assertThat(canonicalizeHostname("ħ")).isEqualTo("xn--1ea"); } @Test - void testCanonicalizeDomainName_allowsRdnsNames() { - assertThat(canonicalizeDomainName("119.63.227.45-ns1.jhz-tt.uk")) + void testCanonicalizeHostname_allowsRdnsNames() { + assertThat(canonicalizeHostname("119.63.227.45-ns1.jhz-tt.uk")) .isEqualTo("119.63.227.45-ns1.jhz-tt.uk"); } @Test - void testCanonicalizeDomainName_throwsOn34HyphenRule() { + void testCanonicalizeHostname_throwsOn34HyphenRule() { IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> canonicalizeDomainName("119.63.227.45--ns1.jhz-tt.uk")); + () -> canonicalizeHostname("119.63.227.45--ns1.jhz-tt.uk")); assertThat(thrown).hasCauseThat().hasMessageThat().contains("HYPHEN_3_4"); } @Test - void testCanonicalizeDomainName_acePrefixUnicodeChars() { - assertThrows(IllegalArgumentException.class, () -> canonicalizeDomainName("xn--みんな")); + void testCanonicalizeHostname_acePrefixUnicodeChars() { + assertThrows(IllegalArgumentException.class, () -> canonicalizeHostname("xn--みんな")); } @Test