allow use websocket (#11677)
Some checks are pending
CI / x86_64-unknown-linux-gnu (ubuntu-22.04) (push) Waiting to run
Full Flutter CI / run-ci (push) Waiting to run

1. Enable the RustDesk client to use WebSocket for either controlling or being controlled.
2. Fix TCP sending `register_pk` frequently

Note:
1. Because hbb_common directly uses `use_ws` to read config directly, rustdesk also directly reads config

Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
21pages 2025-05-09 12:18:49 +08:00 committed by GitHub
parent 86bbdf7a5d
commit 9475743b4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
61 changed files with 413 additions and 64 deletions

23
Cargo.lock generated
View file

@ -5724,7 +5724,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"webpki-roots 0.25.4",
"winreg 0.50.0",
]
@ -7051,8 +7051,15 @@ checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
dependencies = [
"futures-util",
"log",
"native-tls",
"rustls 0.23.26",
"rustls-native-certs 0.8.1",
"rustls-pki-types",
"tokio",
"tokio-native-tls",
"tokio-rustls 0.26.0",
"tungstenite",
"webpki-roots 0.26.9",
]
[[package]]
@ -7253,10 +7260,15 @@ dependencies = [
"http 1.3.1",
"httparse",
"log",
"native-tls",
"rand 0.9.0",
"rustls 0.23.26",
"rustls-native-certs 0.8.1",
"rustls-pki-types",
"sha1",
"thiserror 2.0.11",
"utf-8",
"webpki-roots 0.26.9",
]
[[package]]
@ -7753,6 +7765,15 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29aad86cec885cafd03e8305fd727c418e970a521322c91688414d5b8efba16b"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.8"

View file

@ -147,9 +147,13 @@ const String kOptionDirectxCapture = "enable-directx-capture";
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
const String kOptionEnableTrustedDevices = "enable-trusted-devices";
// network options
const String kOptionAllowWebSocket = "allow-websocket";
// buildin opitons
const String kOptionHideServerSetting = "hide-server-settings";
const String kOptionHideProxySetting = "hide-proxy-settings";
const String kOptionHideWebSocketSetting = "hide-websocket-settings";
const String kOptionHideRemotePrinterSetting = "hide-remote-printer-settings";
const String kOptionHideSecuritySetting = "hide-security-settings";
const String kOptionHideNetworkSetting = "hide-network-settings";

View file

@ -1475,11 +1475,70 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
bind.mainGetBuildinOption(key: kOptionHideServerSetting) == 'Y';
final hideProxy =
isWeb || bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
// final hideWebSocket = isWeb ||
// bind.mainGetBuildinOption(key: kOptionHideWebSocketSetting) == 'Y';
final hideWebSocket = true;
if (hideServer && hideProxy) {
if (hideServer && hideProxy && hideWebSocket) {
return Offstage();
}
// Helper function to create network setting ListTiles
Widget listTile({
required IconData icon,
required String title,
VoidCallback? onTap,
Widget? trailing,
bool showTooltip = false,
String tooltipMessage = '',
}) {
final titleWidget = showTooltip
? Row(
children: [
Tooltip(
waitDuration: Duration(milliseconds: 1000),
message: translate(tooltipMessage),
child: Row(
children: [
Text(
translate(title),
style: TextStyle(fontSize: _kContentFontSize),
),
SizedBox(width: 5),
Icon(
Icons.help_outline,
size: 14,
color: Theme.of(context)
.textTheme
.titleLarge
?.color
?.withOpacity(0.7),
),
],
),
),
],
)
: Text(
translate(title),
style: TextStyle(fontSize: _kContentFontSize),
);
return ListTile(
leading: Icon(icon, color: _accentColor),
title: titleWidget,
enabled: !locked,
onTap: onTap,
trailing: trailing,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding: EdgeInsets.symmetric(horizontal: 16),
minLeadingWidth: 0,
horizontalTitleGap: 10,
);
}
return _Card(
title: 'Network',
children: [
@ -1488,39 +1547,36 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!hideServer)
ListTile(
leading: Icon(Icons.dns_outlined, color: _accentColor),
title: Text(
translate('ID/Relay Server'),
style: TextStyle(fontSize: _kContentFontSize),
),
enabled: !locked,
listTile(
icon: Icons.dns_outlined,
title: 'ID/Relay Server',
onTap: () => showServerSettings(gFFI.dialogManager),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding: EdgeInsets.symmetric(horizontal: 16),
minLeadingWidth: 0,
horizontalTitleGap: 10,
),
if (!hideServer && !hideProxy)
if (!hideServer && (!hideProxy || !hideWebSocket))
Divider(height: 1, indent: 16, endIndent: 16),
if (!hideProxy)
ListTile(
leading:
Icon(Icons.network_ping_outlined, color: _accentColor),
title: Text(
translate('Socks5/Http(s) Proxy'),
style: TextStyle(fontSize: _kContentFontSize),
),
enabled: !locked,
listTile(
icon: Icons.network_ping_outlined,
title: 'Socks5/Http(s) Proxy',
onTap: changeSocks5Proxy,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
if (!hideProxy && !hideWebSocket)
Divider(height: 1, indent: 16, endIndent: 16),
if (!hideWebSocket)
listTile(
icon: Icons.web_asset_outlined,
title: 'Use WebSocket',
showTooltip: true,
tooltipMessage: 'websocket_tip',
trailing: Switch(
value: mainGetBoolOptionSync(kOptionAllowWebSocket),
onChanged: locked
? null
: (value) {
mainSetBoolOption(kOptionAllowWebSocket, value);
setState(() {});
},
),
contentPadding: EdgeInsets.symmetric(horizontal: 16),
minLeadingWidth: 0,
horizontalTitleGap: 10,
),
],
),

View file

@ -80,6 +80,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
var _enableDirectIPAccess = false;
var _enableRecordSession = false;
var _enableHardwareCodec = false;
var _allowWebSocket = false;
var _autoRecordIncomingSession = false;
var _autoRecordOutgoingSession = false;
var _allowAutoDisconnect = false;
@ -91,6 +92,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
var _hideServer = false;
var _hideProxy = false;
var _hideNetwork = false;
var _hideWebSocket = false;
var _enableTrustedDevices = false;
_SettingsState() {
@ -105,6 +107,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
bind.mainGetOptionSync(key: kOptionEnableRecordSession));
_enableHardwareCodec = option2bool(kOptionEnableHwcodec,
bind.mainGetOptionSync(key: kOptionEnableHwcodec));
_allowWebSocket = mainGetBoolOptionSync(kOptionAllowWebSocket);
_autoRecordIncomingSession = option2bool(kOptionAllowAutoRecordIncoming,
bind.mainGetOptionSync(key: kOptionAllowAutoRecordIncoming));
_autoRecordOutgoingSession = option2bool(kOptionAllowAutoRecordOutgoing,
@ -120,6 +123,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
_hideNetwork =
bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y';
_hideWebSocket =
true; //bind.mainGetBuildinOption(key: kOptionHideWebSocketSetting) == 'Y';
_enableTrustedDevices = mainGetBoolOptionSync(kOptionEnableTrustedDevices);
}
@ -667,6 +672,21 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
onPressed: (context) {
changeSocks5Proxy();
}),
if (!disabledSettings && !_hideNetwork && !_hideWebSocket)
SettingsTile.switchTile(
title: Text(translate('Use WebSocket')),
initialValue: _allowWebSocket,
onToggle: isOptionFixed(kOptionAllowWebSocket)
? null
: (v) async {
await mainSetBoolOption(kOptionAllowWebSocket, v);
final newValue =
await mainGetBoolOption(kOptionAllowWebSocket);
setState(() {
_allowWebSocket = newValue;
});
},
),
SettingsTile(
title: Text(translate('Language')),
leading: Icon(Icons.translate),

View file

@ -49,14 +49,15 @@ class PlatformFFI {
}
bool registerEventHandler(
String eventName, String handlerName, HandleEvent handler) {
String eventName, String handlerName, HandleEvent handler,
{bool replace = false}) {
debugPrint('registerEventHandler $eventName $handlerName');
var handlers = _eventHandlers[eventName];
if (handlers == null) {
_eventHandlers[eventName] = {handlerName: handler};
return true;
} else {
if (handlers.containsKey(handlerName)) {
if (!replace && handlers.containsKey(handlerName)) {
return false;
} else {
handlers[handlerName] = handler;

View file

@ -1852,5 +1852,49 @@ class RustdeskImpl {
throw UnimplementedError("sessionGetConnToken");
}
String mainGetPrinterNames({dynamic hint}) {
return '';
}
Future<void> sessionPrinterResponse(
{required UuidValue sessionId,
required int id,
required String path,
required String printerName,
dynamic hint}) {
throw UnimplementedError("sessionPrinterResponse");
}
Future<String> mainGetCommon({required String key, dynamic hint}) {
throw UnimplementedError("mainGetCommon");
}
String mainGetCommonSync({required String key, dynamic hint}) {
throw UnimplementedError("mainGetCommonSync");
}
Future<void> mainSetCommon(
{required String key, required String value, dynamic hint}) {
throw UnimplementedError("mainSetCommon");
}
Future<String> sessionHandleScreenshot(
{required UuidValue sessionId, required String action, dynamic hint}) {
throw UnimplementedError("sessionHandleScreenshot");
}
String? sessionGetCommonSync(
{required UuidValue sessionId,
required String key,
required String param,
dynamic hint}) {
throw UnimplementedError("sessionGetCommonSync");
}
Future<void> sessionTakeScreenshot(
{required UuidValue sessionId, required int display, dynamic hint}) {
throw UnimplementedError("sessionTakeScreenshot");
}
void dispose() {}
}

@ -1 +1 @@
Subproject commit d64954ae2211e5154fd9036ec4862413f24bbd7b
Subproject commit 7839dcf4e4ddc3feab9b52b0ae1903d443bc084d

View file

@ -46,7 +46,7 @@ use hbb_common::{
anyhow::{anyhow, Context},
bail,
config::{
self, Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT,
self, use_ws, Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT,
READ_TIMEOUT, RELAY_PORT, RENDEZVOUS_PORT, RENDEZVOUS_SERVERS,
},
fs::JobType,
@ -216,7 +216,8 @@ impl Client {
if hbb_common::is_ip_str(peer) {
return Ok((
(
connect_tcp(check_port(peer, RELAY_PORT + 1), CONNECT_TIMEOUT).await?,
connect_tcp_local(check_port(peer, RELAY_PORT + 1), None, CONNECT_TIMEOUT)
.await?,
true,
None,
),
@ -226,7 +227,11 @@ impl Client {
// Allow connect to {domain}:{port}
if hbb_common::is_domain_port_str(peer) {
return Ok((
(connect_tcp(peer, CONNECT_TIMEOUT).await?, true, None),
(
connect_tcp_local(peer, None, CONNECT_TIMEOUT).await?,
true,
None,
),
(0, "".to_owned()),
));
}
@ -291,7 +296,7 @@ impl Client {
log::info!("#{} punch attempt with {}, id: {}", i, my_addr, peer);
let mut msg_out = RendezvousMessage::new();
use hbb_common::protobuf::Enum;
let nat_type = if interface.is_force_relay() {
let nat_type = if interface.is_force_relay() || use_ws() || Config::is_proxy() {
NatType::SYMMETRIC
} else {
NatType::from_i32(my_nat_type).unwrap_or(NatType::UNKNOWN_NAT)

View file

@ -14,7 +14,7 @@ use hbb_common::{
anyhow::{anyhow, Context},
bail, base64,
bytes::Bytes,
config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT, RENDEZVOUS_PORT},
config::{self, use_ws, Config, CONNECT_TIMEOUT, READ_TIMEOUT, RENDEZVOUS_PORT},
futures::future::join_all,
futures_util::future::poll_fn,
get_version_number, log,
@ -504,6 +504,27 @@ audio_rechannel!(audio_rechannel_8_5, 8, 5);
audio_rechannel!(audio_rechannel_8_6, 8, 6);
audio_rechannel!(audio_rechannel_8_7, 8, 7);
pub struct CheckTestNatType {
is_direct: bool,
}
impl CheckTestNatType {
pub fn new() -> Self {
Self {
is_direct: Config::get_socks().is_none() && !config::use_ws(),
}
}
}
impl Drop for CheckTestNatType {
fn drop(&mut self) {
let is_direct = Config::get_socks().is_none() && !config::use_ws();
if self.is_direct != is_direct {
test_nat_type();
}
}
}
pub fn test_nat_type() {
use std::sync::atomic::{AtomicBool, Ordering};
std::thread::spawn(move || {
@ -514,9 +535,8 @@ pub fn test_nat_type() {
IS_RUNNING.store(true, Ordering::SeqCst);
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let is_direct = crate::ipc::get_socks().is_none(); // sync socks BTW
#[cfg(any(target_os = "android", target_os = "ios"))]
let is_direct = Config::get_socks().is_none(); // sync socks BTW
crate::ipc::get_socks_ws();
let is_direct = Config::get_socks().is_none() && !config::use_ws();
if !is_direct {
Config::set_nat_type(NatType::SYMMETRIC as _);
IS_RUNNING.store(false, Ordering::SeqCst);
@ -1319,6 +1339,13 @@ pub fn check_process(arg: &str, mut same_uid: bool) -> bool {
}
pub async fn secure_tcp(conn: &mut Stream, key: &str) -> ResultType<()> {
// Skip additional encryption when using WebSocket connections (wss://)
// as WebSocket Secure (wss://) already provides transport layer encryption.
// This doesn't affect the end-to-end encryption between clients,
// it only avoids redundant encryption between client and server.
if use_ws() {
return Ok(());
}
let rs_pk = get_rs_pk(key);
let Some(rs_pk) = rs_pk else {
bail!("Handshake failed: invalid public key from rendezvous server");

View file

@ -891,7 +891,10 @@ pub fn main_set_option(key: String, value: String) {
);
}
if key.eq("custom-rendezvous-server") {
if key.eq("custom-rendezvous-server")
|| key.eq(config::keys::OPTION_ALLOW_WEBSOCKET)
|| key.eq("api-server")
{
set_option(key, value.clone());
#[cfg(target_os = "android")]
crate::rendezvous_mediator::RendezvousMediator::restart();
@ -2533,7 +2536,10 @@ pub fn main_set_common(_key: String, _value: String) {
);
} else if _key == "update-me" {
if let Some(new_version_file) = get_download_file_from_url(&_value) {
log::debug!("New version file is downloaed, update begin, {:?}", new_version_file.to_str());
log::debug!(
"New version file is downloaed, update begin, {:?}",
new_version_file.to_str()
);
if let Some(f) = new_version_file.to_str() {
// 1.4.0 does not support "--update"
// But we can assume that the new version supports it.

View file

@ -1,4 +1,5 @@
use crate::{
common::CheckTestNatType,
privacy_mode::PrivacyModeState,
ui_interface::{get_local_option, set_local_option},
};
@ -22,7 +23,7 @@ pub use clipboard::ClipboardFile;
use hbb_common::{
allow_err, bail, bytes,
bytes_codec::BytesCodec,
config::{self, Config, Config2},
config::{self, keys::OPTION_ALLOW_WEBSOCKET, Config, Config2},
futures::StreamExt as _,
futures_util::sink::SinkExt,
log, password_security as password, timeout,
@ -282,6 +283,7 @@ pub enum Data {
ControllingSessionCount(usize),
#[cfg(target_os = "windows")]
PortForwardSessionCount(Option<usize>),
SocksWs(Option<Box<(Option<config::Socks5Server>, String)>>),
}
#[tokio::main(flavor = "current_thread")]
@ -348,29 +350,40 @@ pub async fn new_listener(postfix: &str) -> ResultType<Incoming> {
}
}
pub struct CheckIfRestart(String, Vec<String>, String, String);
pub struct CheckIfRestart {
stop_service: String,
rendezvous_servers: Vec<String>,
audio_input: String,
voice_call_input: String,
ws: String,
api_server: String,
}
impl CheckIfRestart {
pub fn new() -> CheckIfRestart {
CheckIfRestart(
Config::get_option("stop-service"),
Config::get_rendezvous_servers(),
Config::get_option("audio-input"),
Config::get_option("voice-call-input"),
)
CheckIfRestart {
stop_service: Config::get_option("stop-service"),
rendezvous_servers: Config::get_rendezvous_servers(),
audio_input: Config::get_option("audio-input"),
voice_call_input: Config::get_option("voice-call-input"),
ws: Config::get_option(OPTION_ALLOW_WEBSOCKET),
api_server: Config::get_option("api-server"),
}
}
}
impl Drop for CheckIfRestart {
fn drop(&mut self) {
if self.0 != Config::get_option("stop-service")
|| self.1 != Config::get_rendezvous_servers()
if self.stop_service != Config::get_option("stop-service")
|| self.rendezvous_servers != Config::get_rendezvous_servers()
|| self.ws != Config::get_option(OPTION_ALLOW_WEBSOCKET)
|| self.api_server != Config::get_option("api-server")
{
RendezvousMediator::restart();
}
if self.2 != Config::get_option("audio-input") {
if self.audio_input != Config::get_option("audio-input") {
crate::audio_service::restart();
}
if self.3 != Config::get_option("voice-call-input") {
if self.voice_call_input != Config::get_option("voice-call-input") {
crate::audio_service::set_voice_call_input_device(
Some(Config::get_option("voice-call-input")),
true,
@ -455,16 +468,29 @@ async fn handle(data: Data, stream: &mut Connection) {
allow_err!(stream.send(&Data::Socks(Config::get_socks())).await);
}
Some(data) => {
let _nat = CheckTestNatType::new();
if data.proxy.is_empty() {
Config::set_socks(None);
} else {
Config::set_socks(Some(data));
}
crate::common::test_nat_type();
RendezvousMediator::restart();
log::info!("socks updated");
}
},
Data::SocksWs(s) => match s {
None => {
allow_err!(
stream
.send(&Data::SocksWs(Some(Box::new((
Config::get_socks(),
Config::get_option(OPTION_ALLOW_WEBSOCKET)
)))))
.await
);
}
_ => {}
},
#[cfg(feature = "flutter")]
Data::VideoConnCount(None) => {
let n = crate::server::AUTHED_CONNS
@ -501,7 +527,8 @@ async fn handle(data: Data, stream: &mut Connection) {
None
};
} else if name == "hide_cm" {
value = if crate::hbbs_http::sync::is_pro() || crate::common::is_custom_client() {
value = if crate::hbbs_http::sync::is_pro() || crate::common::is_custom_client()
{
Some(hbb_common::password_security::hide_cm().to_string())
} else {
None
@ -544,6 +571,7 @@ async fn handle(data: Data, stream: &mut Connection) {
}
Some(value) => {
let _chk = CheckIfRestart::new();
let _nat = CheckTestNatType::new();
if let Some(v) = value.get("privacy-mode-impl-key") {
crate::privacy_mode::switch(v);
}
@ -1113,6 +1141,7 @@ pub fn set_option(key: &str, value: &str) {
#[tokio::main(flavor = "current_thread")]
pub async fn set_options(value: HashMap<String, String>) -> ResultType<()> {
let _nat = CheckTestNatType::new();
if let Ok(mut c) = connect(1000, "").await {
c.send(&Data::Options(Some(value.clone()))).await?;
// do not put below before connect, because we need to check should_exit
@ -1170,6 +1199,7 @@ pub async fn get_socks() -> Option<config::Socks5Server> {
#[tokio::main(flavor = "current_thread")]
pub async fn set_socks(value: config::Socks5Server) -> ResultType<()> {
let _nat = CheckTestNatType::new();
Config::set_socks(if value.proxy.is_empty() {
None
} else {
@ -1182,6 +1212,29 @@ pub async fn set_socks(value: config::Socks5Server) -> ResultType<()> {
Ok(())
}
async fn get_socks_ws_(ms_timeout: u64) -> ResultType<(Option<config::Socks5Server>, String)> {
let mut c = connect(ms_timeout, "").await?;
c.send(&Data::SocksWs(None)).await?;
if let Some(Data::SocksWs(Some(value))) = c.next_timeout(ms_timeout).await? {
Config::set_socks(value.0.clone());
Config::set_option(OPTION_ALLOW_WEBSOCKET.to_string(), value.1.clone());
Ok(*value)
} else {
Ok((
Config::get_socks(),
Config::get_option(OPTION_ALLOW_WEBSOCKET),
))
}
}
#[tokio::main(flavor = "current_thread")]
pub async fn get_socks_ws() -> (Option<config::Socks5Server>, String) {
get_socks_ws_(1_000).await.unwrap_or((
Config::get_socks(),
Config::get_option(OPTION_ALLOW_WEBSOCKET),
))
}
pub fn get_proxy_status() -> bool {
Config::get_socks().is_some()
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "下载失败,您可以重试或者点击\"下载\"按钮,从发布网址下载,并手动升级。"),
("Auto update", ""),
("update-failed-check-msi-tip", "安装方式检测失败。请点击\"下载\"按钮,从发布网址下载,并手动升级。"),
("websocket_tip", "使用 WebSocket 时,仅支持中继连接。"),
("Use WebSocket", "使用 WebSocket"),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "Download fehlgeschlagen. Sie können es erneut versuchen oder auf die Schaltfläche \"Herunterladen\" klicken, um von der Versionsseite herunterzuladen und manuell zu aktualisieren."),
("Auto update", "Automatisch aktualisieren"),
("update-failed-check-msi-tip", "Prüfung der Installationsmethode fehlgeschlagen. Bitte klicken Sie auf die Schaltfläche \"Herunterladen\", um von der Versionsseite herunterzuladen und manuell zu aktualisieren."),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -257,6 +257,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("screenshot-action-tip", "Please select how to continue with the screenshot."),
("{}-to-update-tip", "{} will close now and install the new version."),
("download-new-version-failed-tip", "Download failed. You can try again or click the \"Download\" button to download from the release page and upgrade manually."),
("update-failed-check-msi-tip", "Installation method check failed. Please click the \"Download\" button to download from the release page and upgrade manually.")
("update-failed-check-msi-tip", "Installation method check failed. Please click the \"Download\" button to download from the release page and upgrade manually."),
("websocket_tip", "When using WebSocket, only relay connections are supported."),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "Download non riuscito.\nÈ possibile riprovare o selezionare 'Download' per scaricare e aggiornarlo manualmente."),
("Auto update", "Aggiornamento automatico"),
("update-failed-check-msi-tip", "Controllo metodo installazione non riuscito.\nSeleziona 'Download' per scaricare il programma e aggiornarlo manualmente."),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "새 버전 다운로드에 실패했습니다"),
("Auto update", "자동 업데이트"),
("update-failed-check-msi-tip", "업데이트에 실패했습니다. .msi 설치 파일을 확인하세요."),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "Fout bij het downloaden. Je kunt het opnieuw proberen of op de knop Downloaden klikken om de applicatie van de officiële website te downloaden en handmatig bij te werken."),
("Auto update", "Automatisch updaten"),
("update-failed-check-msi-tip", "Kan de installatiemethode niet bepalen. Klik op “Downloaden” om de applicatie van de officiële website te downloaden en handmatig bij te werken."),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "Pobieranie nie powiodło się. Możesz spróbować ponownie lub kliknąć przycisk \"Pobierz\", aby pobrać ze strony programu i uaktualnić ręcznie."),
("Auto update", "Automatyczna aktualizacja"),
("update-failed-check-msi-tip", "Sprawdzenie metody instalacji nie powiodło się. Kliknij przycisk \"Pobierz\", aby pobrać ze strony wydania i uaktualnić ręcznie."),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", "Ошибка загрузки. Можно повторить попытку или нажать кнопку \"Скачать\", чтобы скачать приложение с официального сайта и обновить вручную."),
("Auto update", "Автоматическое обновление"),
("update-failed-check-msi-tip", "Невозможно определить метод установки. Нажмите кнопку \"Скачать\", чтобы скачать приложение с официального сайта и обновить его вручную."),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -694,5 +694,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("download-new-version-failed-tip", ""),
("Auto update", ""),
("update-failed-check-msi-tip", ""),
("websocket_tip", ""),
("Use WebSocket", ""),
].iter().cloned().collect();
}

View file

@ -12,7 +12,9 @@ use uuid::Uuid;
use hbb_common::{
allow_err,
anyhow::{self, bail},
config::{self, keys::*, option2bool, Config, CONNECT_TIMEOUT, REG_INTERVAL, RENDEZVOUS_PORT},
config::{
self, keys::*, option2bool, use_ws, Config, CONNECT_TIMEOUT, REG_INTERVAL, RENDEZVOUS_PORT,
},
futures::future::join_all,
log,
protobuf::Message as _,
@ -139,6 +141,7 @@ impl RendezvousMediator {
pub async fn start_udp(server: ServerPtr, host: String) -> ResultType<()> {
let host = check_port(&host, RENDEZVOUS_PORT);
log::info!("start udp: {host}");
let (mut socket, mut addr) = socket_client::new_udp_for(&host, CONNECT_TIMEOUT).await?;
let mut rz = Self {
addr: addr.clone(),
@ -323,6 +326,7 @@ impl RendezvousMediator {
pub async fn start_tcp(server: ServerPtr, host: String) -> ResultType<()> {
let host = check_port(&host, RENDEZVOUS_PORT);
log::info!("start tcp: {}", hbb_common::websocket::check_ws(&host));
let mut conn = connect_tcp(host.clone(), CONNECT_TIMEOUT).await?;
let key = crate::get_key(true).await;
crate::secure_tcp(&mut conn, &key).await?;
@ -336,7 +340,7 @@ impl RendezvousMediator {
let mut last_register_sent: Option<Instant> = None;
let mut last_recv_msg = Instant::now();
// we won't support connecting to multiple rendzvous servers any more, so we can use a global variable here.
Config::set_host_key_confirmed(&host, false);
Config::set_host_key_confirmed(&rz.host_prefix, false);
loop {
let mut update_latency = || {
let latency = last_register_sent
@ -350,6 +354,8 @@ impl RendezvousMediator {
last_recv_msg = Instant::now();
let bytes = res.ok_or_else(|| anyhow::anyhow!("Rendezvous connection is reset by the peer"))??;
if bytes.is_empty() {
// After fixing frequent register_pk, for websocket, nginx need to set proxy_read_timeout to more than 60 seconds, eg: 120s
// https://serverfault.com/questions/1060525/why-is-my-websocket-connection-gets-closed-in-60-seconds
conn.send_bytes(bytes::Bytes::new()).await?;
continue; // heartbeat
}
@ -365,7 +371,7 @@ impl RendezvousMediator {
bail!("Rendezvous connection is timeout");
}
if (!Config::get_key_confirmed() ||
!Config::get_host_key_confirmed(&host)) &&
!Config::get_host_key_confirmed(&rz.host_prefix)) &&
last_register_sent.map(|x| x.elapsed().as_millis() as i64).unwrap_or(REG_INTERVAL) >= REG_INTERVAL {
rz.register_pk(Sink::Stream(&mut conn)).await?;
last_register_sent = Some(Instant::now());
@ -381,6 +387,7 @@ impl RendezvousMediator {
//If the investment agent type is http or https, then tcp forwarding is enabled.
if (cfg!(debug_assertions) && option_env!("TEST_TCP").is_some())
|| Config::is_proxy()
|| use_ws()
|| get_builtin_option(config::keys::OPTION_DISABLE_UDP) == "Y"
{
Self::start_tcp(server, host).await
@ -449,7 +456,12 @@ impl RendezvousMediator {
async fn handle_intranet(&self, fla: FetchLocalAddr, server: ServerPtr) -> ResultType<()> {
let relay_server = self.get_relay_server(fla.relay_server.clone());
// nat64, go relay directly, because current hbbs will crash if demangle ipv6 address
if is_ipv4(&self.addr) && !config::is_disable_tcp_listen() && !Config::is_proxy() {
// websocket, go relay directly
if is_ipv4(&self.addr)
&& !config::is_disable_tcp_listen()
&& !Config::is_proxy()
&& !use_ws()
{
if let Err(err) = self
.handle_intranet_(fla.clone(), server.clone(), relay_server.clone())
.await
@ -501,9 +513,12 @@ impl RendezvousMediator {
async fn handle_punch_hole(&self, ph: PunchHole, server: ServerPtr) -> ResultType<()> {
let relay_server = self.get_relay_server(ph.relay_server);
// for ensure, websocket go relay directly
if ph.nat_type.enum_value() == Ok(NatType::SYMMETRIC)
|| Config::get_nat_type() == NatType::SYMMETRIC as i32
|| config::is_disable_tcp_listen()
|| use_ws()
|| Config::is_proxy()
{
let uuid = Uuid::new_v4().to_string();
return self

View file

@ -325,6 +325,7 @@ class MyIdMenu: Reactor.Component {
<li #custom-server>{translate('ID/Relay Server')}</li>
<li #whitelist title={translate('whitelist_tip')}>{translate('IP Whitelisting')}</li>
<li #socks5-server>{translate('Socks5 Proxy')}</li>
{ false && <li #allow-websocket><span>{svg_checkmark}</span>{translate('Use WebSocket')}</li> }
<div .separator />
<li #stop-service class={service_stopped ? "line-through" : "selected"}><span>{svg_checkmark}</span>{translate("Enable service")}</li>
{is_win && handler.is_installed() ? <ShareRdp /> : ""}

View file

@ -429,7 +429,10 @@ pub fn set_option(key: String, value: String) {
ipc::set_options(options.clone()).ok();
}
#[cfg(any(target_os = "android", target_os = "ios"))]
Config::set_option(key, value);
{
let _nat = crate::CheckTestNatType::new();
Config::set_option(key, value);
}
}
#[inline]
@ -479,12 +482,12 @@ pub fn set_socks(proxy: String, username: String, password: String) {
ipc::set_socks(socks).ok();
#[cfg(target_os = "android")]
{
let _nat = crate::CheckTestNatType::new();
if socks.proxy.is_empty() {
Config::set_socks(None);
} else {
Config::set_socks(Some(socks));
}
crate::common::test_nat_type();
crate::RendezvousMediator::restart();
log::info!("socks updated");
}