]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/node/dns.rs
api/compat: drop more compat imports from api_schema.rs
[proxmox-backup.git] / src / api2 / node / dns.rs
1 use std::sync::{Arc, Mutex};
2
3 use failure::*;
4 use lazy_static::lazy_static;
5 use openssl::sha;
6 use regex::Regex;
7 use serde_json::{json, Value};
8
9 use proxmox::{sortable, identity};
10 use proxmox::api::{ApiHandler, ApiMethod, Router, RpcEnvironment};
11 use proxmox::api::schema::*;
12 use proxmox::tools::fs::{file_get_contents, file_set_contents};
13 use proxmox::tools::*; // required to use IPRE!() macro ???
14
15 use crate::api2::types::*;
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 #[sortable]
109 pub const ROUTER: Router = Router::new()
110 .get(
111 &ApiMethod::new(
112 &ApiHandler::Sync(&get_dns),
113 &ObjectSchema::new(
114 "Read DNS settings.",
115 &sorted!([ ("node", false, &NODE_SCHEMA) ]),
116 )
117 ).returns(
118 &ObjectSchema::new(
119 "Returns DNS server IPs and sreach domain.",
120 &sorted!([
121 ("digest", false, &PVE_CONFIG_DIGEST_SCHEMA),
122 ("search", true, &SEARCH_DOMAIN_SCHEMA),
123 ("dns1", true, &FIRST_DNS_SERVER_SCHEMA),
124 ("dns2", true, &SECOND_DNS_SERVER_SCHEMA),
125 ("dns3", true, &THIRD_DNS_SERVER_SCHEMA),
126 ]),
127 ).schema()
128 )
129 )
130 .put(
131 &ApiMethod::new(
132 &ApiHandler::Sync(&update_dns),
133 &ObjectSchema::new(
134 "Returns DNS server IPs and sreach domain.",
135 &sorted!([
136 ("node", false, &NODE_SCHEMA),
137 ("search", false, &SEARCH_DOMAIN_SCHEMA),
138 ("dns1", true, &FIRST_DNS_SERVER_SCHEMA),
139 ("dns2", true, &SECOND_DNS_SERVER_SCHEMA),
140 ("dns3", true, &THIRD_DNS_SERVER_SCHEMA),
141 ("digest", true, &PVE_CONFIG_DIGEST_SCHEMA),
142 ]),
143 )
144 ).protected(true)
145 );