Bruce Wayne пре 5 година
родитељ
комит
a664f5567e

+ 48 - 13
NatTypeTester/MainWindow.xaml

@@ -42,7 +42,7 @@
 				</ComboBox>
 			</Grid>
 			<TabControl>
-				<TabItem Header="RFC 3489">
+				<TabItem Header="RFC 5780" x:Name="RFC5780Tab">
 					<Grid>
 						<Grid.RowDefinitions>
 							<RowDefinition />
@@ -50,6 +50,45 @@
 							<RowDefinition />
 							<RowDefinition />
 							<RowDefinition />
+							<RowDefinition />
+						</Grid.RowDefinitions>
+						<Grid.ColumnDefinitions>
+							<ColumnDefinition Width="Auto" />
+							<ColumnDefinition />
+							<ColumnDefinition Width="65" />
+						</Grid.ColumnDefinitions>
+						<TextBlock Grid.Row="0" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Binding test" />
+						<TextBlock Grid.Row="1" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Mapping behavior" />
+						<TextBlock Grid.Row="2" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Filtering behavior" />
+						<TextBlock Grid.Row="3" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Local end" />
+						<TextBlock Grid.Row="4" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Public end" />
+
+						<TextBox x:Name="BindingTestTextBox" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"
+						         Height="23.24" Margin="5" IsReadOnly="True"
+						         VerticalContentAlignment="Center" VerticalAlignment="Center" />
+						<TextBox x:Name="MappingBehaviorTextBox" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
+						         Height="23.24" Margin="5" IsReadOnly="True"
+						         VerticalContentAlignment="Center" VerticalAlignment="Center" />
+						<TextBox x:Name="FilteringBehaviorTextBox" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
+						         Height="23.24" Margin="5" IsReadOnly="True"
+						         VerticalContentAlignment="Center" VerticalAlignment="Center" />
+						<TextBox x:Name="LocalAddressTextBox" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"
+						         Height="23.24" Margin="5" IsReadOnly="False"
+						         VerticalContentAlignment="Center" VerticalAlignment="Center" />
+						<TextBox x:Name="MappingAddressTextBox" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2"
+						         Height="23.24" Margin="5" IsReadOnly="True"
+						         VerticalContentAlignment="Center" VerticalAlignment="Center" />
+
+						<Button x:Name="DiscoveryButton" Grid.Row="5" Grid.Column="2" Content="Test" Margin="5" />
+					</Grid>
+				</TabItem>
+				<TabItem Header="RFC 3489" x:Name="RFC3489Tab">
+					<Grid>
+						<Grid.RowDefinitions>
+							<RowDefinition />
+							<RowDefinition />
+							<RowDefinition />
+							<RowDefinition />
 						</Grid.RowDefinitions>
 						<Grid.ColumnDefinitions>
 							<ColumnDefinition Width="Auto" />
@@ -57,27 +96,23 @@
 							<ColumnDefinition Width="65" />
 						</Grid.ColumnDefinitions>
 
-						<TextBlock Grid.Row="1" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="NAT type" />
-						<TextBlock Grid.Row="2" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Local end" />
-						<TextBlock Grid.Row="3" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Public end" />
+						<TextBlock Grid.Row="0" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="NAT type" />
+						<TextBlock Grid.Row="1" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Local end" />
+						<TextBlock Grid.Row="2" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Public end" />
 
-						<TextBox x:Name="NatTypeTextBox" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
+						<TextBox x:Name="NatTypeTextBox" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"
 						Height="23.24" Margin="5" IsReadOnly="True"
 						VerticalContentAlignment="Center" VerticalAlignment="Center"/>
-						<TextBox x:Name="LocalEndTextBox" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
+						<TextBox x:Name="LocalEndTextBox" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
 						Height="23.24" Margin="5"
-						VerticalContentAlignment="Center" VerticalAlignment="Center"
-						Text="0.0.0.0:0" />
-						<TextBox x:Name="PublicEndTextBox" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2"
+						VerticalContentAlignment="Center" VerticalAlignment="Center" />
+						<TextBox x:Name="PublicEndTextBox" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2"
 						Height="23.24" Margin="5" IsReadOnly="True"
 						VerticalContentAlignment="Center" VerticalAlignment="Center" />
 
-						<Button x:Name="TestButton" Grid.Row="4" Grid.Column="2" Content="Test" Margin="5"/>
+						<Button x:Name="TestButton" Grid.Row="3" Grid.Column="2" Content="Test" Margin="5"/>
 					</Grid>
 				</TabItem>
-				<TabItem Header="RFC 5780">
-
-				</TabItem>
 			</TabControl>
 		</StackPanel>
 	</Grid>

+ 48 - 1
NatTypeTester/MainWindow.xaml.cs

@@ -18,6 +18,8 @@ namespace NatTypeTester
 
             this.WhenActivated(disposableRegistration =>
             {
+                #region Server
+
                 this.Bind(ViewModel,
                         vm => vm.StunServer,
                         v => v.ServersComboBox.Text
@@ -28,6 +30,10 @@ namespace NatTypeTester
                         v => v.ServersComboBox.ItemsSource
                 ).DisposeWith(disposableRegistration);
 
+                #endregion
+
+                #region RFC3489
+
                 this.OneWayBind(ViewModel,
                         vm => vm.ClassicNatType,
                         v => v.NatTypeTextBox.Text
@@ -48,10 +54,51 @@ namespace NatTypeTester
                                 view => view.TestButton)
                         .DisposeWith(disposableRegistration);
 
-                this.Events().KeyDown
+                RFC3489Tab.Events().KeyDown
                         .Where(x => x.Key == Key.Enter && TestButton.IsEnabled)
                         .Subscribe(y => { TestButton.Command.Execute(Unit.Default); })
                         .DisposeWith(disposableRegistration);
+
+                #endregion
+
+                #region RFC5780
+
+                this.OneWayBind(ViewModel,
+                        vm => vm.BindingTest,
+                        v => v.BindingTestTextBox.Text
+                ).DisposeWith(disposableRegistration);
+
+                this.OneWayBind(ViewModel,
+                        vm => vm.MappingBehavior,
+                        v => v.MappingBehaviorTextBox.Text
+                ).DisposeWith(disposableRegistration);
+
+                this.OneWayBind(ViewModel,
+                        vm => vm.FilteringBehavior,
+                        v => v.FilteringBehaviorTextBox.Text
+                ).DisposeWith(disposableRegistration);
+
+                this.Bind(ViewModel,
+                        vm => vm.LocalAddress,
+                        v => v.LocalAddressTextBox.Text
+                ).DisposeWith(disposableRegistration);
+
+                this.OneWayBind(ViewModel,
+                        vm => vm.MappingAddress,
+                        v => v.MappingAddressTextBox.Text
+                ).DisposeWith(disposableRegistration);
+
+                this.BindCommand(ViewModel,
+                                viewModel => viewModel.DiscoveryNatType,
+                                view => view.DiscoveryButton)
+                        .DisposeWith(disposableRegistration);
+
+                RFC5780Tab.Events().KeyDown
+                        .Where(x => x.Key == Key.Enter && DiscoveryButton.IsEnabled)
+                        .Subscribe(y => { DiscoveryButton.Command.Execute(Unit.Default); })
+                        .DisposeWith(disposableRegistration);
+
+                #endregion
             });
         }
     }

+ 88 - 1
NatTypeTester/ViewModels/MainWindowViewModel.cs

@@ -25,7 +25,7 @@ namespace NatTypeTester.ViewModels
             set => this.RaiseAndSetIfChanged(ref _classicNatType, value);
         }
 
-        private string _localEnd;
+        private string _localEnd = NetUtils.DefaultLocalEnd;
         public string LocalEnd
         {
             get => _localEnd;
@@ -43,6 +43,47 @@ namespace NatTypeTester.ViewModels
 
         #endregion
 
+        #region RFC5780
+
+        private string _bindingTest;
+        public string BindingTest
+        {
+            get => _bindingTest;
+            set => this.RaiseAndSetIfChanged(ref _bindingTest, value);
+        }
+
+        private string _mappingBehavior;
+        public string MappingBehavior
+        {
+            get => _mappingBehavior;
+            set => this.RaiseAndSetIfChanged(ref _mappingBehavior, value);
+        }
+
+        private string _filteringBehavior;
+        public string FilteringBehavior
+        {
+            get => _filteringBehavior;
+            set => this.RaiseAndSetIfChanged(ref _filteringBehavior, value);
+        }
+
+        private string _localAddress = NetUtils.DefaultLocalEnd;
+        public string LocalAddress
+        {
+            get => _localAddress;
+            set => this.RaiseAndSetIfChanged(ref _localAddress, value);
+        }
+
+        private string _mappingAddress;
+        public string MappingAddress
+        {
+            get => _mappingAddress;
+            set => this.RaiseAndSetIfChanged(ref _mappingAddress, value);
+        }
+
+        public ReactiveCommand<Unit, Unit> DiscoveryNatType { get; }
+
+        #endregion
+
         #region Servers
 
         private string _stunServer;
@@ -75,6 +116,7 @@ namespace NatTypeTester.ViewModels
                 .Bind(StunServers)
                 .Subscribe();
             TestClassicNatType = ReactiveCommand.CreateFromObservable(TestClassicNatTypeImpl);
+            DiscoveryNatType = ReactiveCommand.CreateFromObservable(DiscoveryNatTypeImpl);
         }
 
         private async void LoadStunServer()
@@ -129,5 +171,50 @@ namespace NatTypeTester.ViewModels
                 }
             });
         }
+
+        private IObservable<Unit> DiscoveryNatTypeImpl()
+        {
+            return Observable.Defer(() => Observable.StartAsync(async () =>
+            {
+                try
+                {
+                    var server = new StunServer();
+                    if (server.Parse(StunServer))
+                    {
+                        using var client = new StunClient5389UDP(server.Hostname, server.Port, NetUtils.ParseEndpoint(LocalAddress));
+
+                        client.BindingTestResultChanged
+                                .ObserveOn(RxApp.MainThreadScheduler)
+                                .Subscribe(t => BindingTest = $@"{t}");
+
+                        client.MappingBehaviorChanged
+                                .ObserveOn(RxApp.MainThreadScheduler)
+                                .Subscribe(t => MappingBehavior = $@"{t}");
+
+                        client.FilteringBehaviorChanged
+                                .ObserveOn(RxApp.MainThreadScheduler)
+                                .Subscribe(t => FilteringBehavior = $@"{t}");
+
+                        client.PubChanged
+                                .ObserveOn(RxApp.MainThreadScheduler)
+                                .Subscribe(t => MappingAddress = $@"{t}");
+
+                        client.LocalChanged
+                                .ObserveOn(RxApp.MainThreadScheduler)
+                                .Subscribe(t => LocalAddress = $@"{t}");
+
+                        await client.QueryAsync();
+                    }
+                    else
+                    {
+                        throw new Exception(@"Wrong STUN Server!");
+                    }
+                }
+                catch (Exception ex)
+                {
+                    MessageBox.Show(ex.Message, nameof(NatTypeTester), MessageBoxButton.OK, MessageBoxImage.Error);
+                }
+            })).SubscribeOn(RxApp.TaskpoolScheduler);
+        }
     }
 }

+ 11 - 10
STUN/Client/StunClient3489.cs

@@ -24,11 +24,11 @@ namespace STUN.Client
         private readonly Subject<NatType> _natTypeSubj = new Subject<NatType>();
         public IObservable<NatType> NatTypeChanged => _natTypeSubj.AsObservable();
 
-        private readonly Subject<IPEndPoint> _pubSubj = new Subject<IPEndPoint>();
-        public IObservable<IPEndPoint> PubChanged => _pubSubj.AsObservable();
+        protected readonly Subject<IPEndPoint> PubSubj = new Subject<IPEndPoint>();
+        public IObservable<IPEndPoint> PubChanged => PubSubj.AsObservable();
 
-        private readonly Subject<IPEndPoint> _localSubj = new Subject<IPEndPoint>();
-        public IObservable<IPEndPoint> LocalChanged => _localSubj.AsObservable();
+        protected readonly Subject<IPEndPoint> LocalSubj = new Subject<IPEndPoint>();
+        public IObservable<IPEndPoint> LocalChanged => LocalSubj.AsObservable();
 
         #endregion
 
@@ -85,7 +85,7 @@ namespace STUN.Client
         {
             var res = new ClassicStunResult();
             _natTypeSubj.OnNext(res.NatType);
-            _pubSubj.OnNext(res.PublicEndPoint);
+            PubSubj.OnNext(res.PublicEndPoint);
 
             try
             {
@@ -101,7 +101,7 @@ namespace STUN.Client
 
                 if (local1 != null)
                 {
-                    _localSubj.OnNext(LocalEndPoint);
+                    LocalSubj.OnNext(LocalEndPoint);
                 }
 
                 var mappedAddress1 = AttributeExtensions.GetMappedAddressAttribute(response1);
@@ -117,7 +117,7 @@ namespace STUN.Client
                     return res;
                 }
 
-                _pubSubj.OnNext(mappedAddress1); // 显示 test I 得到的映射地址
+                PubSubj.OnNext(mappedAddress1); // 显示 test I 得到的映射地址
 
                 var test2 = new StunMessage5389
                 {
@@ -194,7 +194,7 @@ namespace STUN.Client
             finally
             {
                 _natTypeSubj.OnNext(res.NatType);
-                _pubSubj.OnNext(res.PublicEndPoint);
+                PubSubj.OnNext(res.PublicEndPoint);
             }
         }
 
@@ -236,11 +236,12 @@ namespace STUN.Client
             return (null, null, null);
         }
 
-        public void Dispose()
+        public virtual void Dispose()
         {
             UdpClient?.Dispose();
             _natTypeSubj.OnCompleted();
-            _pubSubj.OnCompleted();
+            PubSubj.OnCompleted();
+            LocalSubj.OnCompleted();
         }
     }
 }

+ 163 - 107
STUN/Client/StunClient5389UDP.cs

@@ -7,6 +7,8 @@ using System;
 using System.Diagnostics;
 using System.Linq;
 using System.Net;
+using System.Reactive.Linq;
+using System.Reactive.Subjects;
 using System.Threading.Tasks;
 
 namespace STUN.Client
@@ -17,6 +19,19 @@ namespace STUN.Client
     /// </summary>
     public class StunClient5389UDP : StunClient3489
     {
+        #region Subject
+
+        private readonly Subject<BindingTestResult> _bindingSubj = new Subject<BindingTestResult>();
+        public IObservable<BindingTestResult> BindingTestResultChanged => _bindingSubj.AsObservable();
+
+        private readonly Subject<MappingBehavior> _mappingBehaviorSubj = new Subject<MappingBehavior>();
+        public IObservable<MappingBehavior> MappingBehaviorChanged => _mappingBehaviorSubj.AsObservable();
+
+        private readonly Subject<FilteringBehavior> _filteringBehaviorSubj = new Subject<FilteringBehavior>();
+        public IObservable<FilteringBehavior> FilteringBehaviorChanged => _filteringBehaviorSubj.AsObservable();
+
+        #endregion
+
         public StunClient5389UDP(string server, ushort port = 3478, IPEndPoint local = null, IDnsQuery dnsQuery = null)
         : base(server, port, local, dnsQuery)
         {
@@ -25,7 +40,13 @@ namespace STUN.Client
 
         public async Task<StunResult5389> QueryAsync()
         {
-            var result = await FilteringBehaviorTestAsync();
+            var result = new StunResult5389();
+            _bindingSubj.OnNext(result.BindingTestResult);
+            _mappingBehaviorSubj.OnNext(result.MappingBehavior);
+            _filteringBehaviorSubj.OnNext(result.FilteringBehavior);
+            PubSubj.OnNext(result.PublicEndPoint);
+
+            result = await FilteringBehaviorTestAsync();
             if (result.BindingTestResult != BindingTestResult.Success
             || result.FilteringBehavior == FilteringBehavior.UnsupportedServer
             )
@@ -33,46 +54,53 @@ namespace STUN.Client
                 return result;
             }
 
-            if (Equals(result.PublicEndPoint, result.LocalEndPoint))
+            try
             {
-                result.MappingBehavior = MappingBehavior.Direct;
-                return result;
-            }
+                if (Equals(result.PublicEndPoint, result.LocalEndPoint))
+                {
+                    result.MappingBehavior = MappingBehavior.Direct;
+                    return result;
+                }
 
-            // MappingBehaviorTest test II
-            var result2 = await BindingTestBaseAsync(new IPEndPoint(result.OtherEndPoint.Address, RemoteEndPoint.Port));
-            if (result2.BindingTestResult != BindingTestResult.Success)
-            {
-                result.MappingBehavior = MappingBehavior.Fail;
-                return result;
-            }
+                // MappingBehaviorTest test II
+                var result2 = await BindingTestBaseAsync(new IPEndPoint(result.OtherEndPoint.Address, RemoteEndPoint.Port), false);
+                if (result2.BindingTestResult != BindingTestResult.Success)
+                {
+                    result.MappingBehavior = MappingBehavior.Fail;
+                    return result;
+                }
+
+                if (Equals(result2.PublicEndPoint, result.PublicEndPoint))
+                {
+                    result.MappingBehavior = MappingBehavior.EndpointIndependent;
+                    return result;
+                }
+
+                // MappingBehaviorTest test III
+                var result3 = await BindingTestBaseAsync(result.OtherEndPoint, false);
+                if (result3.BindingTestResult != BindingTestResult.Success)
+                {
+                    result.MappingBehavior = MappingBehavior.Fail;
+                    return result;
+                }
+
+                result.MappingBehavior = Equals(result3.PublicEndPoint, result2.PublicEndPoint) ? MappingBehavior.AddressDependent : MappingBehavior.AddressAndPortDependent;
 
-            if (Equals(result2.PublicEndPoint, result.PublicEndPoint))
-            {
-                result.MappingBehavior = MappingBehavior.EndpointIndependent;
                 return result;
             }
-
-            // MappingBehaviorTest test III
-            var result3 = await BindingTestBaseAsync(result.OtherEndPoint);
-            if (result3.BindingTestResult != BindingTestResult.Success)
+            finally
             {
-                result.MappingBehavior = MappingBehavior.Fail;
-                return result;
+                _mappingBehaviorSubj.OnNext(result.MappingBehavior);
             }
-
-            result.MappingBehavior = Equals(result3.PublicEndPoint, result2.PublicEndPoint) ? MappingBehavior.AddressDependent : MappingBehavior.AddressAndPortDependent;
-
-            return result;
         }
 
         public async Task<StunResult5389> BindingTestAsync()
         {
-            var result = await BindingTestBaseAsync(RemoteEndPoint);
+            var result = await BindingTestBaseAsync(RemoteEndPoint, true);
             return result;
         }
 
-        private async Task<StunResult5389> BindingTestBaseAsync(IPEndPoint remote)
+        private async Task<StunResult5389> BindingTestBaseAsync(IPEndPoint remote, bool notifyChanged)
         {
             BindingTestResult res;
 
@@ -80,6 +108,7 @@ namespace STUN.Client
             var (response1, _, local1) = await TestAsync(test, remote, remote);
             var mappedAddress1 = AttributeExtensions.GetXorMappedAddressAttribute(response1);
             var otherAddress = AttributeExtensions.GetOtherAddressAttribute(response1);
+            var local = local1 == null ? null : new IPEndPoint(local1, LocalEndPoint.Port);
 
             if (response1 == null)
             {
@@ -94,10 +123,17 @@ namespace STUN.Client
                 res = BindingTestResult.Success;
             }
 
+            if (notifyChanged)
+            {
+                _bindingSubj.OnNext(res);
+            }
+            LocalSubj.OnNext(LocalEndPoint);
+            PubSubj.OnNext(mappedAddress1);
+
             return new StunResult5389
             {
                 BindingTestResult = res,
-                LocalEndPoint = local1 == null ? null : new IPEndPoint(local1, LocalEndPoint.Port),
+                LocalEndPoint = local,
                 PublicEndPoint = mappedAddress1,
                 OtherEndPoint = otherAddress
             };
@@ -106,109 +142,121 @@ namespace STUN.Client
         public async Task<StunResult5389> MappingBehaviorTestAsync()
         {
             // test I
-            var result1 = await BindingTestBaseAsync(RemoteEndPoint);
-
-            if (result1.BindingTestResult != BindingTestResult.Success)
+            var result1 = await BindingTestBaseAsync(RemoteEndPoint, true);
+            try
             {
-                return result1;
-            }
+                if (result1.BindingTestResult != BindingTestResult.Success)
+                {
+                    return result1;
+                }
 
-            if (result1.OtherEndPoint == null
-                || Equals(result1.OtherEndPoint.Address, RemoteEndPoint.Address)
-                || result1.OtherEndPoint.Port == RemoteEndPoint.Port)
-            {
-                result1.MappingBehavior = MappingBehavior.UnsupportedServer;
-                return result1;
-            }
+                if (result1.OtherEndPoint == null
+                    || Equals(result1.OtherEndPoint.Address, RemoteEndPoint.Address)
+                    || result1.OtherEndPoint.Port == RemoteEndPoint.Port)
+                {
+                    result1.MappingBehavior = MappingBehavior.UnsupportedServer;
+                    return result1;
+                }
 
-            if (Equals(result1.PublicEndPoint, result1.LocalEndPoint))
-            {
-                result1.MappingBehavior = MappingBehavior.Direct;
-                return result1;
-            }
+                if (Equals(result1.PublicEndPoint, result1.LocalEndPoint))
+                {
+                    result1.MappingBehavior = MappingBehavior.Direct;
+                    return result1;
+                }
 
-            // test II
-            var result2 = await BindingTestBaseAsync(new IPEndPoint(result1.OtherEndPoint.Address, RemoteEndPoint.Port));
-            if (result2.BindingTestResult != BindingTestResult.Success)
-            {
-                result1.MappingBehavior = MappingBehavior.Fail;
-                return result1;
-            }
+                // test II
+                var result2 = await BindingTestBaseAsync(new IPEndPoint(result1.OtherEndPoint.Address, RemoteEndPoint.Port), false);
+                if (result2.BindingTestResult != BindingTestResult.Success)
+                {
+                    result1.MappingBehavior = MappingBehavior.Fail;
+                    return result1;
+                }
+
+                if (Equals(result2.PublicEndPoint, result1.PublicEndPoint))
+                {
+                    result1.MappingBehavior = MappingBehavior.EndpointIndependent;
+                    return result1;
+                }
+
+                // test III
+                var result3 = await BindingTestBaseAsync(result1.OtherEndPoint, false);
+                if (result3.BindingTestResult != BindingTestResult.Success)
+                {
+                    result1.MappingBehavior = MappingBehavior.Fail;
+                    return result1;
+                }
+
+                result1.MappingBehavior = Equals(result3.PublicEndPoint, result2.PublicEndPoint) ? MappingBehavior.AddressDependent : MappingBehavior.AddressAndPortDependent;
 
-            if (Equals(result2.PublicEndPoint, result1.PublicEndPoint))
-            {
-                result1.MappingBehavior = MappingBehavior.EndpointIndependent;
                 return result1;
             }
-
-            // test III
-            var result3 = await BindingTestBaseAsync(result1.OtherEndPoint);
-            if (result3.BindingTestResult != BindingTestResult.Success)
+            finally
             {
-                result1.MappingBehavior = MappingBehavior.Fail;
-                return result1;
+                _mappingBehaviorSubj.OnNext(result1.MappingBehavior);
             }
-
-            result1.MappingBehavior = Equals(result3.PublicEndPoint, result2.PublicEndPoint) ? MappingBehavior.AddressDependent : MappingBehavior.AddressAndPortDependent;
-
-            return result1;
         }
 
         public async Task<StunResult5389> FilteringBehaviorTestAsync()
         {
             // test I
-            var result1 = await BindingTestBaseAsync(RemoteEndPoint);
-
-            if (result1.BindingTestResult != BindingTestResult.Success)
+            var result1 = await BindingTestBaseAsync(RemoteEndPoint, true);
+            try
             {
-                return result1;
-            }
+                if (result1.BindingTestResult != BindingTestResult.Success)
+                {
+                    return result1;
+                }
 
-            if (result1.OtherEndPoint == null
-                || Equals(result1.OtherEndPoint.Address, RemoteEndPoint.Address)
-                || result1.OtherEndPoint.Port == RemoteEndPoint.Port)
-            {
-                result1.FilteringBehavior = FilteringBehavior.UnsupportedServer;
-                return result1;
-            }
+                if (result1.OtherEndPoint == null
+                    || Equals(result1.OtherEndPoint.Address, RemoteEndPoint.Address)
+                    || result1.OtherEndPoint.Port == RemoteEndPoint.Port)
+                {
+                    result1.FilteringBehavior = FilteringBehavior.UnsupportedServer;
+                    return result1;
+                }
 
-            // test II
-            var test2 = new StunMessage5389
-            {
-                StunMessageType = StunMessageType.BindingRequest,
-                Attributes = new[] { AttributeExtensions.BuildChangeRequest(true, true) }
-            };
-            var (response2, _, _) = await TestAsync(test2, RemoteEndPoint, result1.OtherEndPoint);
+                // test II
+                var test2 = new StunMessage5389
+                {
+                    StunMessageType = StunMessageType.BindingRequest,
+                    Attributes = new[] { AttributeExtensions.BuildChangeRequest(true, true) }
+                };
+                var (response2, _, _) = await TestAsync(test2, RemoteEndPoint, result1.OtherEndPoint);
 
-            if (response2 != null)
-            {
-                result1.FilteringBehavior = FilteringBehavior.EndpointIndependent;
-                return result1;
-            }
+                if (response2 != null)
+                {
+                    result1.FilteringBehavior = FilteringBehavior.EndpointIndependent;
+                    return result1;
+                }
 
-            // test III
-            var test3 = new StunMessage5389
-            {
-                StunMessageType = StunMessageType.BindingRequest,
-                Attributes = new[] { AttributeExtensions.BuildChangeRequest(false, true) }
-            };
-            var (response3, remote3, _) = await TestAsync(test3, RemoteEndPoint, RemoteEndPoint);
+                // test III
+                var test3 = new StunMessage5389
+                {
+                    StunMessageType = StunMessageType.BindingRequest,
+                    Attributes = new[] { AttributeExtensions.BuildChangeRequest(false, true) }
+                };
+                var (response3, remote3, _) = await TestAsync(test3, RemoteEndPoint, RemoteEndPoint);
 
-            if (response3 == null)
-            {
-                result1.FilteringBehavior = FilteringBehavior.AddressAndPortDependent;
-                return result1;
-            }
+                if (response3 == null)
+                {
+                    result1.FilteringBehavior = FilteringBehavior.AddressAndPortDependent;
+                    return result1;
+                }
 
-            if (Equals(remote3.Address, RemoteEndPoint.Address) && remote3.Port != RemoteEndPoint.Port)
-            {
-                result1.FilteringBehavior = FilteringBehavior.AddressAndPortDependent;
+                if (Equals(remote3.Address, RemoteEndPoint.Address) && remote3.Port != RemoteEndPoint.Port)
+                {
+                    result1.FilteringBehavior = FilteringBehavior.AddressAndPortDependent;
+                }
+                else
+                {
+                    result1.FilteringBehavior = FilteringBehavior.UnsupportedServer;
+                }
+                return result1;
             }
-            else
+            finally
             {
-                result1.FilteringBehavior = FilteringBehavior.UnsupportedServer;
+                _filteringBehaviorSubj.OnNext(result1.FilteringBehavior);
             }
-            return result1;
         }
 
         private async Task<(StunMessage5389, IPEndPoint, IPAddress)> TestAsync(StunMessage5389 sendMessage, IPEndPoint remote, IPEndPoint receive)
@@ -246,5 +294,13 @@ namespace STUN.Client
             }
             return (null, null, null);
         }
+
+        public override void Dispose()
+        {
+            base.Dispose();
+            _bindingSubj.OnCompleted();
+            _mappingBehaviorSubj.OnCompleted();
+            _filteringBehaviorSubj.OnCompleted();
+        }
     }
 }