コード例 #1
0
ファイル: SIP_Dialog.cs プロジェクト: dioptre/nkd
        /// <summary>
        /// Initializes dialog.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="transaction">Owner transaction.</param>
        /// <param name="response">SIP response what caused dialog creation.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception>
        internal protected virtual void Init(SIP_Stack stack,SIP_Transaction transaction,SIP_Response response)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(transaction == null){
                throw new ArgumentNullException("transaction");
            }
            if(response == null){
                throw new ArgumentNullException("response");
            }

            m_pStack = stack;
                        
            #region UAS

            /* RFC 3261 12.1.1.
                The UAS then constructs the state of the dialog.  This state MUST be
                maintained for the duration of the dialog.

                If the request arrived over TLS, and the Request-URI contained a SIPS
                URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the request, taken in order and preserving all URI
                parameters.  If no Record-Route header field is present in the
                request, the route set MUST be set to the empty set.  This route set,
                even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the request.

                The remote sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The local sequence
                number MUST be empty.  The call identifier component of the dialog ID
                MUST be set to the value of the Call-ID in the request.  The local
                tag component of the dialog ID MUST be set to the tag in the To field
                in the response to the request (which always includes a tag), and the
                remote tag component of the dialog ID MUST be set to the tag from the
                From field in the request.  A UAS MUST be prepared to receive a
                request without a tag in the From field, in which case the tag is
                considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate From tags.

                The remote URI MUST be set to the URI in the From field, and the
                local URI MUST be set to the URI in the To field.
            */

            if(transaction is SIP_ServerTransaction){
                m_IsSecure = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet = (SIP_t_AddressParam[])Net_Utils.ReverseArray(transaction.Request.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
                m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_LocalSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = response.To.Tag;
                m_RemoteTag = transaction.Request.From.Tag;
                m_pRemoteUri = transaction.Request.From.Address.Uri;
                m_pLocalUri = transaction.Request.To.Address.Uri;
                m_pLocalContact = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;

                List<string> allow = new List<string>();
                foreach(SIP_t_Method m in response.Allow.GetAllValues()){
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List<string> supported = new List<string>();
                foreach(SIP_t_OptionTag s in response.Supported.GetAllValues()){
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion

            #region UAC

            /* RFC 3261 12.1.2.
                When a UAC receives a response that establishes a dialog, it
                constructs the state of the dialog.  This state MUST be maintained
                for the duration of the dialog.

                If the request was sent over TLS, and the Request-URI contained a
                SIPS URI, the "secure" flag is set to TRUE.

                The route set MUST be set to the list of URIs in the Record-Route
                header field from the response, taken in reverse order and preserving
                all URI parameters.  If no Record-Route header field is present in
                the response, the route set MUST be set to the empty set.  This route
                set, even if empty, overrides any pre-existing route set for future
                requests in this dialog.  The remote target MUST be set to the URI
                from the Contact header field of the response.

                The local sequence number MUST be set to the value of the sequence
                number in the CSeq header field of the request.  The remote sequence
                number MUST be empty (it is established when the remote UA sends a
                request within the dialog).  The call identifier component of the
                dialog ID MUST be set to the value of the Call-ID in the request.
                The local tag component of the dialog ID MUST be set to the tag in
                the From field in the request, and the remote tag component of the
                dialog ID MUST be set to the tag in the To field of the response.  A
                UAC MUST be prepared to receive a response without a tag in the To
                field, in which case the tag is considered to have a value of null.

                    This is to maintain backwards compatibility with RFC 2543, which
                    did not mandate To tags.

                The remote URI MUST be set to the URI in the To field, and the local
                URI MUST be set to the URI in the From field.
            */

            else{
                // TODO: Validate request or client transaction must do it ?

                m_IsSecure  = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet = (SIP_t_AddressParam[])Net_Utils.ReverseArray(response.RecordRoute.GetAllValues());
                m_pRemoteTarget = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri;                
                m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber;
                m_RemoteSeqNo = 0;
                m_CallID = transaction.Request.CallID;
                m_LocalTag = transaction.Request.From.Tag;
                m_RemoteTag = response.To.Tag;
                m_pRemoteUri = transaction.Request.To.Address.Uri;
                m_pLocalUri = transaction.Request.From.Address.Uri;
                m_pLocalContact = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri;
                
                List<string> allow = new List<string>();
                foreach(SIP_t_Method m in response.Allow.GetAllValues()){
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List<string> supported = new List<string>();
                foreach(SIP_t_OptionTag s in response.Supported.GetAllValues()){
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion            

            m_pFlow = transaction.Flow;
            AddTransaction(transaction);
        }
コード例 #2
0
ファイル: SIP_Dialog.cs プロジェクト: dioptre/nkd
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            lock(m_pLock){
                if(this.State == SIP_DialogState.Disposed){
                    return;
                }

                SetState(SIP_DialogState.Disposed,true);
   
                this.RequestReceived = null;
                m_pStack             = null;
                m_CallID             = null;
                m_LocalTag           = null;
                m_RemoteTag          = null;
                m_pLocalUri          = null;
                m_pRemoteUri         = null;
                m_pLocalContact      = null;
                m_pRemoteTarget      = null;
                m_pRouteSet          = null;
                m_pFlow              = null;           
            }
        }
コード例 #3
0
ファイル: SIP_Dialog.cs プロジェクト: dioptre/nkd
        // TODO: Early timer.


        #region method ProcessRequest

        /// <summary>
        /// Processes specified request through this dialog.
        /// </summary>
        /// <param name="e">Method arguments.</param>
        /// <returns>Returns true if this dialog processed specified response, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>e</b> is null reference.</exception>
        internal protected virtual bool ProcessRequest(SIP_RequestReceivedEventArgs e)
        {
            if(e == null){
                throw new ArgumentNullException("e");
            }

            /* RFC 3261 12.2.2.
                If the remote sequence number is empty, it MUST be set to the value
                of the sequence number in the CSeq header field value in the request.
                If the remote sequence number was not empty, but the sequence number
                of the request is lower than the remote sequence number, the request
                is out of order and MUST be rejected with a 500 (Server Internal
                Error) response.  If the remote sequence number was not empty, and
                the sequence number of the request is greater than the remote
                sequence number, the request is in order.  It is possible for the
                CSeq sequence number to be higher than the remote sequence number by
                more than one.  This is not an error condition, and a UAS SHOULD be
                prepared to receive and process requests with CSeq values more than
                one higher than the previous received request.  The UAS MUST then set
                the remote sequence number to the value of the sequence number in the
                CSeq header field value in the request.
            */

            if(m_RemoteSeqNo == 0){
                m_RemoteSeqNo = e.Request.CSeq.SequenceNumber;
            }
            else if(e.Request.CSeq.SequenceNumber < m_RemoteSeqNo){
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": The mid-dialog request is out of order(late arriving request).",e.Request));

                return true;
            }
            else{
                m_RemoteSeqNo = e.Request.CSeq.SequenceNumber;
            }

            /* RFC 3261 12.2.2.
                When a UAS receives a target refresh request, it MUST replace the
                dialog's remote target URI with the URI from the Contact header field
                in that request, if present.

                Per RFC we must do it after 2xx response. There are some drwabacks, like old contact not accessible any more
                due to NAT, so only valid contact new contact. Because of it currently we always change contact.
            */
            if(IsTargetRefresh(e.Request.RequestLine.Method) && e.Request.Contact.Count != 0){
                m_pRemoteTarget = (SIP_Uri)e.Request.Contact.GetTopMostValue().Address.Uri;
            }

            OnRequestReceived(e);
                        
            return e.IsHandled;
        }
コード例 #4
0
        /// <summary>
        /// Parses SIP_Uri from SIP-URI string.
        /// </summary>
        /// <param name="value">SIP-URI  string.</param>
        /// <returns>Returns parsed SIP_Uri object.</returns>
        /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception>
        /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception>
        public static SIP_Uri Parse(string value)
        {
            // Syntax: sip:/sips: username@host:port *[;parameter] [?header *[&header]]

            if(value == null){
                throw new ArgumentNullException("value");
            }

            value = Uri.UnescapeDataString(value);

            if(!(value.ToLower().StartsWith("sip:") || value.ToLower().StartsWith("sips:"))){
                throw new SIP_ParseException("Specified value is invalid SIP-URI !");
            }

            SIP_Uri retVal = new SIP_Uri();
            StringReader r = new StringReader(value);

            // IsSecure
            retVal.IsSecure = r.QuotedReadToDelimiter(':').ToLower() == "sips";

            // Get username
            if(r.SourceString.IndexOf('@') > -1){
                retVal.User = r.QuotedReadToDelimiter('@');
            }

            // Gets host[:port]
            string[] host_port = r.QuotedReadToDelimiter(new char[]{';','?'},false).Split(':');
            retVal.Host = host_port[0];
            // Optional port specified
            if(host_port.Length == 2){
                retVal.Port = Convert.ToInt32(host_port[1]);
            }

            // We have parameters and/or header
            if(r.Available > 0){
                // Get parameters
                string[] parameters = TextUtils.SplitQuotedString(r.QuotedReadToDelimiter('?'),';');
                foreach(string parameter in parameters){
                    if(parameter.Trim() != ""){
                        string[] name_value = parameter.Trim().Split(new char[]{'='},2);
                        if(name_value.Length == 2){
                            retVal.Parameters.Add(name_value[0],name_value[1]);
                        }
                        else{
                            retVal.Parameters.Add(name_value[0],null);
                        }
                    }
                }

                // We have header
                if(r.Available > 0){
                    retVal.m_Header = r.ReadToEnd();
                }
            }

            return retVal;
        }