diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs index dfc2a672..2d8fce1d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs @@ -299,11 +299,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { try { + LinuxError result = WinSockHelper.ValidateSocketOption(option, level, write: false); + + if (result != LinuxError.SUCCESS) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Invalid GetSockOpt Option: {option} Level: {level}"); + + return result; + } + if (!WinSockHelper.TryConvertSocketOption(option, level, out SocketOptionName optionName)) { Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt Option: {option} Level: {level}"); + optionValue.Clear(); - return LinuxError.EOPNOTSUPP; + return LinuxError.SUCCESS; } byte[] tempOptionValue = new byte[optionValue.Length]; @@ -324,11 +334,20 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { try { + LinuxError result = WinSockHelper.ValidateSocketOption(option, level, write: true); + + if (result != LinuxError.SUCCESS) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Invalid SetSockOpt Option: {option} Level: {level}"); + + return result; + } + if (!WinSockHelper.TryConvertSocketOption(option, level, out SocketOptionName optionName)) { Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt Option: {option} Level: {level}"); - return LinuxError.EOPNOTSUPP; + return LinuxError.SUCCESS; } int value = optionValue.Length >= 4 ? MemoryMarshal.Read(optionValue) : MemoryMarshal.Read(optionValue); diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs index 9df18023..e2ef75f8 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs @@ -183,6 +183,104 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { BsdSocketOption.TcpKeepCnt, SocketOptionName.TcpKeepAliveRetryCount }, }; + [Flags] + private enum OptionDir + { + Get = 1 << 0, + Set = 1 << 1, + GetSet = Get | Set, + } + + private static readonly Dictionary _validSoSocketOptionMap = new() + { + { BsdSocketOption.SoDebug, OptionDir.GetSet }, + { BsdSocketOption.SoAcceptConn, OptionDir.Get }, + { BsdSocketOption.SoReuseAddr, OptionDir.GetSet }, + { BsdSocketOption.SoKeepAlive, OptionDir.GetSet }, + { BsdSocketOption.SoDontRoute, OptionDir.GetSet }, + { BsdSocketOption.SoBroadcast, OptionDir.GetSet }, + { BsdSocketOption.SoUseLoopBack, OptionDir.GetSet }, + { BsdSocketOption.SoLinger, OptionDir.GetSet }, + { BsdSocketOption.SoOobInline, OptionDir.GetSet }, + { BsdSocketOption.SoReusePort, OptionDir.GetSet }, + { BsdSocketOption.SoTimestamp, OptionDir.GetSet }, + { BsdSocketOption.SoNoSigpipe, OptionDir.GetSet }, + { BsdSocketOption.SoAcceptFilter, OptionDir.GetSet }, + { BsdSocketOption.SoSndBuf, OptionDir.GetSet }, + { BsdSocketOption.SoRcvBuf, OptionDir.GetSet }, + { BsdSocketOption.SoSndLoWat, OptionDir.GetSet }, + { BsdSocketOption.SoRcvLoWat, OptionDir.GetSet }, + { BsdSocketOption.SoSndTimeo, OptionDir.GetSet }, + { BsdSocketOption.SoRcvTimeo, OptionDir.GetSet }, + { BsdSocketOption.SoError, OptionDir.Get }, + { BsdSocketOption.SoType, OptionDir.Get }, + { BsdSocketOption.SoLabel, OptionDir.Get }, + { BsdSocketOption.SoPeerLabel, OptionDir.Get }, + { BsdSocketOption.SoListenQLimit, OptionDir.Get }, + { BsdSocketOption.SoListenQLen, OptionDir.Get }, + { BsdSocketOption.SoListenIncQLen, OptionDir.Get }, + { BsdSocketOption.SoSetFib, OptionDir.Set }, + { BsdSocketOption.SoUserCookie, OptionDir.Set }, + { BsdSocketOption.SoProtocol, OptionDir.Get }, + { BsdSocketOption.SoBinTime, OptionDir.GetSet }, + { BsdSocketOption.SoNoOffload, OptionDir.Set }, + { BsdSocketOption.SoNoDdp, OptionDir.Set }, + { BsdSocketOption.SoReusePortLb, OptionDir.GetSet }, + }; + + private static readonly Dictionary _validIpSocketOptionMap = new() + { + { BsdSocketOption.IpOptions, OptionDir.GetSet }, + { BsdSocketOption.IpHdrIncl, OptionDir.GetSet }, + { BsdSocketOption.IpTos, OptionDir.GetSet }, + { BsdSocketOption.IpTtl, OptionDir.GetSet }, + { BsdSocketOption.IpRecvOpts, OptionDir.GetSet }, + { BsdSocketOption.IpRecvRetOpts, OptionDir.GetSet }, + { BsdSocketOption.IpRecvDstAddr, OptionDir.GetSet }, + { BsdSocketOption.IpRetOpts, OptionDir.GetSet }, + { BsdSocketOption.IpMulticastIf, OptionDir.GetSet }, + { BsdSocketOption.IpMulticastTtl, OptionDir.GetSet }, + { BsdSocketOption.IpMulticastLoop, OptionDir.GetSet }, + { BsdSocketOption.IpAddMembership, OptionDir.GetSet }, + { BsdSocketOption.IpDropMembership, OptionDir.GetSet }, + { BsdSocketOption.IpMulticastVif, OptionDir.GetSet }, + { BsdSocketOption.IpRsvpOn, OptionDir.GetSet }, + { BsdSocketOption.IpRsvpOff, OptionDir.GetSet }, + { BsdSocketOption.IpRsvpVifOn, OptionDir.GetSet }, + { BsdSocketOption.IpRsvpVifOff, OptionDir.GetSet }, + { BsdSocketOption.IpPortRange, OptionDir.GetSet }, + { BsdSocketOption.IpRecvIf, OptionDir.GetSet }, + { BsdSocketOption.IpIpsecPolicy, OptionDir.GetSet }, + { BsdSocketOption.IpOnesBcast, OptionDir.GetSet }, + { BsdSocketOption.IpBindany, OptionDir.GetSet }, + { BsdSocketOption.IpBindMulti, OptionDir.GetSet }, + { BsdSocketOption.IpRssListenBucket, OptionDir.GetSet }, + { BsdSocketOption.IpOrigDstAddr, OptionDir.GetSet }, + { BsdSocketOption.IpRecvTtl, OptionDir.GetSet }, + { BsdSocketOption.IpMinTtl, OptionDir.GetSet }, + { BsdSocketOption.IpDontFrag, OptionDir.GetSet }, + { BsdSocketOption.IpRecvTos, OptionDir.GetSet }, + { BsdSocketOption.IpAddSourceMembership, OptionDir.GetSet }, + { BsdSocketOption.IpDropSourceMembership, OptionDir.GetSet }, + { BsdSocketOption.IpBlockSource, OptionDir.GetSet }, + { BsdSocketOption.IpUnblockSource, OptionDir.GetSet }, + }; + + private static readonly Dictionary _validTcpSocketOptionMap = new() + { + { BsdSocketOption.TcpNoDelay, OptionDir.GetSet }, + { BsdSocketOption.TcpMaxSeg, OptionDir.GetSet }, + { BsdSocketOption.TcpNoPush, OptionDir.GetSet }, + { BsdSocketOption.TcpNoOpt, OptionDir.GetSet }, + { BsdSocketOption.TcpMd5Sig, OptionDir.GetSet }, + { BsdSocketOption.TcpInfo, OptionDir.GetSet }, + { BsdSocketOption.TcpCongestion, OptionDir.GetSet }, + { BsdSocketOption.TcpKeepInit, OptionDir.GetSet }, + { BsdSocketOption.TcpKeepIdle, OptionDir.GetSet }, + { BsdSocketOption.TcpKeepIntvl, OptionDir.GetSet }, + { BsdSocketOption.TcpKeepCnt, OptionDir.GetSet }, + }; + public static LinuxError ConvertError(WsaError errorCode) { if (OperatingSystem.IsMacOS()) @@ -221,5 +319,29 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl return table.TryGetValue(option, out name); } + + public static LinuxError ValidateSocketOption(BsdSocketOption option, SocketOptionLevel level, bool write) + { + var table = level switch + { + SocketOptionLevel.Socket => _validSoSocketOptionMap, + SocketOptionLevel.IP => _validIpSocketOptionMap, + SocketOptionLevel.Tcp => _validTcpSocketOptionMap, + _ => null, + }; + + OptionDir dir = write ? OptionDir.Set : OptionDir.Get; + + if (table == null || !table.TryGetValue(option, out OptionDir validDir)) + { + return LinuxError.ENOPROTOOPT; + } + else if ((validDir & dir) != dir) + { + return LinuxError.EOPNOTSUPP; + } + + return LinuxError.SUCCESS; + } } }