1 use std
::sync
::{Arc, Mutex}
;
4 use lazy_static
::lazy_static
;
7 use serde_json
::{json, Value}
;
8 use ::serde
::{Deserialize, Serialize}
;
10 use proxmox
::api
::{api, ApiMethod, Router, RpcEnvironment, Permission}
;
11 use proxmox
::tools
::fs
::{file_get_contents, replace_file, CreateOptions}
;
12 use proxmox
::{IPRE, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32}
;
15 PROXMOX_CONFIG_DIGEST_SCHEMA
, FIRST_DNS_SERVER_SCHEMA
, SECOND_DNS_SERVER_SCHEMA
,
16 THIRD_DNS_SERVER_SCHEMA
, NODE_SCHEMA
, SEARCH_DOMAIN_SCHEMA
,
17 PRIV_SYS_AUDIT
, PRIV_SYS_MODIFY
,
20 static RESOLV_CONF_FN
: &str = "/etc/resolv.conf";
23 #[derive(Serialize, Deserialize)]
24 #[allow(non_camel_case_types)]
25 /// Deletable property name
26 pub enum DeletableProperty
{
27 /// Delete first nameserver entry
29 /// Delete second nameserver entry
31 /// Delete third nameserver entry
35 pub fn read_etc_resolv_conf() -> Result
<Value
, Error
> {
37 let mut result
= json
!({}
);
41 let raw
= file_get_contents(RESOLV_CONF_FN
)?
;
43 result
["digest"] = Value
::from(proxmox
::tools
::digest_to_hex(&sha
::sha256(&raw
)));
45 let data
= String
::from_utf8(raw
)?
;
48 static ref DOMAIN_REGEX
: Regex
= Regex
::new(r
"^\s*(?:search|domain)\s+(\S+)\s*").unwrap();
49 static ref SERVER_REGEX
: Regex
= Regex
::new(
50 concat
!(r
"^\s*nameserver\s+(", IPRE
!(), r
")\s*")).unwrap();
53 let mut options
= String
::new();
55 for line
in data
.lines() {
57 if let Some(caps
) = DOMAIN_REGEX
.captures(&line
) {
58 result
["search"] = Value
::from(&caps
[1]);
59 } else if let Some(caps
) = SERVER_REGEX
.captures(&line
) {
61 if nscount
> 3 { continue }
;
62 let nameserver
= &caps
[1];
63 let id
= format
!("dns{}", nscount
);
64 result
[id
] = Value
::from(nameserver
);
66 if !options
.is_empty() { options.push('\n'); }
67 options
.push_str(line
);
71 if !options
.is_empty() {
72 result
["options"] = options
.into();
81 description
: "Update DNS settings.",
87 schema
: SEARCH_DOMAIN_SCHEMA
,
92 schema
: FIRST_DNS_SERVER_SCHEMA
,
96 schema
: SECOND_DNS_SERVER_SCHEMA
,
100 schema
: THIRD_DNS_SERVER_SCHEMA
,
103 description
: "List of properties to delete.",
107 type: DeletableProperty
,
112 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
117 permission
: &Permission
::Privilege(&["system", "network", "dns"], PRIV_SYS_MODIFY
, false),
120 /// Update DNS settings
122 search
: Option
<String
>,
123 dns1
: Option
<String
>,
124 dns2
: Option
<String
>,
125 dns3
: Option
<String
>,
126 delete
: Option
<Vec
<DeletableProperty
>>,
127 digest
: Option
<String
>,
128 ) -> Result
<Value
, Error
> {
131 static ref MUTEX
: Arc
<Mutex
<()>> = Arc
::new(Mutex
::new(()));
134 let _guard
= MUTEX
.lock();
136 let mut config
= read_etc_resolv_conf()?
;
137 let old_digest
= config
["digest"].as_str().unwrap();
139 if let Some(digest
) = digest
{
140 crate::tools
::assert_if_modified(old_digest
, &digest
)?
;
143 if let Some(delete
) = delete
{
144 for delete_prop
in delete
{
145 let config
= config
.as_object_mut().unwrap();
147 DeletableProperty
::dns1
=> { config.remove("dns1"); }
,
148 DeletableProperty
::dns2
=> { config.remove("dns2"); }
,
149 DeletableProperty
::dns3
=> { config.remove("dns3"); }
,
154 if let Some(search
) = search { config["search"] = search.into(); }
155 if let Some(dns1
) = dns1 { config["dns1"] = dns1.into(); }
156 if let Some(dns2
) = dns2 { config["dns2"] = dns2.into(); }
157 if let Some(dns3
) = dns3 { config["dns3"] = dns3.into(); }
159 let mut data
= String
::new();
161 if let Some(search
) = config
["search"].as_str() {
162 data
.push_str(&format
!("search {}\n", search
));
164 for opt
in &["dns1", "dns2", "dns3"] {
165 if let Some(server
) = config
[opt
].as_str() {
166 data
.push_str(&format
!("nameserver {}\n", server
));
169 if let Some(options
) = config
["options"].as_str() {
170 data
.push_str(options
);
173 replace_file(RESOLV_CONF_FN
, data
.as_bytes(), CreateOptions
::new())?
;
187 description
: "Returns DNS server IPs and sreach domain.",
191 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
195 schema
: SEARCH_DOMAIN_SCHEMA
,
199 schema
: FIRST_DNS_SERVER_SCHEMA
,
203 schema
: SECOND_DNS_SERVER_SCHEMA
,
207 schema
: THIRD_DNS_SERVER_SCHEMA
,
212 permission
: &Permission
::Privilege(&["system", "network", "dns"], PRIV_SYS_AUDIT
, false),
215 /// Read DNS settings.
219 _rpcenv
: &mut dyn RpcEnvironment
,
220 ) -> Result
<Value
, Error
> {
222 read_etc_resolv_conf()
225 pub const ROUTER
: Router
= Router
::new()
226 .get(&API_METHOD_GET_DNS
)
227 .put(&API_METHOD_UPDATE_DNS
);