|
|
@@ -0,0 +1,611 @@
|
|
|
+using Ryujinx.Common.Logging;
|
|
|
+using Ryujinx.Common.Memory;
|
|
|
+using Ryujinx.Common.Utilities;
|
|
|
+using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
|
|
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm.Proxy;
|
|
|
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm.Types;
|
|
|
+using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Net;
|
|
|
+using System.Threading;
|
|
|
+
|
|
|
+namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
|
|
|
+{
|
|
|
+ internal class LanDiscovery : IDisposable
|
|
|
+ {
|
|
|
+ private const int DefaultPort = 11452;
|
|
|
+ private const ushort CommonChannel = 6;
|
|
|
+ private const byte CommonLinkLevel = 3;
|
|
|
+ private const byte CommonNetworkType = 2;
|
|
|
+
|
|
|
+ private const int FailureTimeout = 4000;
|
|
|
+
|
|
|
+ private readonly LdnMitmClient _parent;
|
|
|
+ private readonly LanProtocol _protocol;
|
|
|
+ private bool _initialized;
|
|
|
+ private readonly Ssid _fakeSsid;
|
|
|
+ private ILdnTcpSocket _tcp;
|
|
|
+ private LdnProxyUdpServer _udp, _udp2;
|
|
|
+ private readonly List<LdnProxyTcpSession> _stations = new();
|
|
|
+ private readonly object _lock = new();
|
|
|
+
|
|
|
+ private readonly AutoResetEvent _apConnected = new(false);
|
|
|
+
|
|
|
+ internal readonly IPAddress LocalAddr;
|
|
|
+ internal readonly IPAddress LocalBroadcastAddr;
|
|
|
+ internal NetworkInfo NetworkInfo;
|
|
|
+
|
|
|
+ public bool IsHost => _tcp is LdnProxyTcpServer;
|
|
|
+
|
|
|
+ private readonly Random _random = new();
|
|
|
+
|
|
|
+ // NOTE: Credit to https://stackoverflow.com/a/39338188
|
|
|
+ private static IPAddress GetBroadcastAddress(IPAddress address, IPAddress mask)
|
|
|
+ {
|
|
|
+ uint ipAddress = BitConverter.ToUInt32(address.GetAddressBytes(), 0);
|
|
|
+ uint ipMaskV4 = BitConverter.ToUInt32(mask.GetAddressBytes(), 0);
|
|
|
+ uint broadCastIpAddress = ipAddress | ~ipMaskV4;
|
|
|
+
|
|
|
+ return new IPAddress(BitConverter.GetBytes(broadCastIpAddress));
|
|
|
+ }
|
|
|
+
|
|
|
+ private static NetworkInfo GetEmptyNetworkInfo()
|
|
|
+ {
|
|
|
+ NetworkInfo networkInfo = new()
|
|
|
+ {
|
|
|
+ NetworkId = new NetworkId
|
|
|
+ {
|
|
|
+ SessionId = new Array16<byte>(),
|
|
|
+ },
|
|
|
+ Common = new CommonNetworkInfo
|
|
|
+ {
|
|
|
+ MacAddress = new Array6<byte>(),
|
|
|
+ Ssid = new Ssid
|
|
|
+ {
|
|
|
+ Name = new Array33<byte>(),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ Ldn = new LdnNetworkInfo
|
|
|
+ {
|
|
|
+ NodeCountMax = LdnConst.NodeCountMax,
|
|
|
+ SecurityParameter = new Array16<byte>(),
|
|
|
+ Nodes = new Array8<NodeInfo>(),
|
|
|
+ AdvertiseData = new Array384<byte>(),
|
|
|
+ Reserved4 = new Array140<byte>(),
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ for (int i = 0; i < LdnConst.NodeCountMax; i++)
|
|
|
+ {
|
|
|
+ networkInfo.Ldn.Nodes[i] = new NodeInfo
|
|
|
+ {
|
|
|
+ MacAddress = new Array6<byte>(),
|
|
|
+ UserName = new Array33<byte>(),
|
|
|
+ Reserved2 = new Array16<byte>(),
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return networkInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ public LanDiscovery(LdnMitmClient parent, IPAddress ipAddress, IPAddress ipv4Mask)
|
|
|
+ {
|
|
|
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Initialize LanDiscovery using IP: {ipAddress}");
|
|
|
+
|
|
|
+ _parent = parent;
|
|
|
+ LocalAddr = ipAddress;
|
|
|
+ LocalBroadcastAddr = GetBroadcastAddress(ipAddress, ipv4Mask);
|
|
|
+
|
|
|
+ _fakeSsid = new Ssid
|
|
|
+ {
|
|
|
+ Length = LdnConst.SsidLengthMax,
|
|
|
+ };
|
|
|
+ _random.NextBytes(_fakeSsid.Name.AsSpan()[..32]);
|
|
|
+
|
|
|
+ _protocol = new LanProtocol(this);
|
|
|
+ _protocol.Accept += OnConnect;
|
|
|
+ _protocol.SyncNetwork += OnSyncNetwork;
|
|
|
+ _protocol.DisconnectStation += DisconnectStation;
|
|
|
+
|
|
|
+ NetworkInfo = GetEmptyNetworkInfo();
|
|
|
+
|
|
|
+ ResetStations();
|
|
|
+
|
|
|
+ if (!InitUdp())
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, "LanDiscovery Initialize: InitUdp failed.");
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ _initialized = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void OnSyncNetwork(NetworkInfo info)
|
|
|
+ {
|
|
|
+ bool updated = false;
|
|
|
+
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ if (!NetworkInfo.Equals(info))
|
|
|
+ {
|
|
|
+ NetworkInfo = info;
|
|
|
+ updated = true;
|
|
|
+
|
|
|
+ Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"Host IP: {NetworkHelpers.ConvertUint(info.Ldn.Nodes[0].Ipv4Address)}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (updated)
|
|
|
+ {
|
|
|
+ _parent.InvokeNetworkChange(info, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ _apConnected.Set();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void OnConnect(LdnProxyTcpSession station)
|
|
|
+ {
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ station.NodeId = LocateEmptyNode();
|
|
|
+
|
|
|
+ if (_stations.Count > LdnConst.StationCountMax || station.NodeId == -1)
|
|
|
+ {
|
|
|
+ station.Disconnect();
|
|
|
+ station.Dispose();
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ _stations.Add(station);
|
|
|
+
|
|
|
+ UpdateNodes();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void DisconnectStation(LdnProxyTcpSession station)
|
|
|
+ {
|
|
|
+ if (!station.IsDisposed)
|
|
|
+ {
|
|
|
+ if (station.IsConnected)
|
|
|
+ {
|
|
|
+ station.Disconnect();
|
|
|
+ }
|
|
|
+
|
|
|
+ station.Dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ if (_stations.Remove(station))
|
|
|
+ {
|
|
|
+ NetworkInfo.Ldn.Nodes[station.NodeId] = new NodeInfo()
|
|
|
+ {
|
|
|
+ MacAddress = new Array6<byte>(),
|
|
|
+ UserName = new Array33<byte>(),
|
|
|
+ Reserved2 = new Array16<byte>(),
|
|
|
+ };
|
|
|
+
|
|
|
+ UpdateNodes();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool SetAdvertiseData(byte[] data)
|
|
|
+ {
|
|
|
+ if (data.Length > LdnConst.AdvertiseDataSizeMax)
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, "AdvertiseData exceeds size limit.");
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ data.CopyTo(NetworkInfo.Ldn.AdvertiseData.AsSpan());
|
|
|
+ NetworkInfo.Ldn.AdvertiseDataSize = (ushort)data.Length;
|
|
|
+
|
|
|
+ // NOTE: Otherwise this results in SessionKeepFailed or MasterDisconnected
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ if (NetworkInfo.Ldn.Nodes[0].IsConnected == 1)
|
|
|
+ {
|
|
|
+ UpdateNodes(true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void InitNetworkInfo()
|
|
|
+ {
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ NetworkInfo.Common.MacAddress = GetFakeMac();
|
|
|
+ NetworkInfo.Common.Channel = CommonChannel;
|
|
|
+ NetworkInfo.Common.LinkLevel = CommonLinkLevel;
|
|
|
+ NetworkInfo.Common.NetworkType = CommonNetworkType;
|
|
|
+ NetworkInfo.Common.Ssid = _fakeSsid;
|
|
|
+
|
|
|
+ NetworkInfo.Ldn.Nodes = new Array8<NodeInfo>();
|
|
|
+
|
|
|
+ for (int i = 0; i < LdnConst.NodeCountMax; i++)
|
|
|
+ {
|
|
|
+ NetworkInfo.Ldn.Nodes[i].NodeId = (byte)i;
|
|
|
+ NetworkInfo.Ldn.Nodes[i].IsConnected = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected Array6<byte> GetFakeMac(IPAddress address = null)
|
|
|
+ {
|
|
|
+ address ??= LocalAddr;
|
|
|
+
|
|
|
+ byte[] ip = address.GetAddressBytes();
|
|
|
+
|
|
|
+ var macAddress = new Array6<byte>();
|
|
|
+ new byte[] { 0x02, 0x00, ip[0], ip[1], ip[2], ip[3] }.CopyTo(macAddress.AsSpan());
|
|
|
+
|
|
|
+ return macAddress;
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool InitTcp(bool listening, IPAddress address = null, int port = DefaultPort)
|
|
|
+ {
|
|
|
+ Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LanDiscovery InitTcp: IP: {address}, listening: {listening}");
|
|
|
+
|
|
|
+ if (_tcp != null)
|
|
|
+ {
|
|
|
+ _tcp.DisconnectAndStop();
|
|
|
+ _tcp.Dispose();
|
|
|
+ _tcp = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ ILdnTcpSocket tcpSocket;
|
|
|
+
|
|
|
+ if (listening)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ address ??= LocalAddr;
|
|
|
+
|
|
|
+ tcpSocket = new LdnProxyTcpServer(_protocol, address, port);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Failed to create LdnProxyTcpServer: {ex}");
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!tcpSocket.Start())
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (address == null)
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ tcpSocket = new LdnProxyTcpClient(_protocol, address, port);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Failed to create LdnProxyTcpClient: {ex}");
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _tcp = tcpSocket;
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool InitUdp()
|
|
|
+ {
|
|
|
+ _udp?.Stop();
|
|
|
+ _udp2?.Stop();
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // NOTE: Linux won't receive any broadcast packets if the socket is not bound to the broadcast address.
|
|
|
+ // Windows only works if bound to localhost or the local address.
|
|
|
+ // See this discussion: https://stackoverflow.com/questions/13666789/receiving-udp-broadcast-packets-on-linux
|
|
|
+ if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
|
|
+ {
|
|
|
+ _udp2 = new LdnProxyUdpServer(_protocol, LocalBroadcastAddr, DefaultPort);
|
|
|
+ }
|
|
|
+
|
|
|
+ _udp = new LdnProxyUdpServer(_protocol, LocalAddr, DefaultPort);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Failed to create LdnProxyUdpServer: {ex}");
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public NetworkInfo[] Scan(ushort channel, ScanFilter filter)
|
|
|
+ {
|
|
|
+ _udp.ClearScanResults();
|
|
|
+
|
|
|
+ if (_protocol.SendBroadcast(_udp, LanPacketType.Scan, DefaultPort) < 0)
|
|
|
+ {
|
|
|
+ return Array.Empty<NetworkInfo>();
|
|
|
+ }
|
|
|
+
|
|
|
+ List<NetworkInfo> outNetworkInfo = new();
|
|
|
+
|
|
|
+ foreach (KeyValuePair<ulong, NetworkInfo> item in _udp.GetScanResults())
|
|
|
+ {
|
|
|
+ bool copy = true;
|
|
|
+
|
|
|
+ if (filter.Flag.HasFlag(ScanFilterFlag.LocalCommunicationId))
|
|
|
+ {
|
|
|
+ copy &= filter.NetworkId.IntentId.LocalCommunicationId == item.Value.NetworkId.IntentId.LocalCommunicationId;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (filter.Flag.HasFlag(ScanFilterFlag.SessionId))
|
|
|
+ {
|
|
|
+ copy &= filter.NetworkId.SessionId.AsSpan().SequenceEqual(item.Value.NetworkId.SessionId.AsSpan());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (filter.Flag.HasFlag(ScanFilterFlag.NetworkType))
|
|
|
+ {
|
|
|
+ copy &= filter.NetworkType == (NetworkType)item.Value.Common.NetworkType;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (filter.Flag.HasFlag(ScanFilterFlag.Ssid))
|
|
|
+ {
|
|
|
+ Span<byte> gameSsid = item.Value.Common.Ssid.Name.AsSpan()[item.Value.Common.Ssid.Length..];
|
|
|
+ Span<byte> scanSsid = filter.Ssid.Name.AsSpan()[filter.Ssid.Length..];
|
|
|
+ copy &= gameSsid.SequenceEqual(scanSsid);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (filter.Flag.HasFlag(ScanFilterFlag.SceneId))
|
|
|
+ {
|
|
|
+ copy &= filter.NetworkId.IntentId.SceneId == item.Value.NetworkId.IntentId.SceneId;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (copy)
|
|
|
+ {
|
|
|
+ if (item.Value.Ldn.Nodes[0].UserName[0] != 0)
|
|
|
+ {
|
|
|
+ outNetworkInfo.Add(item.Value);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "LanDiscovery Scan: Got empty Username. There might be a timing issue somewhere...");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return outNetworkInfo.ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void ResetStations()
|
|
|
+ {
|
|
|
+ lock (_lock)
|
|
|
+ {
|
|
|
+ foreach (LdnProxyTcpSession station in _stations)
|
|
|
+ {
|
|
|
+ station.Disconnect();
|
|
|
+ station.Dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ _stations.Clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int LocateEmptyNode()
|
|
|
+ {
|
|
|
+ Array8<NodeInfo> nodes = NetworkInfo.Ldn.Nodes;
|
|
|
+
|
|
|
+ for (int i = 1; i < nodes.Length; i++)
|
|
|
+ {
|
|
|
+ if (nodes[i].IsConnected == 0)
|
|
|
+ {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void UpdateNodes(bool forceUpdate = false)
|
|
|
+ {
|
|
|
+ int countConnected = 1;
|
|
|
+
|
|
|
+ foreach (LdnProxyTcpSession station in _stations.Where(station => station.IsConnected))
|
|
|
+ {
|
|
|
+ countConnected++;
|
|
|
+
|
|
|
+ station.OverrideInfo();
|
|
|
+
|
|
|
+ // NOTE: This is not part of the original implementation.
|
|
|
+ NetworkInfo.Ldn.Nodes[station.NodeId] = station.NodeInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ byte nodeCount = (byte)countConnected;
|
|
|
+
|
|
|
+ bool networkInfoChanged = forceUpdate || NetworkInfo.Ldn.NodeCount != nodeCount;
|
|
|
+
|
|
|
+ NetworkInfo.Ldn.NodeCount = nodeCount;
|
|
|
+
|
|
|
+ foreach (LdnProxyTcpSession station in _stations)
|
|
|
+ {
|
|
|
+ if (station.IsConnected)
|
|
|
+ {
|
|
|
+ if (_protocol.SendPacket(station, LanPacketType.SyncNetwork, SpanHelpers.AsSpan<NetworkInfo, byte>(ref NetworkInfo).ToArray()) < 0)
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Failed to send {LanPacketType.SyncNetwork} to station {station.NodeId}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (networkInfoChanged)
|
|
|
+ {
|
|
|
+ _parent.InvokeNetworkChange(NetworkInfo, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected NodeInfo GetNodeInfo(NodeInfo node, UserConfig userConfig, ushort localCommunicationVersion)
|
|
|
+ {
|
|
|
+ uint ipAddress = NetworkHelpers.ConvertIpv4Address(LocalAddr);
|
|
|
+
|
|
|
+ node.MacAddress = GetFakeMac();
|
|
|
+ node.IsConnected = 1;
|
|
|
+ node.UserName = userConfig.UserName;
|
|
|
+ node.LocalCommunicationVersion = localCommunicationVersion;
|
|
|
+ node.Ipv4Address = ipAddress;
|
|
|
+
|
|
|
+ return node;
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool CreateNetwork(SecurityConfig securityConfig, UserConfig userConfig, NetworkConfig networkConfig)
|
|
|
+ {
|
|
|
+ if (!InitTcp(true))
|
|
|
+ {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ InitNetworkInfo();
|
|
|
+
|
|
|
+ NetworkInfo.Ldn.NodeCountMax = networkConfig.NodeCountMax;
|
|
|
+ NetworkInfo.Ldn.SecurityMode = (ushort)securityConfig.SecurityMode;
|
|
|
+
|
|
|
+ NetworkInfo.Common.Channel = networkConfig.Channel == 0 ? (ushort)6 : networkConfig.Channel;
|
|
|
+
|
|
|
+ NetworkInfo.NetworkId.SessionId = new Array16<byte>();
|
|
|
+ _random.NextBytes(NetworkInfo.NetworkId.SessionId.AsSpan());
|
|
|
+ NetworkInfo.NetworkId.IntentId = networkConfig.IntentId;
|
|
|
+
|
|
|
+ NetworkInfo.Ldn.Nodes[0] = GetNodeInfo(NetworkInfo.Ldn.Nodes[0], userConfig, networkConfig.LocalCommunicationVersion);
|
|
|
+ NetworkInfo.Ldn.Nodes[0].IsConnected = 1;
|
|
|
+ NetworkInfo.Ldn.NodeCount++;
|
|
|
+
|
|
|
+ _parent.InvokeNetworkChange(NetworkInfo, true);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void DestroyNetwork()
|
|
|
+ {
|
|
|
+ if (_tcp != null)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _tcp.DisconnectAndStop();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _tcp.Dispose();
|
|
|
+ _tcp = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ResetStations();
|
|
|
+ }
|
|
|
+
|
|
|
+ public NetworkError Connect(NetworkInfo networkInfo, UserConfig userConfig, uint localCommunicationVersion)
|
|
|
+ {
|
|
|
+ _apConnected.Reset();
|
|
|
+
|
|
|
+ if (networkInfo.Ldn.NodeCount == 0)
|
|
|
+ {
|
|
|
+ return NetworkError.Unknown;
|
|
|
+ }
|
|
|
+
|
|
|
+ IPAddress address = NetworkHelpers.ConvertUint(networkInfo.Ldn.Nodes[0].Ipv4Address);
|
|
|
+
|
|
|
+ Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Connecting to host: {address}");
|
|
|
+
|
|
|
+ if (!InitTcp(false, address))
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, "Could not initialize TCPClient");
|
|
|
+
|
|
|
+ return NetworkError.ConnectNotFound;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!_tcp.Connect())
|
|
|
+ {
|
|
|
+ Logger.Error?.PrintMsg(LogClass.ServiceLdn, "Failed to connect.");
|
|
|
+
|
|
|
+ return NetworkError.ConnectFailure;
|
|
|
+ }
|
|
|
+
|
|
|
+ NodeInfo myNode = GetNodeInfo(new NodeInfo(), userConfig, (ushort)localCommunicationVersion);
|
|
|
+ if (_protocol.SendPacket(_tcp, LanPacketType.Connect, SpanHelpers.AsSpan<NodeInfo, byte>(ref myNode).ToArray()) < 0)
|
|
|
+ {
|
|
|
+ return NetworkError.Unknown;
|
|
|
+ }
|
|
|
+
|
|
|
+ return _apConnected.WaitOne(FailureTimeout) ? NetworkError.None : NetworkError.ConnectTimeout;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ if (_initialized)
|
|
|
+ {
|
|
|
+ DisconnectAndStop();
|
|
|
+ ResetStations();
|
|
|
+ _initialized = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ _protocol.Accept -= OnConnect;
|
|
|
+ _protocol.SyncNetwork -= OnSyncNetwork;
|
|
|
+ _protocol.DisconnectStation -= DisconnectStation;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void DisconnectAndStop()
|
|
|
+ {
|
|
|
+ if (_udp != null)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _udp.Stop();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _udp.Dispose();
|
|
|
+ _udp = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_udp2 != null)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _udp2.Stop();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _udp2.Dispose();
|
|
|
+ _udp2 = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_tcp != null)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _tcp.DisconnectAndStop();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ _tcp.Dispose();
|
|
|
+ _tcp = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|