2010-04-13 Sebastien Pouliot <sebastien@ximian.com>
[mono-project.git] / mcs / class / System.Net / System.Net.Policy / CrossDomainPolicyManager.cs
blobd89b970e850049274ce188e0c76dd4685973268c
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.
30 #if NET_2_1
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;
41 namespace System.Net.Policy {
43 internal static class CrossDomainPolicyManager {
45 public static string GetRoot (Uri uri)
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);
52 #if !TEST
53 public const string ClientAccessPolicyFile = "/clientaccesspolicy.xml";
54 public const string CrossDomainFile = "/crossdomain.xml";
56 const int Timeout = 10000;
58 // Web Access Policy
60 static Dictionary<string,ICrossDomainPolicy> policies = new Dictionary<string,ICrossDomainPolicy> ();
62 static internal ICrossDomainPolicy PolicyDownloadPolicy = new PolicyDownloadPolicy ();
63 static ICrossDomainPolicy site_of_origin_policy = new SiteOfOriginPolicy ();
64 static ICrossDomainPolicy no_access_policy = new NoAccessPolicy ();
66 static Uri GetRootUri (Uri uri)
68 return new Uri (GetRoot (uri));
71 public static Uri GetSilverlightPolicyUri (Uri uri)
73 return new Uri (GetRootUri (uri), CrossDomainPolicyManager.ClientAccessPolicyFile);
76 public static Uri GetFlashPolicyUri (Uri uri)
78 return new Uri (GetRootUri (uri), CrossDomainPolicyManager.CrossDomainFile);
81 public static ICrossDomainPolicy GetCachedWebPolicy (Uri uri)
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;
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;
95 private static void AddPolicy (Uri responseUri, ICrossDomainPolicy policy)
97 string root = GetRoot (responseUri);
98 policies [root] = policy;
101 // see moon/test/2.0/WebPolicies/Pages.xaml.cs for all test cases
102 private static bool CheckContentType (string contentType)
104 const string application_xml = "application/xml";
106 // most common case: all text/* are accepted
107 if (contentType.StartsWith ("text/"))
108 return true;
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
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] == ';';
119 return false;
122 public static ICrossDomainPolicy BuildSilverlightPolicy (HttpWebResponse response)
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;
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.
138 return policy;
141 public static ICrossDomainPolicy BuildFlashPolicy (HttpWebResponse response)
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.
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;
160 // the flash policy was the last chance, keep a NoAccess into the cache
161 if (policy == null)
162 policy = no_access_policy;
164 AddPolicy (response.ResponseUri, policy);
165 return policy;
168 // Socket Policy
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
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;
181 // make sure this work in a IPv6-only environment
182 static AddressFamily GetBestFamily ()
184 if (Socket.OSSupportsIPv4)
185 return AddressFamily.InterNetwork;
186 else if (Socket.OSSupportsIPv6)
187 return AddressFamily.InterNetworkV6;
188 else
189 return AddressFamily.Unspecified;
192 static Stream GetPolicyStream (IPEndPoint endpoint)
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);
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;
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));
225 if ((transfer == 0) || (transfer < e.Buffer.Length)) {
226 ms.Position = 0;
227 mre.Set ();
228 } else {
229 socket.ReceiveAsync (e);
231 break;
235 socket.ConnectAsync (saea);
237 // behave like there's no policy (no socket access) if we timeout
238 if (!mre.WaitOne (Timeout))
239 return null;
241 return ms;
244 public static ClientAccessPolicy CreateForEndPoint (IPEndPoint endpoint)
246 Stream s = GetPolicyStream (endpoint);
247 if (s == null)
248 return null;
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.
259 return policy;
262 static public bool CheckEndPoint (EndPoint endpoint)
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");
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);
277 // no access granted if no policy is available
278 if (policy == null)
279 return false;
281 // does the policy allows access ?
282 return policy.IsAllowed (ip);
284 #endif
288 #endif