clash.rs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. use crate::utils::{config, dirs};
  2. use anyhow::Result;
  3. use serde::{Deserialize, Serialize};
  4. use serde_yaml::{Mapping, Value};
  5. #[derive(Default, Debug, Clone, Deserialize, Serialize)]
  6. pub struct ClashInfo {
  7. /// clash sidecar status
  8. pub status: String,
  9. /// clash core port
  10. pub port: Option<String>,
  11. /// same as `external-controller`
  12. pub server: Option<String>,
  13. /// clash secret
  14. pub secret: Option<String>,
  15. /// mode
  16. pub mode: Option<String>,
  17. }
  18. impl ClashInfo {
  19. /// parse the clash's config.yaml
  20. /// get some information
  21. pub fn from(config: &Mapping) -> ClashInfo {
  22. let key_port_1 = Value::from("port");
  23. let key_port_2 = Value::from("mixed-port");
  24. let key_server = Value::from("external-controller");
  25. let key_secret = Value::from("secret");
  26. let key_mode = Value::from("mode");
  27. let port = match config.get(&key_port_1) {
  28. Some(value) => match value {
  29. Value::String(val_str) => Some(val_str.clone()),
  30. Value::Number(val_num) => Some(val_num.to_string()),
  31. _ => None,
  32. },
  33. _ => None,
  34. };
  35. let port = match port {
  36. Some(_) => port,
  37. None => match config.get(&key_port_2) {
  38. Some(value) => match value {
  39. Value::String(val_str) => Some(val_str.clone()),
  40. Value::Number(val_num) => Some(val_num.to_string()),
  41. _ => None,
  42. },
  43. _ => None,
  44. },
  45. };
  46. // `external-controller` could be
  47. // "127.0.0.1:9090" or ":9090"
  48. let server = match config.get(&key_server) {
  49. Some(value) => {
  50. let val_str = value.as_str().unwrap_or("");
  51. if val_str.starts_with(":") {
  52. Some(format!("127.0.0.1{val_str}"))
  53. } else {
  54. Some(val_str.into())
  55. }
  56. }
  57. _ => None,
  58. };
  59. let secret = match config.get(&key_secret) {
  60. Some(value) => match value {
  61. Value::String(val_str) => Some(val_str.clone()),
  62. Value::Bool(val_bool) => Some(val_bool.to_string()),
  63. Value::Number(val_num) => Some(val_num.to_string()),
  64. _ => None,
  65. },
  66. _ => None,
  67. };
  68. let mode = match config.get(&key_mode) {
  69. Some(value) => match value {
  70. Value::String(val_str) => Some(val_str.clone()),
  71. _ => None,
  72. },
  73. _ => None,
  74. };
  75. ClashInfo {
  76. status: "init".into(),
  77. port,
  78. server,
  79. secret,
  80. mode,
  81. }
  82. }
  83. }
  84. pub struct Clash {
  85. /// maintain the clash config
  86. pub config: Mapping,
  87. /// some info
  88. pub info: ClashInfo,
  89. }
  90. impl Clash {
  91. pub fn new() -> Clash {
  92. let config = Clash::read_config();
  93. let info = ClashInfo::from(&config);
  94. Clash { config, info }
  95. }
  96. /// get clash config
  97. pub fn read_config() -> Mapping {
  98. config::read_yaml::<Mapping>(dirs::clash_path())
  99. }
  100. /// save the clash config
  101. pub fn save_config(&self) -> Result<()> {
  102. config::save_yaml(
  103. dirs::clash_path(),
  104. &self.config,
  105. Some("# Default Config For Clash Core\n\n"),
  106. )
  107. }
  108. /// patch update the clash config
  109. /// if the port is changed then return true
  110. pub fn patch_config(&mut self, patch: Mapping) -> Result<(bool, bool)> {
  111. let port_key = Value::from("mixed-port");
  112. let server_key = Value::from("external-controller");
  113. let secret_key = Value::from("secret");
  114. let mode_key = Value::from("mode");
  115. let mut change_port = false;
  116. let mut change_info = false;
  117. let mut change_mode = false;
  118. for (key, value) in patch.into_iter() {
  119. if key == port_key {
  120. change_port = true;
  121. }
  122. if key == mode_key {
  123. change_mode = true;
  124. }
  125. if key == port_key || key == server_key || key == secret_key || key == mode_key {
  126. change_info = true;
  127. }
  128. self.config.insert(key, value);
  129. }
  130. if change_info {
  131. self.info = ClashInfo::from(&self.config);
  132. }
  133. self.save_config()?;
  134. Ok((change_port, change_mode))
  135. }
  136. /// revise the `tun` and `dns` config
  137. pub fn _tun_mode(mut config: Mapping, enable: bool) -> Mapping {
  138. macro_rules! revise {
  139. ($map: expr, $key: expr, $val: expr) => {
  140. let ret_key = Value::String($key.into());
  141. $map.insert(ret_key, Value::from($val));
  142. };
  143. }
  144. // if key not exists then append value
  145. macro_rules! append {
  146. ($map: expr, $key: expr, $val: expr) => {
  147. let ret_key = Value::String($key.into());
  148. if !$map.contains_key(&ret_key) {
  149. $map.insert(ret_key, Value::from($val));
  150. }
  151. };
  152. }
  153. // tun config
  154. let tun_val = config.get(&Value::from("tun"));
  155. let mut new_tun = Mapping::new();
  156. if tun_val.is_some() && tun_val.as_ref().unwrap().is_mapping() {
  157. new_tun = tun_val.as_ref().unwrap().as_mapping().unwrap().clone();
  158. }
  159. revise!(new_tun, "enable", enable);
  160. if enable {
  161. append!(new_tun, "stack", "gvisor");
  162. append!(new_tun, "dns-hijack", vec!["198.18.0.2:53"]);
  163. append!(new_tun, "auto-route", true);
  164. append!(new_tun, "auto-detect-interface", true);
  165. }
  166. revise!(config, "tun", new_tun);
  167. if enable {
  168. // dns config
  169. let dns_val = config.get(&Value::from("dns"));
  170. let mut new_dns = Mapping::new();
  171. if dns_val.is_some() && dns_val.as_ref().unwrap().is_mapping() {
  172. new_dns = dns_val.as_ref().unwrap().as_mapping().unwrap().clone();
  173. }
  174. revise!(new_dns, "enable", enable);
  175. // 借鉴cfw的默认配置
  176. append!(new_dns, "enhanced-mode", "fake-ip");
  177. append!(
  178. new_dns,
  179. "nameserver",
  180. vec!["114.114.114.114", "223.5.5.5", "8.8.8.8"]
  181. );
  182. append!(new_dns, "fallback", vec![] as Vec<&str>);
  183. #[cfg(target_os = "windows")]
  184. append!(
  185. new_dns,
  186. "fake-ip-filter",
  187. vec![
  188. "dns.msftncsi.com",
  189. "www.msftncsi.com",
  190. "www.msftconnecttest.com"
  191. ]
  192. );
  193. revise!(config, "dns", new_dns);
  194. }
  195. config
  196. }
  197. /// only 5 default fields available (clash config fields)
  198. /// convert to lowercase
  199. pub fn strict_filter(config: Mapping) -> Mapping {
  200. // Only the following fields are allowed:
  201. // proxies/proxy-providers/proxy-groups/rule-providers/rules
  202. let valid_keys = vec![
  203. "proxies",
  204. "proxy-providers",
  205. "proxy-groups",
  206. "rules",
  207. "rule-providers",
  208. ];
  209. let mut new_config = Mapping::new();
  210. for (key, value) in config.into_iter() {
  211. key.as_str().map(|key_str| {
  212. // change to lowercase
  213. let mut key_str = String::from(key_str);
  214. key_str.make_ascii_lowercase();
  215. // filter
  216. if valid_keys.contains(&&*key_str) {
  217. new_config.insert(Value::String(key_str), value);
  218. }
  219. });
  220. }
  221. new_config
  222. }
  223. /// more clash config fields available
  224. /// convert to lowercase
  225. pub fn loose_filter(config: Mapping) -> Mapping {
  226. // all of these can not be revised by script or merge
  227. // http/https/socks port should be under control
  228. let not_allow = vec![
  229. "port",
  230. "socks-port",
  231. "mixed-port",
  232. "allow-lan",
  233. "mode",
  234. "external-controller",
  235. "secret",
  236. "log-level",
  237. ];
  238. let mut new_config = Mapping::new();
  239. for (key, value) in config.into_iter() {
  240. key.as_str().map(|key_str| {
  241. // change to lowercase
  242. let mut key_str = String::from(key_str);
  243. key_str.make_ascii_lowercase();
  244. // filter
  245. if !not_allow.contains(&&*key_str) {
  246. new_config.insert(Value::String(key_str), value);
  247. }
  248. });
  249. }
  250. new_config
  251. }
  252. }
  253. impl Default for Clash {
  254. fn default() -> Self {
  255. Clash::new()
  256. }
  257. }