/// <summary> /// Processes specified response through this dialog. /// </summary> /// <param name="response">SIP response to process.</param> /// <returns>Returns true if this dialog processed specified response, otherwise false.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null.</exception> internal protected virtual bool ProcessResponse(SIP_Response response) { if (response == null) { throw new ArgumentNullException("response"); } return(false); }
/// <summary> /// Gets existing or creates new dialog. /// </summary> /// <param name="transaction">Owner transaction what forces to create dialog.</param> /// <param name="response">Response what forces to create dialog.</param> /// <returns>Returns dialog.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>transaction</b> or <b>response</b> is null.</exception> public SIP_Dialog GetOrCreateDialog(SIP_Transaction transaction, SIP_Response response) { if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } string dialogID = ""; if (transaction is SIP_ServerTransaction) { dialogID = response.CallID + "-" + response.To.Tag + "-" + response.From.Tag; } else { dialogID = response.CallID + "-" + response.From.Tag + "-" + response.To.Tag; } lock (m_pDialogs){ SIP_Dialog dialog = null; m_pDialogs.TryGetValue(dialogID, out dialog); // Dialog doesn't exist, create it. if (dialog == null) { if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE) { dialog = new SIP_Dialog_Invite(); } else if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.REFER) { dialog = new SIP_Dialog_Refer(); } else { throw new ArgumentException("Method '" + response.CSeq.RequestMethod + "' has no dialog handler."); } dialog.Init(m_pStack, transaction, response); dialog.StateChanged += delegate(object s, EventArgs a){ if (dialog.State == SIP_DialogState.Terminated) { m_pDialogs.Remove(dialog.ID); } }; m_pDialogs.Add(dialog.ID, dialog); } return(dialog); } }
/// <summary> /// Adds specified response to transaction responses collection. /// </summary> /// <param name="response">SIP response.</param> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception> protected void AddResponse(SIP_Response response) { if (response == null) { throw new ArgumentNullException("response"); } // Don't store more than 15 responses, otherwise hacker may try todo buffer overrun with provisional responses. if (m_pResponses.Count < 15 || response.StatusCode >= 200) { m_pResponses.Add(response); } }
/// <summary> /// Default constructor. /// </summary> /// <param name="transaction">Server transaction.</param> /// <param name="response">SIP response.</param> /// <exception cref="ArgumentNullException">Is raised when any of the arguments is null.</exception> public SIP_ResponseSentEventArgs(SIP_ServerTransaction transaction, SIP_Response response) { if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } m_pTransaction = transaction; m_pResponse = response; }
/// <summary> /// Sends specified response to flow remote end point. /// </summary> /// <param name="response">SIP response 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>response</b> is null reference.</exception> public void Send(SIP_Response response) { lock (m_pLock){ if (m_IsDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (response == null) { throw new ArgumentNullException("response"); } SendInternal(response.ToByteData()); m_LastPing = DateTime.Now; } }
/// <summary> /// Parses SIP_Response from stream. /// </summary> /// <param name="stream">Stream what contains valid SIP response.</param> /// <returns>Returns parsed SIP_Response obeject.</returns> /// <exception cref="ArgumentNullException">Raised when <b>stream</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public static SIP_Response Parse(Stream stream) { /* Syntax: * SIP-Version SP Status-Code SP Reason-Phrase * SIP-Message */ if (stream == null) { throw new ArgumentNullException("stream"); } SIP_Response retVal = new SIP_Response(); // Parse Response-line StreamLineReader r = new StreamLineReader(stream); r.Encoding = "utf-8"; string[] version_code_text = r.ReadLineString().Split(new char[] { ' ' }, 3); if (version_code_text.Length != 3) { throw new SIP_ParseException("Invalid SIP Status-Line syntax ! Syntax: {SIP-Version SP Status-Code SP Reason-Phrase}."); } // SIP-Version try{ retVal.SipVersion = Convert.ToDouble(version_code_text[0].Split('/')[1], System.Globalization.NumberFormatInfo.InvariantInfo); } catch { throw new SIP_ParseException("Invalid Status-Line SIP-Version value !"); } // Status-Code try{ retVal.StatusCode = Convert.ToInt32(version_code_text[1]); } catch { throw new SIP_ParseException("Invalid Status-Line Status-Code value !"); } // Reason-Phrase retVal.ReasonPhrase = version_code_text[2]; // Parse SIP-Message retVal.InternalParse(stream); return(retVal); }
/// <summary> /// Is raised when INVITE 100 (Trying) response must be sent if no response sent by transaction user. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pTimer100_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { lock (this.SyncRoot){ // RFC 3261 17.2.1. TU didn't generate response in 200 ms, send '100 Trying' to stop request retransmission. if (this.State == SIP_TransactionState.Proceeding && this.Responses.Length == 0) { /* RFC 3261 17.2.1. * The 100 (Trying) response is constructed according to the procedures in Section 8.2.6, except that the * insertion of tags in the To header field of the response (when none was present in the request) * is downgraded from MAY to SHOULD NOT. * * RFC 3261 8.2.6. * When a 100 (Trying) response is generated, any Timestamp header field present in the request MUST * be copied into this 100 (Trying) response. If there is a delay in generating the response, the UAS * SHOULD add a delay value into the Timestamp value in the response. This value MUST contain the difference * between the time of sending of the response and receipt of the request, measured in seconds. */ SIP_Response tryingResponse = this.Stack.CreateResponse(SIP_ResponseCodes.x100_Trying, this.Request); if (this.Request.Timestamp != null) { tryingResponse.Timestamp = new SIP_t_Timestamp(this.Request.Timestamp.Time, (DateTime.Now - this.CreateTime).Seconds); } try{ this.Stack.TransportLayer.SendResponse(this, tryingResponse); } catch (Exception x) { OnTransportError(x); SetState(SIP_TransactionState.Terminated); return; } } if (m_pTimer100 != null) { m_pTimer100.Dispose(); m_pTimer100 = null; } } }
/// <summary> /// Matches SIP response to client transaction. If not matching transaction found, returns null. /// </summary> /// <param name="response">SIP response to match.</param> internal SIP_ClientTransaction MatchClientTransaction(SIP_Response response) { /* RFC 3261 17.1.3 Matching Responses to Client Transactions. * 1. If the response has the same value of the branch parameter in * the top Via header field as the branch parameter in the top * Via header field of the request that created the transaction. * * 2. If the method parameter in the CSeq header field matches the * method of the request that created the transaction. The * method is needed since a CANCEL request constitutes a * different transaction, but shares the same value of the branch * parameter. */ SIP_ClientTransaction retVal = null; string transactionID = response.Via.GetTopMostValue().Branch + "-" + response.CSeq.RequestMethod; lock (m_pClientTransactions){ m_pClientTransactions.TryGetValue(transactionID, out retVal); } return(retVal); }
/// <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> /// Sends specified response to remote party. /// </summary> /// <param name="response">SIP response to send.</param> /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception> /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception> public void SendResponse(SIP_Response response) { lock (this.SyncRoot){ if (this.State == SIP_TransactionState.Disposed) { throw new ObjectDisposedException(this.GetType().Name); } if (response == null) { throw new ArgumentNullException("response"); } try{ #region INVITE /* RFC 6026 7.1. INVITE server transaction. (Udpates RFC 3261) * |INVITE |pass INV to TU * INVITE V send 100 if TU won't in 200 ms * send response+------------+ +--------| |--------+ 101-199 from TU | | | | send response +------->| |<-------+ | Proceeding | | |--------+ Transport Err. | | | Inform TU | |<-------+ +------------+ | 300-699 from TU | |2xx from TU | send response | |send response +--------------+ +------------+ | | | INVITE V Timer G fires | | send response +-----------+ send response | +--------| |--------+ | | | | | | +------->| Completed |<-------+ INVITE | Transport Err. | | - | Inform TU +--------| |----+ +-----+ | +---+ | +-----------+ | ACK | | v | v | ^ | | - | +------------+ | | | | | | |---+ ACK +----------+ | | +->| Accepted | | to TU | Transport Err. | | | |<--+ | Inform TU | V +------------+ | +-----------+ | ^ | | | | | | | | | Confirmed | | +-----+ | | | | 2xx from TU | Timer H fires | +-----------+ | send response | - | | | | | Timer I fires | | | - | Timer L fires | V | - | +------------+ | | | |<----+ +------->| Terminated | | | +------------+ | */ if (this.Method == SIP_Methods.INVITE) { #region Proceeding if (this.State == SIP_TransactionState.Proceeding) { AddResponse(response); // 1xx if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); } // 2xx else if (response.StatusCodeType == SIP_StatusCodeType.Success) { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); SetState(SIP_TransactionState.Accpeted); /* RFC 6025 7.1. * When the "Accepted" state is entered, timer L MUST be set to fire in 64*T1. */ m_pTimerL = new TimerEx(64 * SIP_TimerConstants.T1); m_pTimerL.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerL_Elapsed); m_pTimerL.Enabled = true; // Log if (this.Stack.Logger != null) { this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer L(ACK wait) started, will trigger after " + m_pTimerL.Interval + "."); } } // 3xx - 6xx else { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); SetState(SIP_TransactionState.Completed); /* RFC 3261 17.2.1. * For unreliable transports, timer G is set to fire in T1 seconds, and is not set to fire for reliable transports. */ if (!this.Flow.IsReliable) { m_pTimerG = new TimerEx(SIP_TimerConstants.T1, false); m_pTimerG.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerG_Elapsed); m_pTimerG.Enabled = true; // 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) started, will trigger after " + m_pTimerG.Interval + "."); } } /* RFC 3261 17.2.1. * When the "Completed" state is entered, timer H MUST be set to fire in 64*T1 seconds for all transports. */ m_pTimerH = new TimerEx(64 * SIP_TimerConstants.T1); m_pTimerH.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerH_Elapsed); m_pTimerH.Enabled = true; // 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) started, will trigger after " + m_pTimerH.Interval + "."); } } } #endregion #region Accepted else if (this.State == SIP_TransactionState.Accpeted) { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); } #endregion #region Completed else if (this.State == SIP_TransactionState.Completed) { // We do nothing here, we just wait ACK to arrive. } #endregion #region Confirmed else if (this.State == SIP_TransactionState.Confirmed) { // We do nothing, just wait ACK retransmissions. } #endregion #region Terminated else if (this.State == SIP_TransactionState.Terminated) { // We should never rreach here, but if so, skip it. } #endregion } #endregion #region Non-INVITE /* RFC 3261 17.2.2. |Request received |pass to TU * V +-----------+ | | | Trying |-------------+ | | | +-----------+ |200-699 from TU | |send response |1xx from TU | |send response | | | | Request V 1xx from TU | | send response+-----------+send response| +--------| |--------+ | | | Proceeding| | | +------->| |<-------+ | +<--------------| | | |Trnsprt Err +-----------+ | |Inform TU | | | | | | |200-699 from TU | | |send response | | Request V | | send response+-----------+ | | +--------| | | | | | Completed |<------------+ | +------->| | +<--------------| | |Trnsprt Err +-----------+ |Inform TU | | |Timer J fires | |- | | | V | +-----------+ | | | +-------------->| Terminated| | | +-----------+ */ else { #region Trying if (this.State == SIP_TransactionState.Trying) { AddResponse(response); // 1xx if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); SetState(SIP_TransactionState.Proceeding); } // 2xx - 6xx else { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); SetState(SIP_TransactionState.Completed); /* RFC 3261 17.2.2. * When the server transaction enters the "Completed" state, it MUST set * Timer J to fire in 64*T1 seconds for unreliable transports, and zero * seconds for reliable transports. */ m_pTimerJ = new TimerEx(64 * SIP_TimerConstants.T1, false); m_pTimerJ.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerJ_Elapsed); m_pTimerJ.Enabled = true; // Log if (this.Stack.Logger != null) { this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will trigger after " + m_pTimerJ.Interval + "."); } } } #endregion #region Proceeding else if (this.State == SIP_TransactionState.Proceeding) { AddResponse(response); // 1xx if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); } // 2xx - 6xx else { this.Stack.TransportLayer.SendResponse(this, response); OnResponseSent(response); SetState(SIP_TransactionState.Completed); /* RFC 3261 17.2.2. * When the server transaction enters the "Completed" state, it MUST set * Timer J to fire in 64*T1 seconds for unreliable transports, and zero * seconds for reliable transports. */ m_pTimerJ = new TimerEx(64 * SIP_TimerConstants.T1, false); m_pTimerJ.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerJ_Elapsed); m_pTimerJ.Enabled = true; // Log if (this.Stack.Logger != null) { this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will trigger after " + m_pTimerJ.Interval + "."); } } } #endregion #region Completed else if (this.State == SIP_TransactionState.Completed) { // Do nothing. } #endregion #region Terminated else if (this.State == SIP_TransactionState.Terminated) { // Do nothing. } #endregion } #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> /// 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 override 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"); } base.Init(stack, transaction, response); if (transaction is SIP_ServerTransaction) { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Early, false); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){ if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; /* RFC 3261 13.3.1.4. * If the server retransmits the 2xx response for 64*T1 seconds without * receiving an ACK, the dialog is confirmed, but the session SHOULD be * terminated. */ if (this.State == SIP_DialogState.Early) { this.SetState(SIP_DialogState.Confirmed, true); Terminate("ACK was not received for initial INVITE 2xx response.", true); } else if (this.State == SIP_DialogState.Terminating) { this.SetState(SIP_DialogState.Confirmed, false); Terminate(m_TerminateReason, true); } } }; } else { throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } else { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Confirmed, false); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){ if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we receive 2xx response, dialog will switch to confirmed state. ((SIP_ClientTransaction)transaction).ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs a){ if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Confirmed, true); } }; } else { throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } }
/// <summary> /// Clones this request. /// </summary> /// <returns>Returns new cloned request.</returns> public SIP_Response Copy() { SIP_Response retVal = SIP_Response.Parse(this.ToByteData()); return(retVal); }
/// <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> /// Default constructor. /// </summary> /// <param name="stack">Reference to SIP stack.</param> /// <param name="transaction">Client transaction what response it is. This value can be null if no matching client response.</param> /// <param name="response">Received response.</param> internal SIP_ResponseReceivedEventArgs(SIP_Stack stack, SIP_ClientTransaction transaction, SIP_Response response) { m_pStack = stack; m_pResponse = response; m_pTransaction = transaction; }
/// <summary> /// Creates authorization for each challange in <b>response</b>. /// </summary> /// <param name="request">SIP request where to add authorization values.</param> /// <param name="response">SIP response which challanges to authorize.</param> /// <param name="credentials">Credentials for authorization.</param> /// <returns>Returns true if all challanges were authorized. If any of the challanges was not authorized, returns false.</returns> private bool Authorize(SIP_Request request, SIP_Response response, NetworkCredential[] credentials) { if (request == null) { throw new ArgumentNullException("request"); } if (response == null) { throw new ArgumentNullException("response"); } if (credentials == null) { throw new ArgumentNullException("credentials"); } bool allAuthorized = true; #region WWWAuthenticate foreach (SIP_t_Challenge challange in response.WWWAuthenticate.GetAllValues()) { Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData, request.RequestLine.Method); // Serach credential for the specified challange. NetworkCredential credential = null; foreach (NetworkCredential c in credentials) { if (c.Domain.ToLower() == authDigest.Realm.ToLower()) { credential = c; break; } } // We don't have credential for this challange. if (credential == null) { allAuthorized = false; } // Authorize challange. else { authDigest.UserName = credential.UserName; authDigest.Password = credential.Password; authDigest.CNonce = Auth_HttpDigest.CreateNonce(); authDigest.Uri = request.RequestLine.Uri.ToString(); request.Authorization.Add(authDigest.ToAuthorization()); } } #endregion #region ProxyAuthenticate foreach (SIP_t_Challenge challange in response.ProxyAuthenticate.GetAllValues()) { Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData, request.RequestLine.Method); // Serach credential for the specified challange. NetworkCredential credential = null; foreach (NetworkCredential c in credentials) { if (c.Domain.ToLower() == authDigest.Realm.ToLower()) { credential = c; break; } } // We don't have credential for this challange. if (credential == null) { allAuthorized = false; } // Authorize challange. else { authDigest.UserName = credential.UserName; authDigest.Password = credential.Password; authDigest.CNonce = Auth_HttpDigest.CreateNonce(); authDigest.Uri = request.RequestLine.Uri.ToString(); request.ProxyAuthorization.Add(authDigest.ToAuthorization()); } } #endregion return(allAuthorized); }