private Task <SocketError> ByeServerFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { logger.LogDebug("Response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + sipTransaction.TransactionRequest.URI.ToString() + "."); SIPNonInviteTransaction byeTransaction = sipTransaction as SIPNonInviteTransaction; if ((sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised) && SIPAccount != null) { // Resend BYE with credentials. SIPAuthorisationDigest authRequest = sipResponse.Header.AuthenticationHeader.SIPDigest; SIPURI contactUri = sipResponse.Header.Contact.Any() ? sipResponse.Header.Contact[0].ContactURI : sipResponse.Header.From.FromURI; authRequest.SetCredentials(SIPAccount.SIPUsername, SIPAccount.SIPPassword, contactUri.ToString(), SIPMethodsEnum.BYE.ToString()); SIPRequest authByeRequest = byeTransaction.TransactionRequest; authByeRequest.Header.AuthenticationHeader = new SIPAuthenticationHeader(authRequest); authByeRequest.Header.AuthenticationHeader.SIPDigest.Response = authRequest.Digest; authByeRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId(); authByeRequest.Header.CSeq = authByeRequest.Header.CSeq + 1; SIPNonInviteTransaction authByeTransaction = new SIPNonInviteTransaction(m_sipTransport, authByeRequest, null); authByeTransaction.SendRequest(); } return(Task.FromResult(SocketError.Success)); } catch (Exception excp) { logger.LogError("Exception ByServerFinalResponseReceived. " + excp.Message); return(Task.FromResult(SocketError.Fault)); } }
private Task <SocketError> ByeServerFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { logger.LogDebug("Response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + sipTransaction.TransactionRequest.URI.ToString() + "."); SIPNonInviteTransaction transaction = sipTransaction as SIPNonInviteTransaction; transaction.NonInviteTransactionFinalResponseReceived -= ByeServerFinalResponseReceived; if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised) { string username = (m_sipCallDescriptor.AuthUsername == null || m_sipCallDescriptor.AuthUsername.Trim().Length <= 0 ? m_sipCallDescriptor.Username : m_sipCallDescriptor.AuthUsername); var authRequest = transaction.TransactionRequest.DuplicateAndAuthenticate(sipResponse.Header.AuthenticationHeaders, username, m_sipCallDescriptor.Password); SIPNonInviteTransaction authByeTransaction = new SIPNonInviteTransaction(m_sipTransport, authRequest, m_outboundProxy); authByeTransaction.NonInviteTransactionFailed += (tx, reason) => logger.LogWarning($"Authenticated Bye request for {m_sipCallDescriptor.Uri} failed with {reason}."); authByeTransaction.SendRequest(); } return(Task.FromResult(SocketError.Success)); } catch (Exception excp) { logger.LogError($"Exception ByServerFinalResponseReceived. {excp.Message}"); return(Task.FromResult(SocketError.Fault)); } }
/// <summary> /// Sends in initial notification request for a new subscription where it makes sense to do so. /// </summary> /// <param name="subscribeRequest">The client request that resulted in the subscription creation.</param> /// <param name="sipAccount">The authenticated SIP account that created the subscription.</param> private void SendInitialNotification(SIPRequest subscribeRequest, ISIPAccount sipAccount) { if (SIPEventPackageType.IsValid(subscribeRequest.Header.Event)) { var eventPackage = SIPEventPackageType.Parse(subscribeRequest.Header.Event); if (eventPackage == SIPEventPackagesEnum.MessageSummary) { // Send a dummy message waiting indicator message so that a client can verify a notification request can // be received. var dstUri = subscribeRequest.Header.Contact[0].ContactURI; var accountURI = new SIPURI(sipAccount.SIPUsername, sipAccount.SIPDomain, null, dstUri.Scheme, dstUri.Protocol); var notifyReq = SIPRequest.GetRequest(SIPMethodsEnum.NOTIFY, subscribeRequest.Header.Contact[0].ContactURI, new SIPToHeader(null, accountURI.CopyOf(), CallProperties.CreateNewTag()), new SIPFromHeader(null, accountURI.CopyOf(), CallProperties.CreateNewTag())); notifyReq.Header.CallId = subscribeRequest.Header.CallId; notifyReq.Header.CSeq = subscribeRequest.Header.CSeq++; notifyReq.Header.Server = m_serverAgent; notifyReq.Header.Event = SIPEventPackageType.MESSAGE_SUMMARY_EVENT_VALUE; notifyReq.Header.SubscriptionState = "active"; notifyReq.Header.SetDateHeader(); notifyReq.Header.ContentType = SIPMIMETypes.MWI_CONTENT_TYPE; notifyReq.Body = "Messages-Waiting: no"; // A little trick here is using the remote SIP end point as the destination rather than the Contact URI. // Ideally some extra logic to check for IPv4 NAT should be applied. But since this server is known to // be operating in the cloud and only send NOTIFY requests to Internet clients it should be a reasonable // option. SIPNonInviteTransaction notifyTx = new SIPNonInviteTransaction(m_sipTransport, notifyReq, subscribeRequest.RemoteSIPEndPoint); notifyTx.SendRequest(); } } }
/// <summary> /// Cancels an in progress call. This method should be called prior to the remote user agent server answering the call. /// </summary> public void Cancel() { try { m_callCancelled = true; // Cancel server call. if (m_serverTransaction == null) { logger.LogDebug("Cancelling forwarded call leg " + m_sipCallDescriptor.Uri + ", server transaction has not been created yet no CANCEL request required."); } else if (m_cancelTransaction != null) { if (m_cancelTransaction.TransactionState != SIPTransactionStatesEnum.Completed) { logger.LogDebug("Call " + m_serverTransaction.TransactionRequest.URI.ToString() + " has already been cancelled once, trying again."); m_cancelTransaction.SendRequest(); } else { logger.LogDebug("Call " + m_serverTransaction.TransactionRequest.URI.ToString() + " has already responded to CANCEL, probably overlap in messages not re-sending."); } } else //if (m_serverTransaction.TransactionState == SIPTransactionStatesEnum.Proceeding || m_serverTransaction.TransactionState == SIPTransactionStatesEnum.Trying) { logger.LogDebug("Cancelling forwarded call leg, sending CANCEL to " + m_serverTransaction.TransactionRequest.URI.ToString() + "."); // No response has been received from the server so no CANCEL request necessary, stop any retransmits of the INVITE. m_serverTransaction.CancelCall(); SIPRequest cancelRequest = GetCancelRequest(m_serverTransaction.TransactionRequest); // If auth header is included inside INVITE request, we re-include them inside CANCEL request if (m_serverTransaction.TransactionRequest.Header.HasAuthenticationHeader) { string username = (m_sipCallDescriptor.AuthUsername == null || m_sipCallDescriptor.AuthUsername.Trim().Length <= 0 ? m_sipCallDescriptor.Username : m_sipCallDescriptor.AuthUsername); SIPAuthorisationDigest authDigest = m_serverTransaction.TransactionRequest.Header.AuthenticationHeaders.First().SIPDigest; authDigest.SetCredentials(username, m_sipCallDescriptor.Password, m_sipCallDescriptor.Uri, SIPMethodsEnum.CANCEL.ToString()); var authHeader = new SIPAuthenticationHeader(authDigest); authHeader.SIPDigest.IncrementNonceCount(); authHeader.SIPDigest.Response = authDigest.GetDigest(); cancelRequest.Header.AuthenticationHeaders.Clear(); cancelRequest.Header.AuthenticationHeaders.Add(authHeader); } m_cancelTransaction = new SIPNonInviteTransaction(m_sipTransport, cancelRequest, m_outboundProxy); m_cancelTransaction.SendRequest(); } CallFailed?.Invoke(this, "Call cancelled by user.", null); } catch (Exception excp) { logger.LogError("Exception CancelServerCall. " + excp.Message); } }
private void SendInitialRegister() { try { if (m_attempts >= m_maxRegisterAttempts) { logger.LogWarning($"Registration to {m_sipAccountAOR} reached the maximum number of allowed attempts without a failure condition."); m_isRegistered = false; RegistrationTemporaryFailure?.Invoke(m_sipAccountAOR, "Registration reached the maximum number of allowed attempts."); m_waitForRegistrationMRE.Set(); } else { m_attempts++; SIPEndPoint registrarSIPEndPoint = m_outboundProxy; if (registrarSIPEndPoint == null) { SIPURI uri = SIPURI.ParseSIPURIRelaxed(m_registrarHost); var lookupResult = m_sipTransport.ResolveSIPUriAsync(uri).Result; if (lookupResult == null || lookupResult == SIPEndPoint.Empty) { logger.LogWarning("Could not resolve " + m_registrarHost + " when sending initial registration request."); } else { registrarSIPEndPoint = lookupResult; } } if (registrarSIPEndPoint == null) { logger.LogWarning($"SIPRegistrationAgent could not resolve {m_registrarHost}."); RegistrationFailed?.Invoke(m_sipAccountAOR, $"Could not resolve {m_registrarHost}."); } else { logger.LogDebug($"Initiating registration to {m_registrarHost} at {registrarSIPEndPoint} for {m_sipAccountAOR}."); SIPRequest regRequest = GetRegistrationRequest(); SIPNonInviteTransaction regTransaction = new SIPNonInviteTransaction(m_sipTransport, regRequest, registrarSIPEndPoint); regTransaction.NonInviteTransactionFinalResponseReceived += (lep, rep, tn, rsp) => { ServerResponseReceived(lep, rep, tn, rsp); return(Task.FromResult(SocketError.Success)); }; regTransaction.NonInviteTransactionFailed += RegistrationTransactionFailed; regTransaction.SendRequest(); } } } catch (Exception excp) { logger.LogError("Exception SendInitialRegister to " + m_registrarHost + ". " + excp.Message); RegistrationFailed?.Invoke(m_sipAccountAOR, "Exception SendInitialRegister to " + m_registrarHost + ". " + excp.Message); } }
/// <summary> /// Processes a transfer once the REFER request has been determined. /// </summary> /// <param name="referRequest">The REFER request for the transfer.</param> /// <param name="timeout">Timeout for the transfer request to get accepted.</param> /// <param name="ct">Cancellation token. Can be set to cancel the transfer prior to it being /// accepted or timing out.</param> /// <returns>True if the transfer was accepted by the Transferee or false if not.</returns> private async Task <bool> Transfer(SIPRequest referRequest, TimeSpan timeout, CancellationToken ct) { if (Dialogue == null) { logger.LogWarning("Transfer was called on the SIPUserAgent when no dialogue was available."); return(false); } else { TaskCompletionSource <bool> transferAccepted = new TaskCompletionSource <bool>(); SIPNonInviteTransaction referTx = new SIPNonInviteTransaction(m_transport, referRequest, null); SIPTransactionResponseReceivedDelegate referTxStatusHandler = (localSIPEndPoint, remoteEndPoint, sipTransaction, sipResponse) => { // This handler has to go on a separate thread or the SIPTransport "ProcessInMessage" thread will be blocked. Task.Run(() => { if (sipResponse.Header.CSeqMethod == SIPMethodsEnum.REFER && sipResponse.Status == SIPResponseStatusCodesEnum.Accepted) { logger.LogInformation("Call transfer was accepted by remote server."); transferAccepted.SetResult(true); } else { transferAccepted.SetResult(false); } }, ct); return(Task.FromResult(SocketError.Success)); }; referTx.NonInviteTransactionFinalResponseReceived += referTxStatusHandler; referTx.SendRequest(); await Task.WhenAny(transferAccepted.Task, Task.Delay((int)timeout.TotalMilliseconds, ct)).ConfigureAwait(false); referTx.NonInviteTransactionFinalResponseReceived -= referTxStatusHandler; if (transferAccepted.Task.IsCompleted) { return(transferAccepted.Task.Result); } else { logger.LogWarning($"Call transfer request timed out after {timeout.TotalMilliseconds}ms."); return(false); } } }
private Task <SocketError> ServerResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { string reasonPhrase = (sipResponse.ReasonPhrase.IsNullOrBlank()) ? sipResponse.Status.ToString() : sipResponse.ReasonPhrase; logger.LogDebug("Server response " + sipResponse.StatusCode + " " + reasonPhrase + " received for " + sipTransaction.TransactionRequest.Method + " to " + m_callDescriptor.Uri + "."); if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised) { if (sipResponse.Header.HasAuthenticationHeader) { if ((m_callDescriptor.Username != null || m_callDescriptor.AuthUsername != null) && m_callDescriptor.Password != null) { string username = (m_callDescriptor.AuthUsername != null) ? m_callDescriptor.AuthUsername : m_callDescriptor.Username; SIPRequest authenticatedRequest = sipTransaction.TransactionRequest.DuplicateAndAuthenticate( sipResponse.Header.AuthenticationHeaders, username, m_callDescriptor.Password); SIPNonInviteTransaction authTransaction = new SIPNonInviteTransaction(m_sipTransport, authenticatedRequest, m_outboundProxy); authTransaction.NonInviteTransactionFinalResponseReceived += AuthResponseReceived; authTransaction.NonInviteTransactionFailed += TransactionFailed; authTransaction.SendRequest(); } else { logger.LogDebug("Send request received an authentication required response but no credentials were available."); ResponseReceived?.Invoke(sipResponse); } } else { logger.LogDebug("Send request failed with " + sipResponse.StatusCode + " but no authentication header was supplied for " + sipTransaction.TransactionRequest.Method + " to " + m_callDescriptor.Uri + "."); ResponseReceived?.Invoke(sipResponse); } } else { ResponseReceived?.Invoke(sipResponse); } } catch (Exception excp) { logger.LogError("Exception SIPNonInviteClientUserAgent ServerResponseReceived (" + remoteEndPoint + "). " + excp.Message); } return(Task.FromResult(SocketError.Success)); }
public void SendRequest(SIPMethodsEnum method) { try { SIPRequest req = GetRequest(method); SIPNonInviteTransaction tran = new SIPNonInviteTransaction(m_sipTransport, req, m_outboundProxy); ManualResetEvent waitForResponse = new ManualResetEvent(false); tran.NonInviteTransactionFailed += TransactionFailed; tran.NonInviteTransactionFinalResponseReceived += ServerResponseReceived; tran.SendRequest(); } catch (Exception excp) { logger.LogError("Exception SIPNonInviteClientUserAgent SendRequest to " + m_callDescriptor.Uri + ". " + excp.Message); throw; } }
/// <summary> /// Used to hangup the call or indicate that the client hungup. /// </summary> /// <param name="clientHungup">True if the BYE request was received from the client. False if the hangup /// needs to originate from this agent.</param> public void Hangup(bool clientHungup) { if (!m_isHungup) { m_isHungup = true; if (SIPDialogue == null) { return; } // Only need to send a BYE request if the client didn't already do so. if (clientHungup == false) { try { // Cases found where the Contact in the INVITE was to a different protocol than the original request. var inviteContact = m_uasTransaction.TransactionRequest.Header.Contact.FirstOrDefault(); if (inviteContact == null) { logger.LogWarning( "The Contact header on the INVITE request was missing, BYE request cannot be generated."); } else { //SIPRequest byeRequest = GetByeRequest(); var byeRequest = SIPDialogue.GetInDialogRequest(SIPMethodsEnum.BYE); SIPNonInviteTransaction byeTransaction = new SIPNonInviteTransaction(m_sipTransport, byeRequest, m_outboundProxy); byeTransaction.NonInviteTransactionFinalResponseReceived += ByeServerFinalResponseReceived; byeTransaction.SendRequest(); } } catch (Exception excp) { logger.LogError("Exception SIPServerUserAgent Hangup. " + excp.Message); throw; } } } }
public void Hangup() { if (m_sipDialogue == null) { return; } try { //SIPRequest byeRequest = GetByeRequest(m_serverTransaction.TransactionFinalResponse, m_sipDialogue.RemoteTarget); SIPRequest byeRequest = m_sipDialogue.GetInDialogRequest(SIPMethodsEnum.BYE); byeRequest.SetSendFromHints(m_serverTransaction.TransactionRequest.LocalSIPEndPoint); m_byeTransaction = new SIPNonInviteTransaction(m_sipTransport, byeRequest, m_outboundProxy); m_byeTransaction.NonInviteTransactionFinalResponseReceived += ByeServerFinalResponseReceived; m_byeTransaction.NonInviteTransactionFailed += (tx, reason) => logger.LogWarning($"Bye request for {m_sipCallDescriptor.Uri} failed with {reason}."); m_byeTransaction.SendRequest(); } catch (Exception excp) { logger.LogError("Exception SIPClientUserAgent Hangup. " + excp.Message); } }
private Task <SocketError> ByeServerFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { logger.LogDebug("Response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + sipTransaction.TransactionRequest.URI.ToString() + "."); SIPNonInviteTransaction byeTransaction = sipTransaction as SIPNonInviteTransaction; if ((sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised) && SIPAccount != null) { var authRequest = sipTransaction.TransactionRequest.DuplicateAndAuthenticate(sipResponse.Header.AuthenticationHeaders, SIPAccount.SIPUsername, SIPAccount.SIPPassword); SIPNonInviteTransaction authByeTransaction = new SIPNonInviteTransaction(m_sipTransport, authRequest, null); authByeTransaction.SendRequest(); } return(Task.FromResult(SocketError.Success)); } catch (Exception excp) { logger.LogError("Exception ByServerFinalResponseReceived. " + excp.Message); return(Task.FromResult(SocketError.Fault)); } }
private Task <SocketError> SubscribeTransactionFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { if (sipResponse.Status == SIPResponseStatusCodesEnum.IntervalTooBrief) { // The expiry interval used was too small. Adjust and try again. m_expiry = (sipResponse.Header.MinExpires > 0) ? sipResponse.Header.MinExpires : m_expiry * 2; logger.LogWarning("A subscribe request was rejected with IntervalTooBrief, adjusting expiry to " + m_expiry + " and trying again."); Subscribe(m_resourceURI, m_expiry, m_sipEventPackage, m_subscribeCallID, null); } else if (sipResponse.Status == SIPResponseStatusCodesEnum.Forbidden) { // The subscription is never going to succeed so cancel it. SubscriptionFailed?.Invoke(m_resourceURI, sipResponse.Status, "A Forbidden response was received on a subscribe attempt to " + m_resourceURI.ToString() + " for user " + m_authUsername + "."); m_exit = true; m_waitForSubscribeResponse.Set(); } else if (sipResponse.Status == SIPResponseStatusCodesEnum.BadEvent) { // The subscription is never going to succeed so cancel it. SubscriptionFailed?.Invoke(m_resourceURI, sipResponse.Status, "A BadEvent response was received on a subscribe attempt to " + m_resourceURI.ToString() + " for event package " + m_sipEventPackage.ToString() + "."); m_exit = true; m_waitForSubscribeResponse.Set(); } else if (sipResponse.Status == SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist) { // The notifier server does not have a record for the existing subscription. SubscriptionFailed?.Invoke(m_resourceURI, sipResponse.Status, "Subscribe failed with response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + "."); m_waitForSubscribeResponse.Set(); } else if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised) { if (m_authUsername.IsNullOrBlank() || m_authPassword.IsNullOrBlank()) { // No point trying to authenticate if there are no credentials to use. SubscriptionFailed?.Invoke(m_resourceURI, sipResponse.Status, "Authentication requested on subscribe request when no credentials available."); m_waitForSubscribeResponse.Set(); } else if (sipResponse.Header.AuthenticationHeader != null) { if (m_attempts >= MAX_SUBSCRIBE_ATTEMPTS) { m_subscribed = false; SubscriptionFailed?.Invoke(m_resourceURI, SIPResponseStatusCodesEnum.InternalServerError, "Subscription reached the maximum number of allowed attempts."); m_waitForSubscribeResponse.Set(); } else { logger.LogDebug("Attempting authentication for subscribe request for event package " + m_sipEventPackage.ToString() + " and " + m_resourceURI.ToString() + "."); m_attempts++; // Resend SUBSCRIBE with credentials. SIPAuthorisationDigest authRequest = sipResponse.Header.AuthenticationHeader.SIPDigest; authRequest.SetCredentials(m_authUsername, m_authPassword, m_resourceURI.ToString(), SIPMethodsEnum.SUBSCRIBE.ToString()); SIPRequest authSubscribeRequest = sipTransaction.TransactionRequest; authSubscribeRequest.Header.AuthenticationHeader = new SIPAuthenticationHeader(authRequest); authSubscribeRequest.Header.AuthenticationHeader.SIPDigest.Response = authRequest.Digest; authSubscribeRequest.Header.Vias.TopViaHeader.Branch = CallProperties.CreateBranchId(); m_localCSeq = sipTransaction.TransactionRequest.Header.CSeq + 1; authSubscribeRequest.Header.CSeq = m_localCSeq; authSubscribeRequest.Header.CallId = m_subscribeCallID; if (!m_filter.IsNullOrBlank()) { authSubscribeRequest.Body = m_filter; authSubscribeRequest.Header.ContentLength = m_filter.Length; authSubscribeRequest.Header.ContentType = m_filterTextType; } // Create a new transaction to establish the authenticated server call. SIPNonInviteTransaction subscribeTransaction = new SIPNonInviteTransaction(m_sipTransport, authSubscribeRequest, m_outboundProxy); subscribeTransaction.NonInviteTransactionFinalResponseReceived += SubscribeTransactionFinalResponseReceived; subscribeTransaction.NonInviteTransactionFailed += SubscribeTransactionFailed; //m_sipTransport.SendTransaction(subscribeTransaction); subscribeTransaction.SendRequest(); } } else { SubscriptionFailed?.Invoke(sipTransaction.TransactionRequestURI, sipResponse.Status, "Subscribe requested authentication but did not provide an authentication header."); m_waitForSubscribeResponse.Set(); } } else if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode <= 299) { logger.LogDebug("Authenticating subscribe request for event package " + m_sipEventPackage.ToString() + " and " + m_resourceURI.ToString() + " was successful."); m_subscribed = true; m_subscriptionToTag = sipResponse.Header.To.ToTag; SubscriptionSuccessful?.Invoke(m_resourceURI); m_waitForSubscribeResponse.Set(); } else { SubscriptionFailed?.Invoke(m_resourceURI, sipResponse.Status, "Subscribe failed with response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + "."); m_waitForSubscribeResponse.Set(); } return(Task.FromResult(SocketError.Success)); } catch (Exception excp) { logger.LogError("Exception SubscribeTransactionFinalResponseReceived. " + excp.Message); SubscriptionFailed?.Invoke(m_resourceURI, SIPResponseStatusCodesEnum.InternalServerError, "Exception processing subscribe response. " + excp.Message); m_waitForSubscribeResponse.Set(); return(Task.FromResult(SocketError.Fault)); } }
/// <summary> /// Initiates a SUBSCRIBE request to a notification server. /// </summary> /// <param name="subscribeURI">The SIP user that dialog notifications are being subscribed to.</param> public void Subscribe(SIPURI subscribeURI, int expiry, SIPEventPackagesEnum sipEventPackage, string subscribeCallID, SIPURI contactURI) { try { if (m_attempts >= MAX_SUBSCRIBE_ATTEMPTS) { logger.LogWarning($"Subscription to {subscribeURI} reached the maximum number of allowed attempts without a failure condition."); m_subscribed = false; SubscriptionFailed?.Invoke(subscribeURI, SIPResponseStatusCodesEnum.InternalServerError, "Subscription reached the maximum number of allowed attempts."); m_waitForSubscribeResponse.Set(); } else { m_attempts++; m_localCSeq++; SIPRequest subscribeRequest = SIPRequest.GetRequest( SIPMethodsEnum.SUBSCRIBE, m_resourceURI, new SIPToHeader(null, subscribeURI, m_subscriptionToTag), new SIPFromHeader(null, new SIPURI(m_authUsername, m_authDomain, null, m_resourceURI.Scheme, SIPProtocolsEnum.udp), m_subscriptionFromTag)); if (contactURI != null) { subscribeRequest.Header.Contact = new List <SIPContactHeader>() { new SIPContactHeader(null, contactURI) }; } else { subscribeRequest.Header.Contact = new List <SIPContactHeader>() { SIPContactHeader.GetDefaultSIPContactHeader(subscribeRequest.URI.Scheme) }; } subscribeRequest.Header.CSeq = m_localCSeq; subscribeRequest.Header.Expires = expiry; subscribeRequest.Header.Event = SIPEventPackageType.GetEventHeader(sipEventPackage); subscribeRequest.Header.CallId = subscribeCallID; if (!m_filter.IsNullOrBlank()) { subscribeRequest.Body = m_filter; subscribeRequest.Header.ContentLength = m_filter.Length; subscribeRequest.Header.ContentType = m_filterTextType; } SIPNonInviteTransaction subscribeTransaction = new SIPNonInviteTransaction(m_sipTransport, subscribeRequest, m_outboundProxy); subscribeTransaction.NonInviteTransactionFinalResponseReceived += SubscribeTransactionFinalResponseReceived; subscribeTransaction.NonInviteTransactionFailed += SubscribeTransactionFailed; LastSubscribeAttempt = DateTime.Now; subscribeTransaction.SendRequest(); } } catch (Exception excp) { logger.LogError("Exception SIPNotifierClient Subscribe. " + excp.Message); SubscriptionFailed?.Invoke(m_resourceURI, SIPResponseStatusCodesEnum.InternalServerError, "Exception Subscribing. " + excp.Message); m_waitForSubscribeResponse.Set(); } }
private Task <SocketError> ServerFinalResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { logger.LogDebug("Response " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + " for " + m_serverTransaction.TransactionRequest.URI.ToString() + "."); m_serverTransaction.UACInviteTransactionInformationResponseReceived -= ServerInformationResponseReceived; m_serverTransaction.UACInviteTransactionFinalResponseReceived -= ServerFinalResponseReceived; if (m_callCancelled && sipResponse.Status == SIPResponseStatusCodesEnum.RequestTerminated) { // No action required. Correctly received request terminated on an INVITE we cancelled. } else if (m_callCancelled) { #region Call has been cancelled, hangup. if (m_hungupOnCancel) { logger.LogDebug("A cancelled call to " + m_sipCallDescriptor.Uri + " has been answered AND has already been hungup, no further action being taken."); } else { m_hungupOnCancel = true; logger.LogDebug("A cancelled call to " + m_sipCallDescriptor.Uri + " has been answered, hanging up."); if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0) { SIPURI byeURI = sipResponse.Header.Contact[0].ContactURI; SIPRequest byeRequest = GetByeRequest(sipResponse, byeURI); SIPNonInviteTransaction byeTransaction = new SIPNonInviteTransaction(m_sipTransport, byeRequest, m_outboundProxy); byeTransaction.SendRequest(); } else { logger.LogDebug("No contact header provided on response for cancelled call to " + m_sipCallDescriptor.Uri + " no further action."); } } #endregion } else if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised) { #region Authenticate client call to third party server. if (!m_callCancelled) { if (m_sipCallDescriptor.Password.IsNullOrBlank()) { // No point trying to authenticate if there is no password to use. logger.LogDebug("Forward leg failed, authentication was requested but no credentials were available."); CallFailed?.Invoke(this, "Authentication requested when no credentials available", sipResponse); } else if (m_serverAuthAttempts == 0) { m_serverAuthAttempts = 1; // Resend INVITE with credentials. string username = (m_sipCallDescriptor.AuthUsername != null && m_sipCallDescriptor.AuthUsername.Trim().Length > 0) ? m_sipCallDescriptor.AuthUsername : m_sipCallDescriptor.Username; var authRequest = m_serverTransaction.TransactionRequest.DuplicateAndAuthenticate(sipResponse.Header.AuthenticationHeaders, username, m_sipCallDescriptor.Password); // Create a new UAC transaction to establish the authenticated server call. m_serverTransaction = new UACInviteTransaction(m_sipTransport, authRequest, m_outboundProxy); if (m_serverTransaction.CDR != null) { m_serverTransaction.CDR.Updated(); } m_serverTransaction.UACInviteTransactionInformationResponseReceived += ServerInformationResponseReceived; m_serverTransaction.UACInviteTransactionFinalResponseReceived += ServerFinalResponseReceived; m_serverTransaction.UACInviteTransactionFailed += ServerTransactionFailed; m_serverTransaction.SendInviteRequest(); } else { CallFailed?.Invoke(this, "Authentication with provided credentials failed", sipResponse); } } #endregion } else { if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode <= 299) { m_sipDialogue = new SIPDialogue(m_serverTransaction); m_sipDialogue.CallDurationLimit = m_sipCallDescriptor.CallDurationLimit; } CallAnswered?.Invoke(this, sipResponse); } return(Task.FromResult(SocketError.Success)); } catch (Exception excp) { logger.LogDebug("Exception ServerFinalResponseReceived. " + excp); return(Task.FromResult(SocketError.Fault)); } }
/// <summary> /// The event handler for responses to the initial register request. /// </summary> private void ServerResponseReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) { try { logger.LogDebug($"Server response {sipResponse.Status} received for {m_sipAccountAOR}."); if (sipResponse.Status == SIPResponseStatusCodesEnum.ProxyAuthenticationRequired || sipResponse.Status == SIPResponseStatusCodesEnum.Unauthorised) { if (sipResponse.Header.HasAuthenticationHeader) { if (m_attempts >= m_maxRegisterAttempts) { logger.LogDebug("Registration to " + m_sipAccountAOR.ToString() + " reached the maximum number of allowed attempts without a failure condition."); m_isRegistered = false; RegistrationTemporaryFailure?.Invoke(m_sipAccountAOR, "Registration reached the maximum number of allowed attempts."); m_waitForRegistrationMRE.Set(); } else { m_attempts++; string username = (m_authUsername != null) ? m_authUsername : m_sipAccountAOR.User; var authenticatedRequest = sipTransaction.TransactionRequest.DuplicateAndAuthenticate( sipResponse.Header.AuthenticationHeaders, username, m_password); SIPEndPoint registrarSIPEndPoint = m_outboundProxy; if (registrarSIPEndPoint == null) { SIPURI uri = SIPURI.ParseSIPURIRelaxed(m_registrarHost); var lookupResult = m_sipTransport.ResolveSIPUriAsync(uri).Result; if (lookupResult == null) { logger.LogWarning("Could not resolve " + m_registrarHost + "."); } else { registrarSIPEndPoint = lookupResult; } } if (registrarSIPEndPoint == null) { logger.LogWarning("SIPRegistrationAgent could not resolve " + m_registrarHost + "."); RegistrationFailed?.Invoke(m_sipAccountAOR, "Could not resolve " + m_registrarHost + "."); } else { SIPNonInviteTransaction regAuthTransaction = new SIPNonInviteTransaction(m_sipTransport, authenticatedRequest, registrarSIPEndPoint); regAuthTransaction.NonInviteTransactionFinalResponseReceived += (lep, rep, tn, rsp) => { AuthResponseReceived(lep, rep, tn, rsp); return(Task.FromResult(SocketError.Success)); }; regAuthTransaction.NonInviteTransactionFailed += RegistrationTransactionFailed; regAuthTransaction.SendRequest(); } } } else { logger.LogWarning($"Registration failed with {sipResponse.Status} but no authentication header was supplied for {m_sipAccountAOR}."); m_isRegistered = false; RegistrationTemporaryFailure?.Invoke(m_sipAccountAOR, $"Registration failed with {sipResponse.Status} but no authentication header was supplied."); m_waitForRegistrationMRE.Set(); } } else { if (sipResponse.Status == SIPResponseStatusCodesEnum.Ok) { if (m_expiry > 0) { m_isRegistered = true; m_expiry = GetUpdatedExpiry(sipResponse); RegistrationSuccessful?.Invoke(m_sipAccountAOR); } else { m_isRegistered = false; RegistrationRemoved?.Invoke(m_sipAccountAOR); } m_waitForRegistrationMRE.Set(); } else if (sipResponse.Status == SIPResponseStatusCodesEnum.Forbidden || sipResponse.Status == SIPResponseStatusCodesEnum.NotFound) { // SIP account does not appear to exist. m_exit = m_exitOnUnequivocalFailure; logger.LogWarning($"Registration unequivocal failure with {sipResponse.Status} for {m_sipAccountAOR}{(m_exit ? ", no further registration attempts will be made" : "")}."); string reasonPhrase = (sipResponse.ReasonPhrase.IsNullOrBlank()) ? sipResponse.Status.ToString() : sipResponse.ReasonPhrase; RegistrationFailed?.Invoke(m_sipAccountAOR, "Registration failed with " + (int)sipResponse.Status + " " + reasonPhrase + "."); m_waitForRegistrationMRE.Set(); } else if (sipResponse.Status == SIPResponseStatusCodesEnum.IntervalTooBrief && m_expiry != 0) { m_expiry = GetUpdatedExpiryForIntervalTooBrief(sipResponse); logger.LogWarning("Registration for " + m_sipAccountAOR.ToString() + " had a too short expiry, updated to +" + m_expiry + " and trying again."); SendInitialRegister(); } else { logger.LogWarning($"Registration failed with {sipResponse.Status} for {m_sipAccountAOR}."); m_isRegistered = false; RegistrationTemporaryFailure?.Invoke(m_sipAccountAOR, $"Registration failed with {sipResponse.Status}."); m_waitForRegistrationMRE.Set(); } } } catch (Exception excp) { logger.LogError($"Exception SIPRegistrationUserAgent ServerResponseReceived ({remoteEndPoint}). {excp}"); } }