mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-20 12:55:00 +03:00
75f3702dd3
The panel's SOCKS5 egress for AmneziaWG outbounds bound the fixed 127.0.0.1:64900, and every generated socks bridge dialed that constant. 64900 sits inside Windows' dynamic port range, where the OS can reserve whole blocks (this host excludes 64885-64984), so on the Windows builds release.yml ships the listener could stay down and every AmneziaWG outbound with it. Listen now tries 64900 first and falls back to any free loopback port; bridges, the outbound probe and the port-conflict check use EgressPort(), the port actually held. Bridges are generated apart from the listener, so BuildSocksBridge records the port it wrote and the AmneziaWG job requests an Xray restart while the listener holds a different one. Where 64900 is free nothing changes. The job's restart request is two lines of wiring no test reaches; the staleness it acts on is pinned by TestBridgesStaleUntilRegeneratedForTheBoundPort.
50 lines
1.2 KiB
Go
50 lines
1.2 KiB
Go
package amneziawgnet
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// bridgedPort is the port the last generated socks bridge dials; 0 before any.
|
|
var bridgedPort atomic.Int64
|
|
|
|
// BuildSocksBridge swaps an "amneziawg" outbound for its loopback socks
|
|
// form, preserving sibling keys; false = unbridgeable, fail loudly upstream.
|
|
func BuildSocksBridge(raw []byte) ([]byte, bool) {
|
|
var ob map[string]any
|
|
if err := json.Unmarshal(raw, &ob); err != nil {
|
|
return nil, false
|
|
}
|
|
tag, _ := ob["tag"].(string)
|
|
if tag == "" {
|
|
return nil, false
|
|
}
|
|
port := EgressPort()
|
|
settings := map[string]any{
|
|
"address": "127.0.0.1",
|
|
"port": port,
|
|
"user": tag,
|
|
"pass": SocksPassword(),
|
|
}
|
|
bs, err := json.Marshal(settings)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
ob["protocol"] = "socks"
|
|
ob["settings"] = json.RawMessage(bs)
|
|
out, err := json.Marshal(ob)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
bridgedPort.Store(int64(port))
|
|
return out, true
|
|
}
|
|
|
|
// BridgesStale reports that the egress listener holds another port than the last
|
|
// generated socks bridge dials, so Xray has to regenerate its config.
|
|
func BridgesStale() bool {
|
|
bound, listening := GetEgressServer().boundPort()
|
|
bridged := int(bridgedPort.Load())
|
|
return listening && bridged != 0 && bridged != bound
|
|
}
|