Files

85 lines
2.3 KiB
Go

package api
import (
"fmt"
"net"
"strconv"
"github.com/gin-gonic/gin"
"vpn-platform/backend/internal/model"
"vpn-platform/backend/internal/service"
"vpn-platform/backend/internal/store"
)
// nextOIDCPort suggests a free loopback listener; starting the tunnel still
// checks the port again because another process may bind it in the meantime.
func (s *Server) nextOIDCPort(c *gin.Context) {
var tunnels []model.Tunnel
if err := store.DB.Where("protocol = ?", "openvpn").Find(&tunnels).Error; err != nil {
c.JSON(500, gin.H{"error": "无法读取已配置的 OpenVPN 专线"})
return
}
reserved := make(map[int]bool)
maxConfigured := 19089
for _, tunnel := range tunnels {
cfg, err := store.TunnelConfig(&tunnel)
if err != nil {
continue
}
if service.Str(cfg, "proto") == "tcp" || service.Str(cfg, "proto") == "tcp-server" {
reserved[int(service.Num(cfg, "port", 1194))] = true
}
if service.Str(cfg, "auth_mode") != "oidc" {
continue
}
port := int(service.Num(cfg, "oidc_http_port", 19090))
reserved[port] = true
if port >= 19090 && port > maxConfigured {
maxConfigured = port
}
}
start, direction := maxConfigured+1, 1
if start > 65535 {
start = 19090
}
if from := c.Query("from"); from != "" {
var err error
start, err = strconv.Atoi(from)
if err != nil || start < 1024 || start > 65535 {
c.JSON(400, gin.H{"error": "端口必须为 1024-65535"})
return
}
if c.Query("direction") == "-1" {
direction = -1
} else if c.Query("direction") != "" && c.Query("direction") != "1" {
c.JSON(400, gin.H{"error": "方向必须为 1 或 -1"})
return
}
}
if port, ok := findAvailableOIDCPort(start, direction, reserved, loopbackTCPAvailable); ok {
c.JSON(200, gin.H{"port": port})
return
}
c.JSON(409, gin.H{"error": "该方向没有可用的本机认证端口,请手动选择其他起始端口"})
}
func findAvailableOIDCPort(start, direction int, reserved map[int]bool, available func(int) bool) (int, bool) {
for port := start; port >= 1024 && port <= 65535; port += direction {
if !reserved[port] && available(port) {
return port, true
}
}
return 0, false
}
func loopbackTCPAvailable(port int) bool {
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
return false
}
_ = listener.Close()
return true
}