2022-01-10 18:43:13 +01:00
|
|
|
package peer
|
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// State contains the latest state of a peer
|
|
|
|
type State struct {
|
|
|
|
IP string
|
|
|
|
PubKey string
|
|
|
|
FQDN string
|
|
|
|
ConnStatus ConnStatus
|
|
|
|
ConnStatusUpdate time.Time
|
|
|
|
Relayed bool
|
|
|
|
Direct bool
|
|
|
|
LocalIceCandidateType string
|
|
|
|
RemoteIceCandidateType string
|
|
|
|
}
|
|
|
|
|
|
|
|
// LocalPeerState contains the latest state of the local peer
|
|
|
|
type LocalPeerState struct {
|
|
|
|
IP string
|
|
|
|
PubKey string
|
|
|
|
KernelInterface bool
|
|
|
|
FQDN string
|
|
|
|
}
|
|
|
|
|
|
|
|
// SignalState contains the latest state of a signal connection
|
|
|
|
type SignalState struct {
|
|
|
|
URL string
|
|
|
|
Connected bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// ManagementState contains the latest state of a management connection
|
|
|
|
type ManagementState struct {
|
|
|
|
URL string
|
|
|
|
Connected bool
|
|
|
|
}
|
2022-01-10 18:43:13 +01:00
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
// FullStatus contains the full state held by the Status instance
|
|
|
|
type FullStatus struct {
|
|
|
|
Peers []State
|
|
|
|
ManagementState ManagementState
|
|
|
|
SignalState SignalState
|
|
|
|
LocalPeerState LocalPeerState
|
|
|
|
}
|
|
|
|
|
|
|
|
// Status holds a state of peers, signal and management connections
|
|
|
|
type Status struct {
|
2023-03-16 17:22:36 +01:00
|
|
|
mux sync.Mutex
|
|
|
|
peers map[string]State
|
|
|
|
changeNotify map[string]chan struct{}
|
|
|
|
signalState bool
|
|
|
|
managementState bool
|
|
|
|
localPeer LocalPeerState
|
|
|
|
offlinePeers []State
|
|
|
|
mgmAddress string
|
|
|
|
signalAddress string
|
2023-03-17 10:37:27 +01:00
|
|
|
notifier *notifier
|
2023-06-19 11:20:34 +02:00
|
|
|
|
|
|
|
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
|
|
|
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
|
|
|
// set to true this variable and at the end of the processing we will reset it by the FinishPeerListModifications()
|
|
|
|
peerListChangedForNotification bool
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
2022-01-10 18:43:13 +01:00
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
// NewRecorder returns a new Status instance
|
2023-03-16 17:22:36 +01:00
|
|
|
func NewRecorder(mgmAddress string) *Status {
|
2023-03-03 19:49:18 +01:00
|
|
|
return &Status{
|
|
|
|
peers: make(map[string]State),
|
|
|
|
changeNotify: make(map[string]chan struct{}),
|
2023-03-07 10:17:25 +01:00
|
|
|
offlinePeers: make([]State, 0),
|
2023-03-17 10:37:27 +01:00
|
|
|
notifier: newNotifier(),
|
2023-03-16 17:22:36 +01:00
|
|
|
mgmAddress: mgmAddress,
|
2022-01-10 18:43:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-07 10:17:25 +01:00
|
|
|
// ReplaceOfflinePeers replaces
|
|
|
|
func (d *Status) ReplaceOfflinePeers(replacement []State) {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
d.offlinePeers = make([]State, len(replacement))
|
|
|
|
copy(d.offlinePeers, replacement)
|
2023-06-19 11:20:34 +02:00
|
|
|
|
|
|
|
// todo we should set to true in case if the list changed only
|
|
|
|
d.peerListChangedForNotification = true
|
2023-03-07 10:17:25 +01:00
|
|
|
}
|
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
// AddPeer adds peer to Daemon status map
|
2023-06-19 11:20:34 +02:00
|
|
|
func (d *Status) AddPeer(peerPubKey string, fqdn string) error {
|
2023-03-03 19:49:18 +01:00
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
_, ok := d.peers[peerPubKey]
|
|
|
|
if ok {
|
|
|
|
return errors.New("peer already exist")
|
|
|
|
}
|
2023-06-19 11:20:34 +02:00
|
|
|
d.peers[peerPubKey] = State{
|
|
|
|
PubKey: peerPubKey,
|
|
|
|
ConnStatus: StatusDisconnected,
|
|
|
|
FQDN: fqdn,
|
|
|
|
}
|
|
|
|
d.peerListChangedForNotification = true
|
2023-03-03 19:49:18 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetPeer adds peer to Daemon status map
|
|
|
|
func (d *Status) GetPeer(peerPubKey string) (State, error) {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
state, ok := d.peers[peerPubKey]
|
|
|
|
if !ok {
|
|
|
|
return State{}, errors.New("peer not found")
|
|
|
|
}
|
|
|
|
return state, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemovePeer removes peer from Daemon status map
|
|
|
|
func (d *Status) RemovePeer(peerPubKey string) error {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
_, ok := d.peers[peerPubKey]
|
2023-06-19 11:20:34 +02:00
|
|
|
if !ok {
|
|
|
|
return errors.New("no peer with to remove")
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
2023-06-19 11:20:34 +02:00
|
|
|
delete(d.peers, peerPubKey)
|
|
|
|
d.peerListChangedForNotification = true
|
|
|
|
return nil
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdatePeerState updates peer status
|
|
|
|
func (d *Status) UpdatePeerState(receivedState State) error {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
peerState, ok := d.peers[receivedState.PubKey]
|
|
|
|
if !ok {
|
|
|
|
return errors.New("peer doesn't exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
if receivedState.IP != "" {
|
|
|
|
peerState.IP = receivedState.IP
|
|
|
|
}
|
|
|
|
|
2023-06-17 09:03:52 +02:00
|
|
|
skipNotification := shouldSkipNotify(receivedState, peerState)
|
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
if receivedState.ConnStatus != peerState.ConnStatus {
|
|
|
|
peerState.ConnStatus = receivedState.ConnStatus
|
|
|
|
peerState.ConnStatusUpdate = receivedState.ConnStatusUpdate
|
|
|
|
peerState.Direct = receivedState.Direct
|
|
|
|
peerState.Relayed = receivedState.Relayed
|
|
|
|
peerState.LocalIceCandidateType = receivedState.LocalIceCandidateType
|
|
|
|
peerState.RemoteIceCandidateType = receivedState.RemoteIceCandidateType
|
|
|
|
}
|
|
|
|
|
|
|
|
d.peers[receivedState.PubKey] = peerState
|
|
|
|
|
2023-06-17 09:03:52 +02:00
|
|
|
if skipNotification {
|
2023-06-01 16:00:44 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
ch, found := d.changeNotify[receivedState.PubKey]
|
|
|
|
if found && ch != nil {
|
|
|
|
close(ch)
|
|
|
|
d.changeNotify[receivedState.PubKey] = nil
|
|
|
|
}
|
|
|
|
|
2023-03-17 10:37:27 +01:00
|
|
|
d.notifyPeerListChanged()
|
2023-03-03 19:49:18 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-10 16:33:13 +01:00
|
|
|
func shouldSkipNotify(received, curr State) bool {
|
2023-06-11 21:51:33 +02:00
|
|
|
switch {
|
2023-11-10 16:33:13 +01:00
|
|
|
case received.ConnStatus == StatusConnecting:
|
2023-06-11 21:51:33 +02:00
|
|
|
return true
|
2023-11-10 16:33:13 +01:00
|
|
|
case received.ConnStatus == StatusDisconnected && curr.ConnStatus == StatusConnecting:
|
2023-06-11 21:51:33 +02:00
|
|
|
return true
|
2023-11-10 16:33:13 +01:00
|
|
|
case received.ConnStatus == StatusDisconnected && curr.ConnStatus == StatusDisconnected:
|
2023-06-11 21:51:33 +02:00
|
|
|
return curr.IP != ""
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
// UpdatePeerFQDN update peer's state fqdn only
|
|
|
|
func (d *Status) UpdatePeerFQDN(peerPubKey, fqdn string) error {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
peerState, ok := d.peers[peerPubKey]
|
|
|
|
if !ok {
|
|
|
|
return errors.New("peer doesn't exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
peerState.FQDN = fqdn
|
|
|
|
d.peers[peerPubKey] = peerState
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-06-19 11:20:34 +02:00
|
|
|
// FinishPeerListModifications this event invoke the notification
|
|
|
|
func (d *Status) FinishPeerListModifications() {
|
|
|
|
d.mux.Lock()
|
|
|
|
|
|
|
|
if !d.peerListChangedForNotification {
|
|
|
|
d.mux.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
d.peerListChangedForNotification = false
|
|
|
|
d.mux.Unlock()
|
|
|
|
|
|
|
|
d.notifyPeerListChanged()
|
|
|
|
}
|
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
// GetPeerStateChangeNotifier returns a change notifier channel for a peer
|
|
|
|
func (d *Status) GetPeerStateChangeNotifier(peer string) <-chan struct{} {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
ch, found := d.changeNotify[peer]
|
|
|
|
if !found || ch == nil {
|
|
|
|
ch = make(chan struct{})
|
|
|
|
d.changeNotify[peer] = ch
|
|
|
|
}
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateLocalPeerState updates local peer status
|
|
|
|
func (d *Status) UpdateLocalPeerState(localPeerState LocalPeerState) {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
d.localPeer = localPeerState
|
2023-03-24 18:51:35 +01:00
|
|
|
d.notifyAddressChanged()
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// CleanLocalPeerState cleans local peer status
|
|
|
|
func (d *Status) CleanLocalPeerState() {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
d.localPeer = LocalPeerState{}
|
2023-03-24 18:51:35 +01:00
|
|
|
d.notifyAddressChanged()
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarkManagementDisconnected sets ManagementState to disconnected
|
2023-03-16 17:22:36 +01:00
|
|
|
func (d *Status) MarkManagementDisconnected() {
|
2023-03-03 19:49:18 +01:00
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
2023-03-17 10:37:27 +01:00
|
|
|
defer d.onConnectionChanged()
|
|
|
|
|
2023-03-16 17:22:36 +01:00
|
|
|
d.managementState = false
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarkManagementConnected sets ManagementState to connected
|
2023-03-16 17:22:36 +01:00
|
|
|
func (d *Status) MarkManagementConnected() {
|
2023-03-03 19:49:18 +01:00
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
2023-03-17 10:37:27 +01:00
|
|
|
defer d.onConnectionChanged()
|
|
|
|
|
2023-03-24 18:51:35 +01:00
|
|
|
d.managementState = true
|
2023-03-16 17:22:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateSignalAddress update the address of the signal server
|
|
|
|
func (d *Status) UpdateSignalAddress(signalURL string) {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
d.signalAddress = signalURL
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateManagementAddress update the address of the management server
|
|
|
|
func (d *Status) UpdateManagementAddress(mgmAddress string) {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
d.mgmAddress = mgmAddress
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarkSignalDisconnected sets SignalState to disconnected
|
2023-03-16 17:22:36 +01:00
|
|
|
func (d *Status) MarkSignalDisconnected() {
|
2023-03-03 19:49:18 +01:00
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
2023-03-17 10:37:27 +01:00
|
|
|
defer d.onConnectionChanged()
|
|
|
|
|
2023-03-24 18:51:35 +01:00
|
|
|
d.signalState = false
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarkSignalConnected sets SignalState to connected
|
2023-03-16 17:22:36 +01:00
|
|
|
func (d *Status) MarkSignalConnected() {
|
2023-03-03 19:49:18 +01:00
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
2023-03-17 10:37:27 +01:00
|
|
|
defer d.onConnectionChanged()
|
|
|
|
|
2023-03-16 17:22:36 +01:00
|
|
|
d.signalState = true
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetFullStatus gets full status
|
|
|
|
func (d *Status) GetFullStatus() FullStatus {
|
|
|
|
d.mux.Lock()
|
|
|
|
defer d.mux.Unlock()
|
|
|
|
|
|
|
|
fullStatus := FullStatus{
|
2023-03-16 17:22:36 +01:00
|
|
|
ManagementState: ManagementState{
|
|
|
|
d.mgmAddress,
|
|
|
|
d.managementState,
|
|
|
|
},
|
|
|
|
SignalState: SignalState{
|
|
|
|
d.signalAddress,
|
|
|
|
d.signalState,
|
|
|
|
},
|
|
|
|
LocalPeerState: d.localPeer,
|
2023-03-03 19:49:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, status := range d.peers {
|
|
|
|
fullStatus.Peers = append(fullStatus.Peers, status)
|
|
|
|
}
|
|
|
|
|
2023-03-07 10:17:25 +01:00
|
|
|
fullStatus.Peers = append(fullStatus.Peers, d.offlinePeers...)
|
|
|
|
|
2023-03-03 19:49:18 +01:00
|
|
|
return fullStatus
|
|
|
|
}
|
2023-03-17 10:37:27 +01:00
|
|
|
|
|
|
|
// ClientStart will notify all listeners about the new service state
|
|
|
|
func (d *Status) ClientStart() {
|
|
|
|
d.notifier.clientStart()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClientStop will notify all listeners about the new service state
|
|
|
|
func (d *Status) ClientStop() {
|
|
|
|
d.notifier.clientStop()
|
|
|
|
}
|
|
|
|
|
2023-03-29 10:39:54 +02:00
|
|
|
// ClientTeardown will notify all listeners about the service is under teardown
|
|
|
|
func (d *Status) ClientTeardown() {
|
|
|
|
d.notifier.clientTearDown()
|
|
|
|
}
|
|
|
|
|
2023-04-03 16:59:13 +02:00
|
|
|
// SetConnectionListener set a listener to the notifier
|
|
|
|
func (d *Status) SetConnectionListener(listener Listener) {
|
|
|
|
d.notifier.setListener(listener)
|
2023-03-17 10:37:27 +01:00
|
|
|
}
|
|
|
|
|
2023-04-03 16:59:13 +02:00
|
|
|
// RemoveConnectionListener remove the listener from the notifier
|
|
|
|
func (d *Status) RemoveConnectionListener() {
|
|
|
|
d.notifier.removeListener()
|
2023-03-17 10:37:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Status) onConnectionChanged() {
|
|
|
|
d.notifier.updateServerStates(d.managementState, d.signalState)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Status) notifyPeerListChanged() {
|
2023-08-04 14:14:08 +02:00
|
|
|
d.notifier.peerListChanged(d.numOfPeers())
|
2023-03-17 10:37:27 +01:00
|
|
|
}
|
2023-03-24 18:51:35 +01:00
|
|
|
|
|
|
|
func (d *Status) notifyAddressChanged() {
|
|
|
|
d.notifier.localAddressChanged(d.localPeer.FQDN, d.localPeer.IP)
|
|
|
|
}
|
2023-08-04 14:14:08 +02:00
|
|
|
|
|
|
|
func (d *Status) numOfPeers() int {
|
|
|
|
return len(d.peers) + len(d.offlinePeers)
|
|
|
|
}
|