Adding reference source for System.Net
[mono.git] / mcs / class / referencesource / System / net / System / Net / SocketPermission.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SocketPermission.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8
9     using System.Collections;
10     using System.Security;
11     using System.Security.Permissions;
12     using System.Globalization;
13     using System.Threading;
14
15     //NOTE: While SocketPermissionAttribute resides in System.DLL,
16     //      no classes from that DLL are able to make declarative usage of SocketPermission.
17
18
19     // THE syntax of this attribute is as followed
20     // [SocketPermsion(SecurityAction.Assert, Access=Connect, Host=hostname, Transport=Tcp/Udp/All, port=portN/All)]
21     // [SocketPermsion(SecurityAction.Assert, Access=Accept, Host=localname, Transport=Tcp/Udp/All, port=portN/All)]
22     //
23     // WHERE:
24     //=======
25     // - hostname is either a DNS hostname OR an IP address 1.2.3.4 or an IP wildcard 1.2.*.*
26     // - protocol is either Tcp, Udp or All
27     // - port is a numeric value or -1 that means "All Ports"
28     //
29     //  All the properites Host, Protocol and Port must be specified.
30     // "localIP" means that you put here a valid address or DNS name or the localhost
31     //
32     //  NetworkAccess specifies the scope of permission, i.e. for connecting to remote peer,
33     //  or for accepting data on the local resources.
34     //
35
36     [   AttributeUsage( AttributeTargets.Method | AttributeTargets.Constructor |
37                         AttributeTargets.Class  | AttributeTargets.Struct      |
38                         AttributeTargets.Assembly,
39                         AllowMultiple = true, Inherited = false )]
40
41     [Serializable]
42     public sealed class SocketPermissionAttribute: CodeAccessSecurityAttribute
43     {
44         private string  m_access = null;
45         private string  m_host   = null;
46         private string  m_port   = null;
47         private string  m_transport  = null;
48
49         private const string strAccess     = "Access";
50         private const string strConnect    = "Connect";
51         private const string strAccept     = "Accept";
52         private const string strHost       = "Host";
53         private const string strTransport  = "Transport";
54         private const string strPort       = "Port";
55
56         public SocketPermissionAttribute( SecurityAction action ): base( action )
57         {
58         }
59
60         public string Access {
61             get { return m_access; }
62             set {
63                 if (m_access != null) {
64                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strAccess, value), "value");
65                 }
66                 m_access = value;
67             }
68         }
69
70         public string Host {
71             get { return m_host; }
72             set {
73                 if (m_host != null) {
74                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strHost, value), "value");
75                 }
76                 m_host = value;
77             }
78         }
79
80         public string Transport {
81             get { return m_transport;}
82             set {
83                 if (m_transport != null) {
84                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strTransport, value), "value");
85                 }
86                 m_transport = value;
87             }
88         }
89
90         public string Port {
91             get { return m_port;}
92             set {
93                 if (m_port != null) {
94                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_multi, strPort, value), "value");
95                 }
96                 m_port = value;
97             }
98         }
99
100         public override IPermission CreatePermission()
101         {
102             SocketPermission perm = null;
103             if (Unrestricted) {
104                 perm = new SocketPermission( PermissionState.Unrestricted);
105             }
106             else {
107                 perm = new SocketPermission(PermissionState.None);
108                 if (m_access == null) {
109                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strAccess));
110                 }
111                 if (m_host == null) {
112                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strHost));
113                 }
114                 if (m_transport == null) {
115                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strTransport));
116                 }
117                 if (m_port == null) {
118                     throw new ArgumentException(SR.GetString(SR.net_perm_attrib_count, strPort));
119                 }
120                 ParseAddPermissions(perm);
121             }
122             return perm;
123         }
124
125
126         private void ParseAddPermissions(SocketPermission perm) {
127
128             NetworkAccess access;
129             if (0 == string.Compare(m_access, strConnect, StringComparison.OrdinalIgnoreCase )) {
130                 access = NetworkAccess.Connect;
131             }
132             else
133             if (0 == string.Compare(m_access, strAccept, StringComparison.OrdinalIgnoreCase )) {
134                 access = NetworkAccess.Accept;
135             }
136             else {
137                 throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, strAccess, m_access));
138             }
139
140             TransportType transport;
141             try {
142                 transport = (TransportType) Enum.Parse(typeof(TransportType), m_transport, true);
143             }
144             catch (Exception e) {
145                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {                                       
146                             throw;
147                     }
148                 throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, strTransport, m_transport), e);
149             }
150
151             int port;
152             if (string.Compare(m_port, "All", StringComparison.OrdinalIgnoreCase ) == 0) {
153                 m_port = "-1";
154             }
155             try {
156                 port = Int32.Parse(m_port, NumberFormatInfo.InvariantInfo);
157             }
158             catch (Exception e) {
159                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {                                       
160                             throw;
161                     }
162                 throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, strPort, m_port), e);
163             }
164
165             if (!ValidationHelper.ValidateTcpPort(port) && port != SocketPermission.AllPorts) {
166                 throw new ArgumentOutOfRangeException("port", port, SR.GetString(SR.net_perm_invalid_val, strPort, m_port));
167             }
168             perm.AddPermission(access, transport, m_host, port);
169         }
170
171     }
172
173
174     /// <devdoc>
175     ///    <para>
176     ///       Controls rights to make or accept connections on a transport address.
177     ///    </para>
178     /// </devdoc>
179     [Serializable]
180     public sealed class SocketPermission : CodeAccessPermission, IUnrestrictedPermission {
181
182         private ArrayList m_connectList;
183         private ArrayList m_acceptList;
184         private bool m_noRestriction;
185
186
187         /// <devdoc>
188         ///    <para>
189         ///       Returns the enumeration of permissions to connect a remote peer.
190         ///    </para>
191         /// </devdoc>
192         public IEnumerator ConnectList    {get {return m_connectList.GetEnumerator();}}
193
194         /// <devdoc>
195         ///    <para>
196         ///       Returns the enumeration of permissions to accept incoming connections.
197         ///    </para>
198         /// </devdoc>
199         public IEnumerator AcceptList     {get {return m_acceptList.GetEnumerator();}}
200
201
202         /// <devdoc>
203         ///    <para>
204         ///       Defines a constant representing all ports.
205         ///    </para>
206         /// </devdoc>
207         public const int AllPorts = unchecked((int)0xFFFFFFFF);
208
209         //<
210         internal const int AnyPort = unchecked((int)0);
211
212         /// <devdoc>
213         ///    <para>
214         ///       Creates a new instance of the <see cref='System.Net.SocketPermission'/>
215         ///       class that passes all demands
216         ///       or that fails all demands.
217         ///    </para>
218         /// </devdoc>
219         public SocketPermission(PermissionState state) {
220             initialize();
221             m_noRestriction = (state == PermissionState.Unrestricted);
222         }
223
224         internal SocketPermission(bool free) {
225             initialize();
226             m_noRestriction = free;
227         }
228
229
230         /// <devdoc>
231         ///    <para>
232         ///       Creates a new instance of the SocketPermissions class for the given transport address with the specified permission.
233         ///    </para>
234         /// </devdoc>
235         public SocketPermission(NetworkAccess access, TransportType transport, String hostName, int portNumber) {
236             initialize();
237             m_noRestriction = false;
238             AddPermission(access, transport, hostName, portNumber);
239         }
240
241         /// <devdoc>
242         ///    <para>
243         ///       Adds a permission to the set of permissions for a transport address.
244         ///    </para>
245         /// </devdoc>
246         public void AddPermission(NetworkAccess access, TransportType transport, string hostName, int portNumber) {
247             if (hostName == null) {
248                 throw new ArgumentNullException("hostName");
249             }
250
251             EndpointPermission endPoint = new EndpointPermission(hostName, portNumber, transport);
252
253             AddPermission(access, endPoint);
254         }
255
256         internal void AddPermission(NetworkAccess access, EndpointPermission endPoint) {
257             if (m_noRestriction) {    // Is the permission unrestricted?
258                 return;             // YES-- then additional endpoints have no effect
259             }
260             if ((access & NetworkAccess.Connect) != 0)
261                     m_connectList.Add(endPoint);
262             if ((access & NetworkAccess.Accept) != 0)
263                     m_acceptList.Add(endPoint);
264         }
265
266         // IUnrestrictedPermission interface methods
267         /// <devdoc>
268         ///    <para>
269         ///       Checks the overall permission state of the object.
270         ///    </para>
271         /// </devdoc>
272         public bool IsUnrestricted() {
273             return m_noRestriction;
274         }
275
276         // IPermission interface methods
277         /// <devdoc>
278         ///    <para>
279         ///       Creates
280         ///       a copy of a <see cref='System.Net.SocketPermission'/> instance.
281         ///    </para>
282         /// </devdoc>
283         public override IPermission Copy() {
284
285             SocketPermission sp = new SocketPermission(m_noRestriction);
286
287             sp.m_connectList = (ArrayList)m_connectList.Clone();
288             sp.m_acceptList = (ArrayList)m_acceptList.Clone();
289                         return sp;
290         }
291
292         private bool FindSubset(ArrayList source, ArrayList target) {
293             foreach (EndpointPermission e in source) {
294
295                 bool found = false;
296
297                 foreach (EndpointPermission ee in target) {
298                     if (e.SubsetMatch(ee)) {
299                         found = true;
300                         break;
301                     }
302                 }
303                 if (!found) {
304                     return false;
305                 }
306             }
307             return true;
308         }
309
310         /// <devdoc>
311         /// <para>Returns the logical union between two <see cref='System.Net.SocketPermission'/> instances.</para>
312         /// </devdoc>
313         public override IPermission Union(IPermission target) {
314             // Pattern suggested by Security engine
315             if (target==null) {
316                 return this.Copy();
317             }
318             SocketPermission other = target as SocketPermission;
319             if(other == null) {
320                 throw new ArgumentException(SR.GetString(SR.net_perm_target), "target");
321             }
322             if (m_noRestriction || other.m_noRestriction) {
323                 return new SocketPermission(true);
324             }
325             SocketPermission result = (SocketPermission)other.Copy();
326
327             for (int i = 0; i < m_connectList.Count; i++) {
328                 result.AddPermission(NetworkAccess.Connect, (EndpointPermission)m_connectList[i]);
329             }
330             for (int i = 0; i < m_acceptList.Count; i++) {
331                 result.AddPermission(NetworkAccess.Accept, (EndpointPermission)m_acceptList[i]);
332             }
333             return result;
334         }
335
336         /// <devdoc>
337         ///    <para>
338         ///       Returns the logical intersection between two <see cref='System.Net.SocketPermission'/> instances.
339         ///    </para>
340         /// </devdoc>
341         public override IPermission Intersect(IPermission target) {
342             // Pattern suggested by Security engine
343             if (target == null) {
344                 return null;
345             }
346
347             SocketPermission other = target as SocketPermission;
348             if(other == null) {
349                 throw new ArgumentException(SR.GetString(SR.net_perm_target), "target");
350             }
351
352             SocketPermission result;
353             if (m_noRestriction) {
354                 result = (SocketPermission)(other.Copy());
355             }
356             else if (other.m_noRestriction) {
357                 result = (SocketPermission)(this.Copy());
358             }
359             else {
360                 result = new SocketPermission(false);
361                 intersectLists(m_connectList, other.m_connectList, result.m_connectList);
362                 intersectLists(m_acceptList, other.m_acceptList, result.m_acceptList);
363             }
364
365             // return null if resulting permission is restricted and empty
366             if (!result.m_noRestriction &&
367                 result.m_connectList.Count == 0 && result.m_acceptList.Count == 0) {
368                 return null;
369             }
370             return result;
371         }
372
373         /// <devdoc>
374         /// <para>Compares two <see cref='System.Net.SocketPermission'/> instances.</para>
375         /// </devdoc>
376         public override bool IsSubsetOf(IPermission target) {
377             // Pattern suggested by security engine
378             if (target == null) {
379                 return (m_noRestriction == false && m_connectList.Count == 0 && m_acceptList.Count == 0);
380             }
381
382             SocketPermission other = target as SocketPermission;
383             if (other == null) {
384                 throw new ArgumentException(SR.GetString(SR.net_perm_target), "target");
385             }
386
387             if (other.IsUnrestricted()) {
388                 return true;
389             } else if (this.IsUnrestricted()) {
390                 return false;
391             } else if (this.m_acceptList.Count + this.m_connectList.Count ==0) {
392                 return true;
393             } else if (other.m_acceptList.Count + other.m_connectList.Count ==0) {
394                 return false;
395             }
396
397             bool result = false;
398             try {
399                 if (FindSubset(m_connectList, other.m_connectList) &&
400                     FindSubset(m_acceptList, other.m_acceptList)) {
401                     result = true;
402                 }
403             }
404             finally {
405                 //  This is around a back door into DNS
406                 //  Security engine will call isSubsetOf and probably have
407                 //  DNS permission asserted. We call DNS resolve.
408                 //  Before return do cleanup of DNS results.
409
410                 //  Only "this" needs cleanup, the policy object is not available for
411                 //  an application to look at.
412                 this.CleanupDNS();
413             }
414
415             return result;
416         }
417
418         //
419         //This is to cleanup DNS resolution results
420         //
421         private void CleanupDNS() {
422             foreach(EndpointPermission e in m_connectList) {
423                 //DNS hostnames never produce 'cached=true'
424                 if (e.cached) {
425                     continue;
426                 }
427                 e.address = null;
428             }
429
430             foreach(EndpointPermission e in m_acceptList) {
431                 //DNS hostnames never produce 'cached=true'
432                 if (e.cached) {
433                     continue;
434                 }
435                 e.address = null;
436             }
437         }
438
439         /// <devdoc>
440         /// </devdoc>
441         public override void FromXml(SecurityElement securityElement) {
442             if (securityElement == null) {
443
444                 //
445                 // null SecurityElement
446                 //
447
448                 throw new ArgumentNullException("securityElement");
449             }
450             if (!securityElement.Tag.Equals("IPermission")) {
451
452                 //
453                 // SecurityElement must be a permission element
454                 //
455
456                 throw new ArgumentException(SR.GetString(SR.net_not_ipermission), "securityElement");
457             }
458
459             string className = securityElement.Attribute("class");
460
461             if (className == null) {
462
463                 //
464                 // SecurityElement must be a permission element for this type
465                 //
466
467                 throw new ArgumentException(SR.GetString(SR.net_no_classname), "securityElement");
468             }
469             if (className.IndexOf(this.GetType().FullName) < 0) {
470
471                 //
472                 // SecurityElement must be a permission element for this type
473                 //
474
475                 throw new ArgumentException(SR.GetString(SR.net_no_typename), "securityElement");
476             }
477
478             //
479             // Start recovering the state from XML encoding
480             //
481
482             initialize();
483
484
485             String str = securityElement.Attribute("Unrestricted");
486
487             if (str != null) {
488                 m_noRestriction = (0 == string.Compare( str, "true", StringComparison.OrdinalIgnoreCase ));
489                 if(m_noRestriction)
490                     return;
491             }
492
493             m_noRestriction = false;
494             m_connectList = new ArrayList();
495             m_acceptList = new ArrayList();
496
497             SecurityElement et = securityElement.SearchForChildByTag("ConnectAccess");
498             if (et != null) {
499                 ParseAddXmlElement(et, m_connectList, "ConnectAccess, ");
500             }
501             et = securityElement.SearchForChildByTag("AcceptAccess");
502             if (et != null) {
503                 ParseAddXmlElement(et, m_acceptList, "AcceptAccess, ");
504             }
505         }
506
507         private static void ParseAddXmlElement(SecurityElement et, ArrayList listToAdd, string accessStr) {
508
509             foreach(SecurityElement uriElem in et.Children) {
510                 if (uriElem.Tag.Equals("ENDPOINT")) {
511                     Hashtable attributes = uriElem.Attributes;
512                     string tmpStr;
513
514                     try {
515                         tmpStr = attributes["host"] as string;
516                     }
517                     catch{
518                         tmpStr = null;
519                     }
520
521                     if (tmpStr == null) {
522                         throw new ArgumentNullException(accessStr + "host");
523                     }
524                     string host = tmpStr;
525
526                     try {
527                         tmpStr = attributes["transport"] as string;
528                     }
529                     catch{
530                         tmpStr = null;
531                     }
532                     if (tmpStr == null) {
533                         throw new ArgumentNullException(accessStr + "transport");
534                     }
535                     TransportType transport;
536                     try {
537                         transport = (TransportType) Enum.Parse(typeof(TransportType), tmpStr, true);
538                     }
539                     catch (Exception exception) {
540                         if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
541                                     throw;
542                             }
543                         throw new ArgumentException(accessStr + "transport", exception);
544                     }
545
546                     try {
547                         tmpStr = attributes["port"] as string;
548                     }
549                     catch{
550                         tmpStr = null;
551                     }
552                     if (tmpStr == null) {
553                         throw new  ArgumentNullException(accessStr + "port");
554                     }
555                     if (string.Compare(tmpStr, "All", StringComparison.OrdinalIgnoreCase ) == 0) {
556                         tmpStr = "-1";
557                     }
558                     int port;
559                     try {
560                         port = Int32.Parse(tmpStr, NumberFormatInfo.InvariantInfo);
561                     }
562                     catch (Exception exception) {
563                         if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) {                                       
564                                     throw;
565                             }
566                         throw new ArgumentException(SR.GetString(SR.net_perm_invalid_val, accessStr + "port", tmpStr), exception);
567                     }
568
569                     if (!ValidationHelper.ValidateTcpPort(port) && port != SocketPermission.AllPorts) {
570                         throw new ArgumentOutOfRangeException("port", port, SR.GetString(SR.net_perm_invalid_val, accessStr + "port", tmpStr));
571                     }
572
573
574                     listToAdd.Add(new EndpointPermission(host, port , transport));
575                 }
576                 else {
577                     // improper tag found, just ignore
578                 }
579             }
580         }
581
582         /// <devdoc>
583         ///    <para>[To be supplied.]</para>
584         /// </devdoc>
585         public override SecurityElement ToXml() {
586
587             SecurityElement securityElement = new SecurityElement( "IPermission" );
588
589             securityElement.AddAttribute("class", this.GetType().FullName + ", " + this.GetType().Module.Assembly.FullName.Replace( '\"', '\'' ));
590             securityElement.AddAttribute("version", "1");
591
592             if (!IsUnrestricted()) {
593                 if (m_connectList.Count > 0) {
594
595                     SecurityElement permList = new SecurityElement("ConnectAccess");
596                     foreach(EndpointPermission permission in m_connectList) {
597                         SecurityElement endpoint = new SecurityElement("ENDPOINT");
598                         endpoint.AddAttribute("host", permission.Hostname);
599                         endpoint.AddAttribute("transport", permission.Transport.ToString());
600                         endpoint.AddAttribute("port",   permission.Port != AllPorts?
601                                                         permission.Port.ToString(NumberFormatInfo.InvariantInfo): "All");
602                         permList.AddChild(endpoint);
603                     }
604                     securityElement.AddChild(permList);
605                 }
606
607                 if (m_acceptList.Count > 0) {
608
609                     SecurityElement permList = new SecurityElement("AcceptAccess");
610                     foreach(EndpointPermission permission in m_acceptList) {
611                         SecurityElement endpoint = new SecurityElement("ENDPOINT");
612                         endpoint.AddAttribute("host", permission.Hostname);
613                         endpoint.AddAttribute("transport", permission.Transport.ToString());
614                         endpoint.AddAttribute("port",   permission.Port != AllPorts?
615                                                         permission.Port.ToString(NumberFormatInfo.InvariantInfo): "All");
616                         permList.AddChild(endpoint);
617                     }
618                     securityElement.AddChild(permList);
619                 }
620             }
621             else {
622                 securityElement.AddAttribute("Unrestricted", "true");
623             }
624             return securityElement;
625         }
626
627         private void initialize() {
628             m_noRestriction = false;
629             m_connectList = new ArrayList();
630             m_acceptList = new ArrayList();
631         }
632
633         private static void intersectLists(ArrayList A, ArrayList B, ArrayList result) {
634             // The optimization is done according to the following truth
635             // (A|B|C) intersect (B|C|E|D)) == B|C|(A inter E)|(A inter D)
636             //
637             // We also check on any duplicates in the result
638
639
640             bool[] aDone=new bool[A.Count];            //used to avoid duplicates in result
641             bool[] bDone=new bool[B.Count];
642             int ia=0;
643             int ib=0;
644             // Round 1st
645             // Getting rid of same permissons in the input arrays (assuming X /\ X = X)
646             foreach (EndpointPermission a in  A) {
647                 ib = 0;
648                 foreach (EndpointPermission b in  B) {
649                     // check to see if b is in the result already
650                     if (!bDone[ib]) {
651                         //if both elements are the same, copy it into result
652                         if (a.Equals(b)) {
653                             result.Add(a);
654                             aDone[ia]=bDone[ib]=true;
655                             //since permissions are ORed we can break and go to the next A
656                             break;
657                         }
658                     }
659                     ++ib;
660                 } //foreach b in B
661                 ++ia;
662             } //foreach a in A
663
664             ia = 0;
665             // Round second
666             // Grab only intersections of objects not found in both A and B
667             foreach (EndpointPermission a in  A) {
668
669                 if (!aDone[ia]) {
670                     ib = 0;
671                     foreach(EndpointPermission b in B) {
672                         if (!bDone[ib]) {
673                             EndpointPermission intesection = a.Intersect(b);
674                             if (intesection != null) {
675                                 bool found = false;
676                                 // check to see if we already have the same result
677                                 foreach (EndpointPermission  res in result) {
678                                     if (res.Equals(intesection)) {
679                                         found = true;
680                                         break;
681                                     }
682                                 }
683                                 if (!found) {
684                                     result.Add(intesection);
685                                 }
686                             }
687                         } //!Done[ib]
688                         ++ib;
689                     } //foreach b in B
690                 } //!Done[ia]
691                 ++ia;
692             } //foreach a in A
693         }
694
695     }// class SocketPermission
696
697
698     /// <devdoc>
699     ///       Represents an element of SocketPermission object contents.
700     /// </devdoc>
701     [Serializable]
702     public class EndpointPermission {
703
704         //
705         // <
706
707
708
709
710
711         internal String hostname;
712         internal int port;
713         internal TransportType transport;
714         internal bool wildcard;
715         internal IPAddress[] address;
716         internal bool cached = false;
717
718         private static char[] DotSeparator = new char[] {'.'};
719         private const String encSeperator = "#";
720
721         /// <devdoc>
722         ///    <para>
723         ///       Returns the hostname part of EndpointPermission object
724         ///    </para>
725         /// </devdoc>
726         public String           Hostname        { get {return hostname;}}
727
728         /// <devdoc>
729         ///    <para>
730         ///       Returns the transport of EndpointPermission object
731         ///    </para>
732         /// </devdoc>
733         public TransportType    Transport       { get {return transport;}}
734
735         /// <devdoc>
736         ///    <para>
737         ///       Returns the Port part of EndpointPermission object
738         ///    </para>
739         /// </devdoc>
740         public int              Port            { get {return port;}}
741         //
742         // <
743
744
745
746
747
748         internal EndpointPermission(String epname, int port, TransportType trtype) {
749
750             if (CheckEndPointName(epname) == EndPointType.Invalid) {
751                 throw new ArgumentException(SR.GetString(SR.net_perm_epname, epname), "epname");
752             }
753             if (!ValidationHelper.ValidateTcpPort(port) && port != SocketPermission.AllPorts) {
754                 throw new ArgumentOutOfRangeException("port", SR.GetString(SR.net_perm_invalid_val, "Port", port.ToString(NumberFormatInfo.InvariantInfo)));
755             }
756
757             hostname = epname;
758             this.port = port;
759             transport = trtype;
760             wildcard = false;
761         }
762
763         //
764         // This is ONLY a syntatic check on equality, hostnames are compared as strings!
765         //
766         public override bool Equals(object obj) {
767
768             EndpointPermission ep = (EndpointPermission)obj;
769
770             if (String.Compare(hostname, ep.hostname, StringComparison.OrdinalIgnoreCase ) != 0) {
771                 return false;
772             }
773             if (port != ep.port) {
774                 return false;
775             }
776             if (transport != ep.transport) {
777                 return false;
778             }
779             return true;
780         }
781
782         public override int GetHashCode() {
783             return ToString().GetHashCode();
784         }
785
786         //
787         // <
788
789
790         internal bool IsDns {
791             get {
792                 if (IsValidWildcard) {
793                     return false;
794                 }
795                 return CheckEndPointName(hostname) == EndPointType.DnsOrWildcard;
796             }
797         }
798
799
800         //
801         // In this version wildcards are only allowed over IP ranges
802         // not DNS names. For example "*.microsoft.com" is not allowed
803         // A valid wildcard will have exactly three periods
804         //IPv6 wildcards are NOT supported
805         //
806         private bool IsValidWildcard {
807             get {
808
809                 int len = hostname.Length;
810
811                 //
812                 // Check minimum length
813                 //
814
815                 if (len < 3) {
816                     return false;
817                 }
818
819                 //
820                 // First and last characters cannot be periods
821                 //
822
823                 if ((hostname[0] == '.') || (hostname[len - 1] == '.')) {
824                     return false;
825                 }
826
827                 int dotCount = 0;
828                 int anyCount = 0;
829
830                 for (int i = 0; i < hostname.Length; i++) {
831                     if (hostname[i] == '.') {
832                         dotCount++;
833                     }
834                     else if (hostname[i] == '*') {
835                         ++anyCount;
836                     }
837                     else if (!Char.IsDigit(hostname[i])) {  // Not a digit?
838                         return false;                       // Reject wildcard
839                     }
840                 }
841                 return (dotCount == 3) && (anyCount > 0);
842             }
843         }
844
845         internal bool MatchAddress(EndpointPermission e) {
846
847             // For Asp.Net config we made it valid empty string in a hostname,
848             // but it will match to nothing.
849             if(this.Hostname.Length == 0 || e.Hostname.Length == 0) {
850                 return false;
851             }
852
853             //
854             // This is a fix for INADDR_ANY in Bind()
855             // if this.Hostname == "0.0.0.0" then it matches only to e.Hostname="*.*.*.*"
856             //
857             // The reason is to not pass "0.0.0.0" into Resolve()
858             if(this.Hostname.Equals("0.0.0.0"))
859             {
860                 if(e.Hostname.Equals("*.*.*.*") || e.Hostname.Equals("0.0.0.0"))
861                     return true;
862                 return false;
863             }
864
865             if (IsDns && e.IsDns) {
866
867                 //
868                 // <
869
870
871
872                 return (String.Compare(hostname, e.hostname, StringComparison.OrdinalIgnoreCase ) == 0);
873             }
874             Resolve();
875             e.Resolve();
876
877             //
878             // if Resolve() didn't work for some reason then we're out of luck
879             //
880
881             if (((address == null) && !wildcard) || ((e.address == null) && !e.wildcard)) {
882                 return false;
883             }
884
885             //
886             // try matching IP addresses against other wildcard address(es) or
887             // wildcard
888             //
889
890             if (this.wildcard && !e.wildcard) {
891                 return false;                           // as a wildcard I cannot be subset of a host.
892
893             }
894             else if (e.wildcard) {
895                 if (this.wildcard) {
896                     // check against my _wildcard_
897                     if (MatchWildcard(e.hostname)) {
898                         return true;
899                     }
900                 }
901                 else {
902                     // check against my _addresses_
903                     for (int i = 0; i < address.Length; ++i) {
904                         if (e.MatchWildcard(address[i].ToString())) {
905                             return true;
906                         }
907                     }
908                 }
909             } else {
910                 //both are _not_ wildcards
911                 for (int i = 0; i < address.Length; ++i) {
912                     for (int j = 0; j < e.address.Length; ++j) {
913                         if (address[i].Equals(e.address[j])) {
914                             return true;
915                         }
916                     }
917                 }
918             }
919             return false;
920         }
921
922         internal bool MatchWildcard(string str) {
923
924             string [] wcPieces = hostname.Split(DotSeparator);
925             string [] strPieces = str.Split(DotSeparator);
926
927             if ((strPieces.Length != 4) || (wcPieces.Length != 4)) {
928                 return false;
929             }
930             for (int i = 0; i < 4; i++) {
931                 if ((strPieces[i] != wcPieces[i]) && (wcPieces[i] != "*")) {
932                     return false;
933                 }
934             }
935             return true;
936         }
937
938         internal void Resolve() {
939
940             //
941             // if we already resolved this name then don't do it again
942             //
943
944             if (cached) {
945                 return;
946             }
947
948             //
949             // IP wildcards are not resolved
950             //
951
952             if (wildcard) {
953                 return;
954             }
955
956             //
957             // IP addresses with wildcards are allowed in permissions
958             //
959
960             if (IsValidWildcard) {
961                 wildcard = true;
962                 cached = true;
963                 return;
964             }
965
966             //
967             // Check if the permission was specified as numeric IP.
968             //
969             IPAddress ipaddr;
970             if (IPAddress.TryParse(hostname, out ipaddr))
971             {
972                 address = new IPAddress[1];
973                 address[0] = ipaddr;
974                 cached = true;
975                 return;
976             }
977
978             //
979             // Not numeric: use GetHostByName to determine addresses
980             //
981             try {
982                 IPHostEntry ipHostEntry;
983                 if (Dns.TryInternalResolve(hostname, out ipHostEntry)) {
984                     address = ipHostEntry.AddressList;
985                 }
986
987                 // NB: It never caches DNS responses
988                 //
989
990             }
991             catch (SecurityException) {
992                 throw;
993             }
994             catch {
995                 // ignore second exception
996             }
997         }
998
999         internal bool SubsetMatch(EndpointPermission e) {
1000             return ((transport == e.transport) || (e.transport == TransportType.All))
1001                     && ((port == e.port) || (e.port == SocketPermission.AllPorts) || port == SocketPermission.AnyPort)
1002                     && MatchAddress(e);
1003         }
1004
1005         public override String ToString() {
1006             return hostname + encSeperator + port + encSeperator + ((int)transport).ToString(NumberFormatInfo.InvariantInfo);
1007         }
1008
1009         internal EndpointPermission Intersect(EndpointPermission E) {
1010
1011             String commonName=null;
1012             TransportType commonTransport;
1013             int commonPort;
1014
1015             //
1016             // Look at the transport
1017             //
1018
1019             if (transport == E.transport) {           // same transport
1020                 commonTransport = transport;
1021             }
1022             // NO: check if one of the permissions authorize all transports
1023             else if (transport == TransportType.All) {
1024                 commonTransport = E.transport;
1025             }
1026             else if (E.transport == TransportType.All) {
1027                 commonTransport = transport;
1028             }
1029             else {   // transport dont match-- intersection is empty
1030                 return null;
1031             }
1032
1033             //
1034             // Determine common port
1035             //
1036
1037             if (port == E.port) {
1038                 commonPort = port;
1039             }
1040             else if (port == SocketPermission.AllPorts) {
1041                 commonPort = E.port;
1042             }
1043             else if (E.port == SocketPermission.AllPorts) {
1044                 commonPort = port;
1045             }
1046             else {
1047                 return null;
1048             }
1049
1050             //Work out common hostname part
1051             //
1052             // This is a fix for INADDR_ANY in Bind()
1053             // if this.Hostname == "0.0.0.0" then it matches only to e.Hostname="*.*.*.*"
1054             //
1055             // The reason is to not pass "0.0.0.0" into Resolve()
1056             if(this.Hostname.Equals("0.0.0.0"))
1057             {
1058                 if(E.Hostname.Equals("*.*.*.*") || E.Hostname.Equals("0.0.0.0"))
1059                     commonName = this.Hostname;//i.e. 0.0.0.0
1060                 else
1061                     return null;
1062             }
1063             else if(E.Hostname.Equals("0.0.0.0"))
1064             {
1065                 if(this.Hostname.Equals("*.*.*.*") || this.Hostname.Equals("0.0.0.0"))
1066                     commonName = E.Hostname; //i.e. 0.0.0.0
1067                 else
1068                     return null;
1069             }
1070             else if (IsDns && E.IsDns) {
1071                 //
1072                 // If both are DNS names we compare names as strings
1073                 //
1074                 if(String.Compare(hostname, E.hostname, StringComparison.OrdinalIgnoreCase ) != 0) {
1075                     return null;
1076                 }
1077                 else {
1078                     commonName = hostname;
1079                 }
1080             }
1081             else
1082             {
1083                 Resolve();
1084                 E.Resolve();
1085                 //after this step we got both clases updated with valid
1086                 //wildcard and address members. It's safe now to access those members directly
1087
1088                 //
1089                 // if Resolve() didn't work for some reason then we're out of luck
1090                 //
1091
1092                 if (((address == null) && !wildcard) || ((E.address == null) && !E.wildcard)) {
1093                     return null;
1094                 }
1095
1096
1097                 //
1098                 // Find intersection of address lists
1099                 if(wildcard && E.wildcard) {
1100                     string [] wcPieces = hostname.Split(DotSeparator);
1101                     string [] strPieces = E.hostname.Split(DotSeparator);
1102                     string  result="";
1103
1104                     if ((strPieces.Length != 4) || (wcPieces.Length != 4)) {
1105                         return null;
1106                     }
1107                     for (int i = 0; i < 4; i++) {
1108                         if(i != 0) {
1109                             result+=".";
1110                         }
1111                         if (strPieces[i] == wcPieces[i]) {
1112                             result+=strPieces[i];
1113                         }
1114                         else
1115                         if (strPieces[i] == "*") {
1116                             result+=wcPieces[i];
1117                         }
1118                         else
1119                         if (wcPieces[i] == "*") {
1120                             result+=strPieces[i];
1121                         }
1122                         else
1123                             return null;
1124                     }
1125                     commonName = result;
1126                 }else
1127                 if (wildcard) {                                                 //if ME is a wildcard
1128                     //
1129                     //
1130                     // Check for wildcard IP matching
1131                     //
1132                     for (int i = 0; i < E.address.Length; ++i) {
1133                         if (MatchWildcard(E.address[i].ToString())) {
1134                             commonName = E.hostname;    //SHE fits into my wildcard
1135                             break;
1136                         }
1137                     }
1138                 }
1139                 else if (E.wildcard) {                                   //if SHE is a wildcard
1140                     for (int i = 0; i < address.Length; ++i) {
1141                         if (E.MatchWildcard(address[i].ToString())) {
1142                             commonName = hostname;      //ME fit  into her wildcard
1143                             break;
1144                         }
1145                     }
1146                 }
1147                 else
1148                 {
1149                     //
1150                     // Not wildcard: check aginst  IP addresses list
1151                     //
1152
1153                     if (address == E.address) {                 // they both are NOT null (already checked)
1154                         commonName = hostname;
1155                     }
1156
1157                     //
1158                     // Search the IP addresses for match
1159                     //
1160                     for (int i = 0; commonName == null && i < address.Length; i++) {
1161                         for (int k = 0; k < E.address.Length; k++) {
1162                             if (address[i].Equals(E.address[k])) {
1163                                 commonName = hostname;
1164                                 break;
1165                             }
1166                         }
1167                     }
1168                 }
1169                 if(commonName == null) {
1170                     return null;
1171                 }
1172             }
1173
1174             return new EndpointPermission(commonName, commonPort, commonTransport);
1175         }
1176 /*
1177 FROM RFC 952
1178 ------------
1179 ASSUMPTIONS
1180 1   A "name" (Net, Host, Gateway, or Domain name) is a text string up
1181     to 24 characters drawn from the alphabet (A-Z), digits (0-9), minus sign (-), and period (.).
1182     Note that periods are only allowed when they serve to delimit components of "domain style names".
1183     (See RFC-921, "Domain Name System Implementation Schedule", for background).
1184     No blank or space characters are permitted as part of a name.
1185     No distinction is made between upper and lower case.
1186     The first character must be an alpha character.
1187     The last character must not be a minus sign or period.
1188     Single character names or nicknames are not allowed.
1189
1190     Implementaion below is relaxed in terms of:
1191     - Hostname may start with a digit (as per RFC1123 )
1192     - Hostname may contain '_' character (historical Inet issue)
1193     - Hostname may be a single-character string (historical Inet issue)
1194     - Hostname may contain '*' as a wildcard for an EndPointPermission
1195     - Hostname may be empty (to support config templates)
1196     - Hostname may be an IPv6 string comprised of A-F, 0-9, '.', ':', and '%' chars
1197 */
1198     private enum EndPointType {
1199             Invalid,
1200             IPv6,
1201             DnsOrWildcard,
1202             IPv4
1203     };
1204
1205     private static EndPointType CheckEndPointName(string name) {
1206         if (name == null) {
1207             return EndPointType.Invalid;
1208         }
1209         bool isIPv6       = false;
1210         bool isDnsOrWC    = false;
1211         bool isHexLetter  = false;
1212         for(int i=0; i < name.Length; ++i) {
1213             char ch = name[i];
1214             switch(ch) {
1215             case '.':   //note _all_ dots name is an error
1216                         continue;
1217             case '-':   //if _all_ chars are those we call Dns (to confirm error)
1218             case '_':
1219             case '*':   isDnsOrWC = true;
1220                         continue;
1221             case ':':
1222             case '%':   isIPv6 = true;
1223                         continue;
1224             default:    break;
1225             }
1226
1227             //Check on letters but NOT hex digits
1228             if ((ch > 'f' && ch <= 'z') || (ch > 'F' && ch <= 'Z')) {
1229                 isDnsOrWC = true;
1230                 continue;
1231             }
1232             //Check on HEX letters
1233             if((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
1234                 isHexLetter = true;
1235                 continue;
1236             }
1237             //Here only digits left (others are invalid)
1238             if (!(ch >= '0' && ch <= '9'))
1239                 return EndPointType.Invalid;
1240         }
1241
1242         // The logic is (solely for the purpose of SocketPermssion class)
1243         //  isIPv6 && isDnsOrWC   = EndPointType.Invalid
1244         //  isIPv6 && !isDnsOrWC  = EndPointType.IPv6
1245         //  !isIPv6 && isDnsOrWC  = EndPointType.DnsOrWildcard
1246         //  !isIPv6 && !isDnsOrWC && isHexLetter = EndPointType.DnsOrWildcard;
1247         //  else = EndPointType.IPv4
1248         return isIPv6 ? (isDnsOrWC? EndPointType.Invalid: EndPointType.IPv6)
1249                       : (isDnsOrWC? EndPointType.DnsOrWildcard :
1250                                     isHexLetter? EndPointType.DnsOrWildcard :EndPointType.IPv4);
1251     }
1252
1253
1254     } // class EndpointPermission
1255
1256
1257 } // namespace System.Net