package cmd import ( "fmt" "strconv" "strings" "time" "port-knocker/internal" "github.com/gin-gonic/gin" ) // setupKnockRoutes настраивает роуты для выполнения port knocking func setupKnockRoutes(api *gin.RouterGroup) { // Execute: вход inline или YAML конфиг api.POST("/execute", func(c *gin.Context) { var req struct { Targets string `json:"targets"` Delay string `json:"delay"` Verbose bool `json:"verbose"` WaitConnection bool `json:"waitConnection"` Gateway string `json:"gateway"` ConfigYaml string `json:"config_yaml"` } if err := c.BindJSON(&req); err != nil { c.JSON(400, gin.H{"error": fmt.Sprintf("bad json: %v", err)}) return } knocker := internal.NewPortKnocker() // Определяем режим: inline или YAML if strings.TrimSpace(req.ConfigYaml) != "" { // YAML режим - загружаем конфигурацию из строки config, err := internal.LoadConfigFromString(req.ConfigYaml) if err != nil { c.JSON(400, gin.H{"error": fmt.Sprintf("invalid yaml: %v", err)}) return } // Применяем дополнительные параметры из запроса if req.Gateway != "" { for i := range config.Targets { config.Targets[i].Gateway = req.Gateway } } if err := knocker.ExecuteWithConfig(config, req.Verbose, req.WaitConnection); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{"status": "ok"}) return } else { // Inline режим if strings.TrimSpace(req.Targets) == "" { c.JSON(400, gin.H{"error": "targets is required in inline mode"}) return } config, err := parseInlineTargetsWithWait(req.Targets, req.Delay, req.WaitConnection) if err != nil { c.JSON(400, gin.H{"error": fmt.Sprintf("invalid targets: %v", err)}) return } // Применяем gateway к каждой цели if req.Gateway != "" { for i := range config.Targets { config.Targets[i].Gateway = req.Gateway } } if err := knocker.ExecuteWithConfig(&config, req.Verbose, req.WaitConnection); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{"status": "ok"}) } }) } // parseInlineTargetsWithWait парсит inline строку целей в Config с поддержкой waitConnection func parseInlineTargetsWithWait(targets, delay string, waitConnection bool) (internal.Config, error) { var config internal.Config // Парсим targets targetStrings := strings.Split(targets, ";") for _, targetStr := range targetStrings { targetStr = strings.TrimSpace(targetStr) if targetStr == "" { continue } parts := strings.Split(targetStr, ":") if len(parts) != 3 { return config, fmt.Errorf("invalid target format: %s (expected protocol:host:port)", targetStr) } protocol := strings.TrimSpace(parts[0]) host := strings.TrimSpace(parts[1]) portStr := strings.TrimSpace(parts[2]) if protocol != "tcp" && protocol != "udp" { return config, fmt.Errorf("unsupported protocol: %s (only tcp/udp supported)", protocol) } port, err := strconv.Atoi(portStr) if err != nil { return config, fmt.Errorf("invalid port: %s", portStr) } // Парсим delay var targetDelay internal.Duration if delay != "" { duration, err := time.ParseDuration(delay) if err != nil { return config, fmt.Errorf("invalid delay: %s", delay) } targetDelay = internal.Duration(duration) } else { targetDelay = internal.Duration(time.Second) } target := internal.Target{ Protocol: protocol, Host: host, Ports: []int{port}, Delay: targetDelay, WaitConnection: waitConnection, } config.Targets = append(config.Targets, target) } if len(config.Targets) == 0 { return config, fmt.Errorf("no valid targets found") } return config, nil }