diff --git a/STUN/Attributes/STUNXorMappedAddressAttribute.cs b/STUN/Attributes/STUNXorMappedAddressAttribute.cs index b947ae6..260f437 100644 --- a/STUN/Attributes/STUNXorMappedAddressAttribute.cs +++ b/STUN/Attributes/STUNXorMappedAddressAttribute.cs @@ -1,4 +1,7 @@ -namespace STUN.Attributes +using System; +using System.Net; + +namespace STUN.Attributes { public class STUNXorMappedAddressAttribute : STUNEndPointAttribute { @@ -6,5 +9,58 @@ public override string ToString() { return string.Format("XOR-MAPPED-ADDRESS {0}", EndPoint); } + + public override void Parse(STUNMessage msg, STUNBinaryReader binary, int length) + { + binary.BaseStream.Position++; + + var ipFamily = binary.ReadByte(); + // Port is computed by taking the mapped port in host byte order, + // XOR'ing it with the most significant 16 bits of the magic cookie. + byte[] p = binary.ReadBytes(2); + for (int i = 0; i < 2; ++i) + { + p[i] = (byte)(p[i] ^ msg.TransactionID[i]); + } + + if (BitConverter.IsLittleEndian) + { + Array.Reverse(p); + } + + var port = BitConverter.ToUInt16(p, 0); + + IPAddress address; + + if (ipFamily == 1) + { + // IPv4 address is computed by taking the mapped IP + // address in host byte order and XOR'ing it with the magic cookie. + byte [] a = binary.ReadBytes(4); + for (int i = 0; i < 4; ++i) + { + a[i] = (byte)(a[i] ^ msg.TransactionID[i]); + } + address = new IPAddress(a); + } + else if (ipFamily == 2) + { + // IPv6 address is computed by taking the mapped IP address + // in host byte order, XOR'ing it with the concatenation of the magic + // cookie and the 96 - bit transaction ID. + byte[] a = binary.ReadBytes(16); + for (int i = 0; i < 16; ++i) + { + a[i] = (byte)(a[i] ^ msg.TransactionID[i]); + } + address = new IPAddress(a); + } + else + { + throw new Exception("Unsupported IP Family " + ipFamily.ToString()); + } + + EndPoint = new IPEndPoint(address, port); + } } } \ No newline at end of file diff --git a/STUN/Attributes/StunAsciiTextAttribute.cs b/STUN/Attributes/StunAsciiTextAttribute.cs index 596abf2..d2b93a1 100644 --- a/STUN/Attributes/StunAsciiTextAttribute.cs +++ b/STUN/Attributes/StunAsciiTextAttribute.cs @@ -10,7 +10,7 @@ public class STUNAsciiTextAttribute : STUNAttribute { public string Text { get; set; } - public override void Parse(STUNBinaryReader binary, int length) + public override void Parse(STUNMessage msg, STUNBinaryReader binary, int length) { Text = Encoding.ASCII.GetString(binary.ReadBytes(length)); } diff --git a/STUN/Attributes/StunChangeRequestAttribute.cs b/STUN/Attributes/StunChangeRequestAttribute.cs index 5bfba17..3043a27 100644 --- a/STUN/Attributes/StunChangeRequestAttribute.cs +++ b/STUN/Attributes/StunChangeRequestAttribute.cs @@ -24,7 +24,7 @@ public STUNChangeRequestAttribute(bool ip, bool port) ChangePort = port; } - public override void Parse(STUNBinaryReader binary, int length) + public override void Parse(STUNMessage msg, STUNBinaryReader binary, int length) { binary.BaseStream.Position += 3; var b = binary.ReadByte(); diff --git a/STUN/Attributes/StunEndPointAttribute.cs b/STUN/Attributes/StunEndPointAttribute.cs index fab21ca..d333abb 100644 --- a/STUN/Attributes/StunEndPointAttribute.cs +++ b/STUN/Attributes/StunEndPointAttribute.cs @@ -12,7 +12,7 @@ public class STUNEndPointAttribute : STUNAttribute { public IPEndPoint EndPoint { get; set; } - public override void Parse(STUNBinaryReader binary, int length) + public override void Parse(STUNMessage msg, STUNBinaryReader binary, int length) { binary.BaseStream.Position++; var ipFamily = binary.ReadByte(); diff --git a/STUN/Attributes/StunErrorCodeAttribute.cs b/STUN/Attributes/StunErrorCodeAttribute.cs index c272c28..5232da0 100644 --- a/STUN/Attributes/StunErrorCodeAttribute.cs +++ b/STUN/Attributes/StunErrorCodeAttribute.cs @@ -11,7 +11,7 @@ public class STUNErrorCodeAttribute : STUNAttribute public STUNErrorCodes Error { get; set; } public string Phrase { get; set; } - public override void Parse(STUNBinaryReader binary, int length) + public override void Parse(STUNMessage msg, STUNBinaryReader binary, int length) { throw new NotImplementedException(); } diff --git a/STUN/Attributes/StunMessageIntegrityAttribute.cs b/STUN/Attributes/StunMessageIntegrityAttribute.cs index 52b27e3..f8a6bac 100644 --- a/STUN/Attributes/StunMessageIntegrityAttribute.cs +++ b/STUN/Attributes/StunMessageIntegrityAttribute.cs @@ -8,7 +8,7 @@ namespace STUN.Attributes { public class STUNMessageIntegrityAttribute : STUNAttribute { - public override void Parse(STUNBinaryReader binary, int length) + public override void Parse(STUNMessage msg, STUNBinaryReader binary, int length) { throw new NotImplementedException(); } diff --git a/STUN/Attributes/StunReflectedFromAttribute.cs b/STUN/Attributes/StunReflectedFromAttribute.cs index 576cf7f..2f8fc96 100644 --- a/STUN/Attributes/StunReflectedFromAttribute.cs +++ b/STUN/Attributes/StunReflectedFromAttribute.cs @@ -8,7 +8,7 @@ namespace STUN.Attributes { public class STUNReflectedFromAttribute : STUNAttribute { - public override void Parse(STUNBinaryReader binary, int length) + public override void Parse(STUNMessage msg, STUNBinaryReader binary, int length) { throw new NotImplementedException(); } diff --git a/STUN/STUN.csproj b/STUN/STUN.csproj index 17c788c..f5d24ab 100644 --- a/STUN/STUN.csproj +++ b/STUN/STUN.csproj @@ -1,7 +1,7 @@  - netstandard1.3;netstandard2.0;net45 + netstandard2.0;net452 0.5.0 Moien007 diff --git a/STUN/STUNAttribute.cs b/STUN/STUNAttribute.cs index 1b018a2..ba030c0 100644 --- a/STUN/STUNAttribute.cs +++ b/STUN/STUNAttribute.cs @@ -33,7 +33,7 @@ static STUNAttribute() AddAttribute(0x802C); } - public abstract void Parse(STUNBinaryReader binary, int length); + public abstract void Parse(STUNMessage msg, STUNBinaryReader binary, int length); public virtual void Write(STUNBinaryWriter binary) { diff --git a/STUN/STUNClient.cs b/STUN/STUNClient.cs index 280a2cc..4e140c9 100644 --- a/STUN/STUNClient.cs +++ b/STUN/STUNClient.cs @@ -20,7 +20,7 @@ public static class STUNClient /// /// Period of time in miliseconds to wait for server response. /// - public static int ReceiveTimeout = 5000; + public static int ReceiveTimeout = 500; /// Server address /// Query type diff --git a/STUN/STUNMessage.cs b/STUN/STUNMessage.cs index 5e370fa..af327a3 100644 --- a/STUN/STUNMessage.cs +++ b/STUN/STUNMessage.cs @@ -79,7 +79,7 @@ public void Parse(STUNBinaryReader binary) if (type != null) { var attr = Activator.CreateInstance(type) as STUNAttribute; - attr.Parse(binary, attrLength); + attr.Parse(this, binary, attrLength); Attributes.Add(attr); } else diff --git a/STUN/STUNNatMappingBehavior.cs b/STUN/STUNNatMappingBehavior.cs index 861393f..703ee2d 100644 --- a/STUN/STUNNatMappingBehavior.cs +++ b/STUN/STUNNatMappingBehavior.cs @@ -2,6 +2,7 @@ { public enum STUNNatMappingBehavior { + NoMapping, EndpointIndependentMapping, AddressDependMapping, AddressAndPortDependMapping diff --git a/STUN/STUNQueryError.cs b/STUN/STUNQueryError.cs index 77db035..1267669 100644 --- a/STUN/STUNQueryError.cs +++ b/STUN/STUNQueryError.cs @@ -26,7 +26,7 @@ public enum STUNQueryError /// /// Indicates the server didn't response a request within a time interval /// - Timedout, + Timeout, /// /// Indicates the server did not support nat detection /// diff --git a/STUN/STUNQueryResult.cs b/STUN/STUNQueryResult.cs index 8d75c5d..452be46 100644 --- a/STUN/STUNQueryResult.cs +++ b/STUN/STUNQueryResult.cs @@ -46,6 +46,8 @@ public class STUNQueryResult /// public STUNNATType NATType { get; set; } + public STUNNatFilteringBehavior FilteringBehavior { get; set; } + /// /// Contains the public endpoint that queried from server. /// diff --git a/STUN/STUNRfc3489.cs b/STUN/STUNRfc3489.cs index 7963f41..7cf296e 100644 --- a/STUN/STUNRfc3489.cs +++ b/STUN/STUNRfc3489.cs @@ -28,7 +28,7 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT // didn't receive anything if (responseBuffer == null) { - result.QueryError = STUNQueryError.Timedout; + result.QueryError = STUNQueryError.Timeout; return result; } @@ -240,7 +240,7 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT if (responseBuffer == null) { - result.QueryError = STUNQueryError.Timedout; + result.QueryError = STUNQueryError.Timeout; return result; } @@ -312,7 +312,7 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT if (!message.TryParse(responseBuffer)) { - result.QueryError = STUNQueryError.Timedout; + result.QueryError = STUNQueryError.Timeout; return result; } diff --git a/STUN/STUNRfc5780.cs b/STUN/STUNRfc5780.cs index ff6d112..9753aef 100644 --- a/STUN/STUNRfc5780.cs +++ b/STUN/STUNRfc5780.cs @@ -9,7 +9,7 @@ public class STUNRfc5780 { public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryType queryType, int ReceiveTimeout) { - STUNNatMappingBehavior mappingBehavior = STUNNatMappingBehavior.EndpointIndependentMapping; + STUNNatMappingBehavior mappingBehavior = STUNNatMappingBehavior.NoMapping; STUNNatFilteringBehavior filteringBehavior = STUNNatFilteringBehavior.EndpointIndependentFiltering; var result = new STUNQueryResult(); // the query result result.Socket = socket; @@ -31,7 +31,7 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT // didn't receive anything if (responseBuffer == null) { - result.QueryError = STUNQueryError.Timedout; + result.QueryError = STUNQueryError.Timeout; return result; } @@ -53,7 +53,7 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT var errorAttr = message.Attributes.FirstOrDefault(p => p is STUNErrorCodeAttribute) as STUNErrorCodeAttribute; - // if server responsed our request with error + // if server responded our request with error if (message.MessageType == STUNMessageTypes.BindingErrorResponse) { if (errorAttr == null) @@ -69,7 +69,7 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT return result; } - // return if receive something else binding response + // return if receive something else than binding response if (message.MessageType != STUNMessageTypes.BindingResponse) { result.QueryError = STUNQueryError.BadResponse; @@ -94,12 +94,6 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT return result; } - - if (xorAddressAttribute.EndPoint.Equals(socket.LocalEndPoint)) - { - result.NATType = STUNNATType.OpenInternet; - } - var otherAddressAttribute = message.Attributes.FirstOrDefault(p => p is STUNOtherAddressAttribute) as STUNOtherAddressAttribute; @@ -118,68 +112,85 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT otherAddressAttribute.EndPoint = changedAddressAttribute.EndPoint; } - // Make test 2 - bind different ip address but primary port message = new STUNMessage(STUNMessageTypes.BindingRequest, transID); // create a bind request IPEndPoint secondaryServer = new IPEndPoint(otherAddressAttribute.EndPoint.Address, server.Port); + socket.SendTo(message.GetBytes(), secondaryServer); responseBuffer = STUNUtils.Receive(socket, ReceiveTimeout); // Secondary server presented but is down if (responseBuffer == null) { - result.QueryError = STUNQueryError.NotSupported; + result.QueryError = STUNQueryError.Timeout; return result; } if (!message.TryParse(responseBuffer)) { - result.QueryError = STUNQueryError.NotSupported; + result.QueryError = STUNQueryError.BadResponse; return result; } var xorAddressAttribute2 = message.Attributes.FirstOrDefault(p => p is STUNXorMappedAddressAttribute) as STUNXorMappedAddressAttribute; - if (xorAddressAttribute2 != null) + if (xorAddressAttribute2 == null) { - if (xorAddressAttribute.EndPoint.Equals(xorAddressAttribute2.EndPoint)) + result.QueryError = STUNQueryError.BadResponse; + return result; + } + + if (xorAddressAttribute.EndPoint.Equals(xorAddressAttribute2.EndPoint)) + { + if (xorAddressAttribute.EndPoint.Equals(socket.LocalEndPoint) || + Dns.GetHostAddresses(Dns.GetHostName()).Contains(xorAddressAttribute.EndPoint.Address) + ) + { + mappingBehavior = STUNNatMappingBehavior.NoMapping; + } + else { mappingBehavior = STUNNatMappingBehavior.EndpointIndependentMapping; } + } + else // Make test 3 + { + IPEndPoint secondaryServerPort = new IPEndPoint(otherAddressAttribute.EndPoint.Address, + otherAddressAttribute.EndPoint.Port); + + message = new STUNMessage(STUNMessageTypes.BindingRequest, transID); // create a bind request + socket.SendTo(message.GetBytes(), secondaryServerPort); + responseBuffer = STUNUtils.Receive(socket, ReceiveTimeout); + + if (responseBuffer == null) + { + result.QueryError = STUNQueryError.Timeout; + return result; + } - // Make test 3 + if (!message.TryParse(responseBuffer)) + { + result.QueryError = STUNQueryError.BadResponse; + return result; + } + + var xorAddressAttribute3 = message.Attributes.FirstOrDefault(p => p is STUNXorMappedAddressAttribute) + as STUNXorMappedAddressAttribute; + + if (xorAddressAttribute3 == null) + { + result.QueryError = STUNQueryError.BadResponse; + return result; + } + + if (xorAddressAttribute3.EndPoint.Equals(xorAddressAttribute2.EndPoint)) + { + mappingBehavior = STUNNatMappingBehavior.AddressDependMapping; + } else { - IPEndPoint secondaryServerPort = new IPEndPoint(otherAddressAttribute.EndPoint.Address, - otherAddressAttribute.EndPoint.Port); - - message = new STUNMessage(STUNMessageTypes.BindingRequest, transID); // create a bind request - socket.SendTo(message.GetBytes(), secondaryServerPort); - responseBuffer = STUNUtils.Receive(socket, ReceiveTimeout); - - if (!message.TryParse(responseBuffer)) - { - result.QueryError = STUNQueryError.NotSupported; - return result; - } - - var xorAddressAttribute3 = - message.Attributes.FirstOrDefault(p => p is STUNXorMappedAddressAttribute) - as STUNXorMappedAddressAttribute; - - if (xorAddressAttribute3 != null) - { - if (xorAddressAttribute3.EndPoint.Equals(xorAddressAttribute2.EndPoint)) - { - mappingBehavior = STUNNatMappingBehavior.AddressDependMapping; - } - - else - { - mappingBehavior = STUNNatMappingBehavior.AddressAndPortDependMapping; - } - } + mappingBehavior = STUNNatMappingBehavior.AddressAndPortDependMapping; } } @@ -200,9 +211,7 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT { filteringBehavior = STUNNatFilteringBehavior.EndpointIndependentFiltering; } - - // Test 3 - send request to original server with change port attribute - else + else // Test 3 - send request to original server with change port attribute { message = new STUNMessage(STUNMessageTypes.BindingRequest, transID); message.Attributes.Add(new STUNChangeRequestAttribute(false, true)); @@ -215,34 +224,35 @@ public static STUNQueryResult Query(Socket socket, IPEndPoint server, STUNQueryT { filteringBehavior = STUNNatFilteringBehavior.AddressDependFiltering; } - else { filteringBehavior = STUNNatFilteringBehavior.AddressAndPortDependFiltering; } } - if (filteringBehavior == STUNNatFilteringBehavior.AddressAndPortDependFiltering && - mappingBehavior == STUNNatMappingBehavior.AddressAndPortDependMapping) + result.FilteringBehavior = filteringBehavior; + if (mappingBehavior == STUNNatMappingBehavior.NoMapping) { - result.NATType = STUNNATType.Symmetric; - } - - if (filteringBehavior == STUNNatFilteringBehavior.EndpointIndependentFiltering && - mappingBehavior == STUNNatMappingBehavior.EndpointIndependentMapping) - { - result.NATType = STUNNATType.FullCone; + result.NATType = STUNNATType.OpenInternet; } - - if (filteringBehavior == STUNNatFilteringBehavior.EndpointIndependentFiltering && - mappingBehavior == STUNNatMappingBehavior.AddressDependMapping) + else if (mappingBehavior == STUNNatMappingBehavior.EndpointIndependentMapping) { - result.NATType = STUNNATType.Restricted; + if (filteringBehavior == STUNNatFilteringBehavior.EndpointIndependentFiltering) + { + result.NATType = STUNNATType.FullCone; + } + else if (filteringBehavior == STUNNatFilteringBehavior.AddressDependFiltering) + { + result.NATType = STUNNATType.Restricted; + } + else // (filteringBehavior == STUNNatFilteringBehavior.AddressAndPortDependFiltering) + { + result.NATType = STUNNATType.PortRestricted; + } } - - if (result.NATType == STUNNATType.Unspecified) + else { - result.NATType = STUNNATType.PortRestricted; + result.NATType = STUNNATType.Symmetric; } return result; diff --git a/STUN/STUNUtils.cs b/STUN/STUNUtils.cs index 2106124..bcba4d0 100644 --- a/STUN/STUNUtils.cs +++ b/STUN/STUNUtils.cs @@ -2,7 +2,7 @@ using System.Net; using System.Net.Sockets; -namespace STUN.Attributes +namespace STUN { public class STUNUtils { diff --git a/TestApp/Program.cs b/TestApp/Program.cs index 9ad08f0..0a4c58e 100644 --- a/TestApp/Program.cs +++ b/TestApp/Program.cs @@ -9,7 +9,10 @@ class Program { static void Main(string[] args) { - if (!STUNUtils.TryParseHostAndPort("stun.schlund.de:3478", out IPEndPoint stunEndPoint)) + // args[0] - STUN server IP and port, e.g. "stun.schlund.de:3478" + String stunAddresAndPort = args[0]; + + if (!STUNUtils.TryParseHostAndPort(stunAddresAndPort, out IPEndPoint stunEndPoint)) throw new Exception("Failed to resolve STUN server address"); STUNClient.ReceiveTimeout = 500; diff --git a/TestApp/Properties/launchSettings.json b/TestApp/Properties/launchSettings.json new file mode 100644 index 0000000..4c1c189 --- /dev/null +++ b/TestApp/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "TestApp": { + "commandName": "Project", + "commandLineArgs": "172.16.0.3:3478 10.0.0.2" + } + } +} \ No newline at end of file