]>
Commit | Line | Data |
---|---|---|
a2479cfa WB |
1 | use std::sync::{Arc, Mutex}; |
2 | ||
b2b3485d | 3 | use failure::*; |
a2479cfa WB |
4 | use lazy_static::lazy_static; |
5 | use openssl::sha; | |
6 | use regex::Regex; | |
7 | use serde_json::{json, Value}; | |
b2b3485d | 8 | |
552c2259 | 9 | use proxmox::{sortable, identity}; |
a2479cfa WB |
10 | use proxmox::api::{ApiHandler, ApiMethod, Router, RpcEnvironment}; |
11 | use proxmox::api::schema::*; | |
e18a6c9e | 12 | use proxmox::tools::fs::{file_get_contents, file_set_contents}; |
7f66c29e DM |
13 | use proxmox::tools::*; // required to use IPRE!() macro ??? |
14 | ||
4ebf0eab | 15 | use crate::api2::types::*; |
8f973f81 | 16 | |
8f973f81 DM |
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 | ||
e18a6c9e | 25 | let raw = file_get_contents(RESOLV_CONF_FN)?; |
8f973f81 | 26 | |
bffd40d6 | 27 | result["digest"] = Value::from(proxmox::tools::digest_to_hex(&sha::sha256(&raw))); |
de6b0721 DM |
28 | |
29 | let data = String::from_utf8(raw)?; | |
8f973f81 DM |
30 | |
31 | lazy_static! { | |
af2fddea DM |
32 | static ref DOMAIN_REGEX: Regex = Regex::new(r"^\s*(?:search|domain)\s+(\S+)\s*").unwrap(); |
33 | static ref SERVER_REGEX: Regex = Regex::new( | |
8f973f81 DM |
34 | concat!(r"^\s*nameserver\s+(", IPRE!(), r")\s*")).unwrap(); |
35 | } | |
36 | ||
de6b0721 | 37 | for line in data.lines() { |
8f973f81 | 38 | |
46b79b9e DM |
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) { | |
8f973f81 DM |
42 | nscount += 1; |
43 | if nscount > 3 { continue }; | |
46b79b9e | 44 | let nameserver = &caps[1]; |
8f973f81 | 45 | let id = format!("dns{}", nscount); |
46b79b9e | 46 | result[id] = Value::from(nameserver); |
8f973f81 DM |
47 | } |
48 | } | |
49 | ||
50 | Ok(result) | |
51 | } | |
b2b3485d | 52 | |
6049b71f DM |
53 | fn update_dns( |
54 | param: Value, | |
55 | _info: &ApiMethod, | |
dd5495d6 | 56 | _rpcenv: &mut dyn RpcEnvironment, |
6049b71f | 57 | ) -> Result<Value, Error> { |
46b79b9e | 58 | |
af2fddea DM |
59 | lazy_static! { |
60 | static ref MUTEX: Arc<Mutex<usize>> = Arc::new(Mutex::new(0)); | |
61 | } | |
62 | ||
9f49fe1d | 63 | let _guard = MUTEX.lock(); |
af2fddea | 64 | |
e18a6c9e | 65 | let search = crate::tools::required_string_param(¶m, "search")?; |
46b79b9e | 66 | |
e18a6c9e | 67 | let raw = file_get_contents(RESOLV_CONF_FN)?; |
bffd40d6 | 68 | let old_digest = proxmox::tools::digest_to_hex(&sha::sha256(&raw)); |
af2fddea DM |
69 | |
70 | if let Some(digest) = param["digest"].as_str() { | |
e18a6c9e | 71 | crate::tools::assert_if_modified(&old_digest, &digest)?; |
af2fddea DM |
72 | } |
73 | ||
74 | let old_data = String::from_utf8(raw)?; | |
75 | ||
46b79b9e DM |
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 | ||
af2fddea DM |
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 | ||
e18a6c9e | 94 | file_set_contents(RESOLV_CONF_FN, data.as_bytes(), None)?; |
46b79b9e DM |
95 | |
96 | Ok(Value::Null) | |
97 | } | |
98 | ||
6049b71f DM |
99 | fn get_dns( |
100 | _param: Value, | |
101 | _info: &ApiMethod, | |
dd5495d6 | 102 | _rpcenv: &mut dyn RpcEnvironment, |
6049b71f | 103 | ) -> Result<Value, Error> { |
b2b3485d | 104 | |
8f973f81 | 105 | read_etc_resolv_conf() |
b2b3485d DM |
106 | } |
107 | ||
552c2259 | 108 | #[sortable] |
255f378a DM |
109 | pub const ROUTER: Router = Router::new() |
110 | .get( | |
111 | &ApiMethod::new( | |
112 | &ApiHandler::Sync(&get_dns), | |
113 | &ObjectSchema::new( | |
114 | "Read DNS settings.", | |
552c2259 | 115 | &sorted!([ ("node", false, &NODE_SCHEMA) ]), |
8f973f81 | 116 | ) |
255f378a DM |
117 | ).returns( |
118 | &ObjectSchema::new( | |
119 | "Returns DNS server IPs and sreach domain.", | |
552c2259 | 120 | &sorted!([ |
255f378a DM |
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), | |
552c2259 | 126 | ]), |
255f378a | 127 | ).schema() |
46b79b9e | 128 | ) |
255f378a DM |
129 | ) |
130 | .put( | |
131 | &ApiMethod::new( | |
132 | &ApiHandler::Sync(&update_dns), | |
133 | &ObjectSchema::new( | |
134 | "Returns DNS server IPs and sreach domain.", | |
552c2259 | 135 | &sorted!([ |
255f378a DM |
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), | |
552c2259 | 142 | ]), |
255f378a DM |
143 | ) |
144 | ).protected(true) | |
145 | ); |