/// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="sipStack">Reference to SIP stack.</param>
        /// <param name="request">SIP request to what caused to create client transaction.</param>
        /// <param name="destination">Remote destination info.</param>
        /// <param name="addVia">Specified if transaction adds new Via: header. If this value is false,
        /// then its user responsibility to add valid Via: header to <b>request</b> argument.</param>
        internal SIP_ClientTransaction(SIP_Stack sipStack,SIP_Request request,SIP_Destination destination,bool addVia)
        {
            m_pSipStack    = sipStack;
            m_pRequest     = request;
            m_pDestination = destination;

            m_pResponses = new List<SIP_Response>();

            // Add Via: header field.
            if(addVia){
                m_ID = SIP_t_ViaParm.CreateBranch();
                SIP_t_ViaParm via = new SIP_t_ViaParm();
                via.ProtocolName = "SIP";
                via.ProtocolVersion = "2.0";
                via.ProtocolTransport = m_pDestination.Transport;
                via.SentBy = "transport_layer_will_replace_it";
                via.Branch = m_ID;
                request.Via.AddToTop(via.ToStringValue());
            }
            // User provided Via:.
            else{
                // Validate Via:
                SIP_t_ViaParm via = request.Via.GetTopMostValue();
                if(via == null){
                    throw new ArgumentException("Via: header is missing !");
                }
                if(via.Branch == null){
                    throw new ArgumentException("Via: header 'branch' prameter is missing !");
                }

                m_ID = via.Branch;
            }
        }
예제 #2
0
        private void HandleAck(SIP_Dialog dialog, SIP_ClientTransaction transaction)
        {
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }

            /* RFC 3261 6.
                The ACK for a 2xx response to an INVITE request is a separate transaction.
              
               RFC 3261 13.2.2.4.
                The UAC core MUST generate an ACK request for each 2xx received from
                the transaction layer.  The header fields of the ACK are constructed
                in the same way as for any request sent within a dialog (see Section
                12) with the exception of the CSeq and the header fields related to
                authentication.  The sequence number of the CSeq header field MUST be
                the same as the INVITE being acknowledged, but the CSeq method MUST
                be ACK.  The ACK MUST contain the same credentials as the INVITE.  If
                the 2xx contains an offer (based on the rules above), the ACK MUST
                carry an answer in its body.
            */

            SIP_t_ViaParm via = new SIP_t_ViaParm();
            via.ProtocolName = "SIP";
            via.ProtocolVersion = "2.0";
            via.ProtocolTransport = transaction.Flow.Transport;
            via.SentBy = new HostEndPoint(transaction.Flow.LocalEP);
            via.Branch = SIP_t_ViaParm.CreateBranch();
            via.RPort = 0;

            SIP_Request ackRequest = dialog.CreateRequest(SIP_Methods.ACK);
            ackRequest.Via.AddToTop(via.ToStringValue());
            ackRequest.CSeq = new SIP_t_CSeq(transaction.Request.CSeq.SequenceNumber, SIP_Methods.ACK);
            // Authorization
            foreach (SIP_HeaderField h in transaction.Request.Authorization.HeaderFields)
            {
                ackRequest.Authorization.Add(h.Value);
            }
            // Proxy-Authorization 
            foreach (SIP_HeaderField h in transaction.Request.ProxyAuthorization.HeaderFields)
            {
                ackRequest.Authorization.Add(h.Value);
            }

            // Send ACK.
            SendAck(dialog, ackRequest);

            // Start receive 2xx retransmissions.
            transaction.ResponseReceived += delegate(object sender, SIP_ResponseReceivedEventArgs e)
            {
                if (dialog.State == SIP_DialogState.Disposed || dialog.State == SIP_DialogState.Terminated)
                {
                    return;
                }

                // Don't send ACK for forked 2xx, our sent BYE(to all early dialogs) or their early timer will kill these dialogs.
                // Send ACK only to our accepted dialog 2xx response retransmission.
                if (e.Response.From.Tag == ackRequest.From.Tag && e.Response.To.Tag == ackRequest.To.Tag)
                {
                    SendAck(dialog, ackRequest);
                }
            };
        }
        /// <summary>
        /// Creates new client transaction.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <param name="addVia">If true, transaction will add <b>Via:</b> header, otherwise it's user responsibility.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <returns>Returns created transaction.</returns>
        public SIP_ClientTransaction CreateClientTransaction(SIP_Flow flow,SIP_Request request,bool addVia)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            // Add Via:
            if(addVia){
                SIP_t_ViaParm via = new SIP_t_ViaParm();
                via.ProtocolName = "SIP";
                via.ProtocolVersion = "2.0";
                via.ProtocolTransport = flow.Transport;
                via.SentBy = new HostEndPoint("transport_layer_will_replace_it",-1);
                via.Branch = SIP_t_ViaParm.CreateBranch();
                via.RPort = 0;
                request.Via.AddToTop(via.ToStringValue());
            }

            lock(m_pClientTransactions){
                SIP_ClientTransaction transaction = new SIP_ClientTransaction(m_pStack,flow,request);
                m_pClientTransactions.Add(transaction.Key,transaction);
                transaction.StateChanged += new EventHandler(delegate(object s,EventArgs e){
                    if(transaction.State == SIP_TransactionState.Terminated){
                        lock(m_pClientTransactions){
                            m_pClientTransactions.Remove(transaction.Key);
                        }
                    }
                });

                return transaction;
            }
        }