/// <summary> /// Event handler for a packet receive on the UDP encapsulation socket. /// </summary> /// <param name="receiver">The UDP receiver that received the packet.</param> /// <param name="localPort">The local port the packet was received on.</param> /// <param name="remoteEndPoint">The remote end point the packet was received from.</param> /// <param name="packet">A buffer containing the packet.</param> private void OnEncapsulationSocketPacketReceived(UdpReceiver receiver, int localPort, IPEndPoint remoteEndPoint, byte[] packet) { try { if (!SctpPacket.VerifyChecksum(packet, 0, packet.Length)) { logger.LogWarning($"SCTP packet from UDP {remoteEndPoint} dropped due to invalid checksum."); } else { var sctpPacket = SctpPacket.Parse(packet, 0, packet.Length); // Process packet. if (sctpPacket.Header.VerificationTag == 0) { GotInit(sctpPacket, remoteEndPoint); } else if (sctpPacket.Chunks.Any(x => x.KnownType == SctpChunkType.COOKIE_ECHO)) { // The COOKIE ECHO chunk is the 3rd step in the SCTP handshake when the remote party has // requested a new association be created. var cookie = base.GetCookie(sctpPacket); if (cookie.IsEmpty()) { logger.LogWarning($"SCTP error acquiring handshake cookie from COOKIE ECHO chunk."); } else { logger.LogDebug($"SCTP creating new association for {remoteEndPoint}."); var association = new SctpAssociation(this, cookie, localPort); if (_associations.TryAdd(association.ID, association)) { if (sctpPacket.Chunks.Count > 1) { // There could be DATA chunks after the COOKIE ECHO chunk. association.OnPacketReceived(sctpPacket); } } else { logger.LogError($"SCTP failed to add new association to dictionary."); } } } else { // TODO: Lookup the existing association for the packet. _associations.Values.First().OnPacketReceived(sctpPacket); } } } catch (Exception excp) { logger.LogError($"Exception SctpTransport.OnEncapsulationSocketPacketReceived. {excp}"); } }
/// <summary> /// This method runs on a dedicated thread to listen for incoming SCTP /// packets on the DTLS transport. /// </summary> private void DoReceive(object state) { byte[] recvBuffer = new byte[SctpAssociation.DEFAULT_ADVERTISED_RECEIVE_WINDOW]; while (!_isClosed) { try { int bytesRead = transport.Receive(recvBuffer, 0, recvBuffer.Length, RECEIVE_TIMEOUT_MILLISECONDS); if (bytesRead == DtlsSrtpTransport.DTLS_RETRANSMISSION_CODE) { // Timed out waiting for a packet, this is by design and the receive attempt should // be retired. continue; } else if (bytesRead > 0) { if (!SctpPacket.VerifyChecksum(recvBuffer, 0, bytesRead)) { logger.LogWarning($"SCTP packet received on DTLS transport dropped due to invalid checksum."); } else { var pkt = SctpPacket.Parse(recvBuffer, 0, bytesRead); if (pkt.Chunks.Any(x => x.KnownType == SctpChunkType.INIT)) { var initChunk = pkt.Chunks.First(x => x.KnownType == SctpChunkType.INIT) as SctpInitChunk; logger.LogDebug($"SCTP INIT packet received, initial tag {initChunk.InitiateTag}, initial TSN {initChunk.InitialTSN}."); GotInit(pkt, null); } else if (pkt.Chunks.Any(x => x.KnownType == SctpChunkType.COOKIE_ECHO)) { // The COOKIE ECHO chunk is the 3rd step in the SCTP handshake when the remote party has // requested a new association be created. var cookie = base.GetCookie(pkt); if (cookie.IsEmpty()) { logger.LogWarning($"SCTP error acquiring handshake cookie from COOKIE ECHO chunk."); } else { RTCSctpAssociation.GotCookie(cookie); if (pkt.Chunks.Count() > 1) { // There could be DATA chunks after the COOKIE ECHO chunk. RTCSctpAssociation.OnPacketReceived(pkt); } } } else { RTCSctpAssociation.OnPacketReceived(pkt); } } } else if (_isClosed) { // The DTLS transport has been closed or is no longer available. logger.LogWarning($"SCTP the RTCSctpTransport DTLS transport returned an error."); break; } } catch (ApplicationException appExcp) { // Treat application exceptions as recoverable, things like SCTP packet parse failures. logger.LogWarning($"SCTP error processing RTCSctpTransport receive. {appExcp.Message}"); } catch (TlsFatalAlert alert) when(alert.InnerException is SocketException) { var sockExcp = alert.InnerException as SocketException; logger.LogWarning($"SCTP RTCSctpTransport receive socket failure {sockExcp.SocketErrorCode}."); break; } catch (Exception excp) { logger.LogError($"SCTP fatal error processing RTCSctpTransport receive. {excp}"); break; } } if (!_isClosed) { logger.LogWarning($"SCTP association {RTCSctpAssociation.ID} receive thread stopped."); } SetState(RTCSctpTransportState.Closed); }