diff --git a/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.SKJOIN_SKREJOIN.cs b/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.SKJOIN_SKREJOIN.cs index b9fea30..4c843e4 100644 --- a/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.SKJOIN_SKREJOIN.cs +++ b/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.SKJOIN_SKREJOIN.cs @@ -13,6 +13,18 @@ namespace Smdn.Net.SkStackIP; #pragma warning disable IDE0040 partial class SkStackClient { #pragma warning restore IDE0040 + private static SkStackPanaSessionEstablishmentException CreatePanaSessionEstablishmentException( + IPAddress paaAddress, + SkStackEventNumber eventNumber, + IPAddress address + ) + => new( + message: null, + paaAddress: paaAddress, + address: address, + eventNumber: eventNumber + ); + /// /// Sends a command SKJOIN. /// @@ -22,6 +34,17 @@ partial class SkStackClient { public ValueTask SendSKJOINAsync( IPAddress ipv6address, CancellationToken cancellationToken = default + ) + => SendSKJOINAsync( + ipv6address: ipv6address, + createPanaSessionEstablishmentException: (eventNumber, address) => CreatePanaSessionEstablishmentException(ipv6address, eventNumber, address), + cancellationToken: cancellationToken + ); + + internal ValueTask SendSKJOINAsync( + IPAddress ipv6address, + Func? createPanaSessionEstablishmentException, + CancellationToken cancellationToken ) { if (ipv6address is null) @@ -29,11 +52,15 @@ public ValueTask SendSKJOINAsync( if (ipv6address.AddressFamily != AddressFamily.InterNetworkV6) throw new ArgumentException($"`{nameof(ipv6address)}.{nameof(IPAddress.AddressFamily)}` must be {nameof(AddressFamily.InterNetworkV6)}"); - return SKJOIN(ipv6address, cancellationToken); + return SKJOIN(ipv6address, createPanaSessionEstablishmentException, cancellationToken); - async ValueTask SKJOIN(IPAddress addr, CancellationToken ct) + async ValueTask SKJOIN( + IPAddress addr, + Func? createException, + CancellationToken ct + ) { - var (response, _) = await SKJOIN_SKREJOIN(SkStackCommandNames.SKJOIN, addr, ct).ConfigureAwait(false); + var (response, _) = await SKJOIN_SKREJOIN(SkStackCommandNames.SKJOIN, addr, createException, ct).ConfigureAwait(false); return response; } @@ -51,7 +78,12 @@ IPAddress Address )> SendSKREJOINAsync( CancellationToken cancellationToken = default ) - => SKJOIN_SKREJOIN(SkStackCommandNames.SKREJOIN, ipv6address: null, cancellationToken); + => SKJOIN_SKREJOIN( + SkStackCommandNames.SKREJOIN, + ipv6address: null, + createPanaSessionEstablishmentException: null, + cancellationToken + ); private async ValueTask<( SkStackResponse Response, @@ -60,10 +92,11 @@ IPAddress Address SKJOIN_SKREJOIN( ReadOnlyMemory command, IPAddress? ipv6address, + Func? createPanaSessionEstablishmentException, CancellationToken cancellationToken ) { - var eventHandler = new SKJOINEventHandler(); + var eventHandler = new SKJOINEventHandler(createPanaSessionEstablishmentException); var resp = await SendCommandAsync( command: command, writeArguments: writer => { @@ -85,16 +118,20 @@ CancellationToken cancellationToken return (resp, eventHandler.Address!); } - private class SKJOINEventHandler : SkStackEventHandlerBase { + private sealed class SKJOINEventHandler(Func? createPanaSessionEstablishmentException) : SkStackEventHandlerBase { public bool HasAddressSet { get; private set; } public IPAddress? Address { get; private set; } + private readonly Func? createPanaSessionEstablishmentException = createPanaSessionEstablishmentException; private SkStackEventNumber eventNumber; + private static SkStackPanaSessionEstablishmentException CreatePanaSessionEstablishmentException(SkStackEventNumber eventNumber, IPAddress address) + => new(null, address, eventNumber); + public void ThrowIfEstablishmentError() { if (eventNumber != SkStackEventNumber.PanaSessionEstablishmentCompleted) - throw new SkStackPanaSessionEstablishmentException($"PANA session establishment failed. (0x{eventNumber:X})", Address!, eventNumber); + throw createPanaSessionEstablishmentException?.Invoke(eventNumber, Address!) ?? CreatePanaSessionEstablishmentException(eventNumber, Address!); } public override bool TryProcessEvent(SkStackEvent ev) diff --git a/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs b/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs index 523cc2f..9499355 100644 --- a/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs +++ b/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs @@ -201,7 +201,7 @@ private static int ValidatePanIdAndThrowIfInvalid(int panId, string paramName) /// /// /// - /// + /// /// /// private async ValueTask AuthenticateAsPanaClientAsyncCore( @@ -344,6 +344,15 @@ await SendSKSREGAsync( // Then attempt to establish the PANA session. await SendSKJOINAsync( ipv6address: paaAddressNotNull, + createPanaSessionEstablishmentException: (eventNumber, address) => + new SkStackPanaSessionEstablishmentException( + message: null, + paaAddress: paaAddressNotNull, + channel: channelNotNull, + panId: panIdNotNull, + address: address, + eventNumber: eventNumber + ), cancellationToken: cancellationToken ).ConfigureAwait(false); diff --git a/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackPanaSessionEstablishmentException.cs b/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackPanaSessionEstablishmentException.cs index 30a95f0..efd5bba 100644 --- a/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackPanaSessionEstablishmentException.cs +++ b/src/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackPanaSessionEstablishmentException.cs @@ -10,20 +10,69 @@ namespace Smdn.Net.SkStackIP; /// /// The exception that represents an error on the establishment of a PANA session. /// -/// +/// public class SkStackPanaSessionEstablishmentException : SkStackPanaSessionException { + /// Gets the of the PANA Authentication Agent(PAA) that attempted PANA authentication. + public IPAddress? PaaAddress { get; } + + /// Gets the channel number used when attempting to establish a PANA session. + /// If this exception occurs as a result of the method, the non- value will be set to this property. Otherwise, it will be . + public SkStackChannel? Channel { get; } + + /// Gets the PAN ID used when attempting to establish a PANA session. + /// If this exception occurs as a result of the method, the non- value will be set to this property. Otherwise, it will be . + public int? PanId { get; } + + internal SkStackPanaSessionEstablishmentException( + string? message, + IPAddress address, + SkStackEventNumber eventNumber, + Exception? innerException = null + ) + : base( + message: message ?? $"PANA session establishment failed. (0x{eventNumber:X})", + address: address, + eventNumber: eventNumber, + innerException: innerException + ) + { + } + + internal SkStackPanaSessionEstablishmentException( + string? message, + IPAddress paaAddress, + IPAddress address, + SkStackEventNumber eventNumber, + Exception? innerException = null + ) + : base( + message: message ?? $"PANA session establishment failed. (0x{eventNumber:X}, PAA={paaAddress})", + address: address, + eventNumber: eventNumber, + innerException: innerException + ) + { + PaaAddress = paaAddress; + } + internal SkStackPanaSessionEstablishmentException( - string message, + string? message, + IPAddress paaAddress, + SkStackChannel channel, + int panId, IPAddress address, SkStackEventNumber eventNumber, Exception? innerException = null ) : base( - message: message, + message: message ?? $"PANA session establishment failed. (0x{eventNumber:X}, PAA={paaAddress}, Channel={channel}, PanID=0x{panId:X4})", address: address, eventNumber: eventNumber, innerException: innerException ) { + PaaAddress = paaAddress; + Channel = channel; + PanId = panId; } } diff --git a/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.4.SKJOIN.cs b/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.4.SKJOIN.cs index e446ef8..9459fed 100644 --- a/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.4.SKJOIN.cs +++ b/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.4.SKJOIN.cs @@ -138,6 +138,9 @@ async Task RaisePanaSessionEstablishmentEventsAsync() Assert.That(ex!.EventNumber, Is.EqualTo(SkStackEventNumber.PanaSessionEstablishmentError)); Assert.That(ex.Address, Is.EqualTo(IPAddress.Parse(SelfIPv6Address))); + Assert.That(ex.PaaAddress, Is.EqualTo(IPAddress.Parse(PaaIPv6Address))); + Assert.That(ex.Channel, Is.Null); + Assert.That(ex.PanId, Is.Null); Assert.That(raisedEventCount, Is.EqualTo(0), nameof(raisedEventCount)); diff --git a/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.5.SKREJOIN.cs b/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.5.SKREJOIN.cs index efa445e..9886fac 100644 --- a/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.5.SKREJOIN.cs +++ b/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Commands.3.5.SKREJOIN.cs @@ -132,6 +132,9 @@ async Task RaisePanaSessionEstablishmentEventsAsync() Assert.That(ex!.EventNumber, Is.EqualTo(SkStackEventNumber.PanaSessionEstablishmentError)); Assert.That(ex.Address, Is.EqualTo(IPAddress.Parse(SelfIPv6Address))); + Assert.That(ex.PaaAddress, Is.Null); + Assert.That(ex.Channel, Is.Null); + Assert.That(ex.PanId, Is.Null); Assert.That(raisedEventCount, Is.EqualTo(0), nameof(raisedEventCount)); diff --git a/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs b/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs index a6e0251..551ee68 100644 --- a/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs +++ b/tests/Smdn.Net.SkStackIP/Smdn.Net.SkStackIP/SkStackClient.Functions.PANA.AuthenticateAsPaC.cs @@ -96,7 +96,13 @@ ILogger logger cancellationToken: cts.Token ), exceptPanaSessionEstablishmentException - ? Throws.TypeOf() + ? Throws + .TypeOf() + .And.Property(nameof(SkStackPanaSessionEstablishmentException.PaaAddress)).EqualTo(IPAddress.Parse(paaIPv6Address)) + .And.Property(nameof(SkStackPanaSessionEstablishmentException.Channel)).EqualTo(SkStackChannel.Channels[paaChannel]) + .And.Property(nameof(SkStackPanaSessionEstablishmentException.PanId)).EqualTo(paaPanId) + .And.Property(nameof(SkStackPanaSessionEstablishmentException.EventNumber)).EqualTo(eventNumberOfPanaSessionEstablishment) + .And.Property(nameof(SkStackPanaSessionEstablishmentException.Address)).EqualTo(IPAddress.Parse(selfIPv6Address)) : Throws.Nothing );