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_router
::{ApiMethod, Router, RpcEnvironment, Permission}
;
11 use proxmox_schema
::api
;
12 use proxmox
::tools
::fs
::{file_get_contents, replace_file, CreateOptions}
;
13 use proxmox
::{IPRE, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32}
;
16 PROXMOX_CONFIG_DIGEST_SCHEMA
, FIRST_DNS_SERVER_SCHEMA
, SECOND_DNS_SERVER_SCHEMA
,
17 THIRD_DNS_SERVER_SCHEMA
, NODE_SCHEMA
, SEARCH_DOMAIN_SCHEMA
,
18 PRIV_SYS_AUDIT
, PRIV_SYS_MODIFY
,
21 static RESOLV_CONF_FN
: &str = "/etc/resolv.conf";
24 #[derive(Serialize, Deserialize)]
25 #[allow(non_camel_case_types)]
26 /// Deletable property name
27 pub enum DeletableProperty
{
28 /// Delete first nameserver entry
30 /// Delete second nameserver entry
32 /// Delete third nameserver entry
36 pub fn read_etc_resolv_conf() -> Result
<Value
, Error
> {
38 let mut result
= json
!({}
);
42 let raw
= file_get_contents(RESOLV_CONF_FN
)?
;
44 result
["digest"] = Value
::from(proxmox
::tools
::digest_to_hex(&sha
::sha256(&raw
)));
46 let data
= String
::from_utf8(raw
)?
;
49 static ref DOMAIN_REGEX
: Regex
= Regex
::new(r
"^\s*(?:search|domain)\s+(\S+)\s*").unwrap();
50 static ref SERVER_REGEX
: Regex
= Regex
::new(
51 concat
!(r
"^\s*nameserver\s+(", IPRE
!(), r
")\s*")).unwrap();
54 let mut options
= String
::new();
56 for line
in data
.lines() {
58 if let Some(caps
) = DOMAIN_REGEX
.captures(&line
) {
59 result
["search"] = Value
::from(&caps
[1]);
60 } else if let Some(caps
) = SERVER_REGEX
.captures(&line
) {
62 if nscount
> 3 { continue }
;
63 let nameserver
= &caps
[1];
64 let id
= format
!("dns{}", nscount
);
65 result
[id
] = Value
::from(nameserver
);
67 if !options
.is_empty() { options.push('\n'); }
68 options
.push_str(line
);
72 if !options
.is_empty() {
73 result
["options"] = options
.into();
82 description
: "Update DNS settings.",
88 schema
: SEARCH_DOMAIN_SCHEMA
,
93 schema
: FIRST_DNS_SERVER_SCHEMA
,
97 schema
: SECOND_DNS_SERVER_SCHEMA
,
101 schema
: THIRD_DNS_SERVER_SCHEMA
,
104 description
: "List of properties to delete.",
108 type: DeletableProperty
,
113 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
118 permission
: &Permission
::Privilege(&["system", "network", "dns"], PRIV_SYS_MODIFY
, false),
121 /// Update DNS settings
123 search
: Option
<String
>,
124 dns1
: Option
<String
>,
125 dns2
: Option
<String
>,
126 dns3
: Option
<String
>,
127 delete
: Option
<Vec
<DeletableProperty
>>,
128 digest
: Option
<String
>,
129 ) -> Result
<Value
, Error
> {
132 static ref MUTEX
: Arc
<Mutex
<()>> = Arc
::new(Mutex
::new(()));
135 let _guard
= MUTEX
.lock();
137 let mut config
= read_etc_resolv_conf()?
;
138 let old_digest
= config
["digest"].as_str().unwrap();
140 if let Some(digest
) = digest
{
141 crate::tools
::assert_if_modified(old_digest
, &digest
)?
;
144 if let Some(delete
) = delete
{
145 for delete_prop
in delete
{
146 let config
= config
.as_object_mut().unwrap();
148 DeletableProperty
::dns1
=> { config.remove("dns1"); }
,
149 DeletableProperty
::dns2
=> { config.remove("dns2"); }
,
150 DeletableProperty
::dns3
=> { config.remove("dns3"); }
,
155 if let Some(search
) = search { config["search"] = search.into(); }
156 if let Some(dns1
) = dns1 { config["dns1"] = dns1.into(); }
157 if let Some(dns2
) = dns2 { config["dns2"] = dns2.into(); }
158 if let Some(dns3
) = dns3 { config["dns3"] = dns3.into(); }
160 let mut data
= String
::new();
162 if let Some(search
) = config
["search"].as_str() {
163 data
.push_str(&format
!("search {}\n", search
));
165 for opt
in &["dns1", "dns2", "dns3"] {
166 if let Some(server
) = config
[opt
].as_str() {
167 data
.push_str(&format
!("nameserver {}\n", server
));
170 if let Some(options
) = config
["options"].as_str() {
171 data
.push_str(options
);
174 replace_file(RESOLV_CONF_FN
, data
.as_bytes(), CreateOptions
::new())?
;
188 description
: "Returns DNS server IPs and sreach domain.",
192 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
196 schema
: SEARCH_DOMAIN_SCHEMA
,
200 schema
: FIRST_DNS_SERVER_SCHEMA
,
204 schema
: SECOND_DNS_SERVER_SCHEMA
,
208 schema
: THIRD_DNS_SERVER_SCHEMA
,
213 permission
: &Permission
::Privilege(&["system", "network", "dns"], PRIV_SYS_AUDIT
, false),
216 /// Read DNS settings.
220 _rpcenv
: &mut dyn RpcEnvironment
,
221 ) -> Result
<Value
, Error
> {
223 read_etc_resolv_conf()
226 pub const ROUTER
: Router
= Router
::new()
227 .get(&API_METHOD_GET_DNS
)
228 .put(&API_METHOD_UPDATE_DNS
);