/// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack,SIP_Flow flow,SIP_Request request,SIP_ServerTransaction transaction)
 {
     m_pStack       = stack;
     m_pFlow        = flow;
     m_pRequest     = request;
     m_pTransaction = transaction;
 }
        /// <summary>
        /// Creates new SIP server transaction for specified request.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns added server transaction.</returns>
        /// <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>
        public SIP_ServerTransaction CreateServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

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

                return(transaction);
            }
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="request">Request to forward.</param>
        /// <param name="forkingMode">Specifies how proxy context must handle forking.</param>
        /// <param name="noCancel">Specifies if proxy should not send Cancel to forked requests.</param>
        /// <param name="noRecurse">Specifies what proxy server does when it gets 3xx response. If true proxy will forward
        /// request to new specified address if false, proxy will return 3xx response to caller.</param>
        /// <param name="destinations">Possible destinations. NOTE: These values must be in priority order !</param>
        /// <exception cref="ArgumentNullException">Is raised when any of the reference type prameters is null.</exception>
        public SIP_ProxyContext(SIP_Stack stack,SIP_Request request,SIP_ForkingMode forkingMode,bool noCancel,bool noRecurse,SIP_Destination[] destinations)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(request == null){
                throw new ArgumentNullException("serverTransaction");
            }
            if(destinations == null){
                throw new ArgumentNullException("destinations");
            }

            m_pStack = stack;

            m_pServerTransaction = stack.TransactionLayer.CreateServerTransaction(request);
            m_pServerTransaction.CanCreateDialog = false;
            m_pServerTransaction.Canceled += new EventHandler(m_pServerTransaction_Canceled);
            m_pServerTransaction.Terminated += new EventHandler(m_pServerTransaction_Terminated);

            m_ForkingMode = forkingMode;
            m_NoCancel    = noCancel;
            m_NoRecurse   = noRecurse;

            m_pClientTransactions = new List<SIP_ClientTransaction>();
            m_pResponses          = new List<SIP_Response>();
            m_CreateTime          = DateTime.Now;

            // Queue destinations up, higest to lowest.
            m_pRemainingDestinations = new Queue<SIP_Destination>();
            foreach(SIP_Destination destination in destinations){
                m_pRemainingDestinations.Enqueue(destination);
            }
        }
        /// <summary>
        /// Creates new SIP client transaction for specified request.
        /// </summary>
        /// <param name="request">SIP request.</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 vlid Via: header to <b>request</b> argument.</param>
        /// <returns>Returns ncreated SIP client transaction.</returns>
        public SIP_ClientTransaction CreateClientTransaction(SIP_Request request,SIP_Destination destination,bool addVia)
        {
            SIP_ClientTransaction transaction = new SIP_ClientTransaction(m_pSipStack,request,destination,addVia);
            m_pClientTransactions.Add(transaction);

            return transaction;
        }
        /// <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;
            }
        }
Example #6
0
        /// <summary>
        /// Sends specified request to the specified data flow.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        private void SendToFlow(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            #region Contact (RFC 3261 8.1.1.8)

            /*
             *  The Contact header field provides a SIP or SIPS URI that can be used
             *  to contact that specific instance of the UA for subsequent requests.
             *  The Contact header field MUST be present and contain exactly one SIP
             *  or SIPS URI in any request that can result in the establishment of a
             *  dialog.  For the methods defined in this specification, that includes
             *  only the INVITE request.  For these requests, the scope of the
             *  Contact is global.  That is, the Contact header field value contains
             *  the URI at which the UA would like to receive requests, and this URI
             *  MUST be valid even if used in subsequent requests outside of any
             *  dialogs.
             *
             *  If the Request-URI or top Route header field value contains a SIPS
             *  URI, the Contact header field MUST contain a SIPS URI as well.
             */

            SIP_t_ContactParam contact = request.Contact.GetTopMostValue();

            // Add contact header If request-Method can establish dialog and contact header not present.
            if (SIP_Utils.MethodCanEstablishDialog(request.RequestLine.Method) && contact == null)
            {
                SIP_Uri from = (SIP_Uri)request.From.Address.Uri;

                request.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + from.User + "@" + flow.LocalPublicEP.ToString());

                // REMOVE ME: 22.10.2010
                //request.Contact.Add((flow.IsSecure ? "sips:" : "sip:" ) + from.User + "@" + m_pStack.TransportLayer.GetContactHost(flow).ToString());
            }
            // If contact SIP URI and host = auto-allocate, allocate it as needed.
            else if (contact != null && contact.Address.Uri is SIP_Uri && ((SIP_Uri)contact.Address.Uri).Host == "auto-allocate")
            {
                ((SIP_Uri)contact.Address.Uri).Host = flow.LocalPublicEP.ToString();

                // REMOVE ME: 22.10.2010
                //((SIP_Uri)contact.Address.Uri).Host =  m_pStack.TransportLayer.GetContactHost(flow).ToString();
            }

            #endregion

            m_pTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow, request, true);
            m_pTransaction.ResponseReceived += new EventHandler <SIP_ResponseReceivedEventArgs>(ClientTransaction_ResponseReceived);
            m_pTransaction.TimedOut         += new EventHandler(ClientTransaction_TimedOut);
            m_pTransaction.TransportError   += new EventHandler <ExceptionEventArgs>(ClientTransaction_TransportError);

            // Start transaction processing.
            m_pTransaction.Start();
        }
Example #7
0
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 /// <param name="dialog">SIP dialog which received request.</param>
 /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request, SIP_Dialog dialog, SIP_ServerTransaction transaction)
 {
     m_pStack       = stack;
     m_pFlow        = flow;
     m_pRequest     = request;
     m_pDialog      = dialog;
     m_pTransaction = transaction;
 }
        /// <summary>
        /// Sends notify request to remote end point.
        /// </summary>
        /// <param name="notify">SIP NOTIFY request.</param>
        public void Notify(SIP_Request notify)
        {
            if(notify == null){
                throw new ArgumentNullException("notify");
            }

            // TODO:
        }
Example #9
0
        /// <summary>
        /// Sends notify request to remote end point.
        /// </summary>
        /// <param name="notify">SIP NOTIFY request.</param>
        public void Notify(SIP_Request notify)
        {
            if (notify == null)
            {
                throw new ArgumentNullException("notify");
            }

            // TODO:
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Owner SIP stack.</param>
 /// <param name="flow">SIP data flow which is used to send request.</param>
 /// <param name="request">SIP request that transaction will handle.</param>
 /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
 /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
 internal SIP_ClientTransaction(SIP_Stack stack,SIP_Flow flow,SIP_Request request) : base(stack,flow,request)
 {
     // Log
     if(this.Stack.Logger != null){
         this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] created.");
     }
    
     SetState(SIP_TransactionState.WaitingToStart);            
 }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which received request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_ServerTransaction(SIP_Stack stack,SIP_Flow flow,SIP_Request request) : base(stack,flow,request)
        {
            // Log
            if(this.Stack.Logger != null){
                this.Stack.Logger.AddText(this.ID,"Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] created.");
            }

            Start();            
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">Transaction data flow.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_Transaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack     = stack;
            m_pFlow      = flow;
            m_pRequest   = request;
            m_Method     = request.RequestLine.Method;
            m_CreateTime = DateTime.Now;
            m_pResponses = new List <SIP_Response>();

            // 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' parameter is missing !");
            }

            m_ID = via.Branch;

            if (this is SIP_ServerTransaction)
            {
                /*
                 *  We use branch and sent-by as indexing key for transaction, the only special what we need to
                 *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
                 *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                 *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
                 */
                string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
                if (request.RequestLine.Method == SIP_Methods.CANCEL)
                {
                    key += "-CANCEL";
                }
                m_Key = key;
            }
            else
            {
                m_Key = m_ID + "-" + request.RequestLine.Method;
            }
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which is used to send request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        internal SIP_ClientTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : base(stack, flow, request)
        {
            // Log
            if (this.Stack.Logger != null)
            {
                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=false] created.");
            }

            SetState(SIP_TransactionState.WaitingToStart);
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="sipStack">Reference to SIP stack.</param>
        /// <param name="request">SIP request what caused to create server transaction.</param>
        internal SIP_ServerTransaction(SIP_Stack sipStack,SIP_Request request)
        {
            m_pSipStack = sipStack;
            m_pRequest  = request;

            m_ID         = request.Via.GetTopMostValue().Branch;
            m_pResponses = new List<SIP_Response>();

            Begin();
        }
Example #15
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">SIP data flow which received request.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_ServerTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) : base(stack, flow, request)
        {
            // Log
            if (this.Stack.Logger != null)
            {
                this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] created.");
            }

            Start();
        }
Example #16
0
        /// <summary>
        /// Clones this request.
        /// </summary>
        /// <returns>Returns new cloned request.</returns>
        public SIP_Request Copy()
        {
            SIP_Request retVal = SIP_Request.Parse(this.ToByteData());

            retVal.Flow           = m_pFlow;
            retVal.LocalEndPoint  = m_pLocalEP;
            retVal.RemoteEndPoint = m_pRemoteEP;

            return(retVal);
        }
Example #17
0
        /// <summary>
        /// Terminates dialog.
        /// </summary>
        /// <param name="reason">Termination reason. This value may be null.</param>
        /// <param name="sendBye">If true BYE is sent to remote party.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        public virtual void Terminate(string reason, bool sendBye)
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Disposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (this.State == SIP_DialogState.Terminating || this.State == SIP_DialogState.Terminated)
                {
                    return;
                }

                /* RFC 3261 15.
                 *  The caller's UA MAY send a BYE for either confirmed or early dialogs, and the callee's UA MAY send a BYE on
                 *  confirmed dialogs, but MUST NOT send a BYE on early dialogs.
                 *
                 * RFC 3261 15.1.
                 *  Once the BYE is constructed, the UAC core creates a new non-INVITE client transaction, and passes it the BYE request.
                 *  The UAC MUST consider the session terminated (and therefore stop sending or listening for media) as soon as the BYE
                 *  request is passed to the client transaction. If the response for the BYE is a 481 (Call/Transaction Does Not Exist)
                 *  or a 408 (Request Timeout) or no response at all is received for the BYE (that is, a timeout is returned by the
                 *  client transaction), the UAC MUST consider the session and the dialog terminated.
                 */

                this.SetState(SIP_DialogState.Terminating, true);

                if (sendBye)
                {
                    // TODO: UAS early

                    if (this.State == SIP_DialogState.Confirmed)
                    {
                        SIP_Request bye = CreateRequest(SIP_Methods.BYE);
                        if (!string.IsNullOrEmpty(reason))
                        {
                            SIP_t_ReasonValue r = new SIP_t_ReasonValue();
                            r.Protocol = "SIP";
                            r.Text     = reason;
                            bye.Reason.Add(r.ToStringValue());
                        }

                        // Send BYE, just wait BYE to complete, we don't care about response code.
                        SIP_RequestSender sender = CreateRequestSender(bye);
                        sender.Completed += delegate(object s, EventArgs a){
                            this.SetState(SIP_DialogState.Terminated, true);
                        };
                        sender.Start();
                    }
                }
                else
                {
                    this.SetState(SIP_DialogState.Terminated, true);
                }
            }
        }
        /// <summary>
        /// Creates new SIP server transaction for specified request.
        /// </summary>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns added server transaction.</returns>
        public SIP_ServerTransaction CreateServerTransaction(SIP_Request request)
        {
            string transactionID = request.Via.GetTopMostValue().Branch + "-" + request.CSeq.RequestMethod;

            // TODO: use Dictionary ? that works much faster.
            // FIX ME:
            SIP_ServerTransaction transaction = new SIP_ServerTransaction(m_pSipStack,request);
            m_pServerTransactions.Add(transaction);

            return transaction;
        }
        /// <summary>
        /// Sends specified request to the host whats specified in Request-URI. NOTE: Request-URI must
        /// be sip or sips URI, otherwise exception is thrown.
        /// </summary>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="SIP_TransportException">Is raised when sending fails.</exception>
        public void SendRequest(SIP_Request request)
        {
            // Request-URI can be only sip or sips uri, otherwise we don't know where to send request.
            SIP_Uri requestURI = null;
            try{
                requestURI = SIP_Uri.Parse(request.Uri);
            }
            catch{
                throw new SIP_TransportException("Property request.URI is not SIP uri !");
            }

            SendRequest(request,new SIP_Destination(requestURI));
        }
Example #20
0
        /// <summary>
        /// Gets specified realm SIP proxy credentials. Returns null if none exists for specified realm.
        /// </summary>
        /// <param name="request">SIP reques.</param>
        /// <param name="realm">Realm(domain).</param>
        /// <returns>Returns specified realm credentials or null if none.</returns>
        public static SIP_t_Credentials GetCredentials(SIP_Request request,string realm)
        {
            foreach(SIP_SingleValueHF<SIP_t_Credentials> authorization in request.ProxyAuthorization.HeaderFields){
                if(authorization.ValueX.Method.ToLower() == "digest"){
                    Auth_HttpDigest authDigest = new Auth_HttpDigest(authorization.ValueX.AuthData,request.RequestLine.Method);
                    if(authDigest.Realm.ToLower() == realm.ToLower()){
                        return authorization.ValueX;
                    }
                }
            }

            return null;
        }
Example #21
0
            /// <summary>
            /// Processes retransmited INVITE 2xx response.
            /// </summary>
            /// <param name="response">INVITE 2xx response.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
            public void Process(SIP_Response response)
            {
                if (response == null)
                {
                    throw new ArgumentNullException("response");
                }

                lock (m_pLock){
                    SIP_Request ack = CreateAck();

                    try{
                        // Try existing flow.
                        m_pDialog.Flow.Send(ack);

                        // Log
                        if (m_pDialog.Stack.Logger != null)
                        {
                            byte[] ackBytes = ack.ToByteData();

                            m_pDialog.Stack.Logger.AddWrite(
                                m_pDialog.ID,
                                null,
                                ackBytes.Length,
                                "Dialog [id='" + m_pDialog.ID + "] ACK sent for 2xx response.",
                                m_pDialog.Flow.LocalEP,
                                m_pDialog.Flow.RemoteEP,
                                ackBytes
                                );
                        }
                    }
                    catch {
                        /* RFC 3261 13.2.2.4.
                         *  Once the ACK has been constructed, the procedures of [4] are used to
                         *  determine the destination address, port and transport.  However, the
                         *  request is passed to the transport layer directly for transmission,
                         *  rather than a client transaction.
                         */
                        try{
                            m_pDialog.Stack.TransportLayer.SendRequest(ack);
                        }
                        catch (Exception x) {
                            // Log
                            if (m_pDialog.Stack.Logger != null)
                            {
                                m_pDialog.Stack.Logger.AddText("Dialog [id='" + m_pDialog.ID + "'] ACK send for 2xx response failed: " + x.Message + ".");
                            }
                        }
                    }
                }
            }
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public virtual void Dispose()
        {
            SetState(SIP_TransactionState.Disposed);
            OnDisposed();

            m_pStack   = null;
            m_pFlow    = null;
            m_pRequest = null;

            this.StateChanged   = null;
            this.Disposed       = null;
            this.TimedOut       = null;
            this.TransportError = null;
        }
        /// <summary>
        /// Creates and sends ACK for final(3xx - 6xx) failure response.
        /// </summary>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception>
        private void SendAck(SIP_Response response)
        {
            if (response == null)
            {
                throw new ArgumentNullException("resposne");
            }

            /* RFC 3261 17.1.1.3 Construction of the ACK Request.
             *  The ACK request constructed by the client transaction MUST contain
             *  values for the Call-ID, From, and Request-URI that are equal to the
             *  values of those header fields in the request passed to the transport
             *  by the client transaction (call this the "original request").  The To
             *  header field in the ACK MUST equal the To header field in the
             *  response being acknowledged, and therefore will usually differ from
             *  the To header field in the original request by the addition of the
             *  tag parameter.  The ACK MUST contain a single Via header field, and
             *  this MUST be equal to the top Via header field of the original
             *  request.  The CSeq header field in the ACK MUST contain the same
             *  value for the sequence number as was present in the original request,
             *  but the method parameter MUST be equal to "ACK".
             *
             *  If the INVITE request whose response is being acknowledged had Route
             *  header fields, those header fields MUST appear in the ACK.  This is
             *  to ensure that the ACK can be routed properly through any downstream
             *  stateless proxies.
             */

            SIP_Request ackRequest = new SIP_Request(SIP_Methods.ACK);

            ackRequest.RequestLine.Uri = this.Request.RequestLine.Uri;
            ackRequest.Via.AddToTop(this.Request.Via.GetTopMostValue().ToStringValue());
            ackRequest.CallID = this.Request.CallID;
            ackRequest.From   = this.Request.From;
            ackRequest.To     = response.To;
            ackRequest.CSeq   = new SIP_t_CSeq(this.Request.CSeq.SequenceNumber, "ACK");
            foreach (SIP_HeaderField h in response.Header.Get("Route:"))
            {
                ackRequest.Header.Add("Route:", h.Value);
            }
            ackRequest.MaxForwards = 70;

            try{
                // Send request to target.
                this.Stack.TransportLayer.SendRequest(this.Flow, ackRequest, this);
            }
            catch (SIP_TransportException x) {
                OnTransportError(x);
                SetState(SIP_TransactionState.Terminated);
            }
        }
Example #24
0
        /// <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);
                        }
                    }
                });

                SIP_Dialog dialog = MatchDialog(request);
                if (dialog != null)
                {
                    dialog.AddTransaction(transaction);
                }

                return(transaction);
            }
        }
        /// <summary>
        /// Sends specified request to flow remote end point.
        /// </summary>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null reference.</exception>
        public void Send(SIP_Request request)
        {
            lock (m_pLock){
                if (m_IsDisposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                SendInternal(request.ToByteData());
            }
        }
Example #26
0
            /// <summary>
            /// Checks if specified ACK request matches this 2xx response retransmission.
            /// </summary>
            /// <param name="ackRequest">ACK request.</param>
            /// <returns>Returns true if ACK matches, othwerwise false.</returns>
            /// <exception cref="ArgumentNullException">Is raised when <b>ackRequest</b> is null reference value.</exception>
            public bool MatchAck(SIP_Request ackRequest)
            {
                if (ackRequest == null)
                {
                    throw new ArgumentNullException("ackRequest");
                }

                if (ackRequest.CSeq.SequenceNumber == m_pResponse.CSeq.SequenceNumber)
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request.
        /// This value can be null.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>request</b> is null.</exception>
        internal SIP_RequestSender(SIP_Stack stack,SIP_Request request,SIP_Flow flow)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            m_pStack   = stack;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pCredentials = new List<NetworkCredential>();
            m_pHops = new Queue<SIP_Hop>();
        }
        /// <summary>
        /// Matches SIP request to server transaction. If not matching transaction found, returns null.
        /// </summary>
        /// <param name="request">SIP request to match.</param>
        /// <returns>Returns matching transaction or null if no match.</returns>
        internal SIP_ServerTransaction MatchServerTransaction(SIP_Request request)
        {
            /* RFC 3261 17.2.3 Matching Requests to Server Transactions.
             *  This matching rule applies to both INVITE and non-INVITE transactions.
             *
             *  1. the branch parameter in the request is equal to the one in the top Via header
             *     field of the request that created the transaction, and
             *
             *  2. the sent-by value in the top Via of the request is equal to the
             *     one in the request that created the transaction, and
             *
             *  3. the method of the request matches the one that created the transaction, except
             *     for ACK, where the method of the request that created the transaction is INVITE.
             *
             *  Internal implementation notes:
             *      Inernally we use branch + '-' + sent-by for non-CANCEL and for CANCEL
             *      branch + '-' + sent-by + '-' CANCEL. This is because method matching is actually
             *      needed for CANCEL only (CANCEL shares cancelable transaction branch ID).
             */

            SIP_ServerTransaction retVal = null;

            /*
             *  We use branch and sent-by as indexing key for transaction, the only special what we need to
             *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
             *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
             *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
             */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                key += "-CANCEL";
            }

            lock (m_pServerTransactions){
                m_pServerTransactions.TryGetValue(key, out retVal);
            }

            // Don't match ACK for terminated transaction, in that case ACK must be passed to "core".
            if (retVal != null && request.RequestLine.Method == SIP_Methods.ACK && retVal.State == SIP_TransactionState.Terminated)
            {
                retVal = null;
            }

            return(retVal);
        }
Example #29
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner SIP stack.</param>
        /// <param name="flow">Transaction data flow.</param>
        /// <param name="request">SIP request that transaction will handle.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_Transaction(SIP_Stack stack,SIP_Flow flow,SIP_Request request)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            m_pStack     = stack;
            m_pFlow      = flow;
            m_pRequest   = request;
            m_Method     = request.RequestLine.Method;
            m_CreateTime = DateTime.Now;
            m_pResponses = new List<SIP_Response>();

            // 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' parameter is missing !");
            }

            m_ID  = via.Branch;

            if(this is SIP_ServerTransaction){
                /*
                    We use branch and sent-by as indexing key for transaction, the only special what we need to
                    do is to handle CANCEL, because it has same branch as transaction to be canceled.
                    For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                    ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
                */
                string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
                if(request.RequestLine.Method == SIP_Methods.CANCEL){
                    key += "-CANCEL";
                }
                m_Key = key;
            }
            else{
                m_Key = m_ID + "-" + request.RequestLine.Method;
            }
        }
Example #30
0
        /// <summary>
        /// Creates SIP request sender for the specified request.
        /// </summary>
        /// <remarks>All requests sent through this dialog SHOULD use this request sender to send out requests.</remarks>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns created sender.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null.</exception>
        public SIP_RequestSender CreateRequestSender(SIP_Request request)
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Terminated)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                SIP_RequestSender sender = m_pStack.CreateRequestSender(request, this.Flow);

                return(sender);
            }
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">Owner stack.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request.
        /// This value can be null.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b> or <b>request</b> is null.</exception>
        internal SIP_RequestSender(SIP_Stack stack, SIP_Request request, SIP_Flow flow)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            m_pStack   = stack;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pCredentials = new List <NetworkCredential>();
            m_pHops        = new Queue <SIP_Hop>();
        }
Example #32
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="proxy">Owner SIP proxy server.</param>
        /// <param name="request">The request what is represented by this context.</param>
        /// <param name="flow">Data flow what received request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>proxy</b>, <b>request</b> or <b>flow</b> is null reference.</exception>
        internal SIP_RequestContext(SIP_Proxy proxy,SIP_Request request,SIP_Flow flow)
        {
            if(proxy == null){
                throw new ArgumentNullException("proxy");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }

            m_pProxy   = proxy;
            m_pRequest = request;
            m_pFlow    = flow;

            m_pTargets = new List<SIP_ProxyTarget>();
        }
        /// <summary>
        /// Creates SIP request sender for the specified request.
        /// </summary>
        /// <remarks>All requests sent through this dialog SHOULD use this request sender to send out requests.</remarks>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns created sender.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null.</exception>
        public SIP_RequestSender CreateRequestSender(SIP_Request request)
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Terminated)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                // TODO: Request sender must use dialog sequence numbering if authentication done.

                SIP_RequestSender sender = m_pStack.CreateRequestSender(request, this.Flow);

                return(sender);
            }
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">SIP stack.</param>
 /// <param name="request">SIP request.</param>
 /// <param name="hops">Priority ordered "hops" where to send request.</param>
 /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>request</b> or <b>hops</b> is null reference.</exception>
 /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
 public SIP_RequestSender(SIP_Stack stack,SIP_Request request,Queue<SIP_Hop> hops)
 {
     if(stack == null){
         throw new ArgumentNullException("stack");
     }
     if(request == null){
         throw new ArgumentNullException("request");
     }
     if(hops == null){
         throw new ArgumentNullException("hops");
     }
     if(hops.Count == 0){
         throw new ArgumentException("There must be at least 1 hop in 'hops' queue.");
     }
     
     m_pStack   = stack;
     m_pRequest = request;
     m_pHops    = hops;
 }
Example #35
0
            /// <summary>
            /// Cleans up any resources being used.
            /// </summary>
            public void Dispose()
            {
                lock (m_pLock){
                    if (m_IsDisposed)
                    {
                        return;
                    }
                    m_IsDisposed = true;

                    m_pDialog.m_pUacInvite2xxRetransmitWaits.Remove(this);

                    m_pDialog = null;
                    m_pInvite = null;
                    if (m_pTimer != null)
                    {
                        m_pTimer.Dispose();
                        m_pTimer = null;
                    }
                }
            }
        /// <summary>
        /// Creates and send CANCEL request to remote target.
        /// </summary>
        private void SendCancel()
        {
            /* RFC 3261 9.1.
             *  The following procedures are used to construct a CANCEL request.  The
             *  Request-URI, Call-ID, To, the numeric part of CSeq, and From header
             *  fields in the CANCEL request MUST be identical to those in the
             *  request being cancelled, including tags.  A CANCEL constructed by a
             *  client MUST have only a single Via header field value matching the
             *  top Via value in the request being cancelled.  Using the same values
             *  for these header fields allows the CANCEL to be matched with the
             *  request it cancels (Section 9.2 indicates how such matching occurs).
             *  However, the method part of the CSeq header field MUST have a value
             *  of CANCEL.  This allows it to be identified and processed as a
             *  transaction in its own right (See Section 17).
             *
             *  If the request being cancelled contains a Route header field, the
             *  CANCEL request MUST include that Route header field's values.
             *
             *      This is needed so that stateless proxies are able to route CANCEL
             *      requests properly.
             */

            SIP_Request cancelRequest = new SIP_Request(SIP_Methods.CANCEL);

            cancelRequest.RequestLine.Uri = this.Request.RequestLine.Uri;
            cancelRequest.Via.Add(this.Request.Via.GetTopMostValue().ToStringValue());
            cancelRequest.CallID = this.Request.CallID;
            cancelRequest.From   = this.Request.From;
            cancelRequest.To     = this.Request.To;
            cancelRequest.CSeq   = new SIP_t_CSeq(this.Request.CSeq.SequenceNumber, SIP_Methods.CANCEL);
            foreach (SIP_t_AddressParam route in this.Request.Route.GetAllValues())
            {
                cancelRequest.Route.Add(route.ToStringValue());
            }
            cancelRequest.MaxForwards = 70;

            // We must use same data flow to send CANCEL what sent initial request.
            SIP_ClientTransaction transaction = this.Stack.TransactionLayer.CreateClientTransaction(this.Flow, cancelRequest, false);

            transaction.Start();
        }
        /// <summary>
        /// Starts unregistering.
        /// </summary>
        /// <param name="dispose">If true, registration will be disposed after unregister.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        public void BeginUnregister(bool dispose)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }

            m_AutoDispose = dispose;

            // Stop register timer, otherwise we may get register and unregister race condition.
            m_pTimer.Enabled = false;

            if (m_State == SIP_UA_RegistrationState.Registered)
            {
                /* RFC 3261 10.1 Constructing the REGISTER Request.
                 *  Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example,
                 *               "sip:chicago.com").  The "userinfo" and "@" components of the SIP URI MUST NOT be present.
                 */

                SIP_Request unregister = m_pStack.CreateRequest(SIP_Methods.REGISTER, new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR), new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR));
                unregister.RequestLine.Uri = SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1));
                unregister.Route.Add(m_pServer.ToString());
                unregister.Contact.Add("<" + this.Contact + ">;expires=0");

                m_pUnregisterSender = m_pStack.CreateRequestSender(unregister, m_pFlow);
                m_pUnregisterSender.ResponseReceived += new EventHandler <SIP_ResponseReceivedEventArgs>(m_pUnregisterSender_ResponseReceived);
                m_pUnregisterSender.Start();
            }
            else
            {
                SetState(SIP_UA_RegistrationState.Unregistered);
                OnUnregistered();

                if (m_AutoDispose)
                {
                    Dispose();
                }

                m_pUnregisterSender = null;
            }
        }
Example #38
0
        /// <summary>
        /// Default outgoing call constructor.
        /// </summary>
        /// <param name="ua">Owner UA.</param>
        /// <param name="invite">INVITE request.</param>
        /// <exception cref="ArgumentNullException">Is riased when <b>ua</b> or <b>invite</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the argumnets has invalid value.</exception>
        internal SIP_UA_Call(SIP_UA ua,SIP_Request invite)
        {
            if(ua == null){
                throw new ArgumentNullException("ua");
            }
            if(invite == null){
                throw new ArgumentNullException("invite");
            }
            if(invite.RequestLine.Method != SIP_Methods.INVITE){
                throw new ArgumentException("Argument 'invite' is not INVITE request.");
            }

            m_pUA        = ua;
            m_pInvite    = invite;
            m_pLocalUri  = invite.From.Address.Uri;
            m_pRemoteUri = invite.To.Address.Uri;

            m_State = SIP_UA_CallState.WaitingForStart;

            m_pEarlyDialogs = new List<SIP_Dialog>();
        }
        /// <summary>
        /// Ensures that specified request has matching server transaction. If server transaction doesn't exist,
        /// it will be created, otherwise existing transaction will be returned.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns matching transaction.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Is raised when request.Method is ACK request.</exception>
        public SIP_ServerTransaction EnsureServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                throw new InvalidOperationException("ACK request is transaction less request, can't create transaction for it.");
            }

            /*
             *  We use branch and sent-by as indexing key for transaction, the only special what we need to
             *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
             *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
             *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
             */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                key += "-CANCEL";
            }

            lock (m_pServerTransactions){
                SIP_ServerTransaction retVal = null;
                m_pServerTransactions.TryGetValue(key, out retVal);
                // We don't have transaction, create it.
                if (retVal == null)
                {
                    retVal = CreateServerTransaction(flow, request);
                }

                return(retVal);
            }
        }
Example #40
0
            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="dialog">Owner INVITE dialog.</param>
            /// <param name="invite">INVITE request which 2xx retransmission to wait.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>dialog</b> or <b>invite</b> is null reference.</exception>
            public UacInvite2xxRetransmissionWaiter(SIP_Dialog_Invite dialog, SIP_Request invite)
            {
                if (dialog == null)
                {
                    throw new ArgumentNullException("dialog");
                }
                if (invite == null)
                {
                    throw new ArgumentNullException("invite");
                }

                m_pDialog = dialog;
                m_pInvite = invite;

                /* RFC 3261 13.2.2.4.
                 *  The UAC core considers the INVITE transaction completed 64*T1 seconds
                 *  after the reception of the first 2xx response.
                 */
                m_pTimer          = new TimerEx(64 * SIP_TimerConstants.T1, false);
                m_pTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimer_Elapsed);
                m_pTimer.Enabled  = true;
            }
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public void Dispose()
        {
            lock (m_pLock){
                if (m_State == SIP_RequestSenderState.Disposed)
                {
                    return;
                }
                m_State = SIP_RequestSenderState.Disposed;

                OnDisposed();

                this.ResponseReceived = null;
                this.Completed        = null;
                this.Disposed         = null;

                m_pStack       = null;
                m_pRequest     = null;
                m_pCredentials = null;
                m_pHops        = null;
                m_pTransaction = null;
                m_pLock        = null;
            }
        }
        /// <summary>
        /// Matches CANCEL requst to SIP server non-CANCEL transaction. Returns null if no match.
        /// </summary>
        /// <param name="cancelRequest">SIP CANCEL request.</param>
        /// <returns>Returns CANCEL matching server transaction or null if no match.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>cancelTransaction</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when <b>cancelTransaction</b> has invalid.</exception>
        public SIP_ServerTransaction MatchCancelToTransaction(SIP_Request cancelRequest)
        {
            if (cancelRequest == null)
            {
                throw new ArgumentNullException("cancelRequest");
            }
            if (cancelRequest.RequestLine.Method != SIP_Methods.CANCEL)
            {
                throw new ArgumentException("Argument 'cancelRequest' is not SIP CANCEL request.");
            }

            SIP_ServerTransaction retVal = null;

            // NOTE: There we don't add '-CANCEL' because we want to get CANCEL matching transaction, not CANCEL
            //       transaction itself.
            string key = cancelRequest.Via.GetTopMostValue().Branch + '-' + cancelRequest.Via.GetTopMostValue().SentBy;

            lock (m_pServerTransactions){
                m_pServerTransactions.TryGetValue(key, out retVal);
            }

            return(retVal);
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="stack">SIP stack.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="hops">Priority ordered "hops" where to send request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>request</b> or <b>hops</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SIP_RequestSender(SIP_Stack stack, SIP_Request request, Queue <SIP_Hop> hops)
        {
            if (stack == null)
            {
                throw new ArgumentNullException("stack");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (hops == null)
            {
                throw new ArgumentNullException("hops");
            }
            if (hops.Count == 0)
            {
                throw new ArgumentException("There must be at least 1 hop in 'hops' queue.");
            }

            m_pStack   = stack;
            m_pRequest = request;
            m_pHops    = hops;
        }
Example #44
0
        /// <summary>
        /// Re-invites remote party.
        /// </summary>
        /// <param name="contact">New contact value. Value null means current contact value used.</param>
        /// <param name="sdp">SDP media offer.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when there is pending invite and this method is called or dialog is in invalid state.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>sdp</b> is null reference.</exception>
        public void ReInvite(SIP_Uri contact, SDP_Message sdp)
        {
            if (this.State == SIP_DialogState.Disposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (this.HasPendingInvite)
            {
                throw new InvalidOperationException("There is pending INVITE.");
            }
            if (this.State != SIP_DialogState.Confirmed)
            {
                throw new InvalidOperationException("ReInvite is only available in Confirmed state.");
            }
            if (sdp == null)
            {
                throw new ArgumentNullException("sdp");
            }

            lock (this.SyncRoot){
                // TODO:

                SIP_Request reinvite = CreateRequest(SIP_Methods.INVITE);
                if (contact != null)
                {
                    reinvite.Contact.RemoveAll();
                    reinvite.Contact.Add(contact.ToString());
                }
                reinvite.ContentType = "application/sdp";
                // reinvite.Data = sdp.ToStringData();

                // TODO: Create request sender
                // TODO: Try to reuse existing data flow
                //SIP_RequestSender sender = this.Stack.CreateRequestSender(reinvite);
                //sender.Start();
            }
        }
Example #45
0
            /// <summary>
            /// Cleans up any resources being used.
            /// </summary>
            public void Dispose()
            {
                lock(m_pLock){
                    if(m_IsDisposed){
                        return;
                    }
                    m_IsDisposed = true;

                    m_pOwner.TargetHandler_Disposed(this);

                    m_pOwner = null;
                    m_pRequest = null;
                    m_pTargetUri = null;
                    m_pHops = null;
                    if(m_pTransaction != null){
                        m_pTransaction.Dispose();
                        m_pTransaction = null;
                    }
                    if(m_pTimerC != null){
                        m_pTimerC.Dispose();
                        m_pTimerC = null;
                    }
                }
            }
        /// <summary>
        /// Matches CANCEL requst to SIP server non-CANCEL transaction. Returns null if no match.
        /// </summary>
        /// <param name="cancelRequest">SIP CANCEL request.</param>
        /// <returns>Returns CANCEL matching server transaction or null if no match.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>cancelTransaction</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when <b>cancelTransaction</b> has invalid.</exception>
        public SIP_ServerTransaction MatchCancelToTransaction(SIP_Request cancelRequest)
        {
            if(cancelRequest == null){
                throw new ArgumentNullException("cancelRequest");
            }
            if(cancelRequest.RequestLine.Method != SIP_Methods.CANCEL){
                throw new ArgumentException("Argument 'cancelRequest' is not SIP CANCEL request.");
            }

            SIP_ServerTransaction retVal = null;

            // NOTE: There we don't add '-CANCEL' because we want to get CANCEL matching transaction, not CANCEL
            //       transaction itself.
            string key = cancelRequest.Via.GetTopMostValue().Branch + '-' + cancelRequest.Via.GetTopMostValue().SentBy;
            lock(m_pServerTransactions){
                m_pServerTransactions.TryGetValue(key,out retVal);
            }

            return retVal;
        }
        /// <summary>
        /// Matches specified SIP request to SIP dialog. If no matching dialog found, returns null.
        /// </summary>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns matched SIP dialog or null in no match found.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null.</exception>
        internal SIP_Dialog MatchDialog(SIP_Request request)
        {
            if(request == null){
                throw new ArgumentNullException("request");
            }

            SIP_Dialog dialog = null;

            try{
                string callID    = request.CallID;
                string localTag  = request.To.Tag;
                string remoteTag = request.From.Tag;
                if(callID != null && localTag != null && remoteTag != null){
                    string dialogID = callID + "-" + localTag + "-" + remoteTag;
                    lock(m_pDialogs){
                        m_pDialogs.TryGetValue(dialogID,out dialog);
                    }
                }
            }
            catch{
            }

            return dialog;
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack,SIP_Flow flow,SIP_Request request)
     : this(stack,flow,request,null,null)
 {
 }
        /// <summary>
        /// Matches SIP request to server transaction. If not matching transaction found, returns null.
        /// </summary>
        /// <param name="request">SIP request to match.</param>
        /// <returns>Returns matching transaction or null if no match.</returns>
        internal SIP_ServerTransaction MatchServerTransaction(SIP_Request request)
        {
            /* RFC 3261 17.2.3 Matching Requests to Server Transactions.
                This matching rule applies to both INVITE and non-INVITE transactions.

                1. the branch parameter in the request is equal to the one in the top Via header
                   field of the request that created the transaction, and

                2. the sent-by value in the top Via of the request is equal to the
                   one in the request that created the transaction, and

                3. the method of the request matches the one that created the transaction, except
                   for ACK, where the method of the request that created the transaction is INVITE.

                Internal implementation notes:
                    Inernally we use branch + '-' + sent-by for non-CANCEL and for CANCEL
                    branch + '-' + sent-by + '-' CANCEL. This is because method matching is actually
                    needed for CANCEL only (CANCEL shares cancelable transaction branch ID).
            */

            SIP_ServerTransaction retVal = null;

            /*
                We use branch and sent-by as indexing key for transaction, the only special what we need to
                do is to handle CANCEL, because it has same branch as transaction to be canceled.
                For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
                ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
            */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;
            if(request.RequestLine.Method == SIP_Methods.CANCEL){
                key += "-CANCEL";
            }

            lock(m_pServerTransactions){
                m_pServerTransactions.TryGetValue(key,out retVal);
            }

            // Don't match ACK for terminated transaction, in that case ACK must be passed to "core".
            if(retVal != null && request.RequestLine.Method == SIP_Methods.ACK && retVal.State == SIP_TransactionState.Terminated){
                retVal = null;
            }

            return retVal;
        }
Example #50
0
        /// <summary>
        /// Sends specified request to flow remote end point.
        /// </summary>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null reference.</exception>
        public void Send(SIP_Request request)
        {
            lock(m_pLock){
                if(m_IsDisposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if(request == null){
                    throw new ArgumentNullException("request");
                }

                SendInternal(request.ToByteData());
            }
        }
        /// <summary>
        /// Sends request using methods as described in RFC 3261 [4](RFC 3263).
        /// </summary>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null.</exception>
        /// <exception cref="SIP_TransportException">Is raised when transport error happens.</exception>
        public void SendRequest(SIP_Request request)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            SIP_Hop[] hops = m_pStack.GetHops((SIP_Uri)request.RequestLine.Uri,request.ToByteData().Length,false);
            if(hops.Length == 0){
                throw new SIP_TransportException("No target hops for URI '" + request.RequestLine.Uri.ToString() + "'.");
            }

            SIP_TransportException lastException = null;
            foreach(SIP_Hop hop in hops){
                try{
                    SendRequest(request,null,hop);

                    return;
                }
                catch(SIP_TransportException x){
                    lastException = x;
                }
            }

            // If we reach so far, send failed, return last error.
            throw lastException;
        }
Example #52
0
        /// <summary>
        /// Processes specified request through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        internal void ProcessRequest(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (this.SyncRoot){
                if (this.State == SIP_TransactionState.Disposed)
                {
                    return;
                }

                try{
                    // Log
                    if (this.Stack.Logger != null)
                    {
                        byte[] requestData = request.ToByteData();

                        this.Stack.Logger.AddRead(
                            Guid.NewGuid().ToString(),
                            null,
                            0,
                            "Request [transactionID='" + this.ID + "'; method='" + request.RequestLine.Method + "'; cseq='" + request.CSeq.SequenceNumber + "'; " +
                            "transport='" + flow.Transport + "'; size='" + requestData.Length + "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                            flow.LocalEP,
                            flow.RemoteEP,
                            requestData
                            );
                    }

                    #region INVITE

                    if (this.Method == SIP_Methods.INVITE)
                    {
                        #region INVITE

                        if (request.RequestLine.Method == SIP_Methods.INVITE)
                        {
                            if (this.State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.1.
                                 *  If a request retransmission is received while in the "Proceeding" state, the most recent provisional
                                 *  response that was received from the TU MUST be passed to the transport layer for retransmission.
                                 */
                                SIP_Response response = this.LastProvisionalResponse;
                                if (response != null)
                                {
                                    this.Stack.TransportLayer.SendResponse(this, response);
                                }
                            }
                            else if (this.State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.1.
                                 *  While in the "Completed" state, if a request retransmission is received, the server SHOULD
                                 *  pass the response to the transport for retransmission.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.FinalResponse);
                            }
                        }

                        #endregion

                        #region ACK

                        else if (request.RequestLine.Method == SIP_Methods.ACK)
                        {
                            /* RFC 3261 17.2.1
                             *  If an ACK is received while the server transaction is in the "Completed" state, the server transaction
                             *  MUST transition to the "Confirmed" state.  As Timer G is ignored in this state, any retransmissions of the
                             *  response will cease.
                             *
                             *  When this state is entered, timer I is set to fire in T4 seconds for unreliable transports,
                             *  and zero seconds for reliable transports.
                             */

                            if (this.State == SIP_TransactionState.Completed)
                            {
                                SetState(SIP_TransactionState.Confirmed);

                                // Stop timers G,H
                                if (m_pTimerG != null)
                                {
                                    m_pTimerG.Dispose();
                                    m_pTimerG = null;

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) stoped.");
                                    }
                                }
                                if (m_pTimerH != null)
                                {
                                    m_pTimerH.Dispose();
                                    m_pTimerH = null;

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer H(INVITE ACK wait) stoped.");
                                    }
                                }

                                // Start timer I.
                                m_pTimerI          = new TimerEx((flow.IsReliable ? 0 : SIP_TimerConstants.T4), false);
                                m_pTimerI.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerI_Elapsed);
                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer I(INVITE ACK retransission wait) started, will triger after " + m_pTimerI.Interval + ".");
                                }
                                m_pTimerI.Enabled = true;
                            }
                        }

                        #endregion
                    }

                    #endregion

                    #region Non-INVITE

                    else
                    {
                        // Non-INVITE transaction may have only request retransmission requests.
                        if (this.Method == request.RequestLine.Method)
                        {
                            if (this.State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.2.
                                 *  If a retransmission of the request is received while in the "Proceeding" state, the most
                                 *  recently sent provisional response MUST be passed to the transport layer for retransmission.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.LastProvisionalResponse);
                            }
                            else if (this.State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.2.
                                 *  While in the "Completed" state, the server transaction MUST pass the final response to the transport
                                 *  layer for retransmission whenever a retransmission of the request is received.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.FinalResponse);
                            }
                        }
                    }

                    #endregion
                }
                catch (SIP_TransportException x) {
                    // Log
                    if (this.Stack.Logger != null)
                    {
                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] transport exception: " + x.Message);
                    }

                    OnTransportError(x);
                    SetState(SIP_TransactionState.Terminated);
                }
            }
        }
        /// <summary>
        /// Matches SIP request to server transaction. If not matching transaction found, returns null.
        /// </summary>
        /// <param name="request">SIP request to match.</param>
        internal SIP_ServerTransaction MatchServerTransaction(SIP_Request request)
        {
            /* RFC 3261 17.2.3 Matching Requests to Server Transactions.
                This matching rule applies to both INVITE and non-INVITE transactions.

                1. the branch parameter in the request is equal to the one in the top Via header
                   field of the request that created the transaction, and

                2. the sent-by value in the top Via of the request is equal to the
                   one in the request that created the transaction, and

                3. the method of the request matches the one that created the transaction, except
                   for ACK, where the method of the request that created the transaction is INVITE.
            */

            string transactionID = request.Via.GetTopMostValue().Branch;
            string sentBy        = request.Via.GetTopMostValue().SentBy;
            lock(m_pServerTransactions){
                foreach(SIP_ServerTransaction transaction in m_pServerTransactions){
                    if(transactionID == transaction.ID && transaction.Request.Via.GetTopMostValue().SentBy == sentBy){
                        // ACK may not use method for matching, otherwise it never matches.
                        if(request.Method == SIP_Methods.ACK){
                            return transaction;
                        }
                        // CANCEL also won't match method.
                        else if(request.Method == SIP_Methods.CANCEL){
                            return transaction;
                        }
                        else if(request.Method == transaction.Request.Method){
                            return transaction;
                        }
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// Creates new SIP UAS dialog.
        /// </summary>
        /// <param name="transaction">Owner transaction what forces to create dialog.</param>
        /// <param name="request">Server transaction request what response it is.</param>
        /// <param name="response">SIP response what causes dialog creation.</param>
        /// <returns>Returns new SIP dialog.</returns>
        internal SIP_Dialog CreateDialog(SIP_ServerTransaction transaction,SIP_Request request,SIP_Response response)
        {
            // TODO: ren EnsureDialog

            SIP_Dialog dialog = new SIP_Dialog(m_pSipStack,request,response);
            m_pDialogs.Add(dialog);

            return dialog;
        }
        /// <summary>
        /// Starts terminating dialog.
        /// </summary>
        /// <param name="reason">Termination reason. This value may be null.</param>
        /// <param name="sendBye">If true BYE is sent to remote party.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        public void Terminate(string reason, bool sendBye)
        {
            lock (this.SyncRoot){
                if (this.State == SIP_DialogState.Disposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (this.State == SIP_DialogState.Terminating || this.State == SIP_DialogState.Terminated)
                {
                    return;
                }

                m_TerminateReason = reason;

                /* RFC 3261 15.
                 *  The caller's UA MAY send a BYE for either confirmed or early dialogs, and the callee's UA MAY send a BYE on
                 *  confirmed dialogs, but MUST NOT send a BYE on early dialogs.
                 *
                 * RFC 3261 15.1.
                 *  Once the BYE is constructed, the UAC core creates a new non-INVITE client transaction, and passes it the BYE request.
                 *  The UAC MUST consider the session terminated (and therefore stop sending or listening for media) as soon as the BYE
                 *  request is passed to the client transaction. If the response for the BYE is a 481 (Call/Transaction Does Not Exist)
                 *  or a 408 (Request Timeout) or no response at all is received for the BYE (that is, a timeout is returned by the
                 *  client transaction), the UAC MUST consider the session and the dialog terminated.
                 */

                if (sendBye)
                {
                    if ((this.State == SIP_DialogState.Early && m_pActiveInvite is SIP_ClientTransaction) || this.State == SIP_DialogState.Confirmed)
                    {
                        this.SetState(SIP_DialogState.Terminating, true);

                        SIP_Request bye = CreateRequest(SIP_Methods.BYE);
                        if (!string.IsNullOrEmpty(reason))
                        {
                            SIP_t_ReasonValue r = new SIP_t_ReasonValue();
                            r.Protocol = "SIP";
                            r.Text     = reason;
                            bye.Reason.Add(r.ToStringValue());
                        }

                        // Send BYE, just wait BYE to complete, we don't care about response code.
                        SIP_RequestSender sender = CreateRequestSender(bye);
                        sender.Completed += delegate(object s, EventArgs a){
                            this.SetState(SIP_DialogState.Terminated, true);
                        };
                        sender.Start();
                    }
                    else
                    {
                        /* We are "early" UAS dialog, we need todo follwoing:
                         *) If we havent sent final response, send '408 Request terminated' and we are done.
                         *) We have sen't final response, we need to wait ACK to arrive or timeout.
                         *      If will ACK arrives or timeout, send BYE.
                         */

                        if (m_pActiveInvite != null && m_pActiveInvite.FinalResponse == null)
                        {
                            this.Stack.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout, m_pActiveInvite.Request);

                            this.SetState(SIP_DialogState.Terminated, true);
                        }
                        else
                        {
                            // Wait ACK to arrive or timeout.

                            this.SetState(SIP_DialogState.Terminating, true);
                        }
                    }
                }
                else
                {
                    this.SetState(SIP_DialogState.Terminated, true);
                }
            }
        }
 /// <summary>
 /// Sends request to the specified flow.
 /// </summary>
 /// <param name="flow">Data flow.</param>
 /// <param name="request">SIP request.</param>
 /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
 /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
 /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
 public void SendRequest(SIP_Flow flow,SIP_Request request)
 {
     SendRequest(flow,request,null);
 }
        /// <summary>
        /// Sends request to the specified flow.
        /// </summary>
        /// <param name="flow">Data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <param name="transaction">Owner client transaction or null if stateless sending.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments contains invalid value.</exception>
        internal void SendRequest(SIP_Flow flow,SIP_Request request,SIP_ClientTransaction transaction)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(flow == null){
                throw new ArgumentNullException("flow");
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }
            if(request.Via.GetTopMostValue() == null){
                throw new ArgumentException("Argument 'request' doesn't contain required Via: header field.");
            }

            // Set sent-by
            SIP_t_ViaParm via = request.Via.GetTopMostValue();
            via.ProtocolTransport = flow.Transport;
            // Via sent-by is used only to send responses when request maker data flow is not active.
            // Normally this never used, so just report first local listening point as sent-by.
            HostEndPoint sentBy = null;
            foreach(IPBindInfo bind in this.BindInfo){
                if(flow.Transport == SIP_Transport.UDP && bind.Protocol == BindInfoProtocol.UDP){
                    if(!string.IsNullOrEmpty(bind.HostName)){
                        sentBy = new HostEndPoint(bind.HostName,bind.Port);
                    }
                    else{
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(),bind.Port);
                    }
                    break;
                }
                else if(flow.Transport == SIP_Transport.TLS && bind.Protocol == BindInfoProtocol.TCP && bind.SslMode == SslMode.SSL){
                    if(!string.IsNullOrEmpty(bind.HostName)){
                        sentBy = new HostEndPoint(bind.HostName,bind.Port);
                    }
                    else{
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(),bind.Port);
                    }
                    break;
                }
                else if(flow.Transport == SIP_Transport.TCP && bind.Protocol == BindInfoProtocol.TCP){
                    if(!string.IsNullOrEmpty(bind.HostName)){
                        sentBy = new HostEndPoint(bind.HostName,bind.Port);
                    }
                    else{
                        sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(),bind.Port);
                    }
                    break;
                }
            }
            // No local end point for sent-by, just use flow local end point for it.
            if(sentBy == null){
                via.SentBy = new HostEndPoint(flow.LocalEP);
            }
            else{
                via.SentBy = sentBy;
            }

            // Send request.
            flow.Send(request);

            // Log.
            if(m_pStack.Logger != null){
                byte[] requestData = request.ToByteData();

                m_pStack.Logger.AddWrite(
                    Guid.NewGuid().ToString(),
                    null,
                    0,
                    "Request [" + (transaction == null ? "" : "transactionID='" + transaction.ID + "';") + "method='" + request.RequestLine.Method + "'; cseq='" + request.CSeq.SequenceNumber + "'; " +
                    "transport='" + flow.Transport + "'; size='" + requestData.Length + "'; sent '" + flow.LocalEP + "' -> '" + flow.RemoteEP + "'.",
                    flow.LocalEP,
                    flow.RemoteEP,
                    requestData
                );
            }
        }
Example #58
0
        /// <summary>
        /// Creates SIP request sender for the specified request.
        /// </summary>
        /// <remarks>All requests sent through this dialog SHOULD use this request sender to send out requests.</remarks>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns created sender.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null.</exception>
        public SIP_RequestSender CreateRequestSender(SIP_Request request)
        {
            lock(m_pLock){
                if(this.State == SIP_DialogState.Terminated){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if(request == null){
                    throw new ArgumentNullException("request");
                }

                // TODO: Request sender must use dialog sequence numbering if authentication done.
               
                SIP_RequestSender sender = m_pStack.CreateRequestSender(request,this.Flow);

                return sender;
            }
        }
        /// <summary>
        /// Sends request to the specified hop.
        /// </summary>
        /// <param name="request">SIP request.</param>
        /// <param name="localEP">Local end point. Value null means system will allocate it.</param>
        /// <param name="hop">Target hop.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> or <b>hop</b> is null reference.</exception>
        public void SendRequest(SIP_Request request,IPEndPoint localEP,SIP_Hop hop)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }
            if(hop == null){
                throw new ArgumentNullException("hop");
            }

            SendRequest(GetOrCreateFlow(hop.Transport,localEP,hop.EndPoint),request);
        }
Example #60
0
        private void SendAck(SIP_Dialog dialog, SIP_Request ack)
        {
            if (dialog == null)
            {
                throw new ArgumentNullException("dialog");
            }
            if (ack == null)
            {
                throw new ArgumentNullException("ack");
            }

            try
            {
                // Try existing flow.
                dialog.Flow.Send(ack);

                // Log
                if (dialog.Stack.Logger != null)
                {
                    byte[] ackBytes = ack.ToByteData();

                    dialog.Stack.Logger.AddWrite(
                        dialog.ID,
                        null,
                        ackBytes.Length,
                        "Request [DialogID='" + dialog.ID + "';" + "method='" + ack.RequestLine.Method + "'; cseq='" + ack.CSeq.SequenceNumber + "'; " +
                        "transport='" + dialog.Flow.Transport + "'; size='" + ackBytes.Length + "'] sent '" + dialog.Flow.LocalEP + "' -> '" + dialog.Flow.RemoteEP + "'.",
                        dialog.Flow.LocalEP,
                        dialog.Flow.RemoteEP,
                        ackBytes
                    );
                }
            }
            catch
            {
                /* RFC 3261 13.2.2.4.
                    Once the ACK has been constructed, the procedures of [4] are used to
                    determine the destination address, port and transport.  However, the
                    request is passed to the transport layer directly for transmission,
                    rather than a client transaction.
                */
                try
                {
                    dialog.Stack.TransportLayer.SendRequest(ack);
                }
                catch (Exception x)
                {
                    // Log
                    if (dialog.Stack.Logger != null)
                    {
                        dialog.Stack.Logger.AddText("Dialog [id='" + dialog.ID + "'] ACK send for 2xx response failed: " + x.Message + ".");
                    }
                }
            }
        }