Skip to content
Draft
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
62 changes: 60 additions & 2 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 @@ -171,6 +172,8 @@ public ulong Priority
/// </summary>
public DateTime LastCheckSentAt = DateTime.MinValue;

internal DateTime TcpLastCheckSentAt = DateTime.MinValue;

/// <summary>
/// The number of checks that have been sent without a response.
/// </summary>
Expand Down Expand Up @@ -212,11 +215,21 @@ public string RequestTransactionID
/// </summary>
public int TurnPermissionsRequestSent { get; set; } = 0;

internal int TcpBindRequestSent { get; set; } = 0;

/// <summary>
/// This field records the time a Create Permissions response was received.
/// </summary>
public DateTime TurnPermissionsResponseAt { get; set; } = DateTime.MinValue;

public DateTime TurnConnectReportAt { get; internal set; } = DateTime.MinValue;

public DateTime TurnConnectBindedAt { get; internal set; } = DateTime.MinValue;

public int TurnConnectRequestSent { get; internal set; }

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 All @@ -229,7 +242,7 @@ public string RequestTransactionID
/// Timestamp for the most recent binding request received from the remote peer.
/// </summary>
public DateTime LastBindingRequestReceivedAt { get; set; }

/// <summary>
/// Creates a new entry for the ICE session checklist.
/// </summary>
Expand Down Expand Up @@ -286,7 +299,10 @@ internal void GotStunResponse(STUNMessage stunResponse, IPEndPoint remoteEndPoin
stunResponse.Attributes.First(x => x.AttributeType == STUNAttributeTypesEnum.ErrorCode) as
STUNErrorCodeAttribute;
if (errCodeAttribute.ErrorCode == IceServer.STUN_UNAUTHORISED_ERROR_CODE ||
errCodeAttribute.ErrorCode == IceServer.STUN_STALE_NONCE_ERROR_CODE)
errCodeAttribute.ErrorCode == IceServer.STUN_STALE_NONCE_ERROR_CODE
// TODO: shouldn't be here, right...
// || errCodeAttribute.ErrorCode == IceServer.STUN_CONNECTION_ALREADY_EXISTS
)
{
if (LocalCandidate.IceServer == null)
{
Expand All @@ -299,6 +315,11 @@ internal void GotStunResponse(STUNMessage stunResponse, IPEndPoint remoteEndPoin
retry = true;
}
}
else if (errCodeAttribute.ErrorCode == IceServer.STUN_CONNECTION_TIMEOUT_OR_FAILURE)
{
TurnConnectReportAt = DateTime.Now;
retry = true;
}
}

}
Expand Down Expand Up @@ -359,6 +380,43 @@ internal void GotStunResponse(STUNMessage stunResponse, IPEndPoint remoteEndPoin
TurnPermissionsResponseAt = DateTime.Now;
State = retry ? State : ChecklistEntryState.Failed;
}
else if (stunResponse.Header.MessageType == STUNMessageTypesEnum.ConnectSuccess)
{
logger.LogDebug("A TURN Connect sucess 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 request, need ConnectBind
if (State == ChecklistEntryState.InProgress)
{
State = ChecklistEntryState.Waiting;
//Clear CheckSentAt Time to force send it again
FirstCheckSentAt = DateTime.MinValue;
}
}
else if (stunResponse.Header.MessageType == STUNMessageTypesEnum.ConnectionBindSuccess)
{
logger.LogDebug("A TURN ConnectionBind sucess 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 TCP ConnectBind, we need underlying STUN bind.
if (State == ChecklistEntryState.InProgress)
{
State = ChecklistEntryState.Waiting;
//Clear CheckSentAt Time to force send it again
FirstCheckSentAt = DateTime.MinValue;
}
}
else
{
logger.LogWarning("ICE RTP channel received an unexpected STUN response {MessageType} from {RemoteEndPoint}.", stunResponse.Header.MessageType, remoteEndPoint);
Expand Down
37 changes: 31 additions & 6 deletions src/net/ICE/IceServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ public class IceServer
/// </summary>
internal const int STUN_STALE_NONCE_ERROR_CODE = 438;

internal const int STUN_CONNECTION_ALREADY_EXISTS = 446;

internal const int STUN_CONNECTION_TIMEOUT_OR_FAILURE = 447;

// 10 seconds is from https://datatracker.ietf.org/doc/html/rfc6062#section-4.3
internal static TimeSpan waittime => TimeSpan.FromSeconds(10);
internal static TimeSpan rttime => TimeSpan.FromSeconds(10 / (MAX_ERRORS));
//internal static readonly TimeSpan rttime = TimeSpan.FromSeconds(1);

internal STUNUri _uri;
internal string _username;
internal string _password;
Expand Down Expand Up @@ -177,7 +186,26 @@ public class IceServer
/// </summary>
internal int ErrorResponseCount = 0;

/// <summary>
/// Transport protocol for connecting with the server.
/// </summary>
public ProtocolType Protocol { get { return _uri.Protocol; } }
// DevNote
// <remarks>
/// Not to be confused of the protocol for allocated ICE relay candidate with TURN (<see cref="_reqIceProtocol"/>).
// </remarks>

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

internal STUNUri _secondaryRelayUri;

/// <summary>
/// Default constructor.
Expand Down Expand Up @@ -211,9 +239,8 @@ 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 @@ -222,9 +249,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 = _reqIceProtocol == ProtocolType.Tcp ? RTCIceProtocol.tcp : RTCIceProtocol.udp;

candidate.SetAddressProperties(relayProtocol, RelayEndPoint.Address, (ushort)RelayEndPoint.Port,
type, null, 0);
Expand Down
3 changes: 3 additions & 0 deletions src/net/ICE/RTCIceCandidate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class RTCIceCandidate : IRTCIceCandidate
public const string REMOTE_PORT_KEY = "rport";
public const string CANDIDATE_PREFIX = "candidate";

public const ushort TCP_DISCARD_PORT = 9;

/// <summary>
/// The ICE server (STUN or TURN) the candidate was generated from.
/// Will be null for non-ICE server candidates.
Expand Down Expand Up @@ -127,6 +129,7 @@ public RTCIceCandidate(RTCIceCandidateInit init)
component = iceCandidate.component;
address = iceCandidate.address;
port = iceCandidate.port;
protocol = iceCandidate.protocol;
type = iceCandidate.type;
tcpType = iceCandidate.tcpType;
relatedAddress = iceCandidate.relatedAddress;
Expand Down
Loading