Esempio n. 1
0
 /// <summary>
 /// TODO: Documentation Constructor
 /// </summary>
 /// <param name="message"></param>
 public StunClassicRFC(StunMessage message)
 {
     this.boundMessage = message;
 }
Esempio n. 2
0
 /// <summary>查询</summary>
 /// <param name="request"></param>
 /// <param name="remoteEndPoint"></param>
 /// <returns></returns>
 public StunMessage Query(StunMessage request, IPEndPoint remoteEndPoint)
 {
     EnsureSocket();
     return Query(Socket, request, remoteEndPoint);
 }
Esempio n. 3
0
        StunMessage Query(ISocketClient client, StunMessage request, IPEndPoint remoteEndPoint)
        {
            Byte[] buffer = null;
            try
            {
                if (client.Local.IsTcp)
                {
                    // Tcp协议不支持更换IP或者端口
                    if (request.ChangeIP || request.ChangePort) return null;

                    //if (!client.Connected) client.Connect(remoteEndPoint);
                    //client.Send(request.ToArray());
                }
                //else
                //    client.SendTo(request.ToArray(), remoteEndPoint);

                WriteLog("查询 {0} =>{1}", request, remoteEndPoint);

                client.Remote.EndPoint = remoteEndPoint;
                client.Send(request.ToArray());

                buffer = client.Receive();
                if (buffer == null || buffer.Length == 0) return null;
            }
            catch { return null; }

            var rs = StunMessage.Read(new MemoryStream(buffer));
            //if (rs != null && rs.Type != StunMessageType.BindingResponse) return null;
            if (rs == null) return null;

            // 不是同一个会话不要
            if (rs.TransactionID.CompareTo(request.TransactionID) != 0) return null;
            // 不是期望的响应不要
            if (rs.Type != (StunMessageType)((UInt16)request.Type | 0x0100)) return null;
            return rs;
        }
Esempio n. 4
0
        ///// <summary>在指定服务器上执行查询</summary>
        ///// <param name="host"></param>
        ///// <param name="port"></param>
        ///// <returns></returns>
        //public StunResult QueryWithServer(String host, Int32 port = 3478)
        //{
        //    try
        //    {
        //        return QueryWithServer(NetHelper.ParseAddress(host), port);
        //    }
        //    catch { return null; }
        //}

        /// <summary>在指定服务器上执行查询</summary>
        /// <param name="address"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public StunResult QueryWithServer(IPAddress address, Int32 port)
        {
            EnsureSocket();
            var client = Socket as ISocketClient;
            var remote = new IPEndPoint(address, port);

            // Test I
            // 测试网络是否畅通
            var msg = new StunMessage();
            msg.Type = StunMessageType.BindingRequest;
            var rs = Query(client, msg, remote);

            // UDP blocked.
            if (rs == null) return new StunResult(StunNetType.Blocked, null);

            WriteLog("服务器:{0}", rs.ServerName);
            WriteLog("映射地址:{0}", rs.MappedAddress);
            WriteLog("源地址:{0}", rs.SourceAddress);
            WriteLog("新地址:{0}", rs.ChangedAddress);
            var remote2 = rs.ChangedAddress;

            // Test II
            // 要求改变IP和端口
            msg.ChangeIP = true;
            msg.ChangePort = true;
            msg.ResetTransactionID();

            // 如果本地地址就是映射地址,表示没有NAT。这里的本地地址应该有问题,永远都会是0.0.0.0
            //if (client.LocalEndPoint.Equals(test1response.MappedAddress))
            var pub = rs.MappedAddress;
            if (pub != null && client.Local.Port == pub.Port && pub.Address.IsLocal())
            {
                // 要求STUN服务器从另一个地址和端口向当前映射端口发送消息。如果收到,表明是完全开放网络;如果没收到,可能是防火墙阻止了。
                rs = Query(client, msg, remote);
                // Open Internet.
                if (rs != null) return new StunResult(StunNetType.OpenInternet, pub);

                // Symmetric UDP firewall.
                return new StunResult(StunNetType.SymmetricUdpFirewall, pub);
            }
            else
            {
                rs = Query(client, msg, remote);
                if (rs != null && pub == null) pub = rs.MappedAddress;
                // Full cone NAT.
                if (rs != null) return new StunResult(StunNetType.FullCone, pub);

                // Test II
                msg.ChangeIP = false;
                msg.ChangePort = false;
                msg.ResetTransactionID();

                // 如果是Tcp,这里需要准备第二个重用的Socket
                if (client.Local.IsTcp)
                {
                    EnsureSocket2();
                    client = Socket2 as ISocketClient;
                }

                rs = Query(client, msg, remote2);
                // 如果第二服务器没响应,重试
                if (rs == null) rs = Query(client, msg, remote2);
                if (rs != null && pub == null) pub = rs.MappedAddress;
                if (rs == null) return new StunResult(StunNetType.Blocked, pub);

                // 两次映射地址不一样,对称网络
                if (!rs.MappedAddress.Equals(pub)) return new StunResult(StunNetType.Symmetric, pub);

                // Test III
                msg.ChangeIP = false;
                msg.ChangePort = true;
                msg.ResetTransactionID();

                rs = Query(client, msg, remote2);
                if (rs != null && pub == null) pub = rs.MappedAddress;
                // 受限
                if (rs != null) return new StunResult(StunNetType.AddressRestrictedCone, pub);

                // 端口受限
                return new StunResult(StunNetType.PortRestrictedCone, pub);
            }
        }
Esempio n. 5
0
 /// <summary>获取公网地址</summary>
 /// <returns></returns>
 public IPEndPoint GetPublic()
 {
     EnsureSocket();
     //var socket = Socket.Socket;
     var msg = new StunMessage();
     msg.Type = StunMessageType.BindingRequest;
     IPEndPoint ep = null;
     foreach (var item in Servers)
     {
         try
         {
             ep = NetHelper.ParseEndPoint(item, 3478);
         }
         catch { continue; }
         //Int32 p = item.IndexOf(":");
         //if (p > 0)
         //    ep = new IPEndPoint(NetHelper.ParseAddress(item.Substring(0, p)), Int32.Parse(item.Substring(p + 1)));
         //else
         //    ep = new IPEndPoint(NetHelper.ParseAddress(item), 3478);
         var rs = Query(Socket, msg, ep);
         if (rs != null && rs.MappedAddress != null) return rs.MappedAddress;
     }
     return null;
 }
Esempio n. 6
0
 /// <summary>
 /// TODO: Documentation Constructor
 /// </summary>
 /// <param name="message"></param>
 public TurnRFCs(StunMessage message)
 {
     this.boundMessage = message;
 }
Esempio n. 7
0
 /// <summary>
 /// TODO: Documentation Constructor
 /// </summary>
 /// <param name="message"></param>
 public StunRFC(StunMessage message)
 {
     this.boundMessage = message;
 }
Esempio n. 8
0
        /// <summary>
        /// Gets NAT info from STUN server.
        /// </summary>
        /// <param name="host">STUN server name or IP.</param>
        /// <param name="port">STUN server port. Default port is 3478.</param>
        /// <param name="socket">UDP socket to use.</param>
        /// <returns>Returns UDP network info.</returns>
        /// <exception cref="Exception">Throws exception if unexpected error happens.</exception>
        public static StunResult Query(string host, int port, Socket socket)
        {
            if (host == null)
            {
                throw new ArgumentNullException(nameof(host));
            }
            if (socket == null)
            {
                throw new ArgumentNullException(nameof(socket));
            }
            if (port < 1)
            {
                throw new ArgumentException(@"Port value must be >= 1 !");
            }
            if (socket.ProtocolType != ProtocolType.Udp)
            {
                throw new ArgumentException(@"Socket must be UDP socket !");
            }

            var remoteEndPoint = new IPEndPoint(Dns.GetHostAddresses(host)[0], port);

            /*
             * In test I, the client sends a STUN Binding Request to a server, without any flags set in the
             * CHANGE-REQUEST attribute, and without the RESPONSE-ADDRESS attribute. This causes the server
             * to send the response back to the address and port that the request came from.
             *
             * In test II, the client sends a Binding Request with both the "change IP" and "change port" flags
             * from the CHANGE-REQUEST attribute set.
             *
             * In test III, the client sends a Binding Request with only the "change port" flag set.
             *
             +--------+
             |  Test  |
             |   I    |
             +--------+
             |
             |
             |               V
             |              /\              /\
             |           N /  \ Y          /  \ Y             +--------+
             | UDP     <-------/Resp\--------->/ IP \------------->|  Test  |
             | Blocked         \ ?  /          \Same/              |   II   |
             \  /            \? /               +--------+
             \/              \/                    |
             | N                  |
             |                    V
             |                               V                    /\
             +--------+  Sym.      N /  \
             |  Test  |  UDP    <---/Resp\
             |   II   |  Firewall   \ ?  /
             +--------+              \  /
             |                    \/
             |                               V                     |Y
             |    /\                         /\                    |
             | Symmetric  N  /  \       +--------+   N  /  \                   V
             | NAT  <--- / IP \<-----|  Test  |<--- /Resp\               Open
             |  \Same/      |   I    |     \ ?  /               Internet
             \? /       +--------+      \  /
             \/                         \/
             |                           |Y
             |                           |
             |                           V
             |                           Full
             |                           Cone
             |    V              /\
             +--------+        /  \ Y
             |  Test  |------>/Resp\---->Restricted
             |   III  |       \ ?  /
             +--------+        \  /
             \/
             |N
             |       Port
             +------>Restricted
             |
             */

            try
            {
                // Test I
                var test1 = new StunMessage {
                    Type = StunMessageType.BindingRequest
                };
                var test1Response = DoTransaction(test1, socket, remoteEndPoint, 1600);

                // UDP blocked.
                if (test1Response == null)
                {
                    return(new StunResult(NatType.UdpBlocked, null));
                }
                else
                {
                    // Test II
                    var test2 = new StunMessage
                    {
                        Type          = StunMessageType.BindingRequest,
                        ChangeRequest = new StunChangeRequest(true, true)
                    };

                    // No NAT.
                    if (socket.LocalEndPoint.Equals(test1Response.MappedAddress))
                    {
                        var test2Response = DoTransaction(test2, socket, remoteEndPoint, 1600);
                        // Open Internet.
                        if (test2Response != null)
                        {
                            return(new StunResult(NatType.OpenInternet, test1Response.MappedAddress));
                        }
                        // Symmetric UDP firewall.
                        else
                        {
                            return(new StunResult(NatType.SymmetricUdpFirewall, test1Response.MappedAddress));
                        }
                    }
                    // NAT
                    else
                    {
                        var test2Response = DoTransaction(test2, socket, remoteEndPoint, 1600);

                        // Full cone NAT.
                        if (test2Response != null)
                        {
                            return(new StunResult(NatType.FullCone, test1Response.MappedAddress));
                        }
                        else
                        {
                            /*
                             * If no response is received, it performs test I again, but this time, does so to
                             * the address and port from the CHANGED-ADDRESS attribute from the response to test I.
                             */

                            // Test I(II)
                            var test12 = new StunMessage {
                                Type = StunMessageType.BindingRequest
                            };

                            var test12Response = DoTransaction(test12, socket, test1Response.ChangedAddress, 1600);
                            if (test12Response == null)
                            {
                                throw new Exception(@"STUN Test I(II) didn't get response !");
                            }
                            else
                            {
                                // Symmetric NAT
                                if (!test12Response.MappedAddress.Equals(test1Response.MappedAddress))
                                {
                                    return(new StunResult(NatType.Symmetric, test1Response.MappedAddress));
                                }
                                else
                                {
                                    // Test III
                                    var test3 = new StunMessage
                                    {
                                        Type          = StunMessageType.BindingRequest,
                                        ChangeRequest = new StunChangeRequest(false, true)
                                    };

                                    var test3Response = DoTransaction(test3, socket, test1Response.ChangedAddress, 1600);
                                    // Restricted
                                    if (test3Response != null)
                                    {
                                        return(new StunResult(NatType.RestrictedCone, test1Response.MappedAddress));
                                    }
                                    // Port restricted
                                    else
                                    {
                                        return(new StunResult(NatType.PortRestrictedCone, test1Response.MappedAddress));
                                    }
                                }
                            }
                        }
                    }
                }
            }
            finally
            {
                // Junk all late responses.
                var startTime = DateTime.Now;
                while (startTime.AddMilliseconds(200) > DateTime.Now)
                {
                    // We got response.
                    if (socket.Poll(1, SelectMode.SelectRead))
                    {
                        var receiveBuffer = new byte[512];
                        socket.Receive(receiveBuffer);
                    }
                }
            }
        }
Esempio n. 9
0
        /// <summary>
        /// TODO: Documentation turnManager_OnConnectionBindSucceed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="connectedSocket"></param>
        /// <param name="receivedMsg"></param>
        private void turnManager_OnConnectionBindSucceed(object sender, Socket connectedSocket, StunMessage receivedMsg)
        {
            if (this.OnTurnConnectionBindSucceed != null)
            {
                this.OnTurnConnectionBindSucceed(this, connectedSocket, this.StartingSessionSid, this.StartingSessionRecipient);
            }

            if (this.OnConnectionTryEnded != null)
            {
                this.OnConnectionTryEnded(this, this.StartingSessionSid);
            }

            this.CancelStartingSession();
        }
Esempio n. 10
0
 /// <summary>
 /// TODO: Documentation turnManager_OnConnectionAttemptReceived
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="receivedMsg"></param>
 private void turnManager_OnConnectionAttemptReceived(object sender, StunMessage receivedMsg)
 {
     (sender as TurnManager).ConnectionBind(receivedMsg.Turn.ConnectionId, this.TurnUsername, this.TurnPassword);
 }
Esempio n. 11
0
        /// <summary>
        /// TODO: Documentation turnManager_OnAllocateFailed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="receivedMsg"></param>
        /// <param name="sentMsg"></param>
        /// <param name="transactionObject"></param>
        private void turnManager_OnAllocateFailed(object sender, StunMessage receivedMsg, StunMessage sentMsg, object transactionObject)
        {
            if (this.StartingSessionRecipient != null)
            {
                if (this.OnConnectionTryEnded != null)
                {
                    this.OnConnectionTryEnded(this, this.StartingSessionSid);
                }

                this.DestroyTurnSession(this.StartingSessionSid);

                this.CancelStartingSession();
            }
        }
Esempio n. 12
0
        /// <summary>
        /// TODO: Documentation turnManager_OnAllocateSucceed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="allocation"></param>
        /// <param name="sentMsg"></param>
        /// <param name="receivedMsg"></param>
        private void turnManager_OnAllocateSucceed(object sender, TurnAllocation allocation, StunMessage sentMsg, StunMessage receivedMsg)
        {
            if (this.StartingSessionRecipient != null)
            {
                this.turnSessions[this.StartingSessionSid].TurnAllocation = allocation;

                XmlDocument doc = new XmlDocument();

                // Jingle Transport
                JingleIce jingleIce = new JingleIce(doc)
                {
                    Pwd   = JingleUtilities.GenerateIcePwd,
                    Ufrag = JingleUtilities.GenerateIceUfrag
                };

                if (this.OnIceCandidatesGathering != null)
                {
                    this.OnIceCandidatesGathering(this, jingleIce, (sender as TurnManager).HostEP, this.turnSessions[this.StartingSessionSid].UseTurnOnly, allocation);
                }

                this.localCandidates.Add(this.StartingSessionSid, jingleIce.GetCandidates());

                JingleIQ jingleIq = null;

                // Jingle Description
                Element jingleDescription = null;
                String  contentName       = null;

                if (this.OnDescriptionGathering != null)
                {
                    this.OnDescriptionGathering(this, doc, this.StartingSessionSid, ref jingleDescription, ref contentName);
                }

                jingleIq = this.JingleManager.SessionRequest(this.StartingSessionRecipient,
                                                             this.StartingSessionAction,
                                                             this.StartingSessionSid, contentName,
                                                             jingleDescription, jingleIce);

                //this.jingleManager.FindSession(this.StartingSessionSid).Local = jingleIq.Instruction;

                this.Stream.Write(jingleIq);
            }
        }