/// <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> /// 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(); }
/// <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 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> /// 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 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> /// 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> /// 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); } }
/// <summary> /// Cleans up any resources being used. /// </summary> public virtual void Dispose() { lock (m_pLock){ if (this.State == SIP_DialogState.Disposed) { return; } SetState(SIP_DialogState.Disposed, true); this.RequestReceived = null; m_pStack = null; m_CallID = null; m_LocalTag = null; m_RemoteTag = null; m_pLocalUri = null; m_pRemoteUri = null; m_pLocalContact = null; m_pRemoteTarget = null; m_pRouteSet = null; m_pFlow = null; } }
/// <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); } } }); SIP_Dialog dialog = MatchDialog(request); if (dialog != null) { dialog.AddTransaction(transaction); } return(transaction); } }
/// <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) { #region Accepeted if (this.State == SIP_TransactionState.Accpeted) { } #endregion #region Completed else if (this.State == SIP_TransactionState.Completed) { /* 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. */ 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) stopped."); } } 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) stopped."); } } // 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 trigger after " + m_pTimerI.Interval + "."); } m_pTimerI.Enabled = true; } #endregion } #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); } } }
/// <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) { }
/// <summary> /// Initializes dialog. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="transaction">Owner transaction.</param> /// <param name="response">SIP response what caused dialog creation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception> internal protected virtual void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response) { if (stack == null) { throw new ArgumentNullException("stack"); } if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } m_pStack = stack; #region UAS /* RFC 3261 12.1.1. * The UAS then constructs the state of the dialog. This state MUST be * maintained for the duration of the dialog. * * If the request arrived over TLS, and the Request-URI contained a SIPS * URI, the "secure" flag is set to TRUE. * * The route set MUST be set to the list of URIs in the Record-Route * header field from the request, taken in order and preserving all URI * parameters. If no Record-Route header field is present in the * request, the route set MUST be set to the empty set. This route set, * even if empty, overrides any pre-existing route set for future * requests in this dialog. The remote target MUST be set to the URI * from the Contact header field of the request. * * The remote sequence number MUST be set to the value of the sequence * number in the CSeq header field of the request. The local sequence * number MUST be empty. The call identifier component of the dialog ID * MUST be set to the value of the Call-ID in the request. The local * tag component of the dialog ID MUST be set to the tag in the To field * in the response to the request (which always includes a tag), and the * remote tag component of the dialog ID MUST be set to the tag from the * From field in the request. A UAS MUST be prepared to receive a * request without a tag in the From field, in which case the tag is * considered to have a value of null. * * This is to maintain backwards compatibility with RFC 2543, which * did not mandate From tags. * * The remote URI MUST be set to the URI in the From field, and the * local URI MUST be set to the URI in the To field. */ if (transaction is SIP_ServerTransaction) { m_IsSecure = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[])Net_Utils.ReverseArray(transaction.Request.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri; m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber; m_LocalSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = response.To.Tag; m_RemoteTag = transaction.Request.From.Tag; m_pRemoteUri = transaction.Request.From.Address.Uri; m_pLocalUri = transaction.Request.To.Address.Uri; m_pLocalContact = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri; List <string> allow = new List <string>(); foreach (SIP_t_Method m in response.Allow.GetAllValues()) { allow.Add(m.Method); } m_pRemoteAllow = allow.ToArray(); List <string> supported = new List <string>(); foreach (SIP_t_OptionTag s in response.Supported.GetAllValues()) { supported.Add(s.OptionTag); } m_pRemoteSupported = supported.ToArray(); } #endregion #region UAC /* RFC 3261 12.1.2. * When a UAC receives a response that establishes a dialog, it * constructs the state of the dialog. This state MUST be maintained * for the duration of the dialog. * * If the request was sent over TLS, and the Request-URI contained a * SIPS URI, the "secure" flag is set to TRUE. * * The route set MUST be set to the list of URIs in the Record-Route * header field from the response, taken in reverse order and preserving * all URI parameters. If no Record-Route header field is present in * the response, the route set MUST be set to the empty set. This route * set, even if empty, overrides any pre-existing route set for future * requests in this dialog. The remote target MUST be set to the URI * from the Contact header field of the response. * * The local sequence number MUST be set to the value of the sequence * number in the CSeq header field of the request. The remote sequence * number MUST be empty (it is established when the remote UA sends a * request within the dialog). The call identifier component of the * dialog ID MUST be set to the value of the Call-ID in the request. * The local tag component of the dialog ID MUST be set to the tag in * the From field in the request, and the remote tag component of the * dialog ID MUST be set to the tag in the To field of the response. A * UAC MUST be prepared to receive a response without a tag in the To * field, in which case the tag is considered to have a value of null. * * This is to maintain backwards compatibility with RFC 2543, which * did not mandate To tags. * * The remote URI MUST be set to the URI in the To field, and the local * URI MUST be set to the URI in the From field. */ else { // TODO: Validate request or client transaction must do it ? m_IsSecure = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[])Net_Utils.ReverseArray(response.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri; m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber; m_RemoteSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = transaction.Request.From.Tag; m_RemoteTag = response.To.Tag; m_pRemoteUri = transaction.Request.To.Address.Uri; m_pLocalUri = transaction.Request.From.Address.Uri; m_pLocalContact = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri; List <string> allow = new List <string>(); foreach (SIP_t_Method m in response.Allow.GetAllValues()) { allow.Add(m.Method); } m_pRemoteAllow = allow.ToArray(); List <string> supported = new List <string>(); foreach (SIP_t_OptionTag s in response.Supported.GetAllValues()) { supported.Add(s.OptionTag); } m_pRemoteSupported = supported.ToArray(); } #endregion m_pFlow = transaction.Flow; AddTransaction(transaction); }
/// <summary> /// This method is called when REGISTER has finished. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pRegisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) { m_pFlow = e.ClientTransaction.Flow; if (e.Response.StatusCodeType == SIP_StatusCodeType.Provisional) { return; } else if (e.Response.StatusCodeType == SIP_StatusCodeType.Success) { m_pContacts.Clear(); foreach (SIP_t_ContactParam c in e.Response.Contact.GetAllValues()) { m_pContacts.Add(c.Address.Uri); } SetState(SIP_UA_RegistrationState.Registered); OnRegistered(); m_pFlow.SendKeepAlives = true; } else { SetState(SIP_UA_RegistrationState.Error); OnError(e); } // REMOVE ME: if (this.AutoFixContact && (m_pContact is SIP_Uri)) { // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value. SIP_Uri cContact = ((SIP_Uri)m_pContact); IPAddress cContactIP = Net_Utils.IsIPAddress(cContact.Host) ? IPAddress.Parse(cContact.Host) : null; SIP_t_ViaParm via = e.Response.Via.GetTopMostValue(); if (via != null && cContactIP != null) { IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP, via.RPort > 0 ? via.RPort : cContact.Port); if (!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort) { // Unregister old contact. BeginUnregister(false); // Fix contact. cContact.Host = ep.Address.ToString(); cContact.Port = ep.Port; m_pRegisterSender.Dispose(); m_pRegisterSender = null; BeginRegister(m_AutoRefresh); return; } } } if (m_AutoRefresh) { // Set registration refresh timer. m_pTimer.Enabled = true; } m_pRegisterSender.Dispose(); m_pRegisterSender = null; }
/// <summary> /// Is called when client transactions receives response. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void ClientTransaction_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) { lock (m_pLock){ m_pFlow = e.ClientTransaction.Request.Flow; if (e.Response.StatusCode == 401 || e.Response.StatusCode == 407) { // Check if authentication failed(We sent authorization data and it's challenged again, // probably user name or password inccorect) bool hasFailedAuthorization = false; foreach (SIP_t_Challenge challange in e.Response.WWWAuthenticate.GetAllValues()) { foreach (SIP_t_Credentials credentials in m_pTransaction.Request.Authorization.GetAllValues()) { if (new Auth_HttpDigest(challange.AuthData, "").Realm == new Auth_HttpDigest(credentials.AuthData, "").Realm) { hasFailedAuthorization = true; break; } } } foreach (SIP_t_Challenge challange in e.Response.ProxyAuthenticate.GetAllValues()) { foreach (SIP_t_Credentials credentials in m_pTransaction.Request.ProxyAuthorization.GetAllValues()) { if (new Auth_HttpDigest(challange.AuthData, "").Realm == new Auth_HttpDigest(credentials.AuthData, "").Realm) { hasFailedAuthorization = true; break; } } } // Authorization failed, pass response to UA. if (hasFailedAuthorization) { OnResponseReceived(e.Response); } // Try to authorize challanges. else { SIP_Request request = m_pRequest.Copy(); /* RFC 3261 22.2. * When a UAC resubmits a request with its credentials after receiving a * 401 (Unauthorized) or 407 (Proxy Authentication Required) response, * it MUST increment the CSeq header field value as it would normally * when sending an updated request. */ request.CSeq = new SIP_t_CSeq(m_pStack.ConsumeCSeq(), request.CSeq.RequestMethod); // All challanges authorized, resend request. if (Authorize(request, e.Response, this.Credentials.ToArray())) { SIP_Flow flow = m_pTransaction.Flow; CleanUpActiveTransaction(); SendToFlow(flow, request); } // We don't have credentials for one or more challenges. else { OnResponseReceived(e.Response); } } } else { OnResponseReceived(e.Response); if (e.Response.StatusCodeType != SIP_StatusCodeType.Provisional) { OnCompleted(); } } } }