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)] [Service("bsd:u", false)]
class IClient : IpcService class IClient : IpcService
{ {
private static Dictionary<WsaError, LinuxError> _errorMap = new Dictionary<WsaError, LinuxError> private static readonly Dictionary<WsaError, LinuxError> _errorMap = new()
{ {
// WSAEINTR // WSAEINTR
{WsaError.WSAEINTR, LinuxError.EINTR}, {WsaError.WSAEINTR, LinuxError.EINTR},
@ -97,6 +97,50 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{0, 0} {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 bool _isPrivileged;
private List<BsdSocket> _sockets = new List<BsdSocket>(); private List<BsdSocket> _sockets = new List<BsdSocket>();
@ -118,13 +162,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
private static SocketFlags ConvertBsdSocketFlags(BsdSocketFlags bsdSocketFlags) private static SocketFlags ConvertBsdSocketFlags(BsdSocketFlags bsdSocketFlags)
{ {
BsdSocketFlags SupportedFlags =
BsdSocketFlags.Oob |
BsdSocketFlags.Peek |
BsdSocketFlags.DontRoute |
BsdSocketFlags.Trunc |
BsdSocketFlags.CTrunc;
SocketFlags socketFlags = SocketFlags.None; SocketFlags socketFlags = SocketFlags.None;
if (bsdSocketFlags.HasFlag(BsdSocketFlags.Oob)) if (bsdSocketFlags.HasFlag(BsdSocketFlags.Oob))
@ -166,6 +203,25 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return socketFlags; 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) private ResultCode WriteWinSock2Error(ServiceCtx context, WsaError errorCode)
{ {
return WriteBsdResult(context, -1, ConvertError(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>) // GetSockOpt(u32 socket, u32 level, u32 option_name) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>)
public ResultCode GetSockOpt(ServiceCtx context) public ResultCode GetSockOpt(ServiceCtx context)
{ {
int socketFd = context.RequestData.ReadInt32(); int socketFd = context.RequestData.ReadInt32();
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32(); SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
SocketOptionName optionName = (SocketOptionName)context.RequestData.ReadInt32(); BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32();
(ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22(); (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22();
@ -831,7 +887,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if (socket != null) if (socket != null)
{ {
errno = HandleGetSocketOption(context, socket, optionName, level, bufferPosition, bufferSize); errno = HandleGetSocketOption(context, socket, option, level, bufferPosition, bufferSize);
} }
return WriteBsdResult(context, 0, errno); return WriteBsdResult(context, 0, errno);
@ -936,45 +992,26 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
private static LinuxError HandleGetSocketOption( private static LinuxError HandleGetSocketOption(
ServiceCtx context, ServiceCtx context,
BsdSocket socket, BsdSocket socket,
SocketOptionName optionName, BsdSocketOption option,
SocketOptionLevel level, SocketOptionLevel level,
ulong optionValuePosition, ulong optionValuePosition,
ulong optionValueSize) ulong optionValueSize)
{ {
try 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]; byte[] optionValue = new byte[optionValueSize];
switch (optionName) socket.Handle.GetSocketOption(level, optionName, optionValue);
{ context.Memory.Write(optionValuePosition, optionValue);
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);
return LinuxError.SUCCESS; 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;
}
} }
catch (SocketException exception) catch (SocketException exception)
{ {
@ -985,47 +1022,34 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
private static LinuxError HandleSetSocketOption( private static LinuxError HandleSetSocketOption(
ServiceCtx context, ServiceCtx context,
BsdSocket socket, BsdSocket socket,
SocketOptionName optionName, BsdSocketOption option,
SocketOptionLevel level, SocketOptionLevel level,
ulong optionValuePosition, ulong optionValuePosition,
ulong optionValueSize) ulong optionValueSize)
{ {
try try
{ {
switch (optionName) if (!TryConvertSocketOption(option, level, out SocketOptionName optionName))
{ {
case SocketOptionName.Broadcast: Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt Option: {option} Level: {level}");
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));
return LinuxError.SUCCESS; return LinuxError.EOPNOTSUPP;
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;
} }
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) 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) // SetSockOpt(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0> option_value) -> (i32 ret, u32 bsd_errno)
public ResultCode SetSockOpt(ServiceCtx context) public ResultCode SetSockOpt(ServiceCtx context)
{ {
int socketFd = context.RequestData.ReadInt32(); int socketFd = context.RequestData.ReadInt32();
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32(); SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
SocketOptionName optionName = (SocketOptionName)context.RequestData.ReadInt32(); BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32();
(ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21(); (ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21();
@ -1048,7 +1072,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if (socket != null) if (socket != null)
{ {
errno = HandleSetSocketOption(context, socket, optionName, level, bufferPos, bufferSize); errno = HandleSetSocketOption(context, socket, option, level, bufferPos, bufferSize);
} }
return WriteBsdResult(context, 0, errno); return WriteBsdResult(context, 0, errno);

View file

@ -1,5 +1,3 @@
using System.Net.Sockets;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
enum BsdSocketFlags 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
}
}