|
@@ -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();
|
|
|
+ }
|
|
|
}
|
|
|
}
|