aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAc_K <Acoustik666@gmail.com>2018-03-12 02:05:39 +0100
committergdkchan <gab.dark.100@gmail.com>2018-03-11 22:05:39 -0300
commit3aaa4717b6f7400bac862e589a1f345e70e78d56 (patch)
tree37e219259b9564b092533564e21b0cc18036d28e
parent28275a897696a707e222ee14dce20d4b8f65ed58 (diff)
Implement BSD Service (#54)
* Implement BSD Service - Implementation of bsd:s & bsd:u. - Adding an EndianSwap class. * Corrections #1 * Correction2
-rw-r--r--Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs430
-rw-r--r--Ryujinx.Core/OsHle/Services/ServiceFactory.cs1
-rw-r--r--Ryujinx.Core/OsHle/Utilities/EndianSwap.cs7
3 files changed, 421 insertions, 17 deletions
diff --git a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs
index b5944412..da1e51e1 100644
--- a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs
+++ b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs
@@ -1,14 +1,64 @@
+using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
+using Ryujinx.Core.OsHle.Utilities;
+using System;
using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
namespace Ryujinx.Core.OsHle.IpcServices.Bsd
{
+
+ //bsd_errno == (SocketException.ErrorCode - 10000)
+ //https://github.com/freebsd/freebsd/blob/master/sys/sys/errno.h
+ public enum BsdError
+ {
+ ENOTSOCK = 38, /* Socket operation on non-socket */
+ EDESTADDRREQ = 39, /* Destination address required */
+ EMSGSIZE = 40, /* Message too long */
+ EPROTOTYPE = 41, /* Protocol wrong type for socket */
+ ENOPROTOOPT = 42, /* Protocol not available */
+ EPROTONOSUPPORT = 43, /* Protocol not supported */
+ ESOCKTNOSUPPORT = 44, /* Socket type not supported */
+ EOPNOTSUPP = 45, /* Operation not supported */
+ EPFNOSUPPORT = 46, /* Protocol family not supported */
+ EAFNOSUPPORT = 47, /* Address family not supported by protocol family */
+ EADDRINUSE = 48, /* Address already in use */
+ EADDRNOTAVAIL = 49, /* Can't assign requested address */
+ ENETDOWN = 50, /* Network is down */
+ ENETUNREACH = 51, /* Network is unreachable */
+ ENETRESET = 52, /* Network dropped connection on reset */
+ ECONNABORTED = 53, /* Software caused connection abort */
+ ECONNRESET = 54, /* Connection reset by peer */
+ ENOBUFS = 55, /* No buffer space available */
+ EISCONN = 56, /* Socket is already connected */
+ ENOTCONN = 57, /* Socket is not connected */
+ ESHUTDOWN = 58, /* Can't send after socket shutdown */
+ ETOOMANYREFS = 59, /* Too many references: can't splice */
+ ETIMEDOUT = 60, /* Operation timed out */
+ ECONNREFUSED = 61 /* Connection refused */
+ }
+
+ class SocketBsd
+ {
+ public int Family;
+ public int Type;
+ public int Protocol;
+ public IPAddress IpAddress;
+ public IPEndPoint RemoteEP;
+ public Socket Handle;
+ }
+
class ServiceBsd : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+ private List<SocketBsd> Sockets = new List<SocketBsd>();
+
public ServiceBsd()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
@@ -16,26 +66,31 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
{ 0, Initialize },
{ 1, StartMonitoring },
{ 2, Socket },
+ { 6, Poll },
+ { 8, Recv },
{ 10, Send },
- { 14, Connect }
+ { 11, SendTo },
+ { 12, Accept },
+ { 13, Bind },
+ { 14, Connect },
+ { 18, Listen },
+ { 21, SetSockOpt },
+ { 26, Close }
};
}
- //Initialize(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno
+ //(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno
public long Initialize(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;
*/
@@ -52,7 +107,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
return 0;
}
- //StartMonitoring(u64, pid)
+ //(u64, pid)
public long StartMonitoring(ServiceCtx Context)
{
//Todo: Stub
@@ -60,37 +115,378 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
return 0;
}
- //Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
+ //(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
public long Socket(ServiceCtx Context)
{
+ SocketBsd NewBSDSocket = new SocketBsd
+ {
+ Family = Context.RequestData.ReadInt32(),
+ Type = Context.RequestData.ReadInt32(),
+ Protocol = Context.RequestData.ReadInt32()
+ };
+
+ Sockets.Add(NewBSDSocket);
+
+ Sockets[Sockets.Count - 1].Handle = new Socket((AddressFamily)Sockets[Sockets.Count - 1].Family,
+ (SocketType)Sockets[Sockets.Count - 1].Type,
+ (ProtocolType)Sockets[Sockets.Count - 1].Protocol);
+
+ Context.ResponseData.Write(Sockets.Count - 1);
Context.ResponseData.Write(0);
+
+ return 0;
+ }
+
+ //(u32, u32, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>)
+ public long Poll(ServiceCtx Context)
+ {
+ int PollCount = Context.RequestData.ReadInt32();
+ int TimeOut = Context.RequestData.ReadInt32();
+
+ //https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
+ //https://msdn.microsoft.com/fr-fr/library/system.net.sockets.socket.poll(v=vs.110).aspx
+ //https://github.com/switchbrew/libnx/blob/e0457c4534b3c37426d83e1a620f82cb28c3b528/nx/source/services/bsd.c#L343
+ //https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634
+ //https://linux.die.net/man/2/poll
+
+ byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+ Context.Request.SendBuff[0].Position,
+ (int)Context.Request.SendBuff[0].Size);
+ int SocketId = Get32(SentBuffer, 0);
+ short RequestedEvents = (short)Get16(SentBuffer, 4);
+ short ReturnedEvents = (short)Get16(SentBuffer, 6);
+
+ //Todo: Stub - Need to implemented the Type-22 buffer.
+
+ Context.ResponseData.Write(1);
Context.ResponseData.Write(0);
- //Todo: Stub
+ return 0;
+ }
+
+ //(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
+ public long Recv(ServiceCtx Context)
+ {
+ try
+ {
+ int SocketId = Context.RequestData.ReadInt32();
+ int SocketFlags = Context.RequestData.ReadInt32();
+ byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
+ int ReadedBytes = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
+
+ //Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer));
+
+ AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, ReceivedBuffer);
+
+ Context.ResponseData.Write(ReadedBytes);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
+
+ return 0;
+ }
+
+ //(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
+ public long Send(ServiceCtx Context)
+ {
+ int SocketId = Context.RequestData.ReadInt32();
+ int SocketFlags = Context.RequestData.ReadInt32();
+ byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+ Context.Request.SendBuff[0].Position,
+ (int)Context.Request.SendBuff[0].Size);
+
+ try
+ {
+ //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
+
+ int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
+
+ Context.ResponseData.Write(BytesSent);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
+
+ return 0;
+ }
+
+ //(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
+ public long SendTo(ServiceCtx Context)
+ {
+ int SocketId = Context.RequestData.ReadInt32();
+ int SocketFlags = Context.RequestData.ReadInt32();
+ byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+ Context.Request.SendBuff[0].Position,
+ (int)Context.Request.SendBuff[0].Size);
+ byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+ Context.Request.SendBuff[1].Position,
+ (int)Context.Request.SendBuff[1].Size);
+
+ if (!Sockets[SocketId].Handle.Connected)
+ {
+ try
+ {
+ ParseAddrBuffer(SocketId, AddressBuffer);
+
+ Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
+ }
+
+ try
+ {
+ //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
+
+ int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
+
+ Context.ResponseData.Write(BytesSent);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
+
+ return 0;
+ }
+
+ //(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<sockaddr, 0x22, 0> addr)
+ public long Accept(ServiceCtx Context)
+ {
+ int SocketId = Context.RequestData.ReadInt32();
+ long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position;
+
+ Socket HandleAccept = null;
+
+ var TimeOut = Task.Factory.StartNew(() =>
+ {
+ try
+ {
+ HandleAccept = Sockets[SocketId].Handle.Accept();
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
+ });
+
+ TimeOut.Wait(10000);
+
+ if (HandleAccept != null)
+ {
+ SocketBsd NewBSDSocket = new SocketBsd
+ {
+ IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address,
+ RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint),
+ Handle = HandleAccept
+ };
+
+ Sockets.Add(NewBSDSocket);
+
+ using (MemoryStream MS = new MemoryStream())
+ {
+ BinaryWriter Writer = new BinaryWriter(MS);
+
+ Writer.Write((byte)0);
+ Writer.Write((byte)Sockets[Sockets.Count - 1].Handle.AddressFamily);
+ Writer.Write((Int16)((IPEndPoint)Sockets[Sockets.Count - 1].Handle.LocalEndPoint).Port);
+
+ string[] IpAdress = Sockets[Sockets.Count - 1].IpAddress.ToString().Split('.');
+ Writer.Write(byte.Parse(IpAdress[0]));
+ Writer.Write(byte.Parse(IpAdress[1]));
+ Writer.Write(byte.Parse(IpAdress[2]));
+ Writer.Write(byte.Parse(IpAdress[3]));
+
+ AMemoryHelper.WriteBytes(Context.Memory, AddrBufferPtr, MS.ToArray());
+
+ Context.ResponseData.Write(Sockets.Count - 1);
+ Context.ResponseData.Write(0);
+ Context.ResponseData.Write(MS.Length);
+ }
+ }
+ else
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write((int)BsdError.ETIMEDOUT);
+ }
+
+ return 0;
+ }
+
+ //(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
+ public long Bind(ServiceCtx Context)
+ {
+ int SocketId = Context.RequestData.ReadInt32();
+
+ byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+ Context.Request.SendBuff[0].Position,
+ (int)Context.Request.SendBuff[0].Size);
+
+ try
+ {
+ ParseAddrBuffer(SocketId, AddressBuffer);
+
+ Context.ResponseData.Write(0);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
return 0;
}
- //Connect(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
+ //(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long Connect(ServiceCtx Context)
{
- Context.ResponseData.Write(0);
- Context.ResponseData.Write(0);
+ int SocketId = Context.RequestData.ReadInt32();
- //Todo: Stub
+ byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+ Context.Request.SendBuff[0].Position,
+ (int)Context.Request.SendBuff[0].Size);
+
+ try
+ {
+ ParseAddrBuffer(SocketId, AddressBuffer);
+
+ Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP);
+
+ Context.ResponseData.Write(0);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
return 0;
}
- //Send(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
- public long Send(ServiceCtx Context)
+ //(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno)
+ public long Listen(ServiceCtx Context)
{
- Context.ResponseData.Write(0);
- Context.ResponseData.Write(0);
+ int SocketId = Context.RequestData.ReadInt32();
+ int BackLog = Context.RequestData.ReadInt32();
- //Todo: Stub
+ try
+ {
+ Sockets[SocketId].Handle.Bind(Sockets[SocketId].RemoteEP);
+ Sockets[SocketId].Handle.Listen(BackLog);
+
+ Context.ResponseData.Write(0);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
return 0;
}
+
+ //(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
+ public long SetSockOpt(ServiceCtx Context)
+ {
+ int SocketId = Context.RequestData.ReadInt32();
+ int SocketLevel = Context.RequestData.ReadInt32();
+ int SocketOptionName = Context.RequestData.ReadInt32();
+
+ byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory,
+ Context.Request.PtrBuff[0].Position,
+ Context.Request.PtrBuff[0].Size);
+
+ try
+ {
+ Sockets[SocketId].Handle.SetSocketOption((SocketOptionLevel)SocketLevel,
+ (SocketOptionName)SocketOptionName,
+ Get32(SocketOptionValue, 0));
+
+ Context.ResponseData.Write(0);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
+
+ return 0;
+ }
+
+ //(u32 socket) -> (i32 ret, u32 bsd_errno)
+ public long Close(ServiceCtx Context)
+ {
+ int SocketId = Context.RequestData.ReadInt32();
+
+ try
+ {
+ Sockets[SocketId].Handle.Close();
+ Sockets[SocketId] = null;
+
+ Context.ResponseData.Write(0);
+ Context.ResponseData.Write(0);
+ }
+ catch (SocketException Ex)
+ {
+ Context.ResponseData.Write(-1);
+ Context.ResponseData.Write(Ex.ErrorCode - 10000);
+ }
+
+ return 0;
+ }
+
+ public void ParseAddrBuffer(int SocketId, byte[] AddrBuffer)
+ {
+ using (MemoryStream MS = new MemoryStream(AddrBuffer))
+ {
+ BinaryReader Reader = new BinaryReader(MS);
+
+ int Size = Reader.ReadByte();
+ int Family = Reader.ReadByte();
+ int Port = EndianSwap.Swap16(Reader.ReadInt16());
+ string IpAddress = Reader.ReadByte().ToString() +
+ "." + Reader.ReadByte().ToString() +
+ "." + Reader.ReadByte().ToString() +
+ "." + Reader.ReadByte().ToString();
+
+ Logging.Debug($"Try to connect to {IpAddress}:{Port}");
+
+ Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
+ Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
+ }
+ }
+
+ private int Get16(byte[] Data, int Address)
+ {
+ return
+ Data[Address + 0] << 0 |
+ Data[Address + 1] << 8;
+ }
+
+ private int Get32(byte[] Data, int Address)
+ {
+ return
+ Data[Address + 0] << 0 |
+ Data[Address + 1] << 8 |
+ Data[Address + 2] << 16 |
+ Data[Address + 3] << 24;
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs
index 31c8aa2c..2b855d25 100644
--- a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs
+++ b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs
@@ -35,6 +35,7 @@ namespace Ryujinx.Core.OsHle.IpcServices
case "appletOE": return new ServiceAppletOE();
case "audout:u": return new ServiceAudOut();
case "audren:u": return new ServiceAudRen();
+ case "bsd:s": return new ServiceBsd();
case "bsd:u": return new ServiceBsd();
case "friend:a": return new ServiceFriend();
case "fsp-srv": return new ServiceFspSrv();
diff --git a/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs b/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs
new file mode 100644
index 00000000..6758f1f2
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Core.OsHle.Utilities
+{
+ static class EndianSwap
+ {
+ public static short Swap16(short Value) => (short)(((Value >> 8) & 0xff) | (Value << 8));
+ }
+}