]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/node/dns.rs
5f24da2b9345db95ed0b637ba41dd35521ada559
[proxmox-backup.git] / src / api2 / node / dns.rs
1 use failure::*;
2
3 use proxmox::tools::fs::{file_get_contents, file_set_contents};
4 use crate::api2::*;
5 use crate::api_schema::*;
6 //use crate::api_schema::router::*;
7 use crate::api2::types::*;
8
9 use lazy_static::lazy_static;
10
11 use std::sync::{Arc, Mutex};
12 use openssl::sha;
13 use regex::Regex;
14
15 use serde_json::{json, Value};
16
17 static RESOLV_CONF_FN: &str = "/etc/resolv.conf";
18
19 fn read_etc_resolv_conf() -> Result<Value, Error> {
20
21 let mut result = json!({});
22
23 let mut nscount = 0;
24
25 let raw = file_get_contents(RESOLV_CONF_FN)?;
26
27 result["digest"] = Value::from(proxmox::tools::digest_to_hex(&sha::sha256(&raw)));
28
29 let data = String::from_utf8(raw)?;
30
31 lazy_static! {
32 static ref DOMAIN_REGEX: Regex = Regex::new(r"^\s*(?:search|domain)\s+(\S+)\s*").unwrap();
33 static ref SERVER_REGEX: Regex = Regex::new(
34 concat!(r"^\s*nameserver\s+(", IPRE!(), r")\s*")).unwrap();
35 }
36
37 for line in data.lines() {
38
39 if let Some(caps) = DOMAIN_REGEX.captures(&line) {
40 result["search"] = Value::from(&caps[1]);
41 } else if let Some(caps) = SERVER_REGEX.captures(&line) {
42 nscount += 1;
43 if nscount > 3 { continue };
44 let nameserver = &caps[1];
45 let id = format!("dns{}", nscount);
46 result[id] = Value::from(nameserver);
47 }
48 }
49
50 Ok(result)
51 }
52
53 fn update_dns(
54 param: Value,
55 _info: &ApiMethod,
56 _rpcenv: &mut dyn RpcEnvironment,
57 ) -> Result<Value, Error> {
58
59 lazy_static! {
60 static ref MUTEX: Arc<Mutex<usize>> = Arc::new(Mutex::new(0));
61 }
62
63 let _guard = MUTEX.lock();
64
65 let search = crate::tools::required_string_param(&param, "search")?;
66
67 let raw = file_get_contents(RESOLV_CONF_FN)?;
68 let old_digest = proxmox::tools::digest_to_hex(&sha::sha256(&raw));
69
70 if let Some(digest) = param["digest"].as_str() {
71 crate::tools::assert_if_modified(&old_digest, &digest)?;
72 }
73
74 let old_data = String::from_utf8(raw)?;
75
76 let mut data = format!("search {}\n", search);
77
78 for opt in &["dns1", "dns2", "dns3"] {
79 if let Some(server) = param[opt].as_str() {
80 data.push_str(&format!("nameserver {}\n", server));
81 }
82 }
83
84 // append other data
85 lazy_static! {
86 static ref SKIP_REGEX: Regex = Regex::new(r"^(search|domain|nameserver)\s+").unwrap();
87 }
88 for line in old_data.lines() {
89 if SKIP_REGEX.is_match(line) { continue; }
90 data.push_str(line);
91 data.push('\n');
92 }
93
94 file_set_contents(RESOLV_CONF_FN, data.as_bytes(), None)?;
95
96 Ok(Value::Null)
97 }
98
99 fn get_dns(
100 _param: Value,
101 _info: &ApiMethod,
102 _rpcenv: &mut dyn RpcEnvironment,
103 ) -> Result<Value, Error> {
104
105 read_etc_resolv_conf()
106 }
107
108 pub fn router() -> Router {
109
110 let route = Router::new()
111 .get(
112 ApiMethod::new(
113 get_dns,
114 ObjectSchema::new("Read DNS settings.")
115 .required("node", NODE_SCHEMA.clone())
116 ).returns(
117 ObjectSchema::new("Returns DNS server IPs and sreach domain.")
118 .required("digest", PVE_CONFIG_DIGEST_SCHEMA.clone())
119 .optional("search", SEARCH_DOMAIN_SCHEMA.clone())
120 .optional("dns1", FIRST_DNS_SERVER_SCHEMA.clone())
121 .optional("dns2", SECOND_DNS_SERVER_SCHEMA.clone())
122 .optional("dns3", THIRD_DNS_SERVER_SCHEMA.clone())
123 )
124 )
125 .put(
126 ApiMethod::new(
127 update_dns,
128 ObjectSchema::new("Returns DNS server IPs and sreach domain.")
129 .required("node", NODE_SCHEMA.clone())
130 .required("search", SEARCH_DOMAIN_SCHEMA.clone())
131 .optional("dns1", FIRST_DNS_SERVER_SCHEMA.clone())
132 .optional("dns2", SECOND_DNS_SERVER_SCHEMA.clone())
133 .optional("dns3", THIRD_DNS_SERVER_SCHEMA.clone())
134 .optional("digest", PVE_CONFIG_DIGEST_SCHEMA.clone())
135 ).protected(true)
136 );
137
138 route
139 }