Ejemplo n.º 1
0
        /// <summary>
        /// Is called when caller sends new request.
        /// </summary>
        /// <param name="e">Event data.</param>
        private void m_pCaller_RequestReceived(SIP_RequestReceivedEventArgs e)
        {  
            // TODO: If we get UPDATE, but callee won't support it ? generate INVITE instead ?
    /*
            SIP_Request request = m_pCallee.CreateRequest(e.Request.RequestLine.Method);
            CopyMessage(e.Request,request,new string[]{"Via:","Call-Id:","To:","From:","CSeq:","Contact:","Route:","Record-Route:","Max-Forwards:","Allow:","Require:","Supported:"});
            // Remove our Authentication header if it's there.
            foreach(SIP_SingleValueHF<SIP_t_Credentials> header in request.ProxyAuthorization.HeaderFields){
                try{
                    Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData,request.RequestLine.Method);
                    if(m_pOwner.Stack.Realm == digest.Realm){
                        request.ProxyAuthorization.Remove(header);
                    }
                }
                catch{
                    // We don't care errors here. This can happen if remote server xxx auth method here and
                    // we don't know how to parse it, so we leave it as is.
                }
            }

            SIP_ClientTransaction clientTransaction = m_pCallee.CreateTransaction(request);
            clientTransaction.ResponseReceived += new EventHandler<SIP_ResponseReceivedEventArgs>(m_pCallee_ResponseReceived);
            clientTransaction.Tag = e.ServerTransaction;
            clientTransaction.Start();*/
        }
Ejemplo n.º 2
0
        /// <summary>
        /// This method is called when SIP stack received new message.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pStack_RequestReceived(object sender,SIP_RequestReceivedEventArgs e)
        {
            // TODO: Performance: rise events on thread pool or see if this method called on pool aready, then we may not keep lock for events ?

            if(e.Request.RequestLine.Method == SIP_Methods.CANCEL){
                /* RFC 3261 9.2.
                    If the UAS did not find a matching transaction for the CANCEL
                    according to the procedure above, it SHOULD respond to the CANCEL
                    with a 481 (Call Leg/Transaction Does Not Exist).
                  
                    Regardless of the method of the original request, as long as the
                    CANCEL matched an existing transaction, the UAS answers the CANCEL
                    request itself with a 200 (OK) response.
                */

                SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);
                if(trToCancel != null){
                    trToCancel.Cancel();
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok,e.Request));
                }
                else{
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist,e.Request));
                }
            }
            else if(e.Request.RequestLine.Method == SIP_Methods.BYE){
                /* RFC 3261 15.1.2.
                    If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481
                    (Call/Transaction Does Not Exist) response and pass that to the server transaction.
                */
                // TODO:

                SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(e.Request);
                if(dialog != null){
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok,e.Request));
                    dialog.Terminate();
                }
                else{
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist,e.Request));
                }
            }
            else if(e.Request.RequestLine.Method == SIP_Methods.INVITE){
                // Supress INVITE retransmissions.
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x100_Trying,e.Request));
         
                // Create call.
                SIP_UA_Call call = new SIP_UA_Call(this,e.ServerTransaction);
                call.StateChanged += new EventHandler(Call_StateChanged);
                m_pCalls.Add(call);

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

            if(base.ProcessRequest(e)){
                return true;
            }

            if(e.Request.RequestLine.Method == SIP_Methods.NOTIFY){
                OnNotify(e);

                return true;
            }
            else{
                return false;
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Is called when SIP stack has received request.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pStack_RequestReceived(object sender,SIP_RequestReceivedEventArgs e)
        {
            try{
                #region CANCEL

                if(e.Request.RequestLine.Method == SIP_Methods.CANCEL){
                    /* RFC 3261 9.2.
                        If the UAS did not find a matching transaction for the CANCEL
                        according to the procedure above, it SHOULD respond to the CANCEL
                        with a 481 (Call Leg/Transaction Does Not Exist).
                  
                        Regardless of the method of the original request, as long as the
                        CANCEL matched an existing transaction, the UAS answers the CANCEL
                        request itself with a 200 (OK) response.
                    */

                    SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);
                    if(trToCancel != null){
                        trToCancel.Cancel();
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok,e.Request));
                    }
                    else{
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist,e.Request));
                    }
                }

                #endregion

                #region BYE

                else if(e.Request.RequestLine.Method == SIP_Methods.BYE){
                    /* RFC 3261 15.1.2.
                        If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481
                        (Call/Transaction Does Not Exist) response and pass that to the server transaction.
                    */

                    // Currently we match BYE to dialog and it processes it,
                    // so BYE what reaches here doesnt match to any dialog.

                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist,e.Request));
                }

                #endregion

                #region INVITE

                else if(e.Request.RequestLine.Method == SIP_Methods.INVITE){

                    #region Incoming call

                    if(e.Dialog == null){
                        #region Validate incoming call

                        // We don't accept more than 1 call at time.
                        if(m_pIncomingCallUI != null || m_pCall != null){
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x600_Busy_Everywhere,e.Request));

                            return;
                        }

                        // We don't accept SDP offerless calls.
                        if(e.Request.ContentType == null || e.Request.ContentType.ToLower().IndexOf("application/sdp") == -1){
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x606_Not_Acceptable + ": We don't accpet SDP offerless calls.",e.Request));

                            return;
                        }

                        SDP_Message sdpOffer = SDP_Message.Parse(Encoding.UTF8.GetString(e.Request.Data));

                        // Check if we can accept any media stream.
                        bool canAccept = false;
                        foreach(SDP_MediaDescription media in sdpOffer.MediaDescriptions){
                            if(CanSupportMedia(media)){
                                canAccept = true;

                                break;
                            }
                        }
                        if(!canAccept){
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x606_Not_Acceptable,e.Request));

                            return;
                        }
                        
                        #endregion
                                                                        
                        // Send ringing to remote-party.
                        SIP_Response responseRinging = m_pStack.CreateResponse(SIP_ResponseCodes.x180_Ringing,e.Request,e.Flow);
                        responseRinging.To.Tag = SIP_Utils.CreateTag();
                        e.ServerTransaction.SendResponse(responseRinging);

                        SIP_Dialog_Invite dialog = (SIP_Dialog_Invite)m_pStack.TransactionLayer.GetOrCreateDialog(e.ServerTransaction,responseRinging);
                                                
                        // We need invoke here, otherwise we block SIP stack RequestReceived event while incoming call UI showed.
                        this.BeginInvoke(new MethodInvoker(delegate(){
                            try{
                                m_pPlayer.Play(ResManager.GetStream("ringing.wav"),20);

                                // Show incoming call UI.
                                m_pIncomingCallUI = new wfrm_IncomingCall(e.ServerTransaction);
                                // Call accepted.
                                if(m_pIncomingCallUI.ShowDialog(this) == DialogResult.Yes){  
                                    RTP_MultimediaSession rtpMultimediaSession = new RTP_MultimediaSession(RTP_Utils.GenerateCNAME());

                                    // Build local SDP template
                                    SDP_Message sdpLocal = new SDP_Message();
                                    sdpLocal.Version = "0";
                                    sdpLocal.Origin = new SDP_Origin("-",sdpLocal.GetHashCode(),1,"IN","IP4",System.Net.Dns.GetHostAddresses("")[0].ToString());
                                    sdpLocal.SessionName = "SIP Call";            
                                    sdpLocal.Times.Add(new SDP_Time(0,0));
                           
                                    ProcessMediaOffer(dialog,e.ServerTransaction,rtpMultimediaSession,sdpOffer,sdpLocal);

                                    // Create call.
                                    m_pCall = new SIP_Call(m_pStack,dialog,rtpMultimediaSession,sdpLocal);
                                    m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
                                    m_pCall_StateChanged(m_pCall,new EventArgs());

                                    if(m_IsDebug){
                                        wfrm_RTP_Debug rtpDebug = new wfrm_RTP_Debug(m_pCall.RtpMultimediaSession);
                                        rtpDebug.Show();
                                    }
                                }
                                // Call rejected.
                                else{
                                    // Transaction response is sent in call UI.

                                    dialog.Terminate(null,false);
                                }
                                m_pIncomingCallUI = null;
                                m_pPlayer.Stop();
                            }
                            catch(Exception x1){
                                MessageBox.Show("Error: " + x1.Message,"Error:",MessageBoxButtons.OK,MessageBoxIcon.Error);
                            }
                        }));
                    }

                    #endregion

                    #region Re-INVITE

                    else{
                        try{                                                        
                            // Remote-party provided SDP offer.
                            if(e.Request.ContentType != null && e.Request.ContentType.ToLower().IndexOf("application/sdp") > -1){
                                ProcessMediaOffer(m_pCall.Dialog,e.ServerTransaction,m_pCall.RtpMultimediaSession,SDP_Message.Parse(Encoding.UTF8.GetString(e.Request.Data)),m_pCall.LocalSDP);

                                // We are holding a call.
                                if(m_pToggleOnHold.Text == "Unhold"){
                                    // We don't need to do anything here.
                                }
                                // Remote-party is holding a call.
                                else if(IsRemotePartyHolding(SDP_Message.Parse(Encoding.UTF8.GetString(e.Request.Data)))){
                                    // We need invoke here, we are running on thread pool thread.
                                    this.BeginInvoke(new MethodInvoker(delegate(){
                                        m_pStatusBar.Items["text"].Text = "Remote party holding a call";                                    
                                    }));

                                    m_pPlayer.Play(ResManager.GetStream("onhold.wav"),20);
                                }
                                // Call is active.
                                else{
                                    // We need invoke here, we are running on thread pool thread.
                                    this.BeginInvoke(new MethodInvoker(delegate(){
                                        m_pStatusBar.Items["text"].Text = "Call established";                                    
                                    }));

                                    m_pPlayer.Stop();
                                }
                            }
                            // Error: Re-INVITE can't be SDP offerless.
                            else{
                                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": Re-INVITE must contain SDP offer.",e.Request));
                            }
                        }
                        catch(Exception x1){
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x1.Message,e.Request));
                        }
                    }

                    #endregion
                }

                #endregion

                #region ACK

                else if(e.Request.RequestLine.Method == SIP_Methods.ACK){
                    // Abandoned ACK, just skip it.
                }

                #endregion

                #region Other

                else{
                    // ACK is response less method.
                    if(e.Request.RequestLine.Method != SIP_Methods.ACK){
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented,e.Request));
                    }
                }

                #endregion
            }
            catch{
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error,e.Request));
            }
        }
Ejemplo n.º 5
0
 /// <summary>
 /// Raises <b>Notify</b> event.
 /// </summary>
 /// <param name="e">Event args.</param>
 private void OnNotify(SIP_RequestReceivedEventArgs e)
 {
     if(this.Notify != null){
         this.Notify(this,e);
     }
 }
Ejemplo n.º 6
0
        // 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;
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Processes specified request through this dialog.
        /// </summary>
        /// <param name="e">Method arguments.</param>
        /// <returns>Returns true if this dialog processed specified request, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>e</b> is null reference.</exception>
        internal protected override bool ProcessRequest(SIP_RequestReceivedEventArgs e)
        {
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            if (base.ProcessRequest(e))
            {
                return(true);
            }

            // We must support: INVITE(re-invite),UPDATE,ACK,  [BYE will be handled by base class]

            #region INVITE

            if (e.Request.RequestLine.Method == SIP_Methods.INVITE)
            {
                /* RFC 3261 14.2.
                 *  A UAS that receives a second INVITE before it sends the final
                 *  response to a first INVITE with a lower CSeq sequence number on the
                 *  same dialog MUST return a 500 (Server Internal Error) response to the
                 *  second INVITE and MUST include a Retry-After header field with a
                 *  randomly chosen value of between 0 and 10 seconds.
                 *
                 *  A UAS that receives an INVITE on a dialog while an INVITE it had sent
                 *  on that dialog is in progress MUST return a 491 (Request Pending)
                 *  response to the received INVITE.
                 */

                if (m_pActiveInvite != null && m_pActiveInvite is SIP_ServerTransaction && ((SIP_ServerTransaction)m_pActiveInvite).Request.CSeq.SequenceNumber < e.Request.CSeq.SequenceNumber)
                {
                    SIP_Response response = this.Stack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": INVITE with a lower CSeq is pending(RFC 3261 14.2).", e.Request);
                    response.RetryAfter = new SIP_t_RetryAfter("10");
                    e.ServerTransaction.SendResponse(response);

                    return(true);
                }
                if (m_pActiveInvite != null && m_pActiveInvite is SIP_ClientTransaction)
                {
                    e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending, e.Request));

                    return(true);
                }

                // Force server transaction creation and set it as active INVITE transaction.
                m_pActiveInvite = e.ServerTransaction;
                m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                    if (m_pActiveInvite.State == SIP_TransactionState.Terminated)
                    {
                        m_pActiveInvite = null;
                    }
                };
                // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.)
                ((SIP_ServerTransaction)m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a){
                    if (a.Response.StatusCodeType == SIP_StatusCodeType.Success)
                    {
                        m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response));
                    }
                };

                OnReinvite(((SIP_ServerTransaction)m_pActiveInvite));

                return(true);
            }

            #endregion

            #region ACK

            else if (e.Request.RequestLine.Method == SIP_Methods.ACK)
            {
                // Search corresponding INVITE 2xx retransmit entry and dispose it.
                foreach (UasInvite2xxRetransmit t in m_pUasInvite2xxRetransmits)
                {
                    if (t.MatchAck(e.Request))
                    {
                        t.Dispose();
                        if (this.State == SIP_DialogState.Early)
                        {
                            this.SetState(SIP_DialogState.Confirmed, true);

                            // TODO: If Terminating
                        }

                        return(true);
                    }
                }

                return(false);
            }

            #endregion

            #region UPDATE

            //else if(request.RequestLine.Method == SIP_Methods.UPDATE){
            // TODO:
            //}

            #endregion

            // RFC 5057 5.6. Refusing New Usages. Decline(603 Decline) new dialog usages.
            else if (SIP_Utils.MethodCanEstablishDialog(e.Request.RequestLine.Method))
            {
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x603_Decline + " : New dialog usages not allowed (RFC 5057).", e.Request));

                return(true);
            }
            else
            {
                return(false);
            }
        }
Ejemplo n.º 8
0
 /// <summary>
 /// Raises <b>TerminatedByRemoteParty</b> event.
 /// </summary>
 /// <param name="bye">BYE request.</param>
 private void OnTerminatedByRemoteParty(SIP_RequestReceivedEventArgs bye)
 {
     if(this.TerminatedByRemoteParty != null){
         this.TerminatedByRemoteParty(this,bye);
     }
 }
 /// <summary>
 /// This method is called when new request is received.
 /// </summary>
 /// <param name="e">Request event arguments.</param>
 public virtual void OnRequestReceived(SIP_RequestReceivedEventArgs e)
 {
 }
Ejemplo n.º 10
0
        /// <summary>
        /// Authenticates SIP request. This method also sends all needed replys to request sender.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        /// <param name="userName">If authentication sucessful, then authenticated user name is stored to this variable.</param>
        /// <returns>Returns true if request was authenticated.</returns>
        internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e,out string userName)
        {            
            userName = null;
         
            SIP_t_Credentials credentials = SIP_Utils.GetCredentials(e.Request,m_pStack.Realm);
            // No credentials for our realm.
            if(credentials == null){
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required,e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm,m_pStack.DigestNonceManager.CreateNonce(),m_Opaque).ToChallange());
                    
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }
                                       
            Auth_HttpDigest auth = new Auth_HttpDigest(credentials.AuthData,e.Request.RequestLine.Method);
            // Check opaque validity.
            if(auth.Opaque != m_Opaque){
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Opaque value won't match !",e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm,m_pStack.DigestNonceManager.CreateNonce(),m_Opaque).ToChallange());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }
            // Check nonce validity.
            if(!m_pStack.DigestNonceManager.NonceExists(auth.Nonce)){
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Invalid nonce value !",e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm,m_pStack.DigestNonceManager.CreateNonce(),m_Opaque).ToChallange());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }
            // Valid nonce, consume it so that nonce can't be used any more. 
            else{
                m_pStack.DigestNonceManager.RemoveNonce(auth.Nonce);
            }

            SIP_AuthenticateEventArgs eArgs = this.OnAuthenticate(auth);
            // Authenticate failed.
            if(!eArgs.Authenticated){
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Authentication failed.",e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm,m_pStack.DigestNonceManager.CreateNonce(),m_Opaque).ToChallange());
                    
                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return false;
            }

            userName = auth.UserName;

            return true;
        }
Ejemplo n.º 11
0
 /// <summary>
 /// Authenticates SIP request. This method also sends all needed replys to request sender.
 /// </summary>
 /// <param name="e">Request event arguments.</param>
 /// <returns>Returns true if request was authenticated.</returns>
 internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e)
 {
     string userName = null;
     return AuthenticateRequest(e,out userName);
 }
Ejemplo n.º 12
0
        /// <summary>
        /// Forwards specified request to target recipient.
        /// </summary>
        /// <param name="statefull">Specifies if request is sent statefully or statelessly.</param>
        /// <param name="e">Request event arguments.</param>
        /// <param name="addRecordRoute">If true Record-Route header field is added.</param>
        internal void ForwardRequest(bool statefull,SIP_RequestReceivedEventArgs e,bool addRecordRoute)
        {
            SIP_RequestContext requestContext = new SIP_RequestContext(this,e.Request,e.Flow);

            SIP_Request request = e.Request;
            SIP_Uri     route   = null;
                        
            /* RFC 3261 16.
                1. Validate the request (Section 16.3)
                    1. Reasonable Syntax
                    2. URI scheme (NOTE: We do it later)
                    3. Max-Forwards
                    4. (Optional) Loop Detection
                    5. Proxy-Require
                    6. Proxy-Authorization
                2. Preprocess routing information (Section 16.4)
                3. Determine target(s) for the request (Section 16.5)
                x. Custom handling (non-RFC)
                    1. Process custom request handlers.
                    2. URI scheme
                4. Forward the request (Section 16.6)
            */

            #region 1. Validate the request (Section 16.3)

            // 1.1 Reasonable Syntax.
            //      SIP_Message parsing have done it.

            // 1.2 URI scheme check.
            //      We do it later.

            // 1.3 Max-Forwards.
            if(request.MaxForwards <= 0){
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x483_Too_Many_Hops,request));
                return;
            }

            // 1.4 (Optional) Loop Detection.
            //      Skip.

            // 1.5 Proxy-Require.
            //      TODO:

            // 1.6 Proxy-Authorization.
            /*  If an element requires credentials before forwarding a request,
                the request MUST be inspected as described in Section 22.3.  That
                section also defines what the element must do if the inspection
                fails.
            */
                        
            // We need to auth all foreign calls.            
            if(!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) || !this.OnIsLocalUri(((SIP_Uri)request.RequestLine.Uri).Host)){
                // If To: field is registrar AOR and request-URI is local registration contact, skip authentication.
                bool skipAuth = false;
                if(request.To.Address.IsSipOrSipsUri){
                    SIP_Registration registration = m_pRegistrar.GetRegistration(((SIP_Uri)request.To.Address.Uri).Address);
                    if(registration != null){
                        if(registration.GetBinding(request.RequestLine.Uri) != null){
                            skipAuth = true;
                        }
                    }
                }

                if(!skipAuth){
                    string userName = null;

                    // We need to pass-through ACK.
                    if(request.RequestLine.Method == SIP_Methods.ACK){
                    }
                    else if(!AuthenticateRequest(e,out userName)){
                        return;
                    }

                    requestContext.SetUser(userName);
                }
            }

            #endregion

            #region 2. Preprocess routing information (Section 16.4).

            /*
                The proxy MUST inspect the Request-URI of the request.  If the
                Request-URI of the request contains a value this proxy previously
                placed into a Record-Route header field (see Section 16.6 item 4),
                the proxy MUST replace the Request-URI in the request with the last
                value from the Route header field, and remove that value from the
                Route header field.  The proxy MUST then proceed as if it received
                this modified request.

                    This will only happen when the element sending the request to the
                    proxy (which may have been an endpoint) is a strict router.  This
                    rewrite on receive is necessary to enable backwards compatibility
                    with those elements.  It also allows elements following this
                    specification to preserve the Request-URI through strict-routing
                    proxies (see Section 12.2.1.1).

                    This requirement does not obligate a proxy to keep state in order
                    to detect URIs it previously placed in Record-Route header fields.
                    Instead, a proxy need only place enough information in those URIs
                    to recognize them as values it provided when they later appear.

                If the Request-URI contains a maddr parameter, the proxy MUST check
                to see if its value is in the set of addresses or domains the proxy
                is configured to be responsible for.  If the Request-URI has a maddr
                parameter with a value the proxy is responsible for, and the request
                was received using the port and transport indicated (explicitly or by
                default) in the Request-URI, the proxy MUST strip the maddr and any
                non-default port or transport parameter and continue processing as if
                those values had not been present in the request.

                    A request may arrive with a maddr matching the proxy, but on a
                    port or transport different from that indicated in the URI.  Such
                    a request needs to be forwarded to the proxy using the indicated
                    port and transport.

                If the first value in the Route header field indicates this proxy,
                the proxy MUST remove that value from the request.
            */
            
            // Strict route - handle it.
            if((request.RequestLine.Uri is SIP_Uri) && IsRecordRoute(((SIP_Uri)request.RequestLine.Uri)) && request.Route.GetAllValues().Length > 0){
                request.RequestLine.Uri = request.Route.GetAllValues()[request.Route.GetAllValues().Length - 1].Address.Uri;
                SIP_t_AddressParam[] routes = request.Route.GetAllValues();
                route = (SIP_Uri)routes[routes.Length - 1].Address.Uri;
                request.Route.RemoveLastValue();
            }

            // Check if Route header field indicates this proxy.
            if(request.Route.GetAllValues().Length > 0){
                route = (SIP_Uri)request.Route.GetTopMostValue().Address.Uri;

                // We consider loose-route always ours, because otherwise this message never reach here.
                if(route.Param_Lr){
                    request.Route.RemoveTopMostValue();
                }
                // It's our route, remove it.
                else if(IsLocalRoute(route)){
                    request.Route.RemoveTopMostValue();
                }
            }

            #endregion

            #region REGISTER request processing
                       
            if(e.Request.RequestLine.Method == SIP_Methods.REGISTER){
                SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri;

                // REGISTER is meant for us.
                if(this.OnIsLocalUri(requestUri.Host)){
                    if((m_ProxyMode & SIP_ProxyMode.Registrar) != 0){
                        m_pRegistrar.Register(e);

                        return;
                    }
                    else{
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x405_Method_Not_Allowed,e.Request));

                        return;
                    }
                }
                // Forward REGISTER.
                // else{
            }

            #endregion

            #region 3. Determine target(s) for the request (Section 16.5)

            /* 3. Determine target(s) for the request (Section 16.5)
                    Next, the proxy calculates the target(s) of the request.  The set of
                    targets will either be predetermined by the contents of the request
                    or will be obtained from an abstract location service.  Each target
                    in the set is represented as a URI.
             
                    If the domain of the Request-URI indicates a domain this element is
                    not responsible for, the Request-URI MUST be placed into the target
                    set as the only target, and the element MUST proceed to the task of
                    Request Forwarding (Section 16.6).
              
                    If the target set for the request has not been predetermined as
                    described above, this implies that the element is responsible for the
                    domain in the Request-URI, and the element MAY use whatever mechanism
                    it desires to determine where to send the request.  Any of these
                    mechanisms can be modeled as accessing an abstract Location Service.
                    This may consist of obtaining information from a location service
                    created by a SIP Registrar, reading a database, consulting a presence
                    server, utilizing other protocols, or simply performing an
                    algorithmic substitution on the Request-URI.  When accessing the
                    location service constructed by a registrar, the Request-URI MUST
                    first be canonicalized as described in Section 10.3 before being used
                    as an index.  The output of these mechanisms is used to construct the
                    target set.
            */
            
            // Non-SIP
            // Foreign SIP
            // Local SIP

            if(e.Request.RequestLine.Uri is SIP_Uri){                        
                SIP_Uri requestUri = (SIP_Uri)e.Request.RequestLine.Uri;

                // Proxy is not responsible for the domain in the Request-URI.
                if(!this.OnIsLocalUri(requestUri.Host)){
                    /* NAT traversal.
                        When we do record routing, store request sender flow info and request target flow info.
                        Now the tricky part, how proxy later which flow is target (because both sides can send requests).
                          Sender-flow will store from-tag to flow and target-flow will store flowID only (Because we don't know to-tag).
                          Later if request to-tag matches(incoming request), use that flow, otherwise(outgoing request) other flow.
                 
                          flowInfo: sender-flow "/" target-flow
                              sender-flow = from-tag ":" flowID
                              target-flow = flowID                        
                    */

                    SIP_Flow targetFlow = null;
                    string flowInfo = (route != null && route.Parameters["flowInfo"] != null) ? route.Parameters["flowInfo"].Value : null;
                    if(flowInfo != null && request.To.Tag != null){
                        string flow1Tag = flowInfo.Substring(0,flowInfo.IndexOf(':'));
                        string flow1ID  = flowInfo.Substring(flowInfo.IndexOf(':') + 1,flowInfo.IndexOf('/') - flowInfo.IndexOf(':') - 1);
                        string flow2ID  = flowInfo.Substring(flowInfo.IndexOf('/') + 1);
                                             
                        if(flow1Tag == request.To.Tag){
                            targetFlow = m_pStack.TransportLayer.GetFlow(flow1ID);
                        }
                        else{;
                            targetFlow = m_pStack.TransportLayer.GetFlow(flow2ID);
                        }
                    }

                    requestContext.Targets.Add(new SIP_ProxyTarget(requestUri,targetFlow));
                }
                // Proxy is responsible for the domain in the Request-URI.
                else{
                    // Try to get AOR from registrar.
                    SIP_Registration registration = m_pRegistrar.GetRegistration(requestUri.Address);

                    // We have AOR specified in request-URI in registrar server.
                    if(registration != null){
                        // Add all AOR SIP contacts to target set.
                        foreach(SIP_RegistrationBinding binding in registration.Bindings){
                            if(binding.ContactURI is SIP_Uri && binding.TTL > 0){
                                requestContext.Targets.Add(new SIP_ProxyTarget((SIP_Uri)binding.ContactURI,binding.Flow));
                            }
                        }
                    }
                    // We don't have AOR specified in request-URI in registrar server.
                    else{
                        // If the Request-URI indicates a resource at this proxy that does not
                        // exist, the proxy MUST return a 404 (Not Found) response.                    
                        if(!this.OnAddressExists(requestUri.Address)){                        
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found,e.Request));
                            return;
                        }
                    }

                    // If the target set remains empty after applying all of the above, the proxy MUST return an error response, 
                    // which SHOULD be the 480 (Temporarily Unavailable) response.
                    if(requestContext.Targets.Count == 0){
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x480_Temporarily_Unavailable,e.Request));
                        return;
                    }
                }               
            }

            #endregion

            #region x. Custom handling

            // x.1 Process custom request handlers, if any.
            foreach(SIP_ProxyHandler handler in this.Handlers){
                try{
                    SIP_ProxyHandler h = handler;

                    // Reusing existing handler not allowed, create new instance of handler.
                    if(!handler.IsReusable){
                        h = (SIP_ProxyHandler)System.Activator.CreateInstance(handler.GetType());
                    }

                    if(h.ProcessRequest(requestContext)){
                        // Handler processed request, we are done.
                        return;
                    }
                }
                catch(Exception x){
                    m_pStack.OnError(x);
                }
            }

            // x.2 URI scheme.
            //  If no targets and request-URI non-SIP, reject request because custom handlers should have been handled it.
            if(requestContext.Targets.Count == 0 && !SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString())){
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme,e.Request));
                return;
            }

            #endregion

            #region 4. Forward the request (Section 16.6)

            #region Statefull

            if (statefull){
                SIP_ProxyContext proxyContext = this.CreateProxyContext(requestContext,e.ServerTransaction,request,addRecordRoute);
                proxyContext.Start();
            }

            #endregion

            #region Stateless

            else{
                /* RFC 3261 16.6 Request Forwarding.
                For each target, the proxy forwards the request following these steps:
                    1.  Make a copy of the received request
                    2.  Update the Request-URI
                    3.  Update the Max-Forwards header field
                    4.  Optionally add a Record-route header field value
                    5.  Optionally add additional header fields
                    6.  Postprocess routing information
                    7.  Determine the next-hop address, port, and transport
                    8.  Add a Via header field value
                    9.  Add a Content-Length header field if necessary
                    10. Forward the new request
                */

                /* RFC 3261 16.11 Stateless Proxy.                 
                    o  A stateless proxy MUST choose one and only one target from the target set. This choice 
                       MUST only rely on fields in the message and time-invariant properties of the server. In
                       particular, a retransmitted request MUST be forwarded to the same destination each time 
                       it is processed. Furthermore, CANCEL and non-Routed ACK requests MUST generate the same
                       choice as their associated INVITE.
                 
                    However, a stateless proxy cannot simply use a random number generator to compute
                    the first component of the branch ID, as described in Section 16.6 bullet 8.
                    This is because retransmissions of a request need to have the same value, and 
                    a stateless proxy cannot tell a retransmission from the original request.
                
                    We just use: "z9hG4bK-" + md5(topmost branch)                
                */

                bool      isStrictRoute = false;
                SIP_Hop[] hops          = null;
                                
                #region 1.  Make a copy of the received request

                SIP_Request forwardRequest = request.Copy();

                #endregion

                #region 2.  Update the Request-URI

                forwardRequest.RequestLine.Uri = requestContext.Targets[0].TargetUri;

                #endregion

                #region 3.  Update the Max-Forwards header field

                forwardRequest.MaxForwards--;

                #endregion

                #region 4.  Optionally add a Record-route header field value

                #endregion

                #region 5.  Optionally add additional header fields

                #endregion

                #region 6.  Postprocess routing information

                /* 6. Postprocess routing information.
             
                    If the copy contains a Route header field, the proxy MUST inspect the URI in its first value.  
                    If that URI does not contain an lr parameter, the proxy MUST modify the copy as follows:             
                        - The proxy MUST place the Request-URI into the Route header
                          field as the last value.
             
                        - The proxy MUST then place the first Route header field value
                          into the Request-URI and remove that value from the Route header field.
                */
                if(forwardRequest.Route.GetAllValues().Length > 0 && !forwardRequest.Route.GetTopMostValue().Parameters.Contains("lr")){
                    forwardRequest.Route.Add(forwardRequest.RequestLine.Uri.ToString());

                    forwardRequest.RequestLine.Uri = SIP_Utils.UriToRequestUri(forwardRequest.Route.GetTopMostValue().Address.Uri);
                    forwardRequest.Route.RemoveTopMostValue();

                    isStrictRoute = true;
                }

                #endregion

                #region 7.  Determine the next-hop address, port, and transport

                /* 7. Determine the next-hop address, port, and transport.
                      The proxy MAY have a local policy to send the request to a
                      specific IP address, port, and transport, independent of the
                      values of the Route and Request-URI.  Such a policy MUST NOT be
                      used if the proxy is not certain that the IP address, port, and
                      transport correspond to a server that is a loose router.
                      However, this mechanism for sending the request through a
                      specific next hop is NOT RECOMMENDED; instead a Route header
                      field should be used for that purpose as described above.
             
                      In the absence of such an overriding mechanism, the proxy
                      applies the procedures listed in [4] as follows to determine
                      where to send the request.  If the proxy has reformatted the
                      request to send to a strict-routing element as described in
                      step 6 above, the proxy MUST apply those procedures to the
                      Request-URI of the request.  Otherwise, the proxy MUST apply
                      the procedures to the first value in the Route header field, if
                      present, else the Request-URI.  The procedures will produce an
                      ordered set of (address, port, transport) tuples.
                      Independently of which URI is being used as input to the
                      procedures of [4], if the Request-URI specifies a SIPS
                      resource, the proxy MUST follow the procedures of [4] as if the
                      input URI were a SIPS URI.

                      As described in [4], the proxy MUST attempt to deliver the
                      message to the first tuple in that set, and proceed through the
                      set in order until the delivery attempt succeeds.

                      For each tuple attempted, the proxy MUST format the message as
                      appropriate for the tuple and send the request using a new
                      client transaction as detailed in steps 8 through 10.
             
                      Since each attempt uses a new client transaction, it represents
                      a new branch.  Thus, the branch parameter provided with the Via
                      header field inserted in step 8 MUST be different for each
                      attempt.

                      If the client transaction reports failure to send the request
                      or a timeout from its state machine, the proxy continues to the
                      next address in that ordered set.  If the ordered set is
                      exhausted, the request cannot be forwarded to this element in
                      the target set.  The proxy does not need to place anything in
                      the response context, but otherwise acts as if this element of
                      the target set returned a 408 (Request Timeout) final response.
                */            
                SIP_Uri uri = null;
                if(isStrictRoute){
                    uri = (SIP_Uri)forwardRequest.RequestLine.Uri;
                }
                else if(forwardRequest.Route.GetTopMostValue() != null){
                    uri = (SIP_Uri)forwardRequest.Route.GetTopMostValue().Address.Uri;
                }
                else{
                    uri = (SIP_Uri)forwardRequest.RequestLine.Uri;
                }
                
                hops = m_pStack.GetHops(uri,forwardRequest.ToByteData().Length,((SIP_Uri)forwardRequest.RequestLine.Uri).IsSecure);

                if(hops.Length == 0){
                    if(forwardRequest.RequestLine.Method != SIP_Methods.ACK){
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x503_Service_Unavailable + ": No hop(s) for target.",forwardRequest));
                    }

                    return;
                }
                
                #endregion

                #region 8.  Add a Via header field value
                                
                forwardRequest.Via.AddToTop("SIP/2.0/transport-tl-addign sentBy-tl-assign-it;branch=z9hG4bK-" + Net_Utils.ComputeMd5(request.Via.GetTopMostValue().Branch,true));
                
                // Add 'flowID' what received request, you should use the same flow to send response back.
                // For more info see RFC 3261 18.2.2.
                forwardRequest.Via.GetTopMostValue().Parameters.Add("flowID",request.Flow.ID);

                #endregion

                #region 9.  Add a Content-Length header field if necessary

                // Skip, our SIP_Message class is smart and do it when ever it's needed.

                #endregion

                #region 10. Forward the new request

                try{
                    try{
                        if(requestContext.Targets[0].Flow != null){
                            m_pStack.TransportLayer.SendRequest(requestContext.Targets[0].Flow,request);
                            
                            return;
                        }
                    }
                    catch{
                        m_pStack.TransportLayer.SendRequest(request,null,hops[0]);
                    }
                }
                catch(SIP_TransportException x){
                    string dummy = x.Message;

                    if(forwardRequest.RequestLine.Method != SIP_Methods.ACK){
                        /* RFC 3261 16.9 Handling Transport Errors
                            If the transport layer notifies a proxy of an error when it tries to
                            forward a request (see Section 18.4), the proxy MUST behave as if the
                            forwarded request received a 503 (Service Unavailable) response.
                        */
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x503_Service_Unavailable + ": Transport error.",forwardRequest));
                    }
                }

                #endregion

            }

            #endregion

            #endregion
        }
Ejemplo n.º 13
0
        /// <summary>
        /// This method is called when new request is received.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        private void OnRequestReceived(SIP_RequestReceivedEventArgs e)
        {            
            SIP_Request request = e.Request;
            try{  

                #region Statefull

                // Statefull
                if((m_ProxyMode & SIP_ProxyMode.Statefull) != 0){
                    // Statefull proxy is transaction statefull proxy only, 
                    // what don't create dialogs and keep dialog state.

                    /* RFC 3261 16.10.
                        StateFull proxy:
	                        If a matching response context is found, the element MUST
	                        immediately return a 200 (OK) response to the CANCEL request. 

	                        If a response context is not found, the element does not have any
	                        knowledge of the request to apply the CANCEL to.  It MUST statelessly
	                        forward the CANCEL request (it may have statelessly forwarded the
	                        associated request previously).
                    */
                    if(e.Request.RequestLine.Method == SIP_Methods.CANCEL){
                        // Don't do server transaction before we get CANCEL matching transaction.
                        SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);
                        if(trToCancel != null){
                            trToCancel.Cancel();
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok,request));
                        }
                        else{
                            ForwardRequest(false,e,true);
                        }
                    }
                    // ACK never creates transaction, it's always passed directly to transport layer.
                    else if(e.Request.RequestLine.Method == SIP_Methods.ACK){
                        ForwardRequest(false,e,true);
                    }
                    else{
                        ForwardRequest(true,e,true);
                    }
                }

                #endregion
                
                #region B2BUA
                
                // B2BUA
                else if((m_ProxyMode & SIP_ProxyMode.B2BUA) != 0){
                    m_pB2BUA.OnRequestReceived(e);
                }

                #endregion

                #region Stateless

                // Stateless
                else if((m_ProxyMode & SIP_ProxyMode.Stateless) != 0){
                    // Stateless proxy don't do transaction, just forwards all.
                    ForwardRequest(false,e,true);
                }

                #endregion

                #region Proxy won't accept command
                
                else{
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented,request));
                }

                #endregion

            }
            catch(Exception x){
                try{
                    m_pStack.TransportLayer.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x.Message,e.Request));
                }
                catch{
                    // Skip transport layer exception if send fails.
                }

                // Don't raise OnError for transport errors.
                if(!(x is SIP_TransportException)){
                    m_pStack.OnError(x);
                }
            }            
        }
Ejemplo n.º 14
0
 /// <summary>
 /// This method is called when SIP stack receives new request.
 /// </summary>
 /// <param name="sender">Sender.</param>
 /// <param name="e">Event data.</param>
 private void m_pStack_RequestReceived(object sender,SIP_RequestReceivedEventArgs e)
 {
     OnRequestReceived(e);
 }
Ejemplo n.º 15
0
        /// <summary>
        /// This method is called when new request is received.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        internal void OnRequestReceived(SIP_RequestReceivedEventArgs e)
        {
            SIP_Request request = e.Request;

            if(request.RequestLine.Method == SIP_Methods.CANCEL){
                /* RFC 3261 9.2.
                    If the UAS did not find a matching transaction for the CANCEL
                    according to the procedure above, it SHOULD respond to the CANCEL
                    with a 481 (Call Leg/Transaction Does Not Exist).
                  
                    Regardless of the method of the original request, as long as the
                    CANCEL matched an existing transaction, the UAS answers the CANCEL
                    request itself with a 200 (OK) response.
                */

                SIP_ServerTransaction trToCancel = m_pProxy.Stack.TransactionLayer.MatchCancelToTransaction(e.Request);
                if(trToCancel != null){
                    trToCancel.Cancel();
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x200_Ok));
                }
                else{
                    //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
                }
            }
            // We never should ge BYE here, because transport layer must match it to dialog.
            else if(request.RequestLine.Method == SIP_Methods.BYE){
                /* RFC 3261 15.1.2.
                    If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481
                    (Call/Transaction Does Not Exist) response and pass that to the server transaction.
                */
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            // We never should ge ACK here, because transport layer must match it to dialog.
            else if(request.RequestLine.Method == SIP_Methods.ACK){
                // ACK is response less request, so we may not return error to it.
            }
            // B2BUA must respond to OPTIONS request, not to forward it.
            else if(request.RequestLine.Method == SIP_Methods.OPTIONS){     /*          
                SIP_Response response = e.Request.CreateResponse(SIP_ResponseCodes.x200_Ok);                
                // Add Allow to non ACK response.
                if(e.Request.RequestLine.Method != SIP_Methods.ACK){
                    response.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK,MESSAGE,UPDATE");
                }
                // Add Supported to 2xx non ACK response.
                if(response.StatusCodeType == SIP_StatusCodeType.Success && e.Request.RequestLine.Method != SIP_Methods.ACK){
                    response.Supported.Add("100rel,timer");
                }
                e.ServerTransaction.SendResponse(response);*/
            }
            // We never should get PRACK here, because transport layer must match it to dialog.
            else if(request.RequestLine.Method == SIP_Methods.PRACK){
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            // We never should get UPDATE here, because transport layer must match it to dialog.
            else if(request.RequestLine.Method == SIP_Methods.UPDATE){
                //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist));
            }
            else{
                /* draft-marjou-sipping-b2bua-00 4.1.3.
                    When the UAS of the B2BUA receives an upstream SIP request, its
                    associated UAC generates a new downstream SIP request with its new
                    Via, Max-Forwards, Call-Id, CSeq, and Contact header fields. Route
                    header fields of the upstream request are copied in the downstream
                    request, except the first Route header if it is under the
                    responsibility of the B2BUA.  Record-Route header fields of the
                    upstream request are not copied in the new downstream request, as
                    Record-Route is only meaningful for the upstream dialog.  The UAC
                    SHOULD copy other header fields and body from the upstream request
                    into this downstream request before sending it.
                */

                SIP_Request b2buaRequest = e.Request.Copy();
                b2buaRequest.Via.RemoveAll();
                b2buaRequest.MaxForwards = 70;                
                b2buaRequest.CallID = SIP_t_CallID.CreateCallID().CallID;
                b2buaRequest.CSeq.SequenceNumber = 1;
                b2buaRequest.Contact.RemoveAll();
                // b2buaRequest.Contact.Add(m_pProxy.CreateContact(b2buaRequest.To.Address).ToStringValue());
                if(b2buaRequest.Route.Count > 0 && m_pProxy.IsLocalRoute(SIP_Uri.Parse(b2buaRequest.Route.GetTopMostValue().Address.Uri.ToString()))){
                    b2buaRequest.Route.RemoveTopMostValue();
                }                
                b2buaRequest.RecordRoute.RemoveAll();

                // Remove our Authorization header if it's there.
                foreach(SIP_SingleValueHF<SIP_t_Credentials> header in b2buaRequest.ProxyAuthorization.HeaderFields){
                    try{
                        Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData,b2buaRequest.RequestLine.Method);
                        if(m_pProxy.Stack.Realm == digest.Realm){
                            b2buaRequest.ProxyAuthorization.Remove(header);
                        }
                    }
                    catch{
                        // We don't care errors here. This can happen if remote server xxx auth method here and
                        // we don't know how to parse it, so we leave it as is.
                    }
                }

                //--- Add/replace default fields. ------------------------------------------
                b2buaRequest.Allow.RemoveAll();
                b2buaRequest.Supported.RemoveAll();
                // Accept to non ACK,BYE request.
                if(request.RequestLine.Method != SIP_Methods.ACK && request.RequestLine.Method != SIP_Methods.BYE){
                    b2buaRequest.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK");
                }
                // Supported to non ACK request. 
                if(request.RequestLine.Method != SIP_Methods.ACK){
                    b2buaRequest.Supported.Add("100rel,timer");
                }
                // Remove Require: header.
                b2buaRequest.Require.RemoveAll();

                // RFC 4028 7.4. For re-INVITE and UPDATE we need to add Session-Expires and Min-SE: headers.
                if(request.RequestLine.Method == SIP_Methods.INVITE || request.RequestLine.Method == SIP_Methods.UPDATE){
                    b2buaRequest.SessionExpires = new SIP_t_SessionExpires(m_pProxy.Stack.SessionExpries,"uac");
                    b2buaRequest.MinSE = new SIP_t_MinSE(m_pProxy.Stack.MinimumSessionExpries);
                }

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

            if(base.ProcessRequest(e)){
                return true;
            }

            if(e.Request.RequestLine.Method == SIP_Methods.ACK){
                if(this.State == SIP_DialogState.Early){
                    this.SetState(SIP_DialogState.Confirmed,true);
                }
                else if(this.State == SIP_DialogState.Terminating){
                    this.SetState(SIP_DialogState.Confirmed,false);

                    Terminate(m_TerminateReason,true);
                }
            }
            else if(e.Request.RequestLine.Method == SIP_Methods.BYE){
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x200_Ok,e.Request));

                m_IsTerminatedByRemoteParty = true;
                OnTerminatedByRemoteParty(e);
                SetState(SIP_DialogState.Terminated,true);   
             
                return true;
            }
            else if(e.Request.RequestLine.Method == SIP_Methods.INVITE){
                /* RFC 3261 14.2.
                    A UAS that receives a second INVITE before it sends the final
                    response to a first INVITE with a lower CSeq sequence number on the
                    same dialog MUST return a 500 (Server Internal Error) response to the
                    second INVITE and MUST include a Retry-After header field with a
                    randomly chosen value of between 0 and 10 seconds.
                */
                // Dialog base class will handle this case.
                /*
                foreach(SIP_Transaction tr in this.Transactions){
                    if(tr is SIP_ServerTransaction && (tr.State == SIP_TransactionState.Calling || tr.State == SIP_TransactionState.Proceeding)){
                        if(e.Request.CSeq.SequenceNumber < tr.Request.CSeq.SequenceNumber){
                            SIP_Response response = this.Stack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": INVITE with higher Seq sequence number is progress.",e.Request);
                            response.RetryAfter = new SIP_t_RetryAfter((new Random()).Next(1,10).ToString());
                            e.ServerTransaction.SendResponse(response);

                            return true;
                        }
         
                        break;
                    }
                }*/

                /* RFC 3261 14.2.
                    A UAS that receives an INVITE on a dialog while an INVITE it had sent
                    on that dialog is in progress MUST return a 491 (Request Pending)
                    response to the received INVITE.
                */
                if(this.HasPendingInvite){
                    e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending,e.Request));

                    return true;
                }
            }
            // RFC 5057 5.6. Refusing New Usages. Decline(603 Decline) new dialog usages.
            else if(SIP_Utils.MethodCanEstablishDialog(e.Request.RequestLine.Method)){
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x603_Decline + " : New dialog usages in dialog not allowed (RFC 5057).",e.Request));

                return true;
            }

            return false;
        }
Ejemplo n.º 17
0
 /// <summary>
 /// Handles SUBSCRIBE method.
 /// </summary>
 /// <param name="e">Request event arguments.</param>
 internal void Subscribe(SIP_RequestReceivedEventArgs e)
 {
 }
Ejemplo n.º 18
0
        private void m_pStack_RequestReceived(object sender, SIP_RequestReceivedEventArgs e)
        {
            try
            {
                #region CANCEL

                if (e.Request.RequestLine.Method == SIP_Methods.CANCEL)
                {
                    SIP_ServerTransaction trToCancel = m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request);

                    if (trToCancel != null)
                    {
                        trToCancel.Cancel();
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request));
                    }
                    else
                    {
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request));
                    }
                }

                #endregion

                #region BYE

                else if (e.Request.RequestLine.Method == SIP_Methods.BYE)
                {
                    // Currently we match BYE to dialog and it processes it,
                    // so BYE what reaches here doesnt match to any dialog.

                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, e.Request));
                }

                #endregion

                #region INVITE

                else if (e.Request.RequestLine.Method == SIP_Methods.INVITE)
                {

                    #region Incoming call

                    if (e.Dialog == null)
                    {
                        #region Validate incoming call

                        // We don't accept more than 1 call at time.
                        if (connected || m_pCall != null)
                        {
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x600_Busy_Everywhere, e.Request));
                            return;
                        }

                        // We don't accept SDP offerless calls.
                        if (e.Request.ContentType == null || e.Request.ContentType.ToLower().IndexOf("application/sdp") == -1)
                        {
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x606_Not_Acceptable + 
                                ": We don't accpet SDP offerless calls.", e.Request));
                            return;
                        }

                        SDP_Message sdpOffer = SDP_Message.Parse(Encoding.UTF8.GetString(e.Request.Data));

                        // Check if we can accept any media stream.
                        bool canAccept = false;
                        foreach (SDP_MediaDescription media in sdpOffer.MediaDescriptions)
                        {
                            if (CanSupportMedia(media))
                            {
                                canAccept = true;

                                break;
                            }
                        }

                        if (!canAccept)
                        {
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x606_Not_Acceptable, e.Request));

                            return;
                        }

                        #endregion

                        // Send ringing to remote-party.
                        SIP_Response responseRinging = m_pStack.CreateResponse(SIP_ResponseCodes.x180_Ringing, e.Request, e.Flow);
                        responseRinging.To.Tag = SIP_Utils.CreateTag();
                        e.ServerTransaction.SendResponse(responseRinging);

                        SIP_Dialog_Invite dialog = (SIP_Dialog_Invite)m_pStack.TransactionLayer.GetOrCreateDialog(e.ServerTransaction, responseRinging);

                        // We need invoke here, otherwise we block SIP stack RequestReceived event while incoming call UI showed.
                        this.BeginInvoke(new MethodInvoker(delegate()
                        {
                            try
                            {
                                //m_pPlayer.Play(ResManager.GetStream("ringing.wav"), 20);

                                // Call accepted.
                                RTP_MultimediaSession rtpMultimediaSession = new RTP_MultimediaSession(RTP_Utils.GenerateCNAME());

                                // Build local SDP template
                                SDP_Message sdpLocal = new SDP_Message();
                                sdpLocal.Version = "0";
                                sdpLocal.Origin = new SDP_Origin("-", sdpLocal.GetHashCode(), 1, "IN", "IP4", 
                                    System.Net.Dns.GetHostAddresses("")[0].ToString());
                                sdpLocal.SessionName = "SIP Call";
                                sdpLocal.Times.Add(new SDP_Time(0, 0));

                                ProcessMediaOffer(dialog, e.ServerTransaction, rtpMultimediaSession, sdpOffer, sdpLocal);

                                // Create call.
                                m_pCall = new SIP_Call(m_pStack, dialog, rtpMultimediaSession, sdpLocal);
                                m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged);
                                m_pCall_StateChanged(m_pCall, new EventArgs());

                                if (m_IsDebug)
                                {
                                    wfrm_RTP_Debug rtpDebug = new wfrm_RTP_Debug(m_pCall.RtpMultimediaSession);
                                    rtpDebug.Show();
                                }

                                connected = true;

                            }
                            catch (Exception x1)
                            {
                                MessageBox.Show("Error: " + x1.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                connected = false;
                                m_pConnect.Image = global::PowerSDR.Properties.Resources.call;
                            }
                        }));
                    }

                    #endregion

                    #region Re-INVITE

                    else
                    {
                        try
                        {
                            // Remote-party provided SDP offer.
                            if (e.Request.ContentType != null && e.Request.ContentType.ToLower().IndexOf("application/sdp") > -1)
                            {
                                ProcessMediaOffer(m_pCall.Dialog, e.ServerTransaction, m_pCall.RtpMultimediaSession, 
                                    SDP_Message.Parse(Encoding.UTF8.GetString(e.Request.Data)), m_pCall.LocalSDP);

                                // Remote-party is holding a call.
                                if (IsRemotePartyHolding(SDP_Message.Parse(Encoding.UTF8.GetString(e.Request.Data))))
                                {
                                    // We need invoke here, we are running on thread pool thread.
                                    this.BeginInvoke(new MethodInvoker(delegate()
                                    {
                                        m_pStatusBar.Items[0].Text = "Remote party holding a call";
                                    }));

                                    //m_pPlayer.Play(ResManager.GetStream("onhold.wav"), 20);
                                }
                                // Call is active.
                                else
                                {
                                    // We need invoke here, we are running on thread pool thread.
                                    this.BeginInvoke(new MethodInvoker(delegate()
                                    {
                                        m_pStatusBar.Items[0].Text = "Call established";
                                    }));

                                    m_pPlayer.Stop();
                                }
                            }
                            // Error: Re-INVITE can't be SDP offerless.
                            else
                            {
                                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + 
                                    ": Re-INVITE must contain SDP offer.", e.Request));
                            }
                        }
                        catch (Exception x1)
                        {
                            e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + 
                                ": " + x1.Message, e.Request));
                        }
                    }

                    #endregion
                }

                #endregion

                #region ACK

                else if (e.Request.RequestLine.Method == SIP_Methods.ACK)
                {
                    // Abandoned ACK, just skip it.
                }

                #endregion

                #region MESSAGE

                else if (e.Request.RequestLine.Method == SIP_Methods.MESSAGE)
                {
                    e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request));
                    byte[] msg = e.Request.Data;
                    ASCIIEncoding buffer = new ASCIIEncoding();
                    string data = buffer.GetString(msg);
                    string answer = "";

                    if (debug && !console.ConsoleClosing)
                        console.Invoke(new DebugCallbackFunction(console.DebugCallback), data);

                    if (op_mode == VoIP_mode.Server)
                        answer = console.CAT_server_socket.ProcessData(msg, msg.Length);
                    else
                    {
                        if (console.CAT_client_socket.ProcessData(msg, msg.Length, out answer))
                            SendMessage(answer, "CAT");
                    }
                }

                #endregion

                #region Other

                else
                {
                    // ACK is response less method.
                    if (e.Request.RequestLine.Method != SIP_Methods.ACK)
                    {
                        e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented, e.Request));
                    }
                }

                #endregion
            }
            catch
            {
                e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error, e.Request));
            }
        }
Ejemplo n.º 19
0
 /// <summary>
 /// Handles NOTIFY method.
 /// </summary>
 /// <param name="e">Request event arguments.</param>
 internal void Notify(SIP_RequestReceivedEventArgs e)
 {
 }
Ejemplo n.º 20
0
        /// <summary>
        /// Processes specified request through this dialog.
        /// </summary>
        /// <param name="e">Method arguments.</param>
        /// <returns>Returns true if this dialog processed specified request, otherwise false.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>e</b> is null reference.</exception>
        internal protected override bool ProcessRequest(SIP_RequestReceivedEventArgs e)
        {
            if (e == null)
            {
                throw new ArgumentNullException("e");
            }

            if (base.ProcessRequest(e))
            {
                return(true);
            }

            if (e.Request.RequestLine.Method == SIP_Methods.ACK)
            {
                if (this.State == SIP_DialogState.Early)
                {
                    this.SetState(SIP_DialogState.Confirmed, true);
                }
                else if (this.State == SIP_DialogState.Terminating)
                {
                    this.SetState(SIP_DialogState.Confirmed, false);

                    Terminate(m_TerminateReason, true);
                }
            }
            else if (e.Request.RequestLine.Method == SIP_Methods.BYE)
            {
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request));

                m_IsTerminatedByRemoteParty = true;
                OnTerminatedByRemoteParty(e);
                SetState(SIP_DialogState.Terminated, true);

                return(true);
            }
            else if (e.Request.RequestLine.Method == SIP_Methods.INVITE)
            {
                /* RFC 3261 14.2.
                 *  A UAS that receives a second INVITE before it sends the final
                 *  response to a first INVITE with a lower CSeq sequence number on the
                 *  same dialog MUST return a 500 (Server Internal Error) response to the
                 *  second INVITE and MUST include a Retry-After header field with a
                 *  randomly chosen value of between 0 and 10 seconds.
                 */
                // Dialog base class will handle this case.

                /*
                 * foreach(SIP_Transaction tr in this.Transactions){
                 *  if(tr is SIP_ServerTransaction && (tr.State == SIP_TransactionState.Calling || tr.State == SIP_TransactionState.Proceeding)){
                 *      if(e.Request.CSeq.SequenceNumber < tr.Request.CSeq.SequenceNumber){
                 *          SIP_Response response = this.Stack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": INVITE with higher Seq sequence number is progress.",e.Request);
                 *          response.RetryAfter = new SIP_t_RetryAfter((new Random()).Next(1,10).ToString());
                 *          e.ServerTransaction.SendResponse(response);
                 *
                 *          return true;
                 *      }
                 *
                 *      break;
                 *  }
                 * }*/

                /* RFC 3261 14.2.
                 *  A UAS that receives an INVITE on a dialog while an INVITE it had sent
                 *  on that dialog is in progress MUST return a 491 (Request Pending)
                 *  response to the received INVITE.
                 */
                if (this.HasPendingInvite)
                {
                    e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending, e.Request));

                    return(true);
                }
            }
            // RFC 5057 5.6. Refusing New Usages. Decline(603 Decline) new dialog usages.
            else if (SIP_Utils.MethodCanEstablishDialog(e.Request.RequestLine.Method))
            {
                e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x603_Decline + " : New dialog usages in dialog not allowed (RFC 5057).", e.Request));

                return(true);
            }

            return(false);
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Handles REGISTER method.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        internal void Register(SIP_RequestReceivedEventArgs e)
        {
            /* RFC 3261 10.3 Processing REGISTER Requests.
                1. The registrar inspects the Request-URI to determine whether it
                   has access to bindings for the domain identified in the
                   Request-URI.  If not, and if the server also acts as a proxy
                   server, the server SHOULD forward the request to the addressed
                   domain, following the general behavior for proxying messages
                   described in Section 16.
                      
                2. To guarantee that the registrar supports any necessary extensions, 
                   the registrar MUST process the Require header field.
                     
                3. A registrar SHOULD authenticate the UAC.
                     
                4. The registrar SHOULD determine if the authenticated user is
                   authorized to modify registrations for this address-of-record.
                   For example, a registrar might consult an authorization
                   database that maps user names to a list of addresses-of-record
                   for which that user has authorization to modify bindings.  If
                   the authenticated user is not authorized to modify bindings,
                   the registrar MUST return a 403 (Forbidden) and skip the
                   remaining steps.
                     
                5. The registrar extracts the address-of-record from the To header
                   field of the request.  If the address-of-record is not valid
                   for the domain in the Request-URI, the registrar MUST send a
                   404 (Not Found) response and skip the remaining steps.  The URI
                   MUST then be converted to a canonical form.  To do that, all
                   URI parameters MUST be removed (including the user-param), and
                   any escaped characters MUST be converted to their unescaped
                   form.  The result serves as an index into the list of bindings.
                                 
                6. The registrar checks whether the request contains the Contact
                   header field.  If not, it skips to the last step.  If the
                   Contact header field is present, the registrar checks if there
                   is one Contact field value that contains the special value "*"
                   and an Expires field.  If the request has additional Contact
                   fields or an expiration time other than zero, the request is
                   invalid, and the server MUST return a 400 (Invalid Request) and
                   skip the remaining steps.  If not, the registrar checks whether
                   the Call-ID agrees with the value stored for each binding.  If
                   not, it MUST remove the binding.  If it does agree, it MUST
                   remove the binding only if the CSeq in the request is higher
                   than the value stored for that binding.  Otherwise, the update
                   MUST be aborted and the request fails.
                     
                7. The registrar now processes each contact address in the Contact
                   header field in turn.  For each address, it determines the
                   expiration interval as follows:

                     -  If the field value has an "expires" parameter, that value
                        MUST be taken as the requested expiration.

                     -  If there is no such parameter, but the request has an
                        Expires header field, that value MUST be taken as the requested expiration.

                     -  If there is neither, a locally-configured default value MUST
                        be taken as the requested expiration.

                   The registrar MAY choose an expiration less than the requested
                   expiration interval.  If and only if the requested expiration
                   interval is greater than zero AND smaller than one hour AND
                   less than a registrar-configured minimum, the registrar MAY
                   reject the registration with a response of 423 (Interval Too
                   Brief).  This response MUST contain a Min-Expires header field
                   that states the minimum expiration interval the registrar is
                   willing to honor.  It then skips the remaining steps.
              
                   For each address, the registrar then searches the list of
                   current bindings using the URI comparison rules.  If the
                   binding does not exist, it is tentatively added.  If the
                   binding does exist, the registrar checks the Call-ID value.  If
                   the Call-ID value in the existing binding differs from the
                   Call-ID value in the request, the binding MUST be removed if
                   the expiration time is zero and updated otherwise.  If they are
                   the same, the registrar compares the CSeq value.  If the value
                   is higher than that of the existing binding, it MUST update or
                   remove the binding as above.  If not, the update MUST be
                   aborted and the request fails.
    
                   This algorithm ensures that out-of-order requests from the same
                   UA are ignored.

                   Each binding record records the Call-ID and CSeq values from
                   the request.

                   The binding updates MUST be committed (that is, made visible to
                   the proxy or redirect server) if and only if all binding
                   updates and additions succeed.  If any one of them fails (for
                   example, because the back-end database commit failed), the
                   request MUST fail with a 500 (Server Error) response and all
                   tentative binding updates MUST be removed.
                     
                8. The registrar returns a 200 (OK) response.  The response MUST
                   contain Contact header field values enumerating all current
                   bindings.  Each Contact value MUST feature an "expires"
                   parameter indicating its expiration interval chosen by the
                   registrar.  The response SHOULD include a Date header field.
            */

            SIP_ServerTransaction transaction = e.ServerTransaction;
            SIP_Request           request     = e.Request;
            SIP_Uri               to          = null;
            string                userName    = "";


            // Probably we need to do validate in SIP stack.

            #region Validate request

            if(SIP_Utils.IsSipOrSipsUri(request.To.Address.Uri.ToString())){
                to = (SIP_Uri)request.To.Address.Uri;
            }
            else{
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": To: value must be SIP or SIPS URI.",request));
                return;
            }

            #endregion


            #region 1. Check if we are responsible for Request-URI domain

            // if(m_pProxy.OnIsLocalUri(e.Request.Uri)){
            // }
            // TODO:

            #endregion

            #region 2. Check that all required extentions supported

            #endregion

            #region 3. Authenticate request

            if(!m_pProxy.AuthenticateRequest(e,out userName)){
                return;
            }

            #endregion

            #region 4. Check if user user is authorized to modify registrations

            // We do this in next step(5.).

            #endregion

            #region 5. Check if address of record exists
            
            if(!m_pProxy.OnAddressExists(to.Address)){
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found,request));
                return;
            }
            else if(!OnCanRegister(userName,to.Address)){
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x403_Forbidden,request));
                return;
            }

            #endregion

            #region 6. Process * Contact if exists

            // Check if we have star contact.
            SIP_t_ContactParam starContact = null;
            foreach(SIP_t_ContactParam c in request.Contact.GetAllValues()){
                if(c.IsStarContact){
                    starContact = c;
                    break;
                }
            }

            // We have star contact.
            if(starContact != null){
                if(request.Contact.GetAllValues().Length > 1){
                    transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": RFC 3261 10.3.6 -> If star(*) present, only 1 contact allowed.",request));
                    return;
                }
                else if(starContact.Expires != 0){
                    transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": RFC 3261 10.3.6 -> star(*) contact parameter 'expires' value must be always '0'.",request));
                    return;
                }

                // Remove bindings.
                SIP_Registration reg = m_pRegistrations[to.Address];
                if(reg != null){
                    foreach(SIP_RegistrationBinding b in reg.Bindings){
                        if(request.CallID != b.CallID || request.CSeq.SequenceNumber > b.CSeqNo){
                            b.Remove();
                        }
                    }
                }
            }

            #endregion

            #region 7. Process Contact values

            if(starContact == null){
                bool             newReg = false;
                SIP_Registration reg    = m_pRegistrations[to.Address];
                if(reg == null){
                    newReg = true;
                    reg    = new SIP_Registration(userName,to.Address);
                    m_pRegistrations.Add(reg);
                }

                // We may do updates in batch only.
                // We just validate all values then do update(this ensures that update doesn't fail).

                // Check expires and CSeq.
                foreach(SIP_t_ContactParam c in request.Contact.GetAllValues()){
                    if(c.Expires == -1){
                        c.Expires = request.Expires;
                    }
                    if(c.Expires == -1){
                        c.Expires = m_pProxy.Stack.MinimumExpireTime;
                    }
                    // We must accept 0 values - means remove contact.
                    if(c.Expires != 0 && c.Expires < m_pProxy.Stack.MinimumExpireTime){
                        SIP_Response resp = m_pStack.CreateResponse(SIP_ResponseCodes.x423_Interval_Too_Brief,request);
                        resp.MinExpires = m_pProxy.Stack.MinimumExpireTime;
                        transaction.SendResponse(resp);
                        return;
                    }

                    SIP_RegistrationBinding currentBinding = reg.GetBinding(c.Address.Uri);
                    if(currentBinding != null && currentBinding.CallID == request.CallID && request.CSeq.SequenceNumber < currentBinding.CSeqNo){
                        transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ": CSeq value out of order.",request));
                        return;
                    }                    
                }

                // Do binding updates.
                reg.AddOrUpdateBindings(e.ServerTransaction.Flow,request.CallID,request.CSeq.SequenceNumber,request.Contact.GetAllValues());

                // Raise AOR change events.
                if(newReg){
                    OnAorRegistered(reg);
                }
                else{
                    OnAorUpdated(reg);
                }
            }

            #endregion

            #region 8. Create 200 OK response and return all current bindings

            SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok,request);
            response.Date = DateTime.Now;
            SIP_Registration registration = m_pRegistrations[to.Address];
            if(registration != null){
                foreach(SIP_RegistrationBinding b in registration.Bindings){
                    // Don't list expired bindings what wait to be disposed.
                    if(b.TTL > 1){
                        response.Header.Add("Contact:",b.ToContactValue());
                    }
                }
            }
            // Add Authentication-Info:, then client knows next nonce.
            response.AuthenticationInfo.Add("qop=\"auth\",nextnonce=\"" + m_pStack.DigestNonceManager.CreateNonce() + "\"");
            transaction.SendResponse(response);

            #endregion
                        
        }
Ejemplo n.º 22
0
 /// <summary>
 /// Raises <b>RequestReceived</b> event.
 /// </summary>
 /// <param name="e">Event args.</param>
 private void OnRequestReceived(SIP_RequestReceivedEventArgs e)
 {
     if(this.RequestReceived != null){
         this.RequestReceived(this,e);
     }
 }
Ejemplo n.º 23
0
 /// <summary>
 /// Raises <b>RequestReceived</b> event.
 /// </summary>
 /// <param name="request">SIP request.</param>
 protected void OnRequestReceived(SIP_RequestReceivedEventArgs request)
 {
     if(this.RequestReceived != null){
         this.RequestReceived(this,request);
     }
 }