private void CreateTurnPermissions() { try { var localTurnIceCandidate = (from cand in LocalIceCandidates where cand.TurnRelayIPEndPoint != null select cand).First(); var remoteTurnCandidate = (from cand in RemoteIceCandidates where cand.CandidateType == IceCandidateTypesEnum.relay select cand).First(); // Send create permission request STUNv2Message turnPermissionRequest = new STUNv2Message(STUNv2MessageTypesEnum.CreatePermission); turnPermissionRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); //turnBindRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.ChannelNumber, (ushort)3000)); turnPermissionRequest.Attributes.Add(new STUNv2XORAddressAttribute(STUNv2AttributeTypesEnum.XORPeerAddress, remoteTurnCandidate.Port, IPAddress.Parse(remoteTurnCandidate.NetworkAddress))); turnPermissionRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Username, Encoding.UTF8.GetBytes(localTurnIceCandidate.TurnServer.Username))); turnPermissionRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Nonce, Encoding.UTF8.GetBytes(localTurnIceCandidate.TurnServer.Nonce))); turnPermissionRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Realm, Encoding.UTF8.GetBytes(localTurnIceCandidate.TurnServer.Realm))); MD5 md5 = new MD5CryptoServiceProvider(); byte[] hmacKey = md5.ComputeHash(Encoding.UTF8.GetBytes(localTurnIceCandidate.TurnServer.Username + ":" + localTurnIceCandidate.TurnServer.Realm + ":" + localTurnIceCandidate.TurnServer.Password)); byte[] turnPermissionReqBytes = turnPermissionRequest.ToByteBuffer(hmacKey, false); localTurnIceCandidate.LocalRtpSocket.SendTo(turnPermissionReqBytes, localTurnIceCandidate.TurnServer.ServerEndPoint); } catch (Exception excp) { logger.LogError("Exception CreateTurnPermissions. " + excp); } }
public void ParseWebRTCSTUNResponseTestMethod() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); byte[] stunResp = new byte[] { 0x01, 0x01, 0x00, 0x2c, 0x21, 0x12, 0xa4, 0x42, 0x6a, 0x45, 0x38, 0x2b, 0x4e, 0x5a, 0x4b, 0x50, 0x64, 0x31, 0x70, 0x38, 0x00, 0x20, 0x00, 0x08, 0x00, 0x01, 0xe0, 0xda, 0xe1, 0xba, 0x85, 0x3f, 0x00, 0x08, 0x00, 0x14, 0x24, 0x37, 0x24, 0xa0, 0x05, 0x2d, 0x88, 0x97, 0xce, 0xa6, 0x4e, 0x90, 0x69, 0xf6, 0x39, 0x07, 0x7d, 0xb1, 0x6e, 0x71, 0x80, 0x28, 0x00, 0x04, 0xde, 0x6a, 0x05, 0xac }; STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(stunResp, stunResp.Length); STUNv2Header stunHeader = stunMessage.Header; Console.WriteLine("Request type = " + stunHeader.MessageType + "."); Console.WriteLine("Length = " + stunHeader.MessageLength + "."); Console.WriteLine("Transaction ID = " + BitConverter.ToString(stunHeader.TransactionId) + "."); foreach (STUNv2Attribute attribute in stunMessage.Attributes) { if (attribute.AttributeType == STUNv2AttributeTypesEnum.Username) { Console.WriteLine(" " + attribute.AttributeType + " " + Encoding.UTF8.GetString(attribute.Value) + "."); } else { Console.WriteLine(" " + attribute.AttributeType + " " + attribute.Value + "."); } } Assert.AreEqual(STUNv2MessageTypesEnum.BindingSuccessResponse, stunHeader.MessageType); Assert.AreEqual(44, stunHeader.MessageLength); Assert.AreEqual(3, stunMessage.Attributes.Count); }
private void AllocateTurn(IceCandidate iceCandidate) { try { if (iceCandidate.TurnAllocateAttempts >= MAXIMUM_TURN_ALLOCATE_ATTEMPTS) { logger.LogDebug("TURN allocation for local socket " + iceCandidate.LocalAddress + " failed after " + iceCandidate.TurnAllocateAttempts + " attempts."); iceCandidate.IsGatheringComplete = true; } else { iceCandidate.TurnAllocateAttempts++; //logger.LogDebug("Sending STUN connectivity check to client " + client.SocketAddress + "."); STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.Allocate); stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Lifetime, 3600)); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.RequestedTransport, STUNv2AttributeConstants.UdpTransportType)); // UDP byte[] stunReqBytes = stunRequest.ToByteBuffer(null, false); iceCandidate.LocalRtpSocket.SendTo(stunReqBytes, iceCandidate.TurnServer.ServerEndPoint); } } catch (Exception excp) { logger.LogError("Exception AllocateTurn. " + excp); } }
private void SendInitialStunBindingRequest(IceCandidate iceCandidate, ManualResetEvent iceGatheringCompleteMRE) { int attempt = 1; while (attempt < INITIAL_STUN_BINDING_ATTEMPTS_LIMIT && !IsClosed && !iceCandidate.IsGatheringComplete) { logger.LogDebug("Sending STUN binding request " + attempt + " from " + iceCandidate.LocalRtpSocket.LocalEndPoint + " to " + iceCandidate.TurnServer.ServerEndPoint + "."); STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest); stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); byte[] stunReqBytes = stunRequest.ToByteBuffer(null, false); iceCandidate.LocalRtpSocket.SendTo(stunReqBytes, iceCandidate.TurnServer.ServerEndPoint); Thread.Sleep(INITIAL_STUN_BINDING_PERIOD_MILLISECONDS); attempt++; } iceCandidate.IsGatheringComplete = true; // Potentially save a few seconds if all the ICE candidates are now ready. if (LocalIceCandidates.All(x => x.IsGatheringComplete)) { iceGatheringCompleteMRE.Set(); } }
public void ParseWebRTCSTUNRequestTestMethod() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); byte[] stunReq = new byte[] { 0x00, 0x01, 0x00, 0x60, 0x21, 0x12, 0xa4, 0x42, 0x66, 0x55, 0x55, 0x43, 0x4b, 0x48, 0x74, 0x73, 0x68, 0x4e, 0x71, 0x56, // Att1: 0x00, 0x06, 0x00, 0x21, 0x6d, 0x30, 0x71, 0x47, 0x77, 0x53, 0x71, 0x2f, 0x48, 0x56, 0x48, 0x71, 0x41, 0x62, 0x4b, 0x62, 0x3a, 0x73, 0x64, 0x43, 0x48, 0x59, 0x6b, 0x35, 0x6e, 0x46, 0x34, 0x79, 0x44, 0x77, 0x55, 0x39, 0x53, 0x00, 0x00, 0x00, // Att2 0x80, 0x2a, 0x00, 0x08, 0xa0, 0x36, 0xc9, 0x6c, 0x30, 0xc6, 0x2f, 0xd2, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x00, 0x04, 0x6e, 0x7f, 0x1e, 0xff, 0x00, 0x08, 0x00, 0x14, 0x81, 0x4a, 0x4f, 0xaf, 0x3d, 0x99, 0x30, 0x67, 0x66, 0xb9, 0x48, 0x67, 0x83, 0x72, 0xd5, 0xa0, 0x7a, 0x87, 0xb5, 0x3f, 0x80, 0x28, 0x00, 0x04, 0x49, 0x7e, 0x51, 0x17 }; STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(stunReq, stunReq.Length); STUNv2Header stunHeader = stunMessage.Header; Console.WriteLine("Request type = " + stunHeader.MessageType + "."); Console.WriteLine("Length = " + stunHeader.MessageLength + "."); Console.WriteLine("Transaction ID = " + BitConverter.ToString(stunHeader.TransactionId) + "."); Assert.AreEqual(STUNv2MessageTypesEnum.BindingRequest, stunHeader.MessageType); Assert.AreEqual(96, stunHeader.MessageLength); Assert.AreEqual(6, stunMessage.Attributes.Count); }
public void TestMessageIntegrityAttributeForBindingRequest() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); byte[] stunReq = new byte[] { 0x00, 0x01, 0x00, 0x60, 0x21, 0x12, 0xa4, 0x42, 0x69, 0x64, 0x38, 0x2b, 0x4c, 0x45, 0x44, 0x57, 0x4d, 0x31, 0x64, 0x30, 0x00, 0x06, 0x00, 0x21, 0x75, 0x4f, 0x35, 0x73, 0x69, 0x31, 0x75, 0x61, 0x37, 0x63, 0x59, 0x34, 0x74, 0x38, 0x4d, 0x4d, 0x3a, 0x4c, 0x77, 0x38, 0x2f, 0x30, 0x43, 0x31, 0x43, 0x72, 0x76, 0x68, 0x5a, 0x43, 0x31, 0x67, 0x62, 0x00, 0x00, 0x00, 0x80, 0x2a, 0x00, 0x08, 0xc0, 0x3d, 0xf5, 0x13, 0x40, 0xf4, 0x22, 0x46, 0x00, 0x25, 0x00, 0x00, 0x00, 0x24, 0x00, 0x04, 0x6e, 0x7f, 0x1e, 0xff, 0x00, 0x08, 0x00, 0x14, 0x55, 0x82, 0x69, 0xde, 0x17, 0x55, 0xcc, 0x66, 0x29, 0x23, 0xe6, 0x7d, 0xec, 0x87, 0x6c, 0x07, 0x3a, 0xd6, 0x78, 0x15, 0x80, 0x28, 0x00, 0x04, 0x1c, 0xae, 0x89, 0x2e }; STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(stunReq, stunReq.Length); STUNv2Header stunHeader = stunMessage.Header; Console.WriteLine("Request type = " + stunHeader.MessageType + "."); Console.WriteLine("Length = " + stunHeader.MessageLength + "."); Console.WriteLine("Transaction ID = " + BitConverter.ToString(stunHeader.TransactionId) + "."); Assert.AreEqual(STUNv2MessageTypesEnum.BindingRequest, stunHeader.MessageType); Assert.AreEqual(96, stunHeader.MessageLength); Assert.AreEqual(6, stunMessage.Attributes.Count); Assert.AreEqual("69-64-38-2B-4C-45-44-57-4D-31-64-30", BitConverter.ToString(stunMessage.Header.TransactionId)); stunMessage.Attributes.Remove(stunMessage.Attributes.Where(x => x.AttributeType == STUNv2AttributeTypesEnum.MessageIntegrity).Single()); stunMessage.Attributes.Remove(stunMessage.Attributes.Where(x => x.AttributeType == STUNv2AttributeTypesEnum.FingerPrint).Single()); byte[] buffer = stunMessage.ToByteBufferStringKey("r89XhWC9k2kW4Pns75vmwHIa", true); Assert.AreEqual(BitConverter.ToString(stunReq), BitConverter.ToString(buffer)); }
public void PutResponseToBufferTestMethod() { STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse); stunResponse.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); //stunResponse.AddFingerPrintAttribute(); stunResponse.AddXORMappedAddressAttribute(IPAddress.Parse("127.0.0.1"), 1234); byte[] buffer = stunResponse.ToByteBuffer(null, true); }
public void PutResponseToBufferTestMethod() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse); stunResponse.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); //stunResponse.AddFingerPrintAttribute(); stunResponse.AddXORMappedAddressAttribute(IPAddress.Parse("127.0.0.1"), 1234); byte[] buffer = stunResponse.ToByteBuffer(null, true); }
public void BindingRequestWithUsernameToBytesUnitTest() { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); STUNv2Message initMessage = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest); initMessage.AddUsernameAttribute("someusernamex"); byte[] stunMessageBytes = initMessage.ToByteBuffer(null, false); Console.WriteLine(BitConverter.ToString(stunMessageBytes)); Assert.IsTrue(stunMessageBytes.Length % 4 == 0); }
public void ParseCoturnSTUNResponseTestMethod() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); byte[] stunResp = new byte[] { 0x01, 0x01, 0x00, 0x44, 0x21, 0x12, 0xa4, 0x42, 0x6b, 0x4c, 0xf3, 0x18, 0xd0, 0xa7, 0xf5, 0x40, 0x97, 0x30, 0x3a, 0x27, 0x00, 0x20, 0x00, 0x08, 0x00, 0x01, 0x9e, 0x90, 0x1a, 0xb5, 0x08, 0xf3, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0xbf, 0x82, 0x3b, 0xa7, 0xac, 0xb1, 0x80, 0x2b, 0x00, 0x08, 0x00, 0x01, 0x0d, 0x96, 0x67, 0x1d, 0x42, 0xf3, 0x80, 0x22, 0x00, 0x1a, 0x43, 0x6f, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x34, 0x2e, 0x35, 0x2e, 0x30, 0x2e, 0x33, 0x20, 0x27, 0x64, 0x61, 0x6e, 0x20, 0x45, 0x69, 0x64, 0x65, 0x72, 0x27, 0x77, 0x75 }; STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(stunResp, stunResp.Length); STUNv2Header stunHeader = stunMessage.Header; logger.LogDebug("Request type = " + stunHeader.MessageType + "."); logger.LogDebug("Length = " + stunHeader.MessageLength + "."); logger.LogDebug("Transaction ID = " + BitConverter.ToString(stunHeader.TransactionId) + "."); foreach (STUNv2Attribute attribute in stunMessage.Attributes) { if (attribute.AttributeType == STUNv2AttributeTypesEnum.MappedAddress) { STUNv2AddressAttribute addressAttribute = new STUNv2AddressAttribute(attribute.Value); logger.LogDebug(" " + attribute.AttributeType + " " + addressAttribute.Address + ":" + addressAttribute.Port + "."); Assert.Equal("59.167.172.177", addressAttribute.Address.ToString()); Assert.Equal(49026, addressAttribute.Port); } else if (attribute.AttributeType == STUNv2AttributeTypesEnum.XORMappedAddress) { STUNv2XORAddressAttribute xorAddressAttribute = new STUNv2XORAddressAttribute(STUNv2AttributeTypesEnum.XORMappedAddress, attribute.Value); logger.LogDebug(" " + attribute.AttributeType + " " + xorAddressAttribute.Address + ":" + xorAddressAttribute.Port + "."); Assert.Equal("59.167.172.177", xorAddressAttribute.Address.ToString()); Assert.Equal(49026, xorAddressAttribute.Port); } else { logger.LogDebug(" " + attribute.AttributeType + " " + attribute.Value + "."); } } Assert.Equal(STUNv2MessageTypesEnum.BindingSuccessResponse, stunHeader.MessageType); }
public void GenerateHmacAndFingerprintTestMethod() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); string icePassword = "******"; STUNv2Message msg = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse); msg.Header.TransactionId = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; msg.AddXORMappedAddressAttribute(IPAddress.Loopback, 55477); var buffer = msg.ToByteBufferStringKey(icePassword, true); string hmac = "HMAC: "; for (int i = 36; i < 56; i++) { hmac += $"{buffer[i]:X2} "; } logger.LogDebug(hmac); logger.LogDebug($"Fingerprint: {buffer[buffer.Length - 4]:X2} {buffer[buffer.Length - 3]:X2} {buffer[buffer.Length - 2]:X2} {buffer[buffer.Length - 1]:X2}."); }
private void ProcessStunMessage(IceCandidate iceCandidate, STUNv2Message stunMessage, IPEndPoint remoteEndPoint) { //logger.LogDebug("STUN message received from remote " + remoteEndPoint + " " + stunMessage.Header.MessageType + "."); if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingRequest) { STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse); stunResponse.Header.TransactionId = stunMessage.Header.TransactionId; stunResponse.AddXORMappedAddressAttribute(remoteEndPoint.Address, remoteEndPoint.Port); // ToDo: Check authentication. string localIcePassword = LocalIcePassword; byte[] stunRespBytes = stunResponse.ToByteBufferStringKey(localIcePassword, true); iceCandidate.LocalRtpSocket.SendTo(stunRespBytes, remoteEndPoint); iceCandidate.LastStunRequestReceivedAt = DateTime.Now; iceCandidate.IsStunRemoteExchangeComplete = true; if (_isEncryptionDisabled == true) { iceCandidate.RemoteRtpEndPoint = remoteEndPoint; // Don't need to wait for DTLS negotiation. OnIceConnected?.Invoke(iceCandidate, remoteEndPoint); } if (_remoteIceCandidates != null && !_remoteIceCandidates.Any(x => (x.NetworkAddress == remoteEndPoint.Address.ToString() || x.RemoteAddress == remoteEndPoint.Address.ToString()) && (x.Port == remoteEndPoint.Port || x.RemotePort == remoteEndPoint.Port))) { // This STUN request has come from a socket not in the remote ICE candidates list. Add it so we can send our STUN binding request to it. IceCandidate remoteIceCandidate = new IceCandidate() { Transport = "udp", NetworkAddress = remoteEndPoint.Address.ToString(), Port = remoteEndPoint.Port, CandidateType = IceCandidateTypesEnum.host, MediaType = iceCandidate.MediaType }; logger.LogDebug("Adding missing remote ICE candidate for " + remoteEndPoint + " and media type " + iceCandidate.MediaType + "."); _remoteIceCandidates.Add(remoteIceCandidate); } } else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingSuccessResponse) { if (_turnServerEndPoint != null && remoteEndPoint.ToString() == _turnServerEndPoint.ToString()) { if (iceCandidate.IsGatheringComplete == false) { var reflexAddressAttribute = stunMessage.Attributes.FirstOrDefault(y => y.AttributeType == STUNv2AttributeTypesEnum.XORMappedAddress) as STUNv2XORAddressAttribute; if (reflexAddressAttribute != null) { iceCandidate.StunRflxIPEndPoint = new IPEndPoint(reflexAddressAttribute.Address, reflexAddressAttribute.Port); iceCandidate.IsGatheringComplete = true; logger.LogDebug("ICE gathering complete for local socket " + iceCandidate.LocalRtpSocket.LocalEndPoint + ", rflx address " + iceCandidate.StunRflxIPEndPoint + "."); } else { iceCandidate.IsGatheringComplete = true; logger.LogDebug("The STUN binding response received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from " + remoteEndPoint + " did not have an XORMappedAddress attribute, rlfx address can not be determined."); } } } else { iceCandidate.LastStunResponseReceivedAt = DateTime.Now; if (iceCandidate.IsStunLocalExchangeComplete == false) { iceCandidate.IsStunLocalExchangeComplete = true; logger.LogDebug("WebRTC client STUN exchange complete for call " + CallID + ", candidate local socket " + iceCandidate.LocalRtpSocket.LocalEndPoint + ", remote socket " + remoteEndPoint + "."); SetIceConnectionState(IceConnectionStatesEnum.Connected); } } } else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingErrorResponse) { logger.LogWarning("A STUN binding error response was received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from " + remoteEndPoint + "."); } else { logger.LogWarning("An unrecognised STUN request was received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from " + remoteEndPoint + "."); } }
private void StartWebRtcRtpListener(IceCandidate iceCandidate) { string localEndPoint = "?"; try { localEndPoint = iceCandidate.LocalRtpSocket.LocalEndPoint.ToString(); logger.LogDebug("Starting WebRTC RTP listener for call " + CallID + " on socket " + localEndPoint + "."); IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); UdpClient localSocket = new UdpClient(); localSocket.Client = iceCandidate.LocalRtpSocket; while (!IsClosed) { try { //logger.LogDebug("ListenToReceiverWebRTCClient Receive."); byte[] buffer = localSocket.Receive(ref remoteEndPoint); iceCandidate.LastCommunicationAt = DateTime.Now; //logger.LogDebug(buffer.Length + " bytes read on Receiver Client media socket from " + remoteEndPoint.ToString() + "."); //if (buffer.Length > 3 && buffer[0] == 0x16 && buffer[1] == 0xfe) if (buffer[0] >= 20 && buffer[0] <= 64) { //OnMediaPacket(iceCandidate, buffer, remoteEndPoint); OnDtlsPacket?.Invoke(iceCandidate, buffer, remoteEndPoint); } //else if ((buffer[0] & 0x80) == 0) else if (buffer[0] == 0 || buffer[0] == 1) { STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(buffer, buffer.Length); ProcessStunMessage(iceCandidate, stunMessage, remoteEndPoint); } else { OnMediaPacket?.Invoke(iceCandidate, buffer, remoteEndPoint); } } catch (Exception sockExcp) { _communicationFailureCount++; logger.LogWarning("Exception ListenToReceiverWebRTCClient Receive (" + localEndPoint + " and " + remoteEndPoint + ", failure count " + _communicationFailureCount + "). " + sockExcp.Message); // Need to be careful about deciding when the connection has failed. Sometimes the STUN requests we send will arrive before the remote peer is ready and cause a socket exception. // Only shutdown the peer if we are sure all ICE intialisation is complete and the socket exception occurred after the RTP had stated flowing. if (iceCandidate.IsStunLocalExchangeComplete && iceCandidate.IsStunRemoteExchangeComplete && iceCandidate.RemoteRtpEndPoint != null && remoteEndPoint != null && iceCandidate.RemoteRtpEndPoint.ToString() == remoteEndPoint.ToString() && DateTime.Now.Subtract(IceNegotiationStartedAt).TotalSeconds > 10) { logger.LogWarning("WebRtc peer communication failure on call " + CallID + " for local RTP socket " + localEndPoint + " and remote RTP socket " + remoteEndPoint + " ."); iceCandidate.DisconnectionMessage = sockExcp.Message; break; } else if (_communicationFailureCount > COMMUNICATION_FAILURE_COUNT_FOR_CLOSE) { logger.LogWarning("WebRtc peer communication failures on call " + CallID + " exceeded limit of " + COMMUNICATION_FAILURE_COUNT_FOR_CLOSE + " closing peer."); break; } //else if (DateTime.Now.Subtract(peer.IceNegotiationStartedAt).TotalSeconds > ICE_CONNECTION_LIMIT_SECONDS) //{ // logger.LogWarning("WebRTC peer ICE connection establishment timed out on call " + peer.CallID + " for " + iceCandidate.LocalRtpSocket.LocalEndPoint + "."); // break; //} } } Close(); } catch (Exception excp) { logger.LogError("Exception ListenForWebRTCClient (" + localEndPoint + "). " + excp); } }
private void SendStunConnectivityChecks() { try { while (!IsClosed) { try { // If one of the ICE candidates has the remote RTP socket set then the negotiation is complete and the STUN checks are to keep the connection alive. if (LocalIceCandidates.Any(x => x.IsConnected == true)) { var iceCandidate = LocalIceCandidates.First(x => x.IsConnected == true); // Remote RTP endpoint gets set when the DTLS negotiation is finished. if (iceCandidate.RemoteRtpEndPoint != null) { //logger.LogDebug("Sending STUN connectivity check to client " + iceCandidate.RemoteRtpEndPoint + "."); string localUser = LocalIceUser; STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest); stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); stunRequest.AddUsernameAttribute(RemoteIceUser + ":" + localUser); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Priority, new byte[] { 0x6e, 0x7f, 0x1e, 0xff })); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.UseCandidate, null)); // Must send this to get DTLS started. byte[] stunReqBytes = stunRequest.ToByteBufferStringKey(RemoteIcePassword, true); iceCandidate.LocalRtpSocket.SendTo(stunReqBytes, iceCandidate.RemoteRtpEndPoint); iceCandidate.LastSTUNSendAt = DateTime.Now; } var secondsSinceLastResponse = DateTime.Now.Subtract(iceCandidate.LastCommunicationAt).TotalSeconds; if (secondsSinceLastResponse > ICE_TIMEOUT_SECONDS) { logger.LogWarning("No STUN response was received on a connected ICE connection for " + secondsSinceLastResponse + "s, closing connection."); iceCandidate.IsDisconnected = true; if (LocalIceCandidates.Any(x => x.IsConnected == true) == false) { // If there are no connected local candidates left close the peer. Close(); break; } } } else { if (_remoteIceCandidates.Count() > 0) { foreach (var localIceCandidate in LocalIceCandidates.Where(x => x.IsStunLocalExchangeComplete == false && x.StunConnectionRequestAttempts < MAXIMUM_STUN_CONNECTION_ATTEMPTS)) { localIceCandidate.StunConnectionRequestAttempts++; // ToDo: Include srflx and relay addresses. // Only supporting UDP candidates at this stage. foreach (var remoteIceCandidate in RemoteIceCandidates.Where(x => x.Transport.ToLower() == "udp" && x.NetworkAddress.NotNullOrBlank() && x.HasConnectionError == false)) { try { IPAddress remoteAddress = IPAddress.Parse(remoteIceCandidate.NetworkAddress); logger.LogDebug("Sending authenticated STUN binding request " + localIceCandidate.StunConnectionRequestAttempts + " from " + localIceCandidate.LocalRtpSocket.LocalEndPoint + " to WebRTC peer at " + remoteIceCandidate.NetworkAddress + ":" + remoteIceCandidate.Port + "."); string localUser = LocalIceUser; STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest); stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray(); stunRequest.AddUsernameAttribute(RemoteIceUser + ":" + localUser); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Priority, new byte[] { 0x6e, 0x7f, 0x1e, 0xff })); stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.UseCandidate, null)); // Must send this to get DTLS started. byte[] stunReqBytes = stunRequest.ToByteBufferStringKey(RemoteIcePassword, true); localIceCandidate.LocalRtpSocket.SendTo(stunReqBytes, new IPEndPoint(IPAddress.Parse(remoteIceCandidate.NetworkAddress), remoteIceCandidate.Port)); localIceCandidate.LastSTUNSendAt = DateTime.Now; } catch (System.Net.Sockets.SocketException sockExcp) { logger.LogWarning($"SocketException sending STUN request to {remoteIceCandidate.NetworkAddress}:{remoteIceCandidate.Port}, removing candidate. {sockExcp.Message}"); remoteIceCandidate.HasConnectionError = true; } } } } } } catch (Exception excp) { logger.LogError("Exception SendStunConnectivityCheck ConnectivityCheck. " + excp); } if (!IsClosed) { Thread.Sleep(ESTABLISHED_STUN_BINDING_PERIOD_MILLISECONDS); } } } catch (Exception excp) { logger.LogError("Exception SendStunConnectivityCheck. " + excp); } }