fix: http/https proxy (#7821)

* add http(s) proxy

* Add front-end translation

* fix ui description

* For linux platform, add rustls support

* fix: Fix the proxy address test function.

* add: Added default prompts for agency agreement and some multi-language translations

* add: Http proxy request client

* fix: add async http proxy func and format the code

* add: Preliminary support for flutter front-end calling rust back-end http request

* Optimize HTTP calls

* Optimize HTTP calls

* fix: Optimize HTTP requests, refine translations, and fix dependencies

* fix: Win and macOS compilation errors

* fix: web platforms

* fix: Optimize import

* fix: Fix web platform issues

* fix: Fix web platform issues

* fix: update ci

* fix: test ci

* test: test CI

* Revert "fix: update ci"

This reverts commit 2e5f247b2ed0cc63a6f6f7bbaaffd0a1223712e5.

* test: test CI

* test: test CI

* fix: fix lock file

* fix: Optimize imports
This commit is contained in:
yuluo 2024-04-25 11:46:21 +08:00 committed by GitHub
parent 66d1af63b0
commit 43a0a4f8e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
68 changed files with 1174 additions and 131 deletions

204
Cargo.lock generated
View File

@ -300,9 +300,9 @@ dependencies = [
[[package]] [[package]]
name = "async-compression" name = "async-compression"
version = "0.4.5" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60"
dependencies = [ dependencies = [
"flate2", "flate2",
"futures-core", "futures-core",
@ -517,6 +517,12 @@ version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "base64"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]] [[package]]
name = "base64ct" name = "base64ct"
version = "1.6.0" version = "1.6.0"
@ -1825,9 +1831,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.33" version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
@ -2825,9 +2831,9 @@ dependencies = [
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.24" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -2876,6 +2882,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"backtrace", "backtrace",
"base64 0.22.0",
"bytes", "bytes",
"chrono", "chrono",
"confy", "confy",
@ -2887,6 +2894,7 @@ dependencies = [
"flexi_logger", "flexi_logger",
"futures", "futures",
"futures-util", "futures-util",
"httparse",
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
@ -2898,6 +2906,8 @@ dependencies = [
"quinn", "quinn",
"rand 0.8.5", "rand 0.8.5",
"regex", "regex",
"rustls-pki-types",
"rustls-platform-verifier",
"serde 1.0.190", "serde 1.0.190",
"serde_derive", "serde_derive",
"serde_json 1.0.107", "serde_json 1.0.107",
@ -2906,9 +2916,12 @@ dependencies = [
"sysinfo", "sysinfo",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-socks", "tokio-native-tls",
"tokio-rustls 0.26.0",
"tokio-socks 0.5.1-2",
"tokio-util", "tokio-util",
"toml 0.7.8", "toml 0.7.8",
"url",
"uuid", "uuid",
"winapi 0.3.9", "winapi 0.3.9",
"zstd 0.13.0", "zstd 0.13.0",
@ -2985,9 +2998,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.9" version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -2996,9 +3009,9 @@ dependencies = [
[[package]] [[package]]
name = "http-body" name = "http-body"
version = "0.4.5" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [ dependencies = [
"bytes", "bytes",
"http", "http",
@ -3038,9 +3051,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.27" version = "0.14.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -3053,7 +3066,7 @@ dependencies = [
"httpdate", "httpdate",
"itoa 1.0.9", "itoa 1.0.9",
"pin-project-lite", "pin-project-lite",
"socket2 0.4.10", "socket2 0.5.5",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
@ -3071,7 +3084,7 @@ dependencies = [
"hyper", "hyper",
"rustls 0.21.10", "rustls 0.21.10",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls 0.24.1",
] ]
[[package]] [[package]]
@ -4565,7 +4578,7 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa" checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa"
dependencies = [ dependencies = [
"base64", "base64 0.21.5",
"indexmap 1.9.3", "indexmap 1.9.3",
"line-wrap", "line-wrap",
"quick-xml", "quick-xml",
@ -4838,7 +4851,7 @@ dependencies = [
"ring 0.16.20", "ring 0.16.20",
"rustc-hash", "rustc-hash",
"rustls 0.20.9", "rustls 0.20.9",
"rustls-native-certs", "rustls-native-certs 0.6.3",
"slab", "slab",
"thiserror", "thiserror",
"tinyvec", "tinyvec",
@ -5173,10 +5186,10 @@ dependencies = [
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.23" version = "0.11.23"
source = "git+https://github.com/rustdesk-org/reqwest" source = "git+https://github.com/rustdesk-org/reqwest#9cb758c9fb2f4edc62eb790acfd45a6a3da21ed3"
dependencies = [ dependencies = [
"async-compression", "async-compression",
"base64", "base64 0.21.5",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@ -5196,8 +5209,8 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls 0.21.10", "rustls 0.21.10",
"rustls-native-certs", "rustls-native-certs 0.6.3",
"rustls-pemfile", "rustls-pemfile 1.0.3",
"serde 1.0.190", "serde 1.0.190",
"serde_json 1.0.107", "serde_json 1.0.107",
"serde_urlencoded", "serde_urlencoded",
@ -5205,14 +5218,15 @@ dependencies = [
"system-configuration", "system-configuration",
"tokio", "tokio",
"tokio-native-tls", "tokio-native-tls",
"tokio-rustls", "tokio-rustls 0.24.1",
"tokio-socks 0.5.1",
"tokio-util", "tokio-util",
"tower-service", "tower-service",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"webpki-roots", "webpki-roots 0.25.4",
"winreg 0.50.0", "winreg 0.50.0",
] ]
@ -5358,7 +5372,6 @@ dependencies = [
"arboard", "arboard",
"async-process", "async-process",
"async-trait", "async-trait",
"base64",
"bytes", "bytes",
"cc", "cc",
"cfg-if 1.0.0", "cfg-if 1.0.0",
@ -5519,10 +5532,25 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
dependencies = [ dependencies = [
"log", "log",
"ring 0.17.5", "ring 0.17.5",
"rustls-webpki", "rustls-webpki 0.101.7",
"sct", "sct",
] ]
[[package]]
name = "rustls"
version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c4d6d8ad9f2492485e13453acbb291dd08f64441b6609c491f1c2cd2c6b4fe1"
dependencies = [
"log",
"once_cell",
"ring 0.17.5",
"rustls-pki-types",
"rustls-webpki 0.102.2",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "rustls-native-certs" name = "rustls-native-certs"
version = "0.6.3" version = "0.6.3"
@ -5530,7 +5558,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
dependencies = [ dependencies = [
"openssl-probe", "openssl-probe",
"rustls-pemfile", "rustls-pemfile 1.0.3",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-native-certs"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792"
dependencies = [
"openssl-probe",
"rustls-pemfile 2.1.2",
"rustls-pki-types",
"schannel", "schannel",
"security-framework", "security-framework",
] ]
@ -5541,9 +5582,52 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [ dependencies = [
"base64", "base64 0.21.5",
] ]
[[package]]
name = "rustls-pemfile"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
dependencies = [
"base64 0.22.0",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247"
[[package]]
name = "rustls-platform-verifier"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5f0d26fa1ce3c790f9590868f0109289a044acb954525f933e2aa3b871c157d"
dependencies = [
"core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation-sys 0.8.4",
"jni 0.19.0",
"log",
"once_cell",
"rustls 0.23.4",
"rustls-native-certs 0.7.0",
"rustls-platform-verifier-android",
"rustls-webpki 0.102.2",
"security-framework",
"security-framework-sys",
"webpki-roots 0.26.1",
"winapi 0.3.9",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84e217e7fdc8466b5b35d30f8c0a30febd29173df4a3a0c2115d306b9c4117ad"
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.101.7" version = "0.101.7"
@ -5554,6 +5638,17 @@ dependencies = [
"untrusted 0.9.0", "untrusted 0.9.0",
] ]
[[package]]
name = "rustls-webpki"
version = "0.102.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610"
dependencies = [
"ring 0.17.5",
"rustls-pki-types",
"untrusted 0.9.0",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.14" version = "1.0.14"
@ -5666,22 +5761,23 @@ dependencies = [
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "2.9.2" version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
"core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
"core-foundation-sys 0.8.4", "core-foundation-sys 0.8.4",
"libc", "libc",
"num-bigint",
"security-framework-sys", "security-framework-sys",
] ]
[[package]] [[package]]
name = "security-framework-sys" name = "security-framework-sys"
version = "2.9.1" version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef"
dependencies = [ dependencies = [
"core-foundation-sys 0.8.4", "core-foundation-sys 0.8.4",
"libc", "libc",
@ -6404,6 +6500,17 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls 0.23.4",
"rustls-pki-types",
"tokio",
]
[[package]] [[package]]
name = "tokio-socks" name = "tokio-socks"
version = "0.5.1-2" version = "0.5.1-2"
@ -6420,6 +6527,18 @@ dependencies = [
"tokio-util", "tokio-util",
] ]
[[package]]
name = "tokio-socks"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0"
dependencies = [
"either",
"futures-util",
"thiserror",
"tokio",
]
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.10" version = "0.7.10"
@ -6637,9 +6756,9 @@ dependencies = [
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.4" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]] [[package]]
name = "typenum" name = "typenum"
@ -7081,9 +7200,18 @@ dependencies = [
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "0.25.3" version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
@ -7856,6 +7984,12 @@ dependencies = [
"syn 2.0.55", "syn 2.0.55",
] ]
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
[[package]] [[package]]
name = "zip" name = "zip"
version = "0.6.6" version = "0.6.6"

View File

@ -65,7 +65,6 @@ samplerate = { version = "0.2", optional = true }
uuid = { version = "1.3", features = ["v4"] } uuid = { version = "1.3", features = ["v4"] }
clap = "4.2" clap = "4.2"
rpassword = "7.2" rpassword = "7.2"
base64 = "0.21"
num_cpus = "1.15" num_cpus = "1.15"
bytes = { version = "1.4", features = ["serde"] } bytes = { version = "1.4", features = ["serde"] }
default-net = "0.14" default-net = "0.14"
@ -145,10 +144,10 @@ wallpaper = { git = "https://github.com/21pages/wallpaper.rs" }
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
# https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support # https://github.com/rustdesk/rustdesk-server-pro/issues/189, using native-tls for better tls support
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "json", "native-tls", "gzip"], default-features=false } reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "socks", "json", "native-tls", "gzip"], default-features=false }
[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies] [target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "json", "rustls-tls", "rustls-tls-native-roots", "gzip"], default-features=false } reqwest = { git = "https://github.com/rustdesk-org/reqwest", features = ["blocking", "socks", "json", "rustls-tls", "rustls-tls-native-roots", "gzip"], default-features=false }
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
psimple = { package = "libpulse-simple-binding", version = "2.27" } psimple = { package = "libpulse-simple-binding", version = "2.27" }

View File

@ -1117,7 +1117,7 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
child: Column(children: [ child: Column(children: [
server(enabled), server(enabled),
_Card(title: 'Proxy', children: [ _Card(title: 'Proxy', children: [
_Button('Socks5 Proxy', changeSocks5Proxy, _Button('Socks5/Http(s) Proxy', changeSocks5Proxy,
enabled: enabled), enabled: enabled),
]), ]),
]), ]),
@ -2047,7 +2047,7 @@ void changeSocks5Proxy() async {
} }
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate('Socks5 Proxy')), title: Text(translate('Socks5/Http(s) Proxy')),
content: ConstrainedBox( content: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 500), constraints: const BoxConstraints(minWidth: 500),
child: Column( child: Column(
@ -2064,7 +2064,9 @@ void changeSocks5Proxy() async {
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
errorText: proxyMsg.isNotEmpty ? proxyMsg : null), errorText: proxyMsg.isNotEmpty ? proxyMsg : null,
hintText: translate('Default protocol and port are Socks5 and 1080'),
),
controller: proxyController, controller: proxyController,
autofocus: true, autofocus: true,
), ),

View File

@ -10,8 +10,8 @@ import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:bot_toast/bot_toast.dart'; import 'package:bot_toast/bot_toast.dart';
import 'package:http/http.dart' as http;
import '../utils/http_service.dart' as http;
import '../common.dart'; import '../common.dart';
final syncAbOption = 'sync-ab-with-recent-sessions'; final syncAbOption = 'sync-ab-with-recent-sessions';

View File

@ -7,7 +7,7 @@ import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import '../utils/http_service.dart' as http;
class GroupModel { class GroupModel {
final RxBool groupLoading = false.obs; final RxBool groupLoading = false.obs;

View File

@ -6,9 +6,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/hbbs/hbbs.dart'; import 'package:flutter_hbb/common/hbbs/hbbs.dart';
import 'package:flutter_hbb/models/ab_model.dart'; import 'package:flutter_hbb/models/ab_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import '../common.dart'; import '../common.dart';
import '../utils/http_service.dart' as http;
import 'model.dart'; import 'model.dart';
import 'platform_model.dart'; import 'platform_model.dart';
@ -136,7 +136,6 @@ class UserModel {
Future<LoginResponse> login(LoginRequest loginRequest) async { Future<LoginResponse> login(LoginRequest loginRequest) async {
final url = await bind.mainGetApiServer(); final url = await bind.mainGetApiServer();
final resp = await http.post(Uri.parse('$url/api/login'), final resp = await http.post(Uri.parse('$url/api/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(loginRequest.toJson())); body: jsonEncode(loginRequest.toJson()));
final Map<String, dynamic> body; final Map<String, dynamic> body;

View File

@ -0,0 +1,115 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import '../models/platform_model.dart';
export 'package:http/http.dart' show Response;
enum HttpMethod { get, post, put, delete }
class HttpService {
Future<http.Response> sendRequest(
Uri url,
HttpMethod method, {
Map<String, String>? headers,
dynamic body,
}) async {
headers ??= {'Content-Type': 'application/json'};
// Determine if there is currently a proxy setting, and if so, use FFI to call the Rust HTTP method.
final isProxy = await bind.mainGetProxyStatus();
if (!isProxy) {
return await _pollFultterHttp(url, method, headers: headers, body: body);
}
String headersJson = jsonEncode(headers);
String methodName = method.toString().split('.').last;
await bind.mainHttpRequest(
url: url.toString(),
method: methodName.toLowerCase(),
body: body,
header: headersJson);
var resJson = await _pollForResponse(url.toString());
return _parseHttpResponse(resJson);
}
Future<http.Response> _pollFultterHttp(
Uri url,
HttpMethod method, {
Map<String, String>? headers,
dynamic body,
}) async {
var response = http.Response('', 400);
switch (method) {
case HttpMethod.get:
response = await http.get(url, headers: headers);
break;
case HttpMethod.post:
response = await http.post(url, headers: headers, body: body);
break;
case HttpMethod.put:
response = await http.put(url, headers: headers, body: body);
break;
case HttpMethod.delete:
response = await http.delete(url, headers: headers, body: body);
break;
default:
throw Exception('Unsupported HTTP method');
}
return response;
}
Future<String> _pollForResponse(String url) async {
String? responseJson = " ";
while (responseJson == " ") {
responseJson = await bind.mainGetHttpStatus(url: url);
if (responseJson == null) {
throw Exception('The HTTP request failed');
}
if (responseJson == " ") {
await Future.delayed(const Duration(milliseconds: 100));
}
}
return responseJson!;
}
http.Response _parseHttpResponse(String responseJson) {
try {
var parsedJson = jsonDecode(responseJson);
String body = parsedJson['body'];
Map<String, String> headers = {};
for (var key in parsedJson['headers'].keys) {
headers[key] = parsedJson['headers'][key];
}
int statusCode = parsedJson['status_code'];
return http.Response(body, statusCode, headers: headers);
} catch (e) {
throw Exception('Failed to parse response: $e');
}
}
}
Future<http.Response> get(Uri url, {Map<String, String>? headers}) async {
return await HttpService().sendRequest(url, HttpMethod.get, headers: headers);
}
Future<http.Response> post(Uri url,
{Map<String, String>? headers, Object? body, Encoding? encoding}) async {
return await HttpService()
.sendRequest(url, HttpMethod.post, body: body, headers: headers);
}
Future<http.Response> put(Uri url,
{Map<String, String>? headers, Object? body, Encoding? encoding}) async {
return await HttpService()
.sendRequest(url, HttpMethod.put, body: body, headers: headers);
}
Future<http.Response> delete(Uri url,
{Map<String, String>? headers, Object? body, Encoding? encoding}) async {
return await HttpService()
.sendRequest(url, HttpMethod.delete, body: body, headers: headers);
}

View File

@ -770,6 +770,24 @@ class RustdeskImpl {
throw UnimplementedError(); throw UnimplementedError();
} }
Future<bool> mainGetProxyStatus({dynamic hint}) {
return Future(() => false);
}
Future<void> mainHttpRequest({
required String url,
required String method,
String? body,
required String header,
dynamic hint,
}) {
throw UnimplementedError();
}
Future<String?> mainGetHttpStatus({required String url, dynamic hint}){
throw UnimplementedError();
}
String mainGetLocalOption({required String key, dynamic hint}) { String mainGetLocalOption({required String key, dynamic hint}) {
return js.context.callMethod('getByName', ['option:local', key]); return js.context.callMethod('getByName', ['option:local', key]);
} }

View File

@ -41,10 +41,19 @@ uuid = { version = "1.3", features = ["v4"] }
# crash, versions >= 0.29.1 are affected by #GuillaumeGomez/sysinfo/1052 # crash, versions >= 0.29.1 are affected by #GuillaumeGomez/sysinfo/1052
sysinfo = { git = "https://github.com/rustdesk-org/sysinfo" } sysinfo = { git = "https://github.com/rustdesk-org/sysinfo" }
thiserror = "1.0" thiserror = "1.0"
httparse = "1.5"
base64 = "0.22"
url = "2.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
mac_address = "1.1" mac_address = "1.1"
machine-uid = { git = "https://github.com/21pages/machine-uid" } machine-uid = { git = "https://github.com/21pages/machine-uid" }
[target.'cfg(not(any(target_os = "macos", target_os = "windows")))'.dependencies]
tokio-rustls = { version = "0.26", features = ["logging", "tls12", "ring"], default-features = false }
rustls-platform-verifier = "0.3.1"
rustls-pki-types = "1.4"
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]
tokio-native-tls ="0.3"
[features] [features]
quic = [] quic = []

View File

@ -16,6 +16,7 @@ use std::{
}; };
pub use tokio; pub use tokio;
pub use tokio_util; pub use tokio_util;
pub mod proxy;
pub mod socket_client; pub mod socket_client;
pub mod tcp; pub mod tcp;
pub mod udp; pub mod udp;
@ -51,6 +52,7 @@ pub use serde_json;
pub use sysinfo; pub use sysinfo;
pub use toml; pub use toml;
pub use uuid; pub use uuid;
pub use base64;
pub use thiserror; pub use thiserror;
#[cfg(feature = "quic")] #[cfg(feature = "quic")]

View File

@ -0,0 +1,561 @@
use std::{
io::Error as IoError,
net::{SocketAddr, ToSocketAddrs},
};
use base64::{engine::general_purpose, Engine};
use httparse::{Error as HttpParseError, Response, EMPTY_HEADER};
use log::info;
use thiserror::Error as ThisError;
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufStream};
#[cfg(any(target_os = "windows", target_os = "macos"))]
use tokio_native_tls::{native_tls, TlsConnector, TlsStream};
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
use tokio_rustls::{client::TlsStream, TlsConnector};
use tokio_socks::{tcp::Socks5Stream, IntoTargetAddr};
use tokio_util::codec::Framed;
use url::Url;
use crate::{
bytes_codec::BytesCodec,
config::Socks5Server,
tcp::{DynTcpStream, FramedStream},
ResultType,
};
#[derive(Debug, ThisError)]
pub enum ProxyError {
#[error("IO Error: {0}")]
IoError(#[from] IoError),
#[error("Target parse error: {0}")]
TargetParseError(String),
#[error("HTTP parse error: {0}")]
HttpParseError(#[from] HttpParseError),
#[error("The maximum response header length is exceeded: {0}")]
MaximumResponseHeaderLengthExceeded(usize),
#[error("The end of file is reached")]
EndOfFile,
#[error("The url is error: {0}")]
UrlBadScheme(String),
#[error("The url parse error: {0}")]
UrlParseScheme(#[from] url::ParseError),
#[error("No HTTP code was found in the response")]
NoHttpCode,
#[error("The HTTP code is not equal 200: {0}")]
HttpCode200(u16),
#[error("The proxy address resolution failed: {0}")]
AddressResolutionFailed(String),
#[cfg(any(target_os = "windows", target_os = "macos"))]
#[error("The native tls error: {0}")]
NativeTlsError(#[from] tokio_native_tls::native_tls::Error),
}
const MAXIMUM_RESPONSE_HEADER_LENGTH: usize = 4096;
/// The maximum HTTP Headers, which can be parsed.
const MAXIMUM_RESPONSE_HEADERS: usize = 16;
const DEFINE_TIME_OUT: u64 = 600;
pub trait IntoUrl {
// Besides parsing as a valid `Url`, the `Url` must be a valid
// `http::Uri`, in that it makes sense to use in a network request.
fn into_url(self) -> Result<Url, ProxyError>;
fn as_str(&self) -> &str;
}
impl IntoUrl for Url {
fn into_url(self) -> Result<Url, ProxyError> {
if self.has_host() {
Ok(self)
} else {
Err(ProxyError::UrlBadScheme(self.to_string()))
}
}
fn as_str(&self) -> &str {
self.as_ref()
}
}
impl<'a> IntoUrl for &'a str {
fn into_url(self) -> Result<Url, ProxyError> {
Url::parse(self)
.map_err(ProxyError::UrlParseScheme)?
.into_url()
}
fn as_str(&self) -> &str {
self
}
}
impl<'a> IntoUrl for &'a String {
fn into_url(self) -> Result<Url, ProxyError> {
(&**self).into_url()
}
fn as_str(&self) -> &str {
self.as_ref()
}
}
impl<'a> IntoUrl for String {
fn into_url(self) -> Result<Url, ProxyError> {
(&*self).into_url()
}
fn as_str(&self) -> &str {
self.as_ref()
}
}
#[derive(Clone)]
pub struct Auth {
user_name: String,
password: String,
}
impl Auth {
fn get_proxy_authorization(&self) -> String {
format!(
"Proxy-Authorization: Basic {}\r\n",
self.get_basic_authorization()
)
}
pub fn get_basic_authorization(&self) -> String {
let authorization = format!("{}:{}", &self.user_name, &self.password);
general_purpose::STANDARD.encode(authorization.as_bytes())
}
}
#[derive(Clone)]
pub enum ProxyScheme {
Http {
auth: Option<Auth>,
host: String,
},
Https {
auth: Option<Auth>,
host: String,
},
Socks5 {
addr: SocketAddr,
auth: Option<Auth>,
remote_dns: bool,
},
}
impl ProxyScheme {
pub fn maybe_auth(&self) -> Option<&Auth> {
match self {
ProxyScheme::Http { auth, .. }
| ProxyScheme::Https { auth, .. }
| ProxyScheme::Socks5 { auth, .. } => auth.as_ref(),
}
}
fn socks5(addr: SocketAddr) -> Result<Self, ProxyError> {
Ok(ProxyScheme::Socks5 {
addr,
auth: None,
remote_dns: false,
})
}
fn http(host: &str) -> Result<Self, ProxyError> {
Ok(ProxyScheme::Http {
auth: None,
host: host.to_string(),
})
}
fn https(host: &str) -> Result<Self, ProxyError> {
Ok(ProxyScheme::Https {
auth: None,
host: host.to_string(),
})
}
fn set_basic_auth<T: Into<String>, U: Into<String>>(&mut self, username: T, password: U) {
let auth = Auth {
user_name: username.into(),
password: password.into(),
};
match self {
ProxyScheme::Http { auth: a, .. } => *a = Some(auth),
ProxyScheme::Https { auth: a, .. } => *a = Some(auth),
ProxyScheme::Socks5 { auth: a, .. } => *a = Some(auth),
}
}
fn parse(url: Url) -> Result<Self, ProxyError> {
use url::Position;
// Resolve URL to a host and port
let to_addr = || {
let addrs = url.socket_addrs(|| match url.scheme() {
"socks5" => Some(1080),
_ => None,
})?;
addrs
.into_iter()
.next()
.ok_or_else(|| ProxyError::UrlParseScheme(url::ParseError::EmptyHost))
};
let mut scheme: Self = match url.scheme() {
"http" => Self::http(&url[Position::BeforeHost..Position::AfterPort])?,
"https" => Self::https(&url[Position::BeforeHost..Position::AfterPort])?,
"socks5" => Self::socks5(to_addr()?)?,
e => return Err(ProxyError::UrlBadScheme(e.to_string())),
};
if let Some(pwd) = url.password() {
let username = url.username();
scheme.set_basic_auth(username, pwd);
}
Ok(scheme)
}
pub async fn socket_addrs(&self) -> Result<SocketAddr, ProxyError> {
info!("Resolving socket address");
match self {
ProxyScheme::Http { host, .. } => self.resolve_host(host, 80).await,
ProxyScheme::Https { host, .. } => self.resolve_host(host, 443).await,
ProxyScheme::Socks5 { addr, .. } => Ok(addr.clone()),
}
}
async fn resolve_host(&self, host: &str, default_port: u16) -> Result<SocketAddr, ProxyError> {
let (host_str, port) = match host.split_once(':') {
Some((h, p)) => (h, p.parse::<u16>().ok()),
None => (host, None),
};
let addr = (host_str, port.unwrap_or(default_port))
.to_socket_addrs()?
.next()
.ok_or_else(|| ProxyError::AddressResolutionFailed(host.to_string()))?;
Ok(addr)
}
pub fn get_domain(&self) -> Result<String, ProxyError> {
match self {
ProxyScheme::Http { host, .. } | ProxyScheme::Https { host, .. } => {
let domain = host
.split(':')
.next()
.ok_or_else(|| ProxyError::AddressResolutionFailed(host.clone()))?;
Ok(domain.to_string())
}
ProxyScheme::Socks5 { addr, .. } => match addr {
SocketAddr::V4(addr_v4) => Ok(addr_v4.ip().to_string()),
SocketAddr::V6(addr_v6) => Ok(addr_v6.ip().to_string()),
},
}
}
pub fn get_host_and_port(&self) -> Result<String, ProxyError> {
match self {
ProxyScheme::Http { host, .. } => Ok(self.append_default_port(host, 80)),
ProxyScheme::Https { host, .. } => Ok(self.append_default_port(host, 443)),
ProxyScheme::Socks5 { addr, .. } => Ok(format!("{}", addr)),
}
}
fn append_default_port(&self, host: &str, default_port: u16) -> String {
if host.contains(':') {
host.to_string()
} else {
format!("{}:{}", host, default_port)
}
}
}
pub trait IntoProxyScheme {
fn into_proxy_scheme(self) -> Result<ProxyScheme, ProxyError>;
}
impl<S: IntoUrl> IntoProxyScheme for S {
fn into_proxy_scheme(self) -> Result<ProxyScheme, ProxyError> {
// validate the URL
let url = match self.as_str().into_url() {
Ok(ok) => ok,
Err(e) => {
match e {
// If the string does not contain protocol headers, try to parse it using the socks5 protocol
ProxyError::UrlParseScheme(_source) => {
let try_this = format!("socks5://{}", self.as_str());
try_this.into_url()?
}
_ => {
return Err(e);
}
}
}
};
ProxyScheme::parse(url)
}
}
impl IntoProxyScheme for ProxyScheme {
fn into_proxy_scheme(self) -> Result<ProxyScheme, ProxyError> {
Ok(self)
}
}
#[derive(Clone)]
pub struct Proxy {
pub intercept: ProxyScheme,
ms_timeout: u64,
}
impl Proxy {
pub fn new<U: IntoProxyScheme>(proxy_scheme: U, ms_timeout: u64) -> Result<Self, ProxyError> {
Ok(Self {
intercept: proxy_scheme.into_proxy_scheme()?,
ms_timeout,
})
}
pub fn is_http_or_https(&self) -> bool {
return match self.intercept {
ProxyScheme::Socks5 { .. } => false,
_ => true,
};
}
pub fn from_conf(conf: &Socks5Server, ms_timeout: Option<u64>) -> Result<Self, ProxyError> {
let mut proxy;
match ms_timeout {
None => {
proxy = Self::new(&conf.proxy, DEFINE_TIME_OUT)?;
}
Some(time_out) => {
proxy = Self::new(&conf.proxy, time_out)?;
}
}
if !conf.password.is_empty() && !conf.username.is_empty() {
proxy = proxy.basic_auth(&conf.username, &conf.password);
}
Ok(proxy)
}
pub async fn proxy_addrs(&self) -> Result<SocketAddr, ProxyError> {
self.intercept.socket_addrs().await
}
fn basic_auth(mut self, username: &str, password: &str) -> Proxy {
self.intercept.set_basic_auth(username, password);
self
}
pub async fn connect<'t, T>(
self,
target: T,
local_addr: Option<SocketAddr>,
) -> ResultType<FramedStream>
where
T: IntoTargetAddr<'t>,
{
info!("Connect to proxy server");
let proxy = self.proxy_addrs().await?;
let local = if let Some(addr) = local_addr {
addr
} else {
crate::config::Config::get_any_listen_addr(proxy.is_ipv4())
};
let stream = super::timeout(
self.ms_timeout,
crate::tcp::new_socket(local, true)?.connect(proxy),
)
.await??;
stream.set_nodelay(true).ok();
let addr = stream.local_addr()?;
return match self.intercept {
ProxyScheme::Http { .. } => {
info!("Connect to remote http proxy server: {}", proxy);
let stream =
super::timeout(self.ms_timeout, self.http_connect(stream, target)).await??;
Ok(FramedStream(
Framed::new(DynTcpStream(Box::new(stream)), BytesCodec::new()),
addr,
None,
0,
))
}
ProxyScheme::Https { .. } => {
info!("Connect to remote https proxy server: {}", proxy);
let stream =
super::timeout(self.ms_timeout, self.https_connect(stream, target)).await??;
Ok(FramedStream(
Framed::new(DynTcpStream(Box::new(stream)), BytesCodec::new()),
addr,
None,
0,
))
}
ProxyScheme::Socks5 { .. } => {
info!("Connect to remote socket5 proxy server: {}", proxy);
let stream = if let Some(auth) = self.intercept.maybe_auth() {
super::timeout(
self.ms_timeout,
Socks5Stream::connect_with_password_and_socket(
stream,
target,
&auth.user_name,
&auth.password,
),
)
.await??
} else {
super::timeout(
self.ms_timeout,
Socks5Stream::connect_with_socket(stream, target),
)
.await??
};
Ok(FramedStream(
Framed::new(DynTcpStream(Box::new(stream)), BytesCodec::new()),
addr,
None,
0,
))
}
};
}
#[cfg(any(target_os = "windows", target_os = "macos"))]
pub async fn https_connect<'a, Input, T>(
self,
io: Input,
target: T,
) -> Result<BufStream<TlsStream<Input>>, ProxyError>
where
Input: AsyncRead + AsyncWrite + Unpin,
T: IntoTargetAddr<'a>,
{
let tls_connector = TlsConnector::from(native_tls::TlsConnector::new()?);
let stream = tls_connector
.connect(&self.intercept.get_domain()?, io)
.await?;
self.http_connect(stream, target).await
}
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub async fn https_connect<'a, Input, T>(
self,
io: Input,
target: T,
) -> Result<BufStream<TlsStream<Input>>, ProxyError>
where
Input: AsyncRead + AsyncWrite + Unpin,
T: IntoTargetAddr<'a>,
{
use std::convert::TryFrom;
let verifier = rustls_platform_verifier::tls_config();
let url_domain = self.intercept.get_domain()?;
let domain = rustls_pki_types::ServerName::try_from(url_domain.as_str())
.map_err(|e| ProxyError::AddressResolutionFailed(e.to_string()))?
.to_owned();
let tls_connector = TlsConnector::from(std::sync::Arc::new(verifier));
let stream = tls_connector.connect(domain, io).await?;
self.http_connect(stream, target).await
}
pub async fn http_connect<'a, Input, T>(
self,
io: Input,
target: T,
) -> Result<BufStream<Input>, ProxyError>
where
Input: AsyncRead + AsyncWrite + Unpin,
T: IntoTargetAddr<'a>,
{
let mut stream = BufStream::new(io);
let (domain, port) = get_domain_and_port(target)?;
let request = self.make_request(&domain, port);
stream.write_all(request.as_bytes()).await?;
stream.flush().await?;
recv_and_check_response(&mut stream).await?;
Ok(stream)
}
fn make_request(&self, host: &str, port: u16) -> String {
let mut request = format!(
"CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n",
host = host,
port = port
);
if let Some(auth) = self.intercept.maybe_auth() {
request = format!("{}{}", request, auth.get_proxy_authorization());
}
request.push_str("\r\n");
request
}
}
fn get_domain_and_port<'a, T: IntoTargetAddr<'a>>(target: T) -> Result<(String, u16), ProxyError> {
let target_addr = target
.into_target_addr()
.map_err(|e| ProxyError::TargetParseError(e.to_string()))?;
match target_addr {
tokio_socks::TargetAddr::Ip(addr) => Ok((addr.ip().to_string(), addr.port())),
tokio_socks::TargetAddr::Domain(name, port) => Ok((name.to_string(), port)),
}
}
async fn get_response<IO>(stream: &mut BufStream<IO>) -> Result<String, ProxyError>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
use tokio::io::AsyncBufReadExt;
let mut response = String::new();
loop {
if stream.read_line(&mut response).await? == 0 {
return Err(ProxyError::EndOfFile);
}
if MAXIMUM_RESPONSE_HEADER_LENGTH < response.len() {
return Err(ProxyError::MaximumResponseHeaderLengthExceeded(
response.len(),
));
}
if response.ends_with("\r\n\r\n") {
return Ok(response);
}
}
}
async fn recv_and_check_response<IO>(stream: &mut BufStream<IO>) -> Result<(), ProxyError>
where
IO: AsyncRead + AsyncWrite + Unpin,
{
let response_string = get_response(stream).await?;
let mut response_headers = [EMPTY_HEADER; MAXIMUM_RESPONSE_HEADERS];
let mut response = Response::new(&mut response_headers);
let response_bytes = response_string.into_bytes();
response.parse(&response_bytes)?;
return match response.code {
Some(code) => {
if code == 200 {
Ok(())
} else {
Err(ProxyError::HttpCode200(code))
}
}
None => Err(ProxyError::NoHttpCode),
};
}

View File

@ -1,11 +1,13 @@
use crate::{ use crate::{
config::{Config, NetworkType}, config::{Config, NetworkType},
proxy::IntoProxyScheme,
tcp::FramedStream, tcp::FramedStream,
udp::FramedSocket, udp::FramedSocket,
ResultType, ResultType,
}; };
use anyhow::Context; use anyhow::Context;
use std::net::SocketAddr; use std::net::SocketAddr;
use log::info;
use tokio::net::ToSocketAddrs; use tokio::net::ToSocketAddrs;
use tokio_socks::{IntoTargetAddr, TargetAddr}; use tokio_socks::{IntoTargetAddr, TargetAddr};
@ -50,19 +52,15 @@ pub fn increase_port<T: std::string::ToString>(host: T, offset: i32) -> String {
} }
pub fn test_if_valid_server(host: &str) -> String { pub fn test_if_valid_server(host: &str) -> String {
let host = check_port(host, 0); info!("Testing server validity for host: {}", host);
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
match Config::get_network_type() { // Even if the current network type is a proxy type,
NetworkType::Direct => match host.to_socket_addrs() { // the system DNS should be used to resolve the proxy server address.
Err(err) => err.to_string(), host.into_proxy_scheme()
Ok(_) => "".to_owned(), .and_then(|scheme| scheme.get_host_and_port())
}, .and_then(|domain| domain.to_socket_addrs().map_err(Into::into))
NetworkType::ProxySocks => match &host.into_target_addr() { .map(|_| "".to_owned()) // on success, return an empty string
Err(err) => err.to_string(), .unwrap_or_else(|e| e.to_string()) // on error, convert the error into a string
Ok(_) => "".to_owned(),
},
}
} }
pub trait IsResolvedSocketAddr { pub trait IsResolvedSocketAddr {
@ -107,15 +105,7 @@ pub async fn connect_tcp_local<
ms_timeout: u64, ms_timeout: u64,
) -> ResultType<FramedStream> { ) -> ResultType<FramedStream> {
if let Some(conf) = Config::get_socks() { if let Some(conf) = Config::get_socks() {
return FramedStream::connect( return FramedStream::connect(target, local, &conf, ms_timeout).await;
conf.proxy.as_str(),
target,
local,
conf.username.as_str(),
conf.password.as_str(),
ms_timeout,
)
.await;
} }
if let Some(target) = target.resolve() { if let Some(target) = target.resolve() {
if let Some(local) = local { if let Some(local) = local {

View File

@ -1,4 +1,4 @@
use crate::{bail, bytes_codec::BytesCodec, ResultType}; use crate::{bail, bytes_codec::BytesCodec, ResultType, config::Socks5Server, proxy::Proxy};
use anyhow::Context as AnyhowCtx; use anyhow::Context as AnyhowCtx;
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{BufMut, Bytes, BytesMut};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
@ -18,20 +18,20 @@ use tokio::{
io::{AsyncRead, AsyncWrite, ReadBuf}, io::{AsyncRead, AsyncWrite, ReadBuf},
net::{lookup_host, TcpListener, TcpSocket, ToSocketAddrs}, net::{lookup_host, TcpListener, TcpSocket, ToSocketAddrs},
}; };
use tokio_socks::{tcp::Socks5Stream, IntoTargetAddr, ToProxyAddrs}; use tokio_socks::IntoTargetAddr;
use tokio_util::codec::Framed; use tokio_util::codec::Framed;
pub trait TcpStreamTrait: AsyncRead + AsyncWrite + Unpin {} pub trait TcpStreamTrait: AsyncRead + AsyncWrite + Unpin {}
pub struct DynTcpStream(Box<dyn TcpStreamTrait + Send + Sync>); pub struct DynTcpStream(pub(crate) Box<dyn TcpStreamTrait + Send + Sync>);
#[derive(Clone)] #[derive(Clone)]
pub struct Encrypt(Key, u64, u64); pub struct Encrypt(Key, u64, u64);
pub struct FramedStream( pub struct FramedStream(
Framed<DynTcpStream, BytesCodec>, pub(crate) Framed<DynTcpStream, BytesCodec>,
SocketAddr, pub(crate) SocketAddr,
Option<Encrypt>, pub(crate) Option<Encrypt>,
u64, pub(crate) u64,
); );
impl Deref for FramedStream { impl Deref for FramedStream {
@ -62,7 +62,7 @@ impl DerefMut for DynTcpStream {
} }
} }
fn new_socket(addr: std::net::SocketAddr, reuse: bool) -> Result<TcpSocket, std::io::Error> { pub(crate) fn new_socket(addr: std::net::SocketAddr, reuse: bool) -> Result<TcpSocket, std::io::Error> {
let socket = match addr { let socket = match addr {
std::net::SocketAddr::V4(..) => TcpSocket::new_v4()?, std::net::SocketAddr::V4(..) => TcpSocket::new_v4()?,
std::net::SocketAddr::V6(..) => TcpSocket::new_v6()?, std::net::SocketAddr::V6(..) => TcpSocket::new_v6()?,
@ -109,51 +109,17 @@ impl FramedStream {
bail!(format!("Failed to connect to {remote_addr}")); bail!(format!("Failed to connect to {remote_addr}"));
} }
pub async fn connect<'a, 't, P, T>( pub async fn connect<'t, T>(
proxy: P,
target: T, target: T,
local_addr: Option<SocketAddr>, local_addr: Option<SocketAddr>,
username: &'a str, proxy_conf: &Socks5Server,
password: &'a str,
ms_timeout: u64, ms_timeout: u64,
) -> ResultType<Self> ) -> ResultType<Self>
where where
P: ToProxyAddrs,
T: IntoTargetAddr<'t>, T: IntoTargetAddr<'t>,
{ {
if let Some(Ok(proxy)) = proxy.to_proxy_addrs().next().await { let proxy = Proxy::from_conf(proxy_conf, Some(ms_timeout))?;
let local = if let Some(addr) = local_addr { proxy.connect::<T>(target, local_addr).await
addr
} else {
crate::config::Config::get_any_listen_addr(proxy.is_ipv4())
};
let stream =
super::timeout(ms_timeout, new_socket(local, true)?.connect(proxy)).await??;
stream.set_nodelay(true).ok();
let stream = if username.trim().is_empty() {
super::timeout(
ms_timeout,
Socks5Stream::connect_with_socket(stream, target),
)
.await??
} else {
super::timeout(
ms_timeout,
Socks5Stream::connect_with_password_and_socket(
stream, target, username, password,
),
)
.await??
};
let addr = stream.local_addr()?;
return Ok(Self(
Framed::new(DynTcpStream(Box::new(stream)), BytesCodec::new()),
addr,
None,
0,
));
}
bail!("could not resolve to any address");
} }
pub fn local_addr(&self) -> SocketAddr { pub fn local_addr(&self) -> SocketAddr {

View File

@ -5,6 +5,8 @@ use std::{
task::Poll, task::Poll,
}; };
use serde_json::Value;
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum GrabState { pub enum GrabState {
Ready, Ready,
@ -123,7 +125,7 @@ use hbb_common::compress::decompress;
use hbb_common::{ use hbb_common::{
allow_err, allow_err,
anyhow::{anyhow, Context}, anyhow::{anyhow, Context},
bail, bail, base64,
bytes::Bytes, bytes::Bytes,
compress::compress as compress_func, compress::compress as compress_func,
config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT}, config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT},
@ -145,7 +147,10 @@ use hbb_common::{
// #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] // #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all}; use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all};
use crate::ui_interface::{get_option, set_option}; use crate::{
hbbs_http::create_http_client_async,
ui_interface::{get_option, set_option},
};
pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future<Output = ()>; pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future<Output = ()>;
@ -972,7 +977,7 @@ pub fn check_software_update() {
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn check_software_update_() -> hbb_common::ResultType<()> { async fn check_software_update_() -> hbb_common::ResultType<()> {
let url = "https://github.com/rustdesk/rustdesk/releases/latest"; let url = "https://github.com/rustdesk/rustdesk/releases/latest";
let latest_release_response = reqwest::get(url).await?; let latest_release_response = create_http_client_async().get(url).send().await?;
let latest_release_version = latest_release_response let latest_release_version = latest_release_response
.url() .url()
.path() .path()
@ -1067,7 +1072,7 @@ pub fn get_audit_server(api: String, custom: String, typ: String) -> String {
} }
pub async fn post_request(url: String, body: String, header: &str) -> ResultType<String> { pub async fn post_request(url: String, body: String, header: &str) -> ResultType<String> {
let mut req = reqwest::Client::new().post(url); let mut req = create_http_client_async().post(url);
if !header.is_empty() { if !header.is_empty() {
let tmp: Vec<&str> = header.split(": ").collect(); let tmp: Vec<&str> = header.split(": ").collect();
if tmp.len() == 2 { if tmp.len() == 2 {
@ -1084,6 +1089,65 @@ pub async fn post_request_sync(url: String, body: String, header: &str) -> Resul
post_request(url, body, header).await post_request(url, body, header).await
} }
#[tokio::main(flavor = "current_thread")]
pub async fn http_request_sync(
url: String,
method: String,
body: Option<String>,
header: String,
) -> ResultType<String> {
let http_client = create_http_client_async();
let mut http_client = match method.as_str() {
"get" => http_client.get(url),
"post" => http_client.post(url),
"put" => http_client.put(url),
"delete" => http_client.delete(url),
_ => return Err(anyhow!("The HTTP request method is not supported!")),
};
let v = serde_json::from_str(header.as_str())?;
if let Value::Object(obj) = v {
for (key, value) in obj.iter() {
http_client = http_client.header(key, value.as_str().unwrap_or_default());
}
} else {
return Err(anyhow!("HTTP header information parsing failed!"));
}
if let Some(b) = body {
http_client = http_client.body(b);
}
let response = http_client
.timeout(std::time::Duration::from_secs(12))
.send()
.await?;
// Serialize response headers
let mut response_headers = serde_json::map::Map::new();
for (key, value) in response.headers() {
response_headers.insert(
key.to_string(),
serde_json::json!(value.to_str().unwrap_or("")),
);
}
let status_code = response.status().as_u16();
let response_body = response.text().await?;
// Construct the JSON object
let mut result = serde_json::map::Map::new();
result.insert("status_code".to_string(), serde_json::json!(status_code));
result.insert(
"headers".to_string(),
serde_json::Value::Object(response_headers),
);
result.insert("body".to_string(), serde_json::json!(response_body));
// Convert map to JSON string
serde_json::to_string(&result).map_err(|e| anyhow!("Failed to serialize response: {}", e))
}
#[inline] #[inline]
pub fn make_privacy_mode_msg_with_details( pub fn make_privacy_mode_msg_with_details(
state: back_notification::PrivacyModeState, state: back_notification::PrivacyModeState,

View File

@ -1,5 +1,9 @@
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; use hbb_common::{
use hbb_common::{bail, sodiumoxide::crypto::sign, ResultType}; bail,
base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _},
sodiumoxide::crypto::sign,
ResultType,
};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)] #[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]

View File

@ -749,6 +749,10 @@ pub fn main_get_async_status() -> String {
get_async_job_status() get_async_job_status()
} }
pub fn main_get_http_status(url: String) -> Option<String> {
get_async_http_status(url)
}
pub fn main_get_option(key: String) -> String { pub fn main_get_option(key: String) -> String {
get_option(key) get_option(key)
} }
@ -805,6 +809,10 @@ pub fn main_set_socks(proxy: String, username: String, password: String) {
set_socks(proxy, username, password) set_socks(proxy, username, password)
} }
pub fn main_get_proxy_status() -> bool {
get_proxy_status()
}
pub fn main_get_socks() -> Vec<String> { pub fn main_get_socks() -> Vec<String> {
get_socks() get_socks()
} }
@ -878,9 +886,8 @@ pub fn main_get_api_server() -> String {
get_api_server() get_api_server()
} }
// This function doesn't seem to be used. pub fn main_http_request(url: String, method: String, body: Option<String>, header: String) {
pub fn main_post_request(url: String, body: String, header: String) { http_request(url,method, body, header)
post_request(url, body, header)
} }
pub fn main_get_local_option(key: String) -> SyncReturn<String> { pub fn main_get_local_option(key: String) -> SyncReturn<String> {

View File

@ -4,8 +4,11 @@ use serde_json::{Map, Value};
#[cfg(feature = "flutter")] #[cfg(feature = "flutter")]
pub mod account; pub mod account;
mod http_client;
pub mod record_upload; pub mod record_upload;
pub mod sync; pub mod sync;
pub use http_client::create_http_client;
pub use http_client::create_http_client_async;
#[derive(Debug)] #[derive(Debug)]
pub enum HbbHttpResponse<T> { pub enum HbbHttpResponse<T> {

View File

@ -1,4 +1,5 @@
use super::HbbHttpResponse; use super::HbbHttpResponse;
use crate::hbbs_http::create_http_client;
use hbb_common::{config::LocalConfig, log, ResultType}; use hbb_common::{config::LocalConfig, log, ResultType};
use reqwest::blocking::Client; use reqwest::blocking::Client;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -130,7 +131,7 @@ impl Default for UserStatus {
impl OidcSession { impl OidcSession {
fn new() -> Self { fn new() -> Self {
Self { Self {
client: Client::new(), client: create_http_client(),
state_msg: REQUESTING_ACCOUNT_AUTH, state_msg: REQUESTING_ACCOUNT_AUTH,
failed_msg: "".to_owned(), failed_msg: "".to_owned(),
code_url: None, code_url: None,
@ -168,7 +169,7 @@ impl OidcSession {
id: &str, id: &str,
uuid: &str, uuid: &str,
) -> ResultType<HbbHttpResponse<AuthBody>> { ) -> ResultType<HbbHttpResponse<AuthBody>> {
let url = reqwest::Url::parse_with_params( let url = Url::parse_with_params(
&format!("{}/api/oidc/auth-query", api_server), &format!("{}/api/oidc/auth-query", api_server),
&[("code", code), ("id", id), ("uuid", uuid)], &[("code", code), ("id", id), ("uuid", uuid)],
)?; )?;

View File

@ -0,0 +1,71 @@
use hbb_common::config::Config;
use hbb_common::log::info;
use hbb_common::proxy::{Proxy, ProxyScheme};
use reqwest::blocking::Client as SyncClient;
use reqwest::Client as AsyncClient;
macro_rules! configure_http_client {
($builder:expr, $Client: ty) => {{
let mut builder = $builder;
let client = if let Some(conf) = Config::get_socks() {
let proxy_result = Proxy::from_conf(&conf, None);
match proxy_result {
Ok(proxy) => {
let proxy_setup = match &proxy.intercept {
ProxyScheme::Http { host, .. } =>{ reqwest::Proxy::http(format!("http://{}", host))},
ProxyScheme::Https { host, .. } => {reqwest::Proxy::https(format!("https://{}", host))},
ProxyScheme::Socks5 { addr, .. } => { reqwest::Proxy::all(&format!("socks5://{}", addr)) }
};
match proxy_setup {
Ok(p) => {
builder = builder.proxy(p);
if let Some(auth) = proxy.intercept.maybe_auth() {
let basic_auth =
format!("Basic {}", auth.get_basic_authorization());
builder = builder.default_headers(
vec![(
reqwest::header::PROXY_AUTHORIZATION,
basic_auth.parse().unwrap(),
)]
.into_iter()
.collect(),
);
}
builder.build().unwrap_or_else(|e| {
info!("Failed to create a proxied client: {}", e);
<$Client>::new()
})
}
Err(e) => {
info!("Failed to set up proxy: {}", e);
<$Client>::new()
}
}
}
Err(e) => {
info!("Failed to configure proxy: {}", e);
<$Client>::new()
}
}
} else {
builder.build().unwrap_or_else(|e| {
info!("Failed to create a client: {}", e);
<$Client>::new()
})
};
client
}};
}
pub fn create_http_client() -> SyncClient {
let builder = SyncClient::builder();
configure_http_client!(builder, SyncClient)
}
pub fn create_http_client_async() -> AsyncClient {
let builder = AsyncClient::builder();
configure_http_client!(builder, AsyncClient)
}

View File

@ -1,3 +1,4 @@
use crate::hbbs_http::create_http_client;
use bytes::Bytes; use bytes::Bytes;
use hbb_common::{bail, config::Config, lazy_static, log, ResultType}; use hbb_common::{bail, config::Config, lazy_static, log, ResultType};
use reqwest::blocking::{Body, Client}; use reqwest::blocking::{Body, Client};
@ -25,7 +26,7 @@ pub fn is_enable() -> bool {
pub fn run(rx: Receiver<RecordState>) { pub fn run(rx: Receiver<RecordState>) {
let mut uploader = RecordUploader { let mut uploader = RecordUploader {
client: Client::new(), client: create_http_client(),
api_server: crate::get_api_server( api_server: crate::get_api_server(
Config::get_option("api-server"), Config::get_option("api-server"),
Config::get_option("custom-rendezvous-server"), Config::get_option("custom-rendezvous-server"),

View File

@ -904,6 +904,9 @@ pub async fn set_socks(value: config::Socks5Server) -> ResultType<()> {
Ok(()) Ok(())
} }
pub fn get_proxy_status() -> bool {
Config::get_socks().is_some()
}
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
pub async fn test_rendezvous_server() -> ResultType<()> { pub async fn test_rendezvous_server() -> ResultType<()> {
let mut c = connect(1000, "").await?; let mut c = connect(1000, "").await?;

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "فارغ"), ("Empty", "فارغ"),
("Invalid folder name", "اسم المجلد غير صحيح"), ("Invalid folder name", "اسم المجلد غير صحيح"),
("Socks5 Proxy", "وكيل Socks5"), ("Socks5 Proxy", "وكيل Socks5"),
("Socks5/Http(s) Proxy", "وكيل Socks5/Http(s)"),
("Discovered", "المكتشفة"), ("Discovered", "المكتشفة"),
("install_daemon_tip", "للبدء مع بدء تشغيل النظام. تحتاج الى تثبيت خدمة النظام."), ("install_daemon_tip", "للبدء مع بدء تشغيل النظام. تحتاج الى تثبيت خدمة النظام."),
("Remote ID", "المعرف البعيد"), ("Remote ID", "المعرف البعيد"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", ""), ("Empty", ""),
("Invalid folder name", ""), ("Invalid folder name", ""),
("Socks5 Proxy", "Socks5 прокси"), ("Socks5 Proxy", "Socks5 прокси"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) прокси"),
("Discovered", ""), ("Discovered", ""),
("install_daemon_tip", "За стартиране с компютъра трябва да инсталирате системна услуга."), ("install_daemon_tip", "За стартиране с компютъра трябва да инсталирате системна услуга."),
("Remote ID", ""), ("Remote ID", ""),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Buit"), ("Empty", "Buit"),
("Invalid folder name", "Nom de carpeta incorrecte"), ("Invalid folder name", "Nom de carpeta incorrecte"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Descobert"), ("Discovered", "Descobert"),
("install_daemon_tip", ""), ("install_daemon_tip", ""),
("Remote ID", "ID remot"), ("Remote ID", "ID remot"),

View File

@ -239,6 +239,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "空空如也"), ("Empty", "空空如也"),
("Invalid folder name", "无效文件夹名称"), ("Invalid folder name", "无效文件夹名称"),
("Socks5 Proxy", "Socks5 代理"), ("Socks5 Proxy", "Socks5 代理"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) 代理"),
("Default protocol and port are Socks5 and 1080", "默认代理协议及端口为Socks5和1080"),
("Discovered", "已发现"), ("Discovered", "已发现"),
("install_daemon_tip", "为了开机启动,请安装系统服务。"), ("install_daemon_tip", "为了开机启动,请安装系统服务。"),
("Remote ID", "远程 ID"), ("Remote ID", "远程 ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Prázdné"), ("Empty", "Prázdné"),
("Invalid folder name", "Neplatný název složky"), ("Invalid folder name", "Neplatný název složky"),
("Socks5 Proxy", "Socks5 proxy"), ("Socks5 Proxy", "Socks5 proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) proxy"),
("Discovered", "Objeveno"), ("Discovered", "Objeveno"),
("install_daemon_tip", "Pokud má být spouštěno při startu systému, je třeba nainstalovat systémovou službu."), ("install_daemon_tip", "Pokud má být spouštěno při startu systému, je třeba nainstalovat systémovou službu."),
("Remote ID", "Vzdálené ID"), ("Remote ID", "Vzdálené ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Tom"), ("Empty", "Tom"),
("Invalid folder name", "Ugyldigt mappenavn"), ("Invalid folder name", "Ugyldigt mappenavn"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Fundet"), ("Discovered", "Fundet"),
("install_daemon_tip", "For at starte efter PC'en er startet op, skal du installere systemtjenesten"), ("install_daemon_tip", "For at starte efter PC'en er startet op, skal du installere systemtjenesten"),
("Remote ID", "Fjern-ID"), ("Remote ID", "Fjern-ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Keine Einträge"), ("Empty", "Keine Einträge"),
("Invalid folder name", "Ungültiger Ordnername"), ("Invalid folder name", "Ungültiger Ordnername"),
("Socks5 Proxy", "SOCKS5-Proxy"), ("Socks5 Proxy", "SOCKS5-Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s)-Proxy"),
("Discovered", "Im LAN erkannt"), ("Discovered", "Im LAN erkannt"),
("install_daemon_tip", "Um mit System zu starten, muss der Systemdienst installiert sein."), ("install_daemon_tip", "Um mit System zu starten, muss der Systemdienst installiert sein."),
("Remote ID", "Entfernte ID"), ("Remote ID", "Entfernte ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Άδειο"), ("Empty", "Άδειο"),
("Invalid folder name", "Μη έγκυρο όνομα φακέλου"), ("Invalid folder name", "Μη έγκυρο όνομα φακέλου"),
("Socks5 Proxy", "Διαμεσολαβητής Socks5"), ("Socks5 Proxy", "Διαμεσολαβητής Socks5"),
("Socks5/Http(s) Proxy", "Διαμεσολαβητής Socks5/Http(s)"),
("Discovered", "Ανακαλύφθηκαν"), ("Discovered", "Ανακαλύφθηκαν"),
("install_daemon_tip", "Για να ξεκινά με την εκκίνηση του υπολογιστή, πρέπει να εγκαταστήσετε την υπηρεσία συστήματος"), ("install_daemon_tip", "Για να ξεκινά με την εκκίνηση του υπολογιστή, πρέπει να εγκαταστήσετε την υπηρεσία συστήματος"),
("Remote ID", "Απομακρυσμένο ID"), ("Remote ID", "Απομακρυσμένο ID"),

View File

@ -60,6 +60,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Add to Favorites", "Add to favorites"), ("Add to Favorites", "Add to favorites"),
("Remove from Favorites", "Remove from favorites"), ("Remove from Favorites", "Remove from favorites"),
("Socks5 Proxy", "Socks5 proxy"), ("Socks5 Proxy", "Socks5 proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) proxy"),
("Default protocol and port are Socks5 and 1080", "Default protocol and port are Socks5 and 1080"),
("install_daemon_tip", "For starting on boot, you need to install system service."), ("install_daemon_tip", "For starting on boot, you need to install system service."),
("Are you sure to close the connection?", "Are you sure you want to close the connection?"), ("Are you sure to close the connection?", "Are you sure you want to close the connection?"),
("One-Finger Tap", "One-finger tap"), ("One-Finger Tap", "One-finger tap"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Malplena"), ("Empty", "Malplena"),
("Invalid folder name", "Dosiernomo nevalida"), ("Invalid folder name", "Dosiernomo nevalida"),
("Socks5 Proxy", "Socks5 prokura servilo"), ("Socks5 Proxy", "Socks5 prokura servilo"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) prokura servilo"),
("Discovered", "Malkovritaj"), ("Discovered", "Malkovritaj"),
("install_daemon_tip", ""), ("install_daemon_tip", ""),
("Remote ID", "Fora identigilo"), ("Remote ID", "Fora identigilo"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Vacío"), ("Empty", "Vacío"),
("Invalid folder name", "Nombre de carpeta incorrecto"), ("Invalid folder name", "Nombre de carpeta incorrecto"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Descubierto"), ("Discovered", "Descubierto"),
("install_daemon_tip", "Para comenzar en el encendido, debe instalar el servicio del sistema."), ("install_daemon_tip", "Para comenzar en el encendido, debe instalar el servicio del sistema."),
("Remote ID", "ID remoto"), ("Remote ID", "ID remoto"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", ""), ("Empty", ""),
("Invalid folder name", ""), ("Invalid folder name", ""),
("Socks5 Proxy", "Socks5 proksi"), ("Socks5 Proxy", "Socks5 proksi"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) proksi"),
("Discovered", ""), ("Discovered", ""),
("install_daemon_tip", "Süsteemikäivitusel käivitamiseks tuleb paigaldada süsteemiteenus."), ("install_daemon_tip", "Süsteemikäivitusel käivitamiseks tuleb paigaldada süsteemiteenus."),
("Remote ID", ""), ("Remote ID", ""),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "موردی وجود ندارد"), ("Empty", "موردی وجود ندارد"),
("Invalid folder name", "نام پوشه نامعتبر است"), ("Invalid folder name", "نام پوشه نامعتبر است"),
("Socks5 Proxy", "Socks5 پروکسی"), ("Socks5 Proxy", "Socks5 پروکسی"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) پروکسی"),
("Discovered", "پیدا شده"), ("Discovered", "پیدا شده"),
("install_daemon_tip", "برای شروع در هنگام راه اندازی، باید سرویس سیستم را نصب کنید"), ("install_daemon_tip", "برای شروع در هنگام راه اندازی، باید سرویس سیستم را نصب کنید"),
("Remote ID", "شناسه راه دور"), ("Remote ID", "شناسه راه دور"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Vide"), ("Empty", "Vide"),
("Invalid folder name", "Nom de dossier invalide"), ("Invalid folder name", "Nom de dossier invalide"),
("Socks5 Proxy", "Socks5 Agents"), ("Socks5 Proxy", "Socks5 Agents"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Agents"),
("Discovered", "Découvert"), ("Discovered", "Découvert"),
("install_daemon_tip", "Pour une exécution au démarrage du système, vous devez installer le service système."), ("install_daemon_tip", "Pour une exécution au démarrage du système, vous devez installer le service système."),
("Remote ID", "ID de l'appareil distant"), ("Remote ID", "ID de l'appareil distant"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", ""), ("Empty", ""),
("Invalid folder name", ""), ("Invalid folder name", ""),
("Socks5 Proxy", "פרוקסי Socks5"), ("Socks5 Proxy", "פרוקסי Socks5"),
("Socks5/Http(s) Proxy", "פרוקסי Socks5/Http(s)"),
("Discovered", ""), ("Discovered", ""),
("install_daemon_tip", "לצורך הפעלה בעת הפעלת המחשב, עליך להתקין שירות מערכת."), ("install_daemon_tip", "לצורך הפעלה בעת הפעלת המחשב, עליך להתקין שירות מערכת."),
("Remote ID", ""), ("Remote ID", ""),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Üres"), ("Empty", "Üres"),
("Invalid folder name", "Helytelen mappa név"), ("Invalid folder name", "Helytelen mappa név"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Felfedezett"), ("Discovered", "Felfedezett"),
("install_daemon_tip", "Az automatikus indításhoz szükséges a szolgáltatás telepítése"), ("install_daemon_tip", "Az automatikus indításhoz szükséges a szolgáltatás telepítése"),
("Remote ID", "Távoli azonosító"), ("Remote ID", "Távoli azonosító"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Kosong"), ("Empty", "Kosong"),
("Invalid folder name", "Nama folder tidak valid"), ("Invalid folder name", "Nama folder tidak valid"),
("Socks5 Proxy", "Proksi Socks5"), ("Socks5 Proxy", "Proksi Socks5"),
("Socks5/Http(s) Proxy", "Proksi Socks5/Http(s)"),
("Discovered", "Telah ditemukan"), ("Discovered", "Telah ditemukan"),
("install_daemon_tip", "Untuk memulai saat boot, Anda perlu menginstal system service."), ("install_daemon_tip", "Untuk memulai saat boot, Anda perlu menginstal system service."),
("Remote ID", "ID Remote"), ("Remote ID", "ID Remote"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Vuoto"), ("Empty", "Vuoto"),
("Invalid folder name", "Nome della cartella non valido"), ("Invalid folder name", "Nome della cartella non valido"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Rilevate"), ("Discovered", "Rilevate"),
("install_daemon_tip", "Per avviare il programma all'accensione, è necessario installarlo come servizio di sistema."), ("install_daemon_tip", "Per avviare il programma all'accensione, è necessario installarlo come servizio di sistema."),
("Remote ID", "ID remoto"), ("Remote ID", "ID remoto"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", ""), ("Empty", ""),
("Invalid folder name", "無効なフォルダ名"), ("Invalid folder name", "無効なフォルダ名"),
("Socks5 Proxy", "SOCKS5プロキシ"), ("Socks5 Proxy", "SOCKS5プロキシ"),
("Socks5/Http(s) Proxy", "Socks5/Http(s)プロキシ"),
("Discovered", "探知済み"), ("Discovered", "探知済み"),
("install_daemon_tip", "起動時に開始するには、システムサービスをインストールする必要があります。"), ("install_daemon_tip", "起動時に開始するには、システムサービスをインストールする必要があります。"),
("Remote ID", "リモートのID"), ("Remote ID", "リモートのID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "비어 있음"), ("Empty", "비어 있음"),
("Invalid folder name", "유효하지 않은 폴더명"), ("Invalid folder name", "유효하지 않은 폴더명"),
("Socks5 Proxy", "Socks5 프록시"), ("Socks5 Proxy", "Socks5 프록시"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) 프록시"),
("Discovered", "찾음"), ("Discovered", "찾음"),
("install_daemon_tip", "부팅된 이후 시스템 서비스에 설치해야 합니다."), ("install_daemon_tip", "부팅된 이후 시스템 서비스에 설치해야 합니다."),
("Remote ID", "원격 ID"), ("Remote ID", "원격 ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Бос"), ("Empty", "Бос"),
("Invalid folder name", "Бұрыс бума атауы"), ("Invalid folder name", "Бұрыс бума атауы"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Табылды"), ("Discovered", "Табылды"),
("install_daemon_tip", "Бут кезінде қосылу үшін жүйелік сербесті орнатуыныз керек."), ("install_daemon_tip", "Бут кезінде қосылу үшін жүйелік сербесті орнатуыныз керек."),
("Remote ID", "Қашықтағы ID"), ("Remote ID", "Қашықтағы ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Tuščia"), ("Empty", "Tuščia"),
("Invalid folder name", "Neteisingas aplanko pavadinimas"), ("Invalid folder name", "Neteisingas aplanko pavadinimas"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Aptikta tinkle"), ("Discovered", "Aptikta tinkle"),
("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"), ("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"),
("Remote ID", "Nuotolinis ID"), ("Remote ID", "Nuotolinis ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Tukšs"), ("Empty", "Tukšs"),
("Invalid folder name", "Nederīgs mapes nosaukums"), ("Invalid folder name", "Nederīgs mapes nosaukums"),
("Socks5 Proxy", "Socks5 starpniekserveris"), ("Socks5 Proxy", "Socks5 starpniekserveris"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) starpniekserveris"),
("Discovered", "Atklāts"), ("Discovered", "Atklāts"),
("install_daemon_tip", "Lai palaistu pie startēšanas, ir jāinstalē sistēmas serviss."), ("install_daemon_tip", "Lai palaistu pie startēšanas, ir jāinstalē sistēmas serviss."),
("Remote ID", "Attālais ID"), ("Remote ID", "Attālais ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Tom"), ("Empty", "Tom"),
("Invalid folder name", "Ugyldig mappenavn"), ("Invalid folder name", "Ugyldig mappenavn"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Oppdaget"), ("Discovered", "Oppdaget"),
("install_daemon_tip", "For å starte når PC'en har startet opp, må du installere systemtjenesten"), ("install_daemon_tip", "For å starte når PC'en har startet opp, må du installere systemtjenesten"),
("Remote ID", "Fjern-ID"), ("Remote ID", "Fjern-ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Leeg"), ("Empty", "Leeg"),
("Invalid folder name", "Ongeldige mapnaam"), ("Invalid folder name", "Ongeldige mapnaam"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Ontdekt"), ("Discovered", "Ontdekt"),
("install_daemon_tip", "Om bij het opstarten van de computer te kunnen beginnen, moet u de systeemservice installeren."), ("install_daemon_tip", "Om bij het opstarten van de computer te kunnen beginnen, moet u de systeemservice installeren."),
("Remote ID", "Externe ID"), ("Remote ID", "Externe ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Pusto"), ("Empty", "Pusto"),
("Invalid folder name", "Nieprawidłowa nazwa folderu"), ("Invalid folder name", "Nieprawidłowa nazwa folderu"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Wykryte"), ("Discovered", "Wykryte"),
("install_daemon_tip", "By uruchomić RustDesk przy starcie systemu, musisz zainstalować usługę systemową."), ("install_daemon_tip", "By uruchomić RustDesk przy starcie systemu, musisz zainstalować usługę systemową."),
("Remote ID", "Zdalne ID"), ("Remote ID", "Zdalne ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Vazio"), ("Empty", "Vazio"),
("Invalid folder name", "Nome de diretório inválido"), ("Invalid folder name", "Nome de diretório inválido"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Descoberto"), ("Discovered", "Descoberto"),
("install_daemon_tip", "Para inicialização junto do sistema, deve instalar o serviço de sistema."), ("install_daemon_tip", "Para inicialização junto do sistema, deve instalar o serviço de sistema."),
("Remote ID", "ID Remoto"), ("Remote ID", "ID Remoto"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Vazio"), ("Empty", "Vazio"),
("Invalid folder name", "Nome de diretório inválido"), ("Invalid folder name", "Nome de diretório inválido"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Descoberto"), ("Discovered", "Descoberto"),
("install_daemon_tip", "Para inicialização junto ao sistema, você deve instalar o serviço de sistema."), ("install_daemon_tip", "Para inicialização junto ao sistema, você deve instalar o serviço de sistema."),
("Remote ID", "ID Remoto"), ("Remote ID", "ID Remoto"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Gol"), ("Empty", "Gol"),
("Invalid folder name", "Denumire folder nevalidă"), ("Invalid folder name", "Denumire folder nevalidă"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Descoperite"), ("Discovered", "Descoperite"),
("install_daemon_tip", "Pentru executare la pornirea sistemului, instalează serviciul de sistem."), ("install_daemon_tip", "Pentru executare la pornirea sistemului, instalează serviciul de sistem."),
("Remote ID", "ID dispozitiv la distanță"), ("Remote ID", "ID dispozitiv la distanță"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Пусто"), ("Empty", "Пусто"),
("Invalid folder name", "Недопустимое имя папки"), ("Invalid folder name", "Недопустимое имя папки"),
("Socks5 Proxy", "SOCKS5-прокси"), ("Socks5 Proxy", "SOCKS5-прокси"),
("Socks5/Http(s) Proxy", "Socks5/Http(s)-прокси"),
("Discovered", "Найдено"), ("Discovered", "Найдено"),
("install_daemon_tip", "Для запуска при загрузке необходимо установить системную службу"), ("install_daemon_tip", "Для запуска при загрузке необходимо установить системную службу"),
("Remote ID", "Удалённый ID"), ("Remote ID", "Удалённый ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Prázdne"), ("Empty", "Prázdne"),
("Invalid folder name", "Neplatný názov adresára"), ("Invalid folder name", "Neplatný názov adresára"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Objavené"), ("Discovered", "Objavené"),
("install_daemon_tip", "Ak chcete, aby sa spúšťal pri štarte systému, musíte nainštalovať systémovú službu."), ("install_daemon_tip", "Ak chcete, aby sa spúšťal pri štarte systému, musíte nainštalovať systémovú službu."),
("Remote ID", "Vzdialené ID"), ("Remote ID", "Vzdialené ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Prazno"), ("Empty", "Prazno"),
("Invalid folder name", "Napačno ime mape"), ("Invalid folder name", "Napačno ime mape"),
("Socks5 Proxy", "Socks5 posredniški strežnik"), ("Socks5 Proxy", "Socks5 posredniški strežnik"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) posredniški strežnik"),
("Discovered", "Odkriti"), ("Discovered", "Odkriti"),
("install_daemon_tip", "Za samodejni zagon ob vklopu računalnika je potrebno dodati sistemsko storitev"), ("install_daemon_tip", "Za samodejni zagon ob vklopu računalnika je potrebno dodati sistemsko storitev"),
("Remote ID", "Oddaljeni ID"), ("Remote ID", "Oddaljeni ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Bosh"), ("Empty", "Bosh"),
("Invalid folder name", "Emri i dosjes i pavlefshëm"), ("Invalid folder name", "Emri i dosjes i pavlefshëm"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "I pambuluar"), ("Discovered", "I pambuluar"),
("install_daemon_tip", "Për të nisur në boot, duhet të instaloni shërbimin e sistemit"), ("install_daemon_tip", "Për të nisur në boot, duhet të instaloni shërbimin e sistemit"),
("Remote ID", "ID në distancë"), ("Remote ID", "ID në distancë"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Prazno"), ("Empty", "Prazno"),
("Invalid folder name", "Pogrešno ime direktorijuma"), ("Invalid folder name", "Pogrešno ime direktorijuma"),
("Socks5 Proxy", "Socks5 proksi"), ("Socks5 Proxy", "Socks5 proksi"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) proksi"),
("Discovered", "Otkriveno"), ("Discovered", "Otkriveno"),
("install_daemon_tip", "Za pokretanje pri startu sistema, treba da instalirate sistemski servis."), ("install_daemon_tip", "Za pokretanje pri startu sistema, treba da instalirate sistemski servis."),
("Remote ID", "Udaljeni ID"), ("Remote ID", "Udaljeni ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Tom"), ("Empty", "Tom"),
("Invalid folder name", "Ogiltigt mappnamn"), ("Invalid folder name", "Ogiltigt mappnamn"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Upptäckt"), ("Discovered", "Upptäckt"),
("install_daemon_tip", "För att starta efter boot måste du installera systemtjänsten."), ("install_daemon_tip", "För att starta efter boot måste du installera systemtjänsten."),
("Remote ID", "Fjärr ID"), ("Remote ID", "Fjärr ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", ""), ("Empty", ""),
("Invalid folder name", ""), ("Invalid folder name", ""),
("Socks5 Proxy", ""), ("Socks5 Proxy", ""),
("Socks5/Http(s) Proxy", ""),
("Discovered", ""), ("Discovered", ""),
("install_daemon_tip", ""), ("install_daemon_tip", ""),
("Remote ID", ""), ("Remote ID", ""),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "ว่างเปล่า"), ("Empty", "ว่างเปล่า"),
("Invalid folder name", "ชื่อโฟลเดอร์ไม่ถูกต้อง"), ("Invalid folder name", "ชื่อโฟลเดอร์ไม่ถูกต้อง"),
("Socks5 Proxy", "พรอกซี Socks5"), ("Socks5 Proxy", "พรอกซี Socks5"),
("Socks5/Http(s) Proxy", "พรอกซี Socks5/Http(s)"),
("Discovered", "ค้นพบ"), ("Discovered", "ค้นพบ"),
("install_daemon_tip", "หากต้องการใช้งานขณะระบบเริ่มต้น คุณจำเป็นจะต้องติดตั้งเซอร์วิส"), ("install_daemon_tip", "หากต้องการใช้งานขณะระบบเริ่มต้น คุณจำเป็นจะต้องติดตั้งเซอร์วิส"),
("Remote ID", "ID ปลายทาง"), ("Remote ID", "ID ปลายทาง"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Boş"), ("Empty", "Boş"),
("Invalid folder name", "Geçersiz klasör adı"), ("Invalid folder name", "Geçersiz klasör adı"),
("Socks5 Proxy", "Socks5 Proxy"), ("Socks5 Proxy", "Socks5 Proxy"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) Proxy"),
("Discovered", "Keşfedilenler"), ("Discovered", "Keşfedilenler"),
("install_daemon_tip", "Başlangıçta başlamak için sistem hizmetini yüklemeniz gerekir."), ("install_daemon_tip", "Başlangıçta başlamak için sistem hizmetini yüklemeniz gerekir."),
("Remote ID", "Uzak ID"), ("Remote ID", "Uzak ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "空空如也"), ("Empty", "空空如也"),
("Invalid folder name", "資料夾名稱無效"), ("Invalid folder name", "資料夾名稱無效"),
("Socks5 Proxy", "Socks5 代理伺服器"), ("Socks5 Proxy", "Socks5 代理伺服器"),
("Socks5/Http(s) Proxy", "Socks5/Http(s) 代理伺服器"),
("Discovered", "已探索"), ("Discovered", "已探索"),
("install_daemon_tip", "若要在開機時啟動,您需要安裝系統服務。"), ("install_daemon_tip", "若要在開機時啟動,您需要安裝系統服務。"),
("Remote ID", "遠端 ID"), ("Remote ID", "遠端 ID"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Пусто"), ("Empty", "Пусто"),
("Invalid folder name", "Неприпустима назва теки"), ("Invalid folder name", "Неприпустима назва теки"),
("Socks5 Proxy", "Проксі-сервер Socks5"), ("Socks5 Proxy", "Проксі-сервер Socks5"),
("Socks5/Http(s) Proxy", "Проксі-сервер Socks5/Http(s)"),
("Discovered", "Знайдено"), ("Discovered", "Знайдено"),
("install_daemon_tip", "Для запуску під час завантаження, вам необхідно встановити системну службу"), ("install_daemon_tip", "Для запуску під час завантаження, вам необхідно встановити системну службу"),
("Remote ID", "Віддалений ідентифікатор"), ("Remote ID", "Віддалений ідентифікатор"),

View File

@ -239,6 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty", "Trống"), ("Empty", "Trống"),
("Invalid folder name", "Tên thư mục không hợp lệ"), ("Invalid folder name", "Tên thư mục không hợp lệ"),
("Socks5 Proxy", "Proxy Socks5"), ("Socks5 Proxy", "Proxy Socks5"),
("Socks5/Http(s) Proxy", "Proxy Socks5/Http(s)"),
("Discovered", "Đuợc phát hiện"), ("Discovered", "Đuợc phát hiện"),
("install_daemon_tip", "Để chạy lúc khởi động máy, bạn cần phải cài dịch vụ hệ thống."), ("install_daemon_tip", "Để chạy lúc khởi động máy, bạn cần phải cài dịch vụ hệ thống."),
("Remote ID", "ID từ xa"), ("Remote ID", "ID từ xa"),

View File

@ -1,6 +1,5 @@
mod custom_server; mod custom_server;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; use hbb_common::{ResultType, base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}};
use hbb_common::ResultType;
use custom_server::*; use custom_server::*;
fn gen_name(lic: &CustomServer) -> ResultType<String> { fn gen_name(lic: &CustomServer) -> ResultType<String> {

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::hbbs_http::create_http_client;
use crate::{ use crate::{
flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}, flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS},
ui_interface::get_api_server, ui_interface::get_api_server,
@ -280,7 +281,7 @@ fn request_plugin_sign(id: String, msg_to_rustdesk: MsgToRustDesk) -> PluginRetu
); );
thread::spawn(move || { thread::spawn(move || {
let sign_url = format!("{}/lic/web/api/plugin-sign", get_api_server()); let sign_url = format!("{}/lic/web/api/plugin-sign", get_api_server());
let client = reqwest::blocking::Client::new(); let client = create_http_client();
let req = PluginSignReq { let req = PluginSignReq {
plugin_id: id.clone(), plugin_id: id.clone(),
version: signature_data.version, version: signature_data.version,

View File

@ -3,6 +3,7 @@
use super::{desc::Meta as PluginMeta, ipc::InstallStatus, *}; use super::{desc::Meta as PluginMeta, ipc::InstallStatus, *};
use crate::flutter; use crate::flutter;
use crate::hbbs_http::create_http_client;
use hbb_common::{allow_err, bail, log, tokio, toml}; use hbb_common::{allow_err, bail, log, tokio, toml};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use serde_json; use serde_json;
@ -67,7 +68,7 @@ fn get_source_plugins() -> HashMap<String, PluginInfo> {
let mut plugins = HashMap::new(); let mut plugins = HashMap::new();
for source in get_plugin_source_list().into_iter() { for source in get_plugin_source_list().into_iter() {
let url = format!("{}/meta.toml", source.url); let url = format!("{}/meta.toml", source.url);
match reqwest::blocking::get(&url) { match create_http_client().get(&url).send() {
Ok(resp) => { Ok(resp) => {
if !resp.status().is_success() { if !resp.status().is_success() {
log::error!( log::error!(
@ -441,6 +442,7 @@ fn update_uninstall_id_set(set: HashSet<String>) -> ResultType<()> {
// install process // install process
pub(super) mod install { pub(super) mod install {
use super::IPC_PLUGIN_POSTFIX; use super::IPC_PLUGIN_POSTFIX;
use crate::hbbs_http::create_http_client;
use crate::{ use crate::{
ipc::{connect, Data}, ipc::{connect, Data},
plugin::ipc::{InstallStatus, Plugin}, plugin::ipc::{InstallStatus, Plugin},
@ -469,7 +471,7 @@ pub(super) mod install {
} }
fn download_to_file(url: &str, file: File) -> ResultType<()> { fn download_to_file(url: &str, file: File) -> ResultType<()> {
let resp = match reqwest::blocking::get(url) { let resp = match create_http_client().get(url).send() {
Ok(resp) => resp, Ok(resp) => resp,
Err(e) => { Err(e) => {
bail!("get plugin from '{}', {}", url, e); bail!("get plugin from '{}', {}", url, e);

View File

@ -15,6 +15,7 @@ use hbb_common::{
config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT, REG_INTERVAL, RENDEZVOUS_PORT}, config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT, REG_INTERVAL, RENDEZVOUS_PORT},
futures::future::join_all, futures::future::join_all,
log, log,
proxy::Proxy,
protobuf::Message as _, protobuf::Message as _,
rendezvous_proto::*, rendezvous_proto::*,
sleep, sleep,
@ -388,7 +389,14 @@ impl RendezvousMediator {
pub async fn start(server: ServerPtr, host: String) -> ResultType<()> { pub async fn start(server: ServerPtr, host: String) -> ResultType<()> {
log::info!("start rendezvous mediator of {}", host); log::info!("start rendezvous mediator of {}", host);
if cfg!(debug_assertions) && option_env!("TEST_TCP").is_some() { //If the investment agent type is http or https, then tcp forwarding is enabled.
let is_http_proxy = if let Some(conf) = Config::get_socks() {
let proxy = Proxy::from_conf(&conf, None)?;
proxy.is_http_or_https()
} else {
false
};
if (cfg!(debug_assertions) && option_env!("TEST_TCP").is_some()) || is_http_proxy {
Self::start_tcp(server, host).await Self::start_tcp(server, host).await
} else { } else {
Self::start_udp(server, host).await Self::start_udp(server, host).await

View File

@ -548,6 +548,10 @@ impl UI {
change_id_shared(id, old_id); change_id_shared(id, old_id);
} }
fn http_request(&self, url: String, method: String, body: Option<String>, header: String) {
http_request(url, method, body, header)
}
fn post_request(&self, url: String, body: String, header: String) { fn post_request(&self, url: String, body: String, header: String) {
post_request(url, body, header) post_request(url, body, header)
} }
@ -560,6 +564,10 @@ impl UI {
get_async_job_status() get_async_job_status()
} }
fn get_http_status(&self, url: String) -> Option<String> {
get_async_http_status(url)
}
fn t(&self, name: String) -> String { fn t(&self, name: String) -> String {
crate::client::translate(name) crate::client::translate(name)
} }

View File

@ -4,7 +4,7 @@ use hbb_common::{
allow_err, allow_err,
bytes::Bytes, bytes::Bytes,
config::{ config::{
self, Config, LocalConfig, PeerConfig, CONNECT_TIMEOUT, HARD_SETTINGS, RENDEZVOUS_PORT, self, Config, LocalConfig, PeerConfig, CONNECT_TIMEOUT, RENDEZVOUS_PORT,
}, },
directories_next, directories_next,
futures::future::join_all, futures::future::join_all,
@ -65,6 +65,7 @@ lazy_static::lazy_static! {
id: "".to_owned(), id: "".to_owned(),
})); }));
static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default(); static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default();
static ref ASYNC_HTTP_STATUS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(HashMap::new()));
static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned())); static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned()));
} }
@ -421,6 +422,16 @@ pub fn set_socks(proxy: String, username: String, password: String) {
.ok(); .ok();
} }
#[inline]
pub fn get_proxy_status() -> bool {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
return ipc::get_proxy_status();
// Currently, only the desktop version has proxy settings.
#[cfg(any(target_os = "android", target_os = "ios"))]
return false;
}
#[cfg(any(target_os = "android", target_os = "ios"))] #[cfg(any(target_os = "android", target_os = "ios"))]
pub fn set_socks(_: String, _: String, _: String) {} pub fn set_socks(_: String, _: String, _: String) {}
@ -708,6 +719,28 @@ pub fn change_id(id: String) {
}); });
} }
#[inline]
pub fn http_request(url: String, method: String, body: Option<String>, header: String) {
// Respond to concurrent requests for resources
let current_request = ASYNC_HTTP_STATUS.clone();
current_request.lock().unwrap().insert(url.clone()," ".to_owned());
std::thread::spawn(move || {
let res = match crate::http_request_sync(url.clone(), method, body, header) {
Err(err) => { log::error!("{}", err); err.to_string() },
Ok(text) => text,
};
current_request.lock().unwrap().insert(url,res);
});
}
#[inline]
pub fn get_async_http_status(url: String) -> Option<String> {
match ASYNC_HTTP_STATUS.lock().unwrap().get(&url) {
None => {None}
Some(_str) => {Some(_str.to_string())}
}
}
#[inline] #[inline]
pub fn post_request(url: String, body: String, header: String) { pub fn post_request(url: String, body: String, header: String) {
*ASYNC_JOB_STATUS.lock().unwrap() = " ".to_owned(); *ASYNC_JOB_STATUS.lock().unwrap() = " ".to_owned();