fix: Remote-Verbindungen – Reverse-Proxy-Support, Shutdown-Handling, Script-Stabilität
- X-Forwarded-Host/Proto Header für korrekte WebSocket-URL hinter Reverse-Proxy - CancellationToken aus IHostApplicationLifetime für sauberen App-Shutdown - WebSocket-Close mit 3s Timeout statt endlosem Warten - PowerShell-Script: Synchroner Empfangs-Loop (Send/Receive-Funktionen) - TLS 1.2 erzwungen für ältere Windows-Versionen - remote_execute Tool auch ohne Workspace-Pfad verfügbar (includeRemote) - Debug-Logs entfernt
This commit is contained in:
1 parent
10da2bd964
commit
0eb9022803
3 files changed
+91
-72
No files matched your search
+61
-55
@@ -20,12 +20,48 @@ Write-Host ""
|
||||
Write-Host " Verbinde zu: $Url" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
function Send-WsMessage {
|
||||
param([System.Net.WebSockets.ClientWebSocket]$ws, [string]$msg, [System.Threading.CancellationToken]$ct)
|
||||
$bytes = [System.Text.Encoding]::UTF8.GetBytes($msg)
|
||||
$ws.SendAsync((New-Object System.ArraySegment[byte] -ArgumentList @(,$bytes)),
|
||||
[System.Net.WebSockets.WebSocketMessageType]::Text, $true, $ct).GetAwaiter().GetResult() | Out-Null
|
||||
}
|
||||
|
||||
function Receive-WsMessage {
|
||||
param([System.Net.WebSockets.ClientWebSocket]$ws, [System.Threading.CancellationToken]$ct)
|
||||
$buf = New-Object byte[] 65536
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
try {
|
||||
do {
|
||||
$seg = New-Object System.ArraySegment[byte] -ArgumentList @(,$buf)
|
||||
$result = $ws.ReceiveAsync($seg, $ct).GetAwaiter().GetResult()
|
||||
if ($result.MessageType -eq [System.Net.WebSockets.WebSocketMessageType]::Close) {
|
||||
return $null
|
||||
}
|
||||
$ms.Write($buf, 0, $result.Count)
|
||||
} while (-not $result.EndOfMessage)
|
||||
return [System.Text.Encoding]::UTF8.GetString($ms.ToArray())
|
||||
} finally {
|
||||
$ms.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$ws = New-Object System.Net.WebSockets.ClientWebSocket
|
||||
$ct = New-Object System.Threading.CancellationToken
|
||||
$cts = New-Object System.Threading.CancellationTokenSource
|
||||
$ct = $cts.Token
|
||||
|
||||
# TLS 1.2 erzwingen (fuer aeltere Windows-Versionen)
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
|
||||
|
||||
# Verbindung aufbauen
|
||||
$ws.ConnectAsync([Uri]$Url, $ct).GetAwaiter().GetResult()
|
||||
try {
|
||||
$ws.ConnectAsync([Uri]$Url, $ct).GetAwaiter().GetResult() | Out-Null
|
||||
} catch {
|
||||
Write-Host " [FEHLER] Verbindung fehlgeschlagen: $_" -ForegroundColor Red
|
||||
Write-Host " URL: $Url" -ForegroundColor Gray
|
||||
return
|
||||
}
|
||||
Write-Host " [OK] Verbunden!" -ForegroundColor Green
|
||||
|
||||
# Hello-Message senden
|
||||
@@ -38,61 +74,28 @@ try {
|
||||
username = $username
|
||||
os = $osVersion
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
$helloBytes = [System.Text.Encoding]::UTF8.GetBytes($hello)
|
||||
$segment = New-Object System.ArraySegment[byte] -ArgumentList @(,$helloBytes)
|
||||
$ws.SendAsync($segment, [System.Net.WebSockets.WebSocketMessageType]::Text, $true, $ct).GetAwaiter().GetResult()
|
||||
Send-WsMessage $ws $hello $ct
|
||||
|
||||
Write-Host " Host: $username@$hostname ($osVersion)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host " Warte auf Befehle... (Strg+C zum Beenden)" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
# Empfangs-Loop
|
||||
$buffer = New-Object byte[] 65536
|
||||
$messageBuffer = New-Object System.IO.MemoryStream
|
||||
|
||||
# Heartbeat-Timer (alle 30 Sekunden)
|
||||
$lastPing = [DateTime]::UtcNow
|
||||
|
||||
# Synchroner Empfangs-Loop
|
||||
while ($ws.State -eq [System.Net.WebSockets.WebSocketState]::Open) {
|
||||
# Heartbeat senden
|
||||
if (([DateTime]::UtcNow - $lastPing).TotalSeconds -gt 30) {
|
||||
$pingBytes = [System.Text.Encoding]::UTF8.GetBytes('{"type":"ping"}')
|
||||
$pingSegment = New-Object System.ArraySegment[byte] -ArgumentList @(,$pingBytes)
|
||||
try {
|
||||
$ws.SendAsync($pingSegment, [System.Net.WebSockets.WebSocketMessageType]::Text, $true, $ct).GetAwaiter().GetResult()
|
||||
} catch {
|
||||
break
|
||||
}
|
||||
$lastPing = [DateTime]::UtcNow
|
||||
}
|
||||
|
||||
# Auf Nachricht warten (mit Timeout fuer Heartbeat)
|
||||
$receiveTask = $ws.ReceiveAsync((New-Object System.ArraySegment[byte] -ArgumentList @(,$buffer)), $ct)
|
||||
if (-not $receiveTask.Wait(5000)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$result = $receiveTask.Result
|
||||
|
||||
if ($result.MessageType -eq [System.Net.WebSockets.WebSocketMessageType]::Close) {
|
||||
# Blockierend auf naechste Nachricht warten
|
||||
$json = Receive-WsMessage $ws $ct
|
||||
if ($null -eq $json) {
|
||||
Write-Host " Server hat Verbindung geschlossen." -ForegroundColor Yellow
|
||||
break
|
||||
}
|
||||
|
||||
$messageBuffer.SetLength(0)
|
||||
$messageBuffer.Write($buffer, 0, $result.Count)
|
||||
|
||||
# Mehrteilige Nachrichten zusammenbauen
|
||||
while (-not $result.EndOfMessage) {
|
||||
$result = $ws.ReceiveAsync((New-Object System.ArraySegment[byte] -ArgumentList @(,$buffer)), $ct).GetAwaiter().GetResult()
|
||||
$messageBuffer.Write($buffer, 0, $result.Count)
|
||||
try {
|
||||
$msg = $json | ConvertFrom-Json
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
|
||||
$json = [System.Text.Encoding]::UTF8.GetString($messageBuffer.ToArray())
|
||||
$msg = $json | ConvertFrom-Json
|
||||
|
||||
switch ($msg.type) {
|
||||
"execute" {
|
||||
$command = $msg.command
|
||||
@@ -105,11 +108,12 @@ try {
|
||||
$stderr = ""
|
||||
|
||||
try {
|
||||
# PowerShell-Befehl ausfuehren
|
||||
$output = & powershell.exe -NoProfile -NonInteractive -Command $command 2>&1
|
||||
$stdout = ($output | Where-Object { $_ -isnot [System.Management.Automation.ErrorRecord] }) -join "`n"
|
||||
$stderrLines = ($output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }) | ForEach-Object { $_.ToString() }
|
||||
$stderr = $stderrLines -join "`n"
|
||||
$stdoutLines = @($output | Where-Object { $_ -isnot [System.Management.Automation.ErrorRecord] })
|
||||
$stderrLines = @($output | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] }) |
|
||||
ForEach-Object { $_.ToString() }
|
||||
$stdout = ($stdoutLines | Out-String).TrimEnd()
|
||||
$stderr = ($stderrLines | Out-String).TrimEnd()
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($null -eq $exitCode) { $exitCode = 0 }
|
||||
} catch {
|
||||
@@ -123,13 +127,11 @@ try {
|
||||
exitCode = $exitCode
|
||||
output = if ($stdout) { $stdout } else { "" }
|
||||
error = if ($stderr) { $stderr } else { "" }
|
||||
} | ConvertTo-Json -Compress
|
||||
} | ConvertTo-Json -Compress -Depth 3
|
||||
|
||||
$responseBytes = [System.Text.Encoding]::UTF8.GetBytes($response)
|
||||
$responseSegment = New-Object System.ArraySegment[byte] -ArgumentList @(,$responseBytes)
|
||||
$ws.SendAsync($responseSegment, [System.Net.WebSockets.WebSocketMessageType]::Text, $true, $ct).GetAwaiter().GetResult()
|
||||
Send-WsMessage $ws $response $ct
|
||||
|
||||
# Kurzausgabe
|
||||
# Kurzausgabe lokal
|
||||
if ($stdout) {
|
||||
$preview = if ($stdout.Length -gt 200) { $stdout.Substring(0, 200) + "..." } else { $stdout }
|
||||
Write-Host " -> $preview" -ForegroundColor Gray
|
||||
@@ -141,13 +143,15 @@ try {
|
||||
Write-Host ""
|
||||
}
|
||||
"pong" {
|
||||
# Heartbeat-Antwort – ignorieren
|
||||
# Heartbeat-Antwort - ignorieren
|
||||
}
|
||||
default {
|
||||
Write-Host " Unbekannte Nachricht: $($msg.type)" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch [System.OperationCanceledException] {
|
||||
# Ctrl+C
|
||||
} catch [System.Net.WebSockets.WebSocketException] {
|
||||
Write-Host " Verbindung verloren." -ForegroundColor Red
|
||||
} catch {
|
||||
@@ -155,9 +159,11 @@ try {
|
||||
} finally {
|
||||
if ($ws -and $ws.State -eq [System.Net.WebSockets.WebSocketState]::Open) {
|
||||
try {
|
||||
$ws.CloseAsync([System.Net.WebSockets.WebSocketCloseStatus]::NormalClosure, "bye", $ct).GetAwaiter().GetResult()
|
||||
$closeCt = (New-Object System.Threading.CancellationTokenSource(3000)).Token
|
||||
$ws.CloseAsync([System.Net.WebSockets.WebSocketCloseStatus]::NormalClosure, "bye", $closeCt).GetAwaiter().GetResult() | Out-Null
|
||||
} catch { }
|
||||
}
|
||||
if ($cts) { $cts.Dispose() }
|
||||
if ($ws) { $ws.Dispose() }
|
||||
Write-Host ""
|
||||
Write-Host " Verbindung beendet." -ForegroundColor Yellow
|
||||
|
||||
Reference in new issue
Block a user