Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48e3de2922 |
Generated
+1
-1
@@ -436,7 +436,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dosh"
|
name = "dosh"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dosh"
|
name = "dosh"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
|||||||
+26
@@ -116,6 +116,31 @@ function Verify-ArchiveChecksum($Url, $Archive) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Expected-ArchiveVersion($Url) {
|
||||||
|
if ($Url -match "/releases/download/v([^/]+)/") {
|
||||||
|
return $Matches[1]
|
||||||
|
}
|
||||||
|
if (-not $BinaryUrl -and -not $BinaryBase -and $BinaryVersion -ne "latest") {
|
||||||
|
return $BinaryVersion.TrimStart("v")
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Verify-ArchiveVersion($ExtractDir, $Url) {
|
||||||
|
$expected = Expected-ArchiveVersion $Url
|
||||||
|
if (-not $expected) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$versionFile = Get-ChildItem -Path $ExtractDir -Recurse -File -Filter VERSION | Select-Object -First 1
|
||||||
|
if (-not $versionFile) {
|
||||||
|
throw "prebuilt archive missing VERSION for $Url"
|
||||||
|
}
|
||||||
|
$actual = (Get-Content $versionFile.FullName -Raw).Trim()
|
||||||
|
if ($actual -ne $expected) {
|
||||||
|
throw "prebuilt archive version mismatch for ${Url}: expected $expected, got $actual"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$bindir = Join-Path $Prefix "bin"
|
$bindir = Join-Path $Prefix "bin"
|
||||||
$configDir = Join-Path $HOME ".config\dosh"
|
$configDir = Join-Path $HOME ".config\dosh"
|
||||||
New-Item -ItemType Directory -Force -Path $bindir, $configDir | Out-Null
|
New-Item -ItemType Directory -Force -Path $bindir, $configDir | Out-Null
|
||||||
@@ -137,6 +162,7 @@ function Install-Prebuilt {
|
|||||||
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $zip
|
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $zip
|
||||||
Verify-ArchiveChecksum $url $zip
|
Verify-ArchiveChecksum $url $zip
|
||||||
Expand-Archive -Force -Path $zip -DestinationPath $extract
|
Expand-Archive -Force -Path $zip -DestinationPath $extract
|
||||||
|
Verify-ArchiveVersion $extract $url
|
||||||
foreach ($bin in @("dosh-client.exe", "dosh-bench.exe")) {
|
foreach ($bin in @("dosh-client.exe", "dosh-bench.exe")) {
|
||||||
$found = Get-ChildItem -Path $extract -Recurse -File -Filter $bin | Select-Object -First 1
|
$found = Get-ChildItem -Path $extract -Recurse -File -Filter $bin | Select-Object -First 1
|
||||||
if (-not $found) {
|
if (-not $found) {
|
||||||
|
|||||||
+35
@@ -268,6 +268,40 @@ verify_archive_checksum() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expected_archive_version() {
|
||||||
|
url="$1"
|
||||||
|
case "$url" in
|
||||||
|
*/releases/download/v*/*)
|
||||||
|
tag="${url#*/releases/download/v}"
|
||||||
|
tag="${tag%%/*}"
|
||||||
|
printf '%s\n' "$tag"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [ -z "$binary_url" ] && [ -z "$binary_base" ] && [ "$binary_version" != "latest" ]; then
|
||||||
|
printf '%s\n' "${binary_version#v}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_archive_version() {
|
||||||
|
extract_dir="$1"
|
||||||
|
url="$2"
|
||||||
|
expected="$(expected_archive_version "$url" || true)"
|
||||||
|
[ -n "$expected" ] || return 0
|
||||||
|
version_file="$(find "$extract_dir" -type f -name VERSION 2>/dev/null | sed -n '1p')"
|
||||||
|
if [ -z "$version_file" ]; then
|
||||||
|
echo "prebuilt archive missing VERSION for $url" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
actual="$(tr -d '\r\n' <"$version_file")"
|
||||||
|
if [ "$actual" != "$expected" ]; then
|
||||||
|
echo "prebuilt archive version mismatch for $url: expected $expected, got $actual" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
try_install_prebuilt() {
|
try_install_prebuilt() {
|
||||||
download_url="$(release_latest_tag_download_url || release_download_url)" || return 1
|
download_url="$(release_latest_tag_download_url || release_download_url)" || return 1
|
||||||
tmpdir="$(mktemp -d)"
|
tmpdir="$(mktemp -d)"
|
||||||
@@ -291,6 +325,7 @@ try_install_prebuilt() {
|
|||||||
echo "prebuilt archive could not be extracted: $download_url" >&2
|
echo "prebuilt archive could not be extracted: $download_url" >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
verify_archive_version "$tmpdir/extract" "$download_url" || return 1
|
||||||
install_extracted_binary "$tmpdir/extract" dosh-client "$bindir/dosh-client" || return 1
|
install_extracted_binary "$tmpdir/extract" dosh-client "$bindir/dosh-client" || return 1
|
||||||
ln -sf dosh-client "$bindir/dosh"
|
ln -sf dosh-client "$bindir/dosh"
|
||||||
if [ "$role" = "server" ] || [ "$role" = "both" ]; then
|
if [ "$role" = "server" ] || [ "$role" = "both" ]; then
|
||||||
|
|||||||
+45
-14
@@ -2797,7 +2797,7 @@ async fn run_terminal(
|
|||||||
} else {
|
} else {
|
||||||
Some(RawMode::enter()?)
|
Some(RawMode::enter()?)
|
||||||
};
|
};
|
||||||
let addr = resolve_addr(&cred.udp_host, cred.udp_port)?;
|
let mut addr = resolve_addr(&cred.udp_host, cred.udp_port)?;
|
||||||
let mut send_seq = 2u64;
|
let mut send_seq = 2u64;
|
||||||
let mut last_packet_at = Instant::now();
|
let mut last_packet_at = Instant::now();
|
||||||
let mut status_tick = tokio::time::interval(Duration::from_secs(1));
|
let mut status_tick = tokio::time::interval(Duration::from_secs(1));
|
||||||
@@ -2989,6 +2989,7 @@ async fn run_terminal(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
refresh_live_addr(&mut addr, &cred)?;
|
||||||
if !forward_only {
|
if !forward_only {
|
||||||
render_frame(&frame)?;
|
render_frame(&frame)?;
|
||||||
predictor.observe_output(&frame.bytes);
|
predictor.observe_output(&frame.bytes);
|
||||||
@@ -3060,6 +3061,7 @@ async fn run_terminal(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
refresh_live_addr(&mut addr, &cred)?;
|
||||||
if !forward_only {
|
if !forward_only {
|
||||||
render_frame(&frame)?;
|
render_frame(&frame)?;
|
||||||
predictor.observe_output(&frame.bytes);
|
predictor.observe_output(&frame.bytes);
|
||||||
@@ -3164,6 +3166,7 @@ async fn run_terminal(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
refresh_live_addr(&mut addr, &cred)?;
|
||||||
if !forward_only {
|
if !forward_only {
|
||||||
render_frame(&frame)?;
|
render_frame(&frame)?;
|
||||||
predictor.observe_output(&frame.bytes);
|
predictor.observe_output(&frame.bytes);
|
||||||
@@ -3534,6 +3537,7 @@ async fn run_terminal(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
|
refresh_live_addr(&mut addr, &cred)?;
|
||||||
if !forward_only {
|
if !forward_only {
|
||||||
render_frame(&frame)?;
|
render_frame(&frame)?;
|
||||||
predictor.observe_output(&frame.bytes);
|
predictor.observe_output(&frame.bytes);
|
||||||
@@ -3826,6 +3830,11 @@ async fn reconnect(
|
|||||||
Ok(Some(frame))
|
Ok(Some(frame))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn refresh_live_addr(addr: &mut SocketAddr, cred: &CachedCredential) -> Result<()> {
|
||||||
|
*addr = resolve_addr(&cred.udp_host, cred.udp_port)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn queue_pending_user_input(
|
fn queue_pending_user_input(
|
||||||
pending: &mut VecDeque<Vec<u8>>,
|
pending: &mut VecDeque<Vec<u8>>,
|
||||||
pending_bytes: &mut usize,
|
pending_bytes: &mut usize,
|
||||||
@@ -5337,19 +5346,20 @@ const TERMINAL_CLEANUP: &[u8] = concat!(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
DisconnectStatus, DynamicForward, FrameBuffer, LocalForward, MAX_PENDING_USER_INPUT_BYTES,
|
CachedCredential, DisconnectStatus, DynamicForward, FrameBuffer, LocalForward,
|
||||||
POST_SUBMIT_ALL_INPUT_HOLD, PredictMode, Predictor, RESTART_STATUS_SCRIPT, RemoteForward,
|
MAX_PENDING_USER_INPUT_BYTES, POST_SUBMIT_ALL_INPUT_HOLD, PredictMode, Predictor,
|
||||||
STARTUP_INPUT_HOLD, SshConfig, StartupGateMode, StatusAction, auth_allows, cache_key,
|
RESTART_STATUS_SCRIPT, RemoteForward, STARTUP_INPUT_HOLD, SshConfig, StartupGateMode,
|
||||||
cache_server_prefix, clear_cached_credentials, ensure_tui_safe_status_overlay,
|
StatusAction, auth_allows, cache_key, cache_server_prefix, clear_cached_credentials,
|
||||||
input_matches_escape, is_local_status_target, latest_release_download_url,
|
ensure_tui_safe_status_overlay, input_matches_escape, is_local_status_target,
|
||||||
load_first_native_identity_with_prompt, parse_dynamic_forward, parse_escape_key,
|
latest_release_download_url, load_first_native_identity_with_prompt, parse_dynamic_forward,
|
||||||
parse_local_forward, parse_remote_forward, parse_ssh_config, post_submit_hold_duration,
|
parse_escape_key, parse_local_forward, parse_remote_forward, parse_ssh_config,
|
||||||
queue_pending_user_input, raw_contains_host_table, recv_response_until,
|
post_submit_hold_duration, queue_pending_user_input, raw_contains_host_table,
|
||||||
render_status_clear, render_status_overlay, requested_env, resolved_startup_command,
|
recv_response_until, refresh_live_addr, render_status_clear, render_status_overlay,
|
||||||
rewrite_forward_command, selected_predict_mode, selected_udp_host, server_version_mismatch,
|
requested_env, resolved_startup_command, rewrite_forward_command, selected_predict_mode,
|
||||||
should_hold_during_startup_gate, should_hold_post_submit_input, split_after_command_submit,
|
selected_udp_host, server_version_mismatch, should_hold_during_startup_gate,
|
||||||
ssh_destination_host, ssh_username, ssh_with_user, startup_command, status_ssh_target,
|
should_hold_post_submit_input, split_after_command_submit, ssh_destination_host,
|
||||||
toml_bare_key_or_quoted, update_check_requested, valid_forward_host,
|
ssh_username, ssh_with_user, startup_command, status_ssh_target, toml_bare_key_or_quoted,
|
||||||
|
update_check_requested, valid_forward_host,
|
||||||
};
|
};
|
||||||
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
||||||
use dosh::native::EnvVar;
|
use dosh::native::EnvVar;
|
||||||
@@ -6052,6 +6062,27 @@ mod tests {
|
|||||||
assert_eq!(post_submit_hold_duration(b"x"), POST_SUBMIT_ALL_INPUT_HOLD);
|
assert_eq!(post_submit_hold_duration(b"x"), POST_SUBMIT_ALL_INPUT_HOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reconnect_refreshes_live_send_address_from_credentials() {
|
||||||
|
let mut addr = "127.0.0.1:50000".parse().unwrap();
|
||||||
|
let cred = CachedCredential {
|
||||||
|
server: "host".to_string(),
|
||||||
|
session: "term".to_string(),
|
||||||
|
mode: "normal".to_string(),
|
||||||
|
udp_host: "127.0.0.1".to_string(),
|
||||||
|
udp_port: 50001,
|
||||||
|
client_id: [1; 16],
|
||||||
|
session_key: [2; 32],
|
||||||
|
session_key_id: [3; 16],
|
||||||
|
attach_ticket: Vec::new(),
|
||||||
|
attach_ticket_psk: [4; 32],
|
||||||
|
last_rendered_seq: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
refresh_live_addr(&mut addr, &cred).unwrap();
|
||||||
|
assert_eq!(addr.port(), 50001);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn update_check_accepts_only_check_flag() {
|
fn update_check_accepts_only_check_flag() {
|
||||||
assert!(!update_check_requested(&[]).unwrap());
|
assert!(!update_check_requested(&[]).unwrap());
|
||||||
|
|||||||
Reference in New Issue
Block a user