Minimize configuration interface API
This commit is contained in:
parent
375a151851
commit
62645eec96
|
@ -12,7 +12,6 @@ celes = "2.1"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
quick-xml = { version = "0.22", features = [ "serialize" ] }
|
quick-xml = { version = "0.22", features = [ "serialize" ] }
|
||||||
rustls = "0.20"
|
rustls = "0.20"
|
||||||
rustls-pemfile = "0.2"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tokio = { version = "1.0", features = [ "full" ] }
|
tokio = { version = "1.0", features = [ "full" ] }
|
||||||
tokio-rustls = "0.23"
|
tokio-rustls = "0.23"
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use std::collections::HashMap;
|
//! use std::collections::HashMap;
|
||||||
|
//! use std::net::ToSocketAddrs;
|
||||||
//!
|
//!
|
||||||
//! use epp_client::config::{EppClientConfig, RegistryConfig};
|
|
||||||
//! use epp_client::EppClient;
|
//! use epp_client::EppClient;
|
||||||
//! use epp_client::domain::check::DomainCheck;
|
//! use epp_client::domain::check::DomainCheck;
|
||||||
//! use epp_client::common::NoExtension;
|
//! use epp_client::common::NoExtension;
|
||||||
|
@ -13,20 +13,10 @@
|
||||||
//! #[tokio::main]
|
//! #[tokio::main]
|
||||||
//! async fn main() {
|
//! async fn main() {
|
||||||
//!
|
//!
|
||||||
//! // Create a config
|
//! // Create an instance of EppClient
|
||||||
//! let mut registry: HashMap<String, RegistryConfig> = HashMap::new();
|
//! let host = "example.com";
|
||||||
//! registry.insert(
|
//! let addr = (host, 7000).to_socket_addrs().unwrap().next().unwrap();
|
||||||
//! "registry_name".to_owned(),
|
//! let mut client = match EppClient::new("registry_name".to_string(), addr, host, None).await {
|
||||||
//! 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 {
|
|
||||||
//! Ok(client) => client,
|
//! Ok(client) => client,
|
||||||
//! Err(e) => panic!("Failed to create EppClient: {}", e)
|
//! Err(e) => panic!("Failed to create EppClient: {}", e)
|
||||||
//! };
|
//! };
|
||||||
|
@ -44,9 +34,9 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use crate::common::NoExtension;
|
use crate::common::{Certificate, NoExtension, PrivateKey};
|
||||||
use crate::config::EppClientConfig;
|
|
||||||
use crate::connection::EppConnection;
|
use crate::connection::EppConnection;
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::hello::{Greeting, GreetingDocument, HelloDocument};
|
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
|
/// Creates a new EppClient object and does an EPP Login to a given registry to become ready
|
||||||
/// for subsequent transactions on this client instance
|
/// for subsequent transactions on this client instance
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
config: &EppClientConfig,
|
registry: String,
|
||||||
registry: &str,
|
addr: SocketAddr,
|
||||||
) -> Result<EppClient, Box<dyn Error>> {
|
hostname: &str,
|
||||||
let registry_creds = match config.registry(registry) {
|
identity: Option<(Vec<Certificate>, PrivateKey)>,
|
||||||
Some(creds) => creds,
|
) -> Result<Self, Box<dyn Error>> {
|
||||||
None => return Err(format!("missing credentials for {}", registry).into()),
|
Ok(Self {
|
||||||
};
|
connection: EppConnection::connect(registry, addr, hostname, identity).await?,
|
||||||
|
|
||||||
Ok(EppClient {
|
|
||||||
connection: EppConnection::connect(registry.to_string(), registry_creds).await?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<u8>);
|
||||||
|
|
||||||
|
/// 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<u8>);
|
||||||
|
|
|
@ -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<String, RegistryConfig> = 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<String, RegistryConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<EppClientTlsFiles>,
|
|
||||||
}
|
|
|
@ -1,20 +1,17 @@
|
||||||
//! Manages registry connections and reading/writing to them
|
//! Manages registry connections and reading/writing to them
|
||||||
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fs::File;
|
use std::net::SocketAddr;
|
||||||
use std::io::{BufReader, Seek, SeekFrom};
|
|
||||||
use std::sync::Arc;
|
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 std::{io, str, u32};
|
||||||
|
|
||||||
use rustls::{Certificate, PrivateKey};
|
|
||||||
use rustls::{OwnedTrustAnchor, RootCertStore};
|
use rustls::{OwnedTrustAnchor, RootCertStore};
|
||||||
use rustls_pemfile;
|
|
||||||
use tokio::{io::AsyncReadExt, io::AsyncWriteExt, net::TcpStream};
|
use tokio::{io::AsyncReadExt, io::AsyncWriteExt, net::TcpStream};
|
||||||
use tokio_rustls::{client::TlsStream, rustls::ClientConfig, TlsConnector};
|
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;
|
use crate::error;
|
||||||
|
|
||||||
/// EPP Connection struct with some metadata for the connection
|
/// 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
|
/// Create an EppConnection instance with the stream to the registry
|
||||||
pub(crate) async fn connect(
|
pub(crate) async fn connect(
|
||||||
registry: String,
|
registry: String,
|
||||||
config: &RegistryConfig,
|
addr: SocketAddr,
|
||||||
|
hostname: &str,
|
||||||
|
identity: Option<(Vec<Certificate>, PrivateKey)>,
|
||||||
) -> Result<EppConnection, Box<dyn Error>> {
|
) -> Result<EppConnection, Box<dyn Error>> {
|
||||||
let mut stream = epp_connect(config).await?;
|
let mut stream = epp_connect(addr, hostname, identity).await?;
|
||||||
|
|
||||||
let mut buf = vec![0u8; 4096];
|
let mut buf = vec![0u8; 4096];
|
||||||
stream.read(&mut buf).await?;
|
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
|
/// Establishes a TLS connection to a registry and returns a ConnectionStream instance containing the
|
||||||
/// socket stream to read/write to the connection
|
/// socket stream to read/write to the connection
|
||||||
async fn epp_connect(
|
async fn epp_connect(
|
||||||
registry_creds: &RegistryConfig,
|
addr: SocketAddr,
|
||||||
|
hostname: &str,
|
||||||
|
identity: Option<(Vec<Certificate>, PrivateKey)>,
|
||||||
) -> Result<TlsStream<TcpStream>, error::Error> {
|
) -> Result<TlsStream<TcpStream>, error::Error> {
|
||||||
info!(
|
info!("Connecting to server: {:?}", addr,);
|
||||||
"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)?;
|
|
||||||
|
|
||||||
let mut roots = RootCertStore::empty();
|
let mut roots = RootCertStore::empty();
|
||||||
roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
|
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_safe_defaults()
|
||||||
.with_root_certificates(roots);
|
.with_root_certificates(roots);
|
||||||
|
|
||||||
let config = match ®istry_creds.tls_files {
|
let config = match identity {
|
||||||
Some(files) => {
|
Some((certs, key)) => {
|
||||||
let (certs_file, key_file) = (&files.cert_chain, &files.key);
|
let certs = certs
|
||||||
let certs = rustls_pemfile::certs(&mut BufReader::new(File::open(certs_file)?))?
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Certificate)
|
.map(|cert| rustls::Certificate(cert.0))
|
||||||
.collect::<Vec<_>>();
|
.collect();
|
||||||
|
builder
|
||||||
let mut key;
|
.with_single_cert(certs, rustls::PrivateKey(key.0))
|
||||||
let mut r = BufReader::new(File::open(key_file).unwrap());
|
.map_err(|e| error::Error::Other(e.to_string()))?
|
||||||
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(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => builder.with_no_client_auth(),
|
None => builder.with_no_client_auth(),
|
||||||
};
|
};
|
||||||
|
@ -188,10 +155,10 @@ async fn epp_connect(
|
||||||
let connector = TlsConnector::from(Arc::new(config));
|
let connector = TlsConnector::from(Arc::new(config));
|
||||||
let stream = TcpStream::connect(&addr).await?;
|
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::Error::new(
|
||||||
stdio::ErrorKind::InvalidInput,
|
stdio::ErrorKind::InvalidInput,
|
||||||
format!("Invalid domain: {}", registry_creds.host),
|
format!("Invalid domain: {}", hostname),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
21
src/lib.rs
21
src/lib.rs
|
@ -40,8 +40,8 @@
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use std::collections::HashMap;
|
//! use std::collections::HashMap;
|
||||||
|
//! use std::net::ToSocketAddrs;
|
||||||
//!
|
//!
|
||||||
//! use epp_client::config::{EppClientConfig, RegistryConfig};
|
|
||||||
//! use epp_client::EppClient;
|
//! use epp_client::EppClient;
|
||||||
//! use epp_client::domain::check::DomainCheck;
|
//! use epp_client::domain::check::DomainCheck;
|
||||||
//! use epp_client::common::NoExtension;
|
//! use epp_client::common::NoExtension;
|
||||||
|
@ -51,20 +51,10 @@
|
||||||
//! #[tokio::main]
|
//! #[tokio::main]
|
||||||
//! async fn main() {
|
//! async fn main() {
|
||||||
//!
|
//!
|
||||||
//! // Create a config
|
//! // Create an instance of EppClient
|
||||||
//! let mut registry: HashMap<String, RegistryConfig> = HashMap::new();
|
//! let host = "example.com";
|
||||||
//! registry.insert(
|
//! let addr = (host, 7000).to_socket_addrs().unwrap().next().unwrap();
|
||||||
//! "registry_name".to_owned(),
|
//! let mut client = match EppClient::new("registry_name".to_string(), addr, host, None).await {
|
||||||
//! 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 {
|
|
||||||
//! Ok(client) => client,
|
//! Ok(client) => client,
|
||||||
//! Err(e) => panic!("Failed to create EppClient: {}", e)
|
//! Err(e) => panic!("Failed to create EppClient: {}", e)
|
||||||
//! };
|
//! };
|
||||||
|
@ -99,7 +89,6 @@
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod config;
|
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod hello;
|
pub mod hello;
|
||||||
|
|
Loading…
Reference in New Issue