fix(backend): randomize new tunnel relay ports safely

This commit is contained in:
sagitchu
2026-04-01 20:16:31 +08:00
parent 8b9cdef0e4
commit 608fbf74de
5 changed files with 90 additions and 18 deletions
+24 -5
View File
@@ -2306,13 +2306,24 @@ func (h *Handler) forwardBatchChangeTunnel(w http.ResponseWriter, r *http.Reques
nd, ndErr := h.getNodeRecord(nid)
if ndErr != nil {
portRangeErr = ndErr
continue
portRangeOk = false
break
}
if validateErr := validateRemoteNodePort(nd, p); validateErr != nil {
portRangeOk = false
portRangeErr = validateErr
break
}
if validateErr := validateLocalNodePort(nd, p); validateErr != nil {
portRangeOk = false
portRangeErr = validateErr
break
}
if validateErr := h.validateForwardPortAvailability(nd, p, id); validateErr != nil {
portRangeOk = false
portRangeErr = validateErr
break
}
}
if !portRangeOk {
fail++
@@ -2732,7 +2743,11 @@ func (h *Handler) prepareTunnelCreateState(tx *gorm.DB, req map[string]interface
}
if !isRemote {
var err error
port, err = h.repo.PickNodePortTx(tx, nodeID, allocated, excludeTunnelID)
if excludeTunnelID > 0 {
port, err = h.repo.PickNodePortTx(tx, nodeID, allocated, excludeTunnelID)
} else {
port, err = h.repo.PickRandomNodePortTx(tx, nodeID, allocated, excludeTunnelID)
}
if err != nil {
return nil, err
}
@@ -2767,7 +2782,11 @@ func (h *Handler) prepareTunnelCreateState(tx *gorm.DB, req map[string]interface
}
if !isRemote {
var err error
port, err = h.repo.PickNodePortTx(tx, nodeID, allocated, excludeTunnelID)
if excludeTunnelID > 0 {
port, err = h.repo.PickNodePortTx(tx, nodeID, allocated, excludeTunnelID)
} else {
port, err = h.repo.PickRandomNodePortTx(tx, nodeID, allocated, excludeTunnelID)
}
if err != nil {
return nil, err
}
@@ -3641,7 +3660,7 @@ func (h *Handler) replaceTunnelChainsTx(tx *gorm.DB, tunnelID int64, req map[str
port := asInt(n["port"], 0)
if port <= 0 {
var pickErr error
port, pickErr = h.repo.PickNodePortTx(tx, nodeID, allocated, 0)
port, pickErr = h.repo.PickRandomNodePortTx(tx, nodeID, allocated, 0)
if pickErr != nil {
return pickErr
}
@@ -3671,7 +3690,7 @@ func (h *Handler) replaceTunnelChainsTx(tx *gorm.DB, tunnelID int64, req map[str
port := asInt(n["port"], 0)
if port <= 0 {
var pickErr error
port, pickErr = h.repo.PickNodePortTx(tx, nodeID, allocated, 0)
port, pickErr = h.repo.PickRandomNodePortTx(tx, nodeID, allocated, 0)
if pickErr != nil {
return pickErr
}
@@ -1,9 +1,11 @@
package repo
import (
"crypto/rand"
"database/sql"
"errors"
"fmt"
"math/big"
"sort"
"strconv"
"strings"
@@ -453,6 +455,14 @@ func (r *Repository) IsRemoteNodeTx(tx *gorm.DB, nodeID int64) (bool, error) {
}
func (r *Repository) PickNodePortTx(tx *gorm.DB, nodeID int64, allocated map[int64]int, excludeTunnelID int64) (int, error) {
return r.pickNodePortTx(tx, nodeID, allocated, excludeTunnelID, false)
}
func (r *Repository) PickRandomNodePortTx(tx *gorm.DB, nodeID int64, allocated map[int64]int, excludeTunnelID int64) (int, error) {
return r.pickNodePortTx(tx, nodeID, allocated, excludeTunnelID, true)
}
func (r *Repository) pickNodePortTx(tx *gorm.DB, nodeID int64, allocated map[int64]int, excludeTunnelID int64, randomPick bool) (int, error) {
if tx == nil {
return 0, errors.New("database unavailable")
}
@@ -505,6 +515,7 @@ func (r *Repository) PickNodePortTx(tx *gorm.DB, nodeID int64, allocated map[int
}
}
var available []int
for _, candidate := range candidates {
if candidate <= 0 {
continue
@@ -512,11 +523,25 @@ func (r *Repository) PickNodePortTx(tx *gorm.DB, nodeID int64, allocated map[int
if _, ok := used[candidate]; ok {
continue
}
allocated[nodeID] = candidate
return candidate, nil
available = append(available, candidate)
}
return 0, errors.New("节点端口已满,无可用端口")
if len(available) == 0 {
return 0, errors.New("节点端口已满,无可用端口")
}
if !randomPick {
allocated[nodeID] = available[0]
return available[0], nil
}
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(available))))
if err != nil {
allocated[nodeID] = available[0]
return available[0], nil
}
port := available[idx.Int64()]
allocated[nodeID] = port
return port, nil
}
func (r *Repository) GetTunnelIPPreference(tunnelID int64) string {