Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions src/net/ICE/IceChecklistEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Microsoft.Extensions.Logging;
using SIPSorcery.Sys;
Expand Down Expand Up @@ -217,6 +218,26 @@ public string RequestTransactionID
/// </summary>
public DateTime TurnPermissionsResponseAt { get; set; } = DateTime.MinValue;

/// <summary>
/// Records the time a TURN Connect success response was received (RFC 6062).
/// </summary>
public DateTime TurnConnectReportAt { get; internal set; } = DateTime.MinValue;

/// <summary>
/// Records the time a TURN ConnectionBind success response was received (RFC 6062).
/// </summary>
public DateTime TurnConnectBindedAt { get; internal set; } = DateTime.MinValue;

/// <summary>
/// Number of TURN Connect requests sent for this entry (RFC 6062).
/// </summary>
public int TurnConnectRequestSent { get; internal set; }

/// <summary>
/// The Connection ID received from a TURN Connect success response (RFC 6062).
/// </summary>
public uint TurnConnectionId { get; internal set; }

/// <summary>
/// If a candidate has been nominated this field records the time the last
/// STUN binding response was received from the remote peer.
Expand Down Expand Up @@ -359,6 +380,66 @@ internal void GotStunResponse(STUNMessage stunResponse, IPEndPoint remoteEndPoin
TurnPermissionsResponseAt = DateTime.Now;
State = retry ? State : ChecklistEntryState.Failed;
}
else if (stunResponse.Header.MessageType == STUNMessageTypesEnum.ConnectSuccessResponse)
{
logger.LogDebug("A TURN Connect success response was received from ICE server {IceServer} (TxID: {TransactionId}).",
LocalCandidate.IceServer._uri, Encoding.ASCII.GetString(stunResponse.Header.TransactionId));

TurnConnectionId = stunResponse.Attributes.FirstOrDefault(x => x.AttributeType == STUNAttributeTypesEnum.ConnectionId)
is STUNConnectionIdAttribute connectionIdAttribute
? connectionIdAttribute.ConnectionId
: 0;

TurnConnectReportAt = DateTime.Now;
TurnConnectRequestSent = 0;

// After Connect succeeds, need ConnectionBind on a new data connection.
if (State == ChecklistEntryState.InProgress)
{
State = ChecklistEntryState.Waiting;
FirstCheckSentAt = DateTime.MinValue;
}
}
else if (stunResponse.Header.MessageType == STUNMessageTypesEnum.ConnectErrorResponse)
{
logger.LogWarning("A TURN Connect error response was received from {RemoteEndPoint}.", remoteEndPoint);

if (stunResponse.Attributes.Any(x => x.AttributeType == STUNAttributeTypesEnum.ErrorCode))
{
var errAttr = stunResponse.Attributes.First(x => x.AttributeType == STUNAttributeTypesEnum.ErrorCode)
as STUNErrorCodeAttribute;
if (errAttr?.ErrorCode == IceServer.STUN_CONNECTION_TIMEOUT_OR_FAILURE)
{
TurnConnectReportAt = DateTime.Now;
retry = true;
}
}

if (!retry)
{
State = ChecklistEntryState.Failed;
}
}
else if (stunResponse.Header.MessageType == STUNMessageTypesEnum.ConnectionBindSuccessResponse)
{
logger.LogDebug("A TURN ConnectionBind success response was received from ICE server {IceServer} (TxID: {TransactionId}).",
LocalCandidate.IceServer._uri, Encoding.ASCII.GetString(stunResponse.Header.TransactionId));

TurnConnectBindedAt = TurnConnectReportAt = DateTime.Now;
TurnConnectRequestSent = 0;

// After ConnectionBind, the underlying STUN binding check is next.
if (State == ChecklistEntryState.InProgress)
{
State = ChecklistEntryState.Waiting;
FirstCheckSentAt = DateTime.MinValue;
}
}
else if (stunResponse.Header.MessageType == STUNMessageTypesEnum.ConnectionBindErrorResponse)
{
logger.LogWarning("A TURN ConnectionBind error response was received from {RemoteEndPoint}.", remoteEndPoint);
State = retry ? State : ChecklistEntryState.Failed;
}
else
{
logger.LogWarning("ICE RTP channel received an unexpected STUN response {MessageType} from {RemoteEndPoint}.", stunResponse.Header.MessageType, remoteEndPoint);
Expand Down
32 changes: 26 additions & 6 deletions src/net/ICE/IceServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ public class IceServer
/// </summary>
internal const int STUN_STALE_NONCE_ERROR_CODE = 438;

/// <summary>
/// RFC 6062: Connection Already Exists error code.
/// </summary>
internal const int STUN_CONNECTION_ALREADY_EXISTS = 446;

/// <summary>
/// RFC 6062: Connection Timeout or Failure error code.
/// </summary>
internal const int STUN_CONNECTION_TIMEOUT_OR_FAILURE = 447;

internal STUNUri _uri;
internal string _username;
internal string _password;
Expand Down Expand Up @@ -178,8 +188,22 @@ public class IceServer
/// </summary>
internal int ErrorResponseCount = 0;

/// <summary>
/// Transport protocol for connecting with the server.
/// </summary>
public ProtocolType Protocol { get { return _uri.Protocol; } }

/// <summary>
/// Protocol of the ICE relay candidate to be allocated.
/// Only affects TURN server usage. Defaults to UDP.
/// </summary>
internal ProtocolType IceRelayProtocol { get; set; } = ProtocolType.Udp;

/// <summary>
/// Secondary relay URI used for the data connection in RFC 6062 TCP relay.
/// </summary>
internal STUNUri _secondaryRelayUri;

public STUNUri Uri { get { return _uri; } }

/// <summary>
Expand Down Expand Up @@ -329,9 +353,7 @@ internal RTCIceCandidate GetCandidate(RTCIceCandidateInit init, RTCIceCandidateT

if (type == RTCIceCandidateType.srflx && ServerReflexiveEndPoint != null)
{
// TODO: Currently implementation always use UDP candidates as we will only support TURN TCP Transport.
//var srflxProtocol = _uri.Protocol == ProtocolType.Tcp ? RTCIceProtocol.tcp : RTCIceProtocol.udp;
var srflxProtocol = RTCIceProtocol.udp;
var srflxProtocol = _uri.Protocol == ProtocolType.Tcp ? RTCIceProtocol.tcp : RTCIceProtocol.udp;
candidate.SetAddressProperties(srflxProtocol, ServerReflexiveEndPoint.Address, (ushort)ServerReflexiveEndPoint.Port,
type, null, 0);
candidate.IceServer = this;
Expand All @@ -340,9 +362,7 @@ internal RTCIceCandidate GetCandidate(RTCIceCandidateInit init, RTCIceCandidateT
}
else if (type == RTCIceCandidateType.relay && RelayEndPoint != null)
{
// TODO: Currently implementation always use UDP candidates as we will only support TURN TCP Transport.
//var relayProtocol = _uri.Protocol == ProtocolType.Tcp ? RTCIceProtocol.tcp : RTCIceProtocol.udp;
var relayProtocol = RTCIceProtocol.udp;
var relayProtocol = IceRelayProtocol == ProtocolType.Tcp ? RTCIceProtocol.tcp : RTCIceProtocol.udp;

candidate.SetAddressProperties(relayProtocol, RelayEndPoint.Address, (ushort)RelayEndPoint.Port,
type, null, 0);
Expand Down
9 changes: 8 additions & 1 deletion src/net/ICE/IceServerResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,14 @@ public void InitialiseIceServers(

var server = new IceServer(stunUri, iceServerID++, cfg.username, cfg.credential);

// immediate bind if it’s already an IP
// Set relay protocol if requested (RFC 6062 TCP relay)
if (stunUri.Scheme == STUNSchemesEnum.turn && cfg.X_ICERelayProtocol == RTCIceProtocol.tcp)
{
server.IceRelayProtocol = System.Net.Sockets.ProtocolType.Tcp;
logger.LogDebug("{caller} will request TCP relay candidate from ICE server {Uri}", nameof(IceServerResolver), stunUri);
}

// immediate bind if it's already an IP
if (IPAddress.TryParse(stunUri.Host, out var ip))
{
server.ServerEndPoint = new IPEndPoint(ip, stunUri.Port);
Expand Down
Loading
Loading