bsd: Revamp API and make socket abstract (#2960)
* bsd: Revamp API and make socket abstract This part of the code was really ancient and needed some love. As such this commit aims at separating the socket core logic from the IClient class and make it uses more modern APIs to read/write/parse data. * Address gdkchan's comment * Move TryConvertSocketOption to WinSockHelper * Allow reusing old fds and add missing locks around SocketInternal and ShutdownAllSockets * bsd: ton of changes - Make sockets per process - Implement eventfds - Rework Poll for support of eventfds - Handle protocol auto selection by type (used by gRPC) - Handle IPv6 socket creation * Address most of gdkchan comments * Fix inverted read logic for BSD socket read * bsd: Make Poll abstract via IBsdSocketPollManager * bsd: Improve naming of everything * Fix build issue from last commit (missed to save on VC) * Switch BsdContext registry to a concurrent dictionary * bsd: Implement socket creation flags logic and the non blocking flag * Remove unused enum from previous commit * bsd: Fix poll logic when 0 fds are present for a given poll manager and when timeout is very small (or 0) * Address gdkchan's comment
This commit is contained in:
parent
d300a5a45b
commit
366fe2dbb2
21 changed files with 1482 additions and 659 deletions
130
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs
Normal file
130
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
class EventFileDescriptor : IFileDescriptor
|
||||
{
|
||||
private ulong _value;
|
||||
private readonly EventFdFlags _flags;
|
||||
private AutoResetEvent _event;
|
||||
|
||||
private object _lock = new object();
|
||||
|
||||
public bool Blocking { get => !_flags.HasFlag(EventFdFlags.NonBlocking); set => throw new NotSupportedException(); }
|
||||
|
||||
public ManualResetEvent WriteEvent { get; }
|
||||
public ManualResetEvent ReadEvent { get; }
|
||||
|
||||
public EventFileDescriptor(ulong value, EventFdFlags flags)
|
||||
{
|
||||
_value = value;
|
||||
_flags = flags;
|
||||
_event = new AutoResetEvent(false);
|
||||
|
||||
WriteEvent = new ManualResetEvent(true);
|
||||
ReadEvent = new ManualResetEvent(true);
|
||||
}
|
||||
|
||||
public int Refcount { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_event.Dispose();
|
||||
WriteEvent.Dispose();
|
||||
ReadEvent.Dispose();
|
||||
}
|
||||
|
||||
public LinuxError Read(out int readSize, Span<byte> buffer)
|
||||
{
|
||||
if (buffer.Length < sizeof(ulong))
|
||||
{
|
||||
readSize = 0;
|
||||
|
||||
return LinuxError.EINVAL;
|
||||
}
|
||||
|
||||
ReadEvent.Reset();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
ref ulong count = ref MemoryMarshal.Cast<byte, ulong>(buffer)[0];
|
||||
|
||||
if (_value == 0)
|
||||
{
|
||||
if (Blocking)
|
||||
{
|
||||
while (_value == 0)
|
||||
{
|
||||
_event.WaitOne();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
readSize = 0;
|
||||
|
||||
return LinuxError.EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
readSize = sizeof(ulong);
|
||||
|
||||
if (_flags.HasFlag(EventFdFlags.Semaphore))
|
||||
{
|
||||
--_value;
|
||||
|
||||
count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = _value;
|
||||
|
||||
_value = 0;
|
||||
}
|
||||
|
||||
ReadEvent.Set();
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError Write(out int writeSize, ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
if (!MemoryMarshal.TryRead(buffer, out ulong count) || count == ulong.MaxValue)
|
||||
{
|
||||
writeSize = 0;
|
||||
|
||||
return LinuxError.EINVAL;
|
||||
}
|
||||
|
||||
WriteEvent.Reset();
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_value > _value + count)
|
||||
{
|
||||
if (Blocking)
|
||||
{
|
||||
_event.WaitOne();
|
||||
}
|
||||
else
|
||||
{
|
||||
writeSize = 0;
|
||||
|
||||
return LinuxError.EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
writeSize = sizeof(ulong);
|
||||
|
||||
_value += count;
|
||||
_event.Set();
|
||||
|
||||
WriteEvent.Set();
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
class EventFileDescriptorPollManager : IPollManager
|
||||
{
|
||||
private static EventFileDescriptorPollManager _instance;
|
||||
|
||||
public static EventFileDescriptorPollManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new EventFileDescriptorPollManager();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompatible(PollEvent evnt)
|
||||
{
|
||||
return evnt.FileDescriptor is EventFileDescriptor;
|
||||
}
|
||||
|
||||
public LinuxError Poll(List<PollEvent> events, int timeoutMilliseconds, out int updatedCount)
|
||||
{
|
||||
updatedCount = 0;
|
||||
|
||||
List<ManualResetEvent> waiters = new List<ManualResetEvent>();
|
||||
|
||||
for (int i = 0; i < events.Count; i++)
|
||||
{
|
||||
PollEvent evnt = events[i];
|
||||
|
||||
EventFileDescriptor socket = (EventFileDescriptor)evnt.FileDescriptor;
|
||||
|
||||
bool isValidEvent = false;
|
||||
|
||||
if (evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Input) ||
|
||||
evnt.Data.InputEvents.HasFlag(PollEventTypeMask.UrgentInput))
|
||||
{
|
||||
waiters.Add(socket.ReadEvent);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
if (evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Output))
|
||||
{
|
||||
waiters.Add(socket.WriteEvent);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
|
||||
if (!isValidEvent)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}");
|
||||
|
||||
return LinuxError.EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int index = WaitHandle.WaitAny(waiters.ToArray(), timeoutMilliseconds);
|
||||
|
||||
if (index != WaitHandle.WaitTimeout)
|
||||
{
|
||||
for (int i = 0; i < events.Count; i++)
|
||||
{
|
||||
PollEvent evnt = events[i];
|
||||
|
||||
EventFileDescriptor socket = (EventFileDescriptor)evnt.FileDescriptor;
|
||||
|
||||
if ((evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Input) ||
|
||||
evnt.Data.InputEvents.HasFlag(PollEventTypeMask.UrgentInput))
|
||||
&& socket.ReadEvent.WaitOne(0))
|
||||
{
|
||||
waiters.Add(socket.ReadEvent);
|
||||
}
|
||||
if ((evnt.Data.InputEvents.HasFlag(PollEventTypeMask.Output))
|
||||
&& socket.WriteEvent.WaitOne(0))
|
||||
{
|
||||
waiters.Add(socket.WriteEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return LinuxError.ETIMEDOUT;
|
||||
}
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
338
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs
Normal file
338
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs
Normal file
|
@ -0,0 +1,338 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
class ManagedSocket : ISocket
|
||||
{
|
||||
public int Refcount { get; set; }
|
||||
|
||||
public AddressFamily AddressFamily => Socket.AddressFamily;
|
||||
|
||||
public SocketType SocketType => Socket.SocketType;
|
||||
|
||||
public ProtocolType ProtocolType => Socket.ProtocolType;
|
||||
|
||||
public bool Blocking { get => Socket.Blocking; set => Socket.Blocking = value; }
|
||||
|
||||
public IntPtr Handle => Socket.Handle;
|
||||
|
||||
public IPEndPoint RemoteEndPoint => Socket.RemoteEndPoint as IPEndPoint;
|
||||
|
||||
public IPEndPoint LocalEndPoint => Socket.LocalEndPoint as IPEndPoint;
|
||||
|
||||
public Socket Socket { get; }
|
||||
|
||||
public ManagedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
|
||||
{
|
||||
Socket = new Socket(addressFamily, socketType, protocolType);
|
||||
Refcount = 1;
|
||||
}
|
||||
|
||||
private ManagedSocket(Socket socket)
|
||||
{
|
||||
Socket = socket;
|
||||
Refcount = 1;
|
||||
}
|
||||
|
||||
private static SocketFlags ConvertBsdSocketFlags(BsdSocketFlags bsdSocketFlags)
|
||||
{
|
||||
SocketFlags socketFlags = SocketFlags.None;
|
||||
|
||||
if (bsdSocketFlags.HasFlag(BsdSocketFlags.Oob))
|
||||
{
|
||||
socketFlags |= SocketFlags.OutOfBand;
|
||||
}
|
||||
|
||||
if (bsdSocketFlags.HasFlag(BsdSocketFlags.Peek))
|
||||
{
|
||||
socketFlags |= SocketFlags.Peek;
|
||||
}
|
||||
|
||||
if (bsdSocketFlags.HasFlag(BsdSocketFlags.DontRoute))
|
||||
{
|
||||
socketFlags |= SocketFlags.DontRoute;
|
||||
}
|
||||
|
||||
if (bsdSocketFlags.HasFlag(BsdSocketFlags.Trunc))
|
||||
{
|
||||
socketFlags |= SocketFlags.Truncated;
|
||||
}
|
||||
|
||||
if (bsdSocketFlags.HasFlag(BsdSocketFlags.CTrunc))
|
||||
{
|
||||
socketFlags |= SocketFlags.ControlDataTruncated;
|
||||
}
|
||||
|
||||
bsdSocketFlags &= ~(BsdSocketFlags.Oob |
|
||||
BsdSocketFlags.Peek |
|
||||
BsdSocketFlags.DontRoute |
|
||||
BsdSocketFlags.DontWait |
|
||||
BsdSocketFlags.Trunc |
|
||||
BsdSocketFlags.CTrunc);
|
||||
|
||||
if (bsdSocketFlags != BsdSocketFlags.None)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported socket flags: {bsdSocketFlags}");
|
||||
}
|
||||
|
||||
return socketFlags;
|
||||
}
|
||||
|
||||
public LinuxError Accept(out ISocket newSocket)
|
||||
{
|
||||
try
|
||||
{
|
||||
newSocket = new ManagedSocket(Socket.Accept());
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
newSocket = null;
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError Bind(IPEndPoint localEndPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket.Bind(localEndPoint);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Socket.Close();
|
||||
}
|
||||
|
||||
public LinuxError Connect(IPEndPoint remoteEndPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket.Connect(remoteEndPoint);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
if (!Blocking && exception.ErrorCode == (int)WsaError.WSAEWOULDBLOCK)
|
||||
{
|
||||
return LinuxError.EINPROGRESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
Socket.Disconnect(true);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Socket.Close();
|
||||
Socket.Dispose();
|
||||
}
|
||||
|
||||
public LinuxError Listen(int backlog)
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket.Listen(backlog);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Poll(int microSeconds, SelectMode mode)
|
||||
{
|
||||
return Socket.Poll(microSeconds, mode);
|
||||
}
|
||||
|
||||
public LinuxError Shutdown(BsdSocketShutdownFlags how)
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket.Shutdown((SocketShutdown)how);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
|
||||
{
|
||||
try
|
||||
{
|
||||
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
receiveSize = -1;
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError ReceiveFrom(out int receiveSize, Span<byte> buffer, int size, BsdSocketFlags flags, out IPEndPoint remoteEndPoint)
|
||||
{
|
||||
remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
|
||||
LinuxError result;
|
||||
|
||||
bool shouldBlockAfterOperation = false;
|
||||
|
||||
try
|
||||
{
|
||||
EndPoint temp = new IPEndPoint(IPAddress.Any, 0);
|
||||
|
||||
if (Blocking && flags.HasFlag(BsdSocketFlags.DontWait))
|
||||
{
|
||||
Blocking = false;
|
||||
shouldBlockAfterOperation = true;
|
||||
}
|
||||
|
||||
receiveSize = Socket.ReceiveFrom(buffer[..size], ConvertBsdSocketFlags(flags), ref temp);
|
||||
|
||||
remoteEndPoint = (IPEndPoint)temp;
|
||||
result = LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
receiveSize = -1;
|
||||
|
||||
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
|
||||
if (shouldBlockAfterOperation)
|
||||
{
|
||||
Blocking = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public LinuxError Send(out int sendSize, ReadOnlySpan<byte> buffer, BsdSocketFlags flags)
|
||||
{
|
||||
try
|
||||
{
|
||||
sendSize = Socket.Send(buffer, ConvertBsdSocketFlags(flags));
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
sendSize = -1;
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError SendTo(out int sendSize, ReadOnlySpan<byte> buffer, int size, BsdSocketFlags flags, IPEndPoint remoteEndPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
sendSize = Socket.SendTo(buffer[..size], ConvertBsdSocketFlags(flags), remoteEndPoint);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
sendSize = -1;
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError GetSocketOption(BsdSocketOption option, SocketOptionLevel level, Span<byte> optionValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!WinSockHelper.TryConvertSocketOption(option, level, out SocketOptionName optionName))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt Option: {option} Level: {level}");
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
|
||||
byte[] tempOptionValue = new byte[optionValue.Length];
|
||||
|
||||
Socket.GetSocketOption(level, optionName, tempOptionValue);
|
||||
|
||||
tempOptionValue.AsSpan().CopyTo(optionValue);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError SetSocketOption(BsdSocketOption option, SocketOptionLevel level, ReadOnlySpan<byte> optionValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!WinSockHelper.TryConvertSocketOption(option, level, out SocketOptionName optionName))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt Option: {option} Level: {level}");
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int value = MemoryMarshal.Read<int>(optionValue);
|
||||
|
||||
if (option == BsdSocketOption.SoLinger)
|
||||
{
|
||||
int value2 = MemoryMarshal.Read<int>(optionValue[4..]);
|
||||
|
||||
Socket.SetSocketOption(level, SocketOptionName.Linger, new LingerOption(value != 0, value2));
|
||||
}
|
||||
else
|
||||
{
|
||||
Socket.SetSocketOption(level, optionName, value);
|
||||
}
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError Read(out int readSize, Span<byte> buffer)
|
||||
{
|
||||
return Receive(out readSize, buffer, BsdSocketFlags.None);
|
||||
}
|
||||
|
||||
public LinuxError Write(out int writeSize, ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
return Send(out writeSize, buffer, BsdSocketFlags.None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
class ManagedSocketPollManager : IPollManager
|
||||
{
|
||||
private static ManagedSocketPollManager _instance;
|
||||
|
||||
public static ManagedSocketPollManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new ManagedSocketPollManager();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompatible(PollEvent evnt)
|
||||
{
|
||||
return evnt.FileDescriptor is ManagedSocket;
|
||||
}
|
||||
|
||||
public LinuxError Poll(List<PollEvent> events, int timeoutMilliseconds, out int updatedCount)
|
||||
{
|
||||
List<Socket> readEvents = new List<Socket>();
|
||||
List<Socket> writeEvents = new List<Socket>();
|
||||
List<Socket> errorEvents = new List<Socket>();
|
||||
|
||||
updatedCount = 0;
|
||||
|
||||
foreach (PollEvent evnt in events)
|
||||
{
|
||||
ManagedSocket socket = (ManagedSocket)evnt.FileDescriptor;
|
||||
|
||||
bool isValidEvent = false;
|
||||
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
|
||||
{
|
||||
readEvents.Add(socket.Socket);
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0)
|
||||
{
|
||||
readEvents.Add(socket.Socket);
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0)
|
||||
{
|
||||
writeEvents.Add(socket.Socket);
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.Error) != 0)
|
||||
{
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
|
||||
if (!isValidEvent)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}");
|
||||
return LinuxError.EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int actualTimeoutMicroseconds = timeoutMilliseconds == -1 ? -1 : timeoutMilliseconds * 1000;
|
||||
|
||||
Socket.Select(readEvents, writeEvents, errorEvents, actualTimeoutMicroseconds);
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
|
||||
foreach (PollEvent evnt in events)
|
||||
{
|
||||
Socket socket = ((ManagedSocket)evnt.FileDescriptor).Socket;
|
||||
|
||||
PollEventTypeMask outputEvents = 0;
|
||||
|
||||
if (errorEvents.Contains(socket))
|
||||
{
|
||||
outputEvents |= PollEventTypeMask.Error;
|
||||
|
||||
if (!socket.Connected || !socket.IsBound)
|
||||
{
|
||||
outputEvents |= PollEventTypeMask.Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
if (readEvents.Contains(socket))
|
||||
{
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
|
||||
{
|
||||
outputEvents |= PollEventTypeMask.Input;
|
||||
}
|
||||
}
|
||||
|
||||
if (writeEvents.Contains(socket))
|
||||
{
|
||||
outputEvents |= PollEventTypeMask.Output;
|
||||
}
|
||||
|
||||
evnt.Data.OutputEvents = outputEvents;
|
||||
}
|
||||
|
||||
updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count;
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
134
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs
Normal file
134
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WSAError.cs
Normal file
|
@ -0,0 +1,134 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
enum WsaError
|
||||
{
|
||||
/*
|
||||
* All Windows Sockets error constants are biased by WSABASEERR from
|
||||
* the "normal"
|
||||
*/
|
||||
WSABASEERR = 10000,
|
||||
|
||||
/*
|
||||
* Windows Sockets definitions of regular Microsoft C error constants
|
||||
*/
|
||||
WSAEINTR = (WSABASEERR + 4),
|
||||
WSAEBADF = (WSABASEERR + 9),
|
||||
WSAEACCES = (WSABASEERR + 13),
|
||||
WSAEFAULT = (WSABASEERR + 14),
|
||||
WSAEINVAL = (WSABASEERR + 22),
|
||||
WSAEMFILE = (WSABASEERR + 24),
|
||||
|
||||
/*
|
||||
* Windows Sockets definitions of regular Berkeley error constants
|
||||
*/
|
||||
WSAEWOULDBLOCK = (WSABASEERR + 35),
|
||||
WSAEINPROGRESS = (WSABASEERR + 36),
|
||||
WSAEALREADY = (WSABASEERR + 37),
|
||||
WSAENOTSOCK = (WSABASEERR + 38),
|
||||
WSAEDESTADDRREQ = (WSABASEERR + 39),
|
||||
WSAEMSGSIZE = (WSABASEERR + 40),
|
||||
WSAEPROTOTYPE = (WSABASEERR + 41),
|
||||
WSAENOPROTOOPT = (WSABASEERR + 42),
|
||||
WSAEPROTONOSUPPORT = (WSABASEERR + 43),
|
||||
WSAESOCKTNOSUPPORT = (WSABASEERR + 44),
|
||||
WSAEOPNOTSUPP = (WSABASEERR + 45),
|
||||
WSAEPFNOSUPPORT = (WSABASEERR + 46),
|
||||
WSAEAFNOSUPPORT = (WSABASEERR + 47),
|
||||
WSAEADDRINUSE = (WSABASEERR + 48),
|
||||
WSAEADDRNOTAVAIL = (WSABASEERR + 49),
|
||||
WSAENETDOWN = (WSABASEERR + 50),
|
||||
WSAENETUNREACH = (WSABASEERR + 51),
|
||||
WSAENETRESET = (WSABASEERR + 52),
|
||||
WSAECONNABORTED = (WSABASEERR + 53),
|
||||
WSAECONNRESET = (WSABASEERR + 54),
|
||||
WSAENOBUFS = (WSABASEERR + 55),
|
||||
WSAEISCONN = (WSABASEERR + 56),
|
||||
WSAENOTCONN = (WSABASEERR + 57),
|
||||
WSAESHUTDOWN = (WSABASEERR + 58),
|
||||
WSAETOOMANYREFS = (WSABASEERR + 59),
|
||||
WSAETIMEDOUT = (WSABASEERR + 60),
|
||||
WSAECONNREFUSED = (WSABASEERR + 61),
|
||||
WSAELOOP = (WSABASEERR + 62),
|
||||
WSAENAMETOOLONG = (WSABASEERR + 63),
|
||||
WSAEHOSTDOWN = (WSABASEERR + 64),
|
||||
WSAEHOSTUNREACH = (WSABASEERR + 65),
|
||||
WSAENOTEMPTY = (WSABASEERR + 66),
|
||||
WSAEPROCLIM = (WSABASEERR + 67),
|
||||
WSAEUSERS = (WSABASEERR + 68),
|
||||
WSAEDQUOT = (WSABASEERR + 69),
|
||||
WSAESTALE = (WSABASEERR + 70),
|
||||
WSAEREMOTE = (WSABASEERR + 71),
|
||||
|
||||
/*
|
||||
* Extended Windows Sockets error constant definitions
|
||||
*/
|
||||
WSASYSNOTREADY = (WSABASEERR + 91),
|
||||
WSAVERNOTSUPPORTED = (WSABASEERR + 92),
|
||||
WSANOTINITIALISED = (WSABASEERR + 93),
|
||||
WSAEDISCON = (WSABASEERR + 101),
|
||||
WSAENOMORE = (WSABASEERR + 102),
|
||||
WSAECANCELLED = (WSABASEERR + 103),
|
||||
WSAEINVALIDPROCTABLE = (WSABASEERR + 104),
|
||||
WSAEINVALIDPROVIDER = (WSABASEERR + 105),
|
||||
WSAEPROVIDERFAILEDINIT = (WSABASEERR + 106),
|
||||
WSASYSCALLFAILURE = (WSABASEERR + 107),
|
||||
WSASERVICE_NOT_FOUND = (WSABASEERR + 108),
|
||||
WSATYPE_NOT_FOUND = (WSABASEERR + 109),
|
||||
WSA_E_NO_MORE = (WSABASEERR + 110),
|
||||
WSA_E_CANCELLED = (WSABASEERR + 111),
|
||||
WSAEREFUSED = (WSABASEERR + 112),
|
||||
|
||||
/*
|
||||
* Error return codes from gethostbyname() and gethostbyaddr()
|
||||
* (when using the resolver). Note that these errors are
|
||||
* retrieved via WSAGetLastError() and must therefore follow
|
||||
* the rules for avoiding clashes with error numbers from
|
||||
* specific implementations or language run-time systems.
|
||||
* For this reason the codes are based at WSABASEERR+1001.
|
||||
* Note also that [WSA]NO_ADDRESS is defined only for
|
||||
* compatibility purposes.
|
||||
*/
|
||||
|
||||
/* Authoritative Answer: Host not found */
|
||||
WSAHOST_NOT_FOUND = (WSABASEERR + 1001),
|
||||
|
||||
/* Non-Authoritative: Host not found, or SERVERFAIL */
|
||||
WSATRY_AGAIN = (WSABASEERR + 1002),
|
||||
|
||||
/* Non-recoverable errors, FORMERR, REFUSED, NOTIMP */
|
||||
WSANO_RECOVERY = (WSABASEERR + 1003),
|
||||
|
||||
/* Valid name, no data record of requested type */
|
||||
WSANO_DATA = (WSABASEERR + 1004),
|
||||
|
||||
/*
|
||||
* Define QOS related error return codes
|
||||
*
|
||||
*/
|
||||
WSA_QOS_RECEIVERS = (WSABASEERR + 1005),
|
||||
/* at least one Reserve has arrived */
|
||||
WSA_QOS_SENDERS = (WSABASEERR + 1006),
|
||||
/* at least one Path has arrived */
|
||||
WSA_QOS_NO_SENDERS = (WSABASEERR + 1007),
|
||||
/* there are no senders */
|
||||
WSA_QOS_NO_RECEIVERS = (WSABASEERR + 1008),
|
||||
/* there are no receivers */
|
||||
WSA_QOS_REQUEST_CONFIRMED = (WSABASEERR + 1009),
|
||||
/* Reserve has been confirmed */
|
||||
WSA_QOS_ADMISSION_FAILURE = (WSABASEERR + 1010),
|
||||
/* error due to lack of resources */
|
||||
WSA_QOS_POLICY_FAILURE = (WSABASEERR + 1011),
|
||||
/* rejected for administrative reasons - bad credentials */
|
||||
WSA_QOS_BAD_STYLE = (WSABASEERR + 1012),
|
||||
/* unknown or conflicting style */
|
||||
WSA_QOS_BAD_OBJECT = (WSABASEERR + 1013),
|
||||
/* problem with some part of the filterspec or providerspecific
|
||||
* buffer in general */
|
||||
WSA_QOS_TRAFFIC_CTRL_ERROR = (WSABASEERR + 1014),
|
||||
/* problem with some part of the flowspec */
|
||||
WSA_QOS_GENERIC_ERROR = (WSABASEERR + 1015)
|
||||
}
|
||||
}
|
165
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs
Normal file
165
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/WinSockHelper.cs
Normal file
|
@ -0,0 +1,165 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
static class WinSockHelper
|
||||
{
|
||||
private static readonly Dictionary<WsaError, LinuxError> _errorMap = new()
|
||||
{
|
||||
// WSAEINTR
|
||||
{WsaError.WSAEINTR, LinuxError.EINTR},
|
||||
// WSAEWOULDBLOCK
|
||||
{WsaError.WSAEWOULDBLOCK, LinuxError.EWOULDBLOCK},
|
||||
// WSAEINPROGRESS
|
||||
{WsaError.WSAEINPROGRESS, LinuxError.EINPROGRESS},
|
||||
// WSAEALREADY
|
||||
{WsaError.WSAEALREADY, LinuxError.EALREADY},
|
||||
// WSAENOTSOCK
|
||||
{WsaError.WSAENOTSOCK, LinuxError.ENOTSOCK},
|
||||
// WSAEDESTADDRREQ
|
||||
{WsaError.WSAEDESTADDRREQ, LinuxError.EDESTADDRREQ},
|
||||
// WSAEMSGSIZE
|
||||
{WsaError.WSAEMSGSIZE, LinuxError.EMSGSIZE},
|
||||
// WSAEPROTOTYPE
|
||||
{WsaError.WSAEPROTOTYPE, LinuxError.EPROTOTYPE},
|
||||
// WSAENOPROTOOPT
|
||||
{WsaError.WSAENOPROTOOPT, LinuxError.ENOPROTOOPT},
|
||||
// WSAEPROTONOSUPPORT
|
||||
{WsaError.WSAEPROTONOSUPPORT, LinuxError.EPROTONOSUPPORT},
|
||||
// WSAESOCKTNOSUPPORT
|
||||
{WsaError.WSAESOCKTNOSUPPORT, LinuxError.ESOCKTNOSUPPORT},
|
||||
// WSAEOPNOTSUPP
|
||||
{WsaError.WSAEOPNOTSUPP, LinuxError.EOPNOTSUPP},
|
||||
// WSAEPFNOSUPPORT
|
||||
{WsaError.WSAEPFNOSUPPORT, LinuxError.EPFNOSUPPORT},
|
||||
// WSAEAFNOSUPPORT
|
||||
{WsaError.WSAEAFNOSUPPORT, LinuxError.EAFNOSUPPORT},
|
||||
// WSAEADDRINUSE
|
||||
{WsaError.WSAEADDRINUSE, LinuxError.EADDRINUSE},
|
||||
// WSAEADDRNOTAVAIL
|
||||
{WsaError.WSAEADDRNOTAVAIL, LinuxError.EADDRNOTAVAIL},
|
||||
// WSAENETDOWN
|
||||
{WsaError.WSAENETDOWN, LinuxError.ENETDOWN},
|
||||
// WSAENETUNREACH
|
||||
{WsaError.WSAENETUNREACH, LinuxError.ENETUNREACH},
|
||||
// WSAENETRESET
|
||||
{WsaError.WSAENETRESET, LinuxError.ENETRESET},
|
||||
// WSAECONNABORTED
|
||||
{WsaError.WSAECONNABORTED, LinuxError.ECONNABORTED},
|
||||
// WSAECONNRESET
|
||||
{WsaError.WSAECONNRESET, LinuxError.ECONNRESET},
|
||||
// WSAENOBUFS
|
||||
{WsaError.WSAENOBUFS, LinuxError.ENOBUFS},
|
||||
// WSAEISCONN
|
||||
{WsaError.WSAEISCONN, LinuxError.EISCONN},
|
||||
// WSAENOTCONN
|
||||
{WsaError.WSAENOTCONN, LinuxError.ENOTCONN},
|
||||
// WSAESHUTDOWN
|
||||
{WsaError.WSAESHUTDOWN, LinuxError.ESHUTDOWN},
|
||||
// WSAETOOMANYREFS
|
||||
{WsaError.WSAETOOMANYREFS, LinuxError.ETOOMANYREFS},
|
||||
// WSAETIMEDOUT
|
||||
{WsaError.WSAETIMEDOUT, LinuxError.ETIMEDOUT},
|
||||
// WSAECONNREFUSED
|
||||
{WsaError.WSAECONNREFUSED, LinuxError.ECONNREFUSED},
|
||||
// WSAELOOP
|
||||
{WsaError.WSAELOOP, LinuxError.ELOOP},
|
||||
// WSAENAMETOOLONG
|
||||
{WsaError.WSAENAMETOOLONG, LinuxError.ENAMETOOLONG},
|
||||
// WSAEHOSTDOWN
|
||||
{WsaError.WSAEHOSTDOWN, LinuxError.EHOSTDOWN},
|
||||
// WSAEHOSTUNREACH
|
||||
{WsaError.WSAEHOSTUNREACH, LinuxError.EHOSTUNREACH},
|
||||
// WSAENOTEMPTY
|
||||
{WsaError.WSAENOTEMPTY, LinuxError.ENOTEMPTY},
|
||||
// WSAEUSERS
|
||||
{WsaError.WSAEUSERS, LinuxError.EUSERS},
|
||||
// WSAEDQUOT
|
||||
{WsaError.WSAEDQUOT, LinuxError.EDQUOT},
|
||||
// WSAESTALE
|
||||
{WsaError.WSAESTALE, LinuxError.ESTALE},
|
||||
// WSAEREMOTE
|
||||
{WsaError.WSAEREMOTE, LinuxError.EREMOTE},
|
||||
// WSAEINVAL
|
||||
{WsaError.WSAEINVAL, LinuxError.EINVAL},
|
||||
// WSAEFAULT
|
||||
{WsaError.WSAEFAULT, LinuxError.EFAULT},
|
||||
// NOERROR
|
||||
{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 }
|
||||
};
|
||||
|
||||
public static LinuxError ConvertError(WsaError errorCode)
|
||||
{
|
||||
if (!_errorMap.TryGetValue(errorCode, out LinuxError errno))
|
||||
{
|
||||
errno = (LinuxError)errorCode;
|
||||
}
|
||||
|
||||
return errno;
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue