public void ProcessNotifyRequest(SIPRequest sipRequest) { try { // Hack to work around MWI request from callcentric not having a trailing CRLF and breaking some softphones like the Bria. if (sipRequest.Header.Event == MWI_EVENT_TYPE && sipRequest.Body.NotNullOrBlank() && sipRequest.Body.Substring(sipRequest.Body.Length - 2) != m_crlf) { sipRequest.Body += m_crlf; } string fromURI = (sipRequest.Header.From != null && sipRequest.Header.From.FromURI != null) ? sipRequest.Header.From.FromURI.ToString() : "unknown"; string domain = GetCanonicalDomain_External(sipRequest.URI.Host, true); if (domain != null) { SIPAccountAsset sipAccount = GetSIPAccount_External(s => s.SIPUsername == sipRequest.URI.User && s.SIPDomain == domain); if (sipAccount != null) { List <SIPRegistrarBinding> bindings = GetSIPAccountBindings_External(b => b.SIPAccountId == sipAccount.Id, null, 0, MAX_FORWARD_BINDINGS); if (bindings != null) { foreach (SIPRegistrarBinding binding in bindings) { SIPURI dstURI = binding.MangledContactSIPURI; SIPEndPoint localSIPEndPoint = (m_outboundProxy != null) ? m_sipTransport.GetDefaultSIPEndPoint(m_outboundProxy.Protocol) : m_sipTransport.GetDefaultSIPEndPoint(dstURI.Protocol); SIPEndPoint dstSIPEndPoint = null; // If the outbound proxy is a loopback address, as it will normally be for local deployments, then it cannot be overriden. if (m_outboundProxy != null && IPAddress.IsLoopback(m_outboundProxy.Address)) { dstSIPEndPoint = m_outboundProxy; } else if (binding.ProxySIPEndPoint != null) { // If the binding has a specific proxy end point sent then the request needs to be forwarded to the proxy's default end point for it to take care of. dstSIPEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(binding.ProxySIPEndPoint.Address, m_defaultSIPPort)); } else if (m_outboundProxy != null) { dstSIPEndPoint = m_outboundProxy; } else { SIPDNSLookupResult lookupResult = m_sipTransport.GetURIEndPoint(dstURI, false); if (lookupResult.LookupError != null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "A NOTIFY request from " + fromURI + " was not forwarded due to DNS failure for " + dstURI.Host + ", " + lookupResult.LookupError + ".", sipAccount.Owner)); } else { dstSIPEndPoint = lookupResult.GetSIPEndPoint(); } } if (dstSIPEndPoint != null) { // Rather than create a brand new request copy the received one and modify the headers that need to be unique. SIPRequest notifyRequest = sipRequest.Copy(); notifyRequest.URI = dstURI; notifyRequest.Header.Contact = SIPContactHeader.CreateSIPContactList(new SIPURI(dstURI.Scheme, localSIPEndPoint)); notifyRequest.Header.To = new SIPToHeader(null, dstURI, null); notifyRequest.Header.CallId = CallProperties.CreateNewCallId(); SIPViaHeader viaHeader = new SIPViaHeader(localSIPEndPoint, CallProperties.CreateBranchId()); notifyRequest.Header.Vias = new SIPViaSet(); notifyRequest.Header.Vias.PushViaHeader(viaHeader); // If the binding has a proxy socket defined set the header to ask the upstream proxy to use it. if (binding.ProxySIPEndPoint != null) { notifyRequest.Header.ProxySendFrom = binding.ProxySIPEndPoint.ToString(); // If the binding has a specific proxy end point sent then the request needs to be forwarded to the proxy's default end point for it to take care of. dstSIPEndPoint = new SIPEndPoint(SIPProtocolsEnum.udp, new IPEndPoint(binding.ProxySIPEndPoint.Address, m_defaultSIPPort)); } Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "Forwarding NOTIFY request from " + fromURI + " to registered binding at " + dstURI.ToString() + ", proxy " + dstSIPEndPoint.ToString() + ".", sipAccount.Owner)); SIPNonInviteTransaction notifyTransaction = m_sipTransport.CreateNonInviteTransaction(notifyRequest, dstSIPEndPoint, localSIPEndPoint, dstSIPEndPoint); notifyTransaction.SendReliableRequest(); } else { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "A NOTIFY request from " + fromURI + " was not forwarded as no destination end point was resolved.", sipAccount.Owner)); } } // Send OK response to server. SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); m_sipTransport.SendResponse(okResponse); } else { // Send unavailable response to server. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "NOTIFY request from " + fromURI + " for " + sipAccount.SIPUsername + "@" + sipAccount.SIPDomain + " but no bindings available, responding with temporarily unavailable.", sipAccount.Owner)); SIPResponse notAvailableResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.TemporarilyUnavailable, null); m_sipTransport.SendResponse(notAvailableResponse); } } else { // Send Not found response to server. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "NOTIFY request from " + fromURI + " for " + sipRequest.URI.ToString() + " but no matching SIP account, responding with not found.", null)); SIPResponse notFoundResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotFound, null); m_sipTransport.SendResponse(notFoundResponse); } } else { // Send Not Serviced response to server. Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Notifier, SIPMonitorEventTypesEnum.MWI, "NOTIFY request from " + fromURI + " for a non-serviced domain responding with not found.", null)); SIPResponse notServicedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotFound, "Domain not serviced"); m_sipTransport.SendResponse(notServicedResponse); } } catch (Exception excp) { logger.Error("Exception SIPNotifyManager ProcessNotifyRequest. " + excp.Message); } }
/// <summary> /// Initiates a SUBSCRIBE request to a notification server. /// </summary> /// <param name="subscribeToURI">The SIP user that dialog notifications are being subscribed to.</param> public void Subscribe(SIPURI subscribeURI, int expiry, SIPEventPackage sipEventPackage, string subscribeCallID, SIPURI contactURI) { try { if (m_attempts >= MAX_SUBSCRIBE_ATTEMPTS) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.NotifierClient, SIPMonitorEventTypesEnum.SubscribeFailed, "Susbcription to " + subscribeURI.ToString() + " reached the maximum number of allowed attempts without a failure condition.", null)); m_subscribed = false; SubscriptionFailed(subscribeURI, SIPResponseStatusCodesEnum.InternalServerError, "Subscription reached the maximum number of allowed attempts."); m_waitForSubscribeResponse.Set(); } else { m_attempts++; m_localCSeq++; SIPRequest subscribeRequest = m_sipTransport.GetRequest( SIPMethodsEnum.SUBSCRIBE, m_resourceURI, new SIPToHeader(null, subscribeURI, m_subscriptionToTag), null); if (contactURI != null) { subscribeRequest.Header.Contact = new List <SIPContactHeader>() { new SIPContactHeader(null, contactURI) }; } subscribeRequest.Header.From = new SIPFromHeader(null, new SIPURI(m_authUsername, m_authDomain, null, SIPSchemesEnum.sip, SIPProtocolsEnum.udp), m_subscriptionFromTag); subscribeRequest.Header.CSeq = m_localCSeq; subscribeRequest.Header.Expires = expiry; subscribeRequest.Header.Event = sipEventPackage.ToString(); subscribeRequest.Header.CallId = subscribeCallID; if (!m_filter.IsNullOrBlank()) { subscribeRequest.Body = m_filter; subscribeRequest.Header.ContentLength = m_filter.Length; subscribeRequest.Header.ContentType = m_filterTextType; } SIPEndPoint dstEndPoint = m_outboundProxy; if (dstEndPoint == null) { SIPDNSLookupResult lookupResult = m_sipTransport.GetURIEndPoint(m_resourceURI, false); if (lookupResult.LookupError != null) { Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.NotifierClient, SIPMonitorEventTypesEnum.SubscribeFailed, "Could not resolve " + m_resourceURI.Host + ", " + lookupResult.LookupError, null)); } else { dstEndPoint = lookupResult.GetSIPEndPoint(); } } SIPNonInviteTransaction subscribeTransaction = m_sipTransport.CreateNonInviteTransaction(subscribeRequest, dstEndPoint, null, m_outboundProxy); subscribeTransaction.NonInviteTransactionFinalResponseReceived += SubscribeTransactionFinalResponseReceived; subscribeTransaction.NonInviteTransactionTimedOut += SubsribeTransactionTimedOut; m_sipTransport.SendSIPReliable(subscribeTransaction); LastSubscribeAttempt = DateTime.Now; } } catch (Exception excp) { logger.Error("Exception SIPNotifierClient Subscribe. " + excp.Message); SubscriptionFailed(m_resourceURI, SIPResponseStatusCodesEnum.InternalServerError, "Exception Subscribing. " + excp.Message); m_waitForSubscribeResponse.Set(); } }
static void Main() { Console.WriteLine("SIPSorcery client user agent example."); Console.WriteLine("Press ctrl-c to exit."); // Plumbing code to facilitate a graceful exit. CancellationTokenSource rtpCts = new CancellationTokenSource(); // Cancellation token to stop the RTP stream. bool isCallHungup = false; bool hasCallFailed = false; AddConsoleLogger(); SIPURI callUri = SIPURI.ParseSIPURI(DEFAULT_DESTINATION_SIP_URI); Log.LogInformation($"Call destination {callUri}."); // Set up a default SIP transport. var sipTransport = new SIPTransport(); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, 0))); // Un/comment this line to see/hide each SIP message sent and received. //EnableTraceLogs(sipTransport); // Determine the local IP address to use for our RTP end point based on the address it will be sending to. var lookupResult = sipTransport.GetURIEndPoint(callUri, false); if (lookupResult != null) { var uasSIPEndPoint = lookupResult.GetSIPEndPoint(); IPAddress rtpAddress = NetServices.GetLocalAddressForRemote(uasSIPEndPoint.GetIPEndPoint().Address); Log.LogInformation($"Call URI {callUri} resolved to {uasSIPEndPoint}."); // Initialise an RTP session to receive the RTP packets from the remote SIP server. Socket rtpSocket = null; Socket controlSocket = null; NetServices.CreateRtpSocket(rtpAddress, 49000, 49100, false, out rtpSocket, out controlSocket); var rtpRecvSession = new RTPSession((int)RTPPayloadTypesEnum.PCMU, null, null); var rtpSendSession = new RTPSession((int)RTPPayloadTypesEnum.PCMU, null, null); // Create a client user agent to place a call to a remote SIP server along with event handlers for the different stages of the call. var uac = new SIPClientUserAgent(sipTransport); uac.CallTrying += (uac, resp) => { Log.LogInformation($"{uac.CallDescriptor.To} Trying: {resp.StatusCode} {resp.ReasonPhrase}."); Log.LogDebug(resp.ToString()); }; uac.CallRinging += (uac, resp) => Log.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}."); uac.CallFailed += (uac, err) => { Log.LogWarning($"{uac.CallDescriptor.To} Failed: {err}"); hasCallFailed = true; rtpCts.Cancel(); }; uac.CallAnswered += (uac, resp) => { if (resp.Status == SIPResponseStatusCodesEnum.Ok) { Log.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); _remoteRtpEndPoint = SDP.GetSDPRTPEndPoint(resp.Body); Log.LogDebug($"Remote RTP socket {_remoteRtpEndPoint}."); } else { Log.LogWarning($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); if (resp.StatusCode >= 400) { // Call failed. hasCallFailed = true; rtpCts.Cancel(); } } }; // The only incoming request that needs to be explicitly handled for this example is if the remote end hangs up the call. sipTransport.SIPTransportRequestReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { if (sipRequest.Method == SIPMethodsEnum.BYE) { SIPNonInviteTransaction byeTransaction = sipTransport.CreateNonInviteTransaction(sipRequest, null); SIPResponse byeResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); byeTransaction.SendFinalResponse(byeResponse); if (uac.IsUACAnswered) { Log.LogInformation("Call was hungup by remote server."); isCallHungup = true; rtpCts.Cancel(); } } }; // It's a good idea to start the RTP receiving socket before the call request is sent. // A SIP server will generally start sending RTP as soon as it has processed the incoming call request and // being ready to receive will stop any ICMP error response being generated. Task.Run(() => RecvRtp(rtpSocket, rtpRecvSession, rtpCts)); Task.Run(() => SendRtp(rtpSocket, rtpSendSession, rtpCts)); // Start the thread that places the call. SIPCallDescriptor callDescriptor = new SIPCallDescriptor( SIPConstants.SIP_DEFAULT_USERNAME, null, callUri.ToString(), SIPConstants.SIP_DEFAULT_FROMURI, null, null, null, null, SIPCallDirection.Out, SDP.SDP_MIME_CONTENTTYPE, GetSDP(rtpSocket.LocalEndPoint as IPEndPoint).ToString(), null); uac.Call(callDescriptor); // Ctrl-c will gracefully exit the call at any point. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; rtpCts.Cancel(); }; // At this point the call is established. We'll wait for a few seconds and then transfer. // Note the empty ContinueWith is to catch and ignore the TaskCancelationExeption generated if // rtpCts is set by the call failing. Task.Delay(DELAY_UNTIL_TRANSFER_MILLISECONDS, rtpCts.Token).ContinueWith(tsk => { }).Wait(); if (!hasCallFailed) { SIPRequest referRequest = GetReferRequest(uac, SIPURI.ParseSIPURI(TRANSFER_DESTINATION_SIP_URI)); SIPNonInviteTransaction referTx = sipTransport.CreateNonInviteTransaction(referRequest, null); referTx.NonInviteTransactionFinalResponseReceived += (SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPTransaction sipTransaction, SIPResponse sipResponse) => { if (sipResponse.Header.CSeqMethod == SIPMethodsEnum.REFER && sipResponse.Status == SIPResponseStatusCodesEnum.Accepted) { Log.LogInformation("Call transfer was accepted by remote server."); isCallHungup = true; rtpCts.Cancel(); } }; referTx.SendReliableRequest(); } // At this point the call transfer has been initiated and everything will be handled in an event handler or on the RTP // receive task. The code below is to gracefully exit. // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed. rtpCts.Token.WaitHandle.WaitOne(); Log.LogInformation("Exiting..."); rtpSocket?.Close(); controlSocket?.Close(); if (!isCallHungup && uac != null) { if (uac.IsUACAnswered) { Log.LogInformation($"Hanging up call to {uac.CallDescriptor.To}."); uac.Hangup(); } else if (!hasCallFailed) { Log.LogInformation($"Cancelling call to {uac.CallDescriptor.To}."); uac.Cancel(); } // Give the BYE or CANCEL request time to be transmitted. Log.LogInformation("Waiting 1s for call to clean up..."); Task.Delay(1000).Wait(); } } SIPSorcery.Net.DNSManager.Stop(); if (sipTransport != null) { Log.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } }