fix RyuLDN proxy routing and add Mii DB logging

Fix is not currently functionnal, i'm just committing this so i can work on another branch locally, but there has been progress in regards of mii sharing.
a substatial part of this code doesn't go towards resolving mii sharing over LDN, its just WIP stuff
This commit is contained in:
Babib3l 2026-05-21 17:29:22 +02:00 committed by Gabriel
parent 76f8446391
commit 7c2dae6360
7 changed files with 450 additions and 25 deletions

View file

@ -1,4 +1,8 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Ldn.Types;
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
@ -6,6 +10,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
private const int NifmRequestId = 90;
private bool _actionFrameEnabled;
protected override bool ValidateLocalCommunicationId => false;
public ISystemLocalCommunicationService(ServiceCtx context) : base(context) { }
// NOTE: This overrides the parent's Initialize method with the same command ID (402)
@ -19,6 +27,123 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
return ResultCode.Success;
}
[CommandCmif(500)] // 18.0.0+
// EnableActionFrame(nn::ldn::ActionFrameSettings)
public ResultCode EnableActionFrame(ServiceCtx context)
{
byte[] settings = context.RequestData.ReadBytes(0x80);
_actionFrameEnabled = true;
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new
{
localCommunicationId = System.BitConverter.ToUInt64(settings, 0),
securityMode = System.BitConverter.ToUInt16(settings, 0x3c),
passphraseSize = System.BitConverter.ToUInt16(settings, 0x3e),
});
return ResultCode.Success;
}
[CommandCmif(501)] // 18.0.0+
// DisableActionFrame()
public ResultCode DisableActionFrame(ServiceCtx context)
{
_actionFrameEnabled = false;
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
return ResultCode.Success;
}
[CommandCmif(502)] // 18.0.0+
// SendActionFrame(buffer<unknown, 0x21>, nn::ldn::MacAddress, nn::ldn::MacAddress, s16 channel, u32 flags)
public ResultCode SendActionFrame(ServiceCtx context)
{
Array6<byte> destination = context.RequestData.ReadStruct<Array6<byte>>();
Array6<byte> bssid = context.RequestData.ReadStruct<Array6<byte>>();
short channel = context.RequestData.ReadInt16();
_ = context.RequestData.ReadInt16();
uint flags = context.RequestData.ReadUInt32();
(ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21();
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new
{
enabled = _actionFrameEnabled,
bufferPosition,
bufferSize,
destination,
bssid,
channel,
flags,
});
return _actionFrameEnabled && bufferPosition != 0 && bufferSize != 0
? ResultCode.Success
: ResultCode.InvalidState;
}
[CommandCmif(503)] // 18.0.0+
// RecvActionFrame(u32 flags, buffer<unknown, 0x22>) -> nn::ldn::MacAddress, nn::ldn::MacAddress, s16 channel, u32 size, s32 link_level
public ResultCode RecvActionFrame(ServiceCtx context)
{
uint flags = context.RequestData.ReadUInt32();
(ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22();
if (bufferPosition != 0 && bufferSize != 0)
{
MemoryHelper.FillWithZeros(context.Memory, bufferPosition, (int)bufferSize);
}
context.ResponseData.WriteStruct(new Array6<byte>());
context.ResponseData.WriteStruct(new Array6<byte>());
context.ResponseData.Write((short)0);
context.ResponseData.Write((ushort)0);
context.ResponseData.Write(0u);
context.ResponseData.Write(0);
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new
{
enabled = _actionFrameEnabled,
bufferSize,
flags,
});
return _actionFrameEnabled ? ResultCode.Success : ResultCode.InvalidState;
}
[CommandCmif(505)] // 18.0.0+
// SetHomeChannel(s16 channel)
public ResultCode SetHomeChannel(ServiceCtx context)
{
short channel = context.RequestData.ReadInt16();
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new { channel });
return ResultCode.Success;
}
[CommandCmif(600)] // 18.0.0+
// SetTxPower(s32 power)
public ResultCode SetTxPower(ServiceCtx context)
{
int power = context.RequestData.ReadInt32();
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new { power });
return ResultCode.Success;
}
[CommandCmif(601)] // 18.0.0+
// ResetTxPower()
public ResultCode ResetTxPower(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
return ResultCode.Success;
}
[CommandCmif(403)]
// InitializeWithVersion(s32 version, pid)
public ResultCode InitializeWithVersion(ServiceCtx context)

View file

@ -49,6 +49,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
private AccessPoint _accessPoint;
private Station _station;
protected virtual bool ValidateLocalCommunicationId => true;
private ushort CheckDevelopmentChannel(ushort channel)
{
return (ushort)(!IsDevelopment ? 0 : channel);
@ -531,7 +533,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
[CommandCmif(200)]
// OpenAccessPoint()
public ResultCode OpenAccessPoint(ServiceCtx context)
public virtual ResultCode OpenAccessPoint(ServiceCtx context)
{
if (_nifmResultCode != ResultCode.Success)
{
@ -620,7 +622,9 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
networkConfig.IntentId.LocalCommunicationId = (long)controlProperty.LocalCommunicationId[0];
}
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
bool isLocalCommunicationIdValid = !ValidateLocalCommunicationId ||
CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
{
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid object!");

View file

@ -473,6 +473,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
public bool CreateNetwork(CreateAccessPointRequest request, byte[] advertiseData)
{
_timeout.DisableTimeout();
_apConnected.Reset();
ConfigureAccessPoint(ref request.RyuNetworkConfig);
@ -528,6 +529,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
public bool CreateNetworkPrivate(CreateAccessPointPrivateRequest request, byte[] advertiseData)
{
_timeout.DisableTimeout();
_apConnected.Reset();
ConfigureAccessPoint(ref request.RyuNetworkConfig);
@ -600,6 +602,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
public NetworkError Connect(ConnectRequest request)
{
_timeout.DisableTimeout();
_apConnected.Reset();
if (!EnsureConnected())
{
@ -622,6 +625,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
public NetworkError ConnectPrivate(ConnectPrivateRequest request)
{
_timeout.DisableTimeout();
_apConnected.Reset();
if (!EnsureConnected())
{

View file

@ -21,6 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
private readonly uint _subnetMask;
private readonly uint _localIp;
private readonly uint _broadcast;
private bool _tcpWarningLogged;
public LdnProxy(ProxyConfig config, IProxyClient client, RyuLdnProtocol protocol)
{
@ -43,9 +44,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
public bool Supported(AddressFamily domain, SocketType type, ProtocolType protocol)
{
if (protocol == ProtocolType.Tcp)
if (protocol == ProtocolType.Tcp && !_tcpWarningLogged)
{
Logger.Error?.PrintMsg(LogClass.ServiceLdn, "Tcp proxy networking is untested. Please report this game so that it can be tested.");
Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "LDN proxy TCP networking is experimental.");
_tcpWarningLogged = true;
}
return domain == AddressFamily.InterNetwork && (protocol == ProtocolType.Tcp || protocol == ProtocolType.Udp);
@ -115,53 +117,168 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
}
}
private void ForConnectionResponseSockets(ProxyInfo info, Action<LdnProxySocket> action)
{
lock (_sockets)
{
foreach (LdnProxySocket socket in _sockets)
{
if (socket.ProtocolType != info.Protocol ||
socket.LocalEndPoint is not IPEndPoint localEndpoint ||
localEndpoint.Port != info.DestPort ||
socket.RemoteEndPoint is not IPEndPoint remoteEndpoint ||
remoteEndpoint.Port != info.SourcePort)
{
continue;
}
action(socket);
}
}
}
private void ForDataSockets(ProxyInfo info, Action<LdnProxySocket> action)
{
lock (_sockets)
{
foreach (LdnProxySocket socket in _sockets)
{
if (socket.ProtocolType != info.Protocol ||
socket.LocalEndPoint is not IPEndPoint localEndpoint ||
localEndpoint.Port != info.DestPort ||
!EndpointMatches(localEndpoint, info.DestIpV4))
{
continue;
}
if (info.Protocol == ProtocolType.Tcp &&
(!socket.Connected ||
socket.RemoteEndPoint is not IPEndPoint remoteEndpoint ||
remoteEndpoint.Port != info.SourcePort ||
!EndpointMatches(remoteEndpoint, info.SourceIpV4)))
{
continue;
}
action(socket);
}
}
}
public void HandleConnectionRequest(LdnHeader header, ProxyConnectRequest request)
{
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy connect received: {FormatInfo(request.Info)}");
bool routed = false;
ForRoutedSockets(request.Info, (socket) =>
{
routed = true;
socket.HandleConnectRequest(request);
});
if (!routed)
{
Logger.Warning?.PrintMsg(LogClass.ServiceLdn, $"ProxyConnect had no listening socket: {FormatInfo(request.Info)}");
}
}
public void HandleConnectionResponse(LdnHeader header, ProxyConnectResponse response)
{
ForRoutedSockets(response.Info, (socket) =>
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy connect reply received: {FormatInfo(response.Info)}");
bool routed = false;
ForConnectionResponseSockets(response.Info, (socket) =>
{
routed = true;
socket.HandleConnectResponse(response);
});
if (!routed)
{
Logger.Warning?.PrintMsg(LogClass.ServiceLdn, $"ProxyConnectReply had no connecting socket: {FormatInfo(response.Info)}");
}
}
public void HandleData(LdnHeader header, ProxyDataHeader proxyHeader, byte[] data)
{
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy data received: {data.Length} bytes, {FormatInfo(proxyHeader.Info)}");
ProxyDataPacket packet = new() { Header = proxyHeader, Data = data };
ForRoutedSockets(proxyHeader.Info, (socket) =>
bool routed = false;
ForDataSockets(proxyHeader.Info, (socket) =>
{
routed = true;
socket.IncomingData(packet);
});
if (!routed)
{
Logger.Warning?.PrintMsg(LogClass.ServiceLdn, $"ProxyData had no receiving socket: {FormatInfo(proxyHeader.Info)}");
}
}
public void HandleDisconnect(LdnHeader header, ProxyDisconnectMessage disconnect)
{
ForRoutedSockets(disconnect.Info, (socket) =>
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy disconnect received: {FormatInfo(disconnect.Info)}");
bool routed = false;
ForDataSockets(disconnect.Info, (socket) =>
{
routed = true;
socket.HandleDisconnect(disconnect);
});
if (!routed)
{
Logger.Warning?.PrintMsg(LogClass.ServiceLdn, $"ProxyDisconnect had no connected socket: {FormatInfo(disconnect.Info)}");
}
}
private uint GetIpV4(IPEndPoint endpoint)
{
ArgumentNullException.ThrowIfNull(endpoint);
if (endpoint.AddressFamily != AddressFamily.InterNetwork)
{
throw new NotSupportedException();
}
if (endpoint.Address.Equals(IPAddress.Any))
{
return _localIp;
}
byte[] address = endpoint.Address.GetAddressBytes();
Array.Reverse(address);
return BitConverter.ToUInt32(address);
}
private bool EndpointMatches(IPEndPoint endpoint, uint ipv4)
{
return endpoint.Address.Equals(IPAddress.Any) ||
endpoint.Address.Equals(IPAddress.IPv6Any) ||
GetIpV4(endpoint) == ipv4;
}
private static string FormatInfo(ProxyInfo info)
{
return $"{FormatIp(info.SourceIpV4)}:{info.SourcePort} -> {FormatIp(info.DestIpV4)}:{info.DestPort} ({info.Protocol})";
}
private static string FormatIp(uint ipv4)
{
byte[] address = BitConverter.GetBytes(ipv4);
Array.Reverse(address);
return new IPAddress(address).ToString();
}
private ProxyInfo MakeInfo(IPEndPoint localEp, IPEndPoint remoteEP, ProtocolType type)
{
return new ProxyInfo
@ -185,6 +302,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
Info = MakeInfo(localEp, remoteEp, type)
};
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy connect sent: {FormatInfo(request.Info)}");
_parent.SendAsync(_protocol.Encode(PacketId.ProxyConnect, request));
}
@ -197,6 +316,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
Info = MakeInfo(localEp, remoteEp, type)
};
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy connect reply sent: {FormatInfo(request.Info)}");
_parent.SendAsync(_protocol.Encode(PacketId.ProxyConnectReply, request));
}
@ -210,6 +331,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
DisconnectReason = 0 // TODO
};
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy disconnect sent: {FormatInfo(request.Info)}");
_parent.SendAsync(_protocol.Encode(PacketId.ProxyDisconnect, request));
}
@ -224,6 +347,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
DataLength = (uint)buffer.Length
};
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy data sent: {buffer.Length} bytes, {FormatInfo(request.Info)}");
_parent.SendAsync(_protocol.Encode(PacketId.ProxyData, request, buffer.ToArray()));
return buffer.Length;

View file

@ -1,6 +1,7 @@
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Net;
@ -41,6 +42,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
private bool _readShutdown;
// private bool _writeShutdown;
private bool _closed;
private long _bytesReceived;
private long _bytesSent;
private int _receiveWouldBlockCount;
private bool _closeSummaryLogged;
private string _sentPreview;
private string _receivedPreview;
private readonly Dictionary<SocketOptionName, int> _socketOptions = new()
{
@ -118,7 +125,16 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
}
}
public bool Writable => Connected || ProtocolType == ProtocolType.Udp;
public bool Error => false;
public bool Error
{
get
{
lock (_errors)
{
return _errors.Count > 0;
}
}
}
public LdnProxySocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, LdnProxy proxy)
{
@ -152,13 +168,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
return localEp;
}
public LdnProxySocket AsAccepted(IPEndPoint remoteEp)
public LdnProxySocket AsAccepted(IPEndPoint localEp, IPEndPoint remoteEp)
{
Connected = true;
LocalEndPoint = localEp;
RemoteEndPoint = remoteEp;
IPEndPoint localEp = EnsureLocalEndpoint(true);
_proxy.SignalConnected(localEp, remoteEp, ProtocolType);
return this;
@ -190,6 +205,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
{
_receiveQueue.Enqueue(packet);
}
_bytesReceived += packet.Data.Length;
_receivedPreview ??= FormatPayloadPreview(packet.Data);
_receiveEvent.Set();
}
}
@ -200,6 +220,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
throw new InvalidOperationException();
}
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy socket accept waiting on {LocalEndPoint}");
// Accept a pending request to this socket.
lock (_connectRequests)
@ -228,12 +250,15 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
// Is this request made for us?
IPEndPoint endpoint = GetEndpoint(request.Info.DestIpV4, request.Info.DestPort);
if (Equals(endpoint, LocalEndPoint))
if (MatchesLocalEndpoint(endpoint))
{
// Yes - let's accept.
IPEndPoint remoteEndpoint = GetEndpoint(request.Info.SourceIpV4, request.Info.SourcePort);
LdnProxySocket socket = new LdnProxySocket(AddressFamily, SocketType, ProtocolType, _proxy).AsAccepted(remoteEndpoint);
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy socket accepted {remoteEndpoint} on {LocalEndPoint}");
LdnProxySocket socket = new LdnProxySocket(AddressFamily, SocketType, ProtocolType, _proxy)
.AsAccepted((IPEndPoint)LocalEndPoint, remoteEndpoint);
lock (_listenSockets)
{
@ -247,6 +272,18 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
}
}
private bool MatchesLocalEndpoint(IPEndPoint endpoint)
{
if (LocalEndPoint is not IPEndPoint localEndpoint || endpoint.Port != localEndpoint.Port)
{
return false;
}
return localEndpoint.Address.Equals(IPAddress.Any) ||
localEndpoint.Address.Equals(IPAddress.IPv6Any) ||
endpoint.Address.Equals(localEndpoint.Address);
}
public void Bind(EndPoint localEP)
{
ArgumentNullException.ThrowIfNull(localEP);
@ -269,6 +306,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
public void Close()
{
LogCloseSummary("close");
_closed = true;
_proxy.UnregisterSocket(this);
@ -291,7 +330,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
public void Connect(EndPoint remoteEP)
{
if (_isListening || !IsBound)
if (_isListening)
{
throw new InvalidOperationException();
}
@ -303,6 +342,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
IPEndPoint localEp = EnsureLocalEndpoint(true);
RemoteEndPoint = (IPEndPoint)remoteEP;
_connecting = true;
_proxy.RequestConnection(localEp, (IPEndPoint)remoteEP, ProtocolType);
@ -331,29 +371,44 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
_connecting = false;
if (_connectResponse.Info.SourceIpV4 != 0)
_connectResponse = obj;
if (obj.Info.SourceIpV4 != 0)
{
IPEndPoint remoteEp = GetEndpoint(obj.Info.SourceIpV4, obj.Info.SourcePort);
RemoteEndPoint = remoteEp;
Connected = true;
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy socket connected to {RemoteEndPoint} from {LocalEndPoint}");
}
else
{
// Connection failed
SignalError(WsaError.WSAECONNREFUSED);
Logger.Warning?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy socket connection refused from {LocalEndPoint}");
}
_connectEvent.Set();
}
public void Disconnect(bool reuseSocket)
{
if (Connected)
{
IPEndPoint localEndpoint = LocalEndPoint as IPEndPoint;
IPEndPoint remoteEndpoint = RemoteEndPoint as IPEndPoint;
LogCloseSummary("local disconnect");
ConnectionEnded();
// The other side needs to be notified that connection ended.
_proxy.EndConnection(LocalEndPoint as IPEndPoint, RemoteEndPoint as IPEndPoint, ProtocolType);
if (localEndpoint != null && remoteEndpoint != null)
{
_proxy.EndConnection(localEndpoint, remoteEndpoint, ProtocolType);
}
}
}
@ -375,6 +430,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
if (_socketOptions.TryGetValue(optionName, out int result))
{
if (optionName == SocketOptionName.Error)
{
lock (_errors)
{
result = _errors.Count > 0 ? _errors.Dequeue() : 0;
}
}
byte[] data = BitConverter.GetBytes(result);
Array.Copy(data, 0, optionValue, 0, Math.Min(data.Length, optionValue.Length));
}
@ -401,12 +464,22 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
_connectRequests.Enqueue(obj);
}
_connectEvent.Set();
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy socket queued connect request on {LocalEndPoint}");
_acceptEvent.Set();
}
public void HandleDisconnect(ProxyDisconnectMessage message)
{
Disconnect(false);
_readShutdown = true;
Logger.Debug?.PrintMsg(LogClass.ServiceLdn, $"LDN proxy socket disconnected by peer on {LocalEndPoint}");
LogCloseSummary("peer disconnect");
ConnectionEnded();
_receiveEvent.Set();
_acceptEvent.Set();
_connectEvent.Set();
}
public int Receive(Span<byte> buffer)
@ -477,7 +550,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
}
else
{
throw new SocketException((int)WsaError.WSAETIMEDOUT);
_receiveWouldBlockCount++;
throw new SocketException((int)WsaError.WSAEWOULDBLOCK);
}
}
}
@ -532,7 +606,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
}
else
{
socketError = SocketError.TimedOut;
_receiveWouldBlockCount++;
socketError = SocketError.WouldBlock;
return -1;
}
}
@ -700,7 +775,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
throw new NotSupportedException();
}
return _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType);
int sent = _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType);
_bytesSent += sent;
_sentPreview ??= FormatPayloadPreview(buffer);
return sent;
}
public int SendTo(ReadOnlySpan<byte> buffer, SocketFlags flags, out SocketError socketError, EndPoint remoteEP)
@ -722,7 +801,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
socketError = SocketError.Success;
return _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType);
int sent = _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType);
_bytesSent += sent;
_sentPreview ??= FormatPayloadPreview(buffer);
return sent;
}
public bool Poll(int microSeconds, SelectMode mode)
@ -774,9 +857,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
case SocketShutdown.Both:
_readShutdown = true;
// _writeShutdown = true;
_receiveEvent.Set();
break;
case SocketShutdown.Receive:
_readShutdown = true;
_receiveEvent.Set();
break;
case SocketShutdown.Send:
// _writeShutdown = true;
@ -786,12 +871,41 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
public void ProxyDestroyed()
{
// Do nothing, for now. Will likely be more useful with TCP.
_closed = true;
_readShutdown = true;
ConnectionEnded();
_receiveEvent.Set();
_acceptEvent.Set();
_connectEvent.Set();
}
private void LogCloseSummary(string reason)
{
if (_closeSummaryLogged || (_bytesReceived == 0 && _bytesSent == 0 && _receiveWouldBlockCount == 0))
{
return;
}
_closeSummaryLogged = true;
Logger.Info?.PrintMsg(
LogClass.ServiceLdn,
$"LDN proxy socket closed ({reason}): local={LocalEndPoint}, remote={RemoteEndPoint}, sent={_bytesSent} bytes, received={_bytesReceived} bytes, receiveWouldBlock={_receiveWouldBlockCount}, sentPreview={_sentPreview ?? "none"}, receivedPreview={_receivedPreview ?? "none"}");
}
private static string FormatPayloadPreview(ReadOnlySpan<byte> data)
{
const int PreviewLength = 32;
ReadOnlySpan<byte> preview = data[..Math.Min(data.Length, PreviewLength)];
return Convert.ToHexString(preview);
}
public void Dispose()
{
LogCloseSummary("dispose");
}
}
}

View file

@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Services.Mii.Types;
using System;
@ -34,7 +35,11 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
{
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
context.ResponseData.Write(GetCount(flag));
uint count = GetCount(flag);
context.ResponseData.Write(count);
Logger.NetLog?.PrintMsg(LogClass.ServiceMii, $"GetCount: flag={flag}, count={count}");
return ResultCode.Success;
}
@ -57,6 +62,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
Logger.NetLog?.PrintMsg(LogClass.ServiceMii, $"Get: flag={flag}, count={count}, result={result}");
return result;
}
@ -78,6 +85,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
Logger.NetLog?.PrintMsg(LogClass.ServiceMii, $"Get1: flag={flag}, count={count}, result={result}");
return result;
}
@ -141,6 +150,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
Logger.NetLog?.PrintMsg(LogClass.ServiceMii, $"Get2: flag={flag}, count={count}, result={result}");
return result;
}
@ -162,6 +173,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
Logger.NetLog?.PrintMsg(LogClass.ServiceMii, $"Get3: flag={flag}, count={count}, result={result}");
return result;
}

View file

@ -172,6 +172,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
catch (InvalidOperationException exception)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket connection failed: {exception.Message}");
return LinuxError.EINVAL;
}
}
public void Disconnect()
@ -449,6 +455,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
Socket.SetSocketOption(level, SocketOptionName.Linger, new LingerOption(value != 0, value2));
}
else if (level == SocketOptionLevel.Socket && (option == BsdSocketOption.SoRcvTimeo || option == BsdSocketOption.SoSndTimeo))
{
Socket.SetSocketOption(level, optionName, ConvertTimeValToMilliseconds(optionValue));
}
else
{
Socket.SetSocketOption(level, optionName, value);
@ -467,6 +477,36 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
}
private static int ConvertTimeValToMilliseconds(ReadOnlySpan<byte> optionValue)
{
long seconds;
long microseconds;
if (optionValue.Length >= 16)
{
seconds = MemoryMarshal.Read<long>(optionValue);
microseconds = MemoryMarshal.Read<long>(optionValue[8..]);
}
else if (optionValue.Length >= 8)
{
seconds = MemoryMarshal.Read<int>(optionValue);
microseconds = MemoryMarshal.Read<int>(optionValue[4..]);
}
else
{
return optionValue.Length >= 4 ? MemoryMarshal.Read<int>(optionValue) : MemoryMarshal.Read<byte>(optionValue);
}
if (seconds <= 0 && microseconds <= 0)
{
return 0;
}
long milliseconds = (seconds * 1000) + ((microseconds + 999) / 1000);
return (int)Math.Clamp(milliseconds, 1, int.MaxValue);
}
public LinuxError Read(out int readSize, Span<byte> buffer)
{
return Receive(out readSize, buffer, BsdSocketFlags.None);