1 use std
::sync
::{Arc, Mutex}
;
3 use ::serde
::{Deserialize, Serialize}
;
5 use lazy_static
::lazy_static
;
8 use serde_json
::{json, Value}
;
10 use pbs_api_types
::{IPRE, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE}
;
11 use proxmox_router
::{ApiMethod, Permission, Router, RpcEnvironment}
;
12 use proxmox_schema
::api
;
13 use proxmox_sys
::fs
::{file_get_contents, replace_file, CreateOptions}
;
16 FIRST_DNS_SERVER_SCHEMA
, NODE_SCHEMA
, PRIV_SYS_AUDIT
, PRIV_SYS_MODIFY
,
17 PROXMOX_CONFIG_DIGEST_SCHEMA
, SEARCH_DOMAIN_SCHEMA
, SECOND_DNS_SERVER_SCHEMA
,
18 THIRD_DNS_SERVER_SCHEMA
,
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
> {
37 let mut result
= json
!({}
);
41 let raw
= file_get_contents(RESOLV_CONF_FN
)?
;
43 result
["digest"] = Value
::from(hex
::encode(&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
=
50 Regex
::new(concat
!(r
"^\s*nameserver\s+(", IPRE
!(), r
")\s*")).unwrap();
53 let mut options
= String
::new();
55 for line
in data
.lines() {
56 if let Some(caps
) = DOMAIN_REGEX
.captures(line
) {
57 result
["search"] = Value
::from(&caps
[1]);
58 } else if let Some(caps
) = SERVER_REGEX
.captures(line
) {
63 let nameserver
= &caps
[1];
64 let id
= format
!("dns{}", nscount
);
65 result
[id
] = Value
::from(nameserver
);
67 if !options
.is_empty() {
70 options
.push_str(line
);
74 if !options
.is_empty() {
75 result
["options"] = options
.into();
84 description
: "Update DNS settings.",
90 schema
: SEARCH_DOMAIN_SCHEMA
,
95 schema
: FIRST_DNS_SERVER_SCHEMA
,
99 schema
: SECOND_DNS_SERVER_SCHEMA
,
103 schema
: THIRD_DNS_SERVER_SCHEMA
,
106 description
: "List of properties to delete.",
110 type: DeletableProperty
,
115 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
120 permission
: &Permission
::Privilege(&["system", "network", "dns"], PRIV_SYS_MODIFY
, false),
123 /// Update DNS settings
125 search
: Option
<String
>,
126 dns1
: Option
<String
>,
127 dns2
: Option
<String
>,
128 dns3
: Option
<String
>,
129 delete
: Option
<Vec
<DeletableProperty
>>,
130 digest
: Option
<String
>,
131 ) -> Result
<Value
, Error
> {
133 static ref MUTEX
: Arc
<Mutex
<()>> = Arc
::new(Mutex
::new(()));
136 let _guard
= MUTEX
.lock();
138 let mut config
= read_etc_resolv_conf()?
;
139 let old_digest
= config
["digest"].as_str().unwrap();
141 if let Some(digest
) = digest
{
142 crate::tools
::assert_if_modified(old_digest
, &digest
)?
;
145 if let Some(delete
) = delete
{
146 for delete_prop
in delete
{
147 let config
= config
.as_object_mut().unwrap();
149 DeletableProperty
::dns1
=> {
150 config
.remove("dns1");
152 DeletableProperty
::dns2
=> {
153 config
.remove("dns2");
155 DeletableProperty
::dns3
=> {
156 config
.remove("dns3");
162 if let Some(search
) = search
{
163 config
["search"] = search
.into();
165 if let Some(dns1
) = dns1
{
166 config
["dns1"] = dns1
.into();
168 if let Some(dns2
) = dns2
{
169 config
["dns2"] = dns2
.into();
171 if let Some(dns3
) = dns3
{
172 config
["dns3"] = dns3
.into();
175 let mut data
= String
::new();
177 if let Some(search
) = config
["search"].as_str() {
178 data
.push_str(&format
!("search {}\n", search
));
180 for opt
in &["dns1", "dns2", "dns3"] {
181 if let Some(server
) = config
[opt
].as_str() {
182 data
.push_str(&format
!("nameserver {}\n", server
));
185 if let Some(options
) = config
["options"].as_str() {
186 data
.push_str(options
);
189 replace_file(RESOLV_CONF_FN
, data
.as_bytes(), CreateOptions
::new(), true)?
;
203 description
: "Returns DNS server IPs and sreach domain.",
207 schema
: PROXMOX_CONFIG_DIGEST_SCHEMA
,
211 schema
: SEARCH_DOMAIN_SCHEMA
,
215 schema
: FIRST_DNS_SERVER_SCHEMA
,
219 schema
: SECOND_DNS_SERVER_SCHEMA
,
223 schema
: THIRD_DNS_SERVER_SCHEMA
,
228 permission
: &Permission
::Privilege(&["system", "network", "dns"], PRIV_SYS_AUDIT
, false),
231 /// Read DNS settings.
235 _rpcenv
: &mut dyn RpcEnvironment
,
236 ) -> Result
<Value
, Error
> {
237 read_etc_resolv_conf()
240 pub const ROUTER
: Router
= Router
::new()
241 .get(&API_METHOD_GET_DNS
)
242 .put(&API_METHOD_UPDATE_DNS
);