IClient.cs 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.HLE.HOS.Ipc;
  3. using Ryujinx.HLE.Utilities;
  4. using System.Collections.Generic;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. namespace Ryujinx.HLE.HOS.Services.Bsd
  9. {
  10. class IClient : IpcService
  11. {
  12. private static Dictionary<WsaError, LinuxError> _errorMap = new Dictionary<WsaError, LinuxError>
  13. {
  14. // WSAEINTR
  15. {WsaError.WSAEINTR, LinuxError.EINTR},
  16. // WSAEWOULDBLOCK
  17. {WsaError.WSAEWOULDBLOCK, LinuxError.EWOULDBLOCK},
  18. // WSAEINPROGRESS
  19. {WsaError.WSAEINPROGRESS, LinuxError.EINPROGRESS},
  20. // WSAEALREADY
  21. {WsaError.WSAEALREADY, LinuxError.EALREADY},
  22. // WSAENOTSOCK
  23. {WsaError.WSAENOTSOCK, LinuxError.ENOTSOCK},
  24. // WSAEDESTADDRREQ
  25. {WsaError.WSAEDESTADDRREQ, LinuxError.EDESTADDRREQ},
  26. // WSAEMSGSIZE
  27. {WsaError.WSAEMSGSIZE, LinuxError.EMSGSIZE},
  28. // WSAEPROTOTYPE
  29. {WsaError.WSAEPROTOTYPE, LinuxError.EPROTOTYPE},
  30. // WSAENOPROTOOPT
  31. {WsaError.WSAENOPROTOOPT, LinuxError.ENOPROTOOPT},
  32. // WSAEPROTONOSUPPORT
  33. {WsaError.WSAEPROTONOSUPPORT, LinuxError.EPROTONOSUPPORT},
  34. // WSAESOCKTNOSUPPORT
  35. {WsaError.WSAESOCKTNOSUPPORT, LinuxError.ESOCKTNOSUPPORT},
  36. // WSAEOPNOTSUPP
  37. {WsaError.WSAEOPNOTSUPP, LinuxError.EOPNOTSUPP},
  38. // WSAEPFNOSUPPORT
  39. {WsaError.WSAEPFNOSUPPORT, LinuxError.EPFNOSUPPORT},
  40. // WSAEAFNOSUPPORT
  41. {WsaError.WSAEAFNOSUPPORT, LinuxError.EAFNOSUPPORT},
  42. // WSAEADDRINUSE
  43. {WsaError.WSAEADDRINUSE, LinuxError.EADDRINUSE},
  44. // WSAEADDRNOTAVAIL
  45. {WsaError.WSAEADDRNOTAVAIL, LinuxError.EADDRNOTAVAIL},
  46. // WSAENETDOWN
  47. {WsaError.WSAENETDOWN, LinuxError.ENETDOWN},
  48. // WSAENETUNREACH
  49. {WsaError.WSAENETUNREACH, LinuxError.ENETUNREACH},
  50. // WSAENETRESET
  51. {WsaError.WSAENETRESET, LinuxError.ENETRESET},
  52. // WSAECONNABORTED
  53. {WsaError.WSAECONNABORTED, LinuxError.ECONNABORTED},
  54. // WSAECONNRESET
  55. {WsaError.WSAECONNRESET, LinuxError.ECONNRESET},
  56. // WSAENOBUFS
  57. {WsaError.WSAENOBUFS, LinuxError.ENOBUFS},
  58. // WSAEISCONN
  59. {WsaError.WSAEISCONN, LinuxError.EISCONN},
  60. // WSAENOTCONN
  61. {WsaError.WSAENOTCONN, LinuxError.ENOTCONN},
  62. // WSAESHUTDOWN
  63. {WsaError.WSAESHUTDOWN, LinuxError.ESHUTDOWN},
  64. // WSAETOOMANYREFS
  65. {WsaError.WSAETOOMANYREFS, LinuxError.ETOOMANYREFS},
  66. // WSAETIMEDOUT
  67. {WsaError.WSAETIMEDOUT, LinuxError.ETIMEDOUT},
  68. // WSAECONNREFUSED
  69. {WsaError.WSAECONNREFUSED, LinuxError.ECONNREFUSED},
  70. // WSAELOOP
  71. {WsaError.WSAELOOP, LinuxError.ELOOP},
  72. // WSAENAMETOOLONG
  73. {WsaError.WSAENAMETOOLONG, LinuxError.ENAMETOOLONG},
  74. // WSAEHOSTDOWN
  75. {WsaError.WSAEHOSTDOWN, LinuxError.EHOSTDOWN},
  76. // WSAEHOSTUNREACH
  77. {WsaError.WSAEHOSTUNREACH, LinuxError.EHOSTUNREACH},
  78. // WSAENOTEMPTY
  79. {WsaError.WSAENOTEMPTY, LinuxError.ENOTEMPTY},
  80. // WSAEUSERS
  81. {WsaError.WSAEUSERS, LinuxError.EUSERS},
  82. // WSAEDQUOT
  83. {WsaError.WSAEDQUOT, LinuxError.EDQUOT},
  84. // WSAESTALE
  85. {WsaError.WSAESTALE, LinuxError.ESTALE},
  86. // WSAEREMOTE
  87. {WsaError.WSAEREMOTE, LinuxError.EREMOTE},
  88. // WSAEINVAL
  89. {WsaError.WSAEINVAL, LinuxError.EINVAL},
  90. // WSAEFAULT
  91. {WsaError.WSAEFAULT, LinuxError.EFAULT},
  92. // NOERROR
  93. {0, 0}
  94. };
  95. private Dictionary<int, ServiceProcessRequest> _commands;
  96. public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
  97. private bool _isPrivileged;
  98. private List<BsdSocket> _sockets = new List<BsdSocket>();
  99. public IClient(bool isPrivileged)
  100. {
  101. _commands = new Dictionary<int, ServiceProcessRequest>
  102. {
  103. { 0, RegisterClient },
  104. { 1, StartMonitoring },
  105. { 2, Socket },
  106. { 3, SocketExempt },
  107. { 4, Open },
  108. { 5, Select },
  109. { 6, Poll },
  110. { 7, Sysctl },
  111. { 8, Recv },
  112. { 9, RecvFrom },
  113. { 10, Send },
  114. { 11, SendTo },
  115. { 12, Accept },
  116. { 13, Bind },
  117. { 14, Connect },
  118. { 15, GetPeerName },
  119. { 16, GetSockName },
  120. { 17, GetSockOpt },
  121. { 18, Listen },
  122. { 19, Ioctl },
  123. { 20, Fcntl },
  124. { 21, SetSockOpt },
  125. { 22, Shutdown },
  126. { 23, ShutdownAllSockets },
  127. { 24, Write },
  128. { 25, Read },
  129. { 26, Close },
  130. { 27, DuplicateSocket }
  131. };
  132. _isPrivileged = isPrivileged;
  133. }
  134. private LinuxError ConvertError(WsaError errorCode)
  135. {
  136. LinuxError errno;
  137. if (!_errorMap.TryGetValue(errorCode, out errno))
  138. {
  139. errno = (LinuxError)errorCode;
  140. }
  141. return errno;
  142. }
  143. private long WriteWinSock2Error(ServiceCtx context, WsaError errorCode)
  144. {
  145. return WriteBsdResult(context, -1, ConvertError(errorCode));
  146. }
  147. private long WriteBsdResult(ServiceCtx context, int result, LinuxError errorCode = 0)
  148. {
  149. if (errorCode != LinuxError.SUCCESS)
  150. {
  151. result = -1;
  152. }
  153. context.ResponseData.Write(result);
  154. context.ResponseData.Write((int)errorCode);
  155. return 0;
  156. }
  157. private BsdSocket RetrieveSocket(int socketFd)
  158. {
  159. if (socketFd >= 0 && _sockets.Count > socketFd)
  160. {
  161. return _sockets[socketFd];
  162. }
  163. return null;
  164. }
  165. private LinuxError SetResultErrno(Socket socket, int result)
  166. {
  167. return result == 0 && !socket.Blocking ? LinuxError.EWOULDBLOCK : LinuxError.SUCCESS;
  168. }
  169. private AddressFamily ConvertFromBsd(int domain)
  170. {
  171. if (domain == 2)
  172. {
  173. return AddressFamily.InterNetwork;
  174. }
  175. // FIXME: AF_ROUTE ignored, is that really needed?
  176. return AddressFamily.Unknown;
  177. }
  178. private long SocketInternal(ServiceCtx context, bool exempt)
  179. {
  180. AddressFamily domain = (AddressFamily)context.RequestData.ReadInt32();
  181. SocketType type = (SocketType)context.RequestData.ReadInt32();
  182. ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32();
  183. if (domain == AddressFamily.Unknown)
  184. {
  185. return WriteBsdResult(context, -1, LinuxError.EPROTONOSUPPORT);
  186. }
  187. else if ((type == SocketType.Seqpacket || type == SocketType.Raw) && !_isPrivileged)
  188. {
  189. if (domain != AddressFamily.InterNetwork || type != SocketType.Raw || protocol != ProtocolType.Icmp)
  190. {
  191. return WriteBsdResult(context, -1, LinuxError.ENOENT);
  192. }
  193. }
  194. BsdSocket newBsdSocket = new BsdSocket
  195. {
  196. Family = (int)domain,
  197. Type = (int)type,
  198. Protocol = (int)protocol,
  199. Handle = new Socket(domain, type, protocol)
  200. };
  201. _sockets.Add(newBsdSocket);
  202. if (exempt)
  203. {
  204. newBsdSocket.Handle.Disconnect(true);
  205. }
  206. return WriteBsdResult(context, _sockets.Count - 1);
  207. }
  208. private IPEndPoint ParseSockAddr(ServiceCtx context, long bufferPosition, long bufferSize)
  209. {
  210. int size = context.Memory.ReadByte(bufferPosition);
  211. int family = context.Memory.ReadByte(bufferPosition + 1);
  212. int port = EndianSwap.Swap16(context.Memory.ReadUInt16(bufferPosition + 2));
  213. byte[] rawIp = context.Memory.ReadBytes(bufferPosition + 4, 4);
  214. return new IPEndPoint(new IPAddress(rawIp), port);
  215. }
  216. private void WriteSockAddr(ServiceCtx context, long bufferPosition, IPEndPoint endPoint)
  217. {
  218. context.Memory.WriteByte(bufferPosition, 0);
  219. context.Memory.WriteByte(bufferPosition + 1, (byte)endPoint.AddressFamily);
  220. context.Memory.WriteUInt16(bufferPosition + 2, EndianSwap.Swap16((ushort)endPoint.Port));
  221. context.Memory.WriteBytes(bufferPosition + 4, endPoint.Address.GetAddressBytes());
  222. }
  223. private void WriteSockAddr(ServiceCtx context, long bufferPosition, BsdSocket socket, bool isRemote)
  224. {
  225. IPEndPoint endPoint = (isRemote ? socket.Handle.RemoteEndPoint : socket.Handle.LocalEndPoint) as IPEndPoint;
  226. WriteSockAddr(context, bufferPosition, endPoint);
  227. }
  228. // Initialize(nn::socket::BsdBufferConfig config, u64 pid, u64 transferMemorySize, KObject<copy, transfer_memory>, pid) -> u32 bsd_errno
  229. public long RegisterClient(ServiceCtx context)
  230. {
  231. /*
  232. typedef struct {
  233. u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0.
  234. u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed).
  235. u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed).
  236. 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.
  237. 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.
  238. u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes).
  239. u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes).
  240. u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8).
  241. } BsdBufferConfig;
  242. */
  243. // bsd_error
  244. context.ResponseData.Write(0);
  245. Logger.PrintStub(LogClass.ServiceBsd, "Stubbed.");
  246. return 0;
  247. }
  248. // StartMonitoring(u64, pid)
  249. public long StartMonitoring(ServiceCtx context)
  250. {
  251. ulong unknown0 = context.RequestData.ReadUInt64();
  252. Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed. Unknown0: {unknown0}");
  253. return 0;
  254. }
  255. // Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
  256. public long Socket(ServiceCtx context)
  257. {
  258. return SocketInternal(context, false);
  259. }
  260. // SocketExempt(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
  261. public long SocketExempt(ServiceCtx context)
  262. {
  263. return SocketInternal(context, true);
  264. }
  265. // Open(u32 flags, array<unknown, 0x21> path) -> (i32 ret, u32 bsd_errno)
  266. public long Open(ServiceCtx context)
  267. {
  268. (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
  269. int flags = context.RequestData.ReadInt32();
  270. byte[] rawPath = context.Memory.ReadBytes(bufferPosition, bufferSize);
  271. string path = Encoding.ASCII.GetString(rawPath);
  272. WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP);
  273. Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed. Path: {path} - " +
  274. $"Flags: {flags}");
  275. return 0;
  276. }
  277. // 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)
  278. public long Select(ServiceCtx context)
  279. {
  280. WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP);
  281. Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed.");
  282. return 0;
  283. }
  284. // Poll(u32 nfds, u32 timeout, buffer<unknown, 0x21, 0> fds) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>)
  285. public long Poll(ServiceCtx context)
  286. {
  287. int fdsCount = context.RequestData.ReadInt32();
  288. int timeout = context.RequestData.ReadInt32();
  289. (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
  290. if (timeout < -1 || fdsCount < 0 || (fdsCount * 8) > bufferSize)
  291. {
  292. return WriteBsdResult(context, -1, LinuxError.EINVAL);
  293. }
  294. PollEvent[] events = new PollEvent[fdsCount];
  295. for (int i = 0; i < fdsCount; i++)
  296. {
  297. int socketFd = context.Memory.ReadInt32(bufferPosition + i * 8);
  298. BsdSocket socket = RetrieveSocket(socketFd);
  299. if (socket == null)
  300. {
  301. return WriteBsdResult(context, -1, LinuxError.EBADF);}
  302. PollEvent.EventTypeMask inputEvents = (PollEvent.EventTypeMask)context.Memory.ReadInt16(bufferPosition + i * 8 + 4);
  303. PollEvent.EventTypeMask outputEvents = (PollEvent.EventTypeMask)context.Memory.ReadInt16(bufferPosition + i * 8 + 6);
  304. events[i] = new PollEvent(socketFd, socket, inputEvents, outputEvents);
  305. }
  306. List<Socket> readEvents = new List<Socket>();
  307. List<Socket> writeEvents = new List<Socket>();
  308. List<Socket> errorEvents = new List<Socket>();
  309. foreach (PollEvent Event in events)
  310. {
  311. bool isValidEvent = false;
  312. if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0)
  313. {
  314. readEvents.Add(Event.Socket.Handle);
  315. errorEvents.Add(Event.Socket.Handle);
  316. isValidEvent = true;
  317. }
  318. if ((Event.InputEvents & PollEvent.EventTypeMask.UrgentInput) != 0)
  319. {
  320. readEvents.Add(Event.Socket.Handle);
  321. errorEvents.Add(Event.Socket.Handle);
  322. isValidEvent = true;
  323. }
  324. if ((Event.InputEvents & PollEvent.EventTypeMask.Output) != 0)
  325. {
  326. writeEvents.Add(Event.Socket.Handle);
  327. errorEvents.Add(Event.Socket.Handle);
  328. isValidEvent = true;
  329. }
  330. if ((Event.InputEvents & PollEvent.EventTypeMask.Error) != 0)
  331. {
  332. errorEvents.Add(Event.Socket.Handle);
  333. isValidEvent = true;
  334. }
  335. if (!isValidEvent)
  336. {
  337. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Poll input event type: {Event.InputEvents}");
  338. return WriteBsdResult(context, -1, LinuxError.EINVAL);
  339. }
  340. }
  341. try
  342. {
  343. System.Net.Sockets.Socket.Select(readEvents, writeEvents, errorEvents, timeout);
  344. }
  345. catch (SocketException exception)
  346. {
  347. return WriteWinSock2Error(context, (WsaError)exception.ErrorCode);
  348. }
  349. for (int i = 0; i < fdsCount; i++)
  350. {
  351. PollEvent Event = events[i];
  352. context.Memory.WriteInt32(bufferPosition + i * 8, Event.SocketFd);
  353. context.Memory.WriteInt16(bufferPosition + i * 8 + 4, (short)Event.InputEvents);
  354. PollEvent.EventTypeMask outputEvents = 0;
  355. Socket socket = Event.Socket.Handle;
  356. if (errorEvents.Contains(socket))
  357. {
  358. outputEvents |= PollEvent.EventTypeMask.Error;
  359. if (!socket.Connected || !socket.IsBound)
  360. {
  361. outputEvents |= PollEvent.EventTypeMask.Disconnected;
  362. }
  363. }
  364. if (readEvents.Contains(socket))
  365. {
  366. if ((Event.InputEvents & PollEvent.EventTypeMask.Input) != 0)
  367. {
  368. outputEvents |= PollEvent.EventTypeMask.Input;
  369. }
  370. }
  371. if (writeEvents.Contains(socket))
  372. {
  373. outputEvents |= PollEvent.EventTypeMask.Output;
  374. }
  375. context.Memory.WriteInt16(bufferPosition + i * 8 + 6, (short)outputEvents);
  376. }
  377. return WriteBsdResult(context, readEvents.Count + writeEvents.Count + errorEvents.Count, LinuxError.SUCCESS);
  378. }
  379. // Sysctl(buffer<unknown, 0x21, 0>, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>)
  380. public long Sysctl(ServiceCtx context)
  381. {
  382. WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP);
  383. Logger.PrintStub(LogClass.ServiceBsd, $"Stubbed.");
  384. return 0;
  385. }
  386. // Recv(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, array<i8, 0x22> message)
  387. public long Recv(ServiceCtx context)
  388. {
  389. int socketFd = context.RequestData.ReadInt32();
  390. SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
  391. (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22();
  392. LinuxError errno = LinuxError.EBADF;
  393. BsdSocket socket = RetrieveSocket(socketFd);
  394. int result = -1;
  395. if (socket != null)
  396. {
  397. if (socketFlags != SocketFlags.None && (socketFlags & SocketFlags.OutOfBand) == 0
  398. && (socketFlags & SocketFlags.Peek) == 0)
  399. {
  400. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Recv flags: {socketFlags}");
  401. return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP);
  402. }
  403. byte[] receivedBuffer = new byte[receiveLength];
  404. try
  405. {
  406. result = socket.Handle.Receive(receivedBuffer, socketFlags);
  407. errno = SetResultErrno(socket.Handle, result);
  408. context.Memory.WriteBytes(receivePosition, receivedBuffer);
  409. }
  410. catch (SocketException exception)
  411. {
  412. errno = ConvertError((WsaError)exception.ErrorCode);
  413. }
  414. }
  415. return WriteBsdResult(context, result, errno);
  416. }
  417. // RecvFrom(u32 sock, u32 flags) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<i8, 0x22, 0> message, buffer<nn::socket::sockaddr_in, 0x22, 0x10>)
  418. public long RecvFrom(ServiceCtx context)
  419. {
  420. int socketFd = context.RequestData.ReadInt32();
  421. SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
  422. (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22();
  423. (long sockAddrInPosition, long sockAddrInSize) = context.Request.GetBufferType0x21();
  424. (long sockAddrOutPosition, long sockAddrOutSize) = context.Request.GetBufferType0x22(1);
  425. LinuxError errno = LinuxError.EBADF;
  426. BsdSocket socket = RetrieveSocket(socketFd);
  427. int result = -1;
  428. if (socket != null)
  429. {
  430. if (socketFlags != SocketFlags.None && (socketFlags & SocketFlags.OutOfBand) == 0
  431. && (socketFlags & SocketFlags.Peek) == 0)
  432. {
  433. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Recv flags: {socketFlags}");
  434. return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP);
  435. }
  436. byte[] receivedBuffer = new byte[receiveLength];
  437. EndPoint endPoint = ParseSockAddr(context, sockAddrInPosition, sockAddrInSize);
  438. try
  439. {
  440. result = socket.Handle.ReceiveFrom(receivedBuffer, receivedBuffer.Length, socketFlags, ref endPoint);
  441. errno = SetResultErrno(socket.Handle, result);
  442. context.Memory.WriteBytes(receivePosition, receivedBuffer);
  443. WriteSockAddr(context, sockAddrOutPosition, (IPEndPoint)endPoint);
  444. }
  445. catch (SocketException exception)
  446. {
  447. errno = ConvertError((WsaError)exception.ErrorCode);
  448. }
  449. }
  450. return WriteBsdResult(context, result, errno);
  451. }
  452. // Send(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
  453. public long Send(ServiceCtx context)
  454. {
  455. int socketFd = context.RequestData.ReadInt32();
  456. SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
  457. (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
  458. LinuxError errno = LinuxError.EBADF;
  459. BsdSocket socket = RetrieveSocket(socketFd);
  460. int result = -1;
  461. if (socket != null)
  462. {
  463. if (socketFlags != SocketFlags.None && socketFlags != SocketFlags.OutOfBand
  464. && socketFlags != SocketFlags.Peek && socketFlags != SocketFlags.DontRoute)
  465. {
  466. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Send flags: {socketFlags}");
  467. return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP);
  468. }
  469. byte[] sendBuffer = context.Memory.ReadBytes(sendPosition, sendSize);
  470. try
  471. {
  472. result = socket.Handle.Send(sendBuffer, socketFlags);
  473. errno = SetResultErrno(socket.Handle, result);
  474. }
  475. catch (SocketException exception)
  476. {
  477. errno = ConvertError((WsaError)exception.ErrorCode);
  478. }
  479. }
  480. return WriteBsdResult(context, result, errno);
  481. }
  482. // SendTo(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<nn::socket::sockaddr_in, 0x21, 0x10>) -> (i32 ret, u32 bsd_errno)
  483. public long SendTo(ServiceCtx context)
  484. {
  485. int socketFd = context.RequestData.ReadInt32();
  486. SocketFlags socketFlags = (SocketFlags)context.RequestData.ReadInt32();
  487. (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
  488. (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21(1);
  489. LinuxError errno = LinuxError.EBADF;
  490. BsdSocket socket = RetrieveSocket(socketFd);
  491. int result = -1;
  492. if (socket != null)
  493. {
  494. if (socketFlags != SocketFlags.None && socketFlags != SocketFlags.OutOfBand
  495. && socketFlags != SocketFlags.Peek && socketFlags != SocketFlags.DontRoute)
  496. {
  497. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Send flags: {socketFlags}");
  498. return WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP);
  499. }
  500. byte[] sendBuffer = context.Memory.ReadBytes(sendPosition, sendSize);
  501. EndPoint endPoint = ParseSockAddr(context, bufferPosition, bufferSize);
  502. try
  503. {
  504. result = socket.Handle.SendTo(sendBuffer, sendBuffer.Length, socketFlags, endPoint);
  505. errno = SetResultErrno(socket.Handle, result);
  506. }
  507. catch (SocketException exception)
  508. {
  509. errno = ConvertError((WsaError)exception.ErrorCode);
  510. }
  511. }
  512. return WriteBsdResult(context, result, errno);
  513. }
  514. // Accept(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr)
  515. public long Accept(ServiceCtx context)
  516. {
  517. int socketFd = context.RequestData.ReadInt32();
  518. (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22();
  519. LinuxError errno = LinuxError.EBADF;
  520. BsdSocket socket = RetrieveSocket(socketFd);
  521. if (socket != null)
  522. {
  523. errno = LinuxError.SUCCESS;
  524. Socket newSocket = null;
  525. try
  526. {
  527. newSocket = socket.Handle.Accept();
  528. }
  529. catch (SocketException exception)
  530. {
  531. errno = ConvertError((WsaError)exception.ErrorCode);
  532. }
  533. if (newSocket == null && errno == LinuxError.SUCCESS)
  534. {
  535. errno = LinuxError.EWOULDBLOCK;
  536. }
  537. else if (errno == LinuxError.SUCCESS)
  538. {
  539. BsdSocket newBsdSocket = new BsdSocket
  540. {
  541. Family = (int)newSocket.AddressFamily,
  542. Type = (int)newSocket.SocketType,
  543. Protocol = (int)newSocket.ProtocolType,
  544. Handle = newSocket
  545. };
  546. _sockets.Add(newBsdSocket);
  547. WriteSockAddr(context, bufferPos, newBsdSocket, true);
  548. WriteBsdResult(context, _sockets.Count - 1, errno);
  549. context.ResponseData.Write(0x10);
  550. return 0;
  551. }
  552. }
  553. return WriteBsdResult(context, -1, errno);
  554. }
  555. // Bind(u32 socket, buffer<nn::socket::sockaddr_in, 0x21, 0x10> addr) -> (i32 ret, u32 bsd_errno)
  556. public long Bind(ServiceCtx context)
  557. {
  558. int socketFd = context.RequestData.ReadInt32();
  559. (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21();
  560. LinuxError errno = LinuxError.EBADF;
  561. BsdSocket socket = RetrieveSocket(socketFd);
  562. if (socket != null)
  563. {
  564. errno = LinuxError.SUCCESS;
  565. try
  566. {
  567. IPEndPoint endPoint = ParseSockAddr(context, bufferPos, bufferSize);
  568. socket.Handle.Bind(endPoint);
  569. }
  570. catch (SocketException exception)
  571. {
  572. errno = ConvertError((WsaError)exception.ErrorCode);
  573. }
  574. }
  575. return WriteBsdResult(context, 0, errno);
  576. }
  577. // Connect(u32 socket, buffer<nn::socket::sockaddr_in, 0x21, 0x10>) -> (i32 ret, u32 bsd_errno)
  578. public long Connect(ServiceCtx context)
  579. {
  580. int socketFd = context.RequestData.ReadInt32();
  581. (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21();
  582. LinuxError errno = LinuxError.EBADF;
  583. BsdSocket socket = RetrieveSocket(socketFd);
  584. if (socket != null)
  585. {
  586. errno = LinuxError.SUCCESS;
  587. try
  588. {
  589. IPEndPoint endPoint = ParseSockAddr(context, bufferPos, bufferSize);
  590. socket.Handle.Connect(endPoint);
  591. }
  592. catch (SocketException exception)
  593. {
  594. errno = ConvertError((WsaError)exception.ErrorCode);
  595. }
  596. }
  597. return WriteBsdResult(context, 0, errno);
  598. }
  599. // GetPeerName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr)
  600. public long GetPeerName(ServiceCtx context)
  601. {
  602. int socketFd = context.RequestData.ReadInt32();
  603. (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22();
  604. LinuxError errno = LinuxError.EBADF;
  605. BsdSocket socket = RetrieveSocket(socketFd);
  606. if (socket != null)
  607. {
  608. errno = LinuxError.SUCCESS;
  609. WriteSockAddr(context, bufferPos, socket, true);
  610. WriteBsdResult(context, 0, errno);
  611. context.ResponseData.Write(0x10);
  612. }
  613. return WriteBsdResult(context, 0, errno);
  614. }
  615. // GetSockName(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<nn::socket::sockaddr_in, 0x22, 0x10> addr)
  616. public long GetSockName(ServiceCtx context)
  617. {
  618. int socketFd = context.RequestData.ReadInt32();
  619. (long bufferPos, long bufferSize) = context.Request.GetBufferType0x22();
  620. LinuxError errno = LinuxError.EBADF;
  621. BsdSocket socket = RetrieveSocket(socketFd);
  622. if (socket != null)
  623. {
  624. errno = LinuxError.SUCCESS;
  625. WriteSockAddr(context, bufferPos, socket, false);
  626. WriteBsdResult(context, 0, errno);
  627. context.ResponseData.Write(0x10);
  628. }
  629. return WriteBsdResult(context, 0, errno);
  630. }
  631. // GetSockOpt(u32 socket, u32 level, u32 option_name) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>)
  632. public long GetSockOpt(ServiceCtx context)
  633. {
  634. int socketFd = context.RequestData.ReadInt32();
  635. int level = context.RequestData.ReadInt32();
  636. int optionName = context.RequestData.ReadInt32();
  637. (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
  638. LinuxError errno = LinuxError.EBADF;
  639. BsdSocket socket = RetrieveSocket(socketFd);
  640. if (socket != null)
  641. {
  642. errno = LinuxError.ENOPROTOOPT;
  643. if (level == 0xFFFF)
  644. {
  645. errno = HandleGetSocketOption(context, socket, (SocketOptionName)optionName, bufferPosition, bufferSize);
  646. }
  647. else
  648. {
  649. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported GetSockOpt Level: {(SocketOptionLevel)level}");
  650. }
  651. }
  652. return WriteBsdResult(context, 0, errno);
  653. }
  654. // Listen(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno)
  655. public long Listen(ServiceCtx context)
  656. {
  657. int socketFd = context.RequestData.ReadInt32();
  658. int backlog = context.RequestData.ReadInt32();
  659. LinuxError errno = LinuxError.EBADF;
  660. BsdSocket socket = RetrieveSocket(socketFd);
  661. if (socket != null)
  662. {
  663. errno = LinuxError.SUCCESS;
  664. try
  665. {
  666. socket.Handle.Listen(backlog);
  667. }
  668. catch (SocketException exception)
  669. {
  670. errno = ConvertError((WsaError)exception.ErrorCode);
  671. }
  672. }
  673. return WriteBsdResult(context, 0, errno);
  674. }
  675. // 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>)
  676. public long Ioctl(ServiceCtx context)
  677. {
  678. int socketFd = context.RequestData.ReadInt32();
  679. BsdIoctl cmd = (BsdIoctl)context.RequestData.ReadInt32();
  680. int bufferCount = context.RequestData.ReadInt32();
  681. LinuxError errno = LinuxError.EBADF;
  682. BsdSocket socket = RetrieveSocket(socketFd);
  683. if (socket != null)
  684. {
  685. switch (cmd)
  686. {
  687. case BsdIoctl.AtMark:
  688. errno = LinuxError.SUCCESS;
  689. (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22();
  690. // FIXME: OOB not implemented.
  691. context.Memory.WriteInt32(bufferPosition, 0);
  692. break;
  693. default:
  694. errno = LinuxError.EOPNOTSUPP;
  695. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported Ioctl Cmd: {cmd}");
  696. break;
  697. }
  698. }
  699. return WriteBsdResult(context, 0, errno);
  700. }
  701. // Fcntl(u32 socket, u32 cmd, u32 arg) -> (i32 ret, u32 bsd_errno)
  702. public long Fcntl(ServiceCtx context)
  703. {
  704. int socketFd = context.RequestData.ReadInt32();
  705. int cmd = context.RequestData.ReadInt32();
  706. int arg = context.RequestData.ReadInt32();
  707. int result = 0;
  708. LinuxError errno = LinuxError.EBADF;
  709. BsdSocket socket = RetrieveSocket(socketFd);
  710. if (socket != null)
  711. {
  712. errno = LinuxError.SUCCESS;
  713. if (cmd == 0x3)
  714. {
  715. result = !socket.Handle.Blocking ? 0x800 : 0;
  716. }
  717. else if (cmd == 0x4 && arg == 0x800)
  718. {
  719. socket.Handle.Blocking = false;
  720. result = 0;
  721. }
  722. else
  723. {
  724. errno = LinuxError.EOPNOTSUPP;
  725. }
  726. }
  727. return WriteBsdResult(context, result, errno);
  728. }
  729. private LinuxError HandleGetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize)
  730. {
  731. try
  732. {
  733. byte[] optionValue = new byte[optionValueSize];
  734. switch (optionName)
  735. {
  736. case SocketOptionName.Broadcast:
  737. case SocketOptionName.DontLinger:
  738. case SocketOptionName.Debug:
  739. case SocketOptionName.Error:
  740. case SocketOptionName.KeepAlive:
  741. case SocketOptionName.OutOfBandInline:
  742. case SocketOptionName.ReceiveBuffer:
  743. case SocketOptionName.ReceiveTimeout:
  744. case SocketOptionName.SendBuffer:
  745. case SocketOptionName.SendTimeout:
  746. case SocketOptionName.Type:
  747. case SocketOptionName.Linger:
  748. socket.Handle.GetSocketOption(SocketOptionLevel.Socket, optionName, optionValue);
  749. context.Memory.WriteBytes(optionValuePosition, optionValue);
  750. return LinuxError.SUCCESS;
  751. case (SocketOptionName)0x200:
  752. socket.Handle.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, optionValue);
  753. context.Memory.WriteBytes(optionValuePosition, optionValue);
  754. return LinuxError.SUCCESS;
  755. default:
  756. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {optionName}");
  757. return LinuxError.EOPNOTSUPP;
  758. }
  759. }
  760. catch (SocketException exception)
  761. {
  762. return ConvertError((WsaError)exception.ErrorCode);
  763. }
  764. }
  765. private LinuxError HandleSetSocketOption(ServiceCtx context, BsdSocket socket, SocketOptionName optionName, long optionValuePosition, long optionValueSize)
  766. {
  767. try
  768. {
  769. switch (optionName)
  770. {
  771. case SocketOptionName.Broadcast:
  772. case SocketOptionName.DontLinger:
  773. case SocketOptionName.Debug:
  774. case SocketOptionName.Error:
  775. case SocketOptionName.KeepAlive:
  776. case SocketOptionName.OutOfBandInline:
  777. case SocketOptionName.ReceiveBuffer:
  778. case SocketOptionName.ReceiveTimeout:
  779. case SocketOptionName.SendBuffer:
  780. case SocketOptionName.SendTimeout:
  781. case SocketOptionName.Type:
  782. case SocketOptionName.ReuseAddress:
  783. socket.Handle.SetSocketOption(SocketOptionLevel.Socket, optionName, context.Memory.ReadInt32(optionValuePosition));
  784. return LinuxError.SUCCESS;
  785. case (SocketOptionName)0x200:
  786. socket.Handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, context.Memory.ReadInt32(optionValuePosition));
  787. return LinuxError.SUCCESS;
  788. case SocketOptionName.Linger:
  789. socket.Handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger,
  790. new LingerOption(context.Memory.ReadInt32(optionValuePosition) != 0, context.Memory.ReadInt32(optionValuePosition + 4)));
  791. return LinuxError.SUCCESS;
  792. default:
  793. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {optionName}");
  794. return LinuxError.EOPNOTSUPP;
  795. }
  796. }
  797. catch (SocketException exception)
  798. {
  799. return ConvertError((WsaError)exception.ErrorCode);
  800. }
  801. }
  802. // SetSockOpt(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0> option_value) -> (i32 ret, u32 bsd_errno)
  803. public long SetSockOpt(ServiceCtx context)
  804. {
  805. int socketFd = context.RequestData.ReadInt32();
  806. int level = context.RequestData.ReadInt32();
  807. int optionName = context.RequestData.ReadInt32();
  808. (long bufferPos, long bufferSize) = context.Request.GetBufferType0x21();
  809. LinuxError errno = LinuxError.EBADF;
  810. BsdSocket socket = RetrieveSocket(socketFd);
  811. if (socket != null)
  812. {
  813. errno = LinuxError.ENOPROTOOPT;
  814. if (level == 0xFFFF)
  815. {
  816. errno = HandleSetSocketOption(context, socket, (SocketOptionName)optionName, bufferPos, bufferSize);
  817. }
  818. else
  819. {
  820. Logger.PrintWarning(LogClass.ServiceBsd, $"Unsupported SetSockOpt Level: {(SocketOptionLevel)level}");
  821. }
  822. }
  823. return WriteBsdResult(context, 0, errno);
  824. }
  825. // Shutdown(u32 socket, u32 how) -> (i32 ret, u32 bsd_errno)
  826. public long Shutdown(ServiceCtx context)
  827. {
  828. int socketFd = context.RequestData.ReadInt32();
  829. int how = context.RequestData.ReadInt32();
  830. LinuxError errno = LinuxError.EBADF;
  831. BsdSocket socket = RetrieveSocket(socketFd);
  832. if (socket != null)
  833. {
  834. errno = LinuxError.EINVAL;
  835. if (how >= 0 && how <= 2)
  836. {
  837. errno = LinuxError.SUCCESS;
  838. try
  839. {
  840. socket.Handle.Shutdown((SocketShutdown)how);
  841. }
  842. catch (SocketException exception)
  843. {
  844. errno = ConvertError((WsaError)exception.ErrorCode);
  845. }
  846. }
  847. }
  848. return WriteBsdResult(context, 0, errno);
  849. }
  850. // ShutdownAllSockets(u32 how) -> (i32 ret, u32 bsd_errno)
  851. public long ShutdownAllSockets(ServiceCtx context)
  852. {
  853. int how = context.RequestData.ReadInt32();
  854. LinuxError errno = LinuxError.EINVAL;
  855. if (how >= 0 && how <= 2)
  856. {
  857. errno = LinuxError.SUCCESS;
  858. foreach (BsdSocket socket in _sockets)
  859. {
  860. if (socket != null)
  861. {
  862. try
  863. {
  864. socket.Handle.Shutdown((SocketShutdown)how);
  865. }
  866. catch (SocketException exception)
  867. {
  868. errno = ConvertError((WsaError)exception.ErrorCode);
  869. break;
  870. }
  871. }
  872. }
  873. }
  874. return WriteBsdResult(context, 0, errno);
  875. }
  876. // Write(u32 socket, buffer<i8, 0x21, 0> message) -> (i32 ret, u32 bsd_errno)
  877. public long Write(ServiceCtx context)
  878. {
  879. int socketFd = context.RequestData.ReadInt32();
  880. (long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
  881. LinuxError errno = LinuxError.EBADF;
  882. BsdSocket socket = RetrieveSocket(socketFd);
  883. int result = -1;
  884. if (socket != null)
  885. {
  886. byte[] sendBuffer = context.Memory.ReadBytes(sendPosition, sendSize);
  887. try
  888. {
  889. result = socket.Handle.Send(sendBuffer);
  890. errno = SetResultErrno(socket.Handle, result);
  891. }
  892. catch (SocketException exception)
  893. {
  894. errno = ConvertError((WsaError)exception.ErrorCode);
  895. }
  896. }
  897. return WriteBsdResult(context, result, errno);
  898. }
  899. // Read(u32 socket) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
  900. public long Read(ServiceCtx context)
  901. {
  902. int socketFd = context.RequestData.ReadInt32();
  903. (long receivePosition, long receiveLength) = context.Request.GetBufferType0x22();
  904. LinuxError errno = LinuxError.EBADF;
  905. BsdSocket socket = RetrieveSocket(socketFd);
  906. int result = -1;
  907. if (socket != null)
  908. {
  909. byte[] receivedBuffer = new byte[receiveLength];
  910. try
  911. {
  912. result = socket.Handle.Receive(receivedBuffer);
  913. errno = SetResultErrno(socket.Handle, result);
  914. }
  915. catch (SocketException exception)
  916. {
  917. errno = ConvertError((WsaError)exception.ErrorCode);
  918. }
  919. }
  920. return WriteBsdResult(context, result, errno);
  921. }
  922. // Close(u32 socket) -> (i32 ret, u32 bsd_errno)
  923. public long Close(ServiceCtx context)
  924. {
  925. int socketFd = context.RequestData.ReadInt32();
  926. LinuxError errno = LinuxError.EBADF;
  927. BsdSocket socket = RetrieveSocket(socketFd);
  928. if (socket != null)
  929. {
  930. socket.Handle.Close();
  931. _sockets[socketFd] = null;
  932. errno = LinuxError.SUCCESS;
  933. }
  934. return WriteBsdResult(context, 0, errno);
  935. }
  936. // DuplicateSocket(u32 socket, u64 reserved) -> (i32 ret, u32 bsd_errno)
  937. public long DuplicateSocket(ServiceCtx context)
  938. {
  939. int socketFd = context.RequestData.ReadInt32();
  940. ulong reserved = context.RequestData.ReadUInt64();
  941. LinuxError errno = LinuxError.ENOENT;
  942. int newSockFd = -1;
  943. if (_isPrivileged)
  944. {
  945. errno = LinuxError.EBADF;
  946. BsdSocket oldSocket = RetrieveSocket(socketFd);
  947. if (oldSocket != null)
  948. {
  949. _sockets.Add(oldSocket);
  950. newSockFd = _sockets.Count - 1;
  951. }
  952. }
  953. return WriteBsdResult(context, newSockFd, errno);
  954. }
  955. }
  956. }