Несанкционированная ошибка в вызове sip с использованием библиотеки lumisoft - PullRequest
0 голосов
/ 21 января 2020

Привет всем, у меня есть IP-камера, которая поддерживает SIP. То, что я хочу сделать, это позвонить в камеру, используя протокол SIP от моего c# клиента. Я использовал библиотеку стека sum lumisoft для этого, но я получаю 401 несанкционированную ошибку, когда я вызываю метод приглашения в полученном ответе. Мой код, который я пробовал до сих пор -

 using System;
 using System.IO;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Drawing;
 using System.Windows.Forms;
 using System.Net;
 using System.Net.Sockets;
 using System.Net.NetworkInformation;
 using LumiSoft.Net;     

namespace VMSServer_Interface.TwoWayAudio.Sip 
 {   
 sealed class SipSendAudio
{

    private SIP_Stack stack = null;
    private int sipPort = 5060;
    private int rtpBasePort = 21240;       
    private Dictionary<int, AudioCodec> m_pAudioCodecs = null;                                    
    private SIP_Call m_pCall = null;              
    private AudioOutDevice m_pAudioOutDevice = null;        
    private AudioInDevice m_pAudioInDevice = null;
    private IPAddress rtpIP = null; 
    public SipSendAudio(Camera camera ): base(camera)
    {

    }

    private void Call_Click(ref string error)
    {

        try
        {
            m_pAudioOutDevice = AudioOut.Devices[0];
            m_pAudioInDevice = AudioIn.Devices[0];          
            m_pAudioCodecs = new Dictionary<int, AudioCodec>();
            m_pAudioCodecs.Add(0, new PCMU());// We are using mu law encoding technique
            // for sip call we have to bind sip stack with ip address                     
            stack = new SIP_Stack();
            stack.UserAgent = "I2V";
            GetBindIP();//Get the required ip to bind with and ssame ip is used to create rtp session
            if (rtpIP == null)
            {
                throw new Exception("Unable to bind to Any IP Address");
            }              
            stack.BindInfo = new IPBindInfo[] { new IPBindInfo("", BindInfoProtocol.UDP,rtpIP , sipPort) };//IPAddress.Any
            stack.Error += new EventHandler<ExceptionEventArgs>(m_pStack_Error);// handle stack error
            stack.Start();

            SIP_t_NameAddress to = null;
            SIP_t_NameAddress from = null;

            string ip = "sip:camera@" + this.Camera.IP_LAN +":5060"; // sip:user@ip:port
            to = new SIP_t_NameAddress(ip);

            if (!to.IsSipOrSipsUri)// Checks if it is valid sip uri or not
            {
                throw new ArgumentException();
            }

            string pc_ip = "sip:client@127.0.0.1";
            from = new SIP_t_NameAddress(pc_ip);


            Call(from, to);// starts calling

        }
        catch (Exception x)
        {
            error = x.Message;
            ExceptionHandler.handleException(x);
        }

    }


    /// <summary>
    /// Is called when SIp stack has unhandled error.
    /// </summary>
    /// <param name="sender">Sender.</param>
    /// <param name="e">Event data.</param>
    private void m_pStack_Error(object sender, ExceptionEventArgs e)
    {
        ExceptionHandler.handleException(" SipSendAudio Stack Error: " + e.Exception.Message);      
            this.SendingAudio = false;//

    }

    private void Call_StateChanged(object sender, EventArgs e)
    {

        if(m_pCall.State == SIP_Call.SIP_CallState.Active)
        {
            this.SendingAudio = true;
        }
        else if (m_pCall.State == SIP_Call.SIP_CallState.Terminated)
        {
            this.SendingAudio = false;

            SDP_Message localSDP = m_pCall.LocalSDP;

            foreach (SDP_MediaDescription media in localSDP.MediaDescriptions)
            {
                if (media.Tags.ContainsKey("rtp_audio_in"))
                {
                    ((AudioIn_RTP)media.Tags["rtp_audio_in"]).Dispose();//disposing tags in media description
                }
                if (media.Tags.ContainsKey("rtp_audio_out"))
                {
                    ((AudioOut_RTP)media.Tags["rtp_audio_out"]).Dispose();
                }
            }

            if (m_pCall.RtpMultimediaSession != null)
            {

                m_pCall.RtpMultimediaSession.Dispose();//disposing rtpmultimedia session
            }

            if (m_pCall.Dialog != null && m_pCall.Dialog.IsTerminatedByRemoteParty)
            {
                //
            }
        }


        else if (m_pCall.State == SIP_Call.SIP_CallState.Disposed)
        {               
            m_pCall = null;
        }
    }

    private void Call(SIP_t_NameAddress from, SIP_t_NameAddress to)
    {
        if (from == null)
        {
            throw new ArgumentNullException("from");
        }
        if (to == null)
        {
            throw new ArgumentNullException("to");
        }

        RTP_MultimediaSession rtpMultimediaSession = new RTP_MultimediaSession(RTP_Utils.GenerateCNAME());
        RTP_Session rtpSession = CreateRtpSession(rtpMultimediaSession);// Create RTP multimedia session beteen sender and receiver

        // Port search failed.
        if (rtpSession == null)
        {
            throw new Exception();
        }

        SDP_Message sdpOffer = new SDP_Message();//creating sdp message that is sent to the remote
        sdpOffer.Version = "0";
        sdpOffer.Origin = new SDP_Origin("-", sdpOffer.GetHashCode(), 1, "IN", "IP4",rtpSession.LocalEP.IP.ToString());// rtpSession.LocalEP.IP.ToString());
        sdpOffer.SessionName = "SIP Call";
        sdpOffer.Times.Add(new SDP_Time(0, 0));

        SDP_MediaDescription mediaStream = new SDP_MediaDescription(SDP_MediaTypes.audio, 0, 1, "RTP/AVP", null);

        rtpSession.NewReceiveStream += delegate(object s, RTP_ReceiveStreamEventArgs e)//gets called whenever new stream is received from remote object
        {
            AudioOut_RTP audioOut = new AudioOut_RTP(m_pAudioOutDevice, e.Stream, m_pAudioCodecs);
            audioOut.Start();// starts playing received audio stream
            mediaStream.Tags["rtp_audio_out"] = audioOut;
        };



        foreach (KeyValuePair<int, AudioCodec> entry in m_pAudioCodecs)// adding available encoding technique
        {
            mediaStream.Attributes.Add(new SDP_Attribute("rtpmap", entry.Key + " " + entry.Value.Name + "/" + entry.Value.CompressedAudioFormat.SamplesPerSecond));
            mediaStream.MediaFormats.Add(entry.Key.ToString());
        }
        mediaStream.Attributes.Add(new SDP_Attribute("ptime", "20"));
        mediaStream.Attributes.Add(new SDP_Attribute("sendrecv", ""));
        mediaStream.Attributes.Add(new SDP_Attribute("sendonly", ""));
        mediaStream.Tags["rtp_session"] = rtpSession;
        mediaStream.Tags["audio_codecs"] = m_pAudioCodecs;
        sdpOffer.MediaDescriptions.Add(mediaStream);//adding media description to the sdp
        IPEndPoint rtpPublicEP = rtpSession.LocalEP.RtpEP;
        IPEndPoint rtcpPublicEP = rtpSession.LocalEP.RtcpEP;

        mediaStream.Port = rtpPublicEP.Port;
        mediaStream.Connection = new SDP_Connection("IN", "IP4", rtpPublicEP.Address.ToString());

        // Create INVITE request.
        SIP_Request invite = stack.CreateRequest(SIP_Methods.INVITE, to, from);
        invite.ContentType = "application/sdp";
        invite.Data = sdpOffer.ToByte();// setting sdp into invite data            

        SIP_RequestSender sender = stack.CreateRequestSender(invite);

        // Create call.
        m_pCall = new SIP_Call(stack, sender, rtpMultimediaSession);
        m_pCall.LocalSDP = sdpOffer;
        m_pCall.StateChanged += new EventHandler(Call_StateChanged);
        m_pCall.onError += new EventHandler<SipCallErrorEventArgs>(CallTerminatedOnErrorHandler);


        bool finalResponseSeen = false;
        List<SIP_Dialog_Invite> earlyDialogs = new List<SIP_Dialog_Invite>();
        sender.Credentials.Add(new NetworkCredential("root", "root1234"));
        //sender.Request.Authorization = GetCredentials();
        sender.ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs e)// gets called when we receive the invite response from the remote
        {

            // Skip 2xx retransmited response.
            if (finalResponseSeen)
            {
                return;
            }                                
            else
            {
                if(e.Response.StatusCode >= 200)
                {
                    finalResponseSeen = true;
                }

                try
                {

                    if (e.Response.StatusCodeType == SIP_StatusCodeType.Provisional)
                    {

                        if (e.Response.StatusCode > 100 && e.Response.To.Tag != null)
                        {
                            earlyDialogs.Add((SIP_Dialog_Invite)e.GetOrCreateDialog);
                        }

                        // 180_Ringing.
                        if (e.Response.StatusCode == 180)
                        {
                            // 
                        }
                    }



                    else if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
                    {
                        SIP_Dialog dialog = e.GetOrCreateDialog;

                        foreach (SIP_Dialog_Invite d in earlyDialogs.ToArray())
                        {
                            if (!d.Equals(dialog))
                            {
                                d.Terminate("Another forking leg accepted.", true);
                            }
                        }

                        m_pCall.InitCalling(dialog, sdpOffer);

                        // Remote-party provided SDP.
                        if (e.Response.ContentType != null && e.Response.ContentType.ToLower().IndexOf("application/sdp") > -1)
                        {
                            try
                            {
                                // SDP offer. We sent offerless INVITE, we need to send SDP answer in ACK request.'
                                if (e.ClientTransaction.Request.ContentType == null || e.ClientTransaction.Request.ContentType.ToLower().IndexOf("application/sdp") == -1)
                                {
                                    // Currently we never do it, so it never happens. This is place holder, if we ever support it.
                                }
                                // SDP answer to our offer.
                                else
                                {
                                    // This method takes care of ACK sending and 2xx response retransmission ACK sending.
                                    HandleAck(m_pCall.Dialog, e.ClientTransaction);

                                    //ProcessMediaAnswer(m_pCall, m_pCall.LocalSDP, SDP_Message.Parse(Encoding.UTF8.GetString(e.Response.Data)));
                                }
                            }
                            catch (Exception x)
                            {
                                m_pCall.Terminate("SDP answer parsing/processing failed.");
                                ExceptionHandler.handleException(x);

                            }
                        }
                        else
                        {
                            // If we provided SDP offer, there must be SDP answer.
                            if (e.ClientTransaction.Request.ContentType != null && e.ClientTransaction.Request.ContentType.ToLower().IndexOf("application/sdp") > -1)
                            {
                                m_pCall.Terminate("Invalid 2xx response, required SDP answer is missing.");//"Invalid 2xx response, required SDP answer is missing."
                            }
                        }

                    }
                    else
                    {

                        foreach (SIP_Dialog_Invite dialog in earlyDialogs.ToArray())
                        {
                            dialog.Terminate("All early dialogs are considered terminated upon reception of the non-2xx final response. (RFC 3261 13.2.2.3)", false);
                        }

                        if (m_pCall.State != SIP_Call.SIP_CallState.Terminating)
                        {

                        }

                        // Terminate call.
                        m_pCall.Terminate("Remote party rejected a call.", sendbye: false);
                    }


                }
                catch (Exception x)
                {
                    ExceptionHandler.handleException(x);
                }
            }               

        };

       sender.Start();
    }      

    private void CallTerminatedOnErrorHandler(object sender, SipCallErrorEventArgs e)
    {
        OnDisconnected(e.Error);
    }

    private RTP_Session CreateRtpSession(RTP_MultimediaSession rtpMultimediaSession)
    {
        if (rtpMultimediaSession == null)
        {
            throw new ArgumentNullException();
        }

        //--- Search RTP IP -------------------------------------------------------//
      // rtpIP is used to create rtp session
        //------------------------------------------------------------------------//

        // Search free ports for RTP session.
        for (int i = 0; i < 100; i += 2)
        {
            try
            {
                return rtpMultimediaSession.CreateSession(new RTP_Address(rtpIP, rtpBasePort, rtpBasePort + 1), new RTP_Clock(1, 8000));
            }
            catch
            {
                rtpBasePort += 2;
            }
        }

        return null;
    }



    private RTP_StreamMode GetRtpStreamMode(SDP_Message sdp, SDP_MediaDescription media)
    {
        if (sdp == null)
        {
            throw new ArgumentNullException("sdp");
        }
        if (media == null)
        {
            throw new ArgumentNullException("media");
        }

        // Try to get per media stream mode.
        foreach (SDP_Attribute a in media.Attributes)
        {
            if (string.Equals(a.Name, "sendrecv", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.SendReceive;
            }
            else if (string.Equals(a.Name, "sendonly", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.Send;
            }
            else if (string.Equals(a.Name, "recvonly", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.Receive;
            }
            else if (string.Equals(a.Name, "inactive", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.Inactive;
            }
        }

        // No per media stream mode, try to get per session stream mode.
        foreach (SDP_Attribute a in sdp.Attributes)
        {
            if (string.Equals(a.Name, "sendrecv", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.SendReceive;
            }
            else if (string.Equals(a.Name, "sendonly", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.Send;
            }
            else if (string.Equals(a.Name, "recvonly", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.Receive;
            }
            else if (string.Equals(a.Name, "inactive", StringComparison.InvariantCultureIgnoreCase))
            {
                return RTP_StreamMode.Inactive;
            }
        }

        return RTP_StreamMode.SendReceive;
    }


    private string GetSdpHost(SDP_Message sdp, SDP_MediaDescription mediaStream)
    {
        if (sdp == null)
        {
            throw new ArgumentNullException("sdp");
        }
        if (mediaStream == null)
        {
            throw new ArgumentNullException("mediaStream");
        }

        // We must have SDP global or per media connection info.
        string host = mediaStream.Connection != null ? mediaStream.Connection.Address : null;
        if (host == null)
        {
            host = sdp.Connection.Address != null ? sdp.Connection.Address : null;

            if (host == null)
            {
                throw new ArgumentException("Invalid SDP message, global or per media 'c'(Connection) attribute is missing.");
            }
        }

        return host;
    }


    /// <summary>
    /// Gets RTP target for SDP media stream.
    /// </summary>
    /// <param name="sdp">SDP message.</param>
    /// <param name="mediaStream">SDP media stream.</param>
    /// <returns>Return RTP target.</returns>
    /// <exception cref="ArgumentNullException">Is raised when <b>sdp</b> or <b>mediaStream</b> is null reference.</exception>
    private RTP_Address GetRtpTarget(SDP_Message sdp, SDP_MediaDescription mediaStream)
    {
        if (sdp == null)
        {
            throw new ArgumentNullException("sdp");
        }
        if (mediaStream == null)
        {
            throw new ArgumentNullException("mediaStream");
        }

        // We must have SDP global or per media connection info.
        string host = mediaStream.Connection != null ? mediaStream.Connection.Address : null;
        if (host == null)
        {
            host = sdp.Connection.Address != null ? sdp.Connection.Address : null;

            if (host == null)
            {
                throw new ArgumentException("Invalid SDP message, global or per media 'c'(Connection) attribute is missing.");
            }
        }

        int remoteRtcpPort = mediaStream.Port + 1;
        // Use specified RTCP port, if specified.
        foreach (SDP_Attribute attribute in mediaStream.Attributes)
        {
            if (string.Equals(attribute.Name, "rtcp", StringComparison.InvariantCultureIgnoreCase))
            {
                remoteRtcpPort = Convert.ToInt32(attribute.Value);

                break;
            }
        }

        return new RTP_Address(System.Net.Dns.GetHostAddresses(host)[0], mediaStream.Port, remoteRtcpPort);
    }


    /// <summary>
    /// This method takes care of ACK sending and 2xx response retransmission ACK sending.
    /// </summary>
    /// <param name="dialog">SIP dialog.</param>
    /// <param name="transaction">SIP client transaction.</param>
    private void HandleAck(SIP_Dialog dialog, SIP_ClientTransaction transaction)
    {
        if (dialog == null)
        {
            throw new ArgumentNullException("dialog");
        }
        if (transaction == null)
        {
            throw new ArgumentNullException("transaction");
        }

        SIP_t_ViaParm via = new SIP_t_ViaParm();
        via.ProtocolName = "SIP";
        via.ProtocolVersion = "2.0";
        via.ProtocolTransport = transaction.Flow.Transport;
        via.SentBy = new HostEndPoint(transaction.Flow.LocalEP);
        via.Branch = SIP_t_ViaParm.CreateBranch();
        via.RPort = 0;

        SIP_Request ackRequest = dialog.CreateRequest(SIP_Methods.ACK);
        ackRequest.Via.AddToTop(via.ToStringValue());
        ackRequest.CSeq = new SIP_t_CSeq(transaction.Request.CSeq.SequenceNumber, SIP_Methods.ACK);
        // Authorization
        foreach (SIP_HeaderField h in transaction.Request.Authorization.HeaderFields)
        {
            ackRequest.Authorization.Add(h.Value);
        }
        // Proxy-Authorization 
        foreach (SIP_HeaderField h in transaction.Request.ProxyAuthorization.HeaderFields)
        {
            ackRequest.Authorization.Add(h.Value);
        }

        // Send ACK.
        SendAck(dialog, ackRequest);

        // Start receive 2xx retransmissions.
        transaction.ResponseReceived += delegate(object sender, SIP_ResponseReceivedEventArgs e)
        {
            if (dialog.State == SIP_DialogState.Disposed || dialog.State == SIP_DialogState.Terminated)
            {
                return;
            }

            // Don't send ACK for forked 2xx, our sent BYE(to all early dialogs) or their early timer will kill these dialogs.
            // Send ACK only to our accepted dialog 2xx response retransmission.
            if (e.Response.From.Tag == ackRequest.From.Tag && e.Response.To.Tag == ackRequest.To.Tag)
            {
                SendAck(dialog, ackRequest);
            }
        };
    }

    /// <summary>
    /// Sends ACK to remote-party.
    /// </summary>
    /// <param name="dialog">SIP dialog.</param>
    /// <param name="ack">SIP ACK request.</param>
    private void SendAck(SIP_Dialog dialog, SIP_Request ack)
    {
        if (dialog == null)
        {
            throw new ArgumentNullException("dialog");
        }
        if (ack == null)
        {
            throw new ArgumentNullException("ack");
        }

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

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

                dialog.Stack.Logger.AddWrite(
                    dialog.ID,
                    null,
                    ackBytes.Length,
                    "Request [DialogID='" + dialog.ID + "';" + "method='" + ack.RequestLine.Method + "'; cseq='" + ack.CSeq.SequenceNumber + "'; " +
                    "transport='" + dialog.Flow.Transport + "'; size='" + ackBytes.Length + "'] sent '" + dialog.Flow.LocalEP + "' -> '" + dialog.Flow.RemoteEP + "'.",
                    dialog.Flow.LocalEP,
                    dialog.Flow.RemoteEP,
                    ackBytes
                );
            }
        }
        catch 
        {

            try
            {
                dialog.Stack.TransportLayer.SendRequest(ack);
            }
            catch (Exception x)
            {
                ExceptionHandler.handleException(x);
            }
        }
    }



    /// <summary>
    /// Check whether both(firstIP and secondIP) ip belong to same network or not
    /// </summary>
    /// <param name="firstIP">string firstIP.</param>
    /// <param name="subNet">string subNet.</param>
    /// <param name="secondIP">string secondIP</param>
    /// <return> true if both are from same network, otherwise false</return>
    private bool CheckWhetherInSameNetwork(string firstIP, string subNet, string secondIP)
    {
        uint subnetmaskInInt = ConvertIPToUint(subNet);//Converting subnet to unsigned int
        uint firstIPInInt = ConvertIPToUint(firstIP);// converting firstip to unsigned int
        uint secondIPInInt = ConvertIPToUint(secondIP);// converting secondip to unsigned int
        uint networkPortionofFirstIP = firstIPInInt & subnetmaskInInt;//network portion of firstip by performing AND operation between firstip and subnet
        uint networkPortionofSecondIP = secondIPInInt & subnetmaskInInt;
        if (networkPortionofFirstIP == networkPortionofSecondIP)
            return true;
        else
            return false;
    }


    /// <summary>
    /// Convert ip address to unsigned integer
    /// </summary>
    /// <param name="ipAddress">string ipaddress</param>
    /// <returns> ip address in unsigned form</returns>
    private uint ConvertIPToUint(string ipAddress)
    {
        System.Net.IPAddress iPAddress = System.Net.IPAddress.Parse(ipAddress);// striing to ip address
        byte[] byteIP = iPAddress.GetAddressBytes();//ip address to byte
        uint ipInUint = (uint)byteIP[3] << 24;
        ipInUint += (uint)byteIP[2] << 16;
        ipInUint += (uint)byteIP[1] << 8;
        ipInUint += (uint)byteIP[0];
        return ipInUint;
    }

    /// <summary>
    /// Get subnet mask of the given ip
    /// </summary>
    /// <param name="address"> IPAddress address</param>
    /// <returns> subnetmask</returns>
    /// <exception> if unable to find subnet mask of the given ip</exception>
    public static IPAddress GetSubnetMask(IPAddress address)
    {
        foreach (NetworkInterface adapter in NetworkInterface.GetAllNetworkInterfaces())
        {
            foreach (UnicastIPAddressInformation unicastIPAddressInformation in adapter.GetIPProperties().UnicastAddresses)
            {
                if (unicastIPAddressInformation.Address.AddressFamily == AddressFamily.InterNetwork)
                {
                    if (address.Equals(unicastIPAddressInformation.Address))
                    {
                        return unicastIPAddressInformation.IPv4Mask;
                    }
                }
            }
        }
        return IPAddress.Parse("255.255.255.0");
    }


    /// <summary>
    /// Get the IP to bind and create rtp session 
    /// </summary>
    private  void GetBindIP()
    {
        string snet = null;
        //IPAddress rtpIP = null;
        string cameraIP =Camera.IP_LAN.ToString();
        foreach (IPAddress ip in Dns.GetHostAddresses(""))
        {
            if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
            {
                string second = ip.ToString();//gets ip of computer
                snet = GetSubnetMask(ip).ToString();//gets subnet of above ip
                bool result = CheckWhetherInSameNetwork(cameraIP, snet, second);//check whether this is the common network or not
                if (result)
                {
                    rtpIP = ip;
                    break;
                }

            }
        }
    }
 }

}

Это приложение winforms, которое имеет ссылку https://github.com/pruiz/LumiSoft.Net dll. Пожалуйста, перейдите к методу Call в файле CS в методе call, когда приглашение отправлено, и я получил некоторый ответ в событии ResponseReceived. Обратный вызов 401 не авторизован. Пожалуйста, скажите мне, как я могу решить неавторизованную ошибку или явно указать авторизацию в приглашении.

Также, если это хорошая реализация SIP с открытым исходным кодом, которая работает нормально, пожалуйста, дайте мне знаю. Я также читал этот вопрос о stackoverflow sip, совершающем вызов - 401 Unauthorized , но они не объясняют решение в деталях.

Моя трассировка пакетов Wireshar - enter image description here

Пожалуйста, помогите мне, я застрял на SIP несанкционированной проблеме. Если какие-либо подробности относительно моего SIP звонка, дайте мне знать, я отредактирую вопрос. Thankyou!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...