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) }