before form mode add
This commit is contained in:
@@ -58,7 +58,7 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
|||||||
c.JSON(400, gin.H{"error": "targets is required in inline mode"})
|
c.JSON(400, gin.H{"error": "targets is required in inline mode"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
config, err := parseInlineTargetsWithWait(req.Targets, req.Delay, req.WaitConnection)
|
config, err := parseInlineTargetsWithWait(req.Targets, req.Delay, req.WaitConnection, req.Gateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{"error": fmt.Sprintf("invalid targets: %v", err)})
|
c.JSON(400, gin.H{"error": fmt.Sprintf("invalid targets: %v", err)})
|
||||||
return
|
return
|
||||||
@@ -71,7 +71,7 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := knocker.ExecuteWithConfig(&config, req.Verbose, req.WaitConnection); err != nil {
|
if err := knocker.ExecuteWithConfig(&config, true || req.Verbose, req.WaitConnection); err != nil {
|
||||||
c.JSON(400, gin.H{"error": err.Error()})
|
c.JSON(400, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ func setupKnockRoutes(api *gin.RouterGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseInlineTargetsWithWait парсит inline строку целей в Config с поддержкой waitConnection
|
// parseInlineTargetsWithWait парсит inline строку целей в Config с поддержкой waitConnection
|
||||||
func parseInlineTargetsWithWait(targets, delay string, waitConnection bool) (internal.Config, error) {
|
func parseInlineTargetsWithWait(targets, delay string, waitConnection bool, gateway string) (internal.Config, error) {
|
||||||
var config internal.Config
|
var config internal.Config
|
||||||
|
|
||||||
// Парсим targets
|
// Парсим targets
|
||||||
@@ -93,14 +93,18 @@ func parseInlineTargetsWithWait(targets, delay string, waitConnection bool) (int
|
|||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(targetStr, ":")
|
parts := strings.Split(targetStr, ":")
|
||||||
if len(parts) != 3 {
|
if !(len(parts) == 3 || len(parts) == 4) {
|
||||||
return config, fmt.Errorf("invalid target format: %s (expected protocol:host:port)", targetStr)
|
return config, fmt.Errorf("invalid target format: %s (expected protocol:host:port or protocol:host:port:gateway)", targetStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol := strings.TrimSpace(parts[0])
|
protocol := strings.TrimSpace(parts[0])
|
||||||
host := strings.TrimSpace(parts[1])
|
host := strings.TrimSpace(parts[1])
|
||||||
portStr := strings.TrimSpace(parts[2])
|
portStr := strings.TrimSpace(parts[2])
|
||||||
|
|
||||||
|
if len(parts) == 4 {
|
||||||
|
gateway = strings.TrimSpace(parts[3])
|
||||||
|
}
|
||||||
|
|
||||||
if protocol != "tcp" && protocol != "udp" {
|
if protocol != "tcp" && protocol != "udp" {
|
||||||
return config, fmt.Errorf("unsupported protocol: %s (only tcp/udp supported)", protocol)
|
return config, fmt.Errorf("unsupported protocol: %s (only tcp/udp supported)", protocol)
|
||||||
}
|
}
|
||||||
@@ -128,6 +132,7 @@ func parseInlineTargetsWithWait(targets, delay string, waitConnection bool) (int
|
|||||||
Ports: []int{port},
|
Ports: []int{port},
|
||||||
Delay: targetDelay,
|
Delay: targetDelay,
|
||||||
WaitConnection: waitConnection,
|
WaitConnection: waitConnection,
|
||||||
|
Gateway: gateway,
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Targets = append(config.Targets, target)
|
config.Targets = append(config.Targets, target)
|
||||||
|
@@ -35,7 +35,7 @@ require (
|
|||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.23.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/sys v0.20.0
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/text v0.15.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
)
|
)
|
||||||
|
@@ -10,9 +10,12 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -278,14 +281,18 @@ func (pk *PortKnocker) knockTarget(target Target, verbose bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Вычисляем таймаут как половину интервала между пакетами
|
// Вычисляем таймаут как половину интервала между пакетами
|
||||||
timeout := time.Duration(target.Delay) / 2
|
timeout := max(time.Duration(target.Delay)/2,
|
||||||
if timeout < 100*time.Millisecond {
|
// минимальный таймаут
|
||||||
timeout = 100 * time.Millisecond // минимальный таймаут
|
100*time.Millisecond)
|
||||||
}
|
|
||||||
|
|
||||||
for i, port := range target.Ports {
|
for i, port := range target.Ports {
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Printf(" Отправка пакета на %s:%d (%s)\n", target.Host, port, protocol)
|
if target.Gateway != "" {
|
||||||
|
fmt.Printf(" Отправка пакета на %s:%d (%s) через шлюз %s\n", target.Host, port, protocol, target.Gateway)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Отправка пакета на %s:%d (%s)\n", target.Host, port, protocol)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pk.sendPacket(target.Host, port, protocol, target.WaitConnection, timeout, target.Gateway); err != nil {
|
if err := pk.sendPacket(target.Host, port, protocol, target.WaitConnection, timeout, target.Gateway); err != nil {
|
||||||
@@ -314,7 +321,8 @@ func (pk *PortKnocker) knockTarget(target Target, verbose bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendPacket отправляет один пакет на указанный хост и порт
|
// sendPacket отправляет один пакет на указанный хост и порт
|
||||||
func (pk *PortKnocker) sendPacket(host string, port int, protocol string, waitConnection bool, timeout time.Duration, gateway string) error {
|
// sendPacket_backup — резервная копия прежней реализации
|
||||||
|
func (pk *PortKnocker) SendPacket_backup(host string, port int, protocol string, waitConnection bool, timeout time.Duration, gateway string) error {
|
||||||
address := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
address := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
||||||
|
|
||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
@@ -381,8 +389,8 @@ func (pk *PortKnocker) sendPacket(host string, port int, protocol string, waitCo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendPacketWithoutConnection отправляет пакет без установления соединения
|
// sendPacketWithoutConnection_backup — резервная копия прежней реализации
|
||||||
func (pk *PortKnocker) sendPacketWithoutConnection(host string, port int, protocol string, localAddr net.Addr) error {
|
func (pk *PortKnocker) SendPacketWithoutConnection_backup(host string, port int, protocol string, localAddr net.Addr) error {
|
||||||
address := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
address := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
||||||
|
|
||||||
switch protocol {
|
switch protocol {
|
||||||
@@ -440,6 +448,155 @@ func (pk *PortKnocker) sendPacketWithoutConnection(host string, port int, protoc
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseGateway возвращает локальные адреса для TCP/UDP и (опционально) имя интерфейса,
|
||||||
|
// если в gateway передано, например, "eth1". Также поддерживается IP[:port].
|
||||||
|
func parseGateway(gateway string) (tcpLocal *net.TCPAddr, udpLocal *net.UDPAddr, ifaceName string, err error) {
|
||||||
|
gateway = strings.TrimSpace(gateway)
|
||||||
|
if gateway == "" {
|
||||||
|
return nil, nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если это похоже на имя интерфейса (буквы/цифры/дефисы, без точного IP)
|
||||||
|
isIfaceName := regexp.MustCompile(`^[A-Za-z0-9_-]+$`).MatchString(gateway) && net.ParseIP(gateway) == nil
|
||||||
|
if isIfaceName {
|
||||||
|
// Привязка по интерфейсу. LocalAddr оставим пустым — маршрут выберется ядром,
|
||||||
|
// а SO_BINDTODEVICE закрепит интерфейс.
|
||||||
|
return nil, nil, gateway, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Иначе трактуем как IP[:port]
|
||||||
|
host := gateway
|
||||||
|
if !strings.Contains(gateway, ":") {
|
||||||
|
host = gateway + ":0"
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpLocal, err = net.ResolveTCPAddr("tcp", host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", fmt.Errorf("не удалось разрешить локальный TCP адрес %s: %w", host, err)
|
||||||
|
}
|
||||||
|
udpLocal, err = net.ResolveUDPAddr("udp", host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, "", fmt.Errorf("не удалось разрешить локальный UDP адрес %s: %w", host, err)
|
||||||
|
}
|
||||||
|
return tcpLocal, udpLocal, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDialer создаёт net.Dialer с опциональной привязкой к LocalAddr и интерфейсу (Linux SO_BINDTODEVICE)
|
||||||
|
func buildDialer(protocol string, tcpLocal *net.TCPAddr, udpLocal *net.UDPAddr, timeout time.Duration, ifaceName string) *net.Dialer {
|
||||||
|
d := &net.Dialer{Timeout: timeout}
|
||||||
|
if protocol == "tcp" && tcpLocal != nil {
|
||||||
|
d.LocalAddr = tcpLocal
|
||||||
|
}
|
||||||
|
if protocol == "udp" && udpLocal != nil {
|
||||||
|
d.LocalAddr = udpLocal
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(ifaceName) != "" {
|
||||||
|
d.Control = func(network, address string, c syscall.RawConn) error {
|
||||||
|
var ctrlErr error
|
||||||
|
err := c.Control(func(fd uintptr) {
|
||||||
|
// Привязка сокета к интерфейсу по имени
|
||||||
|
ctrlErr = unix.SetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_BINDTODEVICE, ifaceName)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ctrlErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendPacket — обновлённая реализация с поддержкой UDPAddr и SO_BINDTODEVICE
|
||||||
|
func (pk *PortKnocker) sendPacket(host string, port int, protocol string, waitConnection bool, timeout time.Duration, gateway string) error {
|
||||||
|
address := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
||||||
|
|
||||||
|
tcpLocal, udpLocal, ifaceName, err := parseGateway(gateway)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case "tcp":
|
||||||
|
dialer := buildDialer("tcp", tcpLocal, nil, timeout, ifaceName)
|
||||||
|
conn, err := dialer.Dial("tcp", address)
|
||||||
|
if err != nil {
|
||||||
|
if waitConnection {
|
||||||
|
return fmt.Errorf("не удалось подключиться к %s: %w", address, err)
|
||||||
|
}
|
||||||
|
// без ожидания — пробуем best-effort отправку
|
||||||
|
return pk.sendPacketWithoutConnection(host, port, protocol, dialer.LocalAddr)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
_, err = conn.Write([]byte{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("не удалось отправить пакет: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case "udp":
|
||||||
|
dialer := buildDialer("udp", nil, udpLocal, timeout, ifaceName)
|
||||||
|
conn, err := dialer.Dial("udp", address)
|
||||||
|
if err != nil {
|
||||||
|
if waitConnection {
|
||||||
|
return fmt.Errorf("не удалось подключиться к %s: %w", address, err)
|
||||||
|
}
|
||||||
|
return pk.sendPacketWithoutConnection(host, port, protocol, dialer.LocalAddr)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
_, err = conn.Write([]byte{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("не удалось отправить пакет: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("неподдерживаемый протокол: %s", protocol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendPacketWithoutConnection — обновлённая реализация best-effort без ожидания соединения
|
||||||
|
func (pk *PortKnocker) sendPacketWithoutConnection(host string, port int, protocol string, localAddr net.Addr) error {
|
||||||
|
address := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case "udp":
|
||||||
|
// Используем Dialer с коротким таймаутом, локальный адрес может быть *net.UDPAddr
|
||||||
|
var udpLocal *net.UDPAddr
|
||||||
|
if la, ok := localAddr.(*net.UDPAddr); ok {
|
||||||
|
udpLocal = la
|
||||||
|
}
|
||||||
|
d := &net.Dialer{Timeout: 200 * time.Millisecond}
|
||||||
|
if udpLocal != nil {
|
||||||
|
d.LocalAddr = udpLocal
|
||||||
|
}
|
||||||
|
conn, err := d.Dial("udp", address)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
} // best-effort
|
||||||
|
defer conn.Close()
|
||||||
|
_, err = conn.Write([]byte{})
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "tcp":
|
||||||
|
// Короткий таймаут и игнор ошибок
|
||||||
|
var tcpLocal *net.TCPAddr
|
||||||
|
if la, ok := localAddr.(*net.TCPAddr); ok {
|
||||||
|
tcpLocal = la
|
||||||
|
}
|
||||||
|
d := &net.Dialer{Timeout: 100 * time.Millisecond}
|
||||||
|
if tcpLocal != nil {
|
||||||
|
d.LocalAddr = tcpLocal
|
||||||
|
}
|
||||||
|
conn, err := d.Dial("tcp", address)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
_, _ = conn.Write([]byte{})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// showEasterEgg показывает забавный ASCII-арт
|
// showEasterEgg показывает забавный ASCII-арт
|
||||||
func (pk *PortKnocker) showEasterEgg() {
|
func (pk *PortKnocker) showEasterEgg() {
|
||||||
fmt.Println("\n🎯 🎯 🎯 EASTER EGG ACTIVATED! 🎯 🎯 🎯")
|
fmt.Println("\n🎯 🎯 🎯 EASTER EGG ACTIVATED! 🎯 🎯 🎯")
|
||||||
|
@@ -51,7 +51,8 @@
|
|||||||
formControlName="mode"
|
formControlName="mode"
|
||||||
[options]="[
|
[options]="[
|
||||||
{ label: 'Inline', value: 'inline' },
|
{ label: 'Inline', value: 'inline' },
|
||||||
{ label: 'YAML', value: 'yaml' }
|
{ label: 'YAML', value: 'yaml' },
|
||||||
|
{ label: 'Form', value: 'form' }
|
||||||
]"
|
]"
|
||||||
optionLabel="label"
|
optionLabel="label"
|
||||||
optionValue="value"
|
optionValue="value"
|
||||||
|
Reference in New Issue
Block a user