/// <summary> /// Gets an ICE candidate for this ICE server once the required server responses have been received. /// Note the related address and port are deliberately not set to avoid leaking information about /// internal network configuration. /// </summary> /// <param name="init">The initialisation parameters for the ICE candidate (mainly local username).</param> /// <param name="type">The type of ICE candidate to get, must be srflx or relay.</param> /// <returns>An ICE candidate that can be sent to the remote peer.</returns> internal RTCIceCandidate GetCandidate(RTCIceCandidateInit init, RTCIceCandidateType type) { RTCIceCandidate candidate = new RTCIceCandidate(init); if (type == RTCIceCandidateType.srflx && ServerReflexiveEndPoint != null) { candidate.SetAddressProperties(RTCIceProtocol.udp, ServerReflexiveEndPoint.Address, (ushort)ServerReflexiveEndPoint.Port, type, null, 0); candidate.IceServer = this; return(candidate); } else if (type == RTCIceCandidateType.relay && RelayEndPoint != null) { candidate.SetAddressProperties(RTCIceProtocol.udp, RelayEndPoint.Address, (ushort)RelayEndPoint.Port, type, null, 0); candidate.IceServer = this; return(candidate); } else { logger.LogWarning($"Could not get ICE server candidate for {_uri} and type {type}."); return(null); } }
private async Task <string> OnMessage(string jsonStr, RTCPeerConnection pc) { if (RTCIceCandidateInit.TryParse(jsonStr, out var iceCandidateInit)) { logger.LogDebug("Got remote ICE candidate."); pc.addIceCandidate(iceCandidateInit); } else if (RTCSessionDescriptionInit.TryParse(jsonStr, out var descriptionInit)) { logger.LogDebug($"Got remote SDP, type {descriptionInit.type}."); var result = pc.setRemoteDescription(descriptionInit); if (result != SetDescriptionResultEnum.OK) { logger.LogWarning($"Failed to set remote description, {result}."); pc.Close("failed to set remote description"); } if (descriptionInit.type == RTCSdpType.offer) { var answerSdp = pc.createAnswer(null); await pc.setLocalDescription(answerSdp); return(answerSdp.toJSON()); } } else { logger.LogWarning($"node-dss could not parse JSON message. {jsonStr}"); } return(null); }
protected override void OnMessage(MessageEventArgs e) { logger.LogDebug($"OnMessage: {e.Data}"); if (RTCIceCandidateInit.TryParse(e.Data, out var iceCandidateInit)) { logger.LogDebug("Got remote ICE candidate."); _pc.addIceCandidate(iceCandidateInit); } else if (RTCSessionDescriptionInit.TryParse(e.Data, out var descriptionInit)) { logger.LogDebug($"Got remote SDP, type {descriptionInit.type}."); var result = _pc.setRemoteDescription(descriptionInit); if (result != SetDescriptionResultEnum.OK) { logger.LogWarning($"Failed to set remote description, {result}."); _pc.Close("failed to set remote description"); this.Close(); } } else { logger.LogWarning($"websocket-server could not parse JSON message. {e.Data}"); } }
/// <summary> /// Acquires an ICE candidate for each IP address that this host has except for: /// - Loopback addresses must not be included. /// - Deprecated IPv4-compatible IPv6 addresses and IPv6 site-local unicast addresses /// must not be included, /// - IPv4-mapped IPv6 address should not be included. /// - If a non-location tracking IPv6 address is available use it and do not included /// location tracking enabled IPv6 addresses (i.e. prefer temporary IPv6 addresses over /// permanent addresses), see RFC6724. /// </summary> /// <remarks>See https://tools.ietf.org/html/rfc8445#section-5.1.1.1</remarks> /// <returns>A list of "host" ICE candidates for the local machine.</returns> private List <RTCIceCandidate> GetHostCandidates() { List <RTCIceCandidate> hostCandidates = new List <RTCIceCandidate>(); RTCIceCandidateInit init = new RTCIceCandidateInit { usernameFragment = LocalIceUser }; var rtpBindAddress = _rtpChannel.RTPLocalEndPoint.Address; // We get a list of local addresses that can be used with the address the RTP socket is bound on. List <IPAddress> localAddresses = null; if (IPAddress.IPv6Any.Equals(rtpBindAddress)) { if (_rtpChannel.RtpSocket.DualMode) { // IPv6 dual mode listening on [::] means we can use all valid local addresses. localAddresses = NetServices.LocalIPAddresses.Where(x => !IPAddress.IsLoopback(x) && !x.IsIPv4MappedToIPv6 && !x.IsIPv6SiteLocal).ToList(); } else { // IPv6 but not dual mode on [::] means can use all valid local IPv6 addresses. localAddresses = NetServices.LocalIPAddresses.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6 && !IPAddress.IsLoopback(x) && !x.IsIPv4MappedToIPv6 && !x.IsIPv6SiteLocal).ToList(); } } else if (IPAddress.Any.Equals(rtpBindAddress)) { // IPv4 on 0.0.0.0 means can use all valid local IPv4 addresses. localAddresses = NetServices.LocalIPAddresses.Where(x => x.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(x)).ToList(); } else { // If not bound on a [::] or 0.0.0.0 means we're only listening on a specific IP address // and that's the only one that can be used for the host candidate. localAddresses = new List <IPAddress> { rtpBindAddress }; } foreach (var localAddress in localAddresses) { var hostCandidate = new RTCIceCandidate(init); hostCandidate.SetAddressProperties(RTCIceProtocol.udp, localAddress, (ushort)_rtpChannel.RTPPort, RTCIceCandidateType.host, null, 0); // We currently only support a single multiplexed connection for all data streams and RTCP. if (hostCandidate.component == RTCIceComponent.rtp && hostCandidate.sdpMLineIndex == 0) { hostCandidates.Add(hostCandidate); OnIceCandidate?.Invoke(hostCandidate); } } return(hostCandidates); }
protected override async void OnMessage(MessageEventArgs e) { //logger.LogDebug($"OnMessage: {e.Data}"); if (RTCIceCandidateInit.TryParse(e.Data, out var iceCandidateInit)) { logger.LogDebug("Got remote ICE candidate."); bool useCandidate = true; if (FilterRemoteICECandidates != null && !string.IsNullOrWhiteSpace(iceCandidateInit.candidate)) { useCandidate = FilterRemoteICECandidates(iceCandidateInit); } if (!useCandidate) { logger.LogDebug($"WebRTCWebSocketPeer excluding ICE candidate due to filter: {iceCandidateInit.candidate}"); } else { _pc.addIceCandidate(iceCandidateInit); } } else if (RTCSessionDescriptionInit.TryParse(e.Data, out var descriptionInit)) { logger.LogDebug($"Got remote SDP, type {descriptionInit.type}."); var result = _pc.setRemoteDescription(descriptionInit); if (result != SetDescriptionResultEnum.OK) { logger.LogWarning($"Failed to set remote description, {result}."); _pc.Close("failed to set remote description"); this.Close(); } else { if (_pc.signalingState == RTCSignalingState.have_remote_offer) { var answerSdp = _pc.createAnswer(AnswerOptions); await _pc.setLocalDescription(answerSdp).ConfigureAwait(false); logger.LogDebug($"Sending SDP answer to client {Context.UserEndPoint}."); //logger.LogDebug(answerSdp.sdp); Context.WebSocket.Send(answerSdp.toJSON()); } } } else { logger.LogWarning($"websocket-server could not parse JSON message. {e.Data}"); } }
public string toJSON() { var rtcCandInit = new RTCIceCandidateInit { sdpMid = sdpMid ?? sdpMLineIndex.ToString(), sdpMLineIndex = sdpMLineIndex, usernameFragment = usernameFragment, candidate = CANDIDATE_PREFIX + ":" + this.ToString() }; return(rtcCandInit.toJSON()); }
public string toJSON() { var rtcCandInit = new RTCIceCandidateInit { sdpMid = sdpMid ?? sdpMLineIndex.ToString(), sdpMLineIndex = sdpMLineIndex, usernameFragment = usernameFragment, candidate = CANDIDATE_PREFIX + ":" + this.ToString() }; return(JsonConvert.SerializeObject(rtcCandInit, new Newtonsoft.Json.Converters.StringEnumConverter())); }
/// <summary> /// Adds a remote ICE candidate to the list this peer is attempting to connect against. /// </summary> /// <param name="candidateInit">The remote candidate to add.</param> public void addIceCandidate(RTCIceCandidateInit candidateInit) { RTCIceCandidate candidate = new RTCIceCandidate(candidateInit); if (_rtpIceChannel.Component == candidate.component) { _rtpIceChannel.AddRemoteCandidate(candidate); } else { logger.LogWarning($"Remote ICE candidate not added as no available ICE session for component {candidate.component}."); } }
private async Task <string> OnMessage(string signal, RTCPeerConnection pc) { string sdpAnswer = null; if (RTCIceCandidateInit.TryParse(signal, out var iceCandidateInit)) { logger.LogDebug($"Got remote ICE candidate, {iceCandidateInit.candidate}"); bool useCandidate = true; if (FilterRemoteICECandidates != null && !string.IsNullOrWhiteSpace(iceCandidateInit.candidate)) { useCandidate = FilterRemoteICECandidates(iceCandidateInit); } if (!useCandidate) { logger.LogDebug($"WebRTCRestPeer excluding ICE candidate due to filter: {iceCandidateInit.candidate}"); } else { _pc.addIceCandidate(iceCandidateInit); } } else if (RTCSessionDescriptionInit.TryParse(signal, out var descriptionInit)) { logger.LogDebug($"Got remote SDP, type {descriptionInit.type}."); //logger.LogDebug(descriptionInit.sdp); var result = pc.setRemoteDescription(descriptionInit); if (result != SetDescriptionResultEnum.OK) { logger.LogWarning($"Failed to set remote description, {result}."); pc.Close("failed to set remote description"); } else if (descriptionInit.type == RTCSdpType.offer) { var answerSdp = pc.createAnswer(AnswerOptions); await pc.setLocalDescription(answerSdp).ConfigureAwait(false); sdpAnswer = answerSdp.toJSON(); } } else { logger.LogWarning($"webrtc-rest could not parse JSON message. {signal}"); } return(sdpAnswer); }
public RTCIceCandidate(RTCIceCandidateInit init) { sdpMid = init.sdpMid; sdpMLineIndex = init.sdpMLineIndex; usernameFragment = init.usernameFragment; if (!String.IsNullOrEmpty(init.candidate)) { var iceCandidate = Parse(init.candidate); foundation = iceCandidate.foundation; priority = iceCandidate.priority; component = iceCandidate.component; address = iceCandidate.address; port = iceCandidate.port; type = iceCandidate.type; relatedAddress = iceCandidate.relatedAddress; relatedPort = iceCandidate.relatedPort; } }
public static bool TryParse(string json, out RTCIceCandidateInit init) { //init = JsonSerializer.Deserialize< RTCIceCandidateInit>(json); init = null; if (string.IsNullOrWhiteSpace(json)) { return(false); } else { init = JSONParser.FromJson <RTCIceCandidateInit>(json); // To qualify as parsed all required fields must be set. return(init != null && init.candidate != null && init.sdpMid != null); } }
protected override void OnMessage(MessageEventArgs e) { logger.LogDebug($"OnMessage: {e.Data}"); if (RTCIceCandidateInit.TryParse(e.Data, out var iceCandidateInit)) { logger.LogDebug("Got remote ICE candidate."); bool useCandidate = true; if (FilterRemoteICECandidates != null && !string.IsNullOrWhiteSpace(iceCandidateInit.candidate)) { useCandidate = FilterRemoteICECandidates(iceCandidateInit); } if (!useCandidate) { logger.LogDebug( $"WebRTCWebSocketPeer excluding ICE candidate due to filter: {iceCandidateInit.candidate}"); } else { _pc.addIceCandidate(iceCandidateInit); } } else if (RTCSessionDescriptionInit.TryParse(e.Data, out var descriptionInit)) { logger.LogDebug($"Got remote SDP, type {descriptionInit.type}."); var result = _pc.setRemoteDescription(descriptionInit); if (result != SetDescriptionResultEnum.OK) { logger.LogWarning($"Failed to set remote description, {result}."); _pc.Close("failed to set remote description"); this.Close(); } } else { logger.LogWarning($"websocket-server could not parse JSON message. {e.Data}"); } }
/// <summary> /// Acquires an ICE candidate for each IP address that this host has except for: /// - Loopback addresses must not be included. /// - Deprecated IPv4-compatible IPv6 addresses and IPv6 site-local unicast addresses /// must not be included, /// - IPv4-mapped IPv6 address should not be included. /// - If a non-location tracking IPv6 address is available use it and do not included /// location tracking enabled IPv6 addresses (i.e. prefer temporary IPv6 addresses over /// permanent addresses), see RFC6724. /// </summary> /// <remarks>See https://tools.ietf.org/html/rfc8445#section-5.1.1.1</remarks> /// <returns>A list of "host" ICE candidates for the local machine.</returns> private List <RTCIceCandidate> GetHostCandidates() { List <RTCIceCandidate> hostCandidates = new List <RTCIceCandidate>(); RTCIceCandidateInit init = new RTCIceCandidateInit { usernameFragment = LocalIceUser }; foreach (var localAddress in NetServices.LocalIPAddresses.Where(x => !IPAddress.IsLoopback(x) && !x.IsIPv4MappedToIPv6 && !x.IsIPv6SiteLocal)) { var hostCandidate = new RTCIceCandidate(init); hostCandidate.SetAddressProperties(RTCIceProtocol.udp, localAddress, (ushort)_rtpChannel.RTPPort, RTCIceCandidateType.host, null, 0); // We currently only support a single multiplexed connection for all data streams and RTCP. if (hostCandidate.component == RTCIceComponent.rtp && hostCandidate.sdpMLineIndex == 0) { hostCandidates.Add(hostCandidate); } } return(hostCandidates); }