private void UACInviteTransaction_TransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { // BranchId for 2xx responses needs to be a new one, non-2xx final responses use same one as original request. if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode < 299) { if (_sendOkAckManually == false) { Send2xxAckRequest(null, null); } } else { // ACK for non 2xx response is part of the INVITE transaction and gets routed to the same endpoint as the INVITE. var ackRequest = GetInTransactionACKRequest(sipResponse, m_transactionRequest.URI, LocalSIPEndPoint); base.SendRequest(RemoteEndPoint, ackRequest); } UACInviteTransactionFinalResponseReceived?.Invoke(localSIPEndPoint, remoteEndPoint, sipTransaction, sipResponse); if (CDR != null) { SIPEndPoint localEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedOn) ?? localSIPEndPoint; SIPEndPoint remoteEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedFrom) ?? remoteEndPoint; CDR.Answered(sipResponse.StatusCode, sipResponse.Status, sipResponse.ReasonPhrase, localEP, remoteEP); } } catch (Exception excp) { logger.Error("Exception UACInviteTransaction_TransactionFinalResponseReceived. " + excp.Message); } }
private void UACInviteTransaction_TransactionRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPRequest sipRequest) { logger.Warn("UACInviteTransaction received unexpected request, " + sipRequest.Method + " from " + remoteEndPoint.ToString() + ", ignoring."); }
private void SIPCancelTransaction_TransactionRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPRequest sipRequest) { try { //logger.Debug("CANCEL request received, attempting to locate and cancel transaction."); //UASInviteTransaction originalTransaction = (UASInviteTransaction)GetTransaction(GetRequestTransactionId(sipRequest.Header.Via.TopViaHeader.Branch, SIPMethodsEnum.INVITE)); SIPResponse cancelResponse; if (m_originalTransaction != null) { //logger.Debug("Transaction found to cancel " + originalTransaction.TransactionId + " type " + originalTransaction.TransactionType + "."); m_originalTransaction.CancelCall(); cancelResponse = GetCancelResponse(sipRequest, SIPResponseStatusCodesEnum.Ok); } else { cancelResponse = GetCancelResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist); } //UpdateTransactionState(SIPTransactionStatesEnum.Completed); SendFinalResponse(cancelResponse); } catch (Exception excp) { logger.Error("Exception SIPCancelTransaction GotRequest. " + excp.Message); } }
private void UACInviteTransaction_TransactionInformationResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { UACInviteTransactionInformationResponseReceived?.Invoke(localSIPEndPoint, remoteEndPoint, sipTransaction, sipResponse); if (CDR != null) { SIPEndPoint localEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedOn) ?? localSIPEndPoint; SIPEndPoint remoteEP = SIPEndPoint.TryParse(sipResponse.Header.ProxyReceivedFrom) ?? remoteEndPoint; CDR.Progress(sipResponse.Status, sipResponse.ReasonPhrase, localEP, remoteEP); } } catch (Exception excp) { logger.Error("Exception UACInviteTransaction_TransactionInformationResponseReceived. " + excp.Message); } }
private void UASInviteTransaction_TransactionResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { //logger.Warn("UASInviteTransaction received unexpected response, " + sipResponse.ReasonPhrase + " from " + remoteEndPoint.ToString() + ", ignoring."); }
private void SIPCancelTransaction_TransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { if (sipResponse.StatusCode < 200) { logger.Warn("A SIP CANCEL transaction received an unexpected SIP information response " + sipResponse.ReasonPhrase + "."); } else { if (CancelTransactionFinalResponseReceived != null) { CancelTransactionFinalResponseReceived(localSIPEndPoint, remoteEndPoint, sipTransaction, sipResponse); } } }
void transaction_TransactionRemoved(SIPTransaction sipTransaction) { Console.WriteLine(sipTransaction.GetType() + " Removed (" + sipTransaction.TransactionId + ")"); }
private void SIPNonInviteTransaction_TransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { NonInviteTransactionFinalResponseReceived?.Invoke(localSIPEndPoint, remoteEndPoint, this, sipResponse); }
void transaction_TransactionTraceMessage(SIPTransaction sipTransaction, string message) { //Console.WriteLine(sipTransaction.GetType() + " Trace (" + sipTransaction.TransactionId + "): " + message); }
void transaction_TransactionStateChanged(SIPTransaction sipTransaction) { Console.WriteLine(sipTransaction.GetType() + " State Change (" + sipTransaction.TransactionId + "): " + sipTransaction.TransactionState); }
void SetTransactionTraceEvents(SIPTransaction transaction) { transaction.TransactionRemoved += new SIPTransactionRemovedDelegate(transaction_TransactionRemoved); transaction.TransactionStateChanged += new SIPTransactionStateChangeDelegate(transaction_TransactionStateChanged); transaction.TransactionTraceMessage += new SIPTransactionTraceMessageDelegate(transaction_TransactionTraceMessage); }
public void RemoveExpiredTransactions() { try { lock (m_transactions) { List <string> expiredTransactionIds = new List <string>(); foreach (SIPTransaction transaction in m_transactions.Values) { if (transaction.TransactionType == SIPTransactionTypesEnum.Invite) { if (transaction.TransactionState == SIPTransactionStatesEnum.Confirmed) { // Need to wait until the transaction timeout period is reached in case any ACK re-transmits are received. if (DateTime.Now.Subtract(transaction.CompletedAt).TotalMilliseconds >= m_t6) { expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.TransactionState == SIPTransactionStatesEnum.Completed) { if (DateTime.Now.Subtract(transaction.CompletedAt).TotalMilliseconds >= m_t6) { expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.HasTimedOut) { // For INVITES need to give timed out transactions time to send the reliable repsonses and receive the ACKs. if (DateTime.Now.Subtract(transaction.TimedOutAt).TotalSeconds >= m_t6) { expiredTransactionIds.Add(transaction.TransactionId); } } else if (transaction.TransactionState == SIPTransactionStatesEnum.Proceeding) { if (DateTime.Now.Subtract(transaction.Created).TotalMilliseconds >= m_maxRingTime) { // INVITE requests that have been ringing too long. transaction.HasTimedOut = true; transaction.TimedOutAt = DateTime.Now; transaction.DeliveryPending = false; transaction.DeliveryFailed = true; transaction.FireTransactionTimedOut(); } } else if (DateTime.Now.Subtract(transaction.Created).TotalMilliseconds >= m_t6) { //logger.Debug("INVITE transaction (" + transaction.TransactionId + ") " + transaction.TransactionRequestURI.ToString() + " in " + transaction.TransactionState + " has been alive for " + DateTime.Now.Subtract(transaction.Created).TotalSeconds.ToString("0") + "."); if (transaction.TransactionState == SIPTransactionStatesEnum.Calling || transaction.TransactionState == SIPTransactionStatesEnum.Trying) { transaction.HasTimedOut = true; transaction.TimedOutAt = DateTime.Now; transaction.DeliveryPending = false; transaction.DeliveryFailed = true; transaction.FireTransactionTimedOut(); } } } else if (transaction.HasTimedOut) { expiredTransactionIds.Add(transaction.TransactionId); } else if (DateTime.Now.Subtract(transaction.Created).TotalMilliseconds >= m_t6) { if (transaction.TransactionState == SIPTransactionStatesEnum.Calling || transaction.TransactionState == SIPTransactionStatesEnum.Trying || transaction.TransactionState == SIPTransactionStatesEnum.Proceeding) { //logger.Warn("Timed out transaction in SIPTransactionEngine, should have been timed out in the SIP Transport layer. " + transaction.TransactionRequest.Method + "."); transaction.DeliveryPending = false; transaction.DeliveryFailed = true; transaction.TimedOutAt = DateTime.Now; transaction.HasTimedOut = true; transaction.FireTransactionTimedOut(); } expiredTransactionIds.Add(transaction.TransactionId); } } foreach (string transactionId in expiredTransactionIds) { if (m_transactions.ContainsKey(transactionId)) { SIPTransaction expiredTransaction = m_transactions[transactionId]; expiredTransaction.FireTransactionRemoved(); RemoveTransaction(expiredTransaction); } } } //PrintPendingTransactions(); } catch (Exception excp) { logger.Error("Exception RemoveExpiredTransaction. " + excp.Message); } }
private void UASInviteTransaction_TransactionRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPRequest sipRequest) { try { if (TransactionState == SIPTransactionStatesEnum.Terminated) { logger.Debug("Request received by UASInviteTransaction for a terminated transaction, ignoring."); } else if (sipRequest.Method != SIPMethodsEnum.INVITE) { logger.Warn("Unexpected " + sipRequest.Method + " passed to UASInviteTransaction."); } else { if (TransactionState != SIPTransactionStatesEnum.Trying) { SIPResponse tryingResponse = GetInfoResponse(m_transactionRequest, SIPResponseStatusCodesEnum.Trying); SendInformationalResponse(tryingResponse); } // Notify new call subscribers. if (NewCallReceived != null) { NewCallReceived(localSIPEndPoint, remoteEndPoint, this, sipRequest); } else { // Nobody wants the call so return an error response. SIPResponse declinedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Decline, "Nothing listening"); SendFinalResponse(declinedResponse); } } } catch (Exception excp) { logger.Error("Exception UASInviteTransaction GotRequest. " + excp.Message); } }
private void SIPNonInviteTransaction_TransactionTimedOut(SIPTransaction sipTransaction) { NonInviteTransactionTimedOut?.Invoke(this); }
/// <summary> /// Transaction matching see RFC3261 17.1.3 & 17.2.3 for matching client and server transactions respectively. /// IMPORTANT NOTE this transaction matching applies to all requests and responses EXCEPT ACK requests to 2xx responses see 13.2.2.4. /// For ACK's to 2xx responses the ACK represents a separate transaction. However for a UAS sending an INVITE response the ACK still has to be /// matched to an existing server transaction in order to transition it to a Confirmed state. /// /// ACK's: /// - The ACK for a 2xx response will have the same CallId, From Tag and To Tag. /// - An ACK for a non-2xx response will have the same branch ID as the INVITE whose response it acknowledges. /// </summary> /// <param name="sipRequest"></param> /// <returns></returns> public SIPTransaction GetTransaction(SIPRequest sipRequest) { // The branch is mandatory but it doesn't stop some UA's not setting it. if (sipRequest.Header.Vias.TopViaHeader.Branch == null || sipRequest.Header.Vias.TopViaHeader.Branch.Trim().Length == 0) { return(null); } var transactionMethod = (sipRequest.Method != SIPMethodsEnum.ACK) ? sipRequest.Method : SIPMethodsEnum.INVITE; var transactionId = SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, transactionMethod); var contactAddress = (sipRequest.Header.Contact != null && sipRequest.Header.Contact.Count > 0) ? sipRequest.Header.Contact[0].ToString() : "no contact"; lock (m_transactions) { //if (transactionMethod == SIPMethodsEnum.ACK) //{ //logger.Info("Matching ACK with contact=" + contactAddress + ", cseq=" + sipRequest.Header.CSeq + "."); //} if (transactionId != null && m_transactions.ContainsKey(transactionId)) { //if (transactionMethod == SIPMethodsEnum.ACK) //{ //logger.Info("ACK for contact=" + contactAddress + ", cseq=" + sipRequest.Header.CSeq + " was matched by branchid."); //} return(m_transactions[transactionId]); } else { // No normal match found so look fo a 2xx INVITE response waiting for an ACK. if (sipRequest.Method == SIPMethodsEnum.ACK) { logger.Debug("Looking for ACK transaction, branchid=" + sipRequest.Header.Vias.TopViaHeader.Branch + "."); foreach (SIPTransaction transaction in m_transactions.Values) { // According to the standard an ACK should only not get matched by the branchid on the original INVITE for a non-2xx response. However // my Cisco phone created a new branchid on ACKs to 487 responses and since the Cisco also used the same Call-ID and From tag on the initial // unauthenticated request and the subsequent authenticated request the condition below was found to be the best way to match the ACK. /*if (transaction.TransactionType == SIPTransactionTypesEnum.Invite && transaction.TransactionFinalResponse != null && transaction.TransactionState == SIPTransactionStatesEnum.Completed) * { * if (transaction.TransactionFinalResponse.Header.CallId == sipRequest.Header.CallId && * transaction.TransactionFinalResponse.Header.To.ToTag == sipRequest.Header.To.ToTag && * transaction.TransactionFinalResponse.Header.From.FromTag == sipRequest.Header.From.FromTag) * { * return transaction; * } * }*/ // As an experiment going to try matching on the Call-ID. This field seems to be unique and therefore the chance // of collisions seemingly very slim. As a safeguard if there happen to be two transactions with the same Call-ID in the list the match will not be made. // One case where the Call-Id match breaks down is for in-Dialogue requests in that case there will be multiple transactions with the same Call-ID and tags. //if (transaction.TransactionType == SIPTransactionTypesEnum.Invite && transaction.TransactionFinalResponse != null && transaction.TransactionState == SIPTransactionStatesEnum.Completed) if (transaction.TransactionType == SIPTransactionTypesEnum.Invite && transaction.TransactionFinalResponse != null) { if (transaction.TransactionRequest.Header.CallId == sipRequest.Header.CallId && transaction.TransactionFinalResponse.Header.To.ToTag == sipRequest.Header.To.ToTag && transaction.TransactionFinalResponse.Header.From.FromTag == sipRequest.Header.From.FromTag && transaction.TransactionFinalResponse.Header.CSeq == sipRequest.Header.CSeq) { //logger.Info("ACK for contact=" + contactAddress + ", cseq=" + sipRequest.Header.CSeq + " was matched by callid, tags and cseq."); return(transaction); } else if (transaction.TransactionRequest.Header.CallId == sipRequest.Header.CallId && transaction.TransactionFinalResponse.Header.CSeq == sipRequest.Header.CSeq && IsCallIdUniqueForPending(sipRequest.Header.CallId)) { string requestEndPoint = (sipRequest.RemoteSIPEndPoint != null) ? sipRequest.RemoteSIPEndPoint.ToString() : " ? "; //logger.Info("ACK for contact=" + contactAddress + ", cseq=" + sipRequest.Header.CSeq + " was matched using Call-ID mechanism (to tags: " + transaction.TransactionFinalResponse.Header.To.ToTag + "=" + sipRequest.Header.To.ToTag + ", from tags:" + transaction.TransactionFinalResponse.Header.From.FromTag + "=" + sipRequest.Header.From.FromTag + ")."); return(transaction); } } } logger.Info("ACK for contact=" + contactAddress + ", cseq=" + sipRequest.Header.CSeq + " was not matched."); } return(null); } } }
private void SIPNonInviteTransaction_TransactionRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPRequest sipRequest) { NonInviteRequestReceived?.Invoke(localSIPEndPoint, remoteEndPoint, this, sipRequest); }
private void SIPCancelTransaction_TransactionRemoved(SIPTransaction transaction) { // Remove event handlers. CancelTransactionFinalResponseReceived = null; }
private void SIPNonInviteTransaction_TransactionRequestRetransmit(SIPTransaction sipTransaction, SIPRequest sipRequest, int retransmitNumber) { NonInviteTransactionRequestRetransmit?.Invoke(sipTransaction, sipRequest, retransmitNumber); }
private void UASInviteTransaction_TransactionTimedOut(SIPTransaction sipTransaction) { UASInviteTransactionTimedOut?.Invoke(this); CDR?.TimedOut(); }