|
|
@@ -3,6 +3,7 @@ package vpn
|
|
|
import (
|
|
|
"encoding/json"
|
|
|
"fmt"
|
|
|
+ "net"
|
|
|
"os"
|
|
|
"os/exec"
|
|
|
"path/filepath"
|
|
|
@@ -34,6 +35,39 @@ func NewWireGuardManager() *WireGuardManager {
|
|
|
return m
|
|
|
}
|
|
|
|
|
|
+// CleanEndpoint normalizes an endpoint address and port.
|
|
|
+// If addr contains a port (e.g. "1.2.3.4:51820", "[2001:db8::1]:51820"), it strips the port
|
|
|
+// from addr and returns the clean host and the parsed port.
|
|
|
+func CleanEndpoint(addr string, port int) (string, int) {
|
|
|
+ addr = strings.TrimSpace(addr)
|
|
|
+ if addr == "" || addr == "(none)" {
|
|
|
+ return "", 0
|
|
|
+ }
|
|
|
+
|
|
|
+ // Remove multiple colons/ports if corrupted (e.g. "10.2.145.253:37616:37616")
|
|
|
+ for {
|
|
|
+ if h, p, err := net.SplitHostPort(addr); err == nil {
|
|
|
+ if parsedPort, err := strconv.Atoi(p); err == nil && parsedPort > 0 {
|
|
|
+ port = parsedPort
|
|
|
+ }
|
|
|
+ addr = h
|
|
|
+ } else if strings.Contains(addr, ":") && !strings.HasPrefix(addr, "[") {
|
|
|
+ lastColon := strings.LastIndex(addr, ":")
|
|
|
+ maybePort := addr[lastColon+1:]
|
|
|
+ if parsedPort, err := strconv.Atoi(maybePort); err == nil && parsedPort > 0 {
|
|
|
+ port = parsedPort
|
|
|
+ addr = addr[:lastColon]
|
|
|
+ } else {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return addr, port
|
|
|
+}
|
|
|
+
|
|
|
func (m *WireGuardManager) load() {
|
|
|
if data, err := os.ReadFile(WGInterfacesStorePath); err == nil {
|
|
|
_ = json.Unmarshal(data, &m.interfaces)
|
|
|
@@ -47,6 +81,10 @@ func (m *WireGuardManager) load() {
|
|
|
if m.peers == nil {
|
|
|
m.peers = make([]WireGuardPeer, 0)
|
|
|
}
|
|
|
+ // Sanitize endpoints for all stored peers
|
|
|
+ for i := range m.peers {
|
|
|
+ m.peers[i].EndpointAddress, m.peers[i].EndpointPort = CleanEndpoint(m.peers[i].EndpointAddress, m.peers[i].EndpointPort)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
func (m *WireGuardManager) save() error {
|
|
|
@@ -298,8 +336,10 @@ func (m *WireGuardManager) ListPeers(ifaceFilter string) []WireGuardPeer {
|
|
|
handshakeStr = time.Since(time.Unix(handshakeUnix, 0)).Round(time.Second).String() + " ago"
|
|
|
}
|
|
|
|
|
|
+ epHost, epPort := CleanEndpoint(endpoint, 0)
|
|
|
dumpMap[iface.Name][pubKey] = WireGuardPeer{
|
|
|
- EndpointAddress: endpoint,
|
|
|
+ EndpointAddress: epHost,
|
|
|
+ EndpointPort: epPort,
|
|
|
LatestHandshake: handshakeStr,
|
|
|
TransferRxBytes: rx,
|
|
|
TransferTxBytes: tx,
|
|
|
@@ -321,7 +361,12 @@ func (m *WireGuardManager) ListPeers(ifaceFilter string) []WireGuardPeer {
|
|
|
// Overlay runtime stats if available
|
|
|
if ifaceDumps, ok := dumpMap[p.Interface]; ok {
|
|
|
if stats, has := ifaceDumps[p.PublicKey]; has {
|
|
|
- item.EndpointAddress = stats.EndpointAddress
|
|
|
+ if stats.EndpointAddress != "" {
|
|
|
+ item.EndpointAddress = stats.EndpointAddress
|
|
|
+ if stats.EndpointPort > 0 {
|
|
|
+ item.EndpointPort = stats.EndpointPort
|
|
|
+ }
|
|
|
+ }
|
|
|
item.LatestHandshake = stats.LatestHandshake
|
|
|
item.TransferRxBytes = stats.TransferRxBytes
|
|
|
item.TransferTxBytes = stats.TransferTxBytes
|
|
|
@@ -329,6 +374,7 @@ func (m *WireGuardManager) ListPeers(ifaceFilter string) []WireGuardPeer {
|
|
|
item.TransferTxHuman = stats.TransferTxHuman
|
|
|
}
|
|
|
}
|
|
|
+ item.EndpointAddress, item.EndpointPort = CleanEndpoint(item.EndpointAddress, item.EndpointPort)
|
|
|
result = append(result, item)
|
|
|
}
|
|
|
return result
|
|
|
@@ -378,6 +424,10 @@ func (m *WireGuardManager) SavePeer(req SaveWireGuardPeerRequest) error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ req.EndpointAddress, req.EndpointPort = CleanEndpoint(req.EndpointAddress, req.EndpointPort)
|
|
|
+ req.WireGuardPeer.EndpointAddress = req.EndpointAddress
|
|
|
+ req.WireGuardPeer.EndpointPort = req.EndpointPort
|
|
|
+
|
|
|
found := false
|
|
|
for i, existing := range m.peers {
|
|
|
if existing.PublicKey == req.PublicKey && existing.Interface == req.Interface {
|
|
|
@@ -457,10 +507,11 @@ func (m *WireGuardManager) SetPeerEnabled(ifaceName, pubKey string, enabled bool
|
|
|
if len(p.AllowedIPs) > 0 {
|
|
|
args = append(args, "allowed-ips", strings.Join(p.AllowedIPs, ","))
|
|
|
}
|
|
|
- if p.EndpointAddress != "" {
|
|
|
- ep := p.EndpointAddress
|
|
|
- if p.EndpointPort > 0 {
|
|
|
- ep = fmt.Sprintf("%s:%d", p.EndpointAddress, p.EndpointPort)
|
|
|
+ epHost, epPort := CleanEndpoint(p.EndpointAddress, p.EndpointPort)
|
|
|
+ if epHost != "" {
|
|
|
+ ep := epHost
|
|
|
+ if epPort > 0 {
|
|
|
+ ep = fmt.Sprintf("%s:%d", epHost, epPort)
|
|
|
}
|
|
|
args = append(args, "endpoint", ep)
|
|
|
}
|
|
|
@@ -547,10 +598,11 @@ func (m *WireGuardManager) generateWGConfigFile(ifaceName string) error {
|
|
|
if len(p.AllowedIPs) > 0 {
|
|
|
sb.WriteString(fmt.Sprintf("AllowedIPs = %s\n", strings.Join(p.AllowedIPs, ", ")))
|
|
|
}
|
|
|
- if p.EndpointAddress != "" {
|
|
|
- ep := p.EndpointAddress
|
|
|
- if p.EndpointPort > 0 {
|
|
|
- ep = fmt.Sprintf("%s:%d", p.EndpointAddress, p.EndpointPort)
|
|
|
+ epHost, epPort := CleanEndpoint(p.EndpointAddress, p.EndpointPort)
|
|
|
+ if epHost != "" {
|
|
|
+ ep := epHost
|
|
|
+ if epPort > 0 {
|
|
|
+ ep = fmt.Sprintf("%s:%d", epHost, epPort)
|
|
|
}
|
|
|
sb.WriteString(fmt.Sprintf("Endpoint = %s\n", ep))
|
|
|
}
|