diff --git a/Cargo.toml b/Cargo.toml index 7601057..5b13847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ celes = "2.1" chrono = "0.4" quick-xml = { version = "0.22", features = [ "serialize" ] } rustls = "0.20" -rustls-pemfile = "0.2" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = [ "full" ] } tokio-rustls = "0.23" diff --git a/src/client.rs b/src/client.rs index 600a211..93ef4fa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,8 +4,8 @@ //! //! ```no_run //! use std::collections::HashMap; +//! use std::net::ToSocketAddrs; //! -//! use epp_client::config::{EppClientConfig, RegistryConfig}; //! use epp_client::EppClient; //! use epp_client::domain::check::DomainCheck; //! use epp_client::common::NoExtension; @@ -13,20 +13,10 @@ //! #[tokio::main] //! async fn main() { //! -//! // Create a config -//! let mut registry: HashMap = HashMap::new(); -//! registry.insert( -//! "registry_name".to_owned(), -//! RegistryConfig { -//! host: "example.com".to_owned(), -//! port: 700, -//! tls_files: None, -//! }, -//! ); -//! let config = EppClientConfig { registry }; -//! -//! // Create an instance of EppClient, passing the config and the registry you want to connect to -//! let mut client = match EppClient::new(&config, "registry_name").await { +//! // Create an instance of EppClient +//! let host = "example.com"; +//! let addr = (host, 7000).to_socket_addrs().unwrap().next().unwrap(); +//! let mut client = match EppClient::new("registry_name".to_string(), addr, host, None).await { //! Ok(client) => client, //! Err(e) => panic!("Failed to create EppClient: {}", e) //! }; @@ -44,9 +34,9 @@ //! ``` use std::error::Error; +use std::net::SocketAddr; -use crate::common::NoExtension; -use crate::config::EppClientConfig; +use crate::common::{Certificate, NoExtension, PrivateKey}; use crate::connection::EppConnection; use crate::error; use crate::hello::{Greeting, GreetingDocument, HelloDocument}; @@ -65,16 +55,13 @@ impl EppClient { /// Creates a new EppClient object and does an EPP Login to a given registry to become ready /// for subsequent transactions on this client instance pub async fn new( - config: &EppClientConfig, - registry: &str, - ) -> Result> { - let registry_creds = match config.registry(registry) { - Some(creds) => creds, - None => return Err(format!("missing credentials for {}", registry).into()), - }; - - Ok(EppClient { - connection: EppConnection::connect(registry.to_string(), registry_creds).await?, + registry: String, + addr: SocketAddr, + hostname: &str, + identity: Option<(Vec, PrivateKey)>, + ) -> Result> { + Ok(Self { + connection: EppConnection::connect(registry, addr, hostname, identity).await?, }) } diff --git a/src/common.rs b/src/common.rs index 9d5d0a2..e59beba 100644 --- a/src/common.rs +++ b/src/common.rs @@ -358,3 +358,13 @@ impl PostalInfo { } } } + +/// This type contains a single DER-encoded X.509 certificate. +/// +/// The rustls-pemfile crate can be used to parse a PEM file. +pub struct Certificate(pub Vec); + +/// This type contains a DER-encoded ASN.1 private key in PKCS#8 or PKCS#1 format. +/// +/// The rustls-pemfile crate can be used to parse a PEM file in these formats. +pub struct PrivateKey(pub Vec); diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 2ba7fb8..0000000 --- a/src/config.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Config -//! -//! This module contains the connection configuration for the EPP client. -//! -//! ## Example -//! -//! ```no_run -//! use std::collections::HashMap; -//! -//! use epp_client::config::{EppClientConfig, RegistryConfig}; -//! -//! // Create a config -//! let mut registry: HashMap = HashMap::new(); -//! registry.insert( -//! "registry_name".to_owned(), -//! RegistryConfig { -//! host: "example.com".to_owned(), -//! port: 700, -//! tls_files: None, -//! }, -//! ); -//! let config = EppClientConfig { registry }; -//! -//! // Get configuration for the relevant registry section -//! let registry = config.registry("verisign").unwrap(); -//! ``` - -use std::collections::HashMap; -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; - -/// Paths to the client certificate and client key PEM files -#[derive(Serialize, Deserialize, Debug)] -pub struct EppClientTlsFiles { - pub cert_chain: PathBuf, - pub key: PathBuf, -} - -/// Config that stores settings for multiple registries -#[derive(Serialize, Deserialize, Debug)] -pub struct EppClientConfig { - pub registry: HashMap, -} - -impl EppClientConfig { - /// Returns the config for a particular registry - pub fn registry(&self, registry: &str) -> Option<&RegistryConfig> { - self.registry.get(registry) - } -} - -/// Connection details to connect to and authenticate with a registry -#[derive(Serialize, Deserialize, Debug)] -pub struct RegistryConfig { - pub host: String, - pub port: u16, - pub tls_files: Option, -} diff --git a/src/connection.rs b/src/connection.rs index 9360623..0d995d5 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,20 +1,17 @@ //! Manages registry connections and reading/writing to them use std::convert::TryInto; -use std::fs::File; -use std::io::{BufReader, Seek, SeekFrom}; +use std::net::SocketAddr; use std::sync::Arc; -use std::{error::Error, io as stdio, net::ToSocketAddrs}; +use std::{error::Error, io as stdio}; use std::{io, str, u32}; -use rustls::{Certificate, PrivateKey}; use rustls::{OwnedTrustAnchor, RootCertStore}; -use rustls_pemfile; use tokio::{io::AsyncReadExt, io::AsyncWriteExt, net::TcpStream}; use tokio_rustls::{client::TlsStream, rustls::ClientConfig, TlsConnector}; -use tracing::{debug, info, warn}; +use tracing::{debug, info}; -use crate::config::RegistryConfig; +use crate::common::{Certificate, PrivateKey}; use crate::error; /// EPP Connection struct with some metadata for the connection @@ -28,9 +25,11 @@ impl EppConnection { /// Create an EppConnection instance with the stream to the registry pub(crate) async fn connect( registry: String, - config: &RegistryConfig, + addr: SocketAddr, + hostname: &str, + identity: Option<(Vec, PrivateKey)>, ) -> Result> { - let mut stream = epp_connect(config).await?; + let mut stream = epp_connect(addr, hostname, identity).await?; let mut buf = vec![0u8; 4096]; stream.read(&mut buf).await?; @@ -121,17 +120,11 @@ impl EppConnection { /// Establishes a TLS connection to a registry and returns a ConnectionStream instance containing the /// socket stream to read/write to the connection async fn epp_connect( - registry_creds: &RegistryConfig, + addr: SocketAddr, + hostname: &str, + identity: Option<(Vec, PrivateKey)>, ) -> Result, error::Error> { - info!( - "Connecting: EPP Server: {} Port: {}", - registry_creds.host, registry_creds.port - ); - - let addr = (registry_creds.host.as_str(), registry_creds.port) - .to_socket_addrs()? - .next() - .ok_or(stdio::ErrorKind::NotFound)?; + info!("Connecting to server: {:?}", addr,); let mut roots = RootCertStore::empty(); roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { @@ -146,41 +139,15 @@ async fn epp_connect( .with_safe_defaults() .with_root_certificates(roots); - let config = match ®istry_creds.tls_files { - Some(files) => { - let (certs_file, key_file) = (&files.cert_chain, &files.key); - let certs = rustls_pemfile::certs(&mut BufReader::new(File::open(certs_file)?))? + let config = match identity { + Some((certs, key)) => { + let certs = certs .into_iter() - .map(Certificate) - .collect::>(); - - let mut key; - let mut r = BufReader::new(File::open(key_file).unwrap()); - let mut rsa_keys = rustls_pemfile::rsa_private_keys(&mut r).unwrap(); - if rsa_keys.len() > 1 { - warn!("Multiple RSA keys found in PEM file {:?}", key_file); - } - key = rsa_keys.pop(); - - if key.is_none() { - r.seek(SeekFrom::Start(0))?; - let mut pkcs8_keys = rustls_pemfile::pkcs8_private_keys(&mut r).unwrap(); - if pkcs8_keys.len() > 1 { - warn!("Multiple PKCS8 keys found in PEM file {:?}", key_file); - } - key = pkcs8_keys.pop(); - } - - match key { - Some(key) => builder - .with_single_cert(certs, PrivateKey(key)) - .map_err(|e| error::Error::Other(e.to_string()))?, - None => { - return Err(error::Error::Other( - "No private key found in PEM file".to_owned(), - )) - } - } + .map(|cert| rustls::Certificate(cert.0)) + .collect(); + builder + .with_single_cert(certs, rustls::PrivateKey(key.0)) + .map_err(|e| error::Error::Other(e.to_string()))? } None => builder.with_no_client_auth(), }; @@ -188,10 +155,10 @@ async fn epp_connect( let connector = TlsConnector::from(Arc::new(config)); let stream = TcpStream::connect(&addr).await?; - let domain = registry_creds.host.as_str().try_into().map_err(|_| { + let domain = hostname.try_into().map_err(|_| { stdio::Error::new( stdio::ErrorKind::InvalidInput, - format!("Invalid domain: {}", registry_creds.host), + format!("Invalid domain: {}", hostname), ) })?; diff --git a/src/lib.rs b/src/lib.rs index 122d571..11ee632 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,8 +40,8 @@ //! //! ```no_run //! use std::collections::HashMap; +//! use std::net::ToSocketAddrs; //! -//! use epp_client::config::{EppClientConfig, RegistryConfig}; //! use epp_client::EppClient; //! use epp_client::domain::check::DomainCheck; //! use epp_client::common::NoExtension; @@ -51,20 +51,10 @@ //! #[tokio::main] //! async fn main() { //! -//! // Create a config -//! let mut registry: HashMap = HashMap::new(); -//! registry.insert( -//! "registry_name".to_owned(), -//! RegistryConfig { -//! host: "example.com".to_owned(), -//! port: 700, -//! tls_files: None, -//! }, -//! ); -//! let config = EppClientConfig { registry }; -//! -//! // Create an instance of EppClient, passing the config and the registry you want to connect to -//! let mut client = match EppClient::new(&config, "registry_name").await { +//! // Create an instance of EppClient +//! let host = "example.com"; +//! let addr = (host, 7000).to_socket_addrs().unwrap().next().unwrap(); +//! let mut client = match EppClient::new("registry_name".to_string(), addr, host, None).await { //! Ok(client) => client, //! Err(e) => panic!("Failed to create EppClient: {}", e) //! }; @@ -99,7 +89,6 @@ pub mod client; pub mod common; -pub mod config; pub mod connection; pub mod error; pub mod hello;