[secDNS] EPP <create> command (#20)
This commit is contained in:
parent
c0e58dec04
commit
976fd2c002
|
@ -0,0 +1,506 @@
|
|||
//! DNS security extensions mapping
|
||||
//!
|
||||
//! As described in [RFC 5910](https://www.rfc-editor.org/rfc/rfc5910)
|
||||
use instant_xml::{Error, Id, Serializer, ToXml};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::request::{Extension, Transaction};
|
||||
|
||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:secDNS-1.1";
|
||||
|
||||
impl<'a> Transaction<CreateData<'a>> for crate::domain::create::DomainCreate<'a> {}
|
||||
|
||||
impl<'a> Extension for CreateData<'a> {
|
||||
type Response = ();
|
||||
}
|
||||
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "create", ns(XMLNS))]
|
||||
pub struct CreateData<'a> {
|
||||
data: DsOrKeyType<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [DsDataType<'a>]> for CreateData<'a> {
|
||||
fn from(s: &'a [DsDataType<'a>]) -> Self {
|
||||
Self {
|
||||
data: DsOrKeyType {
|
||||
maximum_signature_lifetime: None,
|
||||
data: DsOrKeyData::DsData(s),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [KeyDataType<'a>]> for CreateData<'a> {
|
||||
fn from(s: &'a [KeyDataType<'a>]) -> Self {
|
||||
Self {
|
||||
data: DsOrKeyType {
|
||||
maximum_signature_lifetime: None,
|
||||
data: DsOrKeyData::KeyData(s),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<(Duration, &'a [DsDataType<'a>])> for CreateData<'a> {
|
||||
fn from((maximum_signature_lifetime, data): (Duration, &'a [DsDataType<'a>])) -> Self {
|
||||
Self {
|
||||
data: DsOrKeyType {
|
||||
maximum_signature_lifetime: Some(maximum_signature_lifetime),
|
||||
data: DsOrKeyData::DsData(data),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<(Duration, &'a [KeyDataType<'a>])> for CreateData<'a> {
|
||||
fn from((maximum_signature_lifetime, data): (Duration, &'a [KeyDataType<'a>])) -> Self {
|
||||
Self {
|
||||
data: DsOrKeyType {
|
||||
maximum_signature_lifetime: Some(maximum_signature_lifetime),
|
||||
data: DsOrKeyData::KeyData(data),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct supporting either the `dsData` or the `keyData` interface.
|
||||
#[derive(Debug)]
|
||||
pub struct DsOrKeyType<'a> {
|
||||
maximum_signature_lifetime: Option<Duration>,
|
||||
data: DsOrKeyData<'a>,
|
||||
}
|
||||
|
||||
impl ToXml for DsOrKeyType<'_> {
|
||||
fn serialize<W: Write + ?Sized>(
|
||||
&self,
|
||||
_: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<'_, W>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(maximum_signature_lifetime) = self.maximum_signature_lifetime {
|
||||
let nc_name = "maxSigLife";
|
||||
let prefix = serializer.write_start(nc_name, XMLNS)?;
|
||||
serializer.end_start()?;
|
||||
maximum_signature_lifetime
|
||||
.as_secs()
|
||||
.serialize(None, serializer)?;
|
||||
serializer.write_close(prefix, nc_name)?;
|
||||
}
|
||||
match &self.data {
|
||||
DsOrKeyData::DsData(data) => data.serialize(None, serializer)?,
|
||||
DsOrKeyData::KeyData(data) => data.serialize(None, serializer)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(forward)]
|
||||
pub enum DsOrKeyData<'a> {
|
||||
DsData(&'a [DsDataType<'a>]),
|
||||
KeyData(&'a [KeyDataType<'a>]),
|
||||
}
|
||||
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "dsData", ns(XMLNS))]
|
||||
pub struct DsDataType<'a> {
|
||||
#[xml(rename = "keyTag")]
|
||||
key_tag: u16,
|
||||
#[xml(rename = "alg")]
|
||||
algorithm: Algorithm,
|
||||
#[xml(rename = "digestType")]
|
||||
digest_type: DigestAlgorithm,
|
||||
digest: Cow<'a, str>,
|
||||
#[xml(rename = "keyData")]
|
||||
key_data: Option<KeyDataType<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> DsDataType<'a> {
|
||||
pub fn new(
|
||||
key_tag: u16,
|
||||
algorithm: Algorithm,
|
||||
digest_type: DigestAlgorithm,
|
||||
digest: &'a str,
|
||||
key_data: Option<KeyDataType<'a>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key_tag,
|
||||
algorithm,
|
||||
digest_type,
|
||||
digest: digest.into(),
|
||||
key_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// DigestAlgorithm identifies the algorithm used to construct the digest
|
||||
/// https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
// XXX Do NOT derive PartialEq, Hash or Ord because the variant
|
||||
// Other(u8) could clash with one of the other variants. They have to
|
||||
// be hand coded.
|
||||
pub enum DigestAlgorithm {
|
||||
Sha1,
|
||||
Sha256,
|
||||
Gost,
|
||||
Sha384,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
impl From<DigestAlgorithm> for u8 {
|
||||
fn from(s: DigestAlgorithm) -> Self {
|
||||
match s {
|
||||
DigestAlgorithm::Sha1 => 1,
|
||||
DigestAlgorithm::Sha256 => 2,
|
||||
DigestAlgorithm::Gost => 3,
|
||||
DigestAlgorithm::Sha384 => 4,
|
||||
DigestAlgorithm::Other(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for DigestAlgorithm {
|
||||
fn serialize<W: Write + ?Sized>(
|
||||
&self,
|
||||
id: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<'_, W>,
|
||||
) -> Result<(), Error> {
|
||||
u8::from(*self).serialize(id, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Algorithm identifies the public key's cryptographic algorithm
|
||||
/// https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml#dns-sec-alg-numbers-1
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
// XXX Do NOT derive PartialEq, Hash or Ord because the variant
|
||||
// Other(u8) could clash with one of the other variants. They have to
|
||||
// be hand coded.
|
||||
pub enum Algorithm {
|
||||
// Delete DS
|
||||
Delete,
|
||||
/// RSA/MD5
|
||||
RsaMd5,
|
||||
/// Diffie-Hellman
|
||||
Dh,
|
||||
/// DSA/SHA-1
|
||||
Dsa,
|
||||
/// Elliptic Curve
|
||||
Ecc,
|
||||
/// RSA/SHA-1
|
||||
RsaSha1,
|
||||
/// DSA-NSEC3-SHA1
|
||||
DsaNsec3Sha1,
|
||||
/// RSASHA1-NSEC3-SHA1
|
||||
RsaSha1Nsec3Sha1,
|
||||
/// RSA/SHA-256
|
||||
RsaSha256,
|
||||
/// RSA/SHA-512
|
||||
RsaSha512,
|
||||
/// GOST R 34.10-2001
|
||||
EccGost,
|
||||
/// ECDSA Curve P-256 with SHA-256
|
||||
EcdsaP256Sha256,
|
||||
/// ECDSA Curve P-384 with SHA-384
|
||||
EcdsaP384Sha384,
|
||||
/// Ed25519
|
||||
Ed25519,
|
||||
/// Ed448
|
||||
Ed448,
|
||||
/// Indirect
|
||||
Indirect,
|
||||
/// Private
|
||||
PrivateDns,
|
||||
/// Private
|
||||
PrivateOid,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
impl From<Algorithm> for u8 {
|
||||
fn from(s: Algorithm) -> Self {
|
||||
match s {
|
||||
Algorithm::Delete => 0,
|
||||
Algorithm::RsaMd5 => 1,
|
||||
Algorithm::Dh => 2,
|
||||
Algorithm::Dsa => 3,
|
||||
// RFC 4034
|
||||
Algorithm::Ecc => 4,
|
||||
Algorithm::RsaSha1 => 5,
|
||||
Algorithm::DsaNsec3Sha1 => 6,
|
||||
Algorithm::RsaSha1Nsec3Sha1 => 7,
|
||||
Algorithm::RsaSha256 => 8,
|
||||
Algorithm::RsaSha512 => 10,
|
||||
Algorithm::EccGost => 12,
|
||||
Algorithm::EcdsaP256Sha256 => 13,
|
||||
Algorithm::EcdsaP384Sha384 => 14,
|
||||
Algorithm::Ed25519 => 15,
|
||||
Algorithm::Ed448 => 16,
|
||||
Algorithm::Indirect => 252,
|
||||
Algorithm::PrivateDns => 253,
|
||||
Algorithm::PrivateOid => 254,
|
||||
Algorithm::Other(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for Algorithm {
|
||||
fn serialize<W: Write + ?Sized>(
|
||||
&self,
|
||||
id: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<'_, W>,
|
||||
) -> Result<(), Error> {
|
||||
u8::from(*self).serialize(id, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "keyData", ns(XMLNS))]
|
||||
pub struct KeyDataType<'a> {
|
||||
flags: Flags,
|
||||
protocol: Protocol,
|
||||
#[xml(rename = "alg")]
|
||||
algorithm: Algorithm,
|
||||
#[xml(rename = "pubKey")]
|
||||
public_key: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> KeyDataType<'a> {
|
||||
pub fn new(
|
||||
flags: Flags,
|
||||
protocol: Protocol,
|
||||
algorithm: Algorithm,
|
||||
public_key: &'a str,
|
||||
) -> Self {
|
||||
Self {
|
||||
flags,
|
||||
protocol,
|
||||
algorithm,
|
||||
public_key: public_key.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Flags {
|
||||
/// Zone Key flag. If `true` then the DNSKEY record holds a DNS
|
||||
/// zone key. If `false` then the DNSKEY record holds some other
|
||||
/// type of DNS public key.
|
||||
zone_key: bool,
|
||||
/// Secure Entry Point. If `true` then the DNSKEY record holds a
|
||||
/// key intended for use as a secure entry point.
|
||||
secure_entry_point: bool,
|
||||
}
|
||||
|
||||
impl From<Flags> for u16 {
|
||||
fn from(flags: Flags) -> Self {
|
||||
let mut res = 0;
|
||||
if flags.zone_key {
|
||||
res |= 0b1_0000_0000;
|
||||
}
|
||||
if flags.secure_entry_point {
|
||||
res |= 0x1;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for Flags {
|
||||
fn serialize<W: Write + ?Sized>(
|
||||
&self,
|
||||
id: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<'_, W>,
|
||||
) -> Result<(), Error> {
|
||||
u16::from(*self).serialize(id, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// `Flags` for a zone signing key.
|
||||
pub const FLAGS_DNS_ZONE_KEY: Flags = Flags {
|
||||
zone_key: true,
|
||||
secure_entry_point: false,
|
||||
};
|
||||
/// `Flags` for a key signing key.
|
||||
pub const FLAGS_DNS_ZONE_KEY_SEP: Flags = Flags {
|
||||
zone_key: true,
|
||||
secure_entry_point: true,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
// XXX Do NOT derive PartialEq, Hash or Ord because the variant
|
||||
// Other(u8) could clash with one of the other variants. They have to
|
||||
// be hand coded.
|
||||
pub enum Protocol {
|
||||
/// RFC 2535, reserved
|
||||
Tls,
|
||||
/// RFC 2535, reserved
|
||||
Email,
|
||||
/// RFC 5034 DNSSEC
|
||||
Dnssec,
|
||||
/// RFC 2535, reserved
|
||||
Ipsec,
|
||||
/// RFC 2535
|
||||
All,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
impl From<Protocol> for u8 {
|
||||
fn from(s: Protocol) -> Self {
|
||||
match s {
|
||||
Protocol::Tls => 1,
|
||||
Protocol::Email => 2,
|
||||
Protocol::Dnssec => 3,
|
||||
Protocol::Ipsec => 4,
|
||||
Protocol::All => 255,
|
||||
Protocol::Other(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToXml for Protocol {
|
||||
fn serialize<W: Write + ?Sized>(
|
||||
&self,
|
||||
id: Option<Id<'_>>,
|
||||
serializer: &mut Serializer<'_, W>,
|
||||
) -> Result<(), Error> {
|
||||
u8::from(*self).serialize(id, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::domain;
|
||||
use crate::tests::assert_serialized;
|
||||
|
||||
#[test]
|
||||
fn create_ds_data_interface() {
|
||||
let ds_data = [DsDataType::new(
|
||||
12345,
|
||||
Algorithm::Dsa,
|
||||
DigestAlgorithm::Sha1,
|
||||
"49FD46E6C4B45C55D4AC",
|
||||
None,
|
||||
)];
|
||||
let extension = CreateData::from((Duration::from_secs(604800), ds_data.as_ref()));
|
||||
let ns = [
|
||||
domain::HostInfo::Obj(domain::HostObj {
|
||||
name: "ns1.example.com".into(),
|
||||
}),
|
||||
domain::HostInfo::Obj(domain::HostObj {
|
||||
name: "ns2.example.com".into(),
|
||||
}),
|
||||
];
|
||||
let contact = [
|
||||
domain::DomainContact {
|
||||
contact_type: "admin".into(),
|
||||
id: "sh8013".into(),
|
||||
},
|
||||
domain::DomainContact {
|
||||
contact_type: "tech".into(),
|
||||
id: "sh8013".into(),
|
||||
},
|
||||
];
|
||||
let object = domain::DomainCreate::new(
|
||||
"example.com",
|
||||
domain::Period::years(2).unwrap(),
|
||||
Some(&ns),
|
||||
Some("jd1234"),
|
||||
"2fooBAR",
|
||||
Some(&contact),
|
||||
);
|
||||
assert_serialized(
|
||||
"request/extensions/secdns_create_ds.xml",
|
||||
(&object, &extension),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_ds_and_key_data_interface() {
|
||||
let key_data = KeyDataType::new(
|
||||
FLAGS_DNS_ZONE_KEY_SEP,
|
||||
Protocol::Dnssec,
|
||||
Algorithm::Dsa,
|
||||
"AQPJ////4Q==",
|
||||
);
|
||||
let ds_data = [DsDataType::new(
|
||||
12345,
|
||||
Algorithm::Dsa,
|
||||
DigestAlgorithm::Sha1,
|
||||
"49FD46E6C4B45C55D4AC",
|
||||
Some(key_data),
|
||||
)];
|
||||
let extension = CreateData::from((Duration::from_secs(604800), ds_data.as_ref()));
|
||||
let ns = [
|
||||
domain::HostInfo::Obj(domain::HostObj {
|
||||
name: "ns1.example.com".into(),
|
||||
}),
|
||||
domain::HostInfo::Obj(domain::HostObj {
|
||||
name: "ns2.example.com".into(),
|
||||
}),
|
||||
];
|
||||
let contact = [
|
||||
domain::DomainContact {
|
||||
contact_type: "admin".into(),
|
||||
id: "sh8013".into(),
|
||||
},
|
||||
domain::DomainContact {
|
||||
contact_type: "tech".into(),
|
||||
id: "sh8013".into(),
|
||||
},
|
||||
];
|
||||
let object = domain::DomainCreate::new(
|
||||
"example.com",
|
||||
domain::Period::years(2).unwrap(),
|
||||
Some(&ns),
|
||||
Some("jd1234"),
|
||||
"2fooBAR",
|
||||
Some(&contact),
|
||||
);
|
||||
assert_serialized(
|
||||
"request/extensions/secdns_create_ds_key.xml",
|
||||
(&object, &extension),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_key_data_interface() {
|
||||
let key_data = [KeyDataType::new(
|
||||
FLAGS_DNS_ZONE_KEY_SEP,
|
||||
Protocol::Dnssec,
|
||||
Algorithm::RsaMd5,
|
||||
"AQPJ////4Q==",
|
||||
)];
|
||||
let extension = CreateData::from(key_data.as_ref());
|
||||
let ns = [
|
||||
domain::HostInfo::Obj(domain::HostObj {
|
||||
name: "ns1.example.com".into(),
|
||||
}),
|
||||
domain::HostInfo::Obj(domain::HostObj {
|
||||
name: "ns2.example.com".into(),
|
||||
}),
|
||||
];
|
||||
let contact = [
|
||||
domain::DomainContact {
|
||||
contact_type: "admin".into(),
|
||||
id: "sh8013".into(),
|
||||
},
|
||||
domain::DomainContact {
|
||||
contact_type: "tech".into(),
|
||||
id: "sh8013".into(),
|
||||
},
|
||||
];
|
||||
let object = domain::DomainCreate::new(
|
||||
"example.com",
|
||||
domain::Period::years(2).unwrap(),
|
||||
Some(&ns),
|
||||
Some("jd1234"),
|
||||
"2fooBAR",
|
||||
Some(&contact),
|
||||
);
|
||||
assert_serialized(
|
||||
"request/extensions/secdns_create_key.xml",
|
||||
(&object, &extension),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -64,6 +64,7 @@ pub mod extensions {
|
|||
|
||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:rgp-1.0";
|
||||
}
|
||||
pub mod secdns;
|
||||
|
||||
pub mod frnic;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example.com</name>
|
||||
<period unit="y">2</period>
|
||||
<ns>
|
||||
<hostObj>ns1.example.com</hostObj>
|
||||
<hostObj>ns2.example.com</hostObj>
|
||||
</ns>
|
||||
<registrant>jd1234</registrant>
|
||||
<contact type="admin">sh8013</contact>
|
||||
<contact type="tech">sh8013</contact>
|
||||
<authInfo>
|
||||
<pw>2fooBAR</pw>
|
||||
</authInfo>
|
||||
</create>
|
||||
</create>
|
||||
<extension>
|
||||
<create xmlns="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
<maxSigLife>604800</maxSigLife>
|
||||
<dsData>
|
||||
<keyTag>12345</keyTag>
|
||||
<alg>3</alg>
|
||||
<digestType>1</digestType>
|
||||
<digest>49FD46E6C4B45C55D4AC</digest>
|
||||
</dsData>
|
||||
</create>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example.com</name>
|
||||
<period unit="y">2</period>
|
||||
<ns>
|
||||
<hostObj>ns1.example.com</hostObj>
|
||||
<hostObj>ns2.example.com</hostObj>
|
||||
</ns>
|
||||
<registrant>jd1234</registrant>
|
||||
<contact type="admin">sh8013</contact>
|
||||
<contact type="tech">sh8013</contact>
|
||||
<authInfo>
|
||||
<pw>2fooBAR</pw>
|
||||
</authInfo>
|
||||
</create>
|
||||
</create>
|
||||
<extension>
|
||||
<create xmlns="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
<maxSigLife>604800</maxSigLife>
|
||||
<dsData>
|
||||
<keyTag>12345</keyTag>
|
||||
<alg>3</alg>
|
||||
<digestType>1</digestType>
|
||||
<digest>49FD46E6C4B45C55D4AC</digest>
|
||||
<keyData>
|
||||
<flags>257</flags>
|
||||
<protocol>3</protocol>
|
||||
<alg>3</alg>
|
||||
<pubKey>AQPJ////4Q==</pubKey>
|
||||
</keyData>
|
||||
</dsData>
|
||||
</create>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example.com</name>
|
||||
<period unit="y">2</period>
|
||||
<ns>
|
||||
<hostObj>ns1.example.com</hostObj>
|
||||
<hostObj>ns2.example.com</hostObj>
|
||||
</ns>
|
||||
<registrant>jd1234</registrant>
|
||||
<contact type="admin">sh8013</contact>
|
||||
<contact type="tech">sh8013</contact>
|
||||
<authInfo>
|
||||
<pw>2fooBAR</pw>
|
||||
</authInfo>
|
||||
</create>
|
||||
</create>
|
||||
<extension>
|
||||
<create xmlns="urn:ietf:params:xml:ns:secDNS-1.1">
|
||||
<keyData>
|
||||
<flags>257</flags>
|
||||
<protocol>3</protocol>
|
||||
<alg>1</alg>
|
||||
<pubKey>AQPJ////4Q==</pubKey>
|
||||
</keyData>
|
||||
</create>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
Loading…
Reference in New Issue