diff options
| author | Ac_K <Acoustik666@gmail.com> | 2019-09-19 02:45:11 +0200 |
|---|---|---|
| committer | jduncanator <1518948+jduncanator@users.noreply.github.com> | 2019-09-19 10:45:11 +1000 |
| commit | a0720b5681852f3d786d77bd3793b0359dea321c (patch) | |
| tree | 9d8f61e540d1d1d827999902dad95e5c0c1e076e /Ryujinx.HLE/HOS/Services/Sockets/Bsd | |
| parent | 4af3101b22e6957d6aa48a2768566d658699f4ed (diff) | |
Refactoring HOS folder structure (#771)
* Refactoring HOS folder structure
Refactoring HOS folder structure:
- Added some subfolders when needed (Following structure decided in private).
- Added some `Types` folders when needed.
- Little cleanup here and there.
- Add services placeholders for every HOS services (close #766 and #753).
* Remove Types namespaces
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Sockets/Bsd')
5 files changed, 1236 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs new file mode 100644 index 00000000..3a02e06c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -0,0 +1,1180 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.Utilities; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd +{ + [Service("bsd:s", true)] + [Service("bsd:u", false)] + class IClient : IpcService + { + private static Dictionary<WsaError, LinuxError> _errorMap = new Dictionary<WsaError, LinuxError> + { + // 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 bool _isPrivileged; + + private List<BsdSocket> _sockets = new List<BsdSocket>(); + + public IClient(ServiceCtx context, bool isPrivileged) + { + _isPrivileged = isPrivileged; + } + + private LinuxError ConvertError(WsaError errorCode) + { + if (!_errorMap.TryGetValue(errorCode, out LinuxError errno)) + { + errno = (LinuxError)errorCode; + } + + return errno; + } + + private ResultCode WriteWinSock2Error(ServiceCtx context, WsaError errorCode) + { + return WriteBsdResult(context, -1, ConvertError(errorCode)); + } + + private ResultCode WriteBsdResult(ServiceCtx context, int result, LinuxError errorCode = 0) + { + if (errorCode != LinuxError.SUCCESS) + { + result = -1; + } + + context.ResponseData.Write(result); + context.ResponseData.Write((int)errorCode); + + return ResultCode.Success; + } + + private BsdSocket RetrieveSocket(int socketFd) + { + if (socketFd >= 0 && _sockets.Count > socketFd) + { + return _sockets[socketFd]; + } + + return null; + } + + private LinuxError SetResultErrno(Socket socket, int result) + { + return result == 0 && !socket.Blocking ? LinuxError.EWOULDBLOCK : LinuxError.SUCCESS; + } + + private AddressFamily ConvertFromBsd(int domain) + { + if (domain == 2) + { + return AddressFamily.InterNetwork; + } + + // FIXME: AF_ROUTE ignored, is that really needed? + return AddressFamily.Unknown; + } + + private ResultCode SocketInternal(ServiceCtx context, bool exempt) + { + AddressFamily domain = (AddressFamily)context.RequestData.ReadInt32(); + SocketType type = (SocketType)context.RequestData.ReadInt32(); + ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32(); + + if (domain == AddressFamily.Unknown) + { + return WriteBsdResult(context, -1, LinuxError.EPROTONOSUPPORT); + } + else if ((type == SocketType.Seqpacket || type == SocketType.Raw) && !_isPrivileged) + { + if (domain != AddressFamily.InterNetwork || type != SocketType.Raw || protocol != ProtocolType.Icmp) + { + return WriteBsdResult(context, -1, LinuxError.ENOENT); + } + } + + BsdSocket newBsdSocket = new BsdSocket + { + Family = (int)domain, + Type = (int)type, + Protocol = (int)protocol, + Handle = new Socket(domain, type, protocol) + }; + + _sockets.Add(newBsdSocket); + + if (exempt) + { + newBsdSocket.Handle.Disconnect(true); + } + + return WriteBsdResult(context, _sockets.Count - 1); + } + + private IPEndPoint ParseSockAddr(ServiceCtx context, long bufferPosition, long bufferSize) + { + int size = context.Memory.ReadByte(bufferPosition); + int family = context.Memory.ReadByte(bufferPosition + 1); + int port = EndianSwap.Swap16(context.Memory.ReadUInt16(bufferPosition + 2)); + + byte[] rawIp = context.Memory.ReadBytes(bufferPosition + 4, 4); + + return new IPEndPoint(new IPAddress(rawIp), port); + } + + private void WriteSockAddr(ServiceCtx context, long bufferPosition, IPEndPoint endPoint) + { + context.Memory.WriteByte(bufferPosition, 0); + context.Memory.WriteByte(bufferPosition + 1, (byte)endPoint.AddressFamily); + context.Memory.WriteUInt16(bufferPosition + 2, EndianSwap.Swap16((ushort)endPoint.Port)); + context.Memory.WriteBytes(bufferPosition + 4, endPoint.Address.GetAddressBytes()); + } + + private void WriteSockAddr(ServiceCtx context, long bufferPosition, BsdSocket socket, bool isRemote) + { + IPEndPoint endPoint = (isRemote ? socket.Handle.RemoteEndPoint : socket.Handle.LocalEndPoint) as IPEndPoint; + + WriteSockAddr(context, bufferPosition, endPoint); + } + + [Command(0)] + // Initialize(nn::socket::BsdBufferConfig config, u64 pid, u64 transferMemorySize, KObject<copy, transfer_memory>, pid) -> u32 bsd_errno + public ResultCode RegisterClient(ServiceCtx context) + { + /* + typedef struct { + u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. + u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed). + u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed). + u32 tcp_tx_buf_max_size; // Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 tcp_rx_buf_max_size; // Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes). + u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes). + u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8). + } BsdBufferConfig; + */ + + // bsd_error + context.ResponseData.Write(0); + + Logger.PrintStub(LogClass.ServiceBsd); + + return ResultCode.Success; + } + + [Command(1)] + // StartMonitoring(u64, pid) + public ResultCode StartMonitoring(ServiceCtx context) + { + ulong unknown0 = context.RequestData.ReadUInt64(); + + Logger.PrintStub(LogClass.ServiceBsd, new { unknown0 }); + + return ResultCode.Success; + } + + [Command(2)] + // Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + public ResultCode Socket(ServiceCtx context) + { + return SocketInternal(context, false); + } + + [Command(3)] + // SocketExempt(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + public ResultCode SocketExempt(ServiceCtx context) + { + return SocketInternal(context, true); + } + + [Command(4)] + // Open(u32 flags, array<unknown, 0x21> path) -> (i32 ret, u32 bsd_errno) + public ResultCode Open(ServiceCtx context) + { + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + + int flags = context.RequestData.ReadInt32(); + + byte[] rawPath = context.Memory.ReadBytes(bufferPosition, bufferSize); + string path = Encoding.ASCII.GetString(rawPath); + + WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); + + Logger.PrintStub(LogClass.ServiceBsd, new { path, flags }); + + return ResultCode.Success; + } + + [Command(5)] + // Select(u32 nfds, nn::socket::timeout timeout, buffer<nn::socket::fd_set, 0x21, 0> readfds_in, buffer<nn::socket::fd_set, 0x21, 0> writefds_in, buffer<nn::socket::fd_set, 0x21, 0> errorfds_in) -> (i32 ret, u32 bsd_errno, buffer<nn::socket::fd_set, 0x22, 0> readfds_out, buffer<nn::socket::fd_set, 0x22, 0> writefds_out, buffer<nn::socket::fd_set, 0x22, 0> errorfds_out) + public ResultCode Select(ServiceCtx context) + { + WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); + + Logger.PrintStub(LogClass.ServiceBsd); + + return ResultCode.Success; + } + + [Command(6)] + // Poll(u32 nfds, u32 timeout, buffer<unknown, 0x21, 0> fds) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>) + public ResultCode Poll(ServiceCtx context) + { + int fdsCount = context.RequestData.ReadInt32(); + int timeout = context.RequestData.ReadInt32(); + + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(); + + + if (timeout < -1 || fdsCount < 0 || (fdsCount * 8) > bufferSize) + { + return WriteBsdResult(context, -1, LinuxError.EINVAL); + } + + PollEvent[] events = new PollEvent[fdsCount]; + + for (int i = 0; i < fdsCount; i++) + { + int socketFd = context.Memory.ReadInt32(bufferPosition + i * 8); + + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket == null) + { + return WriteBsdResult(context, -1, LinuxError.EBADF);} + + PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.ReadInt16(bufferPosition + i * 8 + 4); + PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.ReadInt16(bufferPosition + i * 8 + 6); + + events[i] = new PollEvent(socketFd, socket, inputEvents, outputEvents); + } + + List<Socket> readEvents = new List<Socket>(); + List<Socket> writeEvents = new List<Socket>(); + List<Socket> errorEvents = new List<Socket>(); + + foreach (PollEvent Event in events) + { + bool isValidEvent = false; + + if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) + { + readEvents.Add(Event.Socket.Handle); + errorEvents.Add(Event.Socket.Handle); + + isValidEvent = true; + } + + if ((Event.InputEvents & PollEvent.EventTypeMask.UrgentInput) != 0) + { + readEvents.Add(Event.Socket.Handle); + errorEvents.Add(Event.Socket.Handle); + + isValidEvent = true; + } + + if ((Event.InputEvents & PollEvent.EventTypeMask.Output) != 0) + { + writeEvents.Add(Event.Socket.Handle); + errorEvents.Add(Event.Socket.Handle); + + isValidEvent = true; + } + + if ((Event.InputEvents & PollEvent.EventTypeMask.Error) != 0) + { + errorEvents.Add(Event.Socket.Handle); + isValidEvent = true; + } + + if (!isValidEvent) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Poll input event type: {Event.InputEvents}"); + return WriteBsdResult(context, -1, LinuxError.EINVAL); + } + } + + try + { + System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout); + } + catch (SocketException exception) + { + return WriteWinSock2Error(context, (WsaError)exception.ErrorCode); + } + + for (int i = 0; i < fdsCount; i++) + { + PollEvent Event = events[i]; + context.Memory.WriteInt32(bufferPosition + i * 8, Event.SocketFd); + context.Memory.WriteInt16(bufferPosition + i * 8 + 4, (short)Event.InputEvents); + + PollEvent.EventTypeMask outputEvents = 0; + + Socket socket = Event.Socket.Handle; + + if (errorEvents.Contains(socket)) + { + outputEvents |= PollEvent.EventTypeMask.Error; + + if (!socket.Connected || !socket.IsBound) + { + outputEvents |= PollEvent.EventTypeMask.Disconnected; + } + } + + if (readEvents.Contains(socket)) + { + if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0) + { + outputEvents |= PollEvent.EventTypeMask.Input; + } + } + + if (writeEvents.Contains(socket)) + { + outputEvents |= PollEvent.EventTypeMask.Output; + } + + context.Memory.WriteInt16(bufferPosition + i * 8 + 6, (short)outputEvents); + } + + return WriteBsdResult(context, readEvents.Count + writeEvents.Count + errorEvents.Count, LinuxError.SUCCESS); + } + + [Command(7)] + // Sysctl(buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>) + public ResultCode Sysctl(ServiceCtx context) + { + WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); + + Logger.PrintStub(LogClass.ServiceBsd); + + return ResultCode.Success; + } + + [Command(8)] + // Recv(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, array<i8, 0x22> message) + public ResultCode Recv(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); + + (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + int result = -1; + + if (socket != null) + { + if (socketFlags != SocketFlags.None && (socketFlags & SocketFlags.OutOfBand) == 0 + && (socketFlags & SocketFlags.Peek) == 0) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Recv flags: {socketFlags}"); + return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); + } + + byte[] receivedBuffer = new byte[receiveLength]; + + try + { + result = socket.Handle.Receive(receivedBuffer, socketFlags); + errno = SetResultErrno(socket.Handle, result); + + context.Memory.WriteBytes(receivePosition, receivedBuffer); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + + return WriteBsdResult(context, result, errno); + } + + [Command(9)] + // RecvFrom(u32 sock, u32 flags) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<i8, 0x22, 0> message, buffer<nn::socket::sockaddr_in, 0x22, 0x10>) + public ResultCode RecvFrom(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); + + (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22(); + (long sockAddrOutPosition, long sockAddrOutSize) = context.Request.GetBufferType0x22(1); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + int result = -1; + + if (socket != null) + { + if (socketFlags != SocketFlags.None && (socketFlags & SocketFlags.OutOfBand) == 0 + && (socketFlags & SocketFlags.Peek) == 0) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Recv flags: {socketFlags}"); + + return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); + } + + byte[] receivedBuffer = new byte[receiveLength]; + EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); + + try + { + result = socket.Handle.ReceiveFrom(receivedBuffer, receivedBuffer.Length, socketFlags, ref endPoint); + errno = SetResultErrno(socket.Handle, result); + + context.Memory.WriteBytes(receivePosition, receivedBuffer); + WriteSockAddr(context, sockAddrOutPosition, (IPEndPoint)endPoint); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + + return WriteBsdResult(context, result, errno); + } + + [Command(10)] + // Send(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno) + public ResultCode Send(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); + + (long sendPosition, long sendSize) = context.Request.GetBufferType0x21(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + int result = -1; + + if (socket != null) + { + if (socketFlags != SocketFlags.None && socketFlags != SocketFlags.OutOfBand + && socketFlags != SocketFlags.Peek && socketFlags != SocketFlags.DontRoute) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Send flags: {socketFlags}"); + + return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); + } + + byte[] sendBuffer = context.Memory.ReadBytes(sendPosition, sendSize); + + try + { + result = socket.Handle.Send(sendBuffer, socketFlags); + errno = SetResultErrno(socket.Handle, result); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + + } + + return WriteBsdResult(context, result, errno); + } + + [Command(11)] + // SendTo(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<nn::socket::sockaddr_in, 0x21, 0x10>) -> (i32 ret, u32 bsd_errno) + public ResultCode SendTo(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32(); + + (long sendPosition, long sendSize) = context.Request.GetBufferType0x21(); + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(1); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + int result = -1; + + if (socket != null) + { + if (socketFlags != SocketFlags.None && socketFlags != SocketFlags.OutOfBand + && socketFlags != SocketFlags.Peek && socketFlags != SocketFlags.DontRoute) + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Send flags: {socketFlags}"); + + return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); + } + + byte[] sendBuffer = context.Memory.ReadBytes(sendPosition, sendSize); + EndPoint endPoint = ParseSockAddr(context, bufferPosition, bufferSize); + + try + { + result = socket.Handle.SendTo(sendBuffer, sendBuffer.Length, socketFlags, endPoint); + errno = SetResultErrno(socket.Handle, result); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + + } + + return WriteBsdResult(context, result, errno); + } + + [Command(12)] + // Accept(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr) + public ResultCode Accept(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.SUCCESS; + + Socket newSocket = null; + + try + { + newSocket = socket.Handle.Accept(); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + + if (newSocket == null && errno == LinuxError.SUCCESS) + { + errno = LinuxError.EWOULDBLOCK; + } + else if (errno == LinuxError.SUCCESS) + { + BsdSocket newBsdSocket = new BsdSocket + { + Family = (int)newSocket.AddressFamily, + Type = (int)newSocket.SocketType, + Protocol = (int)newSocket.ProtocolType, + Handle = newSocket + }; + + _sockets.Add(newBsdSocket); + + WriteSockAddr(context, bufferPos, newBsdSocket, true); + + WriteBsdResult(context, _sockets.Count - 1, errno); + + context.ResponseData.Write(0x10); + + return ResultCode.Success; + } + } + + return WriteBsdResult(context, -1, errno); + } + + [Command(13)] + // Bind(u32 socket, buffer<nn::socket::sockaddr_in, 0x21, 0x10> addr) -> (i32 ret, u32 bsd_errno) + public ResultCode Bind(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.SUCCESS; + + try + { + IPEndPoint endPoint = ParseSockAddr(context, bufferPos, bufferSize); + + socket.Handle.Bind(endPoint); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(14)] + // Connect(u32 socket, buffer<nn::socket::sockaddr_in, 0x21, 0x10>) -> (i32 ret, u32 bsd_errno) + public ResultCode Connect(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.SUCCESS; + try + { + IPEndPoint endPoint = ParseSockAddr(context, bufferPos, bufferSize); + + socket.Handle.Connect(endPoint); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(15)] + // GetPeerName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr) + public ResultCode GetPeerName(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.SUCCESS; + + WriteSockAddr(context, bufferPos, socket, true); + WriteBsdResult(context, 0, errno); + context.ResponseData.Write(0x10); + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(16)] + // GetSockName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr) + public ResultCode GetSockName(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.SUCCESS; + + WriteSockAddr(context, bufferPos, socket, false); + WriteBsdResult(context, 0, errno); + context.ResponseData.Write(0x10); + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(17)] + // 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(); + int level = context.RequestData.ReadInt32(); + int optionName = context.RequestData.ReadInt32(); + + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.ENOPROTOOPT; + + if (level == 0xFFFF) + { + errno = HandleGetSocketOption(context, socket, (SocketOptionName)optionName, bufferPosition, bufferSize); + } + else + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported GetSockOpt Level: {(SocketOptionLevel)level}"); + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(18)] + // Listen(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno) + public ResultCode Listen(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + int backlog = context.RequestData.ReadInt32(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.SUCCESS; + + try + { + socket.Handle.Listen(backlog); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(19)] + // Ioctl(u32 fd, u32 request, u32 bufcount, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>, buffer<unknown, 0x22, 0>) + public ResultCode Ioctl(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + BsdIoctl cmd = (BsdIoctl)context.RequestData.ReadInt32(); + int bufferCount = context.RequestData.ReadInt32(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + switch (cmd) + { + case BsdIoctl.AtMark: + errno = LinuxError.SUCCESS; + + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22(); + + // FIXME: OOB not implemented. + context.Memory.WriteInt32(bufferPosition, 0); + break; + + default: + errno = LinuxError.EOPNOTSUPP; + + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Ioctl Cmd: {cmd}"); + break; + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(20)] + // Fcntl(u32 socket, u32 cmd, u32 arg) -> (i32 ret, u32 bsd_errno) + public ResultCode Fcntl(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + int cmd = context.RequestData.ReadInt32(); + int arg = context.RequestData.ReadInt32(); + + int result = 0; + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.SUCCESS; + + if (cmd == 0x3) + { + result = !socket.Handle.Blocking ? 0x800 : 0; + } + else if (cmd == 0x4 && arg == 0x800) + { + socket.Handle.Blocking = false; + result = 0; + } + else + { + errno = LinuxError.EOPNOTSUPP; + } + } + + return WriteBsdResult(context, result, errno); + } + + private LinuxError HandleGetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize) + { + try + { + 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(SocketOptionLevel.Socket, optionName, optionValue); + context.Memory.WriteBytes(optionValuePosition, optionValue); + + return LinuxError.SUCCESS; + + case (SocketOptionName)0x200: + socket.Handle.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue); + context.Memory.WriteBytes(optionValuePosition, optionValue); + + return LinuxError.SUCCESS; + + default: + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {optionName}"); + + return LinuxError.EOPNOTSUPP; + } + } + catch (SocketException exception) + { + return ConvertError((WsaError)exception.ErrorCode); + } + } + + private LinuxError HandleSetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize) + { + try + { + 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.ReuseAddress: + socket.Handle.SetSocketOption(SocketOptionLevel.Socket, optionName, context.Memory.ReadInt32(optionValuePosition)); + + return LinuxError.SUCCESS; + + case (SocketOptionName)0x200: + socket.Handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, context.Memory.ReadInt32(optionValuePosition)); + + return LinuxError.SUCCESS; + + case SocketOptionName.Linger: + socket.Handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, + new LingerOption(context.Memory.ReadInt32(optionValuePosition) != 0, context.Memory.ReadInt32(optionValuePosition + 4))); + + return LinuxError.SUCCESS; + + default: + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {optionName}"); + + return LinuxError.EOPNOTSUPP; + } + } + catch (SocketException exception) + { + return ConvertError((WsaError)exception.ErrorCode); + } + } + + [Command(21)] + // 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(); + int level = context.RequestData.ReadInt32(); + int optionName = context.RequestData.ReadInt32(); + + (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.ENOPROTOOPT; + + if (level == 0xFFFF) + { + errno = HandleSetSocketOption(context, socket, (SocketOptionName)optionName, bufferPos, bufferSize); + } + else + { + Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt Level: {(SocketOptionLevel)level}"); + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(22)] + // Shutdown(u32 socket, u32 how) -> (i32 ret, u32 bsd_errno) + public ResultCode Shutdown(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + int how = context.RequestData.ReadInt32(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + errno = LinuxError.EINVAL; + + if (how >= 0 && how <= 2) + { + errno = LinuxError.SUCCESS; + + try + { + socket.Handle.Shutdown((SocketShutdown)how); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(23)] + // ShutdownAllSockets(u32 how) -> (i32 ret, u32 bsd_errno) + public ResultCode ShutdownAllSockets(ServiceCtx context) + { + int how = context.RequestData.ReadInt32(); + + LinuxError errno = LinuxError.EINVAL; + + if (how >= 0 && how <= 2) + { + errno = LinuxError.SUCCESS; + + foreach (BsdSocket socket in _sockets) + { + if (socket != null) + { + try + { + socket.Handle.Shutdown((SocketShutdown)how); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + break; + } + } + } + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(24)] + // Write(u32 socket, buffer<i8, 0x21, 0> message) -> (i32 ret, u32 bsd_errno) + public ResultCode Write(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + (long sendPosition, long sendSize) = context.Request.GetBufferType0x21(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + int result = -1; + + if (socket != null) + { + byte[] sendBuffer = context.Memory.ReadBytes(sendPosition, sendSize); + + try + { + result = socket.Handle.Send(sendBuffer); + errno = SetResultErrno(socket.Handle, result); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + + return WriteBsdResult(context, result, errno); + } + + [Command(25)] + // Read(u32 socket) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message) + public ResultCode Read(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + int result = -1; + + if (socket != null) + { + byte[] receivedBuffer = new byte[receiveLength]; + + try + { + result = socket.Handle.Receive(receivedBuffer); + errno = SetResultErrno(socket.Handle, result); + } + catch (SocketException exception) + { + errno = ConvertError((WsaError)exception.ErrorCode); + } + } + + return WriteBsdResult(context, result, errno); + } + + [Command(26)] + // Close(u32 socket) -> (i32 ret, u32 bsd_errno) + public ResultCode Close(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + + LinuxError errno = LinuxError.EBADF; + BsdSocket socket = RetrieveSocket(socketFd); + + if (socket != null) + { + socket.Handle.Close(); + + _sockets[socketFd] = null; + + errno = LinuxError.SUCCESS; + } + + return WriteBsdResult(context, 0, errno); + } + + [Command(27)] + // DuplicateSocket(u32 socket, u64 reserved) -> (i32 ret, u32 bsd_errno) + public ResultCode DuplicateSocket(ServiceCtx context) + { + int socketFd = context.RequestData.ReadInt32(); + ulong reserved = context.RequestData.ReadUInt64(); + + LinuxError errno = LinuxError.ENOENT; + int newSockFd = -1; + + if (_isPrivileged) + { + errno = LinuxError.EBADF; + + BsdSocket oldSocket = RetrieveSocket(socketFd); + + if (oldSocket != null) + { + _sockets.Add(oldSocket); + newSockFd = _sockets.Count - 1; + } + } + + return WriteBsdResult(context, newSockFd, errno); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs new file mode 100644 index 00000000..798fc015 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ServerInterface.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd +{ + [Service("bsdcfg")] + class ServerInterface : IpcService + { + public ServerInterface(ServiceCtx context) { } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs new file mode 100644 index 00000000..421a255c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdIoctl.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd +{ + enum BsdIoctl + { + AtMark = 0x40047307 + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocket.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocket.cs new file mode 100644 index 00000000..2d5bf429 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocket.cs @@ -0,0 +1,13 @@ +using System.Net.Sockets; + +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd +{ + class BsdSocket + { + public int Family; + public int Type; + public int Protocol; + + public Socket Handle; + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs new file mode 100644 index 00000000..ff47a4c7 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/PollEvent.cs @@ -0,0 +1,28 @@ +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd +{ + class PollEvent + { + public enum EventTypeMask + { + Input = 1, + UrgentInput = 2, + Output = 4, + Error = 8, + Disconnected = 0x10, + Invalid = 0x20 + } + + public int SocketFd { get; private set; } + public BsdSocket Socket { get; private set; } + public EventTypeMask InputEvents { get; private set; } + public EventTypeMask OutputEvents { get; private set; } + + public PollEvent(int socketFd, BsdSocket socket, EventTypeMask inputEvents, EventTypeMask outputEvents) + { + SocketFd = socketFd; + Socket = socket; + InputEvents = inputEvents; + OutputEvents = outputEvents; + } + } +}
\ No newline at end of file |
