diff --git a/FneBase.cs b/FneBase.cs index 75e3543..3007437 100644 --- a/FneBase.cs +++ b/FneBase.cs @@ -522,26 +522,6 @@ namespace fnecore } } // public class PeerConnectedEvent : EventArgs - /// - /// Type of FNE instance. - /// - public enum FneType : byte - { - /// - /// Master - /// - MASTER, - /// - /// Peer - /// - PEER, - /// - /// Unknown (should never happen) - /// - UNKNOWN = 0xFF - } // public enum FneType : byte - - /// /// This class implements some base functionality for all other FNE network classes. /// @@ -553,7 +533,6 @@ namespace fnecore protected static Random rand = null; protected bool isStarted = false; - protected FneType fneType; /* ** Properties @@ -574,11 +553,6 @@ namespace fnecore /// public bool IsStarted => isStarted; - /// - /// Gets the this is. - /// - public FneType FneType => fneType; - /// /// Gets/sets the interval that peers will need to ping the master. /// @@ -691,8 +665,6 @@ namespace fnecore this.systemName = systemName; this.peerId = peerId; - this.fneType = FneType.UNKNOWN; - // set a default "noop" logger Logger = (LogLevel level, string message) => { }; } diff --git a/FneMaster.cs b/FneMaster.cs deleted file mode 100644 index 20a95cd..0000000 --- a/FneMaster.cs +++ /dev/null @@ -1,1234 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -/** -* Digital Voice Modem - Fixed Network Equipment Core Library -* AGPLv3 Open Source. Use is subject to license terms. -* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -* -* @package DVM / Fixed Network Equipment Core Library -* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0) -* -* Copyright (C) 2022-2024 Bryan Biedenkapp, N2PLL -* -*/ - -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using System.Text; -using System.Text.Json; // thanks .NET Core... - -using fnecore.DMR; -using fnecore.P25; -using fnecore.NXDN; -using fnecore.EDAC; - -namespace fnecore -{ - /// - /// Event used to process incoming grant request data. - /// - public class GrantRequestEvent : EventArgs - { - /// - /// Peer ID - /// - public uint PeerId { get; } - /// - /// DVM Mode - /// - public DVMState Mode { get; } - /// - /// Source Address - /// - public uint SrcId { get; } - /// - /// Destination Address - /// - public uint DstId { get; } - /// - /// Slot Number - /// - public byte Slot { get; } - /// - /// Flag indicating a unit-to-unit (private call) request. - /// - public bool UnitToUnit { get; } - - /* - ** Methods - */ - /// - /// Initializes a new instance of the class. - /// - private GrantRequestEvent() - { - /* stub */ - } - - /// - /// Initializes a new instance of the class. - /// - /// Peer ID - /// DVM Mode State - /// Source Address - /// Destination Address - /// Slot Number - /// Unit-to-Unit (Private Call) - public GrantRequestEvent(uint peerId, DVMState mode, uint srcId, uint dstId, byte slot, bool unitToUnit) : base() - { - this.PeerId = peerId; - this.Mode = mode; - this.SrcId = srcId; - this.DstId = dstId; - this.Slot = slot; - this.UnitToUnit = unitToUnit; - } - } // public class GrantRequestEvent : EventArgs - - /// - /// Implements an FNE "master". - /// - public class FneMaster : FneBase - { - private UdpListener server = null; - - private bool abortListening = false; - - private CancellationTokenSource listenCancelToken = new CancellationTokenSource(); - private Task listenTask = null; - private CancellationTokenSource maintainenceCancelToken = new CancellationTokenSource(); - private Task maintainenceTask = null; - - private Dictionary peers; - - /* - ** Properties - */ - - /// - /// Gets the for this . - /// - public IPEndPoint EndPoint - { - get - { - if (server != null) - return server.EndPoint; - return null; - } - } - - /// - /// Dictionary of connected peers. - /// - public Dictionary Peers => peers; - - /// - /// Gets/sets the password used for connecting to this master. - /// - public string Passphrase - { - get; - set; - } - - /// - /// Flag indicating whether we are repeating to all connected peers. - /// - public bool Repeat - { - get; - set; - } - - /// - /// Flag indicating whether we are repeating DMR to all connected peers. - /// - public bool NoRepeatDMR - { - get; - set; - } - - /// - /// Flag indicating whether we are repeating P25 to all connected peers. - /// - public bool NoRepeatP25 - { - get; - set; - } - - /// - /// Flag indicating whether we are repeating NXDN to all connected peers. - /// - public bool NoRepeatNXDN - { - get; - set; - } - - /// - /// Gets/sets how many pings are missed before we give up and force a reregister. - /// - public int MaxMissed - { - get; - set; - } - - /// - /// Flag indicating whether peer activity transfers are allowed. - /// - public bool AllowActivityTransfer - { - get; - set; - } - - /// - /// Flag indicating whether peer diagnostic transfers are allowed. - /// - public bool AllowDiagnosticTransfer - { - get; - set; - } - - /* - ** Events/Callbacks - */ - - /// - /// Event action that handles processing a grant request. - /// - public event EventHandler GrantRequestReceived; - - /// - /// Event action that handles peer activity transfers. - /// - public Action ActivityTransfer = null; - - /// - /// Event action that handles peer diagnostic transfers. - /// - public Action DiagnosticTransfer = null; - - /// - /// Event action that handles peer unit registration announcements. - /// - public Action UnitRegistration = null; - - /// - /// Event action that handles peer unit deregistration announcements - /// - public Action UnitDeregistration = null; - - /// - /// Event action that handles peer group affiliation announcements - /// - public Action UnitGroupAffiliation = null; - - /* - ** Methods - */ - - /// - /// Initializes a new instance of the class. - /// - /// - /// - /// - public FneMaster(string systemName, uint peerId, int port) : this(systemName, peerId, new IPEndPoint(IPAddress.Any, port)) - { - /* stub */ - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// - /// - public FneMaster(string systemName, uint peerId, IPEndPoint endpoint) : base(systemName, peerId) - { - fneType = FneType.MASTER; - - server = new UdpListener(endpoint); - peers = new Dictionary(); - - Passphrase = string.Empty; - Repeat = true; - NoRepeatDMR = false; - NoRepeatP25 = false; - NoRepeatNXDN = false; - AllowActivityTransfer = false; - AllowDiagnosticTransfer = false; - } - - /// - /// Starts the main execution loop for this . - /// - public override void Start() - { - if (isStarted) - throw new InvalidOperationException("Cannot start listening when already started."); - - Logger(LogLevel.INFO, $"({systemName}) starting network services, {server.EndPoint}"); - - abortListening = false; - listenTask = Task.Factory.StartNew(Listen, listenCancelToken.Token); - maintainenceTask = Task.Factory.StartNew(Maintainence, maintainenceCancelToken.Token); - - isStarted = true; - } - - /// - /// Stops the main execution loop for this . - /// - public override void Stop() - { - if (!isStarted) - throw new InvalidOperationException("Cannot stop listening when not started."); - - Logger(LogLevel.INFO, $"({systemName}) stopping network services, {server.EndPoint}"); - - // stop UDP listen task - if (listenTask != null) - { - abortListening = true; - listenCancelToken.Cancel(); - - try - { - listenTask.GetAwaiter().GetResult(); - } - catch (OperationCanceledException) { /* stub */ } - finally - { - listenCancelToken.Dispose(); - } - } - - // stop maintainence task - if (maintainenceTask != null) - { - maintainenceCancelToken.Cancel(); - - try - { - maintainenceTask.GetAwaiter().GetResult(); - } - catch (OperationCanceledException) { /* stub */ } - finally - { - maintainenceCancelToken.Dispose(); - } - } - - isStarted = false; - } - - /// - /// Helper to find and get for the given peer ID. - /// - /// Peer ID - /// if connected, otherwise null. - public PeerInformation FindPeer(uint peerId) - { - if (peers.ContainsKey(peerId)) - return peers[peerId]; - return null; - } - - /// - /// Helper to send a raw UDP frame. - /// - /// UDP frame to send - public void Send(UdpFrame frame) - { - if (RawPacketTrace) - Log(LogLevel.DEBUG, $"({systemName}) Network Sent (to {frame.Endpoint}) -- {FneUtils.HexDump(frame.Message, 0)}"); - - server.Send(frame); - } - - /// - /// Helper to send a raw UDP frame. - /// - /// UDP frame to send - public async void SendAsync(UdpFrame frame) - { - if (RawPacketTrace) - Log(LogLevel.DEBUG, $"({systemName}) Network Sent (to {frame.Endpoint}) -- {FneUtils.HexDump(frame.Message, 0)}"); - - await server.SendAsync(frame); - } - - /// - /// Helper to send a raw message to the specified peer. - /// - /// - /// Byte array containing message to send - public void SendPeer(IPEndPoint endpoint, byte[] message) - { - Send(new UdpFrame() - { - Endpoint = endpoint, - Message = message - }); - } - - /// - /// Helper to send a data message to the specified peer. - /// - /// Peer ID - /// Opcode - /// Byte array containing message to send - /// - /// - public void SendPeer(uint peerId, Tuple opcode, byte[] message, ushort pktSeq, uint streamId = 0) - { - if (peers.ContainsKey(peerId)) - { - if (streamId == 0) - streamId = peers[peerId].StreamID; - byte[] data = WriteFrame(message, peerId, this.peerId, opcode, pktSeq, streamId); - SendPeer(peers[peerId].EndPoint, data); - } - } - - /// - /// Helper to send a data message to the specified peer. - /// - /// Peer ID - /// Opcode - /// Byte array containing message to send - /// - public void SendPeer(uint peerId, Tuple opcode, byte[] message, bool incPktSeq = false) - { - if (peers.ContainsKey(peerId)) - { - if (incPktSeq) { - peers[peerId].PacketSequence = ++peers[peerId].PacketSequence; - } - - SendPeer(peerId, opcode, message, peers[peerId].PacketSequence); - } - } - - /// - /// Helper to send a command message to the specified peer. - /// - /// - /// Peer ID - /// Opcode - /// - /// - /// Byte array containing message to send - public void SendPeerCommand(IPEndPoint endpoint, uint peerId, Tuple opcode, ushort pktSeq, uint streamId, byte[] message = null) - { - int messageLength = 0; - if (message != null) - messageLength = message.Length; - - byte[] buffer = new byte[messageLength + 6]; - if (message != null) - Buffer.BlockCopy(message, 0, buffer, 6, message.Length); - - byte[] frame = WriteFrame(buffer, peerId, this.peerId, opcode, pktSeq, streamId); - SendPeer(endpoint, frame); - } - - /// - /// Helper to send a command message to the specified peer. - /// - /// - /// Peer ID - /// Opcode - /// Byte array containing message to send - /// - public void SendPeerCommand(uint peerId, Tuple opcode, byte[] message = null, bool incPktSeq = false) - { - int messageLength = 0; - if (message != null) - messageLength = message.Length; - - byte[] buffer = new byte[messageLength + 6]; - if (message != null) - Buffer.BlockCopy(message, 0, buffer, 6, message.Length); - - SendPeer(peerId, opcode, buffer, incPktSeq); - } - - /// - /// Helper to send a ACK response to the specified peer. - /// - /// Peer ID - public void SendPeerACK(uint peerId) - { - if (peers.ContainsKey(peerId)) - { - peers[peerId].PacketSequence = ++peers[peerId].PacketSequence; - - // send ping response to peer - SendPeerCommand(peerId, CreateOpcode(Constants.NET_FUNC_ACK)); - } - } - - /// - /// Helper to send a NAK response to the specified peer. - /// - /// Peer ID - /// Tag NAK'ed - public void SendPeerNAK(uint peerId, string tag) - { - if (peers.ContainsKey(peerId)) - { - peers[peerId].PacketSequence = ++peers[peerId].PacketSequence; - - // send ping response to peer - SendPeerCommand(peerId, CreateOpcode(Constants.NET_FUNC_NAK)); - Log(LogLevel.WARNING, $"({systemName}) {tag} from unauth PEER {peerId}"); - } - } - - /// - /// Helper to send a NAK response to the specified endpoint. - /// - /// IP endpoint - /// Tag NAK'ed - public void SendNAK(IPEndPoint endpoint, uint peerId, string tag) - { - byte[] buffer = new byte[10]; - FneUtils.WriteBytes(peerId, ref buffer, 6); - - Send(new UdpFrame() - { - Endpoint = endpoint, - Message = WriteFrame(buffer, peerId, this.peerId, CreateOpcode(Constants.NET_FUNC_NAK), 0, CreateStreamID()) - }); - Log(LogLevel.WARNING, $"({systemName}) {tag} from unconnected PEER {endpoint.Address.ToString()}:{endpoint.Port}"); - } - - /// - /// Helper to send a raw message to the connected peers. - /// - /// Opcode - /// Byte array containing message to send - /// RTP Packet Sequence - public void SendPeers(Tuple opcode, byte[] message, uint pktSeq = uint.MaxValue) - { - foreach (PeerInformation peer in peers.Values) - { - if (pktSeq > ushort.MaxValue) - pktSeq = peer.PacketSequence; - - byte[] data = WriteFrame(message, peer.PeerID, this.peerId, opcode, (ushort)pktSeq, peer.StreamID); - SendAsync(new UdpFrame() - { - Endpoint = peer.EndPoint, - Message = data - }); - } - } - - /// - /// Internal helper to compare authorization hashes. - /// - /// FNE message frame - /// Peer Information - private bool CompareAuthHash(byte[] message, PeerInformation info) - { - // get the hash in the frame message - byte[] hash = new byte[message.Length - 8]; - Buffer.BlockCopy(message, 8, hash, 0, hash.Length); - - // calculate our own hash - byte[] inBuf = new byte[4 + Passphrase.Length]; - FneUtils.WriteBytes(info.Salt, ref inBuf, 0); - FneUtils.StringToBytes(Passphrase, inBuf, 4, Passphrase.Length); - byte[] outHash = FneUtils.sha256_hash(inBuf); - - // compare hashes - if (hash.Length == outHash.Length) - { - bool res = true; - for (int i = 0; i < hash.Length; i++) - { - if (hash[i] != outHash[i]) - { - res = false; - break; - } - } - - return res; - } - - return false; - } - - /// - /// - /// - private async void Listen() - { - CancellationToken ct = listenCancelToken.Token; - ct.ThrowIfCancellationRequested(); - - while (!abortListening) - { - try - { - UdpFrame frame = await server.Receive(); - if (RawPacketTrace) - Log(LogLevel.DEBUG, $"({systemName}) Network Received (from {frame.Endpoint}) -- {FneUtils.HexDump(frame.Message, 0)}"); - - // decode RTP frame - if (frame.Message.Length <= 0) - continue; - - RtpHeader rtpHeader; - RtpFNEHeader fneHeader; - int messageLength = 0; - byte[] message = ReadFrame(frame, out messageLength, out rtpHeader, out fneHeader); - if (message == null) - { - Log(LogLevel.ERROR, $"({systemName}) Malformed packet (from {frame.Endpoint}); failed to decode RTP frame"); - continue; - } - - if (message.Length < 1) - { - Log(LogLevel.WARNING, $"({systemName}) Malformed packet (from {frame.Endpoint}) -- {FneUtils.HexDump(message, 0)}"); - continue; - } - - uint peerId = fneHeader.PeerID; - uint streamId = fneHeader.StreamID; - - // update current peer stream ID - if (peerId > 0 && peers.ContainsKey(peerId) && streamId != 0) - { - ushort pktSeq = rtpHeader.Sequence; - - if ((peers[peerId].StreamID == streamId) && (pktSeq != peers[peerId].NextPacketSequence)) - Log(LogLevel.WARNING, $"({systemName}) PEER {peerId} Stream {streamId} out-of-sequence; {pktSeq} != {peers[peerId].NextPacketSequence}"); - - peers[peerId].StreamID = streamId; - peers[peerId].PacketSequence = pktSeq; - peers[peerId].NextPacketSequence = (ushort)(pktSeq + 1); - if (peers[peerId].NextPacketSequence > ushort.MaxValue) - peers[peerId].NextPacketSequence = 0; - } - - // if we don't have a stream ID and are receiving call data -- throw an error and discard - if (streamId == 0 && fneHeader.Function == Constants.NET_FUNC_PROTOCOL) - { - Log(LogLevel.ERROR, $"({systemName}) PEER {peerId} Malformed packet (no stream ID for call?)"); - continue; - } - - // process incoming message frame opcodes - switch (fneHeader.Function) - { - case Constants.NET_FUNC_PROTOCOL: - { - if (fneHeader.SubFunction == Constants.NET_PROTOCOL_SUBFUNC_DMR) // Encapsulated DMR data frame - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - byte seqNo = message[4]; - uint srcId = FneUtils.Bytes3ToUInt32(message, 5); - uint dstId = FneUtils.Bytes3ToUInt32(message, 8); - - byte bits = message[15]; - byte slot = (byte)(((bits & 0x80) == 0x80) ? 1 : 0); - CallType callType = ((bits & 0x40) == 0x40) ? CallType.PRIVATE : CallType.GROUP; - FrameType frameType = (FrameType)((bits & 0x30) >> 4); - - DMRDataType dataType = DMRDataType.IDLE; - if ((bits & 0x20) == 0x20) - dataType = (DMRDataType)(bits & ~(0x20)); - - byte n = (byte)(bits & 0xF); - - // is the stream valid? - bool ret = true; - if (DMRDataValidate != null) - ret = DMRDataValidate(peerId, srcId, dstId, slot, callType, frameType, dataType, streamId, message); - - if (ret) - { - // is this the peer being ignored? - if (PeerIgnored != null) - ret = PeerIgnored(peerId, srcId, dstId, slot, callType, frameType, dataType, streamId); - else - ret = false; - - if (ret) - continue; -#if DEBUG - Log(LogLevel.DEBUG, $"{systemName} DMRD: SRC_PEER {peerId} SRC_ID {srcId} DST_ID {dstId} TS {slot} [STREAM ID {streamId}] PKT SEQ {rtpHeader.Sequence}"); -#endif - // are we repeating to connected peers? - if (Repeat && !NoRepeatDMR) - { - foreach (KeyValuePair kvp in peers) - { - // don't repeat to the peer sending the data... - if (kvp.Key != peerId) - { - // is this peer being ignored - if (PeerIgnored != null) - ret = PeerIgnored(peerId, srcId, dstId, slot, callType, frameType, dataType, streamId); - else - ret = false; - - if (!ret) - SendPeer(kvp.Key, CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_DMR), message, rtpHeader.Sequence); - } - } - } - - // perform any userland actions with the data - FireDMRDataReceived(new DMRDataReceivedEvent(peerId, srcId, dstId, slot, callType, frameType, dataType, n, rtpHeader.Sequence, streamId, message)); - } - } - else - SendPeerNAK(peerId, Constants.TAG_DMR_DATA); - } - } - else if (fneHeader.SubFunction == Constants.NET_PROTOCOL_SUBFUNC_P25) // Encapsulated P25 data frame - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - uint srcId = FneUtils.Bytes3ToUInt32(message, 5); - uint dstId = FneUtils.Bytes3ToUInt32(message, 8); - CallType callType = (message[4] == P25Defines.LC_PRIVATE) ? CallType.PRIVATE : CallType.GROUP; - P25DUID duid = (P25DUID)message[22]; - FrameType frameType = ((duid != P25DUID.TDU) && (duid != P25DUID.TDULC)) ? FrameType.VOICE : FrameType.TERMINATOR; - - // is the stream valid? - bool ret = true; - if (P25DataValidate != null) - ret = P25DataValidate(peerId, srcId, dstId, callType, duid, frameType, streamId, message); - - if (ret) - { - // pre-process P25 data... - FireP25DataPreprocess(new P25DataReceivedEvent(peerId, srcId, dstId, callType, duid, frameType, rtpHeader.Sequence, streamId, message)); - - // is this the peer being ignored? - if (PeerIgnored != null) - ret = PeerIgnored(peerId, srcId, dstId, 0, callType, frameType, (frameType == FrameType.VOICE) ? DMRDataType.VOICE_LC_HEADER : DMRDataType.TERMINATOR_WITH_LC, streamId); - else - ret = false; - - if (ret) - continue; -#if DEBUG - Log(LogLevel.DEBUG, $"{systemName} P25D: SRC_PEER {peerId} SRC_ID {srcId} DST_ID {dstId} [STREAM ID {streamId}] PKT SEQ {rtpHeader.Sequence}"); -#endif - // are we repeating to connected peers? - if (Repeat && !NoRepeatP25) - { - foreach (KeyValuePair kvp in peers) - { - // don't repeat to the peer sending the data... - if (kvp.Key != peerId) - { - // is this peer being ignored - if (PeerIgnored != null) - ret = PeerIgnored(peerId, srcId, dstId, 0, callType, frameType, (frameType == FrameType.VOICE) ? DMRDataType.VOICE_LC_HEADER : DMRDataType.TERMINATOR_WITH_LC, streamId); - else - ret = false; - - if (!ret) - SendPeer(kvp.Key, CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), message, rtpHeader.Sequence); - } - } - } - - // perform any userland actions with the data - FireP25DataReceived(new P25DataReceivedEvent(peerId, srcId, dstId, callType, duid, frameType, rtpHeader.Sequence, streamId, message)); - } - } - else - SendPeerNAK(peerId, Constants.TAG_P25_DATA); - } - } - else if (fneHeader.SubFunction == Constants.NET_PROTOCOL_SUBFUNC_NXDN) // Encapsulated NXDN data frame - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - NXDNMessageType messageType = (NXDNMessageType)message[4]; - uint srcId = FneUtils.Bytes3ToUInt32(message, 5); - uint dstId = FneUtils.Bytes3ToUInt32(message, 8); - - byte bits = message[15]; - CallType callType = ((bits & 0x40) == 0x40) ? CallType.PRIVATE : CallType.GROUP; - FrameType frameType = (messageType != NXDNMessageType.MESSAGE_TYPE_TX_REL) ? FrameType.VOICE : FrameType.TERMINATOR; - - // is the stream valid? - bool ret = true; - if (NXDNDataValidate != null) - ret = NXDNDataValidate(peerId, srcId, dstId, callType, messageType, frameType, streamId, message); - - if (ret) - { - // is this the peer being ignored? - if (PeerIgnored != null) - ret = PeerIgnored(peerId, srcId, dstId, 0, callType, frameType, (frameType == FrameType.VOICE) ? DMRDataType.VOICE_LC_HEADER : DMRDataType.TERMINATOR_WITH_LC, streamId); - else - ret = false; - - if (ret) - continue; -#if DEBUG - Log(LogLevel.DEBUG, $"{systemName} NXDD: SRC_PEER {peerId} SRC_ID {srcId} DST_ID {dstId} [STREAM ID {streamId}] PKT SEQ {rtpHeader.Sequence}"); -#endif - // are we repeating to connected peers? - if (Repeat && !NoRepeatNXDN) - { - foreach (KeyValuePair kvp in peers) - { - // don't repeat to the peer sending the data... - if (kvp.Key != peerId) - { - // is this peer being ignored - if (PeerIgnored != null) - ret = PeerIgnored(peerId, srcId, dstId, 0, callType, frameType, (frameType == FrameType.VOICE) ? DMRDataType.VOICE_LC_HEADER : DMRDataType.TERMINATOR_WITH_LC, streamId); - else - ret = false; - - if (!ret) - SendPeer(kvp.Key, CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_NXDN), message, rtpHeader.Sequence); - } - } - } - - // perform any userland actions with the data - FireNXDNDataReceived(new NXDNDataReceivedEvent(peerId, srcId, dstId, callType, messageType, frameType, rtpHeader.Sequence, streamId, message)); - } - } - else - SendPeerNAK(peerId, Constants.TAG_NXDN_DATA); - } - } - else - Log(LogLevel.ERROR, $"({systemName}) Unknown protocol opcode {FneUtils.BytesToString(message, 0, 4)} -- {FneUtils.HexDump(message, 0)}"); - } - break; - - case Constants.NET_FUNC_RPTL: // Repeater Login - { - if (peerId > 0 && !peers.ContainsKey(peerId)) - { - PeerInformation info = new PeerInformation(); - info.PeerID = peerId; - info.EndPoint = frame.Endpoint; - info.PacketSequence = rtpHeader.Sequence; - info.NextPacketSequence = ++rtpHeader.Sequence; - info.StreamID = streamId; - - info.Salt = (uint)rand.Next(-2147483648, 2147483647); - - Log(LogLevel.INFO, $"({systemName}) Repeater logging in with PEER {peerId}, {info.EndPoint}"); - - byte[] buffer = new byte[4]; - FneUtils.WriteBytes(info.Salt, ref buffer, 0); - - SendPeerCommand(frame.Endpoint, peerId, CreateOpcode(Constants.NET_FUNC_ACK), ++info.PacketSequence, streamId, buffer); - - info.State = ConnectionState.WAITING_AUTHORISATION; - peers.Add(peerId, info); - - Log(LogLevel.INFO, $"({systemName}) Challenge Response sent to PEER {peerId} for login {info.Salt}"); - } - else - { - SendNAK(frame.Endpoint, peerId, Constants.TAG_REPEATER_LOGIN); - - // check if the peer is in our peer list -- if he is, and he isn't in a running state, reset - // the login sequence - if (peerId > 0 && !peers.ContainsKey(peerId)) - { - PeerInformation info = new PeerInformation(); - if (info.State != ConnectionState.RUNNING) - if (peers.ContainsKey(peerId)) - peers.Remove(peerId); - } - } - } - break; - case Constants.NET_FUNC_RPTK: // Repeater Authentication - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - PeerInformation info = peers[peerId]; - info.LastPing = DateTime.Now; - - if (info.State == ConnectionState.WAITING_AUTHORISATION) - { - if (CompareAuthHash(message, info)) - { - info.State = ConnectionState.WAITING_CONFIG; - - SendPeerACK(peerId); - peers[peerId] = info; - Log(LogLevel.INFO, $"({systemName}) PEER {peerId} has completed the login exchange"); - } - else - { - Log(LogLevel.WARNING, $"({systemName}) PEER {peerId} has failed the login exchange"); - SendPeerNAK(peerId, Constants.TAG_REPEATER_AUTH); - if (peers.ContainsKey(peerId)) - peers.Remove(peerId); - } - } - else - { - Log(LogLevel.WARNING, $"({systemName}) PEER {peerId} tried login exchange in wrong state"); - SendPeerNAK(peerId, Constants.TAG_REPEATER_AUTH); - if (peers.ContainsKey(peerId)) - peers.Remove(peerId); - } - } - else - SendNAK(frame.Endpoint, peerId, Constants.TAG_REPEATER_AUTH); - } - break; - case Constants.NET_FUNC_RPTC: // Repeater Configuration - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - PeerInformation info = peers[peerId]; - info.LastPing = DateTime.Now; - - if (info.State == ConnectionState.WAITING_CONFIG) - { - string payload = FneUtils.BytesToString(message, 8, message.Length - 8); - try - { - JsonDocument json = JsonDocument.Parse(payload); - - // identity - info.Details.Identity = json.RootElement.GetProperty("identity").GetString(); - info.Details.RxFrequency = json.RootElement.GetProperty("rxFrequency").GetUInt32(); - info.Details.TxFrequency = json.RootElement.GetProperty("txFrequency").GetUInt32(); - - // system info - JsonElement sysInfo = json.RootElement.GetProperty("info"); - info.Details.Latitude = sysInfo.GetProperty("latitude").GetDouble(); - info.Details.Longitude = sysInfo.GetProperty("longitude").GetDouble(); - info.Details.Height = sysInfo.GetProperty("height").GetInt32(); - info.Details.Location = sysInfo.GetProperty("location").GetString(); - - // channel data - JsonElement channel = json.RootElement.GetProperty("channel"); - info.Details.TxPower = channel.GetProperty("txPower").GetUInt32(); - info.Details.TxOffsetMhz = (float)channel.GetProperty("txOffsetMhz").GetDouble(); - info.Details.ChBandwidthKhz = (float)channel.GetProperty("chBandwidthKhz").GetDouble(); - info.Details.ChannelID = channel.GetProperty("channelId").GetByte(); - info.Details.ChannelNo = channel.GetProperty("channelNo").GetUInt32(); - - // RCON - JsonElement rcon = json.RootElement.GetProperty("rcon"); - info.Details.Password = rcon.GetProperty("password").GetString(); - info.Details.Port = rcon.GetProperty("port").GetInt32(); - - info.Details.Software = json.RootElement.GetProperty("software").GetString(); - } - catch - { - const string outOfDate = "Old/Out of Date Peer"; - - // identity - info.Details.Identity = outOfDate; - info.Details.RxFrequency = 0; - info.Details.TxFrequency = 0; - - // system info - info.Details.Latitude = 0.0d; - info.Details.Longitude = 0.0d; - info.Details.Height = 0; - info.Details.Location = outOfDate; - - // channel data - info.Details.TxOffsetMhz = 0.0f; - info.Details.ChBandwidthKhz = 0.0f; - info.Details.ChannelID = 0; - info.Details.ChannelNo = 0; - info.Details.TxPower = 0; - - // RCON - info.Details.Password = "ABCD1234"; - info.Details.Port = 9990; // default port - - info.Details.Software = "Peer Software Did Not Send JSON Configuration"; - } - - info.State = ConnectionState.RUNNING; - info.Connection = true; - info.PingsReceived = 0; - info.LastPing = DateTime.Now; - - SendPeerACK(peerId); - Log(LogLevel.INFO, $"({systemName}) PEER {peerId} has completed the configuration exchange"); - peers[peerId] = info; - - // userland actions - FirePeerConnected(new PeerConnectedEvent(peerId, info)); - } - else - { - Log(LogLevel.WARNING, $"({systemName}) PEER {peerId} tried configuration exchange in wrong state"); - SendPeerNAK(peerId, Constants.TAG_REPEATER_CONFIG); - if (peers.ContainsKey(peerId)) - peers.Remove(peerId); - } - } - else - SendNAK(frame.Endpoint, peerId, Constants.TAG_REPEATER_CONFIG); - } - break; - - case Constants.NET_FUNC_RPT_CLOSING: // Repeater Closing (Disconnect) - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - Log(LogLevel.INFO, $"({systemName}) PEER {peerId} is closing down"); - - SendPeerACK(peerId); - peers.Remove(peerId); - - // userland actions - if (PeerDisconnected != null) - PeerDisconnected(peerId); - } - } - } - break; - case Constants.NET_FUNC_PING: // Repeater Ping - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - PeerInformation peer = peers[peerId]; - peer.PingsReceived++; - peer.LastPing = DateTime.Now; - //peer.PacketSequence = ++peer.PacketSequence; - - peers[peerId] = peer; - - // send ping response to peer - SendPeerCommand(peerId, CreateOpcode(Constants.NET_FUNC_PONG), null, true); - Log(LogLevel.DEBUG, $"({systemName}) Received and answered RPTPING from PEER {peerId}"); - } - else - SendPeerNAK(peerId, Constants.TAG_REPEATER_PING); - } - } - break; - - case Constants.NET_FUNC_GRANT: // Repeater Grant Request - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - uint srcId = FneUtils.ToUInt32(message, 11); - uint dstId = FneUtils.ToUInt32(message, 15); - byte slot = (byte)(message[19] & 0x07); - bool unitToUnit = (bool)((message[19] & 0x80) == 0x80); - DVMState mode = (DVMState)message[20]; - - if (GrantRequestReceived != null) - GrantRequestReceived(this, new GrantRequestEvent(peerId, mode, srcId, dstId, slot, unitToUnit)); - } - else - SendPeerNAK(peerId, Constants.TAG_REPEATER_GRANT); - } - } - break; - - case Constants.NET_FUNC_TRANSFER: - { - if (fneHeader.SubFunction == Constants.NET_TRANSFER_SUBFUNC_ACTIVITY) // Peer Activity Log Transfer - { - // can we do activity transfers? - if (AllowActivityTransfer) - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - byte[] buffer = new byte[messageLength - 11]; - Buffer.BlockCopy(message, 11, buffer, 0, buffer.Length); - - string msg = Encoding.ASCII.GetString(buffer); - if (ActivityTransfer != null) - ActivityTransfer(peerId, msg); - } - } - } - } - else if (fneHeader.SubFunction == Constants.NET_TRANSFER_SUBFUNC_DIAG) // Peer Diagnostic Log Transfer - { - // can we do diagnostic transfers? - if (AllowDiagnosticTransfer) - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - byte[] buffer = new byte[messageLength - 11]; - Buffer.BlockCopy(message, 11, buffer, 0, buffer.Length); - - string msg = Encoding.ASCII.GetString(buffer); - if (DiagnosticTransfer != null) - DiagnosticTransfer(peerId, msg); - } - } - } - } - else - Log(LogLevel.ERROR, $"({systemName}) Unknown transfer opcode {FneUtils.BytesToString(message, 0, 4)} -- {FneUtils.HexDump(message, 0)}"); - } - break; - - case Constants.NET_FUNC_ANNOUNCE: - { - if (fneHeader.SubFunction == Constants.NET_ANNC_SUBFUNC_GRP_AFFIL) // Announce Group Affiliation - { - // can we do activity transfers? - if (AllowActivityTransfer) - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - uint srcId = FneUtils.Bytes3ToUInt32(message, 0); - uint dstId = FneUtils.Bytes3ToUInt32(message, 3); - if (UnitGroupAffiliation != null) - UnitGroupAffiliation(peerId, srcId, dstId); - } - } - } - } - else if (fneHeader.SubFunction == Constants.NET_ANNC_SUBFUNC_UNIT_REG) // Announce Unit Registration - { - // can we do activity transfers? - if (AllowActivityTransfer) - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - uint srcId = FneUtils.Bytes3ToUInt32(message, 0); - if (UnitRegistration != null) - UnitRegistration(peerId, srcId); - } - } - } - } - else if (fneHeader.SubFunction == Constants.NET_ANNC_SUBFUNC_UNIT_DEREG) // Announce Unit Deregistration - { - // can we do diagnostic transfers? - if (AllowDiagnosticTransfer) - { - if (peerId > 0 && peers.ContainsKey(peerId)) - { - // validate peer (simple validation really) - if (peers[peerId].Connection && peers[peerId].EndPoint.ToString() == frame.Endpoint.ToString()) - { - uint srcId = FneUtils.Bytes3ToUInt32(message, 0); - if (UnitDeregistration != null) - UnitDeregistration(peerId, srcId); - } - } - } - } - else - Log(LogLevel.ERROR, $"({systemName}) Unknown transfer opcode {FneUtils.BytesToString(message, 0, 4)} -- {FneUtils.HexDump(message, 0)}"); - } - break; - - default: - Log(LogLevel.ERROR, $"({systemName}) Unknown opcode {FneUtils.BytesToString(message, 0, 4)} -- {FneUtils.HexDump(message, 0)}"); - break; - } - } - catch (SocketException se) - { - Log(LogLevel.FATAL, $"({systemName}) SOCKET ERROR: {se.SocketErrorCode}; {se.Message}"); - } - - if (ct.IsCancellationRequested) - abortListening = true; - } - } - - /// - /// Internal maintainence routine. - /// - private async void Maintainence() - { - CancellationToken ct = maintainenceCancelToken.Token; - while (!abortListening) - { - lock (peers) - { - // check to see if any peers have been quiet (no ping) longer than allowed - List peersToRemove = new List(); - foreach (KeyValuePair kvp in peers) - { - uint peerId = kvp.Key; - PeerInformation peer = kvp.Value; - - DateTime dt = peer.LastPing.AddSeconds(PingTime * MaxMissed); - if (dt < DateTime.Now) - { - Log(LogLevel.INFO, $"({systemName}) PEER {peerId} has timed out"); - peersToRemove.Add(peerId); - } - } - - // remove any peers - foreach (uint peerId in peersToRemove) - peers.Remove(peerId); - } - - try - { - await Task.Delay(PingTime * 1000, ct); - } - catch (TaskCanceledException) { /* stub */ } - } - } - } // public class FneMaster -} // namespace fnecore diff --git a/FnePeer.cs b/FnePeer.cs index 9863230..dfb9992 100644 --- a/FnePeer.cs +++ b/FnePeer.cs @@ -130,8 +130,6 @@ namespace fnecore /// public FnePeer(string systemName, uint peerId, IPEndPoint endpoint) : base(systemName, peerId) { - fneType = FneType.PEER; - masterEndpoint = endpoint; client = new UdpReceiver(); @@ -143,7 +141,7 @@ namespace fnecore } /// - /// Starts the main execution loop for this . + /// Starts the main execution loop for this . /// public override void Start() { @@ -170,7 +168,7 @@ namespace fnecore } /// - /// Stops the main execution loop for this . + /// Stops the main execution loop for this . /// public override void Stop() { @@ -256,6 +254,50 @@ namespace fnecore SendMaster(opcode, message, pktSeq()); } + /// + /// Helper to send group affiliation announcements to the master. + /// + /// + /// + public void SendMasterGroupAffiliation(uint srcId, uint dstId) + { + // send message to master + byte[] res = new byte[6]; + + FneUtils.Write3Bytes(srcId, ref res, 0); + FneUtils.Write3Bytes(dstId, ref res, 3); + + SendMaster(CreateOpcode(Constants.NET_FUNC_TRANSFER, Constants.NET_ANNC_SUBFUNC_GRP_AFFIL), res, 0); + } + + /// + /// Helper to send unit registration announcements to the master. + /// + /// + public void SendMasterUnitRegistration(uint srcId) + { + // send message to master + byte[] res = new byte[3]; + + FneUtils.Write3Bytes(srcId, ref res, 0); + + SendMaster(CreateOpcode(Constants.NET_FUNC_TRANSFER, Constants.NET_ANNC_SUBFUNC_UNIT_REG), res, 0); + } + + /// + /// Helper to send unit deregistration announcements to the master. + /// + /// + public void SendMasterUnitDeRegistration(uint srcId) + { + // send message to master + byte[] res = new byte[3]; + + FneUtils.Write3Bytes(srcId, ref res, 0); + + SendMaster(CreateOpcode(Constants.NET_FUNC_TRANSFER, Constants.NET_ANNC_SUBFUNC_UNIT_DEREG), res, 0); + } + /// /// Helper to update the RTP packet sequence. /// diff --git a/FneSystemBase.cs b/FneSystemBase.cs index 022867a..8a8c50a 100644 --- a/FneSystemBase.cs +++ b/FneSystemBase.cs @@ -123,7 +123,7 @@ namespace fnecore /// public abstract class FneSystemBase { - protected FneBase fne; + protected FnePeer fne; protected const int DMR_FRAME_LENGTH_BYTES = 33; protected const int DMR_PACKET_SIZE = 55; @@ -178,19 +178,6 @@ namespace fnecore } } - /// - /// Gets the this is. - /// - public FneType FneType - { - get - { - if (fne != null) - return fne.FneType; - return FneType.UNKNOWN; - } - } - /* ** Methods */ @@ -198,9 +185,9 @@ namespace fnecore /// /// Initializes a new instance of the class. /// - /// Instance of or + /// Instance of /// - public FneSystemBase(FneBase fne, LogLevel fneLogLevel = LogLevel.INFO) + public FneSystemBase(FnePeer fne, LogLevel fneLogLevel = LogLevel.INFO) { this.fne = fne; @@ -490,22 +477,9 @@ namespace fnecore Buffer.BlockCopy(raw, 0, payload, 24, raw.Length); payload[23U] = (byte)(P25_MSG_HDR_SIZE + raw.Length); - // what type of FNE are we? - if (FneType == FneType.MASTER) - { - FneMaster master = (FneMaster)fne; - lock (master.Peers) - { - foreach (uint peerId in master.Peers.Keys) - master.SendPeer(peerId, FneBase.CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), payload, 0, callData.TxStreamID); - } - } - else if (FneType == FneType.PEER) - { - FnePeer peer = (FnePeer)fne; - ushort pktSeq = peer.pktSeq(true); - peer.SendMaster(FneBase.CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), payload, pktSeq, callData.TxStreamID); - } + FnePeer peer = (FnePeer)fne; + ushort pktSeq = peer.pktSeq(true); + peer.SendMaster(FneBase.CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), payload, pktSeq, callData.TxStreamID); } /// @@ -523,22 +497,9 @@ namespace fnecore if (grantDemand) payload[14U] |= 0x80; - // what type of FNE are we? - if (FneType == FneType.MASTER) - { - FneMaster master = (FneMaster)fne; - lock (master.Peers) - { - foreach (uint peerId in master.Peers.Keys) - master.SendPeer(peerId, FneBase.CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), payload, 0, callData.TxStreamID); - } - } - else if (FneType == FneType.PEER) - { - FnePeer peer = (FnePeer)fne; - ushort pktSeq = peer.pktSeq(true); - peer.SendMaster(FneBase.CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), payload, pktSeq, callData.TxStreamID); - } + FnePeer peer = (FnePeer)fne; + ushort pktSeq = peer.pktSeq(true); + peer.SendMaster(FneBase.CreateOpcode(Constants.NET_FUNC_PROTOCOL, Constants.NET_PROTOCOL_SUBFUNC_P25), payload, pktSeq, callData.TxStreamID); } } // public abstract class FneSystemBase } // namespace fnecore