diff --git a/epp-client/Cargo.toml b/epp-client/Cargo.toml index 7e6fea3..c71d90c 100644 --- a/epp-client/Cargo.toml +++ b/epp-client/Cargo.toml @@ -15,6 +15,7 @@ bytes = "1" chrono = "0.4" confy = "0.4" futures = "0.3" +env_logger = "0.9" log = "0.4" lazy_static = "1.4" quick-xml = { version = "0.22", features = [ "serialize" ] } diff --git a/epp-client/examples/client.rs b/epp-client/examples/client.rs index bd2ae9a..fb3e35e 100644 --- a/epp-client/examples/client.rs +++ b/epp-client/examples/client.rs @@ -1,5 +1,6 @@ use std::{error::Error, time::SystemTime}; use chrono::NaiveDate; +use env_logger; use epp_client::EppClient; use epp_client::epp::object::{StringValueTrait}; @@ -41,8 +42,6 @@ async fn create_contact(client: &mut EppClient) { let mut contact_create = EppContactCreate::new("eppdev-contact-3", "contact@eppdev.net", postal_info, voice, "eppdev-387323", gen_client_tr_id("eppdev").unwrap().as_str()); contact_create.set_fax(fax); - // println!("xml: {}", contact_create.serialize().unwrap()); - client.transact::<_, EppContactCreateResponse>(&contact_create).await.unwrap(); } @@ -56,8 +55,6 @@ async fn update_contact(client: &mut EppClient) { let add_statuses = vec![ContactStatus { status: "clientTransferProhibited".to_string() }]; contact_update.remove_statuses(add_statuses); - // println!("{}", contact_update.serialize().unwrap()); - client.transact::<_, EppContactUpdateResponse>(&contact_update).await.unwrap(); } @@ -92,8 +89,6 @@ async fn create_domain(client: &mut EppClient) { let domain_create = EppDomainCreate::new("eppdev-1.com", 1, "eppdev-contact-2", "epP4uthd#v", contacts, gen_client_tr_id("eppdev").unwrap().as_str()); - // println!("{}", domain_create.serialize().unwrap()); - client.transact::<_, EppDomainCreateResponse>(&domain_create).await.unwrap(); } @@ -145,8 +140,6 @@ async fn update_domain(client: &mut EppClient) { domain_update.add(add); domain_update.remove(remove); - // println!("{}", domain_update.serialize().unwrap()); - client.transact::<_, EppDomainUpdateResponse>(&domain_update).await.unwrap(); } @@ -211,16 +204,12 @@ async fn create_host(client: &mut EppClient) { let host_create = EppHostCreate::new(host, gen_client_tr_id("eppdev").unwrap().as_str()); - // println!("{}", host_create.serialize().unwrap()); - client.transact::<_, EppHostCreateResponse>(&host_create).await.unwrap(); } async fn query_host(client: &mut EppClient) { let host_info = EppHostInfo::new("host2.eppdev-1.com", gen_client_tr_id("eppdev").unwrap().as_str()); - // println!("{}", host_info.serialize().unwrap()); - client.transact::<_, EppHostInfoResponse>(&host_info).await.unwrap(); } @@ -249,8 +238,6 @@ async fn update_host(client: &mut EppClient) { // host_update.remove(remove); host_update.info(HostChangeInfo { name: "host2.eppdev-1.com".to_string_value() }); - // println!("{}", host_update.serialize().unwrap()); - client.transact::<_, EppHostUpdateResponse>(&host_update).await.unwrap(); } @@ -263,8 +250,6 @@ async fn delete_host(client: &mut EppClient) { async fn poll_message(client: &mut EppClient) { let message_poll = EppMessagePoll::new(gen_client_tr_id("eppdev").unwrap().as_str()); - // println!("{}", message_poll.serialize().unwrap()); - client.transact::<_, EppMessagePollResponse>(&message_poll).await.unwrap(); } @@ -284,11 +269,10 @@ async fn hello(client: &mut EppClient) { #[tokio::main] async fn main() { + env_logger::init(); + let mut client = match EppClient::new("verisign").await { - Ok(client) => { - println!("{:?}", client.greeting()); - client - }, + Ok(client) => client, Err(e) => panic!("Error: {}", e) }; @@ -296,7 +280,7 @@ async fn main() { // hello(&mut client).await; - let response = check_domains(&mut client).await; + check_domains(&mut client).await; // check_contacts(&mut client).await; diff --git a/epp-client/src/connection/client.rs b/epp-client/src/connection/client.rs index c0ba848..46b7937 100644 --- a/epp-client/src/connection/client.rs +++ b/epp-client/src/connection/client.rs @@ -5,6 +5,7 @@ //! ```rust //! use epp_client::EppClient; //! use epp_client::epp::{EppDomainCheck, EppDomainCheckResponse}; +//! use epp_client::epp::generate_client_tr_id; //! //! #[tokio::main] //! async fn main() { @@ -20,7 +21,7 @@ //! println!("{:?}", greeting); //! //! // Execute an EPP Command against the registry with distinct request and response objects -//! let domain_check = EppDomainCheck::new(vec!["eppdev.com", "eppdev.net"], "client-trid-12345"); +//! let domain_check = EppDomainCheck::new(vec!["eppdev.com", "eppdev.net"], generate_client_tr_id(&client).as_str()); //! let response = client.transact::<_, EppDomainCheckResponse>(&domain_check).await.unwrap(); //! println!("{:?}", response); //! } @@ -28,7 +29,7 @@ use futures::executor::block_on; use std::{error::Error, fmt::Debug}; -// use std::time::SystemTime; +use std::time::SystemTime; use std::sync::mpsc; // use std::sync::Arc; @@ -39,6 +40,7 @@ use crate::epp::request::{generate_client_tr_id, EppHello, EppLogin, EppLogout}; use crate::epp::response::{EppGreeting, EppCommandResponseStatus, EppCommandResponse, EppCommandResponseError}; use crate::epp::xml::EppXml; +/// Connects to the registry and returns an logged-in instance of EppClient for further transactions async fn connect(registry: &'static str) -> Result> { let registry_creds = match CONFIG.registry(registry) { Some(creds) => creds, @@ -78,9 +80,8 @@ async fn connect(registry: &'static str) -> Result> { } /// Instances of the EppClient type are used to transact with the registry -/// An instance is first created, then various EPP request and response object -/// types are converted to EPP XML to be sent to the registry and the responses -/// from the registry are converted to local types +/// Once initialized, the EppClient instance can serialize EPP requests to XML and send them +/// to the registry and deserialize the XML responses from the registry to local types pub struct EppClient { credentials: (String, String), ext_uris: Option>, @@ -88,13 +89,15 @@ pub struct EppClient { // pub client_tr_id_fn: Arc String + Send + Sync>, } -// fn default_client_tr_id_fn(client: &EppClient) -> String { -// let timestamp = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { -// Ok(time) => time, -// Err(e) => panic!("Error in client TRID gen function: {}", e) -// }; -// format!("{}:{}", &client.username(), timestamp.as_secs()) -// } +/// A function to generate a simple client TRID. Should only be used for testing, library users +/// should generate a client TRID according to their own requirements +pub fn default_client_tr_id_fn(client: &EppClient) -> String { + let timestamp = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + Ok(time) => time, + Err(e) => panic!("Error in client TRID gen function: {}", e) + }; + format!("{}:{}", &client.username(), timestamp.as_secs()) +} impl EppClient { /// Fetches the username used in the registry connection @@ -113,6 +116,7 @@ impl EppClient { connect(registry).await } + /// Makes a login request to the registry and initializes an EppClient instance with it async fn build(connection: EppConnection, credentials: (String, String), ext_uris: Option>) -> Result> { let mut client = EppClient { connection: connection, @@ -144,12 +148,8 @@ impl EppClient { pub async fn transact(&mut self, request: &T) -> Result { let epp_xml = request.serialize()?; - debug!("request: {}", epp_xml); - let response = self.connection.transact(&epp_xml).await?; - debug!("response: {}", response); - let status = EppCommandResponseStatus::deserialize(&response)?; if status.data.result.code < 2000 { @@ -167,17 +167,17 @@ impl EppClient { self.connection.transact(&xml).await } - /// Return the greeting received on establishment of the connection in raw xml form + /// Returns the greeting received on establishment of the connection in raw xml form pub fn xml_greeting(&self) -> String { return String::from(&self.connection.greeting) } - /// Return the greeting received on establishment of the connection as an `EppGreeting` instance + /// Returns the greeting received on establishment of the connection as an `EppGreeting` instance pub fn greeting(&self) -> Result { EppGreeting::deserialize(&self.connection.greeting) } - /// Send the EPP Logout command to log out of the EPP session + /// Sends the EPP Logout command to log out of the EPP session pub async fn logout(&mut self) -> Result { let client_tr_id = generate_client_tr_id(&self.credentials.0).unwrap(); let epp_logout = EppLogout::new(client_tr_id.as_str()); diff --git a/epp-client/src/connection/registry.rs b/epp-client/src/connection/registry.rs index 8a93c9e..acd504a 100644 --- a/epp-client/src/connection/registry.rs +++ b/epp-client/src/connection/registry.rs @@ -44,7 +44,7 @@ impl EppConnection { async fn write(&mut self, buf: &Vec) -> Result<(), Box> { let wrote = self.stream.writer.write(buf).await?; - debug!("Wrote {} bytes", wrote); + debug!("{}: Wrote {} bytes", self.registry, wrote); Ok(()) } @@ -71,7 +71,7 @@ impl EppConnection { let buf_size :usize = u32::from_be_bytes(buf).try_into()?; let message_size = buf_size - 4; - debug!("Message buffer size: {}", message_size); + debug!("{}: Response buffer size: {}", self.registry, message_size); let mut buf = BytesMut::with_capacity(4096); let mut read_buf = vec![0u8; 4096]; @@ -80,14 +80,14 @@ impl EppConnection { loop { let read = self.stream.reader.read(&mut read_buf).await?; - debug!("Read: {} bytes", read); + debug!("{}: Read: {} bytes", self.registry, read); buf.extend_from_slice(&read_buf[0..read]); read_size = read_size + read; - debug!("Total read: {} bytes", read_size); + debug!("{}: Total read: {} bytes", self.registry, read_size); if read == 0 { - panic!("Unexpected eof") + panic!("{}: Unexpected eof", self.registry) } else if read_size >= message_size { break; } @@ -109,13 +109,17 @@ impl EppConnection { /// Send an EPP XML request to the registry and return the response /// receieved to the request pub async fn transact(&mut self, content: &str) -> Result> { + debug!("{}: request: {}", self.registry, content); self.send_epp_request(&content).await?; - self.get_epp_response().await + let response = self.get_epp_response().await?; + debug!("{}: request: {}", self.registry, response); + + Ok(response) } async fn close(&mut self) -> Result<(), Box> { - debug!("Closing {} connection", self.registry); + info!("{}: Closing connection", self.registry); self.stream.writer.shutdown().await?; Ok(()) @@ -133,7 +137,7 @@ impl Drop for EppConnection { pub async fn epp_connect(registry_creds: &EppClientConnection) -> Result { let (host, port) = registry_creds.connection_details(); - debug!("Server: {}\r\nPort: {}", host, port); + info!("Connecting: EPP Server: {} Port: {}", host, port); let addr = (host.as_str(), port) .to_socket_addrs()? diff --git a/epp-client/src/epp.rs b/epp-client/src/epp.rs index 10869c4..64e4ac9 100644 --- a/epp-client/src/epp.rs +++ b/epp-client/src/epp.rs @@ -1,4 +1,3 @@ -pub mod command; pub mod object; pub mod request; pub mod response; @@ -43,3 +42,5 @@ pub use response::host::info::*; pub use response::host::update::*; pub use response::message::ack::*; pub use response::message::poll::*; + +pub use crate::connection::client::default_client_tr_id_fn as generate_client_tr_id; diff --git a/epp-client/src/epp/command.rs b/epp-client/src/epp/command.rs deleted file mode 100644 index 35c0f81..0000000 --- a/epp-client/src/epp/command.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::epp::object::{ElementName, StringValue}; -use epp_client_macros::*; -use serde::ser::{SerializeStruct, Serializer}; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Debug, PartialEq, ElementName)] -#[element_name(name = "command")] -pub struct Command { - pub command: T, - #[serde(rename = "clTRID")] - pub client_tr_id: StringValue, -} - -impl Serialize for Command { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let command_name = self.command.element_name(); - let mut state = serializer.serialize_struct("command", 2)?; - state.serialize_field(command_name, &self.command)?; - state.serialize_field("clTRID", &self.client_tr_id)?; - state.end() - } -} diff --git a/epp-client/src/epp/object.rs b/epp-client/src/epp/object.rs index 3f695b3..87ee3ab 100644 --- a/epp-client/src/epp/object.rs +++ b/epp-client/src/epp/object.rs @@ -1,3 +1,5 @@ +//! Data types common to EPP Requests and Responses + pub mod data; use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; @@ -5,6 +7,8 @@ use std::fmt::Display; use crate::epp::xml::{EPP_XMLNS, EPP_XMLNS_XSI, EPP_XSI_SCHEMA_LOCATION}; +/// Wraps String for easier serialization to and from values that are inner text +/// for tags rather than attributes #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct StringValue(String); @@ -20,6 +24,7 @@ impl Display for StringValue { } } +/// Trait for StringValue type to add easier conversion from str and String pub trait StringValueTrait { fn to_string_value(&self) -> StringValue; } @@ -36,20 +41,28 @@ impl StringValueTrait for String { } } +/// Trait to set correct value for xml tags when tags are being generated from generic types pub trait ElementName { fn element_name(&self) -> &'static str; } +/// An EPP XML Document that is used either as an EPP XML request or +/// an EPP XML response #[derive(Deserialize, Debug, PartialEq)] #[serde(rename = "epp")] pub struct EppObject { + /// XML namespace for the tag pub xmlns: String, + /// Schema namespace for the tag #[serde(rename = "xmlns:xsi")] pub xmlns_xsi: String, + /// Schema location attribute for #[serde(rename = "xsi:schemaLocation")] pub xsi_schema_location: String, + /// the request or response object that is set or received in the EPP XML document #[serde(alias = "greeting", alias = "response")] pub data: T, + // TODO: save serialized xml in the instance for debugging or client logging purposes // #[serde(skip)] // pub xml: Option, } @@ -69,14 +82,18 @@ impl Serialize for EppObject { } } +/// The