diff --git a/src/acme.rs b/src/acme.rs index 4aed16b..d913676 100644 --- a/src/acme.rs +++ b/src/acme.rs @@ -4,11 +4,10 @@ use std::collections::BTreeMap; use acme_lib::{Directory, create_p256_key, create_p384_key, create_rsa_key}; use acme_lib::persist::FilePersist; use rust_util::XResult; -use aliyun_openapi_core_rust_sdk::RPClient; use crate::util::parse_dns_record; use crate::network::{get_resolver, resolve_first_ipv4}; -use crate::ali_dns::{add_txt_dns_record, build_dns_client, delete_dns_record, list_dns, simple_parse_aliyun_supplier}; use crate::config::{AcmeChallenge, AcmeMode}; +use crate::dns::{DnsClient, DnsClientFactory, DnsRecord}; use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo}; @@ -64,9 +63,9 @@ pub fn request_acme_certificate(acme_request: AcmeRequest, dns_cleaned_domains: let dir = opt_result!(Directory::from_url(persist, url), "Create directory from url failed: {}"); let acc = opt_result!(dir.account(acme_request.contract_email), "Directory set account failed: {}"); let mut ord_new = opt_result!( acc.new_order(acme_request.primary_name, acme_request.alt_names), "Create order failed: {}"); - let ali_yun_client: Option = match acme_request.credential_supplier { - Some(credential_supplier) => Some(build_dns_client( - &opt_result!(simple_parse_aliyun_supplier(credential_supplier), "Parse credential supplier failed: {}"))), + let mut dns_client: Option> = match acme_request.credential_supplier { + Some(credential_supplier) => Some( + opt_result!(DnsClientFactory::build(credential_supplier), "Build dns client failed: {}")), None => None, }; @@ -107,18 +106,16 @@ pub fn request_acme_certificate(acme_request: AcmeRequest, dns_cleaned_domains: if !dns_cleaned_domains.contains(&rr_and_domain.1) { information!("Clearing domain: {}", &rr_and_domain.1); dns_cleaned_domains.push(rr_and_domain.1.clone()); - ali_yun_client.as_ref().map(|client| { - match list_dns(client, &rr_and_domain.1) { + dns_client.as_mut().map(|client| { + match client.list_dns(&rr_and_domain.1) { Err(e) => warning!("List dns for: {}, failed: {}", &rr_and_domain.1, e), - Ok(Err(e)) => warning!("List dns for: {}, failed: {:?}", &rr_and_domain.1, e), - Ok(Ok(s)) => { - for r in &s.domain_records.record { + Ok(records) => { + for r in &records { let rr = &r.rr; if rr == "_acme-challenge" || rr.starts_with("_acme-challenge.") { - match delete_dns_record(client, &r.record_id) { - Err(e) => warning!("Delete dns: {}.{}, failed: {}", r.rr, r.domain_name, e), - Ok(Err(e)) => warning!("Delete dns: {}.{}, failed: {:?}", r.rr, r.domain_name, e), - Ok(Ok(_)) => success!("Delete dns: {}.{}", r.rr, r.domain_name), + match client.delete_dns_record(&r.id) { + Err(e) => warning!("Delete dns: {}.{}, failed: {}", r.rr, r.domain, e), + Ok(_) => success!("Delete dns: {}.{}", r.rr, r.domain), } } } @@ -127,13 +124,18 @@ pub fn request_acme_certificate(acme_request: AcmeRequest, dns_cleaned_domains: }); } - match &ali_yun_client { + match &mut dns_client { Some(client) => { - let add_txt_dns_result = opt_result!(add_txt_dns_record(client, &rr_and_domain.1, &rr_and_domain.0, &proof), "Add DNS TXT record failed: {}"); - match add_txt_dns_result { - Ok(s) => success!("Add dns txt record successes: {}", s.record_id), - Err(e) => return simple_error!("Add dns txt record failed: {:?}", e), - } + let dns_record = DnsRecord { + id: String::new(), + domain: rr_and_domain.1, + rr: rr_and_domain.0, + r#type: "TXT".into(), + ttl: -1, + value: proof, + }; + let _ = opt_result!(client.add_dns_record(&dns_record), "Add DNS TXT record failed: {}"); + success!("Add dns txt record successes: {}.{} -> {}", dns_record.rr, dns_record.domain, dns_record.value); } None => if acme_request.allow_interact { let mut line = String::new(); diff --git a/src/ali_dns.rs b/src/ali_dns.rs index ab72274..df5dcc5 100644 --- a/src/ali_dns.rs +++ b/src/ali_dns.rs @@ -1,10 +1,53 @@ use rust_util::XResult; use serde::{Deserialize, Serialize}; use aliyun_openapi_core_rust_sdk::RPClient; +use crate::dns::DnsClient; static ALI_DNS_ENDPOINT: &str = "https://alidns.aliyuncs.com"; static ALI_DNS_API_VERSION: &str = "2015-01-09"; +pub struct AlibabaCloudDnsClient { + client: RPClient, +} + +impl AlibabaCloudDnsClient { + pub fn build(supplier: &str) -> XResult { + let access_credential = simple_parse_aliyun_supplier(supplier)?; + Ok(AlibabaCloudDnsClient { + client: build_dns_client(&access_credential) + }) + } +} + +impl DnsClient for AlibabaCloudDnsClient { + fn list_dns(&mut self, domain: &str) -> XResult> { + let list_dns_response = opt_result!(list_dns(&self.client, domain)?, "List dns records failed: {:?}"); + let mut dns_records = vec![]; + list_dns_response.domain_records.record.into_iter().for_each(|record| { + dns_records.push(crate::dns::DnsRecord { + id: record.record_id, + domain: record.domain_name, + rr: record.rr, + r#type: record.r#type, + ttl: record.ttl, + value: record.value, + }); + }); + Ok(dns_records) + } + + fn delete_dns_record(&mut self, record_id: &str) -> XResult<()> { + opt_result!(delete_dns_record(&self.client, record_id)?, "Delete dns record failed: {:?}"); + Ok(()) + } + + fn add_dns_record(&mut self, dns_record: &crate::dns::DnsRecord) -> XResult<()> { + let _ = opt_result!(add_dns_record(&self.client, &dns_record.domain, &dns_record.rr, &dns_record.r#type, &dns_record.value), + "Add dns record failed: {}"); + Ok(()) + } +} + #[derive(Debug)] pub struct AccessCredential { access_key_id: String, @@ -111,9 +154,9 @@ pub fn delete_dns_record(client: &RPClient, record_id: &str) -> XResult XResult> { - add_dns_record(client, domain, rr, "TXT", value) -} +// pub fn add_txt_dns_record(client: &RPClient, domain: &str, rr: &str, value: &str) -> XResult> { +// add_dns_record(client, domain, rr, "TXT", value) +// } // domain -> "example.com" // rr -> "@", "_acme-challenge" diff --git a/src/dns.rs b/src/dns.rs new file mode 100644 index 0000000..cab6882 --- /dev/null +++ b/src/dns.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; +use rust_util::XResult; +use crate::ali_dns::AlibabaCloudDnsClient; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DnsRecord { + pub id: String, + pub domain: String, + pub rr: String, + pub r#type: String, + pub ttl: i32, + pub value: String, +} + +pub trait DnsClient { + fn list_dns(&mut self, domain: &str) -> XResult>; + + fn delete_dns_record(&mut self, record_id: &str) -> XResult<()>; + + fn add_dns_record(&mut self, dns_record: &DnsRecord) -> XResult<()>; +} + +pub struct DnsClientFactory {} + +impl DnsClientFactory { + pub fn build(supplier: &str) -> XResult> { + Ok(Box::new(AlibabaCloudDnsClient::build(supplier)?)) + //simple_error!("Build dns client failed: {}", supplier) + } +} diff --git a/src/main.rs b/src/main.rs index 53ca1e7..bd4578a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ mod x509; mod network; mod statics; mod dingtalk; +mod dns; mod ali_dns; // mod simple_thread_pool;