/// <summary>
        /// Creates a RtspResponse based on the SequenceNumber contained in the given RtspRequest
        /// </summary>
        /// <param name="request">The request to utilize the SequenceNumber from, if null the current SequenceNumber is used</param>
        /// <param name="statusCode">The StatusCode of the generated response</param>
        /// <returns>The RtspResponse created</returns>
        internal RtspMessage CreateRtspResponse(RtspMessage request = null, RtspStatusCode statusCode = RtspStatusCode.OK, string body = null)
        {
            RtspMessage response = new RtspMessage(RtspMessageType.Response);

            response.StatusCode = statusCode;

            /*
             12.4.1 400 Bad Request

               The request could not be understood by the server due to malformed
               syntax. The client SHOULD NOT repeat the request without
               modifications [H10.4.1]. If the request does not have a CSeq header,
               the server MUST NOT include a CSeq in the response.
             */

            if (request != null)
            {
                if (request.ContainsHeader(RtspHeaders.Session)) response.SetHeader(RtspHeaders.Session, request.GetHeader(RtspHeaders.Session));

                if (statusCode != RtspStatusCode.BadRequest) response.CSeq = request.CSeq;
            }
            else if (statusCode != RtspStatusCode.BadRequest && LastRequest.CSeq >= 0) response.CSeq = LastRequest.CSeq;

            //Include any body.
            if (false == string.IsNullOrWhiteSpace(body)) response.Body = body;

            return response;
        }
Beispiel #2
0
        /// <summary>
        /// Sends a Rtsp Response on the given client session
        /// </summary>
        /// <param name="message">The RtspResponse to send</param> If this was byte[] then it could handle http
        /// <param name="ci">The session to send the response on</param>
        internal void ProcessSendRtspMessage(RtspMessage message, ClientSession session, bool sendResponse = true)
        {
            //Todo have a SupportedFeatures and RequiredFeatures HashSet.

            //Check Require Header
            //       And
            /* Add Unsupported Header if needed
            Require: play.basic, con.persistent
                       (basic play, TCP is supported)
            setup.playing means that setup and teardown can be used in the play state.
            */

            //If we have a session
            if (session == null || session.IsDisposed) return;

            try
            {
                //if there is a message to send
                if (message != null && false == message.IsDisposed)
                {
                    //AddServerHeaders()->

                    if (false == message.ContainsHeader(RtspHeaders.Server)) message.SetHeader(RtspHeaders.Server, ServerName);

                    if (false == message.ContainsHeader(RtspHeaders.Date)) message.SetHeader(RtspHeaders.Date, DateTime.UtcNow.ToString("r"));

                    #region RFC2326 12.38 Timestamp / Delay

                    /*
                         12.38 Timestamp

                           The timestamp general header describes when the client sent the
                           request to the server. The value of the timestamp is of significance
                           only to the client and may use any timescale. The server MUST echo
                           the exact same value and MAY, if it has accurate information about
                           this, add a floating point number indicating the number of seconds
                           that has elapsed since it has received the request. The timestamp is
                           used by the client to compute the round-trip time to the server so
                           that it can adjust the timeout value for retransmissions.

                           Timestamp  = "Timestamp" ":" *(DIGIT) [ "." *(DIGIT) ] [ delay ]
                           delay      =  *(DIGIT) [ "." *(DIGIT) ]
                         */

                    if (false == message.ContainsHeader(RtspHeaders.Timestamp)
                        &&
                        session.LastRequest != null
                        &&
                        session.LastRequest.ContainsHeader(RtspHeaders.Timestamp))
                    {
                        ////Apparently not joined with ;
                        ////message.SetHeader(RtspHeaders.Timestamp, session.LastRequest[RtspHeaders.Timestamp] + "delay=" + (DateTime.UtcNow - session.LastRequest.Created).TotalSeconds);

                        //Set the value of the Timestamp header as given
                        message.AppendOrSetHeader(RtspHeaders.Timestamp, session.LastRequest[RtspHeaders.Timestamp]);

                        //Add a delay datum
                        message.AppendOrSetHeader(RtspHeaders.Timestamp, "delay=" + (DateTime.UtcNow - session.LastRequest.Created).TotalSeconds);
                    }

                    #endregion

                    string sess = message.GetHeader(RtspHeaders.Session);

                    //Check for a session header
                    if (false == string.IsNullOrWhiteSpace(sess))
                    {
                        //Add the timeout header if there was a session header.
                        if (RtspClientInactivityTimeout > TimeSpan.Zero && false == sess.Contains("timeout")) message.AppendOrSetHeader(RtspHeaders.Session, "timeout=" + (int)(RtspClientInactivityTimeout.TotalSeconds / 2));
                    }

                    //Oops
                    //if (session.m_RtspSocket.ProtocolType == ProtocolType.Tcp && session.Attached.Count > 0) response.SetHeader("Ignore", "$0\09\r\n$\0:\0");

                    //Dispose the last response
                    if (session.LastResponse != null) session.LastResponse.Dispose();

                    //Todo
                    //Content-Encoding should be the same as the request's if possible..

                    //If sending a response
                    if (sendResponse)
                    {
                        //Log response
                        if (Logger != null) Logger.LogResponse(message, session);

                        session.SendRtspData((session.LastResponse = message).ToBytes());

                    }//Otherwise just update the property
                    else session.LastResponse = message;

                    //Indicate the session is not disconnected
                    session.IsDisconnected = false;

                    //Indicate the last response was sent now.
                    session.LastResponse.Transferred = DateTime.UtcNow;
                }
                else
                {
                    //Test the connectivity and start another receive
                    session.SendRtspData(Media.Common.MemorySegment.EmptyBytes);
                }
            }
            catch (Exception ex)
            {
                if (Logger != null) Logger.LogException(ex);

                //if a socket exception occured then handle it.
                if (session != null && ex is SocketException) HandleClientSocketException((SocketException)ex, session);
            }
        }
Beispiel #3
0
        protected virtual void ProcessHttpRtspRequest(IAsyncResult iar)
        {
            if (iar == null) return;

            var context = m_HttpListner.EndGetContext(iar);

            m_HttpListner.BeginGetContext(new AsyncCallback(ProcessHttpRtspRequest), null);

            if (context == null) return;

            if (context.Request.AcceptTypes.Any(t => string.Compare(t, "application/x-rtsp-tunnelled") == 0))
            {
                if (context.Request.HasEntityBody)
                {
                    using (var ips = context.Request.InputStream)
                    {
                        long size = context.Request.ContentLength64;

                        byte[] buffer = new byte[size];

                        int offset = 0;

                        while (size > 0)
                        {
                            size -= offset += ips.Read(buffer, offset, (int)size);
                        }

                        //Ensure the message is really Rtsp
                        RtspMessage request = new RtspMessage(System.Convert.FromBase64String(context.Request.ContentEncoding.GetString(buffer)));

                        //Check for validity
                        if (request.MessageType != RtspMessageType.Invalid)
                        {
                            //Process the request when complete
                            if (request.IsComplete)
                            {
                                ProcessRtspRequest(request, CreateSession(null), false);

                                return;
                            }

                        }
                    }
                }
                else //No body
                {
                    RtspMessage request = new RtspMessage()
                    {
                        Location = new Uri(RtspMessage.ReliableTransport + "://" + context.Request.LocalEndPoint.Address.ToString() +  context.Request.RawUrl),
                        Method = RtspMethod.DESCRIBE
                    };

                    foreach (var header in context.Request.Headers.AllKeys)
                    {
                        request.SetHeader(header, context.Request.Headers[header]);
                    }

                    ProcessRtspRequest(request, CreateSession(null), false);
                }
            }
        }
Beispiel #4
0
        internal virtual void ProcessAuthorizationRequired(IMedia source, ClientSession session, bool sendResponse = true)
        {
            RtspMessage response = new RtspMessage(RtspMessageType.Response);

            response.CSeq = session.LastRequest.CSeq;

            string authHeader = session.LastRequest.GetHeader(RtspHeaders.Authorization);

            RtspStatusCode statusCode;

            bool noAuthHeader = string.IsNullOrWhiteSpace(authHeader);

            if (noAuthHeader && session.LastRequest.ContainsHeader(RtspHeaders.AcceptCredentials)) statusCode = RtspStatusCode.ConnectionAuthorizationRequired;
            //If the last request did not have an authorization header
            else if (noAuthHeader)
            {
                /* -- http://tools.ietf.org/html/rfc2617

            qop
             Indicates what "quality of protection" the client has applied to
             the message. If present, its value MUST be one of the alternatives
             the server indicated it supports in the WWW-Authenticate header.
             These values affect the computation of the request-digest. Note
             that this is a single token, not a quoted list of alternatives as
             in WWW- Authenticate.  This directive is optional in order to
             preserve backward compatibility with a minimal implementation of
             RFC 2617 [6], but SHOULD be used if the server indicated that qop
             is supported by providing a qop directive in the WWW-Authenticate
             header field.

               cnonce
             This MUST be specified if a qop directive is sent (see above), and
             MUST NOT be specified if the server did not send a qop directive in
             the WWW-Authenticate header field.  The cnonce-value is an opaque
             quoted string value provided by the client and used by both client
             and server to avoid chosen plaintext attacks, to provide mutual
             authentication, and to provide some message integrity protection.
             See the descriptions below of the calculation of the response-
             digest and request-digest values.

                 */

                //Could retrieve values from last Request if needed..
                //string realm = "//", nOnceCount = "00000001";

                //Should handle multiple types of auth

                //Should store the nonce and cnonce values on the session
                statusCode = RtspStatusCode.Unauthorized;

                NetworkCredential requiredCredential = null;

                string authenticateHeader = null;

                Uri relativeLocation = source.ServerLocation;

                //Check for Digest first - Todo Finish implementation
                if ((requiredCredential = RequiredCredentials.GetCredential(relativeLocation, "Digest")) != null)
                {
                    //Might need to store values qop nc, cnonce and nonce in session storage for later retrival

                    //Should use auth-int and qop

                    authenticateHeader = string.Format(System.Globalization.CultureInfo.InvariantCulture, "Digest username={0},realm={1},nonce={2},cnonce={3}", requiredCredential.UserName, (string.IsNullOrWhiteSpace(requiredCredential.Domain) ? ServerName : requiredCredential.Domain), ((long)(Utility.Random.Next(int.MaxValue) << 32 | (Utility.Random.Next(int.MaxValue)))).ToString("X"), Utility.Random.Next(int.MaxValue).ToString("X"));
                }
                else if ((requiredCredential = RequiredCredentials.GetCredential(relativeLocation, "Basic")) != null)
                {
                    authenticateHeader = "Basic realm=\"" + (string.IsNullOrWhiteSpace(requiredCredential.Domain) ? ServerName : requiredCredential.Domain + '"');
                }

                if (!string.IsNullOrWhiteSpace(authenticateHeader))
                {
                    response.SetHeader(RtspHeaders.WWWAuthenticate, authenticateHeader);
                }
            }
            else //Authorization header was present but data was incorrect
            {
                //Parse type from authHeader

                string[] parts = authHeader.Split((char)Common.ASCII.Space);

                string authType = null;

                if (parts.Length > 0)
                {
                    authType = parts[0];
                    authHeader = parts[1];
                }

                //should check to ensure wrong type was not used e.g. basic in place of digest...
                if (string.Compare(authType, "digest", true) == 0)
                {
                    //if (session.Storage["nOnce"] != null)
                    //{
                    //    //Increment NonceCount
                    //}
                }

                //Increment session attempts?

                statusCode = RtspStatusCode.Forbidden;
            }

            //Set the status code
            response.StatusCode = statusCode;

            //Send the response
            ProcessInvalidRtspRequest(response, session, sendResponse);
        }