Resolve crashes noted in #135

Fixes #135
This commit is contained in:
Owen
2026-08-11 17:45:51 -04:00
parent 3542fee459
commit 685f632507
2 changed files with 46 additions and 12 deletions

View File

@@ -810,11 +810,14 @@ func (pm *PeerManager) RemoveAlias(siteId int, aliasName string) error {
newAliases = append(newAliases, a)
}
if aliasToRemove != nil {
address := net.ParseIP(aliasToRemove.AliasAddress)
if address != nil {
pm.dnsProxy.RemoveDNSRecordForSite(aliasName, address, siteId)
}
if aliasToRemove == nil {
// Alias already gone (e.g. duplicate/stale remove message) - nothing to do
return nil
}
address := net.ParseIP(aliasToRemove.AliasAddress)
if address != nil {
pm.dnsProxy.RemoveDNSRecordForSite(aliasName, address, siteId)
}
peer.Aliases = newAliases
@@ -899,7 +902,14 @@ endpoint=%s:%d`, util.FixKey(peer.PublicKey), formattedEndpoint, relayPort)
// at the public endpoint (set synchronously in AddPeer), so this just settles the peer onto
// its steady-state connection within ~1-2 seconds.
func (pm *PeerManager) performRapidInitialTest(siteId int, endpoint string, localEndpoints []string) {
if pm.peerMonitor == nil {
// Snapshot the monitor once under lock and use only the local copy from here on -
// pm.peerMonitor can be concurrently nil'd out by Close()/Stop() (e.g. the tunnel
// tears down right after a peer was added), and re-reading the field later in this
// goroutine would race with that.
pm.mu.RLock()
peerMonitor := pm.peerMonitor
pm.mu.RUnlock()
if peerMonitor == nil {
return
}
@@ -911,14 +921,14 @@ func (pm *PeerManager) performRapidInitialTest(siteId int, endpoint string, loca
wg.Add(1)
go func() {
defer wg.Done()
localWinner = pm.peerMonitor.RapidTestLocalEndpoints(siteId, localEndpoints)
localWinner = peerMonitor.RapidTestLocalEndpoints(siteId, localEndpoints)
}()
}
wg.Add(1)
go func() {
defer wg.Done()
holepunchViable = pm.peerMonitor.RapidTestPeer(siteId, endpoint)
holepunchViable = peerMonitor.RapidTestPeer(siteId, endpoint)
}()
wg.Wait()
@@ -932,7 +942,7 @@ func (pm *PeerManager) performRapidInitialTest(siteId int, endpoint string, loca
if !holepunchViable {
// Holepunch failed rapid test, request relay immediately
logger.Info("Rapid test failed for site %d, requesting relay", siteId)
if err := pm.peerMonitor.RequestRelay(siteId); err != nil {
if err := peerMonitor.RequestRelay(siteId); err != nil {
logger.Error("Failed to request relay for site %d: %v", siteId, err)
}
} else {
@@ -959,9 +969,14 @@ func (pm *PeerManager) Stop() {
// Close stops the peer monitor and cleans up resources
func (pm *PeerManager) Close() {
pm.stopRouteOptimizer()
if pm.peerMonitor != nil {
pm.peerMonitor.Close()
pm.peerMonitor = nil
pm.mu.Lock()
peerMonitor := pm.peerMonitor
pm.peerMonitor = nil
pm.mu.Unlock()
if peerMonitor != nil {
peerMonitor.Close()
}
}

View File

@@ -225,6 +225,10 @@ func (c *Client) Connect() error {
// Close closes the WebSocket connection gracefully
func (c *Client) Close() error {
if c == nil {
return nil
}
// Signal shutdown to all goroutines first
select {
case <-c.done:
@@ -253,6 +257,10 @@ func (c *Client) Close() error {
// Disconnect cleanly closes the websocket connection and suspends message intervals, but allows reconnecting later.
func (c *Client) Disconnect() error {
if c == nil {
return nil
}
c.isDisconnected = true
c.setConnected(false)
@@ -275,6 +283,9 @@ func (c *Client) Disconnect() error {
// SendMessage sends a message through the WebSocket connection
func (c *Client) SendMessage(messageType string, data interface{}) error {
if c == nil {
return fmt.Errorf("client is nil")
}
if c.isDisconnected || c.conn == nil {
return fmt.Errorf("not connected")
}
@@ -295,6 +306,10 @@ func (c *Client) SendMessage(messageType string, data interface{}) error {
}
func (c *Client) SendMessageInterval(messageType string, data interface{}, interval time.Duration, maxAttempts int) (stop func(), update func(newData interface{})) {
if c == nil {
return func() {}, func(interface{}) {}
}
stopChan := make(chan struct{})
updateChan := make(chan interface{})
var dataMux sync.Mutex
@@ -779,6 +794,10 @@ func (c *Client) pingMonitor() {
// This should be called after the client is registered and connected.
// It is safe to call multiple times - only the first call will start the monitor.
func (c *Client) StartPingMonitor() {
if c == nil {
return
}
c.pingStartedMux.Lock()
defer c.pingStartedMux.Unlock()