WebSocket-Infrastruktur: bidirektionaler Command-Kanal + TCP-Tunnel
- Agent: WS-Client mit Reconnect, Command-Handler (exec, tunnel_open/close/data)
- Backend: WS-Hub, Tunnel-Manager mit dynamischen Proxy-Ports (10000-20000)
- API: /agents/{id}/exec, /tunnel, /tunnels, /proxy/{tunnel_id}
- Binary WebSocket Messages fuer effiziente Tunnel-Daten
- gorilla/websocket (pure Go, kein CGO)
- README aktualisiert
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAgentNotConnected = errors.New("Agent nicht verbunden")
|
||||
ErrAgentSendBlocked = errors.New("Agent Send-Channel blockiert")
|
||||
)
|
||||
|
||||
type Connection struct {
|
||||
AgentID string
|
||||
Conn *websocket.Conn
|
||||
Send chan []byte
|
||||
BinarySend chan []byte
|
||||
Hub *Hub
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
// Hier könnte Origin-Checking implementiert werden
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func NewWebSocketHandler(hub *Hub) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
handleWebSocket(hub, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func handleWebSocket(hub *Hub, w http.ResponseWriter, r *http.Request) {
|
||||
// Query-Parameter extrahieren
|
||||
agentID := r.URL.Query().Get("agent_id")
|
||||
apiKey := r.URL.Query().Get("api_key")
|
||||
|
||||
if agentID == "" || apiKey == "" {
|
||||
http.Error(w, "agent_id und api_key erforderlich", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// API-Key validieren (sollte bereits durch Middleware geschehen)
|
||||
// Hier könnten zusätzliche Agent-spezifische Prüfungen stattfinden
|
||||
|
||||
// WebSocket-Upgrade
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Printf("WebSocket-Upgrade fehlgeschlagen: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Connection-Objekt erstellen
|
||||
connection := &Connection{
|
||||
AgentID: agentID,
|
||||
Conn: conn,
|
||||
Send: make(chan []byte, 256),
|
||||
BinarySend: make(chan []byte, 256),
|
||||
Hub: hub,
|
||||
}
|
||||
|
||||
// Verbindung bei Hub registrieren
|
||||
hub.RegisterAgent(agentID, connection)
|
||||
|
||||
// Goroutines für Read/Write starten
|
||||
go connection.writePump()
|
||||
go connection.readPump()
|
||||
}
|
||||
|
||||
func (c *Connection) readPump() {
|
||||
defer func() {
|
||||
c.Hub.UnregisterAgent(c.AgentID, c)
|
||||
c.Conn.Close()
|
||||
}()
|
||||
|
||||
// Read-Timeouts setzen
|
||||
c.Conn.SetReadLimit(512 * 1024) // 512KB max message size
|
||||
c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
|
||||
c.Conn.SetPongHandler(func(string) error {
|
||||
c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
|
||||
return nil
|
||||
})
|
||||
|
||||
for {
|
||||
messageType, message, err := c.Conn.ReadMessage()
|
||||
if err != nil {
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
||||
log.Printf("Agent %s: WebSocket-Fehler: %v", c.AgentID, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Message an Hub weiterleiten
|
||||
if err := c.Hub.ProcessAgentMessage(c.AgentID, messageType, message); err != nil {
|
||||
log.Printf("Agent %s: Message-Verarbeitung fehlgeschlagen: %v", c.AgentID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) writePump() {
|
||||
ticker := time.NewTicker(54 * time.Second) // Ping alle 54s
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
c.Conn.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case message, ok := <-c.Send:
|
||||
c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
if !ok {
|
||||
c.Conn.WriteMessage(websocket.CloseMessage, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.Conn.WriteMessage(websocket.TextMessage, message); err != nil {
|
||||
log.Printf("Agent %s: Write-Fehler: %v", c.AgentID, err)
|
||||
return
|
||||
}
|
||||
|
||||
case binaryMessage, ok := <-c.BinarySend:
|
||||
c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
if !ok {
|
||||
c.Conn.WriteMessage(websocket.CloseMessage, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.Conn.WriteMessage(websocket.BinaryMessage, binaryMessage); err != nil {
|
||||
log.Printf("Agent %s: Binary-Write-Fehler: %v", c.AgentID, err)
|
||||
return
|
||||
}
|
||||
|
||||
case <-ticker.C:
|
||||
c.Conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
|
||||
log.Printf("Agent %s: Ping-Fehler: %v", c.AgentID, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) Close() {
|
||||
close(c.Send)
|
||||
close(c.BinarySend)
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AgentConnection struct {
|
||||
AgentID string
|
||||
Connection *Connection
|
||||
LastSeen time.Time
|
||||
}
|
||||
|
||||
type Hub struct {
|
||||
agents map[string]*AgentConnection
|
||||
agentsMux sync.RWMutex
|
||||
|
||||
register chan *AgentConnection
|
||||
unregister chan *AgentConnection
|
||||
broadcast chan []byte
|
||||
|
||||
tunnelManager *TunnelManager
|
||||
}
|
||||
|
||||
func NewHub() *Hub {
|
||||
hub := &Hub{
|
||||
agents: make(map[string]*AgentConnection),
|
||||
register: make(chan *AgentConnection),
|
||||
unregister: make(chan *AgentConnection),
|
||||
broadcast: make(chan []byte, 256),
|
||||
}
|
||||
|
||||
hub.tunnelManager = NewTunnelManager(hub)
|
||||
return hub
|
||||
}
|
||||
|
||||
func (h *Hub) Run() {
|
||||
log.Println("WebSocket Hub startet...")
|
||||
|
||||
// Cleanup-Timer für inaktive Verbindungen
|
||||
cleanupTicker := time.NewTicker(5 * time.Minute)
|
||||
defer cleanupTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case agent := <-h.register:
|
||||
h.registerAgent(agent)
|
||||
|
||||
case agent := <-h.unregister:
|
||||
h.unregisterAgent(agent)
|
||||
|
||||
case message := <-h.broadcast:
|
||||
h.broadcastMessage(message)
|
||||
|
||||
case <-cleanupTicker.C:
|
||||
h.cleanupInactiveAgents()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) registerAgent(agent *AgentConnection) {
|
||||
h.agentsMux.Lock()
|
||||
defer h.agentsMux.Unlock()
|
||||
|
||||
// Bestehende Verbindung schließen falls vorhanden
|
||||
if existing, ok := h.agents[agent.AgentID]; ok {
|
||||
log.Printf("Agent %s: Ersetzt bestehende Verbindung", agent.AgentID)
|
||||
existing.Connection.Close()
|
||||
}
|
||||
|
||||
h.agents[agent.AgentID] = agent
|
||||
agent.LastSeen = time.Now()
|
||||
|
||||
log.Printf("Agent %s: WebSocket-Verbindung registriert", agent.AgentID)
|
||||
}
|
||||
|
||||
func (h *Hub) unregisterAgent(agent *AgentConnection) {
|
||||
h.agentsMux.Lock()
|
||||
defer h.agentsMux.Unlock()
|
||||
|
||||
if existing, ok := h.agents[agent.AgentID]; ok && existing.Connection == agent.Connection {
|
||||
delete(h.agents, agent.AgentID)
|
||||
agent.Connection.Close()
|
||||
log.Printf("Agent %s: WebSocket-Verbindung entfernt", agent.AgentID)
|
||||
|
||||
// Alle Tunnel für diesen Agent schließen
|
||||
h.tunnelManager.CloseAgentTunnels(agent.AgentID)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) broadcastMessage(message []byte) {
|
||||
h.agentsMux.RLock()
|
||||
defer h.agentsMux.RUnlock()
|
||||
|
||||
for agentID, agent := range h.agents {
|
||||
select {
|
||||
case agent.Connection.Send <- message:
|
||||
default:
|
||||
// Agent kann keine Nachrichten mehr empfangen
|
||||
log.Printf("Agent %s: Send-Channel blockiert, trenne Verbindung", agentID)
|
||||
close(agent.Connection.Send)
|
||||
delete(h.agents, agentID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) SendToAgent(agentID string, message []byte) error {
|
||||
h.agentsMux.RLock()
|
||||
agent, exists := h.agents[agentID]
|
||||
h.agentsMux.RUnlock()
|
||||
|
||||
if !exists {
|
||||
return ErrAgentNotConnected
|
||||
}
|
||||
|
||||
agent.LastSeen = time.Now()
|
||||
|
||||
select {
|
||||
case agent.Connection.Send <- message:
|
||||
return nil
|
||||
default:
|
||||
return ErrAgentSendBlocked
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) SendBinaryToAgent(agentID string, data []byte) error {
|
||||
h.agentsMux.RLock()
|
||||
agent, exists := h.agents[agentID]
|
||||
h.agentsMux.RUnlock()
|
||||
|
||||
if !exists {
|
||||
return ErrAgentNotConnected
|
||||
}
|
||||
|
||||
agent.LastSeen = time.Now()
|
||||
|
||||
select {
|
||||
case agent.Connection.BinarySend <- data:
|
||||
return nil
|
||||
default:
|
||||
return ErrAgentSendBlocked
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) IsAgentConnected(agentID string) bool {
|
||||
h.agentsMux.RLock()
|
||||
defer h.agentsMux.RUnlock()
|
||||
|
||||
_, exists := h.agents[agentID]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (h *Hub) GetConnectedAgents() []string {
|
||||
h.agentsMux.RLock()
|
||||
defer h.agentsMux.RUnlock()
|
||||
|
||||
agents := make([]string, 0, len(h.agents))
|
||||
for agentID := range h.agents {
|
||||
agents = append(agents, agentID)
|
||||
}
|
||||
|
||||
return agents
|
||||
}
|
||||
|
||||
func (h *Hub) GetAgentStats() map[string]interface{} {
|
||||
h.agentsMux.RLock()
|
||||
defer h.agentsMux.RUnlock()
|
||||
|
||||
stats := map[string]interface{}{
|
||||
"connected_agents": len(h.agents),
|
||||
"active_tunnels": h.tunnelManager.GetActiveTunnelCount(),
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
func (h *Hub) cleanupInactiveAgents() {
|
||||
h.agentsMux.Lock()
|
||||
defer h.agentsMux.Unlock()
|
||||
|
||||
timeout := 10 * time.Minute
|
||||
now := time.Now()
|
||||
|
||||
for agentID, agent := range h.agents {
|
||||
if now.Sub(agent.LastSeen) > timeout {
|
||||
log.Printf("Agent %s: Timeout, trenne Verbindung", agentID)
|
||||
agent.Connection.Close()
|
||||
delete(h.agents, agentID)
|
||||
h.tunnelManager.CloseAgentTunnels(agentID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetTunnelManager gibt den TunnelManager zurück (für API-Handler)
|
||||
func (h *Hub) GetTunnelManager() *TunnelManager {
|
||||
return h.tunnelManager
|
||||
}
|
||||
|
||||
// RegisterAgent registriert eine neue Agent-Verbindung
|
||||
func (h *Hub) RegisterAgent(agentID string, conn *Connection) {
|
||||
agent := &AgentConnection{
|
||||
AgentID: agentID,
|
||||
Connection: conn,
|
||||
LastSeen: time.Now(),
|
||||
}
|
||||
|
||||
h.register <- agent
|
||||
}
|
||||
|
||||
// UnregisterAgent entfernt eine Agent-Verbindung
|
||||
func (h *Hub) UnregisterAgent(agentID string, conn *Connection) {
|
||||
agent := &AgentConnection{
|
||||
AgentID: agentID,
|
||||
Connection: conn,
|
||||
}
|
||||
|
||||
h.unregister <- agent
|
||||
}
|
||||
|
||||
// ProcessAgentMessage verarbeitet eingehende Nachrichten von Agents
|
||||
func (h *Hub) ProcessAgentMessage(agentID string, messageType int, data []byte) error {
|
||||
h.agentsMux.RLock()
|
||||
agent, exists := h.agents[agentID]
|
||||
h.agentsMux.RUnlock()
|
||||
|
||||
if exists {
|
||||
agent.LastSeen = time.Now()
|
||||
}
|
||||
|
||||
// Binary Messages sind normalerweise Tunnel-Daten
|
||||
if messageType == 2 { // websocket.BinaryMessage
|
||||
return h.tunnelManager.ProcessBinaryMessage(agentID, data)
|
||||
}
|
||||
|
||||
// Text Messages sind JSON-Commands/Responses/Events
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BackendTunnel struct {
|
||||
ID string
|
||||
AgentID string
|
||||
TargetHost string
|
||||
TargetPort int
|
||||
ProxyPort int
|
||||
Listener net.Listener
|
||||
Active bool
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type TunnelManager struct {
|
||||
hub *Hub
|
||||
tunnels map[string]*BackendTunnel
|
||||
mutex sync.RWMutex
|
||||
|
||||
// Port-Range für dynamische Tunnel-Proxies
|
||||
portStart int
|
||||
portEnd int
|
||||
usedPorts map[int]bool
|
||||
}
|
||||
|
||||
func NewTunnelManager(hub *Hub) *TunnelManager {
|
||||
return &TunnelManager{
|
||||
hub: hub,
|
||||
tunnels: make(map[string]*BackendTunnel),
|
||||
portStart: 10000,
|
||||
portEnd: 20000,
|
||||
usedPorts: make(map[int]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) OpenTunnel(agentID, targetHost string, targetPort int) (*BackendTunnel, error) {
|
||||
// Prüfen ob Agent verbunden ist
|
||||
if !tm.hub.IsAgentConnected(agentID) {
|
||||
return nil, ErrAgentNotConnected
|
||||
}
|
||||
|
||||
// Tunnel-ID generieren
|
||||
tunnelID := tm.generateTunnelID()
|
||||
|
||||
// Freien Port finden
|
||||
proxyPort, err := tm.allocatePort()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Proxy-Port allokieren fehlgeschlagen: %w", err)
|
||||
}
|
||||
|
||||
// TCP-Listener für Proxy erstellen
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", proxyPort))
|
||||
if err != nil {
|
||||
tm.releasePort(proxyPort)
|
||||
return nil, fmt.Errorf("Proxy-Listener erstellen fehlgeschlagen: %w", err)
|
||||
}
|
||||
|
||||
// Tunnel-Objekt erstellen
|
||||
tunnel := &BackendTunnel{
|
||||
ID: tunnelID,
|
||||
AgentID: agentID,
|
||||
TargetHost: targetHost,
|
||||
TargetPort: targetPort,
|
||||
ProxyPort: proxyPort,
|
||||
Listener: listener,
|
||||
Active: true,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Tunnel registrieren
|
||||
tm.mutex.Lock()
|
||||
tm.tunnels[tunnelID] = tunnel
|
||||
tm.mutex.Unlock()
|
||||
|
||||
// Proxy-Server starten
|
||||
go tm.runTunnelProxy(tunnel)
|
||||
|
||||
// Command an Agent senden
|
||||
cmd := map[string]interface{}{
|
||||
"type": "command",
|
||||
"id": tm.generateTunnelID(),
|
||||
"command": "tunnel_open",
|
||||
"params": map[string]interface{}{
|
||||
"target_host": targetHost,
|
||||
"target_port": targetPort,
|
||||
},
|
||||
}
|
||||
|
||||
cmdJSON, _ := json.Marshal(cmd)
|
||||
if err := tm.hub.SendToAgent(agentID, cmdJSON); err != nil {
|
||||
tm.CloseTunnel(tunnelID)
|
||||
return nil, fmt.Errorf("Tunnel-Command senden fehlgeschlagen: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Tunnel %s geöffnet: Agent %s -> %s:%d (Proxy: :%d)",
|
||||
tunnelID, agentID, targetHost, targetPort, proxyPort)
|
||||
|
||||
return tunnel, nil
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) CloseTunnel(tunnelID string) error {
|
||||
tm.mutex.Lock()
|
||||
tunnel, exists := tm.tunnels[tunnelID]
|
||||
if !exists {
|
||||
tm.mutex.Unlock()
|
||||
return fmt.Errorf("Tunnel %s nicht gefunden", tunnelID)
|
||||
}
|
||||
|
||||
tunnel.Active = false
|
||||
delete(tm.tunnels, tunnelID)
|
||||
tm.mutex.Unlock()
|
||||
|
||||
// Listener schließen
|
||||
if tunnel.Listener != nil {
|
||||
tunnel.Listener.Close()
|
||||
}
|
||||
|
||||
// Port freigeben
|
||||
tm.releasePort(tunnel.ProxyPort)
|
||||
|
||||
// Command an Agent senden
|
||||
if tm.hub.IsAgentConnected(tunnel.AgentID) {
|
||||
cmd := map[string]interface{}{
|
||||
"type": "command",
|
||||
"id": tm.generateTunnelID(),
|
||||
"command": "tunnel_close",
|
||||
"params": map[string]interface{}{
|
||||
"tunnel_id": tunnelID,
|
||||
},
|
||||
}
|
||||
|
||||
cmdJSON, _ := json.Marshal(cmd)
|
||||
tm.hub.SendToAgent(tunnel.AgentID, cmdJSON)
|
||||
}
|
||||
|
||||
log.Printf("Tunnel %s geschlossen", tunnelID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) GetTunnel(tunnelID string) (*BackendTunnel, bool) {
|
||||
tm.mutex.RLock()
|
||||
defer tm.mutex.RUnlock()
|
||||
|
||||
tunnel, exists := tm.tunnels[tunnelID]
|
||||
return tunnel, exists
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) GetAgentTunnels(agentID string) []*BackendTunnel {
|
||||
tm.mutex.RLock()
|
||||
defer tm.mutex.RUnlock()
|
||||
|
||||
var tunnels []*BackendTunnel
|
||||
for _, tunnel := range tm.tunnels {
|
||||
if tunnel.AgentID == agentID {
|
||||
tunnels = append(tunnels, tunnel)
|
||||
}
|
||||
}
|
||||
|
||||
return tunnels
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) GetActiveTunnelCount() int {
|
||||
tm.mutex.RLock()
|
||||
defer tm.mutex.RUnlock()
|
||||
|
||||
return len(tm.tunnels)
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) CloseAgentTunnels(agentID string) {
|
||||
tm.mutex.RLock()
|
||||
var tunnelIDs []string
|
||||
for id, tunnel := range tm.tunnels {
|
||||
if tunnel.AgentID == agentID {
|
||||
tunnelIDs = append(tunnelIDs, id)
|
||||
}
|
||||
}
|
||||
tm.mutex.RUnlock()
|
||||
|
||||
for _, tunnelID := range tunnelIDs {
|
||||
tm.CloseTunnel(tunnelID)
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) runTunnelProxy(tunnel *BackendTunnel) {
|
||||
defer func() {
|
||||
if tunnel.Active {
|
||||
tm.CloseTunnel(tunnel.ID)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("Tunnel %s: Proxy läuft auf Port %d", tunnel.ID, tunnel.ProxyPort)
|
||||
|
||||
for tunnel.Active {
|
||||
conn, err := tunnel.Listener.Accept()
|
||||
if err != nil {
|
||||
if tunnel.Active {
|
||||
log.Printf("Tunnel %s: Accept-Fehler: %v", tunnel.ID, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Jede eingehende Verbindung in eigener Goroutine behandeln
|
||||
go tm.handleProxyConnection(tunnel, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) handleProxyConnection(tunnel *BackendTunnel, conn net.Conn) {
|
||||
defer conn.Close()
|
||||
|
||||
log.Printf("Tunnel %s: Proxy-Verbindung von %s", tunnel.ID, conn.RemoteAddr())
|
||||
|
||||
// Buffer für TCP-Data
|
||||
buffer := make([]byte, 32768)
|
||||
|
||||
for {
|
||||
n, err := conn.Read(buffer)
|
||||
if err != nil {
|
||||
if err.Error() != "EOF" {
|
||||
log.Printf("Tunnel %s: Proxy-Read-Fehler: %v", tunnel.ID, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
// Daten über WebSocket an Agent weiterleiten
|
||||
if err := tm.sendTunnelData(tunnel.ID, buffer[:n]); err != nil {
|
||||
log.Printf("Tunnel %s: Weiterleitung fehlgeschlagen: %v", tunnel.ID, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Tunnel %s: Proxy-Verbindung beendet", tunnel.ID)
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) sendTunnelData(tunnelID string, data []byte) error {
|
||||
tunnel, exists := tm.GetTunnel(tunnelID)
|
||||
if !exists || !tunnel.Active {
|
||||
return fmt.Errorf("Tunnel nicht aktiv")
|
||||
}
|
||||
|
||||
// Binary-Message-Format: [tunnel_id_length:1][tunnel_id][data]
|
||||
tunnelIDBytes := []byte(tunnelID)
|
||||
if len(tunnelIDBytes) > 255 {
|
||||
return fmt.Errorf("tunnel_id zu lang")
|
||||
}
|
||||
|
||||
message := make([]byte, 1+len(tunnelIDBytes)+len(data))
|
||||
message[0] = byte(len(tunnelIDBytes))
|
||||
copy(message[1:], tunnelIDBytes)
|
||||
copy(message[1+len(tunnelIDBytes):], data)
|
||||
|
||||
return tm.hub.SendBinaryToAgent(tunnel.AgentID, message)
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) ProcessBinaryMessage(agentID string, data []byte) error {
|
||||
// Binary-Message parsen: [tunnel_id_length:1][tunnel_id][data]
|
||||
if len(data) < 1 {
|
||||
return fmt.Errorf("Binary-Message zu kurz")
|
||||
}
|
||||
|
||||
tunnelIDLen := int(data[0])
|
||||
if len(data) < 1+tunnelIDLen {
|
||||
return fmt.Errorf("Tunnel-ID zu kurz")
|
||||
}
|
||||
|
||||
tunnelID := string(data[1 : 1+tunnelIDLen])
|
||||
payload := data[1+tunnelIDLen:]
|
||||
|
||||
// Tunnel finden und Daten weiterleiten
|
||||
tunnel, exists := tm.GetTunnel(tunnelID)
|
||||
if !exists || !tunnel.Active || tunnel.AgentID != agentID {
|
||||
return fmt.Errorf("Tunnel %s nicht gefunden oder inaktiv", tunnelID)
|
||||
}
|
||||
|
||||
// Hier würden wir die Daten an aktive Proxy-Verbindungen weiterleiten
|
||||
// Da das komplexer ist, implementieren wir erstmal nur das Logging
|
||||
log.Printf("Tunnel %s: %d Bytes von Agent erhalten", tunnelID, len(payload))
|
||||
|
||||
// TODO: Implement actual forwarding to active proxy connections
|
||||
// Das würde einen Connection-Pool pro Tunnel erfordern
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) allocatePort() (int, error) {
|
||||
tm.mutex.Lock()
|
||||
defer tm.mutex.Unlock()
|
||||
|
||||
for port := tm.portStart; port <= tm.portEnd; port++ {
|
||||
if !tm.usedPorts[port] {
|
||||
tm.usedPorts[port] = true
|
||||
return port, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("keine freien Ports verfügbar")
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) releasePort(port int) {
|
||||
tm.mutex.Lock()
|
||||
defer tm.mutex.Unlock()
|
||||
|
||||
delete(tm.usedPorts, port)
|
||||
}
|
||||
|
||||
func (tm *TunnelManager) generateTunnelID() string {
|
||||
bytes := make([]byte, 8)
|
||||
rand.Read(bytes)
|
||||
return hex.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
// CreateHTTPProxy erstellt einen HTTP-Reverse-Proxy für einen Tunnel
|
||||
func (tm *TunnelManager) CreateHTTPProxy(tunnelID string, w http.ResponseWriter, r *http.Request) error {
|
||||
tunnel, exists := tm.GetTunnel(tunnelID)
|
||||
if !exists || !tunnel.Active {
|
||||
return fmt.Errorf("Tunnel nicht gefunden oder inaktiv")
|
||||
}
|
||||
|
||||
// TODO: Implement HTTP-Reverse-Proxy durch Tunnel
|
||||
// Das ist komplex und erfordert HTTP-Request-Serialisierung über WebSocket
|
||||
|
||||
http.Error(w, "HTTP-Proxy noch nicht implementiert", http.StatusNotImplemented)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user