Ejemplo n.º 1
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 netwrok info.</returns>
        /// <exception cref="Exception">Throws exception if unexpected error happens.</exception>
        internal static StunResult Query(string host, int port, Socket socket)
        {
            if (host == null)
            {
                throw new ArgumentNullException("host");
            }
            if (socket == null)
            {
                throw new ArgumentNullException("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 !");
            }

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

            socket.ReceiveTimeout = 3000;
            socket.SendTimeout    = 3000;

            /*
             *  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
             |
             */

            // Test I
            StunMessage test1 = new StunMessage();

            test1.Type = StunMessageType.BindingRequest;
            StunMessage test1response = DoTransaction(test1, socket, remoteEndPoint);

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

                // No NAT.
                if (socket.LocalEndPoint.Equals(test1response.MappedAddress))
                {
                    StunMessage test2Response = DoTransaction(test2, socket, remoteEndPoint);
                    // Open Internet.
                    if (test2Response != null)
                    {
                        return(new StunResult(StunNetType.OpenInternet, test1response.MappedAddress));
                    }
                    // Symmetric UDP firewall.
                    else
                    {
                        return(new StunResult(StunNetType.SymmetricUdpFirewall, test1response.MappedAddress));
                    }
                }
                // NAT
                else
                {
                    StunMessage test2Response = DoTransaction(test2, socket, remoteEndPoint);
                    // Full cone NAT.
                    if (test2Response != null)
                    {
                        return(new StunResult(StunNetType.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)
                        StunMessage test12 = new StunMessage();
                        test12.Type = StunMessageType.BindingRequest;

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

                                StunMessage test3Response = DoTransaction(test3, socket, test1response.ChangedAddress);
                                // Restricted
                                if (test3Response != null)
                                {
                                    return(new StunResult(StunNetType.RestrictedCone, test1response.MappedAddress));
                                }
                                // Port restricted
                                else
                                {
                                    return(new StunResult(StunNetType.PortRestrictedCone, test1response.MappedAddress));
                                }
                            }
                        }
                    }
                }
            }
        }