1use std::net::SocketAddr;
4
5use serde::Deserialize;
6
7use crate::error::{Error, Result};
8
9fn default_txt_prefix() -> String {
10 "_modo-verify".into()
11}
12
13fn default_timeout_ms() -> u64 {
14 5000
15}
16
17#[non_exhaustive]
31#[derive(Debug, Clone, Deserialize)]
32#[serde(default)]
33pub struct DnsConfig {
34 pub nameserver: String,
38 #[serde(default = "default_txt_prefix")]
43 pub txt_prefix: String,
44 #[serde(default = "default_timeout_ms")]
46 pub timeout_ms: u64,
47}
48
49impl Default for DnsConfig {
50 fn default() -> Self {
51 Self {
52 nameserver: "8.8.8.8".into(),
53 txt_prefix: "_modo-verify".into(),
54 timeout_ms: 5000,
55 }
56 }
57}
58
59impl DnsConfig {
60 pub fn new(nameserver: impl Into<String>) -> Self {
64 Self {
65 nameserver: nameserver.into(),
66 txt_prefix: "_modo-verify".into(),
67 timeout_ms: 5000,
68 }
69 }
70
71 pub fn parse_nameserver(&self) -> Result<SocketAddr> {
81 if let Ok(addr) = self.nameserver.parse::<SocketAddr>() {
82 return Ok(addr);
83 }
84 let with_port = format!("{}:53", self.nameserver);
85 with_port.parse::<SocketAddr>().map_err(|_| {
86 Error::internal(format!(
87 "invalid dns nameserver address: {}",
88 self.nameserver
89 ))
90 })
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn parse_full_config() {
100 let yaml = r#"
101nameserver: "8.8.8.8:53"
102txt_prefix: "_myapp-verify"
103timeout_ms: 3000
104"#;
105 let config: DnsConfig = serde_yaml_ng::from_str(yaml).unwrap();
106 assert_eq!(config.nameserver, "8.8.8.8:53");
107 assert_eq!(config.txt_prefix, "_myapp-verify");
108 assert_eq!(config.timeout_ms, 3000);
109 }
110
111 #[test]
112 fn defaults_applied_when_fields_omitted() {
113 let yaml = r#"
114nameserver: "8.8.8.8"
115"#;
116 let config: DnsConfig = serde_yaml_ng::from_str(yaml).unwrap();
117 assert_eq!(config.nameserver, "8.8.8.8");
118 assert_eq!(config.txt_prefix, "_modo-verify");
119 assert_eq!(config.timeout_ms, 5000);
120 }
121
122 #[test]
123 fn parse_nameserver_with_port() {
124 let config = DnsConfig {
125 nameserver: "1.1.1.1:53".into(),
126 txt_prefix: "_modo-verify".into(),
127 timeout_ms: 5000,
128 };
129 let addr = config.parse_nameserver().unwrap();
130 assert_eq!(addr.to_string(), "1.1.1.1:53");
131 }
132
133 #[test]
134 fn parse_nameserver_without_port_appends_53() {
135 let config = DnsConfig {
136 nameserver: "8.8.8.8".into(),
137 txt_prefix: "_modo-verify".into(),
138 timeout_ms: 5000,
139 };
140 let addr = config.parse_nameserver().unwrap();
141 assert_eq!(addr.to_string(), "8.8.8.8:53");
142 }
143
144 #[test]
145 fn parse_nameserver_invalid_address_fails() {
146 let config = DnsConfig {
147 nameserver: "not-an-address".into(),
148 txt_prefix: "_modo-verify".into(),
149 timeout_ms: 5000,
150 };
151 let err = config.parse_nameserver().unwrap_err();
152 assert_eq!(err.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
153 }
154}