]>
Commit | Line | Data |
---|---|---|
a2479cfa WB |
1 | use std::sync::{Arc, Mutex}; |
2 | ||
f7d4e4b5 | 3 | use anyhow::{Error}; |
a2479cfa WB |
4 | use lazy_static::lazy_static; |
5 | use openssl::sha; | |
6 | use regex::Regex; | |
7 | use serde_json::{json, Value}; | |
14627d67 | 8 | use ::serde::{Deserialize, Serialize}; |
b2b3485d | 9 | |
4b40148c | 10 | use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; |
feaa1ad3 | 11 | use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions}; |
9ea4bce4 | 12 | use proxmox::{IPRE, IPV4RE, IPV6RE, IPV4OCTET, IPV6H16, IPV6LS32}; |
7f66c29e | 13 | |
4ebf0eab | 14 | use crate::api2::types::*; |
4b40148c | 15 | use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; |
8f973f81 | 16 | |
8f973f81 DM |
17 | static RESOLV_CONF_FN: &str = "/etc/resolv.conf"; |
18 | ||
14627d67 DM |
19 | #[api()] |
20 | #[derive(Serialize, Deserialize)] | |
21 | #[allow(non_camel_case_types)] | |
22 | /// Deletable property name | |
23 | pub enum DeletableProperty { | |
24 | /// Delete first nameserver entry | |
25 | dns1, | |
26 | /// Delete second nameserver entry | |
27 | dns2, | |
28 | /// Delete third nameserver entry | |
29 | dns3, | |
30 | } | |
31 | ||
550e0d88 | 32 | pub fn read_etc_resolv_conf() -> Result<Value, Error> { |
8f973f81 DM |
33 | |
34 | let mut result = json!({}); | |
35 | ||
36 | let mut nscount = 0; | |
37 | ||
e18a6c9e | 38 | let raw = file_get_contents(RESOLV_CONF_FN)?; |
8f973f81 | 39 | |
bffd40d6 | 40 | result["digest"] = Value::from(proxmox::tools::digest_to_hex(&sha::sha256(&raw))); |
de6b0721 DM |
41 | |
42 | let data = String::from_utf8(raw)?; | |
8f973f81 DM |
43 | |
44 | lazy_static! { | |
af2fddea DM |
45 | static ref DOMAIN_REGEX: Regex = Regex::new(r"^\s*(?:search|domain)\s+(\S+)\s*").unwrap(); |
46 | static ref SERVER_REGEX: Regex = Regex::new( | |
8f973f81 DM |
47 | concat!(r"^\s*nameserver\s+(", IPRE!(), r")\s*")).unwrap(); |
48 | } | |
49 | ||
14627d67 DM |
50 | let mut options = String::new(); |
51 | ||
de6b0721 | 52 | for line in data.lines() { |
8f973f81 | 53 | |
46b79b9e DM |
54 | if let Some(caps) = DOMAIN_REGEX.captures(&line) { |
55 | result["search"] = Value::from(&caps[1]); | |
56 | } else if let Some(caps) = SERVER_REGEX.captures(&line) { | |
8f973f81 DM |
57 | nscount += 1; |
58 | if nscount > 3 { continue }; | |
46b79b9e | 59 | let nameserver = &caps[1]; |
8f973f81 | 60 | let id = format!("dns{}", nscount); |
46b79b9e | 61 | result[id] = Value::from(nameserver); |
14627d67 DM |
62 | } else { |
63 | if !options.is_empty() { options.push('\n'); } | |
64 | options.push_str(line); | |
8f973f81 DM |
65 | } |
66 | } | |
67 | ||
14627d67 DM |
68 | if !options.is_empty() { |
69 | result["options"] = options.into(); | |
70 | } | |
71 | ||
8f973f81 DM |
72 | Ok(result) |
73 | } | |
b2b3485d | 74 | |
bd098a7f DM |
75 | #[api( |
76 | protected: true, | |
77 | input: { | |
78 | description: "Update DNS settings.", | |
79 | properties: { | |
80 | node: { | |
81 | schema: NODE_SCHEMA, | |
82 | }, | |
83 | search: { | |
84 | schema: SEARCH_DOMAIN_SCHEMA, | |
14627d67 | 85 | optional: true, |
bd098a7f DM |
86 | }, |
87 | dns1: { | |
88 | optional: true, | |
89 | schema: FIRST_DNS_SERVER_SCHEMA, | |
90 | }, | |
91 | dns2: { | |
92 | optional: true, | |
93 | schema: SECOND_DNS_SERVER_SCHEMA, | |
94 | }, | |
95 | dns3: { | |
96 | optional: true, | |
97 | schema: THIRD_DNS_SERVER_SCHEMA, | |
98 | }, | |
14627d67 DM |
99 | delete: { |
100 | description: "List of properties to delete.", | |
101 | type: Array, | |
102 | optional: true, | |
103 | items: { | |
104 | type: DeletableProperty, | |
105 | } | |
106 | }, | |
bd098a7f DM |
107 | digest: { |
108 | optional: true, | |
109 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
110 | }, | |
111 | }, | |
112 | }, | |
4b40148c | 113 | access: { |
74c08a57 | 114 | permission: &Permission::Privilege(&["system", "network", "dns"], PRIV_SYS_MODIFY, false), |
4b40148c | 115 | } |
bd098a7f DM |
116 | )] |
117 | /// Update DNS settings | |
14627d67 DM |
118 | pub fn update_dns( |
119 | search: Option<String>, | |
120 | dns1: Option<String>, | |
121 | dns2: Option<String>, | |
122 | dns3: Option<String>, | |
123 | delete: Option<Vec<DeletableProperty>>, | |
124 | digest: Option<String>, | |
6049b71f | 125 | ) -> Result<Value, Error> { |
46b79b9e | 126 | |
af2fddea DM |
127 | lazy_static! { |
128 | static ref MUTEX: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); | |
129 | } | |
130 | ||
9f49fe1d | 131 | let _guard = MUTEX.lock(); |
af2fddea | 132 | |
14627d67 DM |
133 | let mut config = read_etc_resolv_conf()?; |
134 | let old_digest = config["digest"].as_str().unwrap(); | |
46b79b9e | 135 | |
14627d67 DM |
136 | if let Some(digest) = digest { |
137 | crate::tools::assert_if_modified(old_digest, &digest)?; | |
138 | } | |
af2fddea | 139 | |
14627d67 DM |
140 | if let Some(delete) = delete { |
141 | for delete_prop in delete { | |
142 | let config = config.as_object_mut().unwrap(); | |
143 | match delete_prop { | |
144 | DeletableProperty::dns1 => { config.remove("dns1"); }, | |
145 | DeletableProperty::dns2 => { config.remove("dns2"); }, | |
146 | DeletableProperty::dns3 => { config.remove("dns3"); }, | |
147 | } | |
148 | } | |
af2fddea DM |
149 | } |
150 | ||
14627d67 DM |
151 | if let Some(search) = search { config["search"] = search.into(); } |
152 | if let Some(dns1) = dns1 { config["dns1"] = dns1.into(); } | |
153 | if let Some(dns2) = dns2 { config["dns2"] = dns2.into(); } | |
154 | if let Some(dns3) = dns3 { config["dns3"] = dns3.into(); } | |
af2fddea | 155 | |
14627d67 | 156 | let mut data = String::new(); |
46b79b9e | 157 | |
14627d67 DM |
158 | if let Some(search) = config["search"].as_str() { |
159 | data.push_str(&format!("search {}\n", search)); | |
160 | } | |
46b79b9e | 161 | for opt in &["dns1", "dns2", "dns3"] { |
14627d67 | 162 | if let Some(server) = config[opt].as_str() { |
46b79b9e DM |
163 | data.push_str(&format!("nameserver {}\n", server)); |
164 | } | |
165 | } | |
14627d67 DM |
166 | if let Some(options) = config["options"].as_str() { |
167 | data.push_str(options); | |
af2fddea DM |
168 | } |
169 | ||
feaa1ad3 | 170 | replace_file(RESOLV_CONF_FN, data.as_bytes(), CreateOptions::new())?; |
46b79b9e DM |
171 | |
172 | Ok(Value::Null) | |
173 | } | |
174 | ||
bd098a7f DM |
175 | #[api( |
176 | input: { | |
177 | properties: { | |
178 | node: { | |
179 | schema: NODE_SCHEMA, | |
180 | }, | |
181 | }, | |
182 | }, | |
183 | returns: { | |
184 | description: "Returns DNS server IPs and sreach domain.", | |
185 | type: Object, | |
186 | properties: { | |
187 | digest: { | |
188 | schema: PROXMOX_CONFIG_DIGEST_SCHEMA, | |
189 | }, | |
190 | search: { | |
191 | optional: true, | |
192 | schema: SEARCH_DOMAIN_SCHEMA, | |
193 | }, | |
194 | dns1: { | |
195 | optional: true, | |
196 | schema: FIRST_DNS_SERVER_SCHEMA, | |
197 | }, | |
198 | dns2: { | |
199 | optional: true, | |
200 | schema: SECOND_DNS_SERVER_SCHEMA, | |
201 | }, | |
202 | dns3: { | |
203 | optional: true, | |
204 | schema: THIRD_DNS_SERVER_SCHEMA, | |
205 | }, | |
206 | }, | |
207 | }, | |
4b40148c | 208 | access: { |
74c08a57 | 209 | permission: &Permission::Privilege(&["system", "network", "dns"], PRIV_SYS_AUDIT, false), |
4b40148c | 210 | } |
bd098a7f DM |
211 | )] |
212 | /// Read DNS settings. | |
14627d67 | 213 | pub fn get_dns( |
6049b71f DM |
214 | _param: Value, |
215 | _info: &ApiMethod, | |
dd5495d6 | 216 | _rpcenv: &mut dyn RpcEnvironment, |
6049b71f | 217 | ) -> Result<Value, Error> { |
b2b3485d | 218 | |
8f973f81 | 219 | read_etc_resolv_conf() |
b2b3485d DM |
220 | } |
221 | ||
255f378a | 222 | pub const ROUTER: Router = Router::new() |
bd098a7f DM |
223 | .get(&API_METHOD_GET_DNS) |
224 | .put(&API_METHOD_UPDATE_DNS); |