Example #1
0
        /// <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);
            }
        }
Example #2
0
        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}");
            }
        }
Example #4
0
        /// <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);
        }
Example #5
0
        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}");
            }
        }
Example #6
0
        public string toJSON()
        {
            var rtcCandInit = new RTCIceCandidateInit
            {
                sdpMid           = sdpMid ?? sdpMLineIndex.ToString(),
                sdpMLineIndex    = sdpMLineIndex,
                usernameFragment = usernameFragment,
                candidate        = CANDIDATE_PREFIX + ":" + this.ToString()
            };

            return(rtcCandInit.toJSON());
        }
Example #7
0
        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()));
        }
Example #8
0
        /// <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);
        }
Example #10
0
        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;
            }
        }
Example #11
0
        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);
            }
        }
Example #12
0
        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}");
            }
        }
Example #13
0
        /// <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);
        }