- Tunnel unterstuetzen mehrere gleichzeitige TCP-Verbindungen - Jede Proxy-Verbindung bekommt eigene Session-ID - Agent: Lazy-Connect (Ziel-Verbindung erst bei Datentransfer) - Backend: Ready-Handshake (wartet auf Agent-Bestaetigung) - Binary WebSocket Messages mit Session-ID statt Tunnel-ID - Connection.Close() Race-Condition behoben (sync.Once) - SendToAgent/SendBinaryToAgent mit Timeout statt Blocking - Hub verarbeitet Agent-Responses (Session-Confirm) - README komplett aktualisiert
153 lines
3.6 KiB
Go
153 lines
3.6 KiB
Go
package ws
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"os/exec"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Handler struct {
|
|
client *Client
|
|
}
|
|
|
|
func NewHandler(client *Client) *Handler {
|
|
return &Handler{client: client}
|
|
}
|
|
|
|
func (h *Handler) HandleCommand(msg Message) {
|
|
log.Printf("Command erhalten: %s (ID: %s)", msg.Command, msg.ID)
|
|
|
|
var response Message
|
|
response.Type = "response"
|
|
response.ID = msg.ID
|
|
|
|
switch msg.Command {
|
|
case "exec":
|
|
response = h.handleExec(msg)
|
|
case "tunnel_open":
|
|
// Legacy: wird nicht mehr verwendet, aber fuer Kompatibilitaet
|
|
response.Status = "ok"
|
|
response.Data = map[string]interface{}{"message": "tunnel_open deprecated, use tunnel_connect"}
|
|
case "tunnel_connect":
|
|
response = h.handleTunnelConnect(msg)
|
|
case "tunnel_disconnect":
|
|
h.handleTunnelDisconnect(msg)
|
|
return // Keine Response noetig
|
|
case "tunnel_close":
|
|
// Legacy: alle Sessions fuer diesen Tunnel schliessen
|
|
response.Status = "ok"
|
|
default:
|
|
response.Status = "error"
|
|
response.Error = fmt.Sprintf("Unbekanntes Command: %s", msg.Command)
|
|
}
|
|
|
|
if err := h.client.SendMessage(response); err != nil {
|
|
log.Printf("Response senden fehlgeschlagen: %v", err)
|
|
}
|
|
}
|
|
|
|
func (h *Handler) handleExec(msg Message) Message {
|
|
response := Message{
|
|
Type: "response",
|
|
ID: msg.ID,
|
|
}
|
|
|
|
command, ok := msg.Params["command"].(string)
|
|
if !ok || command == "" {
|
|
response.Status = "error"
|
|
response.Error = "Parameter 'command' erforderlich"
|
|
return response
|
|
}
|
|
|
|
timeoutSec := 30
|
|
if t, ok := msg.Params["timeout"]; ok {
|
|
if ts, ok := t.(float64); ok {
|
|
timeoutSec = int(ts)
|
|
} else if ts, ok := t.(string); ok {
|
|
if parsed, err := strconv.Atoi(ts); err == nil {
|
|
timeoutSec = parsed
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSec)*time.Second)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(ctx, "/bin/sh", "-c", command)
|
|
output, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
response.Status = "error"
|
|
response.Error = err.Error()
|
|
response.Data = map[string]interface{}{
|
|
"output": string(output),
|
|
}
|
|
} else {
|
|
response.Status = "ok"
|
|
response.Data = map[string]interface{}{
|
|
"output": string(output),
|
|
}
|
|
}
|
|
|
|
return response
|
|
}
|
|
|
|
func (h *Handler) handleTunnelConnect(msg Message) Message {
|
|
response := Message{
|
|
Type: "response",
|
|
ID: msg.ID,
|
|
}
|
|
|
|
sessionID, _ := msg.Params["session_id"].(string)
|
|
tunnelID, _ := msg.Params["tunnel_id"].(string)
|
|
targetHost, _ := msg.Params["target_host"].(string)
|
|
targetPortFloat, _ := msg.Params["target_port"].(float64)
|
|
targetPort := int(targetPortFloat)
|
|
|
|
if sessionID == "" || targetHost == "" || targetPort <= 0 {
|
|
response.Status = "error"
|
|
response.Error = "session_id, target_host und target_port erforderlich"
|
|
return response
|
|
}
|
|
|
|
if err := h.client.tunnelManager.ConnectSession(sessionID, tunnelID, targetHost, targetPort); err != nil {
|
|
response.Status = "error"
|
|
response.Error = fmt.Sprintf("Session-Connect fehlgeschlagen: %v", err)
|
|
return response
|
|
}
|
|
|
|
response.Status = "ok"
|
|
response.Data = map[string]interface{}{
|
|
"session_id": sessionID,
|
|
}
|
|
|
|
return response
|
|
}
|
|
|
|
func (h *Handler) handleTunnelDisconnect(msg Message) {
|
|
sessionID, _ := msg.Params["session_id"].(string)
|
|
if sessionID == "" {
|
|
return
|
|
}
|
|
|
|
h.client.tunnelManager.DisconnectSession(sessionID)
|
|
}
|
|
|
|
// SendSessionData schickt Daten als Binary-Message zurueck zum Backend
|
|
func (h *Handler) SendSessionData(sessionID string, data []byte) error {
|
|
idBytes := []byte(sessionID)
|
|
if len(idBytes) > 255 {
|
|
return fmt.Errorf("session_id zu lang")
|
|
}
|
|
|
|
message := make([]byte, 1+len(idBytes)+len(data))
|
|
message[0] = byte(len(idBytes))
|
|
copy(message[1:], idBytes)
|
|
copy(message[1+len(idBytes):], data)
|
|
|
|
return h.client.SendBinaryData(message)
|
|
}
|