mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-06-02 21:28:46 +02:00
c14ce4d2a5
* Add relevant files from private repo Hopefully I didn't miss anything. JsonHelper.cs is a debug only change I only added line 810-812 in IUserLocalCommunicationService.cs for the new Spacemeowx2Ldn case. * Add a small README.md just for fun * Add note about NetCoreServer update to 5.1.0 * Fix a few issues Fix usage of wrong broadcast address Log warning if empty userstring was received and don't add them to outNetworkInfo * Add warning about incompatibility with public LDN version * Add missing changes from old_master * Adjust ldn_mitm for Ryujinx/Ryujinx#3805 * ldn: Adapt to changes from #4582 * ldn_mitm: First cleanup iteration * ldn_mitm: Second cleanup iteration * Credit spacemeowx2 in README.md * Address first review comments by AcK Adhere to Ryujinx coding style Remove leftover log calls Change category of a few log calls Remove leftover debug notes * Replace return type with void for methods always returning true * Address first review comments by riperiperi Purely stylistic changes: - Adhere to naming style for internal fields - Improve code formatting * Throw InvalidOperationException when calling wrong ldn proxy methods * Add missing newlines in LanDiscovery.Scan() * Fix Linux not receiving broadcast packets * Remove ILdnUdpSocket It's very unlikely that we will ever need a udp client. Thus we should simplify LanDiscovery initialization and remove the parameter of InitUdp(). * ldn_mitm: Improve formatting * fixup! Fix Linux not receiving broadcast packets By opening the udp server on 'LocalBroadcastAddr' Linux refused to answer packets going to LocalAddr. So in order to fix this problem, Linux now opens two LdnProxyUdpServers. * ldn_mitm: Fix assigning incorrect NodeIds This just made connecting a lot more reliable! Thanks @riperiperi * Fix node ids when leaving/joining * Change NodeId behaviour to work like RyuLdn * Change timing for accept and network info being reported. * Wait for connection before sending anything. * Remove ConnectAsync() from ILdnTcpSocket * Only broadcast scan responses if we're hosting a network. * Fix some filters, scan network duplication. * Fix silly mistake * Don't die on duplicates, just replace. * Lock around node updates These can happen from multiple threads. * ldn_mitm: Fix namespaces for Types Improve formatting Add warning if compression failed * Add quicker scan, forgetting networks that disappear. * Always force a network sync when updating AdvertiseData * Fix TCP frame size being too large for compressed frames * Allow ldn_mitm to pass -1 id for room localcommunicationids. * ldn_mitm: Match server socket options * ldn_mitm: Use correct socket options * ldn_mitm: Remove TCP broadcast socket options * config: Rename Spacemeowx2Ldn to LdnMitm * ldn_mitm: Generate random fake SSID * ldn_mitm: Adjust logging statements/levels * ldn_mitm: Add missing Stop() call for udp2 * ldn_mitm: Adjust formatting * ldn_mitm: Add stub comments and adjust existing ones * ldn: Add LdnConst class & set tx/rx buffer sizes correctly * Move LdnConst out of UserServiceCreator Replace a few values with LdnConsts * ldn: Adjust namespaces and client names * ldn_mitm: Adjust formatting * ldn: Rename RyuLdn to LdnRyu * Replace LanProtocol.Read() refs with scoped refs * Add MIT license for ldn_mitm * Clarify that network interface is also used for LDN Although it's currently only used by ldn_mitm, it would probably be more confusing to exclude RyuLdn there. * Fix giving a station node id 0 * Update Nuget packages * Remove LdnHelper * Add update functions for EnableInternetAccess setting * ldn: Log MultiplayerMode and DisableP2P * ldn: Adjust namespaces * Apply formatting * Conform to Ryujinx code style * Remove ldn_mitm from THIRDPARTY.md It shouldn't have been there in the first place. * Improve formatting --------- Co-authored-by: riperiperi <rhy3756547@hotmail.com> Co-authored-by: Ac_K <Acoustik666@gmail.com>
315 lines
10 KiB
C#
315 lines
10 KiB
C#
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 System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
|
|
{
|
|
internal class LanProtocol
|
|
{
|
|
private const uint LanMagic = 0x11451400;
|
|
|
|
public const int BufferSize = 2048;
|
|
public const int TcpTxBufferSize = 0x800;
|
|
public const int TcpRxBufferSize = 0x1000;
|
|
public const int TxBufferSizeMax = 0x2000;
|
|
public const int RxBufferSizeMax = 0x2000;
|
|
|
|
private readonly int _headerSize = Marshal.SizeOf<LanPacketHeader>();
|
|
|
|
private readonly LanDiscovery _discovery;
|
|
|
|
public event Action<LdnProxyTcpSession> Accept;
|
|
public event Action<EndPoint, LanPacketType, byte[]> Scan;
|
|
public event Action<NetworkInfo> ScanResponse;
|
|
public event Action<NetworkInfo> SyncNetwork;
|
|
public event Action<NodeInfo, EndPoint> Connect;
|
|
public event Action<LdnProxyTcpSession> DisconnectStation;
|
|
|
|
public LanProtocol(LanDiscovery parent)
|
|
{
|
|
_discovery = parent;
|
|
}
|
|
|
|
public void InvokeAccept(LdnProxyTcpSession session)
|
|
{
|
|
Accept?.Invoke(session);
|
|
}
|
|
|
|
public void InvokeDisconnectStation(LdnProxyTcpSession session)
|
|
{
|
|
DisconnectStation?.Invoke(session);
|
|
}
|
|
|
|
private void DecodeAndHandle(LanPacketHeader header, byte[] data, EndPoint endPoint = null)
|
|
{
|
|
switch (header.Type)
|
|
{
|
|
case LanPacketType.Scan:
|
|
// UDP
|
|
if (_discovery.IsHost)
|
|
{
|
|
Scan?.Invoke(endPoint, LanPacketType.ScanResponse, SpanHelpers.AsSpan<NetworkInfo, byte>(ref _discovery.NetworkInfo).ToArray());
|
|
}
|
|
break;
|
|
case LanPacketType.ScanResponse:
|
|
// UDP
|
|
ScanResponse?.Invoke(MemoryMarshal.Cast<byte, NetworkInfo>(data)[0]);
|
|
break;
|
|
case LanPacketType.SyncNetwork:
|
|
// TCP
|
|
SyncNetwork?.Invoke(MemoryMarshal.Cast<byte, NetworkInfo>(data)[0]);
|
|
break;
|
|
case LanPacketType.Connect:
|
|
// TCP Session / Station
|
|
Connect?.Invoke(MemoryMarshal.Cast<byte, NodeInfo>(data)[0], endPoint);
|
|
break;
|
|
default:
|
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decode error: Unhandled type {header.Type}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void Read(scoped ref byte[] buffer, scoped ref int bufferEnd, byte[] data, int offset, int size, EndPoint endPoint = null)
|
|
{
|
|
if (endPoint != null && _discovery.LocalAddr.Equals(((IPEndPoint)endPoint).Address))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int index = 0;
|
|
while (index < size)
|
|
{
|
|
if (bufferEnd < _headerSize)
|
|
{
|
|
int copyable2 = Math.Min(size - index, Math.Min(size, _headerSize - bufferEnd));
|
|
|
|
Array.Copy(data, index + offset, buffer, bufferEnd, copyable2);
|
|
|
|
index += copyable2;
|
|
bufferEnd += copyable2;
|
|
}
|
|
|
|
if (bufferEnd >= _headerSize)
|
|
{
|
|
LanPacketHeader header = MemoryMarshal.Cast<byte, LanPacketHeader>(buffer)[0];
|
|
if (header.Magic != LanMagic)
|
|
{
|
|
bufferEnd = 0;
|
|
|
|
Logger.Warning?.PrintMsg(LogClass.ServiceLdn, $"Invalid magic number in received packet. [magic: {header.Magic}] [EP: {endPoint}]");
|
|
|
|
return;
|
|
}
|
|
|
|
int totalSize = _headerSize + header.Length;
|
|
if (totalSize > BufferSize)
|
|
{
|
|
bufferEnd = 0;
|
|
|
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Max packet size {BufferSize} exceeded.");
|
|
|
|
return;
|
|
}
|
|
|
|
int copyable = Math.Min(size - index, Math.Min(size, totalSize - bufferEnd));
|
|
|
|
Array.Copy(data, index + offset, buffer, bufferEnd, copyable);
|
|
|
|
index += copyable;
|
|
bufferEnd += copyable;
|
|
|
|
if (totalSize == bufferEnd)
|
|
{
|
|
byte[] ldnData = new byte[totalSize - _headerSize];
|
|
Array.Copy(buffer, _headerSize, ldnData, 0, ldnData.Length);
|
|
|
|
if (header.Compressed == 1)
|
|
{
|
|
if (Decompress(ldnData, out byte[] decompressedLdnData) != 0)
|
|
{
|
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error:\n {header}, {_headerSize}\n {ldnData}, {ldnData.Length}");
|
|
|
|
return;
|
|
}
|
|
|
|
if (decompressedLdnData.Length != header.DecompressLength)
|
|
{
|
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error: length does not match. ({decompressedLdnData.Length} != {header.DecompressLength})");
|
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error data: '{string.Join("", decompressedLdnData.Select(x => (int)x).ToArray())}'");
|
|
|
|
return;
|
|
}
|
|
|
|
ldnData = decompressedLdnData;
|
|
}
|
|
|
|
DecodeAndHandle(header, ldnData, endPoint);
|
|
|
|
bufferEnd = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public int SendBroadcast(ILdnSocket s, LanPacketType type, int port)
|
|
{
|
|
return SendPacket(s, type, Array.Empty<byte>(), new IPEndPoint(_discovery.LocalBroadcastAddr, port));
|
|
}
|
|
|
|
public int SendPacket(ILdnSocket s, LanPacketType type, byte[] data, EndPoint endPoint = null)
|
|
{
|
|
byte[] buf = PreparePacket(type, data);
|
|
|
|
return s.SendPacketAsync(endPoint, buf) ? 0 : -1;
|
|
}
|
|
|
|
public int SendPacket(LdnProxyTcpSession s, LanPacketType type, byte[] data)
|
|
{
|
|
byte[] buf = PreparePacket(type, data);
|
|
|
|
return s.SendAsync(buf) ? 0 : -1;
|
|
}
|
|
|
|
private LanPacketHeader PrepareHeader(LanPacketHeader header, LanPacketType type)
|
|
{
|
|
header.Magic = LanMagic;
|
|
header.Type = type;
|
|
header.Compressed = 0;
|
|
header.Length = 0;
|
|
header.DecompressLength = 0;
|
|
header.Reserved = new Array2<byte>();
|
|
|
|
return header;
|
|
}
|
|
|
|
private byte[] PreparePacket(LanPacketType type, byte[] data)
|
|
{
|
|
LanPacketHeader header = PrepareHeader(new LanPacketHeader(), type);
|
|
header.Length = (ushort)data.Length;
|
|
|
|
byte[] buf;
|
|
if (data.Length > 0)
|
|
{
|
|
if (Compress(data, out byte[] compressed) == 0)
|
|
{
|
|
header.DecompressLength = header.Length;
|
|
header.Length = (ushort)compressed.Length;
|
|
header.Compressed = 1;
|
|
|
|
buf = new byte[compressed.Length + _headerSize];
|
|
|
|
SpanHelpers.AsSpan<LanPacketHeader, byte>(ref header).ToArray().CopyTo(buf, 0);
|
|
compressed.CopyTo(buf, _headerSize);
|
|
}
|
|
else
|
|
{
|
|
buf = new byte[data.Length + _headerSize];
|
|
|
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, "Compressing packet data failed.");
|
|
|
|
SpanHelpers.AsSpan<LanPacketHeader, byte>(ref header).ToArray().CopyTo(buf, 0);
|
|
data.CopyTo(buf, _headerSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
buf = new byte[_headerSize];
|
|
SpanHelpers.AsSpan<LanPacketHeader, byte>(ref header).ToArray().CopyTo(buf, 0);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
private int Compress(byte[] input, out byte[] output)
|
|
{
|
|
List<byte> outputList = new();
|
|
int i = 0;
|
|
int maxCount = 0xFF;
|
|
|
|
while (i < input.Length)
|
|
{
|
|
byte inputByte = input[i++];
|
|
int count = 0;
|
|
|
|
if (inputByte == 0)
|
|
{
|
|
while (i < input.Length && input[i] == 0 && count < maxCount)
|
|
{
|
|
count += 1;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (inputByte == 0)
|
|
{
|
|
outputList.Add(0);
|
|
|
|
if (outputList.Count == BufferSize)
|
|
{
|
|
output = null;
|
|
|
|
return -1;
|
|
}
|
|
|
|
outputList.Add((byte)count);
|
|
}
|
|
else
|
|
{
|
|
outputList.Add(inputByte);
|
|
}
|
|
}
|
|
|
|
output = outputList.ToArray();
|
|
|
|
return i == input.Length ? 0 : -1;
|
|
}
|
|
|
|
private int Decompress(byte[] input, out byte[] output)
|
|
{
|
|
List<byte> outputList = new();
|
|
int i = 0;
|
|
|
|
while (i < input.Length && outputList.Count < BufferSize)
|
|
{
|
|
byte inputByte = input[i++];
|
|
|
|
outputList.Add(inputByte);
|
|
|
|
if (inputByte == 0)
|
|
{
|
|
if (i == input.Length)
|
|
{
|
|
output = null;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int count = input[i++];
|
|
|
|
for (int j = 0; j < count; j++)
|
|
{
|
|
if (outputList.Count == BufferSize)
|
|
{
|
|
break;
|
|
}
|
|
|
|
outputList.Add(inputByte);
|
|
}
|
|
}
|
|
}
|
|
|
|
output = outputList.ToArray();
|
|
|
|
return i == input.Length ? 0 : -1;
|
|
}
|
|
}
|
|
}
|