Skip to main content

dns_update/
update.rs

1/*
2 * Copyright Stalwart Labs LLC See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
13use crate::Algorithm;
14
15#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
16use hickory_client::proto::dnssec::SigningKey;
17
18#[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
19use crate::providers::ovh::{OvhEndpoint, OvhProvider};
20
21#[cfg(feature = "test_provider")]
22use crate::providers::{in_memory::InMemoryProvider, pebble::PebbleProvider};
23
24#[cfg(feature = "test_provider")]
25use crate::NamedDnsRecord;
26
27#[cfg(feature = "test_provider")]
28use std::sync::{Arc, Mutex};
29
30use crate::{
31    DnsRecord, DnsRecordType, DnsUpdater, IntoFqdn, TsigAlgorithm,
32    providers::{
33        bunny::BunnyProvider,
34        cloudflare::CloudflareProvider,
35        desec::DesecProvider,
36        digitalocean::DigitalOceanProvider,
37        dnsimple::DNSimpleProvider,
38        porkbun::PorkBunProvider,
39        rfc2136::{DnsAddress, Rfc2136Provider},
40        route53::Route53Provider,
41        spaceship::SpaceshipProvider,
42    },
43};
44use std::time::Duration;
45
46impl DnsUpdater {
47    /// Create a new DNS updater using the RFC 2136 protocol and TSIG authentication.
48    pub fn new_rfc2136_tsig(
49        addr: impl TryInto<DnsAddress>,
50        key_name: impl AsRef<str>,
51        key: impl Into<Vec<u8>>,
52        algorithm: TsigAlgorithm,
53    ) -> crate::Result<Self> {
54        Ok(DnsUpdater::Rfc2136(Rfc2136Provider::new_tsig(
55            addr,
56            key_name,
57            key,
58            algorithm.into(),
59        )?))
60    }
61
62    /// Create a new DNS updater using the RFC 2136 protocol and SIG(0) authentication.
63    ///
64    /// **Deprecated:** SIG(0) support will be removed in v0.3.0. Upstream
65    /// `hickory-client` has dropped SIG(0) message authentication in v0.26
66    /// (see hickory-dns/hickory-dns#3437) due to negligible real-world use, and
67    /// every production RFC 2136 endpoint and ACME client uses TSIG. Migrate to
68    /// [`DnsUpdater::new_rfc2136_tsig`].
69    #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
70    #[deprecated(
71        since = "0.2.1",
72        note = "SIG(0) will be removed in v0.3.0, upstream hickory-client v0.26 drops SIG(0) message authentication; use `new_rfc2136_tsig` instead"
73    )]
74    pub fn new_rfc2136_sig0(
75        addr: impl TryInto<DnsAddress>,
76        signer_name: impl AsRef<str>,
77        key: Box<dyn SigningKey>,
78        public_key: impl Into<Vec<u8>>,
79        algorithm: Algorithm,
80    ) -> crate::Result<Self> {
81        Ok(DnsUpdater::Rfc2136(Rfc2136Provider::new_sig0(
82            addr,
83            signer_name,
84            key,
85            public_key,
86            algorithm.into(),
87        )?))
88    }
89
90    /// Create a new DNS updater using the Cloudflare API.
91    pub fn new_cloudflare(
92        secret: impl AsRef<str>,
93        email: Option<impl AsRef<str>>,
94        timeout: Option<Duration>,
95    ) -> crate::Result<Self> {
96        Ok(DnsUpdater::Cloudflare(CloudflareProvider::new(
97            secret, email, timeout,
98        )?))
99    }
100
101    /// Create a new DNS updater using the Cloudflare API.
102    pub fn new_digitalocean(
103        auth_token: impl AsRef<str>,
104        timeout: Option<Duration>,
105    ) -> crate::Result<Self> {
106        Ok(DnsUpdater::DigitalOcean(DigitalOceanProvider::new(
107            auth_token, timeout,
108        )))
109    }
110
111    /// Create a new DNS updater using the Desec.io API.
112    pub fn new_desec(
113        auth_token: impl AsRef<str>,
114        timeout: Option<Duration>,
115    ) -> crate::Result<Self> {
116        Ok(DnsUpdater::Desec(DesecProvider::new(auth_token, timeout)))
117    }
118
119    /// Create a new DNS updater using the OVH API.
120    #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
121    pub fn new_ovh(
122        application_key: impl AsRef<str>,
123        application_secret: impl AsRef<str>,
124        consumer_key: impl AsRef<str>,
125        endpoint: OvhEndpoint,
126        timeout: Option<Duration>,
127    ) -> crate::Result<Self> {
128        Ok(DnsUpdater::Ovh(OvhProvider::new(
129            application_key,
130            application_secret,
131            consumer_key,
132            endpoint,
133            timeout,
134        )?))
135    }
136
137    /// Create a new DNS updater using the Bunny API.
138    pub fn new_bunny(api_key: impl AsRef<str>, timeout: Option<Duration>) -> crate::Result<Self> {
139        Ok(DnsUpdater::Bunny(BunnyProvider::new(api_key, timeout)?))
140    }
141
142    /// Create a new DNS updater using the Porkbun API.
143    pub fn new_porkbun(
144        api_key: impl AsRef<str>,
145        secret_api_key: impl AsRef<str>,
146        timeout: Option<Duration>,
147    ) -> crate::Result<Self> {
148        Ok(DnsUpdater::Porkbun(PorkBunProvider::new(
149            api_key,
150            secret_api_key,
151            timeout,
152        )))
153    }
154
155    /// Create a new DNS updater using the Spaceship API.
156    pub fn new_spaceship(
157        api_key: impl AsRef<str>,
158        api_secret: impl AsRef<str>,
159        timeout: Option<Duration>,
160    ) -> crate::Result<Self> {
161        Ok(DnsUpdater::Spaceship(SpaceshipProvider::new(
162            api_key, api_secret, timeout,
163        )))
164    }
165
166    /// Create a new DNS updater using the DNSimple API.
167    pub fn new_dnsimple(
168        auth_token: impl AsRef<str>,
169        account_id: impl AsRef<str>,
170        timeout: Option<Duration>,
171    ) -> crate::Result<Self> {
172        Ok(DnsUpdater::DNSimple(DNSimpleProvider::new(
173            auth_token, account_id, timeout,
174        )))
175    }
176
177    /// Create a new DNS updater using the Google Cloud DNS API.
178    pub fn new_google_cloud_dns(
179        config: crate::providers::google_cloud_dns::GoogleCloudDnsConfig,
180    ) -> crate::Result<Self> {
181        Ok(DnsUpdater::GoogleCloudDns(
182            crate::providers::google_cloud_dns::GoogleCloudDnsProvider::new(config)?,
183        ))
184    }
185
186    /// Create a new DNS updater using the Route53 API.
187    pub fn new_route53(config: crate::providers::route53::Route53Config) -> crate::Result<Self> {
188        Ok(DnsUpdater::Route53(Route53Provider::new(config)))
189    }
190
191    /// Create a new DNS updater using the Pebble Challenge Test Server.
192    #[cfg(feature = "test_provider")]
193    pub fn new_pebble(base_url: impl AsRef<str>, timeout: Option<Duration>) -> Self {
194        DnsUpdater::Pebble(PebbleProvider::new(base_url, timeout))
195    }
196
197    /// Create a new DNS updater backed by an in-memory record store.
198    #[cfg(feature = "test_provider")]
199    pub fn new_in_memory(records: Arc<Mutex<Vec<NamedDnsRecord>>>) -> Self {
200        DnsUpdater::InMemory(InMemoryProvider::new(records))
201    }
202
203    /// Create a new DNS record.
204    pub async fn create(
205        &self,
206        name: impl IntoFqdn<'_>,
207        record: DnsRecord,
208        ttl: u32,
209        origin: impl IntoFqdn<'_>,
210    ) -> crate::Result<()> {
211        match self {
212            DnsUpdater::Bunny(provider) => provider.create(name, record, ttl, origin).await,
213            DnsUpdater::Cloudflare(provider) => provider.create(name, record, ttl, origin).await,
214            DnsUpdater::Desec(provider) => provider.create(name, record, ttl, origin).await,
215            DnsUpdater::DigitalOcean(provider) => provider.create(name, record, ttl, origin).await,
216            DnsUpdater::DNSimple(provider) => provider.create(name, record, ttl, origin).await,
217            #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
218            DnsUpdater::Ovh(provider) => provider.create(name, record, ttl, origin).await,
219            DnsUpdater::Porkbun(provider) => provider.create(name, record, ttl, origin).await,
220            DnsUpdater::Rfc2136(provider) => provider.create(name, record, ttl, origin).await,
221            DnsUpdater::Route53(provider) => provider.create(name, record, ttl, origin).await,
222            DnsUpdater::Spaceship(provider) => provider.create(name, record, ttl, origin).await,
223            DnsUpdater::GoogleCloudDns(provider) => {
224                provider.create(name, record, ttl, origin).await
225            }
226            #[cfg(feature = "test_provider")]
227            DnsUpdater::Pebble(provider) => provider.create(name, record, ttl, origin).await,
228            #[cfg(feature = "test_provider")]
229            DnsUpdater::InMemory(provider) => provider.create(name, record, ttl, origin).await,
230        }
231    }
232
233    /// Update an existing DNS record.
234    pub async fn update(
235        &self,
236        name: impl IntoFqdn<'_>,
237        record: DnsRecord,
238        ttl: u32,
239        origin: impl IntoFqdn<'_>,
240    ) -> crate::Result<()> {
241        match self {
242            DnsUpdater::Bunny(provider) => provider.update(name, record, ttl, origin).await,
243            DnsUpdater::Cloudflare(provider) => provider.update(name, record, ttl, origin).await,
244            DnsUpdater::Desec(provider) => provider.update(name, record, ttl, origin).await,
245            DnsUpdater::DigitalOcean(provider) => provider.update(name, record, ttl, origin).await,
246            DnsUpdater::DNSimple(provider) => provider.update(name, record, ttl, origin).await,
247            #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
248            DnsUpdater::Ovh(provider) => provider.update(name, record, ttl, origin).await,
249            DnsUpdater::Porkbun(provider) => provider.update(name, record, ttl, origin).await,
250            DnsUpdater::Rfc2136(provider) => provider.update(name, record, ttl, origin).await,
251            DnsUpdater::Route53(provider) => provider.update(name, record, ttl, origin).await,
252            DnsUpdater::Spaceship(provider) => provider.update(name, record, ttl, origin).await,
253            DnsUpdater::GoogleCloudDns(provider) => {
254                provider.update(name, record, ttl, origin).await
255            }
256            #[cfg(feature = "test_provider")]
257            DnsUpdater::Pebble(provider) => provider.update(name, record, ttl, origin).await,
258            #[cfg(feature = "test_provider")]
259            DnsUpdater::InMemory(provider) => provider.update(name, record, ttl, origin).await,
260        }
261    }
262
263    /// Delete an existing DNS record.
264    pub async fn delete(
265        &self,
266        name: impl IntoFqdn<'_>,
267        origin: impl IntoFqdn<'_>,
268        record: DnsRecordType,
269    ) -> crate::Result<()> {
270        match self {
271            DnsUpdater::Bunny(provider) => provider.delete(name, origin, record).await,
272            DnsUpdater::Cloudflare(provider) => provider.delete(name, origin, record).await,
273            DnsUpdater::Desec(provider) => provider.delete(name, origin, record).await,
274            DnsUpdater::DigitalOcean(provider) => provider.delete(name, origin, record).await,
275            DnsUpdater::DNSimple(provider) => provider.delete(name, origin, record).await,
276            #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
277            DnsUpdater::Ovh(provider) => provider.delete(name, origin, record).await,
278            DnsUpdater::Porkbun(provider) => provider.delete(name, origin, record).await,
279            DnsUpdater::Rfc2136(provider) => provider.delete(name, origin, record).await,
280            DnsUpdater::Route53(provider) => provider.delete(name, origin, record).await,
281            DnsUpdater::Spaceship(provider) => provider.delete(name, origin, record).await,
282            DnsUpdater::GoogleCloudDns(provider) => provider.delete(name, origin, record).await,
283            #[cfg(feature = "test_provider")]
284            DnsUpdater::Pebble(provider) => provider.delete(name, origin, record).await,
285            #[cfg(feature = "test_provider")]
286            DnsUpdater::InMemory(provider) => provider.delete(name, origin, record).await,
287        }
288    }
289}