custom_server.rs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. use hbb_common::{
  2. bail,
  3. base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _},
  4. sodiumoxide::crypto::sign,
  5. ResultType,
  6. };
  7. use serde_derive::{Deserialize, Serialize};
  8. #[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]
  9. pub struct CustomServer {
  10. #[serde(default)]
  11. pub key: String,
  12. #[serde(default)]
  13. pub host: String,
  14. #[serde(default)]
  15. pub api: String,
  16. #[serde(default)]
  17. pub relay: String,
  18. }
  19. fn get_custom_server_from_config_string(s: &str) -> ResultType<CustomServer> {
  20. let tmp: String = s.chars().rev().collect();
  21. const PK: &[u8; 32] = &[
  22. 88, 168, 68, 104, 60, 5, 163, 198, 165, 38, 12, 85, 114, 203, 96, 163, 70, 48, 0, 131, 57,
  23. 12, 46, 129, 83, 17, 84, 193, 119, 197, 130, 103,
  24. ];
  25. let pk = sign::PublicKey(*PK);
  26. let data = URL_SAFE_NO_PAD.decode(tmp)?;
  27. if let Ok(lic) = serde_json::from_slice::<CustomServer>(&data) {
  28. return Ok(lic);
  29. }
  30. if let Ok(data) = sign::verify(&data, &pk) {
  31. Ok(serde_json::from_slice::<CustomServer>(&data)?)
  32. } else {
  33. bail!("sign:verify failed");
  34. }
  35. }
  36. pub fn get_custom_server_from_string(s: &str) -> ResultType<CustomServer> {
  37. let s = if s.to_lowercase().ends_with(".exe.exe") {
  38. &s[0..s.len() - 8]
  39. } else if s.to_lowercase().ends_with(".exe") {
  40. &s[0..s.len() - 4]
  41. } else {
  42. s
  43. };
  44. /*
  45. * The following code tokenizes the file name based on commas and
  46. * extracts relevant parts sequentially.
  47. *
  48. * host= is expected to be the first part.
  49. *
  50. * Since Windows renames files adding (1), (2) etc. before the .exe
  51. * in case of duplicates, which causes the host or key values to be
  52. * garbled.
  53. *
  54. * This allows using a ',' (comma) symbol as a final delimiter.
  55. */
  56. if s.to_lowercase().contains("host=") {
  57. let stripped = &s[s.to_lowercase().find("host=").unwrap_or(0)..s.len()];
  58. let strs: Vec<&str> = stripped.split(",").collect();
  59. let mut host = String::default();
  60. let mut key = String::default();
  61. let mut api = String::default();
  62. let mut relay = String::default();
  63. let strs_iter = strs.iter();
  64. for el in strs_iter {
  65. let el_lower = el.to_lowercase();
  66. if el_lower.starts_with("host=") {
  67. host = el.chars().skip(5).collect();
  68. }
  69. if el_lower.starts_with("key=") {
  70. key = el.chars().skip(4).collect();
  71. }
  72. if el_lower.starts_with("api=") {
  73. api = el.chars().skip(4).collect();
  74. }
  75. if el_lower.starts_with("relay=") {
  76. relay = el.chars().skip(6).collect();
  77. }
  78. }
  79. return Ok(CustomServer {
  80. host,
  81. key,
  82. api,
  83. relay,
  84. });
  85. } else {
  86. let s = s
  87. .replace("-licensed---", "--")
  88. .replace("-licensed--", "--")
  89. .replace("-licensed-", "--");
  90. let strs = s.split("--");
  91. for s in strs {
  92. if let Ok(lic) = get_custom_server_from_config_string(s.trim()) {
  93. return Ok(lic);
  94. } else if s.contains("(") {
  95. // https://github.com/rustdesk/rustdesk/issues/4162
  96. for s in s.split("(") {
  97. if let Ok(lic) = get_custom_server_from_config_string(s.trim()) {
  98. return Ok(lic);
  99. }
  100. }
  101. }
  102. }
  103. }
  104. bail!("Failed to parse");
  105. }
  106. #[cfg(test)]
  107. mod test {
  108. use super::*;
  109. #[test]
  110. fn test_filename_license_string() {
  111. assert!(get_custom_server_from_string("rustdesk.exe").is_err());
  112. assert!(get_custom_server_from_string("rustdesk").is_err());
  113. assert_eq!(
  114. get_custom_server_from_string("rustdesk-host=server.example.net.exe").unwrap(),
  115. CustomServer {
  116. host: "server.example.net".to_owned(),
  117. key: "".to_owned(),
  118. api: "".to_owned(),
  119. relay: "".to_owned(),
  120. }
  121. );
  122. assert_eq!(
  123. get_custom_server_from_string("rustdesk-host=server.example.net,.exe").unwrap(),
  124. CustomServer {
  125. host: "server.example.net".to_owned(),
  126. key: "".to_owned(),
  127. api: "".to_owned(),
  128. relay: "".to_owned(),
  129. }
  130. );
  131. // key in these tests is "foobar.,2" base64 encoded
  132. assert_eq!(
  133. get_custom_server_from_string(
  134. "rustdesk-host=server.example.net,api=abc,key=Zm9vYmFyLiwyCg==.exe"
  135. )
  136. .unwrap(),
  137. CustomServer {
  138. host: "server.example.net".to_owned(),
  139. key: "Zm9vYmFyLiwyCg==".to_owned(),
  140. api: "abc".to_owned(),
  141. relay: "".to_owned(),
  142. }
  143. );
  144. assert_eq!(
  145. get_custom_server_from_string(
  146. "rustdesk-host=server.example.net,key=Zm9vYmFyLiwyCg==,.exe"
  147. )
  148. .unwrap(),
  149. CustomServer {
  150. host: "server.example.net".to_owned(),
  151. key: "Zm9vYmFyLiwyCg==".to_owned(),
  152. api: "".to_owned(),
  153. relay: "".to_owned(),
  154. }
  155. );
  156. assert_eq!(
  157. get_custom_server_from_string(
  158. "rustdesk-host=server.example.net,key=Zm9vYmFyLiwyCg==,relay=server.example.net.exe"
  159. )
  160. .unwrap(),
  161. CustomServer {
  162. host: "server.example.net".to_owned(),
  163. key: "Zm9vYmFyLiwyCg==".to_owned(),
  164. api: "".to_owned(),
  165. relay: "server.example.net".to_owned(),
  166. }
  167. );
  168. assert_eq!(
  169. get_custom_server_from_string(
  170. "rustdesk-Host=server.example.net,Key=Zm9vYmFyLiwyCg==,RELAY=server.example.net.exe"
  171. )
  172. .unwrap(),
  173. CustomServer {
  174. host: "server.example.net".to_owned(),
  175. key: "Zm9vYmFyLiwyCg==".to_owned(),
  176. api: "".to_owned(),
  177. relay: "server.example.net".to_owned(),
  178. }
  179. );
  180. let lic = CustomServer {
  181. host: "1.1.1.1".to_owned(),
  182. key: "5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=".to_owned(),
  183. api: "".to_owned(),
  184. relay: "".to_owned(),
  185. };
  186. assert_eq!(
  187. get_custom_server_from_string("rustdesk-licensed-0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye.exe")
  188. .unwrap(), lic);
  189. assert_eq!(
  190. get_custom_server_from_string("rustdesk-licensed-0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye(1).exe")
  191. .unwrap(), lic);
  192. assert_eq!(
  193. get_custom_server_from_string("rustdesk--0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye(1).exe")
  194. .unwrap(), lic);
  195. assert_eq!(
  196. get_custom_server_from_string("rustdesk-licensed-0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye (1).exe")
  197. .unwrap(), lic);
  198. assert_eq!(
  199. get_custom_server_from_string("rustdesk-licensed-0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye (1) (2).exe")
  200. .unwrap(), lic);
  201. assert_eq!(
  202. get_custom_server_from_string("rustdesk-licensed-0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye--abc.exe")
  203. .unwrap(), lic);
  204. assert_eq!(
  205. get_custom_server_from_string("rustdesk-licensed--0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye--.exe")
  206. .unwrap(), lic);
  207. assert_eq!(
  208. get_custom_server_from_string("rustdesk-licensed---0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye--.exe")
  209. .unwrap(), lic);
  210. assert_eq!(
  211. get_custom_server_from_string("rustdesk-licensed--0nI900VsFHZVBVdIlncwpHS4V0bOZ0dtVldrpVO4JHdCp0YV5WdzUGZzdnYRVjI6ISeltmIsISMuEjLx4SMiojI0N3boJye--.exe")
  212. .unwrap(), lic);
  213. }
  214. }