diff --git a/src/net/ICE/RtpIceChannel.cs b/src/net/ICE/RtpIceChannel.cs index 9ce685ffc..e939ba155 100755 --- a/src/net/ICE/RtpIceChannel.cs +++ b/src/net/ICE/RtpIceChannel.cs @@ -2316,6 +2316,11 @@ private SocketError SendTurnAllocateRequest(IceServer iceServer) STUNAttributeConstants.TcpTransportType : STUNAttributeConstants.UdpTransportType));*/ + allocateRequest.Attributes.Add( + new STUNAttribute(STUNAttributeTypesEnum.RequestedAddressFamily, + iceServer.ServerEndPoint.AddressFamily == AddressFamily.InterNetwork ? + STUNAttributeConstants.IPv4AddressFamily : STUNAttributeConstants.IPv6AddressFamily)); + byte[] allocateReqBytes = null; if (iceServer.Nonce != null && iceServer.Realm != null && iceServer._username != null && iceServer._password != null) @@ -2359,6 +2364,11 @@ private SocketError SendTurnRefreshRequest(IceServer iceServer) //allocateRequest.Attributes.Add(new STUNAttribute(STUNAttributeTypesEnum.Lifetime, 3600)); allocateRequest.Attributes.Add(new STUNAttribute(STUNAttributeTypesEnum.Lifetime, ALLOCATION_TIME_TO_EXPIRY_VALUE)); + allocateRequest.Attributes.Add( + new STUNAttribute(STUNAttributeTypesEnum.RequestedAddressFamily, + iceServer.ServerEndPoint.AddressFamily == AddressFamily.InterNetwork ? + STUNAttributeConstants.IPv4AddressFamily : STUNAttributeConstants.IPv6AddressFamily)); + byte[] allocateReqBytes = null; if (iceServer.Nonce != null && iceServer.Realm != null && iceServer._username != null && iceServer._password != null) @@ -2399,7 +2409,7 @@ private SocketError SendTurnCreatePermissionsRequest(string transactionID, IceSe { STUNMessage permissionsRequest = new STUNMessage(STUNMessageTypesEnum.CreatePermission); permissionsRequest.Header.TransactionId = Encoding.ASCII.GetBytes(transactionID); - permissionsRequest.Attributes.Add(new STUNXORAddressAttribute(STUNAttributeTypesEnum.XORPeerAddress, peerEndPoint.Port, peerEndPoint.Address)); + permissionsRequest.Attributes.Add(new STUNXORAddressAttribute(STUNAttributeTypesEnum.XORPeerAddress, peerEndPoint.Port, peerEndPoint.Address, permissionsRequest.Header.TransactionId)); byte[] createPermissionReqBytes = null; diff --git a/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs b/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs index 207ed1dd9..8e839caa4 100644 --- a/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs +++ b/src/net/STUN/STUNAttributes/STUNAddressAttribute.cs @@ -19,19 +19,17 @@ namespace SIPSorcery.Net { - public class STUNAddressAttribute : STUNAttribute + [Obsolete("Provided for backward compatibility with RFC3489 clients.")] + public class STUNAddressAttribute : STUNAddressAttributeBase { - public const UInt16 ADDRESS_ATTRIBUTE_LENGTH = 8; - - public int Family = 1; // Ipv4 = 1, IPv6 = 2. - public int Port; - public IPAddress Address; - - public override UInt16 PaddedLength - { - get { return ADDRESS_ATTRIBUTE_LENGTH; } - } - + /// + /// Obsolete. + ///
For IPv6 support, please parse using + ///
+ ///

+ /// Parses an IPv4 Address attribute. + ///
+ [Obsolete("Provided for backward compatibility with RFC3489 clients.")] public STUNAddressAttribute(byte[] attributeValue) : base(STUNAttributeTypesEnum.MappedAddress, attributeValue) { @@ -47,6 +45,14 @@ public STUNAddressAttribute(byte[] attributeValue) Address = new IPAddress(new byte[] { attributeValue[4], attributeValue[5], attributeValue[6], attributeValue[7] }); } + /// + /// Obsolete. + ///
For IPv6 support, please parse using + ///
+ ///

+ /// Parses an IPv4 Address attribute. + ///
+ [Obsolete("Provided for backward compatibility with RFC3489 clients.")] public STUNAddressAttribute(STUNAttributeTypesEnum attributeType, byte[] attributeValue) : base(attributeType, attributeValue) { @@ -62,6 +68,14 @@ public STUNAddressAttribute(STUNAttributeTypesEnum attributeType, byte[] attribu Address = new IPAddress(new byte[] { attributeValue[4], attributeValue[5], attributeValue[6], attributeValue[7] }); } + /// + /// Obsolete. + ///
For IPv6 support, please parse using + ///
+ ///

+ /// Parses an IPv4 Address attribute. + ///
+ [Obsolete("Provided for backward compatibility with RFC3489 clients.")] public STUNAddressAttribute(STUNAttributeTypesEnum attributeType, int port, IPAddress address) : base(attributeType, null) { diff --git a/src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs b/src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs new file mode 100644 index 000000000..6fea34308 --- /dev/null +++ b/src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace SIPSorcery.Net +{ + public abstract class STUNAddressAttributeBase : STUNAttribute + { + /// + /// Obsolete. + ///
Please use or instead. + ///

+ ///
+ [Obsolete("Default attribute length for IPv4 only.")] + public const UInt16 ADDRESS_ATTRIBUTE_LENGTH = 8; + + public const UInt16 ADDRESS_ATTRIBUTE_IPV4_LENGTH = 8; + public const UInt16 ADDRESS_ATTRIBUTE_IPV6_LENGTH = 20; + + protected UInt16 AddressAttributeLength = ADDRESS_ATTRIBUTE_IPV4_LENGTH; + protected byte[] TransactionId; + + /// + /// Defaults to IPv4 (0x01 // 1) + /// + public int Family = 1; // Ipv4 = 1, IPv6 = 2. + public int Port; + public IPAddress Address; + + public override UInt16 PaddedLength + { + get => AddressAttributeLength; + } + + public STUNAddressAttributeBase(STUNAttributeTypesEnum attributeType, byte[] value) + : base(attributeType, value) + { + } + } +} diff --git a/src/net/STUN/STUNAttributes/STUNAttribute.cs b/src/net/STUN/STUNAttributes/STUNAttribute.cs index fc23979c9..6fc997a65 100644 --- a/src/net/STUN/STUNAttributes/STUNAttribute.cs +++ b/src/net/STUN/STUNAttributes/STUNAttribute.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // Filename: STUNAttribute.cs // // Description: Implements STUN message attributes as defined in RFC5389. @@ -59,6 +59,7 @@ public enum STUNAttributeTypesEnum : ushort ReflectedFrom = 0x000B, // Not used in RFC5389. Realm = 0x0014, Nonce = 0x0015, + RequestedAddressFamily = 0x0017,// Added in RFC6156. XORMappedAddress = 0x0020, Software = 0x8022, // Added in RFC5389. @@ -97,6 +98,15 @@ public class STUNAttributeConstants { public static readonly byte[] UdpTransportType = new byte[] { 0x11, 0x00, 0x00, 0x00 }; // The payload type for UDP in a RequestedTransport type attribute. public static readonly byte[] TcpTransportType = new byte[] { 0x06, 0x00, 0x00, 0x00 }; // The payload type for TCP in a RequestedTransport type attribute. + + /// + /// The requested TURN relay ip address is IPv4 (RFC5389, Section 15.1) + /// + public static readonly byte[] IPv4AddressFamily = new byte[] { 0x01, 0x00, 0x00, 0x00 }; + /// + /// The requested TURN relay ip address is IPv6 (RFC5389, Section 15.1) + /// + public static readonly byte[] IPv6AddressFamily = new byte[] { 0x02, 0x00, 0x00, 0x00 }; } public class STUNAttribute @@ -147,7 +157,9 @@ public STUNAttribute(STUNAttributeTypesEnum attributeType, ulong value) Value = NetConvert.GetBytes(value); } - public static List ParseMessageAttributes(byte[] buffer, int startIndex, int endIndex) + public static List ParseMessageAttributes(byte[] buffer, int startIndex, int endIndex) => ParseMessageAttributes(buffer, startIndex, endIndex, null); + + public static List ParseMessageAttributes(byte[] buffer, int startIndex, int endIndex, STUNHeader header) { if (buffer != null && buffer.Length > startIndex && buffer.Length >= endIndex) { @@ -194,7 +206,7 @@ public static List ParseMessageAttributes(byte[] buffer, int star } else if (attributeType == STUNAttributeTypesEnum.XORMappedAddress || attributeType == STUNAttributeTypesEnum.XORPeerAddress || attributeType == STUNAttributeTypesEnum.XORRelayedAddress) { - attribute = new STUNXORAddressAttribute(attributeType, stunAttributeValue); + attribute = new STUNXORAddressAttribute(attributeType, stunAttributeValue, header.TransactionId); } else if(attributeType == STUNAttributeTypesEnum.ConnectionId) { diff --git a/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs b/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs index 6f90c7c6b..c6cc025c0 100644 --- a/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs +++ b/src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // Filename: STUNXORAddressAttribute.cs // // Description: Implements STUN XOR mapped address attribute as defined in RFC5389. @@ -14,6 +14,7 @@ //----------------------------------------------------------------------------- using System; +using System.Linq; using System.Net; using SIPSorcery.Sys; @@ -23,41 +24,89 @@ namespace SIPSorcery.Net /// This attribute is the same as the mapped address attribute except the address details are XOR'ed with the STUN magic cookie. /// THe reason for this is to stop NAT application layer gateways from doing string replacements of private IP addresses and ports. /// - public class STUNXORAddressAttribute : STUNAttribute + public class STUNXORAddressAttribute : STUNAddressAttributeBase { - public const UInt16 ADDRESS_ATTRIBUTE_LENGTH = 8; - - public int Family = 1; // Ipv4 = 1, IPv6 = 2. - public int Port; - public IPAddress Address; - - public override UInt16 PaddedLength + /// + /// Obsolete. + ///
For IPv6 support, please parse using + ///
+ ///

+ /// Parses an XOR-d (encoded) IPv4 Address attribute. + ///
+ [Obsolete("Provided for backward compatibility with RFC3489 clients.")] + public STUNXORAddressAttribute(STUNAttributeTypesEnum attributeType, byte[] attributeValue) + : this(attributeType, attributeValue, null) { - get { return ADDRESS_ATTRIBUTE_LENGTH; } } - public STUNXORAddressAttribute(STUNAttributeTypesEnum attributeType, byte[] attributeValue) + /// + /// Parses an XOR-d (encoded) Address attribute with IPv4/IPv6 support. + /// + /// of + /// or + /// or + /// the raw bytes + /// the + public STUNXORAddressAttribute(STUNAttributeTypesEnum attributeType, byte[] attributeValue, byte[] transactionId) : base(attributeType, attributeValue) { + Family = attributeValue[1]; + AddressAttributeLength = Family == 1 ? ADDRESS_ATTRIBUTE_IPV4_LENGTH : ADDRESS_ATTRIBUTE_IPV6_LENGTH; + TransactionId = transactionId; + + byte[] address; + if (BitConverter.IsLittleEndian) { Port = NetConvert.DoReverseEndian(BitConverter.ToUInt16(attributeValue, 2)) ^ (UInt16)(STUNHeader.MAGIC_COOKIE >> 16); - UInt32 address = NetConvert.DoReverseEndian(BitConverter.ToUInt32(attributeValue, 4)) ^ STUNHeader.MAGIC_COOKIE; - Address = new IPAddress(NetConvert.DoReverseEndian(address)); + address = BitConverter.GetBytes(NetConvert.DoReverseEndian(BitConverter.ToUInt32(attributeValue, 4)) ^ STUNHeader.MAGIC_COOKIE).Reverse().ToArray(); } else { Port = BitConverter.ToUInt16(attributeValue, 2) ^ (UInt16)(STUNHeader.MAGIC_COOKIE >> 16); - UInt32 address = BitConverter.ToUInt32(attributeValue, 4) ^ STUNHeader.MAGIC_COOKIE; - Address = new IPAddress(address); + address = BitConverter.GetBytes(BitConverter.ToUInt32(attributeValue, 4) ^ STUNHeader.MAGIC_COOKIE); + } + + if (Family == STUNAttributeConstants.IPv6AddressFamily[0] && TransactionId != null) + { + address = address.Concat(BitConverter.GetBytes(BitConverter.ToUInt32(attributeValue, 08) ^ BitConverter.ToUInt32(TransactionId, 0))) + .Concat(BitConverter.GetBytes(BitConverter.ToUInt32(attributeValue, 12) ^ BitConverter.ToUInt32(TransactionId, 4))) + .Concat(BitConverter.GetBytes(BitConverter.ToUInt32(attributeValue, 16) ^ BitConverter.ToUInt32(TransactionId, 8))) + .ToArray(); } + + Address = new IPAddress(address); } + /// + /// Obsolete. + ///
For IPv6 support, please create using + ///

+ /// Creates an XOR-d (encoded) IPv4 Address attribute. + ///
+ [Obsolete("Provided for backward compatibility with RFC3489 clients.")] public STUNXORAddressAttribute(STUNAttributeTypesEnum attributeType, int port, IPAddress address) + : this(attributeType, port, address, null) + { + } + + /// + /// Creates an XOR-d (encoded) Address attribute with IPv4/IPv6 support. + /// + /// of + /// or + /// or + /// Allocated Port + /// Allocated IPAddress + /// the + public STUNXORAddressAttribute(STUNAttributeTypesEnum attributeType, int port, IPAddress address, byte[] transactionId) : base(attributeType, null) { Port = port; Address = address; + Family = address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? 1 : 2; + AddressAttributeLength = Family == 1 ? ADDRESS_ATTRIBUTE_IPV4_LENGTH : ADDRESS_ATTRIBUTE_IPV6_LENGTH; + TransactionId = transactionId; } public override int ToByteBuffer(byte[] buffer, int startIndex) @@ -65,33 +114,45 @@ public override int ToByteBuffer(byte[] buffer, int startIndex) if (BitConverter.IsLittleEndian) { Buffer.BlockCopy(BitConverter.GetBytes(NetConvert.DoReverseEndian((UInt16)base.AttributeType)), 0, buffer, startIndex, 2); - Buffer.BlockCopy(BitConverter.GetBytes(NetConvert.DoReverseEndian(ADDRESS_ATTRIBUTE_LENGTH)), 0, buffer, startIndex + 2, 2); + Buffer.BlockCopy(BitConverter.GetBytes(NetConvert.DoReverseEndian(AddressAttributeLength)), 0, buffer, startIndex + 2, 2); } else { Buffer.BlockCopy(BitConverter.GetBytes((UInt16)base.AttributeType), 0, buffer, startIndex, 2); - Buffer.BlockCopy(BitConverter.GetBytes(ADDRESS_ATTRIBUTE_LENGTH), 0, buffer, startIndex + 2, 2); + Buffer.BlockCopy(BitConverter.GetBytes(AddressAttributeLength), 0, buffer, startIndex + 2, 2); } buffer[startIndex + 4] = 0x00; buffer[startIndex + 5] = (byte)Family; + var address = Address.GetAddressBytes(); + if (BitConverter.IsLittleEndian) { - UInt16 xorPort = Convert.ToUInt16(Convert.ToUInt16(Port) ^ (STUNHeader.MAGIC_COOKIE >> 16)); - UInt32 xorAddress = NetConvert.DoReverseEndian(BitConverter.ToUInt32(Address.GetAddressBytes(), 0)) ^ STUNHeader.MAGIC_COOKIE; + UInt16 xorPort = Convert.ToUInt16(Convert.ToUInt16(Port) ^ (UInt16)(STUNHeader.MAGIC_COOKIE >> 16)); + UInt32 xorAddress = NetConvert.DoReverseEndian(BitConverter.ToUInt32(address, 0)) ^ STUNHeader.MAGIC_COOKIE; Buffer.BlockCopy(BitConverter.GetBytes(NetConvert.DoReverseEndian(xorPort)), 0, buffer, startIndex + 6, 2); Buffer.BlockCopy(BitConverter.GetBytes(NetConvert.DoReverseEndian(xorAddress)), 0, buffer, startIndex + 8, 4); } else { - UInt16 xorPort = Convert.ToUInt16(Convert.ToUInt16(Port) ^ (STUNHeader.MAGIC_COOKIE >> 16)); - UInt32 xorAddress = BitConverter.ToUInt32(Address.GetAddressBytes(), 0) ^ STUNHeader.MAGIC_COOKIE; + UInt16 xorPort = Convert.ToUInt16(Convert.ToUInt16(Port) ^ (UInt16)(STUNHeader.MAGIC_COOKIE >> 16)); + UInt32 xorAddress = BitConverter.ToUInt32(address, 0) ^ STUNHeader.MAGIC_COOKIE; Buffer.BlockCopy(BitConverter.GetBytes(xorPort), 0, buffer, startIndex + 6, 2); Buffer.BlockCopy(BitConverter.GetBytes(xorAddress), 0, buffer, startIndex + 8, 4); } - return STUNAttribute.STUNATTRIBUTE_HEADER_LENGTH + ADDRESS_ATTRIBUTE_LENGTH; + if (Family == STUNAttributeConstants.IPv6AddressFamily[0] && TransactionId != null) + { + Buffer.BlockCopy( + BitConverter.GetBytes(BitConverter.ToUInt32(address, 04) ^ BitConverter.ToUInt32(TransactionId, 0)) + .Concat(BitConverter.GetBytes(BitConverter.ToUInt32(address, 08) ^ BitConverter.ToUInt32(TransactionId, 4))) + .Concat(BitConverter.GetBytes(BitConverter.ToUInt32(address, 12) ^ BitConverter.ToUInt32(TransactionId, 8))) + .ToArray(), + 0, buffer, startIndex + 12, 12); + } + + return STUNAttribute.STUNATTRIBUTE_HEADER_LENGTH + PaddedLength; } public override string ToString() diff --git a/src/net/STUN/STUNMessage.cs b/src/net/STUN/STUNMessage.cs index 28e6aa840..e6ed1a02f 100644 --- a/src/net/STUN/STUNMessage.cs +++ b/src/net/STUN/STUNMessage.cs @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // Filename: STUNMessage.cs // // Description: Implements STUN Message as defined in RFC5389. @@ -86,7 +86,7 @@ public void AddXORPeerAddressAttribute(IPAddress remoteAddress, int remotePort) public void AddXORAddressAttribute(STUNAttributeTypesEnum addressType, IPAddress remoteAddress, int remotePort) { - STUNXORAddressAttribute xorAddressAttribute = new STUNXORAddressAttribute(addressType, remotePort, remoteAddress); + STUNXORAddressAttribute xorAddressAttribute = new STUNXORAddressAttribute(addressType, remotePort, remoteAddress, Header.TransactionId); Attributes.Add(xorAddressAttribute); } @@ -100,7 +100,7 @@ public static STUNMessage ParseSTUNMessage(byte[] buffer, int bufferLength) if (stunMessage.Header.MessageLength > 0) { - stunMessage.Attributes = STUNAttribute.ParseMessageAttributes(buffer, STUNHeader.STUN_HEADER_LENGTH, bufferLength); + stunMessage.Attributes = STUNAttribute.ParseMessageAttributes(buffer, STUNHeader.STUN_HEADER_LENGTH, bufferLength, stunMessage.Header); } if (stunMessage.Attributes.Count > 0 && stunMessage.Attributes.Last().AttributeType == STUNAttributeTypesEnum.FingerPrint)