clash.rs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. use crate::utils::{dirs, help};
  2. use anyhow::Result;
  3. use serde::{Deserialize, Serialize};
  4. use serde_yaml::{Mapping, Value};
  5. use std::{
  6. net::{IpAddr, Ipv4Addr, SocketAddr},
  7. str::FromStr,
  8. };
  9. #[derive(Default, Debug, Clone)]
  10. pub struct IClashTemp(pub Mapping);
  11. impl IClashTemp {
  12. pub fn new() -> Self {
  13. match dirs::clash_path().and_then(|path| help::read_merge_mapping(&path)) {
  14. Ok(map) => Self(Self::guard(map)),
  15. Err(err) => {
  16. log::error!(target: "app", "{err}");
  17. Self::template()
  18. }
  19. }
  20. }
  21. pub fn template() -> Self {
  22. let mut map = Mapping::new();
  23. map.insert("mixed-port".into(), 7897.into());
  24. map.insert("log-level".into(), "info".into());
  25. map.insert("allow-lan".into(), false.into());
  26. map.insert("mode".into(), "rule".into());
  27. map.insert("external-controller".into(), "127.0.0.1:9097".into());
  28. map.insert("secret".into(), "".into());
  29. Self(map)
  30. }
  31. fn guard(mut config: Mapping) -> Mapping {
  32. let port = Self::guard_mixed_port(&config);
  33. let ctrl = Self::guard_server_ctrl(&config);
  34. config.insert("mixed-port".into(), port.into());
  35. config.insert("external-controller".into(), ctrl.into());
  36. config
  37. }
  38. pub fn patch_config(&mut self, patch: Mapping) {
  39. for (key, value) in patch.into_iter() {
  40. self.0.insert(key, value);
  41. }
  42. }
  43. pub fn save_config(&self) -> Result<()> {
  44. help::save_yaml(
  45. &dirs::clash_path()?,
  46. &self.0,
  47. Some("# Generated by Clash Verge"),
  48. )
  49. }
  50. pub fn get_mixed_port(&self) -> u16 {
  51. Self::guard_mixed_port(&self.0)
  52. }
  53. pub fn get_client_info(&self) -> ClashInfo {
  54. let config = &self.0;
  55. ClashInfo {
  56. port: Self::guard_mixed_port(&config),
  57. server: Self::guard_client_ctrl(&config),
  58. secret: config.get("secret").and_then(|value| match value {
  59. Value::String(val_str) => Some(val_str.clone()),
  60. Value::Bool(val_bool) => Some(val_bool.to_string()),
  61. Value::Number(val_num) => Some(val_num.to_string()),
  62. _ => None,
  63. }),
  64. }
  65. }
  66. pub fn guard_mixed_port(config: &Mapping) -> u16 {
  67. let mut port = config
  68. .get("mixed-port")
  69. .and_then(|value| match value {
  70. Value::String(val_str) => val_str.parse().ok(),
  71. Value::Number(val_num) => val_num.as_u64().map(|u| u as u16),
  72. _ => None,
  73. })
  74. .unwrap_or(7897);
  75. if port == 0 {
  76. port = 7897;
  77. }
  78. port
  79. }
  80. pub fn guard_server_ctrl(config: &Mapping) -> String {
  81. config
  82. .get("external-controller")
  83. .and_then(|value| match value.as_str() {
  84. Some(val_str) => {
  85. let val_str = val_str.trim();
  86. let val = match val_str.starts_with(":") {
  87. true => format!("127.0.0.1{val_str}"),
  88. false => val_str.to_owned(),
  89. };
  90. SocketAddr::from_str(val.as_str())
  91. .ok()
  92. .map(|s| s.to_string())
  93. }
  94. None => None,
  95. })
  96. .unwrap_or("127.0.0.1:9097".into())
  97. }
  98. pub fn guard_client_ctrl(config: &Mapping) -> String {
  99. let value = Self::guard_server_ctrl(config);
  100. match SocketAddr::from_str(value.as_str()) {
  101. Ok(mut socket) => {
  102. if socket.ip().is_unspecified() {
  103. socket.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
  104. }
  105. socket.to_string()
  106. }
  107. Err(_) => "127.0.0.1:9097".into(),
  108. }
  109. }
  110. }
  111. #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
  112. pub struct ClashInfo {
  113. /// clash core port
  114. pub port: u16,
  115. /// same as `external-controller`
  116. pub server: String,
  117. /// clash secret
  118. pub secret: Option<String>,
  119. }
  120. #[test]
  121. fn test_clash_info() {
  122. fn get_case<T: Into<Value>, D: Into<Value>>(mp: T, ec: D) -> ClashInfo {
  123. let mut map = Mapping::new();
  124. map.insert("mixed-port".into(), mp.into());
  125. map.insert("external-controller".into(), ec.into());
  126. IClashTemp(IClashTemp::guard(map)).get_client_info()
  127. }
  128. fn get_result<S: Into<String>>(port: u16, server: S) -> ClashInfo {
  129. ClashInfo {
  130. port,
  131. server: server.into(),
  132. secret: None,
  133. }
  134. }
  135. assert_eq!(
  136. IClashTemp(IClashTemp::guard(Mapping::new())).get_client_info(),
  137. get_result(7897, "127.0.0.1:9097")
  138. );
  139. assert_eq!(get_case("", ""), get_result(7897, "127.0.0.1:9097"));
  140. assert_eq!(get_case(65537, ""), get_result(1, "127.0.0.1:9097"));
  141. assert_eq!(
  142. get_case(8888, "127.0.0.1:8888"),
  143. get_result(8888, "127.0.0.1:8888")
  144. );
  145. assert_eq!(
  146. get_case(8888, " :98888 "),
  147. get_result(8888, "127.0.0.1:9097")
  148. );
  149. assert_eq!(
  150. get_case(8888, "0.0.0.0:8080 "),
  151. get_result(8888, "127.0.0.1:8080")
  152. );
  153. assert_eq!(
  154. get_case(8888, "0.0.0.0:8080"),
  155. get_result(8888, "127.0.0.1:8080")
  156. );
  157. assert_eq!(
  158. get_case(8888, "[::]:8080"),
  159. get_result(8888, "127.0.0.1:8080")
  160. );
  161. assert_eq!(
  162. get_case(8888, "192.168.1.1:8080"),
  163. get_result(8888, "192.168.1.1:8080")
  164. );
  165. assert_eq!(
  166. get_case(8888, "192.168.1.1:80800"),
  167. get_result(8888, "127.0.0.1:9097")
  168. );
  169. }
  170. #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
  171. #[serde(rename_all = "kebab-case")]
  172. pub struct IClash {
  173. pub mixed_port: Option<u16>,
  174. pub allow_lan: Option<bool>,
  175. pub log_level: Option<String>,
  176. pub ipv6: Option<bool>,
  177. pub mode: Option<String>,
  178. pub external_controller: Option<String>,
  179. pub secret: Option<String>,
  180. pub dns: Option<IClashDNS>,
  181. pub tun: Option<IClashTUN>,
  182. pub interface_name: Option<String>,
  183. }
  184. #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
  185. #[serde(rename_all = "kebab-case")]
  186. pub struct IClashTUN {
  187. pub enable: Option<bool>,
  188. pub stack: Option<String>,
  189. pub auto_route: Option<bool>,
  190. pub auto_detect_interface: Option<bool>,
  191. pub dns_hijack: Option<Vec<String>>,
  192. }
  193. #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
  194. #[serde(rename_all = "kebab-case")]
  195. pub struct IClashDNS {
  196. pub enable: Option<bool>,
  197. pub listen: Option<String>,
  198. pub default_nameserver: Option<Vec<String>>,
  199. pub enhanced_mode: Option<String>,
  200. pub fake_ip_range: Option<String>,
  201. pub use_hosts: Option<bool>,
  202. pub fake_ip_filter: Option<Vec<String>>,
  203. pub nameserver: Option<Vec<String>>,
  204. pub fallback: Option<Vec<String>>,
  205. pub fallback_filter: Option<IClashFallbackFilter>,
  206. pub nameserver_policy: Option<Vec<String>>,
  207. }
  208. #[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
  209. #[serde(rename_all = "kebab-case")]
  210. pub struct IClashFallbackFilter {
  211. pub geoip: Option<bool>,
  212. pub geoip_code: Option<String>,
  213. pub ipcidr: Option<Vec<String>>,
  214. pub domain: Option<Vec<String>>,
  215. }