Improve SocketOption handling (#2946)

This commit is contained in:
gdkchan 2021-12-29 11:04:38 -03:00 committed by GitHub
parent 322c14ee31
commit 8544b1445b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 221 additions and 80 deletions

View File

@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
[Service("bsd:u", false)]
class IClient : IpcService
{
private static Dictionary<WsaError, LinuxError> _errorMap = new Dictionary<WsaError, LinuxError>
private static readonly Dictionary<WsaError, LinuxError> _errorMap = new()
{
// WSAEINTR
{WsaError.WSAEINTR, LinuxError.EINTR},
@ -97,6 +97,50 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{0, 0}
};
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _soSocketOptionMap = new()
{
{ BsdSocketOption.SoDebug, SocketOptionName.Debug },
{ BsdSocketOption.SoReuseAddr, SocketOptionName.ReuseAddress },
{ BsdSocketOption.SoKeepAlive, SocketOptionName.KeepAlive },
{ BsdSocketOption.SoDontRoute, SocketOptionName.DontRoute },
{ BsdSocketOption.SoBroadcast, SocketOptionName.Broadcast },
{ BsdSocketOption.SoUseLoopBack, SocketOptionName.UseLoopback },
{ BsdSocketOption.SoLinger, SocketOptionName.Linger },
{ BsdSocketOption.SoOobInline, SocketOptionName.OutOfBandInline },
{ BsdSocketOption.SoReusePort, SocketOptionName.ReuseAddress },
{ BsdSocketOption.SoSndBuf, SocketOptionName.SendBuffer },
{ BsdSocketOption.SoRcvBuf, SocketOptionName.ReceiveBuffer },
{ BsdSocketOption.SoSndLoWat, SocketOptionName.SendLowWater },
{ BsdSocketOption.SoRcvLoWat, SocketOptionName.ReceiveLowWater },
{ BsdSocketOption.SoSndTimeo, SocketOptionName.SendTimeout },
{ BsdSocketOption.SoRcvTimeo, SocketOptionName.ReceiveTimeout },
{ BsdSocketOption.SoError, SocketOptionName.Error },
{ BsdSocketOption.SoType, SocketOptionName.Type }
};
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _ipSocketOptionMap = new()
{
{ BsdSocketOption.IpOptions, SocketOptionName.IPOptions },
{ BsdSocketOption.IpHdrIncl, SocketOptionName.HeaderIncluded },
{ BsdSocketOption.IpTtl, SocketOptionName.IpTimeToLive },
{ BsdSocketOption.IpMulticastIf, SocketOptionName.MulticastInterface },
{ BsdSocketOption.IpMulticastTtl, SocketOptionName.MulticastTimeToLive },
{ BsdSocketOption.IpMulticastLoop, SocketOptionName.MulticastLoopback },
{ BsdSocketOption.IpAddMembership, SocketOptionName.AddMembership },
{ BsdSocketOption.IpDropMembership, SocketOptionName.DropMembership },
{ BsdSocketOption.IpDontFrag, SocketOptionName.DontFragment },
{ BsdSocketOption.IpAddSourceMembership, SocketOptionName.AddSourceMembership },
{ BsdSocketOption.IpDropSourceMembership, SocketOptionName.DropSourceMembership }
};
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _tcpSocketOptionMap = new()
{
{ BsdSocketOption.TcpNoDelay, SocketOptionName.NoDelay },
{ BsdSocketOption.TcpKeepIdle, SocketOptionName.TcpKeepAliveTime },
{ BsdSocketOption.TcpKeepIntvl, SocketOptionName.TcpKeepAliveInterval },
{ BsdSocketOption.TcpKeepCnt, SocketOptionName.TcpKeepAliveRetryCount }
};
private bool _isPrivileged;
private List<BsdSocket> _sockets = new List<BsdSocket>();
@ -118,13 +162,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
private static SocketFlags ConvertBsdSocketFlags(BsdSocketFlags bsdSocketFlags)
{
BsdSocketFlags SupportedFlags =
BsdSocketFlags.Oob |
BsdSocketFlags.Peek |
BsdSocketFlags.DontRoute |
BsdSocketFlags.Trunc |
BsdSocketFlags.CTrunc;
SocketFlags socketFlags = SocketFlags.None;
if (bsdSocketFlags.HasFlag(BsdSocketFlags.Oob))
@ -166,6 +203,25 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return socketFlags;
}
private static bool TryConvertSocketOption(BsdSocketOption option, SocketOptionLevel level, out SocketOptionName name)
{
var table = level switch
{
SocketOptionLevel.Socket => _soSocketOptionMap,
SocketOptionLevel.IP => _ipSocketOptionMap,
SocketOptionLevel.Tcp => _tcpSocketOptionMap,
_ => null
};
if (table == null)
{
name = default;
return false;
}
return table.TryGetValue(option, out name);
}
private ResultCode WriteWinSock2Error(ServiceCtx context, WsaError errorCode)
{
return WriteBsdResult(context, -1, ConvertError(errorCode));
@ -820,9 +876,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
// GetSockOpt(u32 socket, u32 level, u32 option_name) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>)
public ResultCode GetSockOpt(ServiceCtx context)
{
int socketFd = context.RequestData.ReadInt32();
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
SocketOptionName optionName = (SocketOptionName)context.RequestData.ReadInt32();
int socketFd = context.RequestData.ReadInt32();
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32();
(ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22();
@ -831,7 +887,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if (socket != null)
{
errno = HandleGetSocketOption(context, socket, optionName, level, bufferPosition, bufferSize);
errno = HandleGetSocketOption(context, socket, option, level, bufferPosition, bufferSize);
}
return WriteBsdResult(context, 0, errno);
@ -936,45 +992,26 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
private static LinuxError HandleGetSocketOption(
ServiceCtx context,
BsdSocket socket,
SocketOptionName optionName,
BsdSocketOption option,
SocketOptionLevel level,
ulong optionValuePosition,
ulong optionValueSize)
{
try
{
if (!TryConvertSocketOption(option, level, out SocketOptionName optionName))
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt Option: {option} Level: {level}");
return LinuxError.EOPNOTSUPP;
}
byte[] optionValue = new byte[optionValueSize];
switch (optionName)
{
case SocketOptionName.Broadcast:
case SocketOptionName.DontLinger:
case SocketOptionName.Debug:
case SocketOptionName.Error:
case SocketOptionName.KeepAlive:
case SocketOptionName.OutOfBandInline:
case SocketOptionName.ReceiveBuffer:
case SocketOptionName.ReceiveTimeout:
case SocketOptionName.SendBuffer:
case SocketOptionName.SendTimeout:
case SocketOptionName.Type:
case SocketOptionName.Linger:
socket.Handle.GetSocketOption(level, optionName, optionValue);
context.Memory.Write(optionValuePosition, optionValue);
socket.Handle.GetSocketOption(level, optionName, optionValue);
context.Memory.Write(optionValuePosition, optionValue);
return LinuxError.SUCCESS;
case (SocketOptionName)0x200:
socket.Handle.GetSocketOption(level, SocketOptionName.ReuseAddress, optionValue);
context.Memory.Write(optionValuePosition, optionValue);
return LinuxError.SUCCESS;
default:
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt OptionName: {optionName}");
return LinuxError.EOPNOTSUPP;
}
return LinuxError.SUCCESS;
}
catch (SocketException exception)
{
@ -985,47 +1022,34 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
private static LinuxError HandleSetSocketOption(
ServiceCtx context,
BsdSocket socket,
SocketOptionName optionName,
BsdSocketOption option,
SocketOptionLevel level,
ulong optionValuePosition,
ulong optionValueSize)
{
try
{
switch (optionName)
if (!TryConvertSocketOption(option, level, out SocketOptionName optionName))
{
case SocketOptionName.Broadcast:
case SocketOptionName.DontLinger:
case SocketOptionName.Debug:
case SocketOptionName.Error:
case SocketOptionName.KeepAlive:
case SocketOptionName.OutOfBandInline:
case SocketOptionName.ReceiveBuffer:
case SocketOptionName.ReceiveTimeout:
case SocketOptionName.SendBuffer:
case SocketOptionName.SendTimeout:
case SocketOptionName.Type:
case SocketOptionName.ReuseAddress:
socket.Handle.SetSocketOption(level, optionName, context.Memory.Read<int>((ulong)optionValuePosition));
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt Option: {option} Level: {level}");
return LinuxError.SUCCESS;
case (SocketOptionName)0x200:
socket.Handle.SetSocketOption(level, SocketOptionName.ReuseAddress, context.Memory.Read<int>((ulong)optionValuePosition));
return LinuxError.SUCCESS;
case SocketOptionName.Linger:
socket.Handle.SetSocketOption(level, SocketOptionName.Linger,
new LingerOption(context.Memory.Read<int>((ulong)optionValuePosition) != 0, context.Memory.Read<int>((ulong)optionValuePosition + 4)));
return LinuxError.SUCCESS;
default:
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {optionName}");
return LinuxError.EOPNOTSUPP;
return LinuxError.EOPNOTSUPP;
}
int value = context.Memory.Read<int>((ulong)optionValuePosition);
if (option == BsdSocketOption.SoLinger)
{
int value2 = context.Memory.Read<int>((ulong)optionValuePosition + 4);
socket.Handle.SetSocketOption(level, SocketOptionName.Linger, new LingerOption(value != 0, value2));
}
else
{
socket.Handle.SetSocketOption(level, optionName, value);
}
return LinuxError.SUCCESS;
}
catch (SocketException exception)
{
@ -1037,9 +1061,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
// SetSockOpt(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0> option_value) -> (i32 ret, u32 bsd_errno)
public ResultCode SetSockOpt(ServiceCtx context)
{
int socketFd = context.RequestData.ReadInt32();
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
SocketOptionName optionName = (SocketOptionName)context.RequestData.ReadInt32();
int socketFd = context.RequestData.ReadInt32();
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32();
(ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21();
@ -1048,7 +1072,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if (socket != null)
{
errno = HandleSetSocketOption(context, socket, optionName, level, bufferPos, bufferSize);
errno = HandleSetSocketOption(context, socket, option, level, bufferPos, bufferSize);
}
return WriteBsdResult(context, 0, errno);

View File

@ -1,5 +1,3 @@
using System.Net.Sockets;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
enum BsdSocketFlags

View File

@ -0,0 +1,119 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
enum BsdSocketOption
{
SoDebug = 0x1,
SoAcceptConn = 0x2,
SoReuseAddr = 0x4,
SoKeepAlive = 0x8,
SoDontRoute = 0x10,
SoBroadcast = 0x20,
SoUseLoopBack = 0x40,
SoLinger = 0x80,
SoOobInline = 0x100,
SoReusePort = 0x200,
SoTimestamp = 0x400,
SoNoSigpipe = 0x800,
SoAcceptFilter = 0x1000,
SoBinTime = 0x2000,
SoNoOffload = 0x4000,
SoNoDdp = 0x8000,
SoReusePortLb = 0x10000,
SoRError = 0x20000,
SoSndBuf = 0x1001,
SoRcvBuf = 0x1002,
SoSndLoWat = 0x1003,
SoRcvLoWat = 0x1004,
SoSndTimeo = 0x1005,
SoRcvTimeo = 0x1006,
SoError = 0x1007,
SoType = 0x1008,
SoLabel = 0x1009,
SoPeerLabel = 0x1010,
SoListenQLimit = 0x1011,
SoListenQLen = 0x1012,
SoListenIncQLen = 0x1013,
SoSetFib = 0x1014,
SoUserCookie = 0x1015,
SoProtocol = 0x1016,
SoTsClock = 0x1017,
SoMaxPacingRate = 0x1018,
SoDomain = 0x1019,
IpOptions = 1,
IpHdrIncl = 2,
IpTos = 3,
IpTtl = 4,
IpRecvOpts = 5,
IpRecvRetOpts = 6,
IpRecvDstAddr = 7,
IpRetOpts = 8,
IpMulticastIf = 9,
IpMulticastTtl = 10,
IpMulticastLoop = 11,
IpAddMembership = 12,
IpDropMembership = 13,
IpMulticastVif = 14,
IpRsvpOn = 15,
IpRsvpOff = 16,
IpRsvpVifOn = 17,
IpRsvpVifOff = 18,
IpPortRange = 19,
IpRecvIf = 20,
IpIpsecPolicy = 21,
IpOnesBcast = 23,
IpBindany = 24,
IpBindMulti = 25,
IpRssListenBucket = 26,
IpOrigDstAddr = 27,
IpFwTableAdd = 40,
IpFwTableDel = 41,
IpFwTableFlush = 42,
IpFwTableGetSize = 43,
IpFwTableList = 44,
IpFw3 = 48,
IpDummyNet3 = 49,
IpFwAdd = 50,
IpFwDel = 51,
IpFwFlush = 52,
IpFwZero = 53,
IpFwGet = 54,
IpFwResetLog = 55,
IpFwNatCfg = 56,
IpFwNatDel = 57,
IpFwNatGetConfig = 58,
IpFwNatGetLog = 59,
IpDummyNetConfigure = 60,
IpDummyNetDel = 61,
IpDummyNetFlush = 62,
IpDummyNetGet = 64,
IpRecvTtl = 65,
IpMinTtl = 66,
IpDontFrag = 67,
IpRecvTos = 68,
IpAddSourceMembership = 70,
IpDropSourceMembership = 71,
IpBlockSource = 72,
IpUnblockSource = 73,
TcpNoDelay = 1,
TcpMaxSeg = 2,
TcpNoPush = 4,
TcpNoOpt = 8,
TcpMd5Sig = 16,
TcpInfo = 32,
TcpCongestion = 64,
TcpKeepInit = 128,
TcpKeepIdle = 256,
TcpKeepIntvl = 512,
TcpKeepCnt = 1024
}
}