Add better testing of domain and host creation using multi-part TLDs

Added validation on domain creation, preventing a domain from being created if
it equals an existing TLD. Added domain create tests for domains using
multi-part TLDs that shared suffixes and prefixes. Added host create tests for
hosts using multi-part TLDs that shared suffixes.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=164297749
This commit is contained in:
bbilbo 2017-08-04 12:53:33 -07:00 committed by Ben McIlwain
parent b9a8853f4b
commit e786c8d6ff
14 changed files with 322 additions and 14 deletions

View file

@ -299,7 +299,8 @@ An EPP flow that creates a new application for a domain resource.
* The checksum in the specified TCNID does not validate.
* Domain name is under tld which doesn't exist.
* 2005
* Domain name must have exactly one part above the tld.
* Domain name must have exactly one part above the TLD.
* Domain name must not equal an existing multi-part TLD.
* The requested fee is expressed in a scale that is invalid for the given
currency.
* The specified TCNID is invalid.
@ -479,7 +480,8 @@ information.
* Domain label is not allowed by IDN table.
* Domain name is under tld which doesn't exist.
* 2005
* Domain name must have exactly one part above the tld.
* Domain name must have exactly one part above the TLD.
* Domain name must not equal an existing multi-part TLD.
* 2201
* Registrar is not authorized to access this TLD.
* 2306
@ -530,7 +532,8 @@ An EPP flow that creates a new domain resource.
* The checksum in the specified TCNID does not validate.
* Domain name is under tld which doesn't exist.
* 2005
* Domain name must have exactly one part above the tld.
* Domain name must have exactly one part above the TLD.
* Domain name must not equal an existing multi-part TLD.
* The requested fee is expressed in a scale that is invalid for the given
currency.
* The specified TCNID is invalid.

View file

@ -112,6 +112,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException}
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
* @error {@link DomainFlowUtils.DomainNameExistsAsTldException}
* @error {@link DomainFlowUtils.BadPeriodUnitException}
* @error {@link DomainFlowTmchUtils.Base64RequiredForEncodedSignedMarksException}
* @error {@link DomainFlowUtils.ClaimsPeriodEndedException}

View file

@ -78,6 +78,7 @@ import org.joda.time.DateTime;
* @error {@link google.registry.flows.exceptions.TooManyResourceChecksException}
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
* @error {@link DomainFlowUtils.DomainNameExistsAsTldException}
* @error {@link DomainFlowUtils.BadPeriodUnitException}
* @error {@link DomainFlowUtils.BadCommandForRegistryPhaseException}
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}

View file

@ -115,6 +115,7 @@ import org.joda.time.DateTime;
* @error {@link DomainFlowUtils.AcceptedTooLongAgoException}
* @error {@link DomainFlowUtils.BadDomainNameCharacterException}
* @error {@link DomainFlowUtils.BadDomainNamePartsCountException}
* @error {@link DomainFlowUtils.DomainNameExistsAsTldException}
* @error {@link DomainFlowUtils.BadPeriodUnitException}
* @error {@link DomainFlowUtils.ClaimsPeriodEndedException}
* @error {@link DomainFlowUtils.CurrencyUnitMismatchException}

View file

@ -25,6 +25,7 @@ import static google.registry.flows.domain.DomainPricingLogic.getMatchingLrpToke
import static google.registry.model.domain.DomainResource.MAX_REGISTRATION_YEARS;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.model.registry.Registries.findTldForName;
import static google.registry.model.registry.Registries.getTlds;
import static google.registry.model.registry.label.ReservationType.FULLY_BLOCKED;
import static google.registry.model.registry.label.ReservationType.NAMESERVER_RESTRICTED;
import static google.registry.model.registry.label.ReservationType.RESERVED_FOR_ANCHOR_TENANT;
@ -181,6 +182,9 @@ public class DomainFlowUtils {
}
validateFirstLabel(parts.get(0));
InternetDomainName domainName = InternetDomainName.from(name);
if (getTlds().contains(domainName.toString())) {
throw new DomainNameExistsAsTldException();
}
Optional<InternetDomainName> tldParsed = findTldForName(domainName);
if (!tldParsed.isPresent()) {
throw new TldDoesNotExistException(domainName.parent().toString());
@ -1055,10 +1059,17 @@ public class DomainFlowUtils {
}
}
/** Domain name must have exactly one part above the tld. */
/** Domain name must have exactly one part above the TLD. */
static class BadDomainNamePartsCountException extends ParameterValueSyntaxErrorException {
public BadDomainNamePartsCountException() {
super("Domain name must have exactly one part above the tld");
super("Domain name must have exactly one part above the TLD");
}
}
/** Domain name must not equal an existing multi-part TLD. */
static class DomainNameExistsAsTldException extends ParameterValueSyntaxErrorException {
public DomainNameExistsAsTldException() {
super("Domain name must not equal an existing multi-part TLD");
}
}

View file

@ -130,7 +130,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
"contact_create_response_jd1234.xml",
DateTime.parse("2000-06-01T00:01:00Z"));
// Create domain example.tld.
// Create domain example.tld
assertCommandAndResponse(
"domain_create_no_hosts_or_dsdata.xml",
"domain_create_response.xml",
@ -167,7 +167,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
"contact_create_response_jd1234.xml",
DateTime.parse("2000-06-01T00:01:00Z"));
// Create domain example.tld.
// Create domain example.tld
assertCommandAndResponse(
"domain_create_no_hosts_or_dsdata.xml",
"domain_create_response.xml",
@ -265,7 +265,7 @@ public class EppLifecycleDomainTest extends EppTestCase {
assertCommandAndResponse("login_valid.xml", "login_response.xml");
createFakesite();
// Create domain example.tld.
// Create domain example.tld
assertCommandAndResponse(
"domain_create_no_hosts_or_dsdata.xml",
"domain_create_response_superordinate.xml",
@ -684,4 +684,84 @@ public class EppLifecycleDomainTest extends EppTestCase {
DateTime.parse("2001-01-07T00:00:00Z"));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testSuccess_multipartTldsWithSharedSuffixes() throws Exception {
createTlds("bar.foo.tld", "foo.tld");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
null,
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
DateTime.parse("2000-06-01T00:00:00Z"));
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
DateTime.parse("2000-06-01T00:01:00Z"));
// Create domain example.bar.foo.tld
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.bar.foo.tld"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.bar.foo.tld"),
DateTime.parse("2000-06-01T00:02:00Z"));
// Create domain example.foo.tld
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.foo.tld"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.foo.tld"),
DateTime.parse("2000-06-01T00:02:00Z"));
// Create domain example.tld
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.tld"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.tld"),
DateTime.parse("2000-06-01T00:02:00Z"));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testSuccess_multipartTldsWithSharedPrefixes() throws Exception {
createTld("tld.foo");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
null,
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
DateTime.parse("2000-06-01T00:00:00Z"));
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
DateTime.parse("2000-06-01T00:01:00Z"));
// Create domain example.tld.foo
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.tld.foo"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.tld.foo"),
DateTime.parse("2000-06-01T00:02:00Z"));
// Create domain example.tld
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.tld"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.tld"),
DateTime.parse("2000-06-01T00:02:00Z"));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
}

View file

@ -14,12 +14,19 @@
package google.registry.flows;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.EppMetricSubject.assertThat;
import static google.registry.testing.HostResourceSubject.assertAboutHosts;
import com.google.common.collect.ImmutableMap;
import com.googlecode.objectify.Key;
import google.registry.model.domain.DomainResource;
import google.registry.model.host.HostResource;
import google.registry.testing.AppEngineRule;
import org.joda.time.DateTime;
import org.junit.Rule;
@ -148,4 +155,106 @@ public class EppLifecycleHostTest extends EppTestCase {
DateTime.parse("2000-06-11T00:08:00Z"));
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
@Test
public void testSuccess_multipartTldsWithSharedSuffixes() throws Exception {
createTlds("bar.foo.tld", "foo.tld", "tld");
assertCommandAndResponse("login_valid.xml", "login_response.xml");
assertCommandAndResponse(
"contact_create_sh8013.xml",
ImmutableMap.<String, String>of(),
"contact_create_response_sh8013.xml",
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
DateTime.parse("2000-06-01T00:00:00Z"));
assertCommandAndResponse(
"contact_create_jd1234.xml",
"contact_create_response_jd1234.xml",
DateTime.parse("2000-06-01T00:01:00Z"));
// Create domain example.bar.foo.tld
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.bar.foo.tld"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.bar.foo.tld"),
DateTime.parse("2000-06-01T00:02:00Z"));
// Create domain example.foo.tld
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.foo.tld"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.foo.tld"),
DateTime.parse("2000-06-01T00:02:00Z"));
// Create domain example.tld
assertCommandAndResponse(
"domain_create_wildcard.xml",
ImmutableMap.of("HOSTNAME", "example.tld"),
"domain_create_response_wildcard.xml",
ImmutableMap.of("DOMAIN", "example.tld"),
DateTime.parse("2000-06-01T00:02:00Z"));
// Create host ns1.example.bar.foo.tld
assertCommandAndResponse(
"host_create_with_ips.xml",
ImmutableMap.of("HOSTNAME", "ns1.example.bar.foo.tld"),
"host_create_response.xml",
ImmutableMap.of("HOSTNAME", "ns1.example.bar.foo.tld", "CRDATE", "2000-06-01T00:03:00Z"),
DateTime.parse("2000-06-01T00:03:00Z"));
// Create host ns1.example.foo.tld
assertCommandAndResponse(
"host_create_with_ips.xml",
ImmutableMap.of("HOSTNAME", "ns1.example.foo.tld"),
"host_create_response.xml",
ImmutableMap.of("HOSTNAME", "ns1.example.foo.tld", "CRDATE", "2000-06-01T00:04:00Z"),
DateTime.parse("2000-06-01T00:04:00Z"));
// Create host ns1.example.tld
assertCommandAndResponse(
"host_create_with_ips.xml",
ImmutableMap.of("HOSTNAME", "ns1.example.tld"),
"host_create_response.xml",
ImmutableMap.of("HOSTNAME", "ns1.example.tld", "CRDATE", "2000-06-01T00:04:00Z"),
DateTime.parse("2000-06-01T00:04:00Z"));
HostResource exampleBarFooTldHost =
loadByForeignKey(
HostResource.class, "ns1.example.bar.foo.tld", DateTime.parse("2000-06-01T00:05:00Z"));
DomainResource exampleBarFooTldDomain =
loadByForeignKey(
DomainResource.class, "example.bar.foo.tld", DateTime.parse("2000-06-01T00:05:00Z"));
assertAboutHosts()
.that(exampleBarFooTldHost)
.hasSuperordinateDomain(Key.create(exampleBarFooTldDomain));
assertThat(exampleBarFooTldDomain.getSubordinateHosts())
.containsExactly("ns1.example.bar.foo.tld");
HostResource exampleFooTldHost =
loadByForeignKey(
HostResource.class, "ns1.example.foo.tld", DateTime.parse("2000-06-01T00:05:00Z"));
DomainResource exampleFooTldDomain =
loadByForeignKey(
DomainResource.class, "example.foo.tld", DateTime.parse("2000-06-01T00:05:00Z"));
assertAboutHosts()
.that(exampleFooTldHost)
.hasSuperordinateDomain(Key.create(exampleFooTldDomain));
assertThat(exampleFooTldDomain.getSubordinateHosts())
.containsExactly("ns1.example.foo.tld");
HostResource exampleTldHost =
loadByForeignKey(
HostResource.class, "ns1.example.tld", DateTime.parse("2000-06-01T00:05:00Z"));
DomainResource exampleTldDomain =
loadByForeignKey(
DomainResource.class, "example.tld", DateTime.parse("2000-06-01T00:05:00Z"));
assertAboutHosts().that(exampleTldHost).hasSuperordinateDomain(Key.create(exampleTldDomain));
assertThat(exampleTldDomain.getSubordinateHosts())
.containsExactly("ns1.example.tld");
assertCommandAndResponse("logout.xml", "logout_response.xml");
}
}

View file

@ -22,6 +22,7 @@ import static google.registry.model.index.DomainApplicationIndex.loadActiveAppli
import static google.registry.model.ofy.ObjectifyService.ofy;
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.deleteTld;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.newDomainApplication;
@ -70,6 +71,7 @@ import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchExceptio
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import google.registry.flows.domain.DomainFlowUtils.DashesInThirdAndFourthException;
import google.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
import google.registry.flows.domain.DomainFlowUtils.DomainNameExistsAsTldException;
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
import google.registry.flows.domain.DomainFlowUtils.EmptyDomainNamePartException;
@ -1768,6 +1770,18 @@ public class DomainApplicationCreateFlowTest
doFailingDomainNameTest("tld", BadDomainNamePartsCountException.class);
}
@Test
public void testFailure_domainNameExistsAsTld_lowercase() throws Exception {
createTlds("foo.tld", "tld");
doFailingDomainNameTest("foo.tld", DomainNameExistsAsTldException.class);
}
@Test
public void testFailure_domainNameExistsAsTld_uppercase() throws Exception {
createTlds("foo.tld", "tld");
doFailingDomainNameTest("FOO.TLD", BadDomainNameCharacterException.class);
}
@Test
public void testFailure_invalidPunycode() throws Exception {
doFailingDomainNameTest("xn--abcdefg.tld", InvalidPunycodeException.class);

View file

@ -16,6 +16,7 @@ package google.registry.flows.domain;
import static google.registry.model.eppoutput.CheckData.DomainCheck.create;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.newDomainApplication;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
@ -39,6 +40,7 @@ import google.registry.flows.domain.DomainFlowUtils.BadPeriodUnitException;
import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchException;
import google.registry.flows.domain.DomainFlowUtils.DashesInThirdAndFourthException;
import google.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
import google.registry.flows.domain.DomainFlowUtils.DomainNameExistsAsTldException;
import google.registry.flows.domain.DomainFlowUtils.EmptyDomainNamePartException;
import google.registry.flows.domain.DomainFlowUtils.FeeChecksDontSupportPhasesException;
import google.registry.flows.domain.DomainFlowUtils.InvalidIdnDomainLabelException;
@ -357,6 +359,18 @@ public class DomainCheckFlowTest
doFailingBadLabelTest("tld", BadDomainNamePartsCountException.class);
}
@Test
public void testFailure_domainNameExistsAsTld_lowercase() throws Exception {
createTlds("foo.tld", "tld");
doFailingBadLabelTest("foo.tld", DomainNameExistsAsTldException.class);
}
@Test
public void testFailure_domainNameExistsAsTld_uppercase() throws Exception {
createTlds("foo.tld", "tld");
doFailingBadLabelTest("FOO.TLD", BadDomainNameCharacterException.class);
}
@Test
public void testFailure_invalidPunycode() throws Exception {
doFailingBadLabelTest("xn--abcdefg.tld", InvalidPunycodeException.class);

View file

@ -26,6 +26,7 @@ import static google.registry.pricing.PricingEngineProxy.isDomainPremium;
import static google.registry.testing.DatastoreHelper.assertBillingEvents;
import static google.registry.testing.DatastoreHelper.assertPollMessagesForResource;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.deleteTld;
import static google.registry.testing.DatastoreHelper.getHistoryEntries;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
@ -72,6 +73,7 @@ import google.registry.flows.domain.DomainFlowUtils.CurrencyUnitMismatchExceptio
import google.registry.flows.domain.DomainFlowUtils.CurrencyValueScaleException;
import google.registry.flows.domain.DomainFlowUtils.DashesInThirdAndFourthException;
import google.registry.flows.domain.DomainFlowUtils.DomainLabelTooLongException;
import google.registry.flows.domain.DomainFlowUtils.DomainNameExistsAsTldException;
import google.registry.flows.domain.DomainFlowUtils.DomainNotAllowedForTldWithCreateRestrictionException;
import google.registry.flows.domain.DomainFlowUtils.DomainReservedException;
import google.registry.flows.domain.DomainFlowUtils.DuplicateContactForRoleException;
@ -372,6 +374,24 @@ public class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow,
assertNoLordn();
}
@Test
public void testFailure_domainNameExistsAsTld_lowercase() throws Exception {
createTlds("foo.tld", "tld");
setEppInput("domain_create_wildcard.xml", ImmutableMap.of("DOMAIN", "foo.tld"));
persistContactsAndHosts();
thrown.expect(DomainNameExistsAsTldException.class);
runFlow();
}
@Test
public void testFailure_domainNameExistsAsTld_uppercase() throws Exception {
createTlds("foo.tld", "tld");
setEppInput("domain_create_wildcard.xml", ImmutableMap.of("DOMAIN", "FOO.TLD"));
persistContactsAndHosts();
thrown.expect(BadDomainNameCharacterException.class);
runFlow();
}
@Test
public void testSuccess_anchorTenantViaExtension() throws Exception {
eppRequestSource = EppRequestSource.TOOL;

View file

@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.testing.DatastoreHelper.assertNoBillingEvents;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.createTlds;
import static google.registry.testing.DatastoreHelper.newDomainResource;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import static google.registry.testing.DatastoreHelper.persistActiveHost;
@ -49,7 +50,6 @@ import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.HostResource;
import google.registry.model.reporting.HistoryEntry;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
/** Unit tests for {@link HostCreateFlow}. */
@ -76,11 +76,6 @@ public class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Hos
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z"));
}
@Before
public void initHostTest() {
createTld("foobar");
}
private void doSuccessfulTest() throws Exception {
clock.advanceOneMilli();
assertTransactionalFlow(true);
@ -124,6 +119,15 @@ public class HostCreateFlowTest extends ResourceFlowTestCase<HostCreateFlow, Hos
assertDnsTasksEnqueued("ns1.example.tld");
}
@Test
public void testFailure_multipartTLDsAndInvalidHost() throws Exception {
createTlds("bar.tld", "tld");
setEppHostCreateInputWithIps("ns1.bar.tld");
thrown.expect(HostNameTooShallowException.class);
runFlow();
}
@Test
public void testSuccess_externalExistedButWasDeleted() throws Exception {
persistDeletedHost(getUniqueIdFromCommand(), clock.nowUtc().minusDays(1));

View file

@ -0,0 +1,19 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg>Command completed successfully</msg>
</result>
<resData>
<domain:creData
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
<domain:crDate>2000-06-01T00:02:00.0Z</domain:crDate>
<domain:exDate>2002-06-01T00:02:00.0Z</domain:exDate>
</domain:creData>
</resData>
<trID>
<clTRID>ABC-12345</clTRID>
<svTRID>server-trid</svTRID>
</trID>
</response>
</epp>

View file

@ -0,0 +1,18 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%HOSTNAME%</domain:name>
<domain:period unit="y">2</domain:period>
<domain:registrant>jd1234</domain:registrant>
<domain:contact type="admin">sh8013</domain:contact>
<domain:contact type="tech">sh8013</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

View file

@ -0,0 +1,13 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<host:create xmlns:host="urn:ietf:params:xml:ns:host-1.0">
<host:name>%HOSTNAME%</host:name>
<host:addr ip="v4">192.0.2.2</host:addr>
<host:addr ip="v4">192.0.2.29</host:addr>
<host:addr ip="v6">1080:0:0:0:8:800:200C:417A</host:addr>
</host:create>
</create>
<clTRID>ABC-12345</clTRID>
</command>
</epp>