Skip to content

Commit

Permalink
WebRTC IPv6 support for TURN (along with REQUESTED-ADDRESS-FAMILY) (#…
Browse files Browse the repository at this point in the history
…1133)

* requested-address-family RFC6156, address family constant values RFC5389

* ipv6 support for xor-addresses

* create base class for address attributes

* obsolescence

* transactionid usage for ipv6

* more transactionid usage

* requested-address-family for refresh turn
  • Loading branch information
ha-ves committed Jul 7, 2024
1 parent 8e75121 commit f35cc1e
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 41 deletions.
12 changes: 11 additions & 1 deletion src/net/ICE/RtpIceChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand Down
38 changes: 26 additions & 12 deletions src/net/STUN/STUNAttributes/STUNAddressAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}

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

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

/// <summary>
/// Obsolete.
/// <br/> For IPv6 support, please parse using
/// <br/> <see cref="STUNXORAddressAttribute(STUNAttributeTypesEnum, byte[], byte[])"/>
/// <br/> <br/>
/// Parses an IPv4 Address attribute.
/// </summary>
[Obsolete("Provided for backward compatibility with RFC3489 clients.")]
public STUNAddressAttribute(STUNAttributeTypesEnum attributeType, int port, IPAddress address)
: base(attributeType, null)
{
Expand Down
41 changes: 41 additions & 0 deletions src/net/STUN/STUNAttributes/STUNAddressAttributeBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;

namespace SIPSorcery.Net
{
public abstract class STUNAddressAttributeBase : STUNAttribute
{
/// <summary>
/// Obsolete.
/// <br/> Please use <see cref="ADDRESS_ATTRIBUTE_IPV4_LENGTH"/> or <see cref="ADDRESS_ATTRIBUTE_IPV6_LENGTH"/> instead.
/// <br/> <br/>
/// </summary>
[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;

/// <summary>
/// Defaults to IPv4 (0x01 // 1)
/// </summary>
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)
{
}
}
}
18 changes: 15 additions & 3 deletions src/net/STUN/STUNAttributes/STUNAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Filename: STUNAttribute.cs
//
// Description: Implements STUN message attributes as defined in RFC5389.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.

/// <summary>
/// The requested TURN relay ip address is IPv4 (RFC5389, Section 15.1)
/// </summary>
public static readonly byte[] IPv4AddressFamily = new byte[] { 0x01, 0x00, 0x00, 0x00 };
/// <summary>
/// The requested TURN relay ip address is IPv6 (RFC5389, Section 15.1)
/// </summary>
public static readonly byte[] IPv6AddressFamily = new byte[] { 0x02, 0x00, 0x00, 0x00 };
}

public class STUNAttribute
Expand Down Expand Up @@ -147,7 +157,9 @@ public STUNAttribute(STUNAttributeTypesEnum attributeType, ulong value)
Value = NetConvert.GetBytes(value);
}

public static List<STUNAttribute> ParseMessageAttributes(byte[] buffer, int startIndex, int endIndex)
public static List<STUNAttribute> ParseMessageAttributes(byte[] buffer, int startIndex, int endIndex) => ParseMessageAttributes(buffer, startIndex, endIndex, null);

public static List<STUNAttribute> ParseMessageAttributes(byte[] buffer, int startIndex, int endIndex, STUNHeader header)
{
if (buffer != null && buffer.Length > startIndex && buffer.Length >= endIndex)
{
Expand Down Expand Up @@ -194,7 +206,7 @@ public static List<STUNAttribute> 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)
{
Expand Down
105 changes: 83 additions & 22 deletions src/net/STUN/STUNAttributes/STUNXORAddressAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Filename: STUNXORAddressAttribute.cs
//
// Description: Implements STUN XOR mapped address attribute as defined in RFC5389.
Expand All @@ -14,6 +14,7 @@
//-----------------------------------------------------------------------------

using System;
using System.Linq;
using System.Net;
using SIPSorcery.Sys;

Expand All @@ -23,75 +24,135 @@ 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.
/// </summary>
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
/// <summary>
/// Obsolete.
/// <br/> For IPv6 support, please parse using
/// <br/> <see cref="STUNXORAddressAttribute(STUNAttributeTypesEnum, byte[], byte[])"/>
/// <br/> <br/>
/// Parses an XOR-d (encoded) IPv4 Address attribute.
/// </summary>
[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)
/// <summary>
/// Parses an XOR-d (encoded) Address attribute with IPv4/IPv6 support.
/// </summary>
/// <param name="attributeType">of <see cref="STUNAttributeTypesEnum.XORMappedAddress"/>
/// or <see cref="STUNAttributeTypesEnum.XORPeerAddress"/>
/// or <see cref="STUNAttributeTypesEnum.XORRelayedAddress"/></param>
/// <param name="attributeValue">the raw bytes</param>
/// <param name="transactionId">the <see cref="STUNHeader.TransactionId"/></param>
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);
}

/// <summary>
/// Obsolete.
/// <br/> For IPv6 support, please create using <see cref="STUNXORAddressAttribute(STUNAttributeTypesEnum, int, IPAddress, byte[])"/>
/// <br/> <br/>
/// Creates an XOR-d (encoded) IPv4 Address attribute.
/// </summary>
[Obsolete("Provided for backward compatibility with RFC3489 clients.")]
public STUNXORAddressAttribute(STUNAttributeTypesEnum attributeType, int port, IPAddress address)
: this(attributeType, port, address, null)
{
}

/// <summary>
/// Creates an XOR-d (encoded) Address attribute with IPv4/IPv6 support.
/// </summary>
/// <param name="attributeType">of <see cref="STUNAttributeTypesEnum.XORMappedAddress"/>
/// or <see cref="STUNAttributeTypesEnum.XORPeerAddress"/>
/// or <see cref="STUNAttributeTypesEnum.XORRelayedAddress"/></param>
/// <param name="port">Allocated Port</param>
/// <param name="address">Allocated IPAddress</param>
/// <param name="transactionId">the <see cref="STUNHeader.TransactionId"/></param>
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)
{
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()
Expand Down
Loading

0 comments on commit f35cc1e

Please sign in to comment.