public static string MangleSDP(string sdpBody, string publicIPAddress, out bool wasMangled)
        {
            wasMangled = false;

            try
            {
                if (sdpBody != null && publicIPAddress != null)
                {
                    string sdpAddress = SDP.GetSDPRTPEndPoint(sdpBody).Address.ToString();

                    // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help.
                    if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress)
                    {
                        //logger.LogDebug("MangleSDP replacing private " + sdpAddress + " with " + publicIPAddress + ".");
                        string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP4 (?<ipaddress>(\d+\.){3}\d+)", "c=IN IP4 " + publicIPAddress, RegexOptions.Singleline);
                        wasMangled = true;

                        return(mangledSDP);
                    }
                }
                else
                {
                    logger.LogWarning("Mangle SDP was called with an empty body or public IP address.");
                }

                return(sdpBody);
            }
            catch (Exception excp)
            {
                logger.LogError("Exception MangleSDP. " + excp.Message);
                return(sdpBody);
            }
        }
Example #2
0
        /// <summary>
        /// This constructor is used to create non-INVITE dialogues for example the dialogues used in SIP event interactions
        /// where the dialogue is created based on a SUBSCRIBE request.
        /// </summary>
        public SIPDialogue(
            SIPRequest nonInviteRequest,
            string toTag)
        {
            Id = Guid.NewGuid();

            CallId          = nonInviteRequest.Header.CallId;
            RouteSet        = (nonInviteRequest.Header.RecordRoutes != null) ? nonInviteRequest.Header.RecordRoutes.Reversed() : null;
            RemoteUserField = nonInviteRequest.Header.From.FromUserField;
            RemoteTag       = nonInviteRequest.Header.From.FromTag;
            LocalUserField  = nonInviteRequest.Header.To.ToUserField;
            LocalUserField.Parameters.Set("tag", toTag);
            LocalTag  = toTag;
            CSeq      = nonInviteRequest.Header.CSeq;
            Inserted  = DateTime.UtcNow;
            Direction = SIPCallDirection.Out;

            // Set the dialogue remote target and take care of mangling if an upstream proxy has indicated it's required.
            RemoteTarget  = nonInviteRequest.Header.Contact[0].ContactURI;
            ProxySendFrom = nonInviteRequest.Header.ProxyReceivedOn;

            if (!nonInviteRequest.Header.ProxyReceivedFrom.IsNullOrBlank())
            {
                // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact.
                // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem.
                if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host))
                {
                    SIPEndPoint remoteUASIPEndPoint = SIPEndPoint.ParseSIPEndPoint(nonInviteRequest.Header.ProxyReceivedFrom);
                    RemoteTarget.Host = remoteUASIPEndPoint.GetIPEndPoint().ToString();
                }
            }
        }
Example #3
0
        /// <summary>
        /// This constructor is used by client user agents or SIP elements acting in a client user agent role. When
        /// acting as a client user agent the local fields are contained in the From header and the remote fields are
        /// in the To header.
        /// </summary>
        public SIPDialogue(
            UACInviteTransaction uacInviteTransaction,
            string owner,
            string adminMemberId)
        {
            Id = Guid.NewGuid();

            CallId   = uacInviteTransaction.TransactionRequest.Header.CallId;
            RouteSet =
                (uacInviteTransaction.TransactionFinalResponse != null &&
                 uacInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null)
                    ? uacInviteTransaction.TransactionFinalResponse.Header.RecordRoutes.Reversed()
                    : null;
            LocalUserField  = uacInviteTransaction.TransactionFinalResponse.Header.From.FromUserField;
            LocalTag        = uacInviteTransaction.TransactionFinalResponse.Header.From.FromTag;
            RemoteUserField = uacInviteTransaction.TransactionFinalResponse.Header.To.ToUserField;
            RemoteTag       = uacInviteTransaction.TransactionFinalResponse.Header.To.ToTag;
            CSeq            = uacInviteTransaction.TransactionRequest.Header.CSeq;
            CDRId           = (uacInviteTransaction.CDR != null) ? uacInviteTransaction.CDR.CDRId : Guid.Empty;
            Owner           = owner;
            AdminMemberId   = adminMemberId;
            ContentType     = uacInviteTransaction.TransactionRequest.Header.ContentType;
            SDP             = uacInviteTransaction.TransactionRequest.Body;
            RemoteSDP       = uacInviteTransaction.TransactionFinalResponse.Body;
            Inserted        = DateTimeOffset.UtcNow;
            Direction       = SIPCallDirection.Out;

            // Set the dialogue remote target and take care of mangling if an upstream proxy has indicated it's required.
            if (uacInviteTransaction.TransactionFinalResponse != null)
            {
                RemoteTarget = new SIPURI(uacInviteTransaction.TransactionRequest.URI.Scheme,
                                          uacInviteTransaction.TransactionFinalResponse.RemoteSIPEndPoint.CopyOf());
            }
            else
            {
                RemoteTarget = new SIPURI(uacInviteTransaction.TransactionRequest.URI.Scheme,
                                          uacInviteTransaction.TransactionRequest.RemoteSIPEndPoint.CopyOf());
            }

            ProxySendFrom = uacInviteTransaction.TransactionFinalResponse.Header.ProxyReceivedOn;
            if (uacInviteTransaction.TransactionFinalResponse.Header.Contact != null &&
                uacInviteTransaction.TransactionFinalResponse.Header.Contact.Count > 0)
            {
                RemoteTarget = uacInviteTransaction.TransactionFinalResponse.Header.Contact[0].ContactURI.CopyOf();
                if (!uacInviteTransaction.TransactionFinalResponse.Header.ProxyReceivedFrom.IsNullOrBlank())
                {
                    // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact.
                    // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem.
                    if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host))
                    {
                        SIPEndPoint remoteUASSIPEndPoint =
                            SIPEndPoint.ParseSIPEndPoint(uacInviteTransaction.TransactionFinalResponse.Header
                                                         .ProxyReceivedFrom);
                        RemoteTarget.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString();
                    }
                }
            }
        }
Example #4
0
        public void Test172IPRangeIsPrivate()
        {
            Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);

            Assert.IsFalse(IPSocket.IsPrivateAddress("172.15.1.1"), "Public IP address was mistakenly identified as private.");
            Assert.IsTrue(IPSocket.IsPrivateAddress("172.16.1.1"), "Private IP address was not correctly identified.");

            Console.WriteLine("-----------------------------------------");
        }
        public void Test172IPRangeIsPrivate()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            Assert.False(IPSocket.IsPrivateAddress("172.15.1.1"), "Public IP address was mistakenly identified as private.");
            Assert.True(IPSocket.IsPrivateAddress("172.16.1.1"), "Private IP address was not correctly identified.");

            logger.LogDebug("-----------------------------------------");
        }
Example #6
0
        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.
                SIPRequest ackRequest = null;
                if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode < 299)
                {
                    if (sipResponse.Header.To != null)
                    {
                        m_remoteTag = sipResponse.Header.To.ToTag;
                    }

                    SIPURI ackURI = m_transactionRequest.URI;
                    if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
                    {
                        ackURI = sipResponse.Header.Contact[0].ContactURI;
                        // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem.
                        if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) &&
                            IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank())
                        {
                            // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact.
                            SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom);
                            ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString();
                        }
                    }

                    // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields.
                    ackRequest = GetNewTransactionACKRequest(sipResponse, ackURI, LocalSIPEndPoint);
                    base.SendRequest(ackRequest);
                }
                else
                {
                    // ACK for non 2xx response is part of the INVITE transaction and gets routed to the same endpoint as the INVITE.
                    ackRequest = GetInTransactionACKRequest(sipResponse, m_transactionRequest.URI, LocalSIPEndPoint);
                    base.SendRequest(RemoteEndPoint, ackRequest);
                }

                if (UACInviteTransactionFinalResponseReceived != null)
                {
                    UACInviteTransactionFinalResponseReceived(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);
            }
        }
        /// <summary>
        /// Mangles private IP addresses in a SIP request replacing them with the IP address the packet was received on.
        /// </summary>
        /// <param name="sipRequest">The unmangled SIP request.</param>
        /// <returns>The mangled SIP request</returns>
        public static void MangleSIPRequest(SIPMonitorServerTypesEnum server, SIPRequest sipRequest, string username,
                                            SIPMonitorLogDelegate logDelegate)
        {
            try
            {
                string bottomViaIPAddress = sipRequest.Header.Vias.BottomViaHeader.ReceivedFromIPAddress;

                if (sipRequest.Header.Contact != null && sipRequest.Header.Contact.Count == 1 &&
                    bottomViaIPAddress != null)
                {
                    string contactHost = sipRequest.Header.Contact[0].ContactURI.Host;

                    // Only mangle if the host is a private IP address and there is something to change.
                    // For example the server could be on the same private subnet in which case it can't help.
                    if (IPSocket.IsPrivateAddress(contactHost) && contactHost != bottomViaIPAddress)
                    {
                        string origContact = sipRequest.Header.Contact[0].ContactURI.Host;
                        sipRequest.Header.Contact[0].ContactURI.Host =
                            sipRequest.Header.Vias.BottomViaHeader.ReceivedFromAddress;

                        //logger.LogDebug("Contact URI identified as containing private address for " + sipRequest.Method + " " + origContact + " adjusting to use bottom via " + bottomViaHost + ".");
                        //FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Registrar, SIPMonitorServerTypesEnum.ContactRegisterInProgress, "Contact on " + sipRequest.Method + " " + origContact + " had private address adjusted to " + bottomViaHost + ".", username));
                    }
                }

                if (sipRequest.Body != null && bottomViaIPAddress != null)
                {
                    bool   wasMangled = false;
                    string mangledSDP = MangleSDP(sipRequest.Body, bottomViaIPAddress, out wasMangled);

                    if (wasMangled)
                    {
                        sipRequest.Body = mangledSDP;
                        sipRequest.Header.ContentLength = sipRequest.Body.Length;

                        if (logDelegate != null)
                        {
                            logDelegate(new SIPMonitorConsoleEvent(server, SIPMonitorEventTypesEnum.DialPlan,
                                                                   "SDP mangled for " + sipRequest.Method.ToString() + " request from " +
                                                                   sipRequest.RemoteSIPEndPoint.ToString() + ", adjusted address " + bottomViaIPAddress +
                                                                   ".", username));
                        }
                    }
                }
            }
            catch (Exception excp)
            {
                Logger.Logger.Error("Exception MangleSIPRequest. ->" + excp.Message);
            }
        }
        // private static ILogger logger = Log.Logger;

        public static string MangleSDP(string sdpBody, string publicIPAddress, out bool wasMangled)
        {
            wasMangled = false;

            try
            {
                if (sdpBody != null && publicIPAddress != null)
                {
                    IPAddress addr = SDP.GetSDPRTPEndPoint(sdpBody).Address;
                    //rj2: need to consider publicAddress and IPv6 for mangling
                    IPAddress pubaddr    = IPAddress.Parse(publicIPAddress);
                    string    sdpAddress = addr.ToString();

                    // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help.
                    if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress &&
                        pubaddr.AddressFamily == AddressFamily.InterNetworkV6 &&
                        addr.AddressFamily == AddressFamily.InterNetworkV6)
                    {
                        string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP6 (?<ipaddress>([:a-fA-F0-9]+))",
                                                          "c=IN IP6" + publicIPAddress, RegexOptions.Singleline);
                        wasMangled = true;

                        return(mangledSDP);
                    }
                    else if (IPSocket.IsPrivateAddress(sdpAddress) && publicIPAddress != sdpAddress &&
                             pubaddr.AddressFamily ==
                             AddressFamily.InterNetwork &&
                             addr.AddressFamily == AddressFamily.InterNetwork)
                    {
                        //logger.LogDebug("MangleSDP replacing private " + sdpAddress + " with " + publicIPAddress + ".");
                        string mangledSDP = Regex.Replace(sdpBody, @"c=IN IP4 (?<ipaddress>(\d+\.){3}\d+)",
                                                          "c=IN IP4 " + publicIPAddress, RegexOptions.Singleline);
                        wasMangled = true;

                        return(mangledSDP);
                    }
                }
                else
                {
                    Logger.Logger.Warn("Mangle SDP was called with an empty body or public IP address.");
                }

                return(sdpBody);
            }
            catch (Exception excp)
            {
                Logger.Logger.Error("Exception MangleSDP. ->" + excp.Message);
                return(sdpBody);
            }
        }
        /// <summary>
        /// Mangles private IP addresses in a SIP response replacing them with the IP address the packet was received on.
        /// </summary>
        /// <param name="sipResponse">The unmangled SIP response.</param>
        /// <returns>The mangled SIP response</returns>
        public static void MangleSIPResponse(SIPMonitorServerTypesEnum server, SIPResponse sipResponse,
                                             SIPEndPoint remoteEndPoint, string username, SIPMonitorLogDelegate logDelegate)
        {
            try
            {
                if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
                {
                    string contactHost = sipResponse.Header.Contact[0].ContactURI.Host;

                    // Only mangle if the host is a private IP address and there is something to change.
                    // For example the server could be on the same private subnet in which case it can't help.
                    if (IPSocket.IsPrivateAddress(contactHost) && contactHost != remoteEndPoint.Address.ToString())
                    {
                        SIPURI origContact = sipResponse.Header.Contact[0].ContactURI;
                        sipResponse.Header.Contact[0].ContactURI = new SIPURI(origContact.Scheme, remoteEndPoint);

                        //logger.LogDebug("INVITE response Contact URI identified as containing private address, original " + origContact + " adjusted to " + remoteEndPoint.ToString() + ".");
                        //FireProxyLogEvent(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.Registrar, SIPMonitorServerTypesEnum.ContactRegisterInProgress, "INVITE Response contact adjusted from " + origContact + " to " + remoteEndPoint.ToString() + ".", username));
                    }
                }

                if (sipResponse.Body != null)
                {
                    bool   wasMangled = false;
                    string mangledSDP = MangleSDP(sipResponse.Body, remoteEndPoint.Address.ToString(), out wasMangled);

                    if (wasMangled)
                    {
                        sipResponse.Body = mangledSDP;
                        sipResponse.Header.ContentLength = sipResponse.Body.Length;

                        if (logDelegate != null)
                        {
                            logDelegate(new SIPMonitorConsoleEvent(server, SIPMonitorEventTypesEnum.DialPlan,
                                                                   "SDP mangled for " + sipResponse.Status.ToString() + " response from " +
                                                                   sipResponse.RemoteSIPEndPoint.ToString() + ", adjusted address " +
                                                                   remoteEndPoint.Address.ToString() + ".", username));
                        }
                    }
                }
            }
            catch (Exception excp)
            {
                Logger.Logger.Error("Exception MangleSIPResponse. ->" + excp.Message);
            }
        }
Example #10
0
        public void Call(SIPCallDescriptor sipCallDescriptor)
        {
            CallDescriptor         = sipCallDescriptor;
            m_xmppClient           = new XMPPClient("talk.google.com", 5222, "google.com", null, null);
            m_xmppClient.IsBound  += IsBound;
            m_xmppClient.Answered += Answered;

            IPEndPoint sdpEndPoint = SDP.GetSDPRTPEndPoint(CallDescriptor.Content);

            if (IPSocket.IsPrivateAddress(sdpEndPoint.Address.ToString()))
            {
                bool wasSDPMangled;
                CallDescriptor.Content = SIPPacketMangler.MangleSDP(CallDescriptor.Content, CallDescriptor.MangleIPAddress.ToString(), out wasSDPMangled);
            }

            ThreadPool.QueueUserWorkItem(delegate { m_xmppClient.Connect(); });
        }
Example #11
0
        /// <summary>
        /// Sends the ACK request as a new transaction. This is required for 2xx responses.
        /// </summary>
        /// <param name="content">The optional content body for the ACK request.</param>
        /// <param name="contentType">The optional content type.</param>
        private SIPRequest Get2xxAckRequest(string content, string contentType)
        {
            try
            {
                var sipResponse = m_transactionFinalResponse;

                if (sipResponse.Header.To != null)
                {
                    m_remoteTag = sipResponse.Header.To.ToTag;
                }

                SIPURI ackURI = m_transactionRequest.URI;
                if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
                {
                    ackURI = sipResponse.Header.Contact[0].ContactURI;
                    // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem.
                    if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) &&
                        IPSocket.IsPrivateAddress(ackURI.Host) &&
                        !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank())
                    {
                        // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact.
                        SIPEndPoint remoteUASSIPEndPoint =
                            SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom);
                        ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString();
                    }
                }

                // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields.
                var ackRequest = GetNewTransactionAcknowledgeRequest(SIPMethodsEnum.ACK, sipResponse, ackURI);

                if (content.NotNullOrBlank())
                {
                    ackRequest.Body = content;
                    ackRequest.Header.ContentLength = ackRequest.Body.Length;
                    ackRequest.Header.ContentType   = contentType;
                }

                return(ackRequest);
            }
            catch (Exception excp)
            {
                Logger.Logger.Error($"Exception Get2xxAckRequest. ->{excp.Message}");
                throw excp;
            }
        }
Example #12
0
        /// <summary>
        /// Places an outgoing SIP call.
        /// </summary>
        /// <param name="destination">The SIP URI to place a call to. The destination can be a full SIP URI in which case the all will
        /// be placed anonymously directly to that URI. Alternatively it can be just the user portion of a URI in which case it will
        /// be sent to the configured SIP server.</param>
        public void Call(MediaManager mediaManager, string destination)
        {
            //_initialisationTask.Wait(_cancelCallTokenSource.Token);

            _mediaManager = mediaManager;
            _mediaManager.NewCall();

            // Determine if this is a direct anonymous call or whether it should be placed using the pre-configured SIP server account.
            SIPURI callURI     = null;
            string sipUsername = null;
            string sipPassword = null;
            string fromHeader  = null;

            if (destination.Contains("@") || m_sipServer == null)
            {
                // Anonymous call direct to SIP server specified in the URI.
                callURI    = SIPURI.ParseSIPURIRelaxed(destination);
                fromHeader = (new SIPFromHeader(m_sipFromName, SIPURI.ParseSIPURI(SIPFromHeader.DEFAULT_FROM_URI), null)).ToString();
            }
            else
            {
                // This call will use the pre-configured SIP account.
                callURI     = SIPURI.ParseSIPURIRelaxed(destination + "@" + m_sipServer);
                sipUsername = m_sipUsername;
                sipPassword = m_sipPassword;
                fromHeader  = (new SIPFromHeader(m_sipFromName, new SIPURI(m_sipUsername, m_sipServer, null), null)).ToString();
            }

            StatusMessage("Starting call to " + callURI.ToString() + ".");

            m_uac               = new SIPClientUserAgent(m_sipTransport, null, null, null, null);
            m_uac.CallTrying   += CallTrying;
            m_uac.CallRinging  += CallRinging;
            m_uac.CallAnswered += CallAnswered;
            m_uac.CallFailed   += CallFailed;

            // Get the SDP requesting that the public IP address be used if the host on the call destination is not a private IP address.
            SDP sdp = _mediaManager.GetSDP(!(IPSocket.IsIPAddress(callURI.Host) && IPSocket.IsPrivateAddress(callURI.Host)));

            System.Diagnostics.Debug.WriteLine(sdp.ToString());
            SIPCallDescriptor callDescriptor = new SIPCallDescriptor(sipUsername, sipPassword, callURI.ToString(), fromHeader, null, null, null, null, SIPCallDirection.Out, _sdpMimeContentType, sdp.ToString(), null);

            m_uac.Call(callDescriptor);
        }
Example #13
0
        /// <summary>
        /// This constructor is used by server user agents or SIP elements acting in a server user agent role. When
        /// acting as a server user agent the local fields are contained in the To header and the remote fields are
        /// in the From header.
        /// </summary>
        public SIPDialogue(
            UASInviteTransaction uasInviteTransaction)
        {
            Id = Guid.NewGuid();

            CallId = uasInviteTransaction.TransactionRequest.Header.CallId;
            //RouteSet = (uasInviteTransaction.TransactionFinalResponse != null && uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes.Reversed() : null;
            RouteSet        = (uasInviteTransaction.TransactionFinalResponse != null && uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes != null) ? uasInviteTransaction.TransactionFinalResponse.Header.RecordRoutes : null;
            LocalUserField  = uasInviteTransaction.TransactionFinalResponse.Header.To.ToUserField;
            LocalTag        = uasInviteTransaction.TransactionFinalResponse.Header.To.ToTag;
            RemoteUserField = uasInviteTransaction.TransactionFinalResponse.Header.From.FromUserField;
            RemoteTag       = uasInviteTransaction.TransactionFinalResponse.Header.From.FromTag;
            CSeq            = uasInviteTransaction.TransactionRequest.Header.CSeq;
            CDRId           = uasInviteTransaction.CDR != null ? uasInviteTransaction.CDR.CDRId : Guid.Empty;
            ContentType     = uasInviteTransaction.TransactionFinalResponse.Header.ContentType;
            SDP             = uasInviteTransaction.TransactionFinalResponse.Body;
            RemoteSDP       = uasInviteTransaction.TransactionRequest.Body ?? uasInviteTransaction.AckRequest.Body;
            Inserted        = DateTime.UtcNow;
            Direction       = SIPCallDirection.In;

            if (uasInviteTransaction.m_gotPrack)
            {
                CSeq++;
            }

            RemoteTarget  = new SIPURI(uasInviteTransaction.TransactionRequest.URI.Scheme, uasInviteTransaction.TransactionRequest.RemoteSIPEndPoint.CopyOf());
            ProxySendFrom = uasInviteTransaction.TransactionRequest.Header.ProxyReceivedOn;
            if (uasInviteTransaction.TransactionRequest.Header.Contact != null && uasInviteTransaction.TransactionRequest.Header.Contact.Count > 0)
            {
                RemoteTarget = uasInviteTransaction.TransactionRequest.Header.Contact[0].ContactURI.CopyOf();
                if (!uasInviteTransaction.TransactionRequest.Header.ProxyReceivedFrom.IsNullOrBlank())
                {
                    // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact.
                    // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem.
                    if (RouteSet == null && IPSocket.IsPrivateAddress(RemoteTarget.Host))
                    {
                        SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(uasInviteTransaction.TransactionRequest.Header.ProxyReceivedFrom);
                        RemoteTarget.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString();
                    }
                }
            }
        }
Example #14
0
        /// <summary>
        /// Places an outgoing SIP call.
        /// </summary>
        /// <param name="destination">The SIP URI to place a call to. The destination can be a full SIP URI in which case the all will
        /// be placed anonymously directly to that URI. Alternatively it can be just the user portion of a URI in which case it will
        /// be sent to the configured SIP server.</param>
        public void Call(string destination)
        {
            // Determine if this is a direct anonymous call or whether it should be placed using the pre-configured SIP server account.
            SIPURI callURI     = null;
            string sipUsername = null;
            string sipPassword = null;
            string fromHeader  = null;

            if (destination.Contains("@") || m_sipServer == null)
            {
                // Anonymous call direct to SIP server specified in the URI.
                callURI = SIPURI.ParseSIPURIRelaxed(destination);
            }
            else
            {
                // This call will use the pre-configured SIP account.
                callURI     = SIPURI.ParseSIPURIRelaxed(destination + "@" + m_sipServer);
                sipUsername = m_sipUsername;
                sipPassword = m_sipPassword;
                fromHeader  = (new SIPFromHeader(m_sipFromName, new SIPURI(m_sipUsername, m_sipServer, null), null)).ToString();
            }

            StatusMessage("Starting call to " + callURI.ToString() + ".");

            m_uac               = new SIPClientUserAgent(m_sipTransport, null, null, null, null);
            m_uac.CallTrying   += CallTrying;
            m_uac.CallRinging  += CallRinging;
            m_uac.CallAnswered += CallAnswered;
            m_uac.CallFailed   += CallFailed;

            _audioChannel = new AudioChannel();

            // Get the SDP requesting that the public IP address be used if the host on the call destination is not a private IP address.
            SDP sdp = _audioChannel.GetSDP(!(IPSocket.IsIPAddress(callURI.Host) && IPSocket.IsPrivateAddress(callURI.Host)));
            SIPCallDescriptor callDescriptor = new SIPCallDescriptor(sipUsername, sipPassword, callURI.ToString(), fromHeader, null, null, null, null, SIPCallDirection.Out, SDP.SDP_MIME_CONTENTTYPE, sdp.ToString(), null);

            m_uac.Call(callDescriptor);
        }
        public void Send2xxAckRequest(string content, string contentType)
        {
            var sipResponse = m_transactionFinalResponse;

            if (sipResponse.Header.To != null)
            {
                m_remoteTag = sipResponse.Header.To.ToTag;
            }

            SIPURI ackURI = m_transactionRequest.URI;

            if (sipResponse.Header.Contact != null && sipResponse.Header.Contact.Count > 0)
            {
                ackURI = sipResponse.Header.Contact[0].ContactURI;
                // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's in a Record-Route header that's its problem.
                if ((sipResponse.Header.RecordRoutes == null || sipResponse.Header.RecordRoutes.Length == 0) &&
                    IPSocket.IsPrivateAddress(ackURI.Host) && !sipResponse.Header.ProxyReceivedFrom.IsNullOrBlank())
                {
                    // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should mangle the contact.
                    SIPEndPoint remoteUASSIPEndPoint =
                        SIPEndPoint.ParseSIPEndPoint(sipResponse.Header.ProxyReceivedFrom);
                    ackURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString();
                }
            }

            // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields.
            var ackRequest = GetNewTransactionACKRequest(sipResponse, ackURI, LocalSIPEndPoint);

            if (content.NotNullOrBlank())
            {
                ackRequest.Body = content;
                ackRequest.Header.ContentLength = ackRequest.Body.Length;
                ackRequest.Header.ContentType   = contentType;
            }

            base.SendRequest(ackRequest);
        }
Example #16
0
        /// <summary>
        /// Generates the ACK or PRACK request to acknowledge a response. This method generates the ACK requests
        /// for INVITE 2xx and PRACK for 1xx responses. The request needs to be sent as part of a new transaction.
        /// Note for constructing the ACK for INVITE >= 300 responses is <seealso cref="GetInTransactionACKRequest"/>.
        /// </summary>
        /// <param name="ackResponse">The response being acknowledged.</param>
        /// <param name="ackMethod">The acknowledgement request method, either ACK or PRACK.</param>
        /// <param name="cseq">The SIP CSeq header value to set on the acknowledge request.</param>
        /// <param name="content">The optional content body for the ACK request.</param>
        /// <param name="contentType">The optional content type.</param>
        private SIPRequest GetAcknowledgeRequest(SIPResponse ackResponse, SIPMethodsEnum ackMethod, int cseq, string content, string contentType)
        {
            if (ackResponse.Header.To != null)
            {
                m_remoteTag = ackResponse.Header.To.ToTag;
            }

            SIPURI requestURI = m_transactionRequest.URI.CopyOf();

            if (ackResponse.Header.Contact?.Count > 0)
            {
                requestURI = ackResponse.Header.Contact[0].ContactURI;
                // Don't mangle private contacts if there is a Record-Route header. If a proxy is putting private IP's
                // in a Record-Route header that's its problem.
                if ((ackResponse.Header.RecordRoutes == null || ackResponse.Header.RecordRoutes.Length == 0) &&
                    IPSocket.IsPrivateAddress(requestURI.Host) && !ackResponse.Header.ProxyReceivedFrom.IsNullOrBlank())
                {
                    // Setting the Proxy-ReceivedOn header is how an upstream proxy will let an agent know it should
                    // mangle the contact.
                    SIPEndPoint remoteUASSIPEndPoint = SIPEndPoint.ParseSIPEndPoint(ackResponse.Header.ProxyReceivedFrom);
                    requestURI.Host = remoteUASSIPEndPoint.GetIPEndPoint().ToString();
                }
            }

            // ACK for 2xx response needs to be a new transaction and gets routed based on SIP request fields.
            var ackRequest = GetNewTxACKRequest(ackMethod, cseq, ackResponse, requestURI);

            if (content.NotNullOrBlank())
            {
                ackRequest.Body = content;
                ackRequest.Header.ContentLength = ackRequest.Body.Length;
                ackRequest.Header.ContentType   = contentType;
            }

            return(ackRequest);
        }
Example #17
0
        private static void SIPTransportRequestReceived(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            if (sipRequest.Method == SIPMethodsEnum.BYE)
            {
                var rtpJob = (from job in m_rtpJobs.Values where job.UAS.CallRequest.Header.CallId == sipRequest.Header.CallId select job).FirstOrDefault();

                if (rtpJob != null)
                {
                    rtpJob.Stop();
                    // Call has been hungup by remote end.
                    Console.WriteLine("Call hungup by client: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n");
                    Publish(rtpJob.QueueName, "BYE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + ".");
                    //Console.WriteLine("Request Received " + localSIPEndPoint + "<-" + remoteEndPoint + "\n" + sipRequest.ToString());
                    //m_uas.SIPDialogue.Hangup(m_sipTransport, null);
                    SIPResponse okResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                    m_sipTransport.SendResponse(okResponse);
                }
                else
                {
                    Console.WriteLine("Unmatched BYE request received for " + sipRequest.URI.ToString() + ".\n");
                    SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null);
                    m_sipTransport.SendResponse(noCallLegResponse);
                }
            }
            else if (sipRequest.Method == SIPMethodsEnum.INVITE)
            {
                Console.WriteLine("Incoming call request: " + localSIPEndPoint + "<-" + remoteEndPoint + " " + sipRequest.URI.ToString() + ".\n");
                Publish(sipRequest.URI.User, "INVITE request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + ".");

                Console.WriteLine(sipRequest.Body);

                SIPPacketMangler.MangleSIPRequest(SIPMonitorServerTypesEnum.Unknown, sipRequest, null, LogTraceMessage);

                UASInviteTransaction uasTransaction = m_sipTransport.CreateUASTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, null);
                var uas = new SIPServerUserAgent(m_sipTransport, null, null, null, SIPCallDirection.In, null, null, LogTraceMessage, uasTransaction);
                uas.CallCancelled += UASCallCancelled;

                RTPDiagnosticsJob rtpJob = new RTPDiagnosticsJob(m_rtpListenIPAddress, m_publicIPAddress, uas, sipRequest);

                string sdpAddress = SDP.GetSDPRTPEndPoint(sipRequest.Body).Address.ToString();

                // Only mangle if there is something to change. For example the server could be on the same private subnet in which case it can't help.
                IPEndPoint expectedRTPEndPoint = new IPEndPoint(rtpJob.RemoteRTPEndPoint.Address, rtpJob.RemoteRTPEndPoint.Port);
                if (IPSocket.IsPrivateAddress(rtpJob.RemoteRTPEndPoint.Address.ToString()))
                {
                    expectedRTPEndPoint.Address = remoteEndPoint.Address;
                }

                Publish(sipRequest.URI.User, "Advertised RTP remote socket " + rtpJob.RemoteRTPEndPoint + ", expecting from " + expectedRTPEndPoint + ".");
                m_rtpJobs.Add(rtpJob.RTPListenEndPoint.Port, rtpJob);

                //ThreadPool.QueueUserWorkItem(delegate { StartRTPListener(rtpJob); });

                Console.WriteLine(rtpJob.LocalSDP.ToString());

                uas.Answer("application/sdp", rtpJob.LocalSDP.ToString(), CallProperties.CreateNewTag(), null, SIPDialogueTransferModesEnum.NotAllowed);

                var hangupTimer = new Timer(delegate
                {
                    if (!rtpJob.StopJob)
                    {
                        if (uas != null && uas.SIPDialogue != null)
                        {
                            if (rtpJob.RTPPacketReceived && !rtpJob.ErrorOnRTPSend)
                            {
                                Publish(sipRequest.URI.User, "Test completed. There were no RTP send or receive errors.");
                            }
                            else if (!rtpJob.RTPPacketReceived)
                            {
                                Publish(sipRequest.URI.User, "Test completed. An error was identified, no RTP packets were received.");
                            }
                            else
                            {
                                Publish(sipRequest.URI.User, "Test completed. An error was identified, there was a problem when attempting to send an RTP packet.");
                            }
                            rtpJob.Stop();
                            uas.SIPDialogue.Hangup(m_sipTransport, null);
                        }
                    }
                }, null, HANGUP_TIMEOUT, Timeout.Infinite);
            }
            else if (sipRequest.Method == SIPMethodsEnum.CANCEL)
            {
                UASInviteTransaction inviteTransaction = (UASInviteTransaction)m_sipTransport.GetTransaction(SIPTransaction.GetRequestTransactionId(sipRequest.Header.Vias.TopViaHeader.Branch, SIPMethodsEnum.INVITE));

                if (inviteTransaction != null)
                {
                    Console.WriteLine("Matching CANCEL request received " + sipRequest.URI.ToString() + ".\n");
                    Publish(sipRequest.URI.User, "CANCEL request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + ".");
                    SIPCancelTransaction cancelTransaction = m_sipTransport.CreateCancelTransaction(sipRequest, remoteEndPoint, localSIPEndPoint, inviteTransaction);
                    cancelTransaction.GotRequest(localSIPEndPoint, remoteEndPoint, sipRequest);
                }
                else
                {
                    Console.WriteLine("No matching transaction was found for CANCEL to " + sipRequest.URI.ToString() + ".\n");
                    SIPResponse noCallLegResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.CallLegTransactionDoesNotExist, null);
                    m_sipTransport.SendResponse(noCallLegResponse);
                }
            }
            else
            {
                Console.WriteLine("SIP " + sipRequest.Method + " request received but no processing has been set up for it, rejecting.\n");
                Publish(sipRequest.URI.User, sipRequest.Method + " request received from " + remoteEndPoint + " for " + sipRequest.URI.ToString() + ".");
                SIPResponse notAllowedResponse = SIPTransport.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                m_sipTransport.SendResponse(notAllowedResponse);
            }
        }