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    #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
64    pub fn new_rfc2136_sig0(
65        addr: impl TryInto<DnsAddress>,
66        signer_name: impl AsRef<str>,
67        key: Box<dyn SigningKey>,
68        public_key: impl Into<Vec<u8>>,
69        algorithm: Algorithm,
70    ) -> crate::Result<Self> {
71        Ok(DnsUpdater::Rfc2136(Rfc2136Provider::new_sig0(
72            addr,
73            signer_name,
74            key,
75            public_key,
76            algorithm.into(),
77        )?))
78    }
79
80    /// Create a new DNS updater using the Cloudflare API.
81    pub fn new_cloudflare(
82        secret: impl AsRef<str>,
83        email: Option<impl AsRef<str>>,
84        timeout: Option<Duration>,
85    ) -> crate::Result<Self> {
86        Ok(DnsUpdater::Cloudflare(CloudflareProvider::new(
87            secret, email, timeout,
88        )?))
89    }
90
91    /// Create a new DNS updater using the Cloudflare API.
92    pub fn new_digitalocean(
93        auth_token: impl AsRef<str>,
94        timeout: Option<Duration>,
95    ) -> crate::Result<Self> {
96        Ok(DnsUpdater::DigitalOcean(DigitalOceanProvider::new(
97            auth_token, timeout,
98        )))
99    }
100
101    /// Create a new DNS updater using the Desec.io API.
102    pub fn new_desec(
103        auth_token: impl AsRef<str>,
104        timeout: Option<Duration>,
105    ) -> crate::Result<Self> {
106        Ok(DnsUpdater::Desec(DesecProvider::new(auth_token, timeout)))
107    }
108
109    /// Create a new DNS updater using the OVH API.
110    #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
111    pub fn new_ovh(
112        application_key: impl AsRef<str>,
113        application_secret: impl AsRef<str>,
114        consumer_key: impl AsRef<str>,
115        endpoint: OvhEndpoint,
116        timeout: Option<Duration>,
117    ) -> crate::Result<Self> {
118        Ok(DnsUpdater::Ovh(OvhProvider::new(
119            application_key,
120            application_secret,
121            consumer_key,
122            endpoint,
123            timeout,
124        )?))
125    }
126
127    /// Create a new DNS updater using the Bunny API.
128    pub fn new_bunny(api_key: impl AsRef<str>, timeout: Option<Duration>) -> crate::Result<Self> {
129        Ok(DnsUpdater::Bunny(BunnyProvider::new(api_key, timeout)?))
130    }
131
132    /// Create a new DNS updater using the Porkbun API.
133    pub fn new_porkbun(
134        api_key: impl AsRef<str>,
135        secret_api_key: impl AsRef<str>,
136        timeout: Option<Duration>,
137    ) -> crate::Result<Self> {
138        Ok(DnsUpdater::Porkbun(PorkBunProvider::new(
139            api_key,
140            secret_api_key,
141            timeout,
142        )))
143    }
144
145    /// Create a new DNS updater using the Spaceship API.
146    pub fn new_spaceship(
147        api_key: impl AsRef<str>,
148        api_secret: impl AsRef<str>,
149        timeout: Option<Duration>,
150    ) -> crate::Result<Self> {
151        Ok(DnsUpdater::Spaceship(SpaceshipProvider::new(
152            api_key, api_secret, timeout,
153        )))
154    }
155
156    /// Create a new DNS updater using the DNSimple API.
157    pub fn new_dnsimple(
158        auth_token: impl AsRef<str>,
159        account_id: impl AsRef<str>,
160        timeout: Option<Duration>,
161    ) -> crate::Result<Self> {
162        Ok(DnsUpdater::DNSimple(DNSimpleProvider::new(
163            auth_token, account_id, timeout,
164        )))
165    }
166
167    /// Create a new DNS updater using the Google Cloud DNS API.
168    pub fn new_google_cloud_dns(
169        config: crate::providers::google_cloud_dns::GoogleCloudDnsConfig,
170    ) -> crate::Result<Self> {
171        Ok(DnsUpdater::GoogleCloudDns(
172            crate::providers::google_cloud_dns::GoogleCloudDnsProvider::new(config)?,
173        ))
174    }
175
176    /// Create a new DNS updater using the Route53 API.
177    pub fn new_route53(config: crate::providers::route53::Route53Config) -> crate::Result<Self> {
178        Ok(DnsUpdater::Route53(Route53Provider::new(config)))
179    }
180
181    /// Create a new DNS updater using the Pebble Challenge Test Server.
182    #[cfg(feature = "test_provider")]
183    pub fn new_pebble(base_url: impl AsRef<str>, timeout: Option<Duration>) -> Self {
184        DnsUpdater::Pebble(PebbleProvider::new(base_url, timeout))
185    }
186
187    /// Create a new DNS updater backed by an in-memory record store.
188    #[cfg(feature = "test_provider")]
189    pub fn new_in_memory(records: Arc<Mutex<Vec<NamedDnsRecord>>>) -> Self {
190        DnsUpdater::InMemory(InMemoryProvider::new(records))
191    }
192
193    /// Create a new DNS record.
194    pub async fn create(
195        &self,
196        name: impl IntoFqdn<'_>,
197        record: DnsRecord,
198        ttl: u32,
199        origin: impl IntoFqdn<'_>,
200    ) -> crate::Result<()> {
201        match self {
202            DnsUpdater::Bunny(provider) => provider.create(name, record, ttl, origin).await,
203            DnsUpdater::Cloudflare(provider) => provider.create(name, record, ttl, origin).await,
204            DnsUpdater::Desec(provider) => provider.create(name, record, ttl, origin).await,
205            DnsUpdater::DigitalOcean(provider) => provider.create(name, record, ttl, origin).await,
206            DnsUpdater::DNSimple(provider) => provider.create(name, record, ttl, origin).await,
207            #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
208            DnsUpdater::Ovh(provider) => provider.create(name, record, ttl, origin).await,
209            DnsUpdater::Porkbun(provider) => provider.create(name, record, ttl, origin).await,
210            DnsUpdater::Rfc2136(provider) => provider.create(name, record, ttl, origin).await,
211            DnsUpdater::Route53(provider) => provider.create(name, record, ttl, origin).await,
212            DnsUpdater::Spaceship(provider) => provider.create(name, record, ttl, origin).await,
213            DnsUpdater::GoogleCloudDns(provider) => {
214                provider.create(name, record, ttl, origin).await
215            }
216            #[cfg(feature = "test_provider")]
217            DnsUpdater::Pebble(provider) => provider.create(name, record, ttl, origin).await,
218            #[cfg(feature = "test_provider")]
219            DnsUpdater::InMemory(provider) => provider.create(name, record, ttl, origin).await,
220        }
221    }
222
223    /// Update an existing DNS record.
224    pub async fn update(
225        &self,
226        name: impl IntoFqdn<'_>,
227        record: DnsRecord,
228        ttl: u32,
229        origin: impl IntoFqdn<'_>,
230    ) -> crate::Result<()> {
231        match self {
232            DnsUpdater::Bunny(provider) => provider.update(name, record, ttl, origin).await,
233            DnsUpdater::Cloudflare(provider) => provider.update(name, record, ttl, origin).await,
234            DnsUpdater::Desec(provider) => provider.update(name, record, ttl, origin).await,
235            DnsUpdater::DigitalOcean(provider) => provider.update(name, record, ttl, origin).await,
236            DnsUpdater::DNSimple(provider) => provider.update(name, record, ttl, origin).await,
237            #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
238            DnsUpdater::Ovh(provider) => provider.update(name, record, ttl, origin).await,
239            DnsUpdater::Porkbun(provider) => provider.update(name, record, ttl, origin).await,
240            DnsUpdater::Rfc2136(provider) => provider.update(name, record, ttl, origin).await,
241            DnsUpdater::Route53(provider) => provider.update(name, record, ttl, origin).await,
242            DnsUpdater::Spaceship(provider) => provider.update(name, record, ttl, origin).await,
243            DnsUpdater::GoogleCloudDns(provider) => {
244                provider.update(name, record, ttl, origin).await
245            }
246            #[cfg(feature = "test_provider")]
247            DnsUpdater::Pebble(provider) => provider.update(name, record, ttl, origin).await,
248            #[cfg(feature = "test_provider")]
249            DnsUpdater::InMemory(provider) => provider.update(name, record, ttl, origin).await,
250        }
251    }
252
253    /// Delete an existing DNS record.
254    pub async fn delete(
255        &self,
256        name: impl IntoFqdn<'_>,
257        origin: impl IntoFqdn<'_>,
258        record: DnsRecordType,
259    ) -> crate::Result<()> {
260        match self {
261            DnsUpdater::Bunny(provider) => provider.delete(name, origin, record).await,
262            DnsUpdater::Cloudflare(provider) => provider.delete(name, origin).await,
263            DnsUpdater::Desec(provider) => provider.delete(name, origin, record).await,
264            DnsUpdater::DigitalOcean(provider) => provider.delete(name, origin).await,
265            DnsUpdater::DNSimple(provider) => provider.delete(name, origin, record).await,
266            #[cfg(any(feature = "ring", feature = "aws-lc-rs"))]
267            DnsUpdater::Ovh(provider) => provider.delete(name, origin, record).await,
268            DnsUpdater::Porkbun(provider) => provider.delete(name, origin, record).await,
269            DnsUpdater::Rfc2136(provider) => provider.delete(name, origin).await,
270            DnsUpdater::Route53(provider) => provider.delete(name, origin, record).await,
271            DnsUpdater::Spaceship(provider) => provider.delete(name, origin, record).await,
272            DnsUpdater::GoogleCloudDns(provider) => provider.delete(name, origin, record).await,
273            #[cfg(feature = "test_provider")]
274            DnsUpdater::Pebble(provider) => provider.delete(name, origin, record).await,
275            #[cfg(feature = "test_provider")]
276            DnsUpdater::InMemory(provider) => provider.delete(name, origin, record).await,
277        }
278    }
279}