| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154 |
- using LibHac.Ns;
- using Ryujinx.Common;
- using Ryujinx.Common.Configuration.Multiplayer;
- using Ryujinx.Common.Logging;
- using Ryujinx.Common.Memory;
- using Ryujinx.Common.Utilities;
- using Ryujinx.Cpu;
- using Ryujinx.HLE.HOS.Ipc;
- using Ryujinx.HLE.HOS.Kernel.Threading;
- using Ryujinx.HLE.HOS.Services.Ldn.Types;
- using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm;
- using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu;
- using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
- using Ryujinx.Horizon.Common;
- using Ryujinx.Memory;
- using System;
- using System.IO;
- using System.Net;
- using System.Net.NetworkInformation;
- using System.Runtime.InteropServices;
- namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
- {
- class IUserLocalCommunicationService : IpcService, IDisposable
- {
- public static string DefaultLanPlayHost = "ryuldn.vudjun.com";
- public static short LanPlayPort = 30456;
- public INetworkClient NetworkClient { get; private set; }
- private const int NifmRequestID = 90;
- private const string DefaultIPAddress = "127.0.0.1";
- private const string DefaultSubnetMask = "255.255.255.0";
- private const bool IsDevelopment = false;
- private readonly KEvent _stateChangeEvent;
- private int _stateChangeEventHandle;
- private NetworkState _state;
- private DisconnectReason _disconnectReason;
- private ResultCode _nifmResultCode;
- private AccessPoint _accessPoint;
- private Station _station;
- public IUserLocalCommunicationService(ServiceCtx context)
- {
- _stateChangeEvent = new KEvent(context.Device.System.KernelContext);
- _state = NetworkState.None;
- _disconnectReason = DisconnectReason.None;
- }
- private ushort CheckDevelopmentChannel(ushort channel)
- {
- return (ushort)(!IsDevelopment ? 0 : channel);
- }
- private SecurityMode CheckDevelopmentSecurityMode(SecurityMode securityMode)
- {
- return !IsDevelopment ? SecurityMode.Retail : securityMode;
- }
- private bool CheckLocalCommunicationIdPermission(ServiceCtx context, ulong localCommunicationIdChecked)
- {
- // TODO: Call nn::arp::GetApplicationControlProperty here when implemented.
- ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
- foreach (var localCommunicationId in controlProperty.LocalCommunicationId.ItemsRo)
- {
- if (localCommunicationId == localCommunicationIdChecked)
- {
- return true;
- }
- }
- return false;
- }
- [CommandCmif(0)]
- // GetState() -> s32 state
- public ResultCode GetState(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- context.ResponseData.Write((int)NetworkState.Error);
- return ResultCode.Success;
- }
- // NOTE: Returns ResultCode.InvalidArgument if _state is null, doesn't occur in our case.
- context.ResponseData.Write((int)_state);
- return ResultCode.Success;
- }
- public void SetState()
- {
- _stateChangeEvent.WritableEvent.Signal();
- }
- public void SetState(NetworkState state)
- {
- _state = state;
- SetState();
- }
- [CommandCmif(1)]
- // GetNetworkInfo() -> buffer<network_info<0x480>, 0x1a>
- public ResultCode GetNetworkInfo(ServiceCtx context)
- {
- ulong bufferPosition = context.Request.RecvListBuff[0].Position;
- MemoryHelper.FillWithZeros(context.Memory, bufferPosition, 0x480);
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
- if (resultCode != ResultCode.Success)
- {
- return resultCode;
- }
- ulong infoSize = MemoryHelper.Write(context.Memory, bufferPosition, networkInfo);
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(infoSize);
- return ResultCode.Success;
- }
- private ResultCode GetNetworkInfoImpl(out NetworkInfo networkInfo)
- {
- if (_state == NetworkState.StationConnected)
- {
- networkInfo = _station.NetworkInfo;
- }
- else if (_state == NetworkState.AccessPointCreated)
- {
- networkInfo = _accessPoint.NetworkInfo;
- }
- else
- {
- networkInfo = new NetworkInfo();
- return ResultCode.InvalidState;
- }
- return ResultCode.Success;
- }
- private NodeLatestUpdate[] GetNodeLatestUpdateImpl(int count)
- {
- if (_state == NetworkState.StationConnected)
- {
- return _station.LatestUpdates.ConsumeLatestUpdate(count);
- }
- else if (_state == NetworkState.AccessPointCreated)
- {
- return _accessPoint.LatestUpdates.ConsumeLatestUpdate(count);
- }
- else
- {
- return Array.Empty<NodeLatestUpdate>();
- }
- }
- [CommandCmif(2)]
- // GetIpv4Address() -> (u32 ip_address, u32 subnet_mask)
- public ResultCode GetIpv4Address(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- // NOTE: Return ResultCode.InvalidArgument if ip_address and subnet_mask are null, doesn't occur in our case.
- if (_state == NetworkState.AccessPointCreated || _state == NetworkState.StationConnected)
- {
- ProxyConfig config = _state switch
- {
- NetworkState.AccessPointCreated => _accessPoint.Config,
- NetworkState.StationConnected => _station.Config,
- _ => default
- };
- if (config.ProxyIp == 0)
- {
- (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(context.Device.Configuration.MultiplayerLanInterfaceId);
- if (unicastAddress == null)
- {
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultIPAddress));
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultSubnetMask));
- }
- else
- {
- Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
- context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
- }
- }
- else
- {
- Logger.Info?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
- context.ResponseData.Write(config.ProxyIp);
- context.ResponseData.Write(config.ProxySubnetMask);
- }
- }
- else
- {
- return ResultCode.InvalidArgument;
- }
- return ResultCode.Success;
- }
- [CommandCmif(3)]
- // GetDisconnectReason() -> u16 disconnect_reason
- public ResultCode GetDisconnectReason(ServiceCtx context)
- {
- // NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case.
- context.ResponseData.Write((short)_disconnectReason);
- return ResultCode.Success;
- }
- public void SetDisconnectReason(DisconnectReason reason)
- {
- if (_state != NetworkState.Initialized)
- {
- _disconnectReason = reason;
- SetState(NetworkState.Initialized);
- }
- }
- [CommandCmif(4)]
- // GetSecurityParameter() -> bytes<0x20, 1> security_parameter
- public ResultCode GetSecurityParameter(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
- if (resultCode != ResultCode.Success)
- {
- return resultCode;
- }
- SecurityParameter securityParameter = new()
- {
- Data = new Array16<byte>(),
- SessionId = networkInfo.NetworkId.SessionId,
- };
- context.ResponseData.WriteStruct(securityParameter);
- return ResultCode.Success;
- }
- [CommandCmif(5)]
- // GetNetworkConfig() -> bytes<0x20, 8> network_config
- public ResultCode GetNetworkConfig(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
- if (resultCode != ResultCode.Success)
- {
- return resultCode;
- }
- NetworkConfig networkConfig = new()
- {
- IntentId = networkInfo.NetworkId.IntentId,
- Channel = networkInfo.Common.Channel,
- NodeCountMax = networkInfo.Ldn.NodeCountMax,
- LocalCommunicationVersion = networkInfo.Ldn.Nodes[0].LocalCommunicationVersion,
- Reserved2 = new Array10<byte>(),
- };
- context.ResponseData.WriteStruct(networkConfig);
- return ResultCode.Success;
- }
- [CommandCmif(100)]
- // AttachStateChangeEvent() -> handle<copy>
- public ResultCode AttachStateChangeEvent(ServiceCtx context)
- {
- if (_stateChangeEventHandle == 0 && context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != Result.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangeEventHandle);
- // Returns ResultCode.InvalidArgument if handle is null, doesn't occur in our case since we already throw an Exception.
- return ResultCode.Success;
- }
- [CommandCmif(101)]
- // GetNetworkInfoLatestUpdate() -> (buffer<network_info<0x480>, 0x1a>, buffer<node_latest_update, 0xa>)
- public ResultCode GetNetworkInfoLatestUpdate(ServiceCtx context)
- {
- ulong bufferPosition = context.Request.RecvListBuff[0].Position;
- MemoryHelper.FillWithZeros(context.Memory, bufferPosition, 0x480);
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
- if (resultCode != ResultCode.Success)
- {
- return resultCode;
- }
- ulong outputPosition = context.Request.RecvListBuff[0].Position;
- ulong outputSize = context.Request.RecvListBuff[0].Size;
- ulong latestUpdateSize = (ulong)Marshal.SizeOf<NodeLatestUpdate>();
- int count = (int)(outputSize / latestUpdateSize);
- NodeLatestUpdate[] latestUpdate = GetNodeLatestUpdateImpl(count);
- MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
- foreach (NodeLatestUpdate node in latestUpdate)
- {
- MemoryHelper.Write(context.Memory, outputPosition, node);
- outputPosition += latestUpdateSize;
- }
- ulong infoSize = MemoryHelper.Write(context.Memory, bufferPosition, networkInfo);
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(infoSize);
- return ResultCode.Success;
- }
- [CommandCmif(102)]
- // Scan(u16 channel, bytes<0x60, 8> scan_filter) -> (u16 count, buffer<network_info, 0x22>)
- public ResultCode Scan(ServiceCtx context)
- {
- return ScanImpl(context);
- }
- [CommandCmif(103)]
- // ScanPrivate(u16 channel, bytes<0x60, 8> scan_filter) -> (u16 count, buffer<network_info, 0x22>)
- public ResultCode ScanPrivate(ServiceCtx context)
- {
- return ScanImpl(context, true);
- }
- private ResultCode ScanImpl(ServiceCtx context, bool isPrivate = false)
- {
- ushort channel = (ushort)context.RequestData.ReadUInt64();
- ScanFilter scanFilter = context.RequestData.ReadStruct<ScanFilter>();
- (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22(0);
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (!isPrivate)
- {
- channel = CheckDevelopmentChannel(channel);
- }
- ResultCode resultCode = ResultCode.InvalidArgument;
- if (bufferSize != 0)
- {
- if (bufferPosition != 0)
- {
- ScanFilterFlag scanFilterFlag = scanFilter.Flag;
- if (!scanFilterFlag.HasFlag(ScanFilterFlag.NetworkType) || scanFilter.NetworkType <= NetworkType.All)
- {
- if (scanFilterFlag.HasFlag(ScanFilterFlag.Ssid))
- {
- if (scanFilter.Ssid.Length <= 31)
- {
- return resultCode;
- }
- }
- if (!scanFilterFlag.HasFlag(ScanFilterFlag.MacAddress))
- {
- if (scanFilterFlag > ScanFilterFlag.All)
- {
- return resultCode;
- }
- if (_state - 3 >= NetworkState.AccessPoint)
- {
- resultCode = ResultCode.InvalidState;
- }
- else
- {
- if (scanFilter.NetworkId.IntentId.LocalCommunicationId == -1 && NetworkClient.NeedsRealId)
- {
- // TODO: Call nn::arp::GetApplicationControlProperty here when implemented.
- ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
- scanFilter.NetworkId.IntentId.LocalCommunicationId = (long)controlProperty.LocalCommunicationId[0];
- }
- resultCode = ScanInternal(context.Memory, channel, scanFilter, bufferPosition, bufferSize, out ulong counter);
- context.ResponseData.Write(counter);
- }
- }
- else
- {
- throw new NotSupportedException();
- }
- }
- }
- }
- return resultCode;
- }
- private ResultCode ScanInternal(IVirtualMemoryManager memory, ushort channel, ScanFilter scanFilter, ulong bufferPosition, ulong bufferSize, out ulong counter)
- {
- ulong networkInfoSize = (ulong)Marshal.SizeOf(typeof(NetworkInfo));
- ulong maxGames = bufferSize / networkInfoSize;
- MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize);
- NetworkInfo[] availableGames = NetworkClient.Scan(channel, scanFilter);
- counter = 0;
- foreach (NetworkInfo networkInfo in availableGames)
- {
- MemoryHelper.Write(memory, bufferPosition + (networkInfoSize * counter), networkInfo);
- if (++counter >= maxGames)
- {
- break;
- }
- }
- return ResultCode.Success;
- }
- [CommandCmif(104)] // 5.0.0+
- // SetWirelessControllerRestriction(u32 wireless_controller_restriction)
- public ResultCode SetWirelessControllerRestriction(ServiceCtx context)
- {
- // NOTE: Return ResultCode.InvalidArgument if an internal IPAddress is null, doesn't occur in our case.
- uint wirelessControllerRestriction = context.RequestData.ReadUInt32();
- if (wirelessControllerRestriction > 1)
- {
- return ResultCode.InvalidArgument;
- }
- if (_state != NetworkState.Initialized)
- {
- return ResultCode.InvalidState;
- }
- // NOTE: WirelessControllerRestriction value is used for the btm service in SetWlanMode call.
- // Since we use our own implementation we can do nothing here.
- return ResultCode.Success;
- }
- [CommandCmif(200)]
- // OpenAccessPoint()
- public ResultCode OpenAccessPoint(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (_state != NetworkState.Initialized)
- {
- return ResultCode.InvalidState;
- }
- CloseStation();
- SetState(NetworkState.AccessPoint);
- _accessPoint = new AccessPoint(this);
- // NOTE: Calls nifm service and return related result codes.
- // Since we use our own implementation we can return ResultCode.Success.
- return ResultCode.Success;
- }
- [CommandCmif(201)]
- // CloseAccessPoint()
- public ResultCode CloseAccessPoint(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (_state == NetworkState.AccessPoint || _state == NetworkState.AccessPointCreated)
- {
- DestroyNetworkImpl(DisconnectReason.DestroyedByUser);
- }
- else
- {
- return ResultCode.InvalidState;
- }
- SetState(NetworkState.Initialized);
- return ResultCode.Success;
- }
- private void CloseAccessPoint()
- {
- _accessPoint?.Dispose();
- _accessPoint = null;
- }
- [CommandCmif(202)]
- // CreateNetwork(bytes<0x44, 2> security_config, bytes<0x30, 1> user_config, bytes<0x20, 8> network_config)
- public ResultCode CreateNetwork(ServiceCtx context)
- {
- return CreateNetworkImpl(context);
- }
- [CommandCmif(203)]
- // CreateNetworkPrivate(bytes<0x44, 2> security_config, bytes<0x20, 1> security_parameter, bytes<0x30, 1>, bytes<0x20, 8> network_config, buffer<unknown, 9> address_entry, int count)
- public ResultCode CreateNetworkPrivate(ServiceCtx context)
- {
- return CreateNetworkImpl(context, true);
- }
- public ResultCode CreateNetworkImpl(ServiceCtx context, bool isPrivate = false)
- {
- SecurityConfig securityConfig = context.RequestData.ReadStruct<SecurityConfig>();
- SecurityParameter securityParameter = isPrivate ? context.RequestData.ReadStruct<SecurityParameter>() : new SecurityParameter();
- UserConfig userConfig = context.RequestData.ReadStruct<UserConfig>();
- context.RequestData.BaseStream.Seek(4, SeekOrigin.Current); // Alignment?
- NetworkConfig networkConfig = context.RequestData.ReadStruct<NetworkConfig>();
- if (networkConfig.IntentId.LocalCommunicationId == -1 && NetworkClient.NeedsRealId)
- {
- // TODO: Call nn::arp::GetApplicationControlProperty here when implemented.
- ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
- networkConfig.IntentId.LocalCommunicationId = (long)controlProperty.LocalCommunicationId[0];
- }
- bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
- if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
- {
- return ResultCode.InvalidObject;
- }
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- networkConfig.Channel = CheckDevelopmentChannel(networkConfig.Channel);
- securityConfig.SecurityMode = CheckDevelopmentSecurityMode(securityConfig.SecurityMode);
- if (networkConfig.NodeCountMax <= LdnConst.NodeCountMax)
- {
- if ((((ulong)networkConfig.LocalCommunicationVersion) & 0x80000000) == 0)
- {
- if (securityConfig.SecurityMode <= SecurityMode.Retail)
- {
- if (securityConfig.Passphrase.Length <= LdnConst.PassphraseLengthMax)
- {
- if (_state == NetworkState.AccessPoint)
- {
- if (isPrivate)
- {
- ulong bufferPosition = context.Request.PtrBuff[0].Position;
- ulong bufferSize = context.Request.PtrBuff[0].Size;
- byte[] addressListBytes = new byte[bufferSize];
- context.Memory.Read(bufferPosition, addressListBytes);
- AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0];
- _accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList);
- }
- else
- {
- _accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig);
- }
- return ResultCode.Success;
- }
- else
- {
- return ResultCode.InvalidState;
- }
- }
- }
- }
- }
- return ResultCode.InvalidArgument;
- }
- [CommandCmif(204)]
- // DestroyNetwork()
- public ResultCode DestroyNetwork(ServiceCtx context)
- {
- return DestroyNetworkImpl(DisconnectReason.DestroyedByUser);
- }
- private ResultCode DestroyNetworkImpl(DisconnectReason disconnectReason)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (disconnectReason - 3 <= DisconnectReason.DisconnectedByUser)
- {
- if (_state == NetworkState.AccessPointCreated)
- {
- CloseAccessPoint();
- SetState(NetworkState.AccessPoint);
- return ResultCode.Success;
- }
- CloseAccessPoint();
- return ResultCode.InvalidState;
- }
- return ResultCode.InvalidArgument;
- }
- [CommandCmif(205)]
- // Reject(u32 node_id)
- public ResultCode Reject(ServiceCtx context)
- {
- uint nodeId = context.RequestData.ReadUInt32();
- return RejectImpl(DisconnectReason.Rejected, nodeId);
- }
- private ResultCode RejectImpl(DisconnectReason disconnectReason, uint nodeId)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (_state != NetworkState.AccessPointCreated)
- {
- return ResultCode.InvalidState; // Must be network host to reject nodes.
- }
- return NetworkClient.Reject(disconnectReason, nodeId);
- }
- [CommandCmif(206)]
- // SetAdvertiseData(buffer<advertise_data, 0x21>)
- public ResultCode SetAdvertiseData(ServiceCtx context)
- {
- (ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x21(0);
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (bufferSize == 0 || bufferSize > LdnConst.AdvertiseDataSizeMax)
- {
- return ResultCode.InvalidArgument;
- }
- if (_state == NetworkState.AccessPoint || _state == NetworkState.AccessPointCreated)
- {
- byte[] advertiseData = new byte[bufferSize];
- context.Memory.Read(bufferPosition, advertiseData);
- return _accessPoint.SetAdvertiseData(advertiseData);
- }
- else
- {
- return ResultCode.InvalidState;
- }
- }
- [CommandCmif(207)]
- // SetStationAcceptPolicy(u8 accept_policy)
- public ResultCode SetStationAcceptPolicy(ServiceCtx context)
- {
- AcceptPolicy acceptPolicy = (AcceptPolicy)context.RequestData.ReadByte();
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (acceptPolicy > AcceptPolicy.WhiteList)
- {
- return ResultCode.InvalidArgument;
- }
- if (_state == NetworkState.AccessPoint || _state == NetworkState.AccessPointCreated)
- {
- return _accessPoint.SetStationAcceptPolicy(acceptPolicy);
- }
- else
- {
- return ResultCode.InvalidState;
- }
- }
- [CommandCmif(208)]
- // AddAcceptFilterEntry(bytes<6, 1> mac_address)
- public ResultCode AddAcceptFilterEntry(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- // TODO
- return ResultCode.Success;
- }
- [CommandCmif(209)]
- // ClearAcceptFilter()
- public ResultCode ClearAcceptFilter(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- // TODO
- return ResultCode.Success;
- }
- [CommandCmif(300)]
- // OpenStation()
- public ResultCode OpenStation(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (_state != NetworkState.Initialized)
- {
- return ResultCode.InvalidState;
- }
- CloseAccessPoint();
- SetState(NetworkState.Station);
- _station?.Dispose();
- _station = new Station(this);
- // NOTE: Calls nifm service and returns related result codes.
- // Since we use our own implementation we can return ResultCode.Success.
- return ResultCode.Success;
- }
- [CommandCmif(301)]
- // CloseStation()
- public ResultCode CloseStation(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (_state == NetworkState.Station || _state == NetworkState.StationConnected)
- {
- DisconnectImpl(DisconnectReason.DisconnectedByUser);
- }
- else
- {
- return ResultCode.InvalidState;
- }
- SetState(NetworkState.Initialized);
- return ResultCode.Success;
- }
- private void CloseStation()
- {
- _station?.Dispose();
- _station = null;
- }
- [CommandCmif(302)]
- // Connect(bytes<0x44, 2> security_config, bytes<0x30, 1> user_config, u32 local_communication_version, u32 option_unknown, buffer<network_info<0x480>, 0x19>)
- public ResultCode Connect(ServiceCtx context)
- {
- return ConnectImpl(context);
- }
- [CommandCmif(303)]
- // ConnectPrivate(bytes<0x44, 2> security_config, bytes<0x20, 1> security_parameter, bytes<0x30, 1> user_config, u32 local_communication_version, u32 option_unknown, bytes<0x20, 8> network_config)
- public ResultCode ConnectPrivate(ServiceCtx context)
- {
- return ConnectImpl(context, true);
- }
- private ResultCode ConnectImpl(ServiceCtx context, bool isPrivate = false)
- {
- SecurityConfig securityConfig = context.RequestData.ReadStruct<SecurityConfig>();
- SecurityParameter securityParameter = isPrivate ? context.RequestData.ReadStruct<SecurityParameter>() : new SecurityParameter();
- UserConfig userConfig = context.RequestData.ReadStruct<UserConfig>();
- uint localCommunicationVersion = context.RequestData.ReadUInt32();
- uint optionUnknown = context.RequestData.ReadUInt32();
- NetworkConfig networkConfig = new();
- NetworkInfo networkInfo = new();
- if (isPrivate)
- {
- context.RequestData.ReadUInt32(); // Padding.
- networkConfig = context.RequestData.ReadStruct<NetworkConfig>();
- }
- else
- {
- ulong bufferPosition = context.Request.PtrBuff[0].Position;
- ulong bufferSize = context.Request.PtrBuff[0].Size;
- byte[] networkInfoBytes = new byte[bufferSize];
- context.Memory.Read(bufferPosition, networkInfoBytes);
- networkInfo = MemoryMarshal.Read<NetworkInfo>(networkInfoBytes);
- }
- if (networkInfo.NetworkId.IntentId.LocalCommunicationId == -1 && NetworkClient.NeedsRealId)
- {
- // TODO: Call nn::arp::GetApplicationControlProperty here when implemented.
- ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
- networkInfo.NetworkId.IntentId.LocalCommunicationId = (long)controlProperty.LocalCommunicationId[0];
- }
- bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId);
- if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
- {
- return ResultCode.InvalidObject;
- }
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- securityConfig.SecurityMode = CheckDevelopmentSecurityMode(securityConfig.SecurityMode);
- ResultCode resultCode = ResultCode.InvalidArgument;
- if (securityConfig.SecurityMode - 1 <= SecurityMode.Debug)
- {
- if (optionUnknown <= 1 && (localCommunicationVersion >> 15) == 0 && securityConfig.PassphraseSize <= 64)
- {
- resultCode = ResultCode.VersionTooLow;
- if (localCommunicationVersion >= 0)
- {
- resultCode = ResultCode.VersionTooHigh;
- if (localCommunicationVersion <= short.MaxValue)
- {
- if (_state != NetworkState.Station)
- {
- resultCode = ResultCode.InvalidState;
- }
- else
- {
- if (isPrivate)
- {
- resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig);
- }
- else
- {
- resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo);
- }
- }
- }
- }
- }
- }
- return resultCode;
- }
- [CommandCmif(304)]
- // Disconnect()
- public ResultCode Disconnect(ServiceCtx context)
- {
- return DisconnectImpl(DisconnectReason.DisconnectedByUser);
- }
- private ResultCode DisconnectImpl(DisconnectReason disconnectReason)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- if (disconnectReason <= DisconnectReason.DisconnectedBySystem)
- {
- if (_state == NetworkState.StationConnected)
- {
- SetState(NetworkState.Station);
- CloseStation();
- _disconnectReason = disconnectReason;
- return ResultCode.Success;
- }
- CloseStation();
- return ResultCode.InvalidState;
- }
- return ResultCode.InvalidArgument;
- }
- [CommandCmif(400)]
- // InitializeOld(pid)
- public ResultCode InitializeOld(ServiceCtx context)
- {
- return InitializeImpl(context, context.Process.Pid, NifmRequestID);
- }
- [CommandCmif(401)]
- // Finalize()
- public ResultCode Finalize(ServiceCtx context)
- {
- if (_nifmResultCode != ResultCode.Success)
- {
- return _nifmResultCode;
- }
- // NOTE: Use true when its called in nn::ldn::detail::ISystemLocalCommunicationService
- ResultCode resultCode = FinalizeImpl(false);
- if (resultCode == ResultCode.Success)
- {
- SetDisconnectReason(DisconnectReason.None);
- }
- if (_stateChangeEventHandle != 0)
- {
- context.Process.HandleTable.CloseHandle(_stateChangeEventHandle);
- _stateChangeEventHandle = 0;
- }
- return resultCode;
- }
- private ResultCode FinalizeImpl(bool isCausedBySystem)
- {
- DisconnectReason disconnectReason;
- switch (_state)
- {
- case NetworkState.None:
- return ResultCode.Success;
- case NetworkState.AccessPoint:
- {
- CloseAccessPoint();
- break;
- }
- case NetworkState.AccessPointCreated:
- {
- if (isCausedBySystem)
- {
- disconnectReason = DisconnectReason.DestroyedBySystem;
- }
- else
- {
- disconnectReason = DisconnectReason.DestroyedByUser;
- }
- DestroyNetworkImpl(disconnectReason);
- break;
- }
- case NetworkState.Station:
- {
- CloseStation();
- break;
- }
- case NetworkState.StationConnected:
- {
- if (isCausedBySystem)
- {
- disconnectReason = DisconnectReason.DisconnectedBySystem;
- }
- else
- {
- disconnectReason = DisconnectReason.DisconnectedByUser;
- }
- DisconnectImpl(disconnectReason);
- break;
- }
- }
- SetState(NetworkState.None);
- NetworkClient?.Dispose();
- NetworkClient = null;
- return ResultCode.Success;
- }
- [CommandCmif(402)] // 7.0.0+
- // Initialize(u64 ip_addresses, pid)
- public ResultCode Initialize(ServiceCtx context)
- {
- _ = new IPAddress(context.RequestData.ReadUInt32());
- _ = new IPAddress(context.RequestData.ReadUInt32());
- // NOTE: It seems the guest can get ip_address and subnet_mask from nifm service and pass it through the initialize.
- // This calls InitializeImpl() twice: The first time with NIFM_REQUEST_ID, and if it fails, a second time with nifm_request_id = 1.
- return InitializeImpl(context, context.Process.Pid, NifmRequestID);
- }
- public ResultCode InitializeImpl(ServiceCtx context, ulong pid, int nifmRequestId)
- {
- ResultCode resultCode = ResultCode.InvalidArgument;
- if (nifmRequestId <= 255)
- {
- if (_state != NetworkState.Initialized)
- {
- // NOTE: Service calls nn::ldn::detail::NetworkInterfaceManager::NetworkInterfaceMonitor::Initialize() with nifmRequestId as argument,
- // then it stores the result code of it in a global variable. Since we use our own implementation, we can just check the connection
- // and return related error codes.
- if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
- {
- MultiplayerMode mode = context.Device.Configuration.MultiplayerMode;
- Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Initializing with multiplayer mode: {mode}");
- switch (mode)
- {
- case MultiplayerMode.LdnRyu:
- try
- {
- string ldnServer = context.Device.Configuration.MultiplayerLdnServer;
- if (string.IsNullOrEmpty(ldnServer))
- {
- ldnServer = DefaultLanPlayHost;
- }
- if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress))
- {
- ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0];
- }
- NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration);
- }
- catch (Exception ex)
- {
- Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu server. Defaulting to stubbed wireless.");
- Logger.Error?.Print(LogClass.ServiceLdn, ex.Message);
- NetworkClient = new LdnDisabledClient();
- }
- break;
- case MultiplayerMode.LdnMitm:
- NetworkClient = new LdnMitmClient(context.Device.Configuration);
- break;
- case MultiplayerMode.Disabled:
- NetworkClient = new LdnDisabledClient();
- break;
- }
- // TODO: Call nn::arp::GetApplicationLaunchProperty here when implemented.
- NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion.Items.ToArray());
- resultCode = ResultCode.Success;
- _nifmResultCode = resultCode;
- SetState(NetworkState.Initialized);
- }
- else
- {
- // NOTE: Service returns different ResultCode here related to the nifm ResultCode.
- resultCode = ResultCode.DeviceDisabled;
- _nifmResultCode = resultCode;
- }
- }
- }
- return resultCode;
- }
- public void Dispose()
- {
- _station?.Dispose();
- _station = null;
- _accessPoint?.Dispose();
- _accessPoint = null;
- NetworkClient?.DisconnectAndStop();
- NetworkClient = null;
- }
- }
- }
|