]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/node/dns.rs
b87f6a53a1cdc3077e99e4a3f595d1e14d52cec3
[proxmox-backup.git] / src / api2 / node / dns.rs
1 use std::sync::{Arc, Mutex};
2
3 use anyhow::{Error};
4 use lazy_static::lazy_static;
5 use openssl::sha;
6 use regex::Regex;
7 use serde_json::{json, Value};
8
9 use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
10 use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
11 use proxmox::{IPRE, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32};
12
13 use crate::api2::types::*;
14 use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
15
16 static RESOLV_CONF_FN: &str = "/etc/resolv.conf";
17
18 pub fn read_etc_resolv_conf() -> Result<Value, Error> {
19
20 let mut result = json!({});
21
22 let mut nscount = 0;
23
24 let raw = file_get_contents(RESOLV_CONF_FN)?;
25
26 result["digest"] = Value::from(proxmox::tools::digest_to_hex(&sha::sha256(&raw)));
27
28 let data = String::from_utf8(raw)?;
29
30 lazy_static! {
31 static ref DOMAIN_REGEX: Regex = Regex::new(r"^\s*(?:search|domain)\s+(\S+)\s*").unwrap();
32 static ref SERVER_REGEX: Regex = Regex::new(
33 concat!(r"^\s*nameserver\s+(", IPRE!(), r")\s*")).unwrap();
34 }
35
36 for line in data.lines() {
37
38 if let Some(caps) = DOMAIN_REGEX.captures(&line) {
39 result["search"] = Value::from(&caps[1]);
40 } else if let Some(caps) = SERVER_REGEX.captures(&line) {
41 nscount += 1;
42 if nscount > 3 { continue };
43 let nameserver = &caps[1];
44 let id = format!("dns{}", nscount);
45 result[id] = Value::from(nameserver);
46 }
47 }
48
49 Ok(result)
50 }
51
52 #[api(
53 protected: true,
54 input: {
55 description: "Update DNS settings.",
56 properties: {
57 node: {
58 schema: NODE_SCHEMA,
59 },
60 search: {
61 schema: SEARCH_DOMAIN_SCHEMA,
62 },
63 dns1: {
64 optional: true,
65 schema: FIRST_DNS_SERVER_SCHEMA,
66 },
67 dns2: {
68 optional: true,
69 schema: SECOND_DNS_SERVER_SCHEMA,
70 },
71 dns3: {
72 optional: true,
73 schema: THIRD_DNS_SERVER_SCHEMA,
74 },
75 digest: {
76 optional: true,
77 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
78 },
79 },
80 },
81 access: {
82 permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false),
83 }
84 )]
85 /// Update DNS settings
86 fn update_dns(
87 param: Value,
88 _info: &ApiMethod,
89 _rpcenv: &mut dyn RpcEnvironment,
90 ) -> Result<Value, Error> {
91
92 lazy_static! {
93 static ref MUTEX: Arc<Mutex<usize>> = Arc::new(Mutex::new(0));
94 }
95
96 let _guard = MUTEX.lock();
97
98 let search = crate::tools::required_string_param(&param, "search")?;
99
100 let raw = file_get_contents(RESOLV_CONF_FN)?;
101 let old_digest = proxmox::tools::digest_to_hex(&sha::sha256(&raw));
102
103 if let Some(digest) = param["digest"].as_str() {
104 crate::tools::assert_if_modified(&old_digest, &digest)?;
105 }
106
107 let old_data = String::from_utf8(raw)?;
108
109 let mut data = format!("search {}\n", search);
110
111 for opt in &["dns1", "dns2", "dns3"] {
112 if let Some(server) = param[opt].as_str() {
113 data.push_str(&format!("nameserver {}\n", server));
114 }
115 }
116
117 // append other data
118 lazy_static! {
119 static ref SKIP_REGEX: Regex = Regex::new(r"^(search|domain|nameserver)\s+").unwrap();
120 }
121 for line in old_data.lines() {
122 if SKIP_REGEX.is_match(line) { continue; }
123 data.push_str(line);
124 data.push('\n');
125 }
126
127 replace_file(RESOLV_CONF_FN, data.as_bytes(), CreateOptions::new())?;
128
129 Ok(Value::Null)
130 }
131
132 #[api(
133 input: {
134 properties: {
135 node: {
136 schema: NODE_SCHEMA,
137 },
138 },
139 },
140 returns: {
141 description: "Returns DNS server IPs and sreach domain.",
142 type: Object,
143 properties: {
144 digest: {
145 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
146 },
147 search: {
148 optional: true,
149 schema: SEARCH_DOMAIN_SCHEMA,
150 },
151 dns1: {
152 optional: true,
153 schema: FIRST_DNS_SERVER_SCHEMA,
154 },
155 dns2: {
156 optional: true,
157 schema: SECOND_DNS_SERVER_SCHEMA,
158 },
159 dns3: {
160 optional: true,
161 schema: THIRD_DNS_SERVER_SCHEMA,
162 },
163 },
164 },
165 access: {
166 permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
167 }
168 )]
169 /// Read DNS settings.
170 fn get_dns(
171 _param: Value,
172 _info: &ApiMethod,
173 _rpcenv: &mut dyn RpcEnvironment,
174 ) -> Result<Value, Error> {
175
176 read_etc_resolv_conf()
177 }
178
179 pub const ROUTER: Router = Router::new()
180 .get(&API_METHOD_GET_DNS)
181 .put(&API_METHOD_UPDATE_DNS);