コード例 #1
0
        private void toResponse(SIP_RequestReceivedEventArgs e)
        {
            SIP_Uri uri = e.Request.RequestLine.Uri as SIP_Uri;

            try
            {
                _sdp     = SDP_Message.Parse(MyEncoder.Encoder.GetString(e.Request.Data));
                RemoteIP = _sdp.Origin.UnicastAddress;
                if (_sdp.MediaDescriptions.Count == 0)
                {
                    e.ServerTransaction.SendResponse(_sipServer.Stack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request, e.Request));
                    return;
                }
                else
                {
                    RemotePort = _sdp.MediaDescriptions[0].Port;
                }
            }
            catch (Exception)
            {
                //解析SDP失败。
                e.ServerTransaction.SendResponse(_sipServer.Stack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request, e.Request));
                return;
            }

            //send 100 Trying;
            e.ServerTransaction.SendResponse(_sipServer.Stack.CreateResponse(SIP_ResponseCodes.x100_Trying, e.Request));

            _videoId = _sipServer.DeviceManager.GetVideoId(uri.User);
            if (_videoId != null)
            {
                RTPServer   rtp     = _sipServer.RTPManager.GetOrAddServer(_videoId);
                SDP_Message respSDP = new SDP_Message();
                respSDP.Version     = "0";
                respSDP.Origin      = new SDP_Origin(uri.User, 0, 0, "IN", "IPV4", rtp.LocalIP);
                respSDP.SessionName = "Play";
                respSDP.Connection  = new SDP_Connection("IN", "IPV4", rtp.LocalIP);
                respSDP.SSRC        = SDP_Utils.SSRC2String(SDP_Utils.GenSSRC(uri.User, true)); //根据国标补充协议标准生成SSRC。
                respSDP.Times.Add(new SDP_Time(0, 0));
                respSDP.MediaDescriptions.Add(new SDP_MediaDescription("video", rtp.Port, 2, "RTP/AVP", new string[] { "96", "97", "98" }));
                respSDP.Attributes.Add(new SDP_Attribute("sendonly", ""));
                respSDP.Attributes.Add(new SDP_Attribute("rtpmap", "96 PS/90000"));
                respSDP.Attributes.Add(new SDP_Attribute("rtpmap", "97 MPEG4/90000"));
                respSDP.Attributes.Add(new SDP_Attribute("rtpmap", "98 H264/90000"));

                SIP_Response resp = _sipServer.Stack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request);
                resp.Data = respSDP.ToByte();
                e.ServerTransaction.SendResponse(resp);
            }
            else
            {
                //没有找到视频源。
                e.ServerTransaction.SendResponse(_sipServer.Stack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, e.Request));
            }
        }
コード例 #2
0
ファイル: SIPDump.cs プロジェクト: skrusty/SIPDump
 public static SIP_Message ParseSIPMessage(byte[] data)
 {
     try
     {
         return(SIP_Request.Parse(data));
     }
     catch
     {
         try
         {
             return(SIP_Response.Parse(data));
         }
         catch
         {
             return(null);
         }
     }
 }
コード例 #3
0
        /// <summary>
        /// Sends ringing to remote party.
        /// </summary>
        /// <param name="sdp">Early media answer or early media offer when initial INVITE don't have SDP.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when call is not in valid state and this method is called.</exception>
        public void SendRinging(SDP_Message sdp)
        {
            if (m_State == SIP_UA_CallState.Disposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (m_State != SIP_UA_CallState.WaitingToAccept)
            {
                throw new InvalidOperationException("Accept method can be called only in 'SIP_UA_CallState.WaitingToAccept' state.");
            }

            SIP_Response response = m_pUA.Stack.CreateResponse(SIP_ResponseCodes.x180_Ringing, m_pInitialInviteTransaction.Request, m_pInitialInviteTransaction.Flow);

            if (sdp != null)
            {
                response.ContentType = "application/sdp";
                response.Data        = sdp.ToByte();

                m_pLocalSDP = sdp;
            }
            m_pInitialInviteTransaction.SendResponse(response);
        }
コード例 #4
0
ファイル: SIP_Proxy.cs プロジェクト: w8w8w8/Lumisoft.Net.Core
        /// <summary>
        /// Authenticates SIP request. This method also sends all needed replys to request sender.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        /// <param name="userName">If authentication sucessful, then authenticated user name is stored to this variable.</param>
        /// <returns>Returns true if request was authenticated.</returns>
        internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e, out string userName)
        {
            userName = null;

            SIP_t_Credentials credentials = SIP_Utils.GetCredentials(e.Request, m_pStack.Realm);

            // No credentials for our realm.
            if (credentials == null)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required, e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }

            Auth_HttpDigest auth = new Auth_HttpDigest(credentials.AuthData, e.Request.RequestLine.Method);

            // Check opaque validity.
            if (auth.Opaque != m_Opaque)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Opaque value won't match !", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }
            // Check nonce validity.
            if (!m_pStack.DigestNonceManager.NonceExists(auth.Nonce))
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Invalid nonce value !", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }
            // Valid nonce, consume it so that nonce can't be used any more.
            else
            {
                m_pStack.DigestNonceManager.RemoveNonce(auth.Nonce);
            }

            SIP_AuthenticateEventArgs eArgs = this.OnAuthenticate(auth);

            // Authenticate failed.
            if (!eArgs.Authenticated)
            {
                SIP_Response notAuthenticatedResponse = m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Authentication failed.", e.Request);
                notAuthenticatedResponse.ProxyAuthenticate.Add(new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque).ToChallenge());

                // Send response
                e.ServerTransaction.SendResponse(notAuthenticatedResponse);
                return(false);
            }

            userName = auth.UserName;

            return(true);
        }
コード例 #5
0
        /// <summary>
        /// Handles REGISTER method.
        /// </summary>
        /// <param name="e">Request event arguments.</param>
        internal void Register(SIP_RequestReceivedEventArgs e)
        {
            /* RFC 3261 10.3 Processing REGISTER Requests.
             *  1. The registrar inspects the Request-URI to determine whether it
             *     has access to bindings for the domain identified in the
             *     Request-URI.  If not, and if the server also acts as a proxy
             *     server, the server SHOULD forward the request to the addressed
             *     domain, following the general behavior for proxying messages
             *     described in Section 16.
             *
             *  2. To guarantee that the registrar supports any necessary extensions,
             *     the registrar MUST process the Require header field.
             *
             *  3. A registrar SHOULD authenticate the UAC.
             *
             *  4. The registrar SHOULD determine if the authenticated user is
             *     authorized to modify registrations for this address-of-record.
             *     For example, a registrar might consult an authorization
             *     database that maps user names to a list of addresses-of-record
             *     for which that user has authorization to modify bindings.  If
             *     the authenticated user is not authorized to modify bindings,
             *     the registrar MUST return a 403 (Forbidden) and skip the
             *     remaining steps.
             *
             *  5. The registrar extracts the address-of-record from the To header
             *     field of the request.  If the address-of-record is not valid
             *     for the domain in the Request-URI, the registrar MUST send a
             *     404 (Not Found) response and skip the remaining steps.  The URI
             *     MUST then be converted to a canonical form.  To do that, all
             *     URI parameters MUST be removed (including the user-param), and
             *     any escaped characters MUST be converted to their unescaped
             *     form.  The result serves as an index into the list of bindings.
             *
             *  6. The registrar checks whether the request contains the Contact
             *     header field.  If not, it skips to the last step.  If the
             *     Contact header field is present, the registrar checks if there
             *     is one Contact field value that contains the special value "*"
             *     and an Expires field.  If the request has additional Contact
             *     fields or an expiration time other than zero, the request is
             *     invalid, and the server MUST return a 400 (Invalid Request) and
             *     skip the remaining steps.  If not, the registrar checks whether
             *     the Call-ID agrees with the value stored for each binding.  If
             *     not, it MUST remove the binding.  If it does agree, it MUST
             *     remove the binding only if the CSeq in the request is higher
             *     than the value stored for that binding.  Otherwise, the update
             *     MUST be aborted and the request fails.
             *
             *  7. The registrar now processes each contact address in the Contact
             *     header field in turn.  For each address, it determines the
             *     expiration interval as follows:
             *
             *       -  If the field value has an "expires" parameter, that value
             *          MUST be taken as the requested expiration.
             *
             *       -  If there is no such parameter, but the request has an
             *          Expires header field, that value MUST be taken as the requested expiration.
             *
             *       -  If there is neither, a locally-configured default value MUST
             *          be taken as the requested expiration.
             *
             *     The registrar MAY choose an expiration less than the requested
             *     expiration interval.  If and only if the requested expiration
             *     interval is greater than zero AND smaller than one hour AND
             *     less than a registrar-configured minimum, the registrar MAY
             *     reject the registration with a response of 423 (Interval Too
             *     Brief).  This response MUST contain a Min-Expires header field
             *     that states the minimum expiration interval the registrar is
             *     willing to honor.  It then skips the remaining steps.
             *
             *     For each address, the registrar then searches the list of
             *     current bindings using the URI comparison rules.  If the
             *     binding does not exist, it is tentatively added.  If the
             *     binding does exist, the registrar checks the Call-ID value.  If
             *     the Call-ID value in the existing binding differs from the
             *     Call-ID value in the request, the binding MUST be removed if
             *     the expiration time is zero and updated otherwise.  If they are
             *     the same, the registrar compares the CSeq value.  If the value
             *     is higher than that of the existing binding, it MUST update or
             *     remove the binding as above.  If not, the update MUST be
             *     aborted and the request fails.
             *
             *     This algorithm ensures that out-of-order requests from the same
             *     UA are ignored.
             *
             *     Each binding record records the Call-ID and CSeq values from
             *     the request.
             *
             *     The binding updates MUST be committed (that is, made visible to
             *     the proxy or redirect server) if and only if all binding
             *     updates and additions succeed.  If any one of them fails (for
             *     example, because the back-end database commit failed), the
             *     request MUST fail with a 500 (Server Error) response and all
             *     tentative binding updates MUST be removed.
             *
             *  8. The registrar returns a 200 (OK) response.  The response MUST
             *     contain Contact header field values enumerating all current
             *     bindings.  Each Contact value MUST feature an "expires"
             *     parameter indicating its expiration interval chosen by the
             *     registrar.  The response SHOULD include a Date header field.
             */

            SIP_ServerTransaction transaction = e.ServerTransaction;
            SIP_Request           request     = e.Request;
            SIP_Uri to       = null;
            string  userName = "";

            // Probably we need to do validate in SIP stack.

            #region Validate request

            if (SIP_Utils.IsSipOrSipsUri(request.To.Address.Uri.ToString()))
            {
                to = (SIP_Uri)request.To.Address.Uri;
            }
            else
            {
                transaction.SendResponse(
                    m_pStack.CreateResponse(
                        SIP_ResponseCodes.x400_Bad_Request + ": To: value must be SIP or SIPS URI.", request));
                return;
            }

            #endregion

            #region 1. Check if we are responsible for Request-URI domain

            // if(m_pProxy.OnIsLocalUri(e.Request.Uri)){
            // }
            // TODO:

            #endregion

            #region 2. Check that all required extentions supported

            #endregion

            #region 3. Authenticate request

            if (!m_pProxy.AuthenticateRequest(e, out userName))
            {
                return;
            }

            #endregion

            #region 4. Check if user user is authorized to modify registrations

            // We do this in next step(5.).

            #endregion

            #region 5. Check if address of record exists

            if (!m_pProxy.OnAddressExists(to.Address))
            {
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, request));
                return;
            }
            else if (!OnCanRegister(userName, to.Address))
            {
                transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x403_Forbidden, request));
                return;
            }

            #endregion

            #region 6. Process * Contact if exists

            // Check if we have star contact.
            SIP_t_ContactParam starContact = null;
            foreach (SIP_t_ContactParam c in request.Contact.GetAllValues())
            {
                if (c.IsStarContact)
                {
                    starContact = c;
                    break;
                }
            }

            // We have star contact.
            if (starContact != null)
            {
                if (request.Contact.GetAllValues().Length > 1)
                {
                    transaction.SendResponse(
                        m_pStack.CreateResponse(
                            SIP_ResponseCodes.x400_Bad_Request +
                            ": RFC 3261 10.3.6 -> If star(*) present, only 1 contact allowed.",
                            request));
                    return;
                }
                else if (starContact.Expires != 0)
                {
                    transaction.SendResponse(
                        m_pStack.CreateResponse(
                            SIP_ResponseCodes.x400_Bad_Request +
                            ": RFC 3261 10.3.6 -> star(*) contact parameter 'expires' value must be always '0'.",
                            request));
                    return;
                }

                // Remove bindings.
                SIP_Registration reg = m_pRegistrations[to.Address];
                if (reg != null)
                {
                    foreach (SIP_RegistrationBinding b in reg.Bindings)
                    {
                        if (request.CallID != b.CallID || request.CSeq.SequenceNumber > b.CSeqNo)
                        {
                            b.Remove();
                        }
                    }
                }
            }

            #endregion

            #region 7. Process Contact values

            if (starContact == null)
            {
                SIP_Registration reg = m_pRegistrations[to.Address];
                if (reg == null)
                {
                    reg = new SIP_Registration(userName, to.Address);
                    m_pRegistrations.Add(reg);
                }

                // We may do updates in batch only.
                // We just validate all values then do update(this ensures that update doesn't fail).

                // Check expires and CSeq.
                foreach (SIP_t_ContactParam c in request.Contact.GetAllValues())
                {
                    if (c.Expires == -1)
                    {
                        c.Expires = request.Expires;
                    }
                    if (c.Expires == -1)
                    {
                        c.Expires = m_pProxy.Stack.MinimumExpireTime;
                    }
                    // We must accept 0 values - means remove contact.
                    if (c.Expires != 0 && c.Expires < m_pProxy.Stack.MinimumExpireTime)
                    {
                        SIP_Response resp = m_pStack.CreateResponse(
                            SIP_ResponseCodes.x423_Interval_Too_Brief, request);
                        resp.MinExpires = m_pProxy.Stack.MinimumExpireTime;
                        transaction.SendResponse(resp);
                        return;
                    }

                    SIP_RegistrationBinding currentBinding = reg.GetBinding(c.Address.Uri);
                    if (currentBinding != null && currentBinding.CallID == request.CallID &&
                        request.CSeq.SequenceNumber < currentBinding.CSeqNo)
                    {
                        transaction.SendResponse(
                            m_pStack.CreateResponse(
                                SIP_ResponseCodes.x400_Bad_Request + ": CSeq value out of order.", request));
                        return;
                    }
                }

                // Do binding updates.
                reg.AddOrUpdateBindings(e.ServerTransaction.Flow,
                                        request.CallID,
                                        request.CSeq.SequenceNumber,
                                        request.Contact.GetAllValues());
            }

            #endregion

            #region 8. Create 200 OK response and return all current bindings

            SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, request);
            response.Date = DateTime.Now;
            SIP_Registration registration = m_pRegistrations[to.Address];
            if (registration != null)
            {
                foreach (SIP_RegistrationBinding b in registration.Bindings)
                {
                    // Don't list expired bindings what wait to be disposed.
                    if (b.TTL > 1)
                    {
                        response.Header.Add("Contact:", b.ToContactValue());
                    }
                }
            }
            // Add Authentication-Info:, then client knows next nonce.
            response.AuthenticationInfo.Add("qop=\"auth\",nextnonce=\"" +
                                            m_pStack.DigestNonceManager.CreateNonce() + "\"");
            transaction.SendResponse(response);

            #endregion
        }
コード例 #6
0
ファイル: SIPDump.cs プロジェクト: skrusty/SIPDump
        private static void device_OnPacketArrival(object sender, CaptureEventArgs e)
        {
            var time = e.Packet.Timeval.Date;
            var len  = e.Packet.Data.Length;

            var packet = PacketDotNet.Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data);

            var udpPacket = PacketDotNet.UdpPacket.GetEncapsulated(packet);

            if (udpPacket != null)
            {
                try
                {
                    // signalling packet
                    SIP_Message msg = ParseSIPMessage(udpPacket.PayloadData);
                    if (msg != null && msg.CallID != null)
                    {
                        SDP_Message sdp = null;

                        try
                        {
                            sdp = SDP_Message.Parse(System.Text.ASCIIEncoding.Default.GetString(msg.Data));
                        }
                        catch { }

                        if (msg is SIP_Request && msg.CallID != null)
                        {
                            SIP_Request r = (SIP_Request)msg;

                            if (!Call.Calls.ContainsKey(r.CallID))
                            {
                                if (r.RequestLine.Method == "INVITE")
                                {
                                    Call.Calls.Add(r.CallID, new Call(r.CallID));
                                    Call.Calls[r.CallID].CallerIP = ((IpPacket)udpPacket.ParentPacket).SourceAddress;
                                    Call.Calls[r.CallID].CalleeIP = ((IpPacket)udpPacket.ParentPacket).DestinationAddress;
                                }
                                else
                                {
                                    return;     // Ignore this conversation
                                }
                            }

                            // if this is an invite, do we have an audio rtp port defined?
                            if (r.RequestLine.Method == "INVITE")
                            {
                                if (sdp != null)
                                {
                                    foreach (var a in sdp.MediaDescriptions)
                                    {
                                        Console.Out.WriteLine(r.CallID + " - Got RTP Media Port: " + ((IpPacket)udpPacket.ParentPacket).SourceAddress + ":" + a.Port.ToString());
                                        if (Call.Calls[r.CallID].CallerIP.ToString() == ((IpPacket)udpPacket.ParentPacket).SourceAddress.ToString())
                                        {
                                            Call.Calls[r.CallID].CallerRTPPort = a.Port;
                                        }
                                        else
                                        {
                                            Call.Calls[r.CallID].CalleeRTPPort = a.Port;
                                        }
                                    }
                                }
                            }

                            if (r.RequestLine.Method == "BYE")
                            {
                                if (Call.Calls.ContainsKey(r.CallID))
                                {
                                    // Log bye was recevied
                                    Call.Calls[r.CallID].SeenBYE = true;

                                    // Now indicate who hung up
                                    Call.Calls[r.CallID].WhoHungUp = ((IpPacket)udpPacket.ParentPacket).SourceAddress == Call.Calls[r.CallID].CallerIP ?
                                                                     Call.CallDirection.Caller : Call.CallDirection.Callee;
                                }
                                else
                                {
                                    Console.WriteLine("Unknown CallID: " + r.CallID);
                                }
                            }
                        }
                        else if (msg is SIP_Response && msg.CallID != null)
                        {
                            SIP_Response r = (SIP_Response)msg;

                            if (sdp != null)
                            {
                                foreach (var a in sdp.MediaDescriptions)
                                {
                                    Console.Out.WriteLine(r.CallID + " - Got RTP Media Port: " + ((IpPacket)udpPacket.ParentPacket).SourceAddress + ":" + a.Port.ToString());
                                    if (Call.Calls[r.CallID].CallerIP.ToString() == ((IpPacket)udpPacket.ParentPacket).SourceAddress.ToString())
                                    {
                                        Call.Calls[r.CallID].CallerRTPPort = a.Port;
                                    }
                                    else
                                    {
                                        Call.Calls[r.CallID].CalleeRTPPort = a.Port;
                                    }
                                }
                            }

                            if (Call.Calls.ContainsKey(r.CallID))
                            {
                                if (r.StatusCodeType == SIP_StatusCodeType.Success && Call.Calls[r.CallID].SeenBYE)
                                {
                                    Call.Calls[r.CallID].Confirmed = true;
                                }
                            }
                        }

                        // Add packet to history
                        if (Call.Calls.ContainsKey(msg.CallID))
                        {
                            Call.Calls[msg.CallID].WritePacket(e.Packet, Call.PacketType.SIPDialog);
                            // Check to see is this call has been terminated
                            if (Call.Calls[msg.CallID].Confirmed)
                            {
                                // Close off the call now last data has been written
                                Console.WriteLine("Call Ended: " + msg.CallID);

                                // Close off the call
                                Call.Calls[msg.CallID].CloseCall();

                                // Remove the call from the in-memory list
                                Call.Calls.Remove(msg.CallID);
                            }
                        }
                    }
                    else
                    {
                        Call c = Call.GetCallByRTPPort(udpPacket.SourcePort);
                        if (c != null)
                        {
                            c.WritePacket(e.Packet, Call.PacketType.RTP);
                        }
                    }
                }
                catch (Exception ex) {
                    Console.WriteLine(ex.ToString());
                }
            }
        }
コード例 #7
0
        public void Handler(Packet packet)
        {
            var udpPacket = UdpPacket.GetEncapsulated(packet);

            // if it's not udp , udpPacket will be null and we don't handle it.
            if (udpPacket != null)
            {
                try
                {
                    // signalling packet
                    SIP_Message msg = ParseSIPMessage(udpPacket.PayloadData);
                    if (msg != null && msg.CallID != null)
                    {
                        SDP_Message sdp = null;
                        Console.WriteLine("SIP capture");
                        try
                        {
                            sdp = SDP_Message.Parse(System.Text.Encoding.Default.GetString(msg.Data));
                        }
                        catch { }

                        if (msg is SIP_Request && msg.CallID != null)
                        {
                            SIP_Request r = (SIP_Request)msg;
                            //already containsKey
                            if (!Call.SIPSessions.ContainsKey(r.CallID))
                            {
                                if (r.RequestLine.Method == "INVITE")
                                {
                                    Call.SIPSessions.Add(r.CallID, new Call(r.CallID));
                                    Call.SIPSessions[r.CallID].CallerIP = ((IpPacket)udpPacket.ParentPacket).SourceAddress;
                                    Call.SIPSessions[r.CallID].CalleeIP = ((IpPacket)udpPacket.ParentPacket).DestinationAddress;
                                }
                                else
                                {
                                    return;     // Ignore this conversation
                                }
                            }

                            // if this is an invite, do we have an audio rtp port defined?
                            if (r.RequestLine.Method == "INVITE")
                            {
                                if (sdp != null)
                                {
                                    foreach (var a in sdp.MediaDescriptions)
                                    {
                                        Console.Out.WriteLine(r.CallID + " - Got RTP Media Port: " + ((IpPacket)udpPacket.ParentPacket).SourceAddress + ":" + a.Port.ToString());
                                        if (Call.SIPSessions[r.CallID].CallerIP.ToString() == ((IpPacket)udpPacket.ParentPacket).SourceAddress.ToString())
                                        {
                                            Call.SIPSessions[r.CallID].CallerRTPPort = a.Port;
                                        }
                                        else
                                        {
                                            Call.SIPSessions[r.CallID].CalleeRTPPort = a.Port;
                                        }
                                        a.MediaFormats.GetType();

                                        break; // First description is about audio . Second is about viedo and we don't need it, so break.
                                    }
                                }
                            }

                            if (r.RequestLine.Method == "BYE")
                            {
                                if (Call.SIPSessions.ContainsKey(r.CallID))
                                {
                                    // Log bye was recevied
                                    Call.SIPSessions[r.CallID].SeenBYE = true;

                                    // Now indicate who hung up
                                    Call.SIPSessions[r.CallID].WhoHungUp = ((IpPacket)udpPacket.ParentPacket).SourceAddress == Call.SIPSessions[r.CallID].CallerIP ?
                                                                           Call.CallDirection.Caller : Call.CallDirection.Callee;
                                }
                                else
                                {
                                    Console.WriteLine("Unknown CallID: " + r.CallID);
                                }
                            }
                        }//    if (msg is SIP_Request && msg.CallID != null)
                        else if (msg is SIP_Response && msg.CallID != null)
                        {
                            SIP_Response r = (SIP_Response)msg;

                            if (r.StatusCode != 183 && r.StatusCode != 100 && r.StatusCode != 200)
                            {
                                Call.SIPSessions[r.CallID].isEnd = true;
                            }

                            if (sdp != null)
                            {
                                foreach (var a in sdp.MediaDescriptions)
                                {
                                    Console.Out.WriteLine(r.CallID + " - Got RTP Media Port: " + ((IpPacket)udpPacket.ParentPacket).SourceAddress + ":" + a.Port.ToString());
                                    if (Call.SIPSessions[r.CallID].CallerIP.ToString() == ((IpPacket)udpPacket.ParentPacket).SourceAddress.ToString())
                                    {
                                        Call.SIPSessions[r.CallID].CallerRTPPort = a.Port;
                                    }
                                    else
                                    {
                                        Call.SIPSessions[r.CallID].CalleeRTPPort = a.Port;
                                    }

                                    break; // First description is about audio . Second is about viedo and we don't need it, so break.
                                }
                            }

                            if (Call.SIPSessions.ContainsKey(r.CallID))
                            {
                                if (r.StatusCodeType == SIP_StatusCodeType.Success && Call.SIPSessions[r.CallID].SeenBYE)
                                {
                                    Call.SIPSessions[r.CallID].Confirmed = true;
                                    Call.SIPSessions[r.CallID].isEnd     = true;
                                }
                            }
                        }

                        // Add packet to history
                        if (Call.SIPSessions.ContainsKey(msg.CallID))
                        {
                            Call.SIPSessions[msg.CallID].WritePacket(packet, Call.PacketType.SIPDialog);
                            // Check to see is this call has been terminated
                            if (Call.SIPSessions[msg.CallID].Confirmed)
                            {
                                // Close off the call now last data has been written
                                Console.WriteLine("Call Ended: " + msg.CallID);

                                // Close off the call
                                Call.SIPSessions[msg.CallID].CloseCall();

                                StringBuilder file        = new StringBuilder(Directory.GetCurrentDirectory() + "//" + Call.SIPSessions[msg.CallID].SIPPacketFilePathAndName);
                                StringBuilder StoragePath = new StringBuilder(Directory.GetCurrentDirectory() + "//" + Call.SIPSessions[msg.CallID].SIPPacketFilePath);
                                pacp_to_wav(file, StoragePath);
                            }

                            if (Call.SIPSessions[msg.CallID].isEnd == true)
                            {
                                Call.SIPSessions.Remove(msg.CallID);
                            }
                        }
                    }
                    else
                    {
                        Call c = Call.GetCallByRTPPort(udpPacket.SourcePort);
                        if (c != null)
                        {
                            c.WritePacket(packet, Call.PacketType.RTP);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
        }
コード例 #8
0
            /// <summary>
            /// Cleans up any resources being used.
            /// </summary>
            public void Dispose()
            {
                lock(m_pLock){
                    if(m_IsDisposed){
                        return;
                    }
                    m_IsDisposed = true;

                    m_pDialog.m_pUasInvite2xxRetransmits.Remove(this);

                    if(m_pTimer != null){
                        m_pTimer.Dispose();
                        m_pTimer = null;
                    }
                    m_pDialog = null;
                    m_pResponse = null;
                }
            }
コード例 #9
0
            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="dialog">Owner INVITE dialog.</param>
            /// <param name="response">INVITE 2xx response.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>dialog</b> or <b>response</b> is null reference.</exception>
            public UasInvite2xxRetransmit(SIP_Dialog_Invite dialog,SIP_Response response)
            {
                if(dialog == null){
                    throw new ArgumentNullException("dialog");
                }
                if(response == null){
                    throw new ArgumentNullException("response");
                }

                m_pDialog   = dialog;
                m_pResponse = response;

                /* RFC 3261 13.3.1.4.
                    Once the response has been constructed, it is passed to the INVITE
                    server transaction.  Note, however, that the INVITE server
                    transaction will be destroyed as soon as it receives this final
                    response and passes it to the transport.  Therefore, it is necessary
                    to periodically pass the response directly to the transport until the
                    ACK arrives.  The 2xx response is passed to the transport with an
                    interval that starts at T1 seconds and doubles for each
                    retransmission until it reaches T2 seconds (T1 and T2 are defined in
                    Section 17).  Response retransmissions cease when an ACK request for
                    the response is received.  This is independent of whatever transport
                    protocols are used to send the response.

                        Since 2xx is retransmitted end-to-end, there may be hops between
                        UAS and UAC that are UDP.  To ensure reliable delivery across
                        these hops, the response is retransmitted periodically even if the
                        transport at the UAS is reliable.
                */

                m_pTimer = new TimerEx(SIP_TimerConstants.T1,false);
                m_pTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimer_Elapsed);
                m_pTimer.Enabled = true;
            }
コード例 #10
0
            /// <summary>
            /// Processes retransmited INVITE 2xx response.
            /// </summary>
            /// <param name="response">INVITE 2xx response.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference.</exception>
            public void Process(SIP_Response response)
            {
                if(response == null){
                    throw new ArgumentNullException("response");
                }

                lock(m_pLock){
                    SIP_Request ack = CreateAck();

                    try{
                        // Try existing flow.
                        m_pDialog.Flow.Send(ack);

                        // Log
                        if(m_pDialog.Stack.Logger != null){
                            byte[] ackBytes = ack.ToByteData();

                            m_pDialog.Stack.Logger.AddWrite(
                                m_pDialog.ID,
                                null,
                                ackBytes.Length,
                                "Dialog [id='" + m_pDialog.ID + "] ACK sent for 2xx response.",
                                m_pDialog.Flow.LocalEP,
                                m_pDialog.Flow.RemoteEP,
                                ackBytes
                            );
                        }
                    }
                    catch{
                        /* RFC 3261 13.2.2.4.
                            Once the ACK has been constructed, the procedures of [4] are used to
                            determine the destination address, port and transport.  However, the
                            request is passed to the transport layer directly for transmission,
                            rather than a client transaction.
                        */
                        try{
                            m_pDialog.Stack.TransportLayer.SendRequest(ack);
                        }
                        catch(Exception x){
                            // Log
                            if(m_pDialog.Stack.Logger != null){
                                m_pDialog.Stack.Logger.AddText("Dialog [id='" + m_pDialog.ID + "'] ACK send for 2xx response failed: " + x.Message + ".");
                            }
                        }
                    }
                }
            }
コード例 #11
0
            /// <summary>
            /// Checks if specified response matches this 2xx response retransmission wait entry.
            /// </summary>
            /// <param name="response">INVITE 2xx response.</param>
            /// <returns>Returns true if response matches, othwerwise false.</returns>
            /// <exception cref="ArgumentNullException">Is raised when <b>response</b> is null reference value.</exception>
            public bool Match(SIP_Response response)
            {
                if(response == null){
                    throw new ArgumentNullException("response");
                }

                if(m_pInvite.CSeq.RequestMethod == response.CSeq.RequestMethod && m_pInvite.CSeq.SequenceNumber == response.CSeq.SequenceNumber){
                    return true;
                }
                else{
                    return false;
                }
            }
コード例 #12
0
        /// <summary>
        /// Sends SIP response to caller. If proxy context is in B2BUA mode, new response is generated 
        /// as needed.
        /// </summary>
        /// <param name="transaction">Client transaction what response it is.</param>
        /// <param name="response">Response to send.</param>
        private void SendResponse(SIP_ClientTransaction transaction, SIP_Response response)
        {
            if (m_IsB2BUA)
            {
                /* draft-marjou-sipping-b2bua-00 4.1.3.
                    When the UAC side of the B2BUA receives the downstream SIP response
                    of a forwarded request, its associated UAS creates an upstream
                    response (except for 100 responses).  The creation of the Via, Max-
                    Forwards, Call-Id, CSeq, Record-Route and Contact header fields
                    follows the rules of [2].  The Record-Route header fields of the
                    downstream response are not copied in the new upstream response, as
                    Record-Route is meaningful for the downstream dialog.  The UAS SHOULD
                    copy other header fields and body from the downstream response into
                    this upstream response before sending it.
                */

                SIP_Request originalRequest = m_pServerTransaction.Request;

                // We need to use caller original request to construct response from proxied response.
                SIP_Response b2buaResponse = response.Copy();
                b2buaResponse.Via.RemoveAll();
                b2buaResponse.Via.AddToTop(originalRequest.Via.GetTopMostValue().ToStringValue());
                b2buaResponse.CallID = originalRequest.CallID;
                b2buaResponse.CSeq = originalRequest.CSeq;
                b2buaResponse.Contact.RemoveAll();
                //b2buaResponse.Contact.Add(m_pProxy.CreateContact(originalRequest.From.Address).ToStringValue());
                b2buaResponse.RecordRoute.RemoveAll();

                b2buaResponse.Allow.RemoveAll();
                b2buaResponse.Supported.RemoveAll();
                // Accept to non ACK,BYE request.
                if (originalRequest.RequestLine.Method != SIP_Methods.ACK &&
                    originalRequest.RequestLine.Method != SIP_Methods.BYE)
                {
                    b2buaResponse.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK");
                }
                // Supported to non ACK request. 
                if (originalRequest.RequestLine.Method != SIP_Methods.ACK)
                {
                    b2buaResponse.Supported.Add("100rel,timer");
                }
                // Remove Require: header.
                b2buaResponse.Require.RemoveAll();

                m_pServerTransaction.SendResponse(b2buaResponse);

                // If INVITE 2xx response do call here.
                if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE &&
                    response.StatusCodeType == SIP_StatusCodeType.Success)
                {
                    m_pProxy.B2BUA.AddCall(m_pServerTransaction.Dialog, transaction.Dialog);
                }
            }
            else
            {
                m_pServerTransaction.SendResponse(response);
            }
        }
コード例 #13
0
        /// <summary>
        /// Processes received response.
        /// </summary>
        /// <param name="handler">Target handler what received response.</param>
        /// <param name="transaction">Client transaction what response it is.</param>
        /// <param name="response">Response received.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>handler</b>,<b>transaction</b> or <b>response</b> is null reference.</exception>
        private void ProcessResponse(TargetHandler handler,
                                     SIP_ClientTransaction transaction,
                                     SIP_Response response)
        {
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            /* RFC 3261 16.7 Response Processing.
                Steps 1 - 2 handled in TargetHandler.
             
                3.  Remove the topmost Via
                4.  Add the response to the response context
                5.  Check to see if this response should be forwarded immediately
                6.  When necessary, choose the best final response from the response context.
                    If no final response has been forwarded after every client
                    transaction associated with the response context has been terminated,
                    the proxy must choose and forward the "best" response from those it
                    has seen so far.

                The following processing MUST be performed on each response that is
                forwarded.  It is likely that more than one response to each request
                will be forwarded: at least each provisional and one final response.

                7.  Aggregate authorization header field values if necessary
                8.  Optionally rewrite Record-Route header field values
                9.  Forward the response
                10. Generate any necessary CANCEL requests
            */

            bool forwardResponse = false;

            lock (m_pLock)
            {
                #region 3.  Remove the topmost Via

                /* 
                    The proxy removes the topmost Via header field value from the
                    response.
                 
                    If no Via header field values remain in the response, the
                    response was meant for this element and MUST NOT be forwarded.
                    The remainder of the processing described in this section is
                    not performed on this message, the UAC processing rules
                    described in Section 8.1.3 are followed instead (transport
                    layer processing has already occurred).

                    This will happen, for instance, when the element generates
                    CANCEL requests as described in Section 10.
                 
                    NOTE: We MAY NOT do it for B2BUA, skip it for B2BUA
                */
                if (!m_IsB2BUA)
                {
                    response.Via.RemoveTopMostValue();
                    if (response.Via.GetAllValues().Length == 0)
                    {
                        return;
                    }
                }

                #endregion

                #region 4.  Add the response to the response context

                /*
                    Final responses received are stored in the response context
                    until a final response is generated on the server transaction
                    associated with this context.  The response may be a candidate
                    for the best final response to be returned on that server
                    transaction.  Information from this response may be needed in
                    forming the best response, even if this response is not chosen.
 
                    If the proxy chooses to recurse on any contacts in a 3xx
                    response by adding them to the target set, it MUST remove them
                    from the response before adding the response to the response
                    context.  However, a proxy SHOULD NOT recurse to a non-SIPS URI
                    if the Request-URI of the original request was a SIPS URI.  If
                    the proxy recurses on all of the contacts in a 3xx response,
                    the proxy SHOULD NOT add the resulting contactless response to
                    the response context.
                  
                    Removing the contact before adding the response to the response
                    context prevents the next element upstream from retrying a
                    location this proxy has already attempted.

                    3xx responses may contain a mixture of SIP, SIPS, and non-SIP
                    URIs.  A proxy may choose to recurse on the SIP and SIPS URIs
                    and place the remainder into the response context to be
                    returned, potentially in the final response.
                */

                if (response.StatusCodeType == SIP_StatusCodeType.Redirection && !m_NoRecurse &&
                    !handler.IsRecursed)
                {
                    // Get SIP contacts and remove them from response.
                    SIP_t_ContactParam[] contacts = response.Contact.GetAllValues();
                    // Remove all contacts from response, we add non-SIP URIs back.
                    response.Contact.RemoveAll();
                    foreach (SIP_t_ContactParam contact in contacts)
                    {
                        // SIP URI add it to fork list.
                        if (contact.Address.IsSipOrSipsUri)
                        {
                            m_pTargets.Enqueue(new TargetHandler(this,
                                                                 null,
                                                                 (SIP_Uri) contact.Address.Uri,
                                                                 m_AddRecordRoute,
                                                                 true));
                        }
                            // Add specified URI back to response.
                        else
                        {
                            response.Contact.Add(contact.ToStringValue());
                        }
                    }

                    // There are remaining non-SIP contacts, so we need to add the response to reponses collection.
                    if (response.Contact.GetAllValues().Length > 0)
                    {
                        m_pResponses.Add(response);
                    }

                    // Handle forking
                    if (m_pTargets.Count > 0)
                    {
                        if (m_ForkingMode == SIP_ForkingMode.Parallel)
                        {
                            while (m_pTargets.Count > 0)
                            {
                                TargetHandler h = m_pTargets.Dequeue();
                                m_pTargetsHandlers.Add(handler);
                                h.Start();
                            }
                        }
                            // Just fork next.
                        else
                        {
                            TargetHandler h = m_pTargets.Dequeue();
                            m_pTargetsHandlers.Add(handler);
                            h.Start();
                        }

                        // Because we forked request to new target(s), we don't need to do steps 5 - 10.
                        return;
                    }
                }
                    // Not 3xx response or recursing disabled.
                else
                {
                    m_pResponses.Add(response);
                }

                #endregion

                #region 5.  Check to see if this response should be forwarded immediately

                /*
                    Until a final response has been sent on the server transaction,
                    the following responses MUST be forwarded immediately:

                    -  Any provisional response other than 100 (Trying)

                    -  Any 2xx response

                    If a 6xx response is received, it is not immediately forwarded,
                    but the stateful proxy SHOULD cancel all client pending
                    transactions as described in Section 10, and it MUST NOT create
                    any new branches in this context.
                 
                    After a final response has been sent on the server transaction,
                    the following responses MUST be forwarded immediately:

                    -  Any 2xx response to an INVITE request
                */

                if (!m_IsFinalResponseSent)
                {
                    if (response.StatusCodeType == SIP_StatusCodeType.Provisional &&
                        response.StatusCode != 100)
                    {
                        forwardResponse = true;
                    }
                    else if (response.StatusCodeType == SIP_StatusCodeType.Success)
                    {
                        forwardResponse = true;
                    }
                    else if (response.StatusCodeType == SIP_StatusCodeType.GlobalFailure)
                    {
                        CancelAllTargets();
                    }
                }
                else
                {
                    if (response.StatusCodeType == SIP_StatusCodeType.Success &&
                        m_pServerTransaction.Request.RequestLine.Method == SIP_Methods.INVITE)
                    {
                        forwardResponse = true;
                    }
                }

                #endregion

                #region x.  Handle sequential forking

                /*
                    Sequential Search: In a sequential search, a proxy server attempts
                    each contact address in sequence, proceeding to the next one
                    only after the previous has generated a final response.  A 2xx
                    or 6xx class final response always terminates a sequential
                    search.
                */
                if (m_ForkingMode == SIP_ForkingMode.Sequential &&
                    response.StatusCodeType != SIP_StatusCodeType.Provisional)
                {
                    if (response.StatusCodeType == SIP_StatusCodeType.Success)
                    {
                        // Do nothing, 2xx will be always forwarded and step 10. Cancels all targets.
                    }
                    else if (response.StatusCodeType == SIP_StatusCodeType.GlobalFailure)
                    {
                        // Do nothing, 6xx is already handled in setp 5.
                    }
                    else if (m_pTargets.Count > 0)
                    {
                        TargetHandler h = m_pTargets.Dequeue();
                        m_pTargetsHandlers.Add(handler);
                        h.Start();

                        // Skip all next steps, we will get new responses from new target.
                        return;
                    }
                }

                #endregion

                #region 6.  When necessary, choose the best final response from the response context

                /* 
                    A stateful proxy MUST send a final response to a response
                    context's server transaction if no final responses have been
                    immediately forwarded by the above rules and all client
                    transactions in this response context have been terminated.

                    The stateful proxy MUST choose the "best" final response among
                    those received and stored in the response context.

                    If there are no final responses in the context, the proxy MUST
                    send a 408 (Request Timeout) response to the server
                    transaction.

                */

                if (!m_IsFinalResponseSent && !forwardResponse && m_pTargets.Count == 0)
                {
                    bool mustChooseBestFinalResponse = true;
                    // Check if all transactions terminated.
                    foreach (TargetHandler h in m_pTargetsHandlers)
                    {
                        if (!h.IsCompleted)
                        {
                            mustChooseBestFinalResponse = false;
                            break;
                        }
                    }

                    if (mustChooseBestFinalResponse)
                    {
                        response = GetBestFinalResponse();
                        if (response == null)
                        {
                            response = Proxy.Stack.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout,
                                                                  m_pServerTransaction.Request);
                        }

                        forwardResponse = true;
                    }
                }

                #endregion

                if (forwardResponse)
                {
                    #region 7.  Aggregate authorization header field values if necessary

                    /* 
                        If the selected response is a 401 (Unauthorized) or 407 (Proxy Authentication Required), 
                        the proxy MUST collect any WWW-Authenticate and Proxy-Authenticate header field values 
                        from all other 401 (Unauthorized) and 407 (Proxy Authentication Required) responses 
                        received so far in this response context and add them to this response without 
                        modification before forwarding. The resulting 401 (Unauthorized) or 407 (Proxy
                        Authentication Required) response could have several WWW-Authenticate AND 
                        Proxy-Authenticate header field values.

                        This is necessary because any or all of the destinations the request was forwarded to 
                        may have requested credentials.  The client needs to receive all of those challenges and 
                        supply credentials for each of them when it retries the request.
                    */
                    if (response.StatusCode == 401 || response.StatusCode == 407)
                    {
                        foreach (SIP_Response resp in m_pResponses.ToArray())
                        {
                            if (response != resp && (resp.StatusCode == 401 || resp.StatusCode == 407))
                            {
                                // WWW-Authenticate
                                foreach (SIP_HeaderField hf in resp.WWWAuthenticate.HeaderFields)
                                {
                                    resp.WWWAuthenticate.Add(hf.Value);
                                }
                                // Proxy-Authenticate
                                foreach (SIP_HeaderField hf in resp.ProxyAuthenticate.HeaderFields)
                                {
                                    resp.ProxyAuthenticate.Add(hf.Value);
                                }
                            }
                        }
                    }

                    #endregion

                    #region 8.  Optionally rewrite Record-Route header field values

                    // This is optional so we currently won't do that.

                    #endregion

                    #region 9.  Forward the response

                    SendResponse(transaction, response);
                    if (response.StatusCodeType != SIP_StatusCodeType.Provisional)
                    {
                        m_IsFinalResponseSent = true;
                    }

                    #endregion

                    #region 10. Generate any necessary CANCEL requests

                    /* 
                        If the forwarded response was a final response, the proxy MUST
                        generate a CANCEL request for all pending client transactions
                        associated with this response context.
                    */
                    if (response.StatusCodeType != SIP_StatusCodeType.Provisional)
                    {
                        CancelAllTargets();
                    }

                    #endregion
                }
            }
        }