File: net\System\Net\SocketPermission.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="SocketPermission.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Net {
 
    using System.Collections;
    using System.Security;
    using System.Security.Permissions;
    using System.Globalization;
    using System.Threading;
 
    //NOTE: While SocketPermissionAttribute resides in System.DLL,
    //      no classes from that DLL are able to make declarative usage of SocketPermission.
 
 
    // THE syntax of this attribute is as followed
    // [SocketPermsion(SecurityAction.Assert, Access=Connect, Host=hostname, Transport=Tcp/Udp/All, port=portN/All)]
    // [SocketPermsion(SecurityAction.Assert, Access=Accept, Host=localname, Transport=Tcp/Udp/All, port=portN/All)]
    //
    // WHERE:
    //=======
    // - hostname is either a DNS hostname OR an IP address 1.2.3.4 or an IP wildcard 1.2.*.*
    // - protocol is either Tcp, Udp or All
    // - port is a numeric value or -1 that means "All Ports"
    //
    //  All the properites Host, Protocol and Port must be specified.
    // "localIP" means that you put here a valid address or DNS name or the localhost
    //
    //  NetworkAccess specifies the scope of permission, i.e. for connecting to remote peer,
    //  or for accepting data on the local resources.
    //
 
    [   AttributeUsage( AttributeTargets.Method | AttributeTargets.Constructor |
                        AttributeTargets.Class  | AttributeTargets.Struct      |
                        AttributeTargets.Assembly,
                        AllowMultiple = true, Inherited = false )]
 
    [Serializable]
    public sealed class SocketPermissionAttribute: CodeAccessSecurityAttribute
    {
        private string  m_access = null;
        private string  m_host   = null;
        private string  m_port   = null;
        private string  m_transport  = null;
 
        private const string strAccess     = "Access";
        private const string strConnect    = "Connect";
        private const string strAccept     = "Accept";
        private const string strHost       = "Host";
        private const string strTransport  = "Transport";
        private const string strPort       = "Port";
 
        public SocketPermissionAttribute( SecurityAction action ): base( action )
        {
        }
 
        public string Access {
            get { return m_access; }
            set {
                if (m_access != null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strAccess, value), "value");
                }
                m_access = value;
            }
        }
 
        public string Host {
            get { return m_host; }
            set {
                if (m_host != null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strHost, value), "value");
                }
                m_host = value;
            }
        }
 
        public string Transport {
            get { return m_transport;}
            set {
                if (m_transport != null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strTransport, value), "value");
                }
                m_transport = value;
            }
        }
 
        public string Port {
            get { return m_port;}
            set {
                if (m_port != null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strPort, value), "value");
                }
                m_port = value;
            }
        }
 
        public override IPermission CreatePermission()
        {
            SocketPermission perm = null;
            if (Unrestricted) {
                perm = new SocketPermission( PermissionState.Unrestricted);
            }
            else {
                perm = new SocketPermission(PermissionState.None);
                if (m_access == null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strAccess));
                }
                if (m_host == null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strHost));
                }
                if (m_transport == null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strTransport));
                }
                if (m_port == null) {
                    throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strPort));
                }
                ParseAddPermissions(perm);
            }
            return perm;
        }
 
 
        private void ParseAddPermissions(SocketPermission perm) {
 
            NetworkAccess access;
            if (0 == string.Compare(m_access, strConnect, StringComparison.OrdinalIgnoreCase )) {
                access = NetworkAccess.Connect;
            }
            else
            if (0 == string.Compare(m_access, strAccept, StringComparison.OrdinalIgnoreCase )) {
                access = NetworkAccess.Accept;
            }
            else {
                throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, strAccess, m_access));
            }
 
            TransportType transport;
            try {
                transport = (TransportType) Enum.Parse(typeof(TransportType), m_transport, true);
            }
            catch (Exception e) {
                if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {                                       
		            throw;
	            }
                throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, strTransport, m_transport), e);
            }
 
            int port;
            if (string.Compare(m_port, "All", StringComparison.OrdinalIgnoreCase ) == 0) {
                m_port = "-1";
            }
            try {
                port = Int32.Parse(m_port, NumberFormatInfo.InvariantInfo);
            }
            catch (Exception e) {
                if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {                                       
		            throw;
	            }
                throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, strPort, m_port), e);
            }
 
            if (!ValidationHelper.ValidateTcpPort(port) && port != SocketPermission.AllPorts) {
                throw new ArgumentOutOfRangeException("port", port, SR.GetString(SR.net_perm_invalid_val, strPort, m_port));
            }
            perm.AddPermission(access, transport, m_host, port);
        }
 
    }
 
 
    /// <devdoc>
    ///    <para>
    ///       Controls rights to make or accept connections on a transport address.
    ///    </para>
    /// </devdoc>
    [Serializable]
    public sealed class SocketPermission : CodeAccessPermission, IUnrestrictedPermission {
 
        private ArrayList m_connectList;
        private ArrayList m_acceptList;
        private bool m_noRestriction;
 
 
        /// <devdoc>
        ///    <para>
        ///       Returns the enumeration of permissions to connect a remote peer.
        ///    </para>
        /// </devdoc>
        public IEnumerator ConnectList    {get {return m_connectList.GetEnumerator();}}
 
        /// <devdoc>
        ///    <para>
        ///       Returns the enumeration of permissions to accept incoming connections.
        ///    </para>
        /// </devdoc>
        public IEnumerator AcceptList     {get {return m_acceptList.GetEnumerator();}}
 
 
        /// <devdoc>
        ///    <para>
        ///       Defines a constant representing all ports.
        ///    </para>
        /// </devdoc>
        public const int AllPorts = unchecked((int)0xFFFFFFFF);
 
        //<
        internal const int AnyPort = unchecked((int)0);
 
        /// <devdoc>
        ///    <para>
        ///       Creates a new instance of the <see cref='System.Net.SocketPermission'/>
        ///       class that passes all demands
        ///       or that fails all demands.
        ///    </para>
        /// </devdoc>
        public SocketPermission(PermissionState state) {
            initialize();
            m_noRestriction = (state == PermissionState.Unrestricted);
        }
 
        internal SocketPermission(bool free) {
            initialize();
            m_noRestriction = free;
        }
 
 
        /// <devdoc>
        ///    <para>
        ///       Creates a new instance of the SocketPermissions class for the given transport address with the specified permission.
        ///    </para>
        /// </devdoc>
        public SocketPermission(NetworkAccess access, TransportType transport, String hostName, int portNumber) {
            initialize();
            m_noRestriction = false;
            AddPermission(access, transport, hostName, portNumber);
        }
 
        /// <devdoc>
        ///    <para>
        ///       Adds a permission to the set of permissions for a transport address.
        ///    </para>
        /// </devdoc>
        public void AddPermission(NetworkAccess access, TransportType transport, string hostName, int portNumber) {
            if (hostName == null) {
                throw new ArgumentNullException("hostName");
            }
 
            EndpointPermission endPoint = new EndpointPermission(hostName, portNumber, transport);
 
            AddPermission(access, endPoint);
        }
 
        internal void AddPermission(NetworkAccess access, EndpointPermission endPoint) {
            if (m_noRestriction) {    // Is the permission unrestricted?
                return;             // YES-- then additional endpoints have no effect
            }
            if ((access & NetworkAccess.Connect) != 0)
                    m_connectList.Add(endPoint);
            if ((access & NetworkAccess.Accept) != 0)
                    m_acceptList.Add(endPoint);
        }
 
        // IUnrestrictedPermission interface methods
        /// <devdoc>
        ///    <para>
        ///       Checks the overall permission state of the object.
        ///    </para>
        /// </devdoc>
        public bool IsUnrestricted() {
            return m_noRestriction;
        }
 
        // IPermission interface methods
        /// <devdoc>
        ///    <para>
        ///       Creates
        ///       a copy of a <see cref='System.Net.SocketPermission'/> instance.
        ///    </para>
        /// </devdoc>
        public override IPermission Copy() {
 
            SocketPermission sp = new SocketPermission(m_noRestriction);
 
            sp.m_connectList = (ArrayList)m_connectList.Clone();
            sp.m_acceptList = (ArrayList)m_acceptList.Clone();
                        return sp;
        }
 
        private bool FindSubset(ArrayList source, ArrayList target) {
            foreach (EndpointPermission e in source) {
 
                bool found = false;
 
                foreach (EndpointPermission ee in target) {
                    if (e.SubsetMatch(ee)) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    return false;
                }
            }
            return true;
        }
 
        /// <devdoc>
        /// <para>Returns the logical union between two <see cref='System.Net.SocketPermission'/> instances.</para>
        /// </devdoc>
        public override IPermission Union(IPermission target) {
            // Pattern suggested by Security engine
            if (target==null) {
                return this.Copy();
            }
            SocketPermission other = target as SocketPermission;
            if(other == null) {
                throw new ArgumentException(SR.GetString(SR.net_perm_target), "target");
            }
            if (m_noRestriction || other.m_noRestriction) {
                return new SocketPermission(true);
            }
            SocketPermission result = (SocketPermission)other.Copy();
 
            for (int i = 0; i < m_connectList.Count; i++) {
                result.AddPermission(NetworkAccess.Connect, (EndpointPermission)m_connectList[i]);
            }
            for (int i = 0; i < m_acceptList.Count; i++) {
                result.AddPermission(NetworkAccess.Accept, (EndpointPermission)m_acceptList[i]);
            }
            return result;
        }
 
        /// <devdoc>
        ///    <para>
        ///       Returns the logical intersection between two <see cref='System.Net.SocketPermission'/> instances.
        ///    </para>
        /// </devdoc>
        public override IPermission Intersect(IPermission target) {
            // Pattern suggested by Security engine
            if (target == null) {
                return null;
            }
 
            SocketPermission other = target as SocketPermission;
            if(other == null) {
                throw new ArgumentException(SR.GetString(SR.net_perm_target), "target");
            }
 
            SocketPermission result;
            if (m_noRestriction) {
                result = (SocketPermission)(other.Copy());
            }
            else if (other.m_noRestriction) {
                result = (SocketPermission)(this.Copy());
            }
            else {
                result = new SocketPermission(false);
                intersectLists(m_connectList, other.m_connectList, result.m_connectList);
                intersectLists(m_acceptList, other.m_acceptList, result.m_acceptList);
            }
 
            // return null if resulting permission is restricted and empty
            if (!result.m_noRestriction &&
                result.m_connectList.Count == 0 && result.m_acceptList.Count == 0) {
                return null;
            }
            return result;
        }
 
        /// <devdoc>
        /// <para>Compares two <see cref='System.Net.SocketPermission'/> instances.</para>
        /// </devdoc>
        public override bool IsSubsetOf(IPermission target) {
            // Pattern suggested by security engine
            if (target == null) {
                return (m_noRestriction == false && m_connectList.Count == 0 && m_acceptList.Count == 0);
            }
 
            SocketPermission other = target as SocketPermission;
            if (other == null) {
                throw new ArgumentException(SR.GetString(SR.net_perm_target), "target");
            }
 
            if (other.IsUnrestricted()) {
                return true;
            } else if (this.IsUnrestricted()) {
                return false;
            } else if (this.m_acceptList.Count + this.m_connectList.Count ==0) {
                return true;
            } else if (other.m_acceptList.Count + other.m_connectList.Count ==0) {
                return false;
            }
 
            bool result = false;
            try {
                if (FindSubset(m_connectList, other.m_connectList) &&
                    FindSubset(m_acceptList, other.m_acceptList)) {
                    result = true;
                }
            }
            finally {
                //  This is around a back door into DNS
                //  Security engine will call isSubsetOf and probably have
                //  DNS permission asserted. We call DNS resolve.
                //  Before return do cleanup of DNS results.
 
                //  Only "this" needs cleanup, the policy object is not available for
                //  an application to look at.
                this.CleanupDNS();
            }
 
            return result;
        }
 
        //
        //This is to cleanup DNS resolution results
        //
        private void CleanupDNS() {
            foreach(EndpointPermission e in m_connectList) {
                //DNS hostnames never produce 'cached=true'
                if (e.cached) {
                    continue;
                }
                e.address = null;
            }
 
            foreach(EndpointPermission e in m_acceptList) {
                //DNS hostnames never produce 'cached=true'
                if (e.cached) {
                    continue;
                }
                e.address = null;
            }
        }
 
        /// <devdoc>
        /// </devdoc>
        public override void FromXml(SecurityElement securityElement) {
            if (securityElement == null) {
 
                //
                // null SecurityElement
                //
 
                throw new ArgumentNullException("securityElement");
            }
            if (!securityElement.Tag.Equals("IPermission")) {
 
                //
                // SecurityElement must be a permission element
                //
 
                throw new ArgumentException(SR.GetString(SR.net_not_ipermission), "securityElement");
            }
 
            string className = securityElement.Attribute("class");
 
            if (className == null) {
 
                //
                // SecurityElement must be a permission element for this type
                //
 
                throw new ArgumentException(SR.GetString(SR.net_no_classname), "securityElement");
            }
            if (className.IndexOf(this.GetType().FullName) < 0) {
 
                //
                // SecurityElement must be a permission element for this type
                //
 
                throw new ArgumentException(SR.GetString(SR.net_no_typename), "securityElement");
            }
 
            //
            // Start recovering the state from XML encoding
            //
 
            initialize();
 
 
            String str = securityElement.Attribute("Unrestricted");
 
            if (str != null) {
                m_noRestriction = (0 == string.Compare( str, "true", StringComparison.OrdinalIgnoreCase ));
                if(m_noRestriction)
                    return;
            }
 
            m_noRestriction = false;
            m_connectList = new ArrayList();
            m_acceptList = new ArrayList();
 
            SecurityElement et = securityElement.SearchForChildByTag("ConnectAccess");
            if (et != null) {
                ParseAddXmlElement(et, m_connectList, "ConnectAccess, ");
            }
            et = securityElement.SearchForChildByTag("AcceptAccess");
            if (et != null) {
                ParseAddXmlElement(et, m_acceptList, "AcceptAccess, ");
            }
        }
 
        private static void ParseAddXmlElement(SecurityElement et, ArrayList listToAdd, string accessStr) {
 
            foreach(SecurityElement uriElem in et.Children) {
                if (uriElem.Tag.Equals("ENDPOINT")) {
                    Hashtable attributes = uriElem.Attributes;
                    string tmpStr;
 
                    try {
                        tmpStr = attributes["host"] as string;
                    }
                    catch{
                        tmpStr = null;
                    }
 
                    if (tmpStr == null) {
                        throw new ArgumentNullException(accessStr + "host");
                    }
                    string host = tmpStr;
 
                    try {
                        tmpStr = attributes["transport"] as string;
                    }
                    catch{
                        tmpStr = null;
                    }
                    if (tmpStr == null) {
                        throw new ArgumentNullException(accessStr + "transport");
                    }
                    TransportType transport;
                    try {
                        transport = (TransportType) Enum.Parse(typeof(TransportType), tmpStr, true);
                    }
                    catch (Exception exception) {
                        if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
		                    throw;
	                    }
                        throw new ArgumentException(accessStr + "transport", exception);
                    }
 
                    try {
                        tmpStr = attributes["port"] as string;
                    }
                    catch{
                        tmpStr = null;
                    }
                    if (tmpStr == null) {
                        throw new  ArgumentNullException(accessStr + "port");
                    }
                    if (string.Compare(tmpStr, "All", StringComparison.OrdinalIgnoreCase ) == 0) {
                        tmpStr = "-1";
                    }
                    int port;
                    try {
                        port = Int32.Parse(tmpStr, NumberFormatInfo.InvariantInfo);
                    }
                    catch (Exception exception) {
                        if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
		                    throw;
	                    }
                        throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, accessStr + "port", tmpStr), exception);
                    }
 
                    if (!ValidationHelper.ValidateTcpPort(port) && port != SocketPermission.AllPorts) {
                        throw new ArgumentOutOfRangeException("port", port, SR.GetString(SR.net_perm_invalid_val, accessStr + "port", tmpStr));
                    }
 
 
                    listToAdd.Add(new EndpointPermission(host, port , transport));
                }
                else {
                    // improper tag found, just ignore
                }
            }
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public override SecurityElement ToXml() {
 
            SecurityElement securityElement = new SecurityElement( "IPermission" );
 
            securityElement.AddAttribute("class", this.GetType().FullName + ", " + this.GetType().Module.Assembly.FullName.Replace( '\"', '\'' ));
            securityElement.AddAttribute("version", "1");
 
            if (!IsUnrestricted()) {
                if (m_connectList.Count > 0) {
 
                    SecurityElement permList = new SecurityElement("ConnectAccess");
                    foreach(EndpointPermission permission in m_connectList) {
                        SecurityElement endpoint = new SecurityElement("ENDPOINT");
                        endpoint.AddAttribute("host", permission.Hostname);
                        endpoint.AddAttribute("transport", permission.Transport.ToString());
                        endpoint.AddAttribute("port",   permission.Port != AllPorts?
                                                        permission.Port.ToString(NumberFormatInfo.InvariantInfo): "All");
                        permList.AddChild(endpoint);
                    }
                    securityElement.AddChild(permList);
                }
 
                if (m_acceptList.Count > 0) {
 
                    SecurityElement permList = new SecurityElement("AcceptAccess");
                    foreach(EndpointPermission permission in m_acceptList) {
                        SecurityElement endpoint = new SecurityElement("ENDPOINT");
                        endpoint.AddAttribute("host", permission.Hostname);
                        endpoint.AddAttribute("transport", permission.Transport.ToString());
                        endpoint.AddAttribute("port",   permission.Port != AllPorts?
                                                        permission.Port.ToString(NumberFormatInfo.InvariantInfo): "All");
                        permList.AddChild(endpoint);
                    }
                    securityElement.AddChild(permList);
                }
            }
            else {
                securityElement.AddAttribute("Unrestricted", "true");
            }
            return securityElement;
        }
 
        private void initialize() {
            m_noRestriction = false;
            m_connectList = new ArrayList();
            m_acceptList = new ArrayList();
        }
 
        private static void intersectLists(ArrayList A, ArrayList B, ArrayList result) {
            // The optimization is done according to the following truth
            // (A|B|C) intersect (B|C|E|D)) == B|C|(A inter E)|(A inter D)
            //
            // We also check on any duplicates in the result
 
 
            bool[] aDone=new bool[A.Count];            //used to avoid duplicates in result
            bool[] bDone=new bool[B.Count];
            int ia=0;
            int ib=0;
            // Round 1st
            // Getting rid of same permissons in the input arrays (assuming X /\ X = X)
            foreach (EndpointPermission a in  A) {
                ib = 0;
                foreach (EndpointPermission b in  B) {
                    // check to see if b is in the result already
                    if (!bDone[ib]) {
                        //if both elements are the same, copy it into result
                        if (a.Equals(b)) {
                            result.Add(a);
                            aDone[ia]=bDone[ib]=true;
                            //since permissions are ORed we can break and go to the next A
                            break;
                        }
                    }
                    ++ib;
                } //foreach b in B
                ++ia;
            } //foreach a in A
 
            ia = 0;
            // Round second
            // Grab only intersections of objects not found in both A and B
            foreach (EndpointPermission a in  A) {
 
                if (!aDone[ia]) {
                    ib = 0;
                    foreach(EndpointPermission b in B) {
                        if (!bDone[ib]) {
                            EndpointPermission intesection = a.Intersect(b);
                            if (intesection != null) {
                                bool found = false;
                                // check to see if we already have the same result
                                foreach (EndpointPermission  res in result) {
                                    if (res.Equals(intesection)) {
                                        found = true;
                                        break;
                                    }
                                }
                                if (!found) {
                                    result.Add(intesection);
                                }
                            }
                        } //!Done[ib]
                        ++ib;
                    } //foreach b in B
                } //!Done[ia]
                ++ia;
            } //foreach a in A
        }
 
    }// class SocketPermission
 
 
    /// <devdoc>
    ///       Represents an element of SocketPermission object contents.
    /// </devdoc>
    [Serializable]
    public class EndpointPermission {
 
        //
        // <
 
 
 
 
 
        internal String hostname;
        internal int port;
        internal TransportType transport;
        internal bool wildcard;
        internal IPAddress[] address;
        internal bool cached = false;
 
        private static char[] DotSeparator = new char[] {'.'};
        private const String encSeperator = "#";
 
        /// <devdoc>
        ///    <para>
        ///       Returns the hostname part of EndpointPermission object
        ///    </para>
        /// </devdoc>
        public String           Hostname        { get {return hostname;}}
 
        /// <devdoc>
        ///    <para>
        ///       Returns the transport of EndpointPermission object
        ///    </para>
        /// </devdoc>
        public TransportType    Transport       { get {return transport;}}
 
        /// <devdoc>
        ///    <para>
        ///       Returns the Port part of EndpointPermission object
        ///    </para>
        /// </devdoc>
        public int              Port            { get {return port;}}
        //
        // <
 
 
 
 
 
        internal EndpointPermission(String epname, int port, TransportType trtype) {
 
            if (CheckEndPointName(epname) == EndPointType.Invalid) {
                throw new ArgumentException(SR.GetString(SR.net_perm_epname, epname), "epname");
            }
            if (!ValidationHelper.ValidateTcpPort(port) && port != SocketPermission.AllPorts) {
                throw new ArgumentOutOfRangeException("port", SR.GetString(SR.net_perm_invalid_val, "Port", port.ToString(NumberFormatInfo.InvariantInfo)));
            }
 
            hostname = epname;
            this.port = port;
            transport = trtype;
            wildcard = false;
        }
 
        //
        // This is ONLY a syntatic check on equality, hostnames are compared as strings!
        //
        public override bool Equals(object obj) {
 
            EndpointPermission ep = (EndpointPermission)obj;
 
            if (String.Compare(hostname, ep.hostname, StringComparison.OrdinalIgnoreCase ) != 0) {
                return false;
            }
            if (port != ep.port) {
                return false;
            }
            if (transport != ep.transport) {
                return false;
            }
            return true;
        }
 
        public override int GetHashCode() {
            return ToString().GetHashCode();
        }
 
        //
        // <
 
 
        internal bool IsDns {
            get {
                if (IsValidWildcard) {
                    return false;
                }
                return CheckEndPointName(hostname) == EndPointType.DnsOrWildcard;
            }
        }
 
 
        //
        // In this version wildcards are only allowed over IP ranges
        // not DNS names. For example "*.microsoft.com" is not allowed
        // A valid wildcard will have exactly three periods
        //IPv6 wildcards are NOT supported
        //
        private bool IsValidWildcard {
            get {
 
                int len = hostname.Length;
 
                //
                // Check minimum length
                //
 
                if (len < 3) {
                    return false;
                }
 
                //
                // First and last characters cannot be periods
                //
 
                if ((hostname[0] == '.') || (hostname[len - 1] == '.')) {
                    return false;
                }
 
                int dotCount = 0;
                int anyCount = 0;
 
                for (int i = 0; i < hostname.Length; i++) {
                    if (hostname[i] == '.') {
                        dotCount++;
                    }
                    else if (hostname[i] == '*') {
                        ++anyCount;
                    }
                    else if (!Char.IsDigit(hostname[i])) {  // Not a digit?
                        return false;                       // Reject wildcard
                    }
                }
                return (dotCount == 3) && (anyCount > 0);
            }
        }
 
        internal bool MatchAddress(EndpointPermission e) {
 
            // For Asp.Net config we made it valid empty string in a hostname,
            // but it will match to nothing.
            if(this.Hostname.Length == 0 || e.Hostname.Length == 0) {
                return false;
            }
 
            //
            // This is a fix for INADDR_ANY in Bind()
            // if this.Hostname == "0.0.0.0" then it matches only to e.Hostname="*.*.*.*"
            //
            // The reason is to not pass "0.0.0.0" into Resolve()
            if(this.Hostname.Equals("0.0.0.0"))
            {
                if(e.Hostname.Equals("*.*.*.*") || e.Hostname.Equals("0.0.0.0"))
                    return true;
                return false;
            }
 
            if (IsDns && e.IsDns) {
 
                //
                // <
 
 
 
                return (String.Compare(hostname, e.hostname, StringComparison.OrdinalIgnoreCase ) == 0);
            }
            Resolve();
            e.Resolve();
 
            //
            // if Resolve() didn't work for some reason then we're out of luck
            //
 
            if (((address == null) && !wildcard) || ((e.address == null) && !e.wildcard)) {
                return false;
            }
 
            //
            // try matching IP addresses against other wildcard address(es) or
            // wildcard
            //
 
            if (this.wildcard && !e.wildcard) {
                return false;                           // as a wildcard I cannot be subset of a host.
 
            }
            else if (e.wildcard) {
                if (this.wildcard) {
                    // check against my _wildcard_
                    if (MatchWildcard(e.hostname)) {
                        return true;
                    }
                }
                else {
                    // check against my _addresses_
                    for (int i = 0; i < address.Length; ++i) {
                        if (e.MatchWildcard(address[i].ToString())) {
                            return true;
                        }
                    }
                }
            } else {
                //both are _not_ wildcards
                for (int i = 0; i < address.Length; ++i) {
                    for (int j = 0; j < e.address.Length; ++j) {
                        if (address[i].Equals(e.address[j])) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }
 
        internal bool MatchWildcard(string str) {
 
            string [] wcPieces = hostname.Split(DotSeparator);
            string [] strPieces = str.Split(DotSeparator);
 
            if ((strPieces.Length != 4) || (wcPieces.Length != 4)) {
                return false;
            }
            for (int i = 0; i < 4; i++) {
                if ((strPieces[i] != wcPieces[i]) && (wcPieces[i] != "*")) {
                    return false;
                }
            }
            return true;
        }
 
        internal void Resolve() {
 
            //
            // if we already resolved this name then don't do it again
            //
 
            if (cached) {
                return;
            }
 
            //
            // IP wildcards are not resolved
            //
 
            if (wildcard) {
                return;
            }
 
            //
            // IP addresses with wildcards are allowed in permissions
            //
 
            if (IsValidWildcard) {
                wildcard = true;
                cached = true;
                return;
            }
 
            //
            // Check if the permission was specified as numeric IP.
            //
            IPAddress ipaddr;
            if (IPAddress.TryParse(hostname, out ipaddr))
            {
                address = new IPAddress[1];
                address[0] = ipaddr;
                cached = true;
                return;
            }
 
            //
            // Not numeric: use GetHostByName to determine addresses
            //
            try {
                IPHostEntry ipHostEntry;
                if (Dns.TryInternalResolve(hostname, out ipHostEntry)) {
                    address = ipHostEntry.AddressList;
                }
 
                // NB: It never caches DNS responses
                //
 
            }
            catch (SecurityException) {
                throw;
            }
            catch {
                // ignore second exception
            }
        }
 
        internal bool SubsetMatch(EndpointPermission e) {
            return ((transport == e.transport) || (e.transport == TransportType.All))
                    && ((port == e.port) || (e.port == SocketPermission.AllPorts) || port == SocketPermission.AnyPort)
                    && MatchAddress(e);
        }
 
        public override String ToString() {
            return hostname + encSeperator + port + encSeperator + ((int)transport).ToString(NumberFormatInfo.InvariantInfo);
        }
 
        internal EndpointPermission Intersect(EndpointPermission E) {
 
            String commonName=null;
            TransportType commonTransport;
            int commonPort;
 
            //
            // Look at the transport
            //
 
            if (transport == E.transport) {           // same transport
                commonTransport = transport;
            }
            // NO: check if one of the permissions authorize all transports
            else if (transport == TransportType.All) {
                commonTransport = E.transport;
            }
            else if (E.transport == TransportType.All) {
                commonTransport = transport;
            }
            else {   // transport dont match-- intersection is empty
                return null;
            }
 
            //
            // Determine common port
            //
 
            if (port == E.port) {
                commonPort = port;
            }
            else if (port == SocketPermission.AllPorts) {
                commonPort = E.port;
            }
            else if (E.port == SocketPermission.AllPorts) {
                commonPort = port;
            }
            else {
                return null;
            }
 
            //Work out common hostname part
            //
            // This is a fix for INADDR_ANY in Bind()
            // if this.Hostname == "0.0.0.0" then it matches only to e.Hostname="*.*.*.*"
            //
            // The reason is to not pass "0.0.0.0" into Resolve()
            if(this.Hostname.Equals("0.0.0.0"))
            {
                if(E.Hostname.Equals("*.*.*.*") || E.Hostname.Equals("0.0.0.0"))
                    commonName = this.Hostname;//i.e. 0.0.0.0
                else
                    return null;
            }
            else if(E.Hostname.Equals("0.0.0.0"))
            {
                if(this.Hostname.Equals("*.*.*.*") || this.Hostname.Equals("0.0.0.0"))
                    commonName = E.Hostname; //i.e. 0.0.0.0
                else
                    return null;
            }
            else if (IsDns && E.IsDns) {
                //
                // If both are DNS names we compare names as strings
                //
                if(String.Compare(hostname, E.hostname, StringComparison.OrdinalIgnoreCase ) != 0) {
                    return null;
                }
                else {
                    commonName = hostname;
                }
            }
            else
            {
                Resolve();
                E.Resolve();
                //after this step we got both clases updated with valid
                //wildcard and address members. It's safe now to access those members directly
 
                //
                // if Resolve() didn't work for some reason then we're out of luck
                //
 
                if (((address == null) && !wildcard) || ((E.address == null) && !E.wildcard)) {
                    return null;
                }
 
 
                //
                // Find intersection of address lists
                if(wildcard && E.wildcard) {
                    string [] wcPieces = hostname.Split(DotSeparator);
                    string [] strPieces = E.hostname.Split(DotSeparator);
                    string  result="";
 
                    if ((strPieces.Length != 4) || (wcPieces.Length != 4)) {
                        return null;
                    }
                    for (int i = 0; i < 4; i++) {
                        if(i != 0) {
                            result+=".";
                        }
                        if (strPieces[i] == wcPieces[i]) {
                            result+=strPieces[i];
                        }
                        else
                        if (strPieces[i] == "*") {
                            result+=wcPieces[i];
                        }
                        else
                        if (wcPieces[i] == "*") {
                            result+=strPieces[i];
                        }
                        else
                            return null;
                    }
                    commonName = result;
                }else
                if (wildcard) {                                                 //if ME is a wildcard
                    //
                    //
                    // Check for wildcard IP matching
                    //
                    for (int i = 0; i < E.address.Length; ++i) {
                        if (MatchWildcard(E.address[i].ToString())) {
                            commonName = E.hostname;    //SHE fits into my wildcard
                            break;
                        }
                    }
                }
                else if (E.wildcard) {                                   //if SHE is a wildcard
                    for (int i = 0; i < address.Length; ++i) {
                        if (E.MatchWildcard(address[i].ToString())) {
                            commonName = hostname;      //ME fit  into her wildcard
                            break;
                        }
                    }
                }
                else
                {
                    //
                    // Not wildcard: check aginst  IP addresses list
                    //
 
                    if (address == E.address) {                 // they both are NOT null (already checked)
                        commonName = hostname;
                    }
 
                    //
                    // Search the IP addresses for match
                    //
                    for (int i = 0; commonName == null && i < address.Length; i++) {
                        for (int k = 0; k < E.address.Length; k++) {
                            if (address[i].Equals(E.address[k])) {
                                commonName = hostname;
                                break;
                            }
                        }
                    }
                }
                if(commonName == null) {
                    return null;
                }
            }
 
            return new EndpointPermission(commonName, commonPort, commonTransport);
        }
/*
FROM RFC 952
------------
ASSUMPTIONS
1   A "name" (Net, Host, Gateway, or Domain name) is a text string up
    to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus sign (-), and period (.).
    Note that periods are only allowed when they serve to delimit components of "domain style names".
    (See RFC-921, "Domain Name System Implementation Schedule", for background).
    No blank or space characters are permitted as part of a name.
    No distinction is made between upper and lower case.
    The first character must be an alpha character.
    The last character must not be a minus sign or period.
    Single character names or nicknames are not allowed.
 
    Implementaion below is relaxed in terms of:
    - Hostname may start with a digit (as per RFC1123 )
    - Hostname may contain '_' character (historical Inet issue)
    - Hostname may be a single-character string (historical Inet issue)
    - Hostname may contain '*' as a wildcard for an EndPointPermission
    - Hostname may be empty (to support config templates)
    - Hostname may be an IPv6 string comprised of A-F, 0-9, '.', ':', and '%' chars
*/
    private enum EndPointType {
            Invalid,
            IPv6,
            DnsOrWildcard,
            IPv4
    };
 
    private static EndPointType CheckEndPointName(string name) {
        if (name == null) {
            return EndPointType.Invalid;
        }
        bool isIPv6       = false;
        bool isDnsOrWC    = false;
        bool isHexLetter  = false;
        for(int i=0; i < name.Length; ++i) {
            char ch = name[i];
            switch(ch) {
            case '.':   //note _all_ dots name is an error
                        continue;
            case '-':   //if _all_ chars are those we call Dns (to confirm error)
            case '_':
            case '*':   isDnsOrWC = true;
                        continue;
            case ':':
            case '%':   isIPv6 = true;
                        continue;
            default:    break;
            }
 
            //Check on letters but NOT hex digits
            if ((ch > 'f' && ch <= 'z') || (ch > 'F' && ch <= 'Z')) {
                isDnsOrWC = true;
                continue;
            }
            //Check on HEX letters
            if((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
                isHexLetter = true;
                continue;
            }
            //Here only digits left (others are invalid)
            if (!(ch >= '0' && ch <= '9'))
                return EndPointType.Invalid;
        }
 
        // The logic is (solely for the purpose of SocketPermssion class)
        //  isIPv6 && isDnsOrWC   = EndPointType.Invalid
        //  isIPv6 && !isDnsOrWC  = EndPointType.IPv6
        //  !isIPv6 && isDnsOrWC  = EndPointType.DnsOrWildcard
        //  !isIPv6 && !isDnsOrWC && isHexLetter = EndPointType.DnsOrWildcard;
        //  else = EndPointType.IPv4
        return isIPv6 ? (isDnsOrWC? EndPointType.Invalid: EndPointType.IPv6)
                      : (isDnsOrWC? EndPointType.DnsOrWildcard :
                                    isHexLetter? EndPointType.DnsOrWildcard :EndPointType.IPv4);
    }
 
 
    } // class EndpointPermission
 
 
} // namespace System.Net