Disallow empty nameservers for domains in TLDs with whitelist

If a TLD has a whitelist on nameservers, domains in such TLD must have
at least one nameserver. Therefore creating domains with empty nameserver
is forbidden, as well as deleting the last nameserver on a domain. We
enforce this policy by checking the number of nameservers for the new resource
to makesure it is not zero if a whitelist exists.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=127318320
This commit is contained in:
Ben McIlwain 2016-07-13 08:13:42 -07:00
parent aa2f283f7c
commit 4ccc016e5c
11 changed files with 129 additions and 19 deletions

View file

@ -21,7 +21,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables; import static google.registry.flows.domain.DomainFlowUtils.validateDomainNameWithIdnTables;
import static google.registry.flows.domain.DomainFlowUtils.validateDsData; import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCount; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts; import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent; import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
@ -215,7 +215,7 @@ public abstract class BaseDomainCreateFlow<R extends DomainBase, B extends Build
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts()); validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
Set<String> fullyQualifiedHostNames = Set<String> fullyQualifiedHostNames =
nullToEmpty(command.getNameserverFullyQualifiedHostNames()); nullToEmpty(command.getNameserverFullyQualifiedHostNames());
validateNameserversCount(fullyQualifiedHostNames.size()); validateNameserversCountForTld(tld, fullyQualifiedHostNames.size());
validateNameserversAllowedOnTld(tld, fullyQualifiedHostNames); validateNameserversAllowedOnTld(tld, fullyQualifiedHostNames);
validateLaunchCreateExtension(); validateLaunchCreateExtension();
// If a signed mark was provided, then it must match the desired domain label. // If a signed mark was provided, then it must match the desired domain label.

View file

@ -21,13 +21,14 @@ import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReference
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes; import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
import static google.registry.flows.domain.DomainFlowUtils.validateDsData; import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCount; import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts; import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld; import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent; import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete; import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import google.registry.flows.EppException; import google.registry.flows.EppException;
import google.registry.flows.EppException.ParameterValuePolicyErrorException; import google.registry.flows.EppException.ParameterValuePolicyErrorException;
import google.registry.flows.EppException.RequiredParameterMissingException; import google.registry.flows.EppException.RequiredParameterMissingException;
@ -40,6 +41,7 @@ import google.registry.model.domain.secdns.DelegationSignerData;
import google.registry.model.domain.secdns.SecDnsUpdateExtension; import google.registry.model.domain.secdns.SecDnsUpdateExtension;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Add;
import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove; import google.registry.model.domain.secdns.SecDnsUpdateExtension.Remove;
import java.util.Set; import java.util.Set;
/** /**
@ -126,7 +128,7 @@ public abstract class BaseDomainUpdateFlow<R extends DomainBase, B extends Build
validateNoDuplicateContacts(newResource.getContacts()); validateNoDuplicateContacts(newResource.getContacts());
validateRequiredContactsPresent(newResource.getRegistrant(), newResource.getContacts()); validateRequiredContactsPresent(newResource.getRegistrant(), newResource.getContacts());
validateDsData(newResource.getDsData()); validateDsData(newResource.getDsData());
validateNameserversCount(newResource.getNameservers().size()); validateNameserversCountForTld(newResource.getTld(), newResource.getNameservers().size());
} }
/** The secDNS:all element must have value 'true' if present. */ /** The secDNS:all element must have value 'true' if present. */

View file

@ -90,6 +90,7 @@ import javax.inject.Inject;
* @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException} * @error {@link DomainFlowUtils.LinkedResourcesDoNotExistException}
* @error {@link DomainFlowUtils.MissingContactTypeException} * @error {@link DomainFlowUtils.MissingContactTypeException}
* @error {@link DomainFlowUtils.NameserversNotAllowedException} * @error {@link DomainFlowUtils.NameserversNotAllowedException}
* @error {@link DomainFlowUtils.NameserversNotSpecifiedException}
* @error {@link DomainFlowUtils.NoMarksFoundMatchingDomainException} * @error {@link DomainFlowUtils.NoMarksFoundMatchingDomainException}
* @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.PremiumNameBlockedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException}

View file

@ -80,6 +80,7 @@ import javax.inject.Inject;
* @error {@link DomainFlowUtils.MissingRegistrantException} * @error {@link DomainFlowUtils.MissingRegistrantException}
* @error {@link DomainFlowUtils.MissingTechnicalContactException} * @error {@link DomainFlowUtils.MissingTechnicalContactException}
* @error {@link DomainFlowUtils.NameserversNotAllowedException} * @error {@link DomainFlowUtils.NameserversNotAllowedException}
* @error {@link DomainFlowUtils.NameserversNotSpecifiedException}
* @error {@link DomainFlowUtils.PremiumNameBlockedException} * @error {@link DomainFlowUtils.PremiumNameBlockedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException}
* @error {@link DomainFlowUtils.TldDoesNotExistException} * @error {@link DomainFlowUtils.TldDoesNotExistException}

View file

@ -274,11 +274,18 @@ public class DomainFlowUtils {
} }
} }
static void validateNameserversCount(int count) throws EppException { static void validateNameserversCountForTld(String tld, int count) throws EppException {
ImmutableSet<String> whitelist = Registry.get(tld).getAllowedFullyQualifiedHostNames();
// For TLDs with a nameserver whitelist, all domains must have at least 1 nameserver.
if (!whitelist.isEmpty() && count == 0) {
throw new NameserversNotSpecifiedException();
}
if (count > MAX_NAMESERVERS_PER_DOMAIN) { if (count > MAX_NAMESERVERS_PER_DOMAIN) {
throw new TooManyNameserversException(String.format( throw new TooManyNameserversException(String.format(
"Only %d nameservers are allowed per domain", MAX_NAMESERVERS_PER_DOMAIN)); "Only %d nameservers are allowed per domain", MAX_NAMESERVERS_PER_DOMAIN));
} }
} }
static void validateNoDuplicateContacts(Set<DesignatedContact> contacts) static void validateNoDuplicateContacts(Set<DesignatedContact> contacts)
@ -313,7 +320,7 @@ public class DomainFlowUtils {
static void validateRegistrantAllowedOnTld(String tld, String registrantContactId) static void validateRegistrantAllowedOnTld(String tld, String registrantContactId)
throws RegistrantNotAllowedException { throws RegistrantNotAllowedException {
ImmutableSet<String> whitelist = Registry.get(tld).getAllowedRegistrantContactIds(); ImmutableSet<String> whitelist = Registry.get(tld).getAllowedRegistrantContactIds();
// Empty whitelists or null registrantContactId are ignored. // Empty whitelist or null registrantContactId are ignored.
if (registrantContactId != null && !whitelist.isEmpty() if (registrantContactId != null && !whitelist.isEmpty()
&& !whitelist.contains(registrantContactId)) { && !whitelist.contains(registrantContactId)) {
throw new RegistrantNotAllowedException(registrantContactId); throw new RegistrantNotAllowedException(registrantContactId);
@ -323,14 +330,14 @@ public class DomainFlowUtils {
static void validateNameserversAllowedOnTld(String tld, Set<String> fullyQualifiedHostNames) static void validateNameserversAllowedOnTld(String tld, Set<String> fullyQualifiedHostNames)
throws EppException { throws EppException {
ImmutableSet<String> whitelist = Registry.get(tld).getAllowedFullyQualifiedHostNames(); ImmutableSet<String> whitelist = Registry.get(tld).getAllowedFullyQualifiedHostNames();
if (whitelist.isEmpty()) { // Empty whitelists are ignored. Set<String> hostnames = nullToEmpty(fullyQualifiedHostNames);
return; if (!whitelist.isEmpty()) { // Empty whitelist is ignored.
} Set<String> disallowedNameservers = difference(hostnames, whitelist);
Set<String> disallowedNameservers = difference(nullToEmpty(fullyQualifiedHostNames), whitelist);
if (!disallowedNameservers.isEmpty()) { if (!disallowedNameservers.isEmpty()) {
throw new NameserversNotAllowedException(disallowedNameservers); throw new NameserversNotAllowedException(disallowedNameservers);
} }
} }
}
static void verifyNotReserved( static void verifyNotReserved(
InternetDomainName domainName, boolean isSunriseApplication) throws EppException { InternetDomainName domainName, boolean isSunriseApplication) throws EppException {
@ -998,4 +1005,12 @@ public class DomainFlowUtils {
Joiner.on(',').join(fullyQualifiedHostNames))); Joiner.on(',').join(fullyQualifiedHostNames)));
} }
} }
/** Nameservers not specified for this TLD with whitelist. */
public static class NameserversNotSpecifiedException extends StatusProhibitsOperationException {
public NameserversNotSpecifiedException() {
super("At least one nameserver must be specified for this TLD");
}
}
} }

View file

@ -59,6 +59,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.MissingContactTypeException} * @error {@link DomainFlowUtils.MissingContactTypeException}
* @error {@link DomainFlowUtils.MissingTechnicalContactException} * @error {@link DomainFlowUtils.MissingTechnicalContactException}
* @error {@link DomainFlowUtils.NameserversNotAllowedException} * @error {@link DomainFlowUtils.NameserversNotAllowedException}
* @error {@link DomainFlowUtils.NameserversNotSpecifiedException}
* @error {@link DomainFlowUtils.RegistrantNotAllowedException} * @error {@link DomainFlowUtils.RegistrantNotAllowedException}
* @error {@link DomainFlowUtils.TooManyDsRecordsException} * @error {@link DomainFlowUtils.TooManyDsRecordsException}
* @error {@link DomainFlowUtils.TooManyNameserversException} * @error {@link DomainFlowUtils.TooManyNameserversException}

View file

@ -77,6 +77,7 @@ import google.registry.flows.domain.DomainFlowUtils.LeadingDashException;
import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException; import google.registry.flows.domain.DomainFlowUtils.LinkedResourcesDoNotExistException;
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException; import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException; import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException;
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException;
import google.registry.flows.domain.DomainFlowUtils.NoMarksFoundMatchingDomainException; import google.registry.flows.domain.DomainFlowUtils.NoMarksFoundMatchingDomainException;
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException; import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException; import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
@ -1205,7 +1206,7 @@ public class DomainApplicationCreateFlowTest
} }
@Test @Test
public void testSuccess_emptyNameserversPassesWhitelist() throws Exception { public void testFailure_emptyNameserverFailsWhitelist() throws Exception {
setEppInput("domain_create_sunrise_encoded_signed_mark_no_hosts.xml"); setEppInput("domain_create_sunrise_encoded_signed_mark_no_hosts.xml");
persistResource(Registry.get("tld").asBuilder() persistResource(Registry.get("tld").asBuilder()
.setAllowedRegistrantContactIds(ImmutableSet.of("jd1234")) .setAllowedRegistrantContactIds(ImmutableSet.of("jd1234"))
@ -1213,9 +1214,8 @@ public class DomainApplicationCreateFlowTest
.build()); .build());
persistContactsAndHosts(); persistContactsAndHosts();
clock.advanceOneMilli(); clock.advanceOneMilli();
doSuccessfulTest("domain_create_sunrise_encoded_signed_mark_response.xml", true); thrown.expect(NameserversNotSpecifiedException.class);
assertAboutApplications().that(getOnlyGlobalResource(DomainApplication.class)) runFlow();
.hasApplicationStatus(ApplicationStatus.VALIDATED);
} }
/** /**

View file

@ -87,6 +87,7 @@ import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException; import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException;
import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException; import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException; import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException;
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException;
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException; import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException; import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException; import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
@ -1261,13 +1262,14 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
} }
@Test @Test
public void testSuccess_emptyNameserversPassesWhitelist() throws Exception { public void testFailure_emptyNameserverFailsWhitelist() throws Exception {
setEppInput("domain_create_no_hosts_or_dsdata.xml"); setEppInput("domain_create_no_hosts_or_dsdata.xml");
persistResource(Registry.get("tld").asBuilder() persistResource(Registry.get("tld").asBuilder()
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("somethingelse.example.net")) .setAllowedFullyQualifiedHostNames(ImmutableSet.of("somethingelse.example.net"))
.build()); .build());
persistContactsAndHosts(); persistContactsAndHosts();
runFlow(); // This is sufficient, as doSuccessfulTests validates hosts, which will fail here. thrown.expect(NameserversNotSpecifiedException.class);
runFlow();
} }
@Test @Test

View file

@ -37,8 +37,10 @@ import static org.joda.money.CurrencyUnit.USD;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.googlecode.objectify.Key; import com.googlecode.objectify.Key;
import com.googlecode.objectify.Ref; import com.googlecode.objectify.Ref;
import google.registry.flows.EppException.UnimplementedExtensionException; import google.registry.flows.EppException.UnimplementedExtensionException;
import google.registry.flows.EppRequestSource; import google.registry.flows.EppRequestSource;
import google.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException; import google.registry.flows.ResourceCreateOrMutateFlow.OnlyToolCanPassMetadataException;
@ -60,6 +62,7 @@ import google.registry.flows.domain.DomainFlowUtils.MissingAdminContactException
import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException; import google.registry.flows.domain.DomainFlowUtils.MissingContactTypeException;
import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException; import google.registry.flows.domain.DomainFlowUtils.MissingTechnicalContactException;
import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException; import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedException;
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedException;
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException; import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException; import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException; import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
@ -78,6 +81,7 @@ import google.registry.model.host.HostResource;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.model.reporting.HistoryEntry; import google.registry.model.reporting.HistoryEntry;
import org.joda.money.Money; import org.joda.money.Money;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.Before; import org.junit.Before;
@ -1100,7 +1104,8 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
persistResource( persistResource(
Registry.get("tld").asBuilder() Registry.get("tld").asBuilder()
.setAllowedRegistrantContactIds(ImmutableSet.of("sh8013")) .setAllowedRegistrantContactIds(ImmutableSet.of("sh8013"))
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns2.example.foo")) .setAllowedFullyQualifiedHostNames(
ImmutableSet.of("ns1.example.foo", "ns2.example.foo"))
.build()); .build());
assertThat(reloadResourceByUniqueId().getNameservers()).doesNotContain( assertThat(reloadResourceByUniqueId().getNameservers()).doesNotContain(
Ref.create(loadByUniqueId(HostResource.class, "ns2.example.foo", clock.nowUtc()))); Ref.create(loadByUniqueId(HostResource.class, "ns2.example.foo", clock.nowUtc())));
@ -1109,6 +1114,21 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
Ref.create(loadByUniqueId(HostResource.class, "ns2.example.foo", clock.nowUtc()))); Ref.create(loadByUniqueId(HostResource.class, "ns2.example.foo", clock.nowUtc())));
} }
@Test
public void testSuccess_changeRegistrantWhitelisted() throws Exception {
setEppInput("domain_update_registrant.xml");
persistReferencedEntities();
persistDomain();
// Only changes registrant, with both nameserver and registrant whitelist on the TLD.
persistResource(
Registry.get("tld").asBuilder()
.setAllowedRegistrantContactIds(ImmutableSet.of("sh8013"))
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
.build());
runFlow();
assertThat(reloadResourceByUniqueId().getRegistrant().get().getContactId()).isEqualTo("sh8013");
}
@Test @Test
public void testSuccess_nameserverAndRegistrantWhitelisted() throws Exception { public void testSuccess_nameserverAndRegistrantWhitelisted() throws Exception {
persistReferencedEntities(); persistReferencedEntities();
@ -1120,4 +1140,40 @@ public class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow,
.build()); .build());
doSuccessfulTest(); doSuccessfulTest();
} }
@Test
public void testSuccess_removeNameserverWhitelisted() throws Exception {
setEppInput("domain_update_remove_nameserver.xml");
persistReferencedEntities();
persistDomain();
persistResource(
reloadResourceByUniqueId().asBuilder()
.addNameservers(ImmutableSet.of(Ref.create(
loadByUniqueId(HostResource.class, "ns2.example.foo", clock.nowUtc()))))
.build());
persistResource(
Registry.get("tld").asBuilder()
.setAllowedFullyQualifiedHostNames(
ImmutableSet.of("ns1.example.foo", "ns2.example.foo"))
.build());
assertThat(reloadResourceByUniqueId().getNameservers()).contains(
Ref.create(loadByUniqueId(HostResource.class, "ns1.example.foo", clock.nowUtc())));
clock.advanceOneMilli();
runFlow();
assertThat(reloadResourceByUniqueId().getNameservers()).doesNotContain(
Ref.create(loadByUniqueId(HostResource.class, "ns1.example.foo", clock.nowUtc())));
}
@Test
public void testFailure_removeLastNameserverWhitelisted() throws Exception {
persistReferencedEntities();
persistDomain();
setEppInput("domain_update_remove_nameserver.xml");
persistResource(
Registry.get("tld").asBuilder()
.setAllowedFullyQualifiedHostNames(ImmutableSet.of("ns1.example.foo"))
.build());
thrown.expect(NameserversNotSpecifiedException.class);
runFlow();
}
} }

View file

@ -0,0 +1,16 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<domain:update
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.tld</domain:name>
<domain:add/>
<domain:rem/>
<domain:chg>
<domain:registrant>sh8013</domain:registrant>
</domain:chg>
</domain:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,16 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<domain:update
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.tld</domain:name>
<domain:rem>
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
</domain:rem>
</domain:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>