copied mono-api-diff.cs from mono-2-2 branch so new patch can be applied and history...
[mono.git] / mcs / class / System.Net / System.Net.Policy / CrossDomainPolicyManager.cs
1 //
2 // CrossDomainPolicyManager.cs
3 //
4 // Authors:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Moonlight List (moonlight-list@lists.ximian.com)
7 //
8 // Copyright (C) 2009-2010 Novell, Inc.  http://www.novell.com
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 #if NET_2_1
31
32 using System;
33 using System.Collections.Generic;
34 using System.IO;
35 using System.Net.Sockets;
36 using System.Reflection;
37 using System.Security;
38 using System.Text;
39 using System.Threading;
40
41 namespace System.Net.Policy {
42
43         internal static class CrossDomainPolicyManager {
44
45                 public static string GetRoot (Uri uri)
46                 {
47                         if ((uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443) || (uri.Port == -1))
48                                 return String.Format ("{0}://{1}/", uri.Scheme, uri.DnsSafeHost);
49                         else
50                                 return String.Format ("{0}://{1}:{2}/", uri.Scheme, uri.DnsSafeHost, uri.Port);
51                 }
52 #if !TEST
53                 public const string ClientAccessPolicyFile = "/clientaccesspolicy.xml";
54                 public const string CrossDomainFile = "/crossdomain.xml";
55
56                 const int Timeout = 10000;
57
58                 // Web Access Policy
59
60                 static Dictionary<string,ICrossDomainPolicy> policies = new Dictionary<string,ICrossDomainPolicy> ();
61
62                 static internal ICrossDomainPolicy PolicyDownloadPolicy = new PolicyDownloadPolicy ();
63                 static ICrossDomainPolicy site_of_origin_policy = new SiteOfOriginPolicy ();
64                 static ICrossDomainPolicy no_access_policy = new NoAccessPolicy ();
65
66                 static Uri GetRootUri (Uri uri)
67                 {
68                         return new Uri (GetRoot (uri));
69                 }
70
71                 public static Uri GetSilverlightPolicyUri (Uri uri)
72                 {
73                         return new Uri (GetRootUri (uri), CrossDomainPolicyManager.ClientAccessPolicyFile);
74                 }
75
76                 public static Uri GetFlashPolicyUri (Uri uri)
77                 {
78                         return new Uri (GetRootUri (uri), CrossDomainPolicyManager.CrossDomainFile);
79                 }
80
81                 public static ICrossDomainPolicy GetCachedWebPolicy (Uri uri)
82                 {
83                         // if we request an Uri from the same site then we return an "always positive" policy
84                         if (SiteOfOriginPolicy.HasSameOrigin (uri, BaseDomainPolicy.ApplicationUri))
85                                 return site_of_origin_policy;
86
87                         // otherwise we search for an already downloaded policy for the web site
88                         string root = GetRoot (uri);
89                         ICrossDomainPolicy policy = null;
90                         policies.TryGetValue (root, out policy);
91                         // and we return it (if we have it) or null (if we dont)
92                         return policy;
93                 }
94
95                 private static void AddPolicy (Uri responseUri, ICrossDomainPolicy policy)
96                 {
97                         string root = GetRoot (responseUri);
98                         policies [root] = policy;
99                 }
100
101                 // see moon/test/2.0/WebPolicies/Pages.xaml.cs for all test cases
102                 private static bool CheckContentType (string contentType)
103                 {
104                         const string application_xml = "application/xml";
105
106                         // most common case: all text/* are accepted
107                         if (contentType.StartsWith ("text/"))
108                                 return true;
109
110                         // special case (e.g. used in nbcolympics)
111                         if (contentType.StartsWith (application_xml)) {
112                                 if (application_xml.Length == contentType.Length)
113                                         return true; // exact match
114
115                                 // e.g. "application/xml; charset=x" - we do not care what comes after ';'
116                                 if (contentType.Length > application_xml.Length)
117                                         return contentType [application_xml.Length] == ';';
118                         }
119                         return false;
120                 }
121
122                 public static ICrossDomainPolicy BuildSilverlightPolicy (HttpWebResponse response)
123                 {
124                         // return null if no Silverlight policy was found, since we offer a second chance with a flash policy
125                         if ((response.StatusCode != HttpStatusCode.OK) || !CheckContentType (response.ContentType))
126                                 return null;
127
128                         ICrossDomainPolicy policy = null;
129                         try {
130                                 policy = ClientAccessPolicy.FromStream (response.GetResponseStream ());
131                                 if (policy != null)
132                                         AddPolicy (response.ResponseUri, policy);
133                         } catch (Exception ex) {
134                                 Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}", 
135                                         response.ResponseUri, ex));
136                                 // and ignore.
137                         }
138                         return policy;
139                 }
140
141                 public static ICrossDomainPolicy BuildFlashPolicy (HttpWebResponse response)
142                 {
143                         ICrossDomainPolicy policy = null;
144                         if ((response.StatusCode == HttpStatusCode.OK) && CheckContentType (response.ContentType)) {
145                                 try {
146                                         policy = FlashCrossDomainPolicy.FromStream (response.GetResponseStream ());
147                                 } catch (Exception ex) {
148                                         Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}", 
149                                                 response.ResponseUri, ex));
150                                         // and ignore.
151                                 }
152                                 if (policy != null) {
153                                         // see DRT# 864 and 865
154                                         string site_control = response.Headers ["X-Permitted-Cross-Domain-Policies"];
155                                         if (!String.IsNullOrEmpty (site_control))
156                                                 (policy as FlashCrossDomainPolicy).SiteControl = site_control;
157                                 }
158                         }
159
160                         // the flash policy was the last chance, keep a NoAccess into the cache
161                         if (policy == null)
162                                 policy = no_access_policy;
163
164                         AddPolicy (response.ResponseUri, policy);
165                         return policy;
166                 }
167
168                 // Socket Policy
169                 //
170                 // - we connect once to a site for the entire application life time
171                 // - this returns us a policy file (silverlight format only) or else no access is granted
172                 // - this policy file
173                 //      - can contain multiple policies
174                 //      - can apply to multiple domains
175                 //      - can grant access to several resources
176
177                 static Dictionary<string,ClientAccessPolicy> socket_policies = new Dictionary<string,ClientAccessPolicy> ();
178                 static byte [] socket_policy_file_request = Encoding.UTF8.GetBytes ("<policy-file-request/>");
179                 const int PolicyPort = 943;
180
181                 // make sure this work in a IPv6-only environment
182                 static AddressFamily GetBestFamily ()
183                 {
184                         if (Socket.OSSupportsIPv4)
185                                 return AddressFamily.InterNetwork;
186                         else if (Socket.OSSupportsIPv6)
187                                 return AddressFamily.InterNetworkV6;
188                         else
189                                 return AddressFamily.Unspecified;
190                 }
191
192                 static Stream GetPolicyStream (IPEndPoint endpoint)
193                 {
194                         MemoryStream ms = new MemoryStream ();
195                         ManualResetEvent mre = new ManualResetEvent (false);
196                         // Silverlight only support TCP
197                         Socket socket = new Socket (GetBestFamily (), SocketType.Stream, ProtocolType.Tcp);
198
199                         // Application code can't connect to port 943, so we need a special/internal API/ctor to allow this
200                         SocketAsyncEventArgs saea = new SocketAsyncEventArgs (true);
201                         saea.RemoteEndPoint = new IPEndPoint (endpoint.Address, PolicyPort);
202                         saea.Completed += delegate (object sender, SocketAsyncEventArgs e) {
203                                 if (e.SocketError != SocketError.Success) {
204                                         mre.Set ();
205                                         return;
206                                 }
207
208                                 switch (e.LastOperation) {
209                                 case SocketAsyncOperation.Connect:
210                                         e.SetBuffer (socket_policy_file_request, 0, socket_policy_file_request.Length);
211                                         socket.SendAsync (e);
212                                         break;
213                                 case SocketAsyncOperation.Send:
214                                         byte [] buffer = new byte [256];
215                                         e.SetBuffer (buffer, 0, buffer.Length);
216                                         socket.ReceiveAsync (e);
217                                         break;
218                                 case SocketAsyncOperation.Receive:
219                                         int transfer = e.BytesTransferred;
220                                         if (transfer > 0) {
221                                                 ms.Write (e.Buffer, 0, transfer);
222                                                 // Console.Write (Encoding.UTF8.GetString (e.Buffer, 0, transfer));
223                                         }
224
225                                         if ((transfer == 0) || (transfer < e.Buffer.Length)) {
226                                                 ms.Position = 0;
227                                                 mre.Set ();
228                                         } else {
229                                                 socket.ReceiveAsync (e);
230                                         }
231                                         break;
232                                 }
233                         };
234
235                         socket.ConnectAsync (saea);
236
237                         // behave like there's no policy (no socket access) if we timeout
238                         if (!mre.WaitOne (Timeout))
239                                 return null;
240
241                         return ms;
242                 }
243
244                 public static ClientAccessPolicy CreateForEndPoint (IPEndPoint endpoint)
245                 {
246                         Stream s = GetPolicyStream (endpoint);
247                         if (s == null)
248                                 return null;
249
250                         ClientAccessPolicy policy = null;
251                         try {
252                                 policy = (ClientAccessPolicy) ClientAccessPolicy.FromStream (s);
253                         } catch (Exception ex) {
254                                 Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}", 
255                                         endpoint, ex.Message));
256                                 // and ignore.
257                         }
258
259                         return policy;
260                 }
261
262                 static public bool CheckEndPoint (EndPoint endpoint)
263                 {
264                         // if needed transform the DnsEndPoint into a usable IPEndPoint
265                         IPEndPoint ip = (endpoint as IPEndPoint);
266                         if (ip == null)
267                                 throw new ArgumentException ("endpoint");
268
269                         // find the policy (cached or to be downloaded) associated with the endpoint
270                         string address = ip.Address.ToString ();
271                         ClientAccessPolicy policy = null;
272                         if (!socket_policies.TryGetValue (address, out policy)) {
273                                 policy = CreateForEndPoint (ip);
274                                 socket_policies.Add (address, policy);
275                         }
276
277                         // no access granted if no policy is available
278                         if (policy == null)
279                                 return false;
280
281                         // does the policy allows access ?
282                         return policy.IsAllowed (ip);
283                 }
284 #endif
285         }
286 }
287
288 #endif
289