CrossDomainPolicyManager.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //
  2. // CrossDomainPolicyManager.cs
  3. //
  4. // Authors:
  5. // Atsushi Enomoto <[email protected]>
  6. // Moonlight List ([email protected])
  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. #if NET_2_1
  30. using System;
  31. using System.Collections.Generic;
  32. using System.IO;
  33. using System.Net.Sockets;
  34. using System.Reflection;
  35. using System.Security;
  36. using System.Text;
  37. using System.Threading;
  38. namespace System.Net.Policy {
  39. internal static class CrossDomainPolicyManager {
  40. public static string GetRoot (Uri uri)
  41. {
  42. if ((uri.Scheme == "http" && uri.Port == 80) || (uri.Scheme == "https" && uri.Port == 443) || (uri.Port == -1))
  43. return String.Format ("{0}://{1}/", uri.Scheme, uri.DnsSafeHost);
  44. else
  45. return String.Format ("{0}://{1}:{2}/", uri.Scheme, uri.DnsSafeHost, uri.Port);
  46. }
  47. #if !TEST
  48. public const string ClientAccessPolicyFile = "/clientaccesspolicy.xml";
  49. public const string CrossDomainFile = "/crossdomain.xml";
  50. const int Timeout = 10000;
  51. // Web Access Policy
  52. static Dictionary<string,ICrossDomainPolicy> policies = new Dictionary<string,ICrossDomainPolicy> ();
  53. static internal ICrossDomainPolicy PolicyDownloadPolicy = new PolicyDownloadPolicy ();
  54. static ICrossDomainPolicy site_of_origin_policy = new SiteOfOriginPolicy ();
  55. static ICrossDomainPolicy no_access_policy = new NoAccessPolicy ();
  56. static Uri GetRootUri (Uri uri)
  57. {
  58. return new Uri (GetRoot (uri));
  59. }
  60. public static Uri GetSilverlightPolicyUri (Uri uri)
  61. {
  62. return new Uri (GetRootUri (uri), CrossDomainPolicyManager.ClientAccessPolicyFile);
  63. }
  64. public static Uri GetFlashPolicyUri (Uri uri)
  65. {
  66. return new Uri (GetRootUri (uri), CrossDomainPolicyManager.CrossDomainFile);
  67. }
  68. public static ICrossDomainPolicy GetCachedWebPolicy (Uri uri)
  69. {
  70. // if we request an Uri from the same site then we return an "always positive" policy
  71. if (SiteOfOriginPolicy.HasSameOrigin (uri, BaseDomainPolicy.ApplicationUri))
  72. return site_of_origin_policy;
  73. // otherwise we search for an already downloaded policy for the web site
  74. string root = GetRoot (uri);
  75. ICrossDomainPolicy policy = null;
  76. policies.TryGetValue (root, out policy);
  77. // and we return it (if we have it) or null (if we dont)
  78. return policy;
  79. }
  80. private static void AddPolicy (Uri responseUri, ICrossDomainPolicy policy)
  81. {
  82. string root = GetRoot (responseUri);
  83. policies [root] = policy;
  84. }
  85. // see moon/test/2.0/WebPolicies/Pages.xaml.cs for all test cases
  86. private static bool CheckContentType (string contentType)
  87. {
  88. const string application_xml = "application/xml";
  89. // most common case: all text/* are accepted
  90. if (contentType.StartsWith ("text/"))
  91. return true;
  92. // special case (e.g. used in nbcolympics)
  93. if (contentType.StartsWith (application_xml)) {
  94. if (application_xml.Length == contentType.Length)
  95. return true; // exact match
  96. // e.g. "application/xml; charset=x" - we do not care what comes after ';'
  97. if (contentType.Length > application_xml.Length)
  98. return contentType [application_xml.Length] == ';';
  99. }
  100. return false;
  101. }
  102. public static ICrossDomainPolicy BuildSilverlightPolicy (HttpWebResponse response)
  103. {
  104. // return null if no Silverlight policy was found, since we offer a second chance with a flash policy
  105. if ((response.StatusCode != HttpStatusCode.OK) || !CheckContentType (response.ContentType))
  106. return null;
  107. ICrossDomainPolicy policy = null;
  108. try {
  109. policy = ClientAccessPolicy.FromStream (response.GetResponseStream ());
  110. if (policy != null)
  111. AddPolicy (response.ResponseUri, policy);
  112. } catch (Exception ex) {
  113. Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}",
  114. response.ResponseUri, ex));
  115. // and ignore.
  116. }
  117. return policy;
  118. }
  119. public static ICrossDomainPolicy BuildFlashPolicy (HttpWebResponse response)
  120. {
  121. ICrossDomainPolicy policy = null;
  122. if ((response.StatusCode == HttpStatusCode.OK) && CheckContentType (response.ContentType)) {
  123. try {
  124. policy = FlashCrossDomainPolicy.FromStream (response.GetResponseStream ());
  125. } catch (Exception ex) {
  126. Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}",
  127. response.ResponseUri, ex));
  128. // and ignore.
  129. }
  130. if (policy != null) {
  131. // see DRT# 864 and 865
  132. string site_control = response.Headers ["X-Permitted-Cross-Domain-Policies"];
  133. if (!String.IsNullOrEmpty (site_control))
  134. (policy as FlashCrossDomainPolicy).SiteControl = site_control;
  135. }
  136. }
  137. // the flash policy was the last chance, keep a NoAccess into the cache
  138. if (policy == null)
  139. policy = no_access_policy;
  140. AddPolicy (response.ResponseUri, policy);
  141. return policy;
  142. }
  143. // Socket Policy
  144. //
  145. // - we connect once to a site for the entire application life time
  146. // - this returns us a policy file (silverlight format only) or else no access is granted
  147. // - this policy file
  148. // - can contain multiple policies
  149. // - can apply to multiple domains
  150. // - can grant access to several resources
  151. static Dictionary<string,ClientAccessPolicy> socket_policies = new Dictionary<string,ClientAccessPolicy> ();
  152. static byte [] socket_policy_file_request = Encoding.UTF8.GetBytes ("<policy-file-request/>");
  153. const int PolicyPort = 943;
  154. // make sure this work in a IPv6-only environment
  155. static AddressFamily GetBestFamily ()
  156. {
  157. if (Socket.OSSupportsIPv4)
  158. return AddressFamily.InterNetwork;
  159. else if (Socket.OSSupportsIPv6)
  160. return AddressFamily.InterNetworkV6;
  161. else
  162. return AddressFamily.Unspecified;
  163. }
  164. static Stream GetPolicyStream (IPEndPoint endpoint)
  165. {
  166. MemoryStream ms = new MemoryStream ();
  167. ManualResetEvent mre = new ManualResetEvent (false);
  168. // Silverlight only support TCP
  169. Socket socket = new Socket (GetBestFamily (), SocketType.Stream, ProtocolType.Tcp);
  170. // Application code can't connect to port 943, so we need a special/internal API/ctor to allow this
  171. SocketAsyncEventArgs saea = new SocketAsyncEventArgs (true);
  172. saea.RemoteEndPoint = new IPEndPoint (endpoint.Address, PolicyPort);
  173. saea.Completed += delegate (object sender, SocketAsyncEventArgs e) {
  174. if (e.SocketError != SocketError.Success) {
  175. mre.Set ();
  176. return;
  177. }
  178. switch (e.LastOperation) {
  179. case SocketAsyncOperation.Connect:
  180. e.SetBuffer (socket_policy_file_request, 0, socket_policy_file_request.Length);
  181. socket.SendAsync (e);
  182. break;
  183. case SocketAsyncOperation.Send:
  184. byte [] buffer = new byte [256];
  185. e.SetBuffer (buffer, 0, buffer.Length);
  186. socket.ReceiveAsync (e);
  187. break;
  188. case SocketAsyncOperation.Receive:
  189. int transfer = e.BytesTransferred;
  190. if (transfer > 0) {
  191. ms.Write (e.Buffer, 0, transfer);
  192. // Console.Write (Encoding.UTF8.GetString (e.Buffer, 0, transfer));
  193. }
  194. if ((transfer == 0) || (transfer < e.Buffer.Length)) {
  195. ms.Position = 0;
  196. mre.Set ();
  197. } else {
  198. socket.ReceiveAsync (e);
  199. }
  200. break;
  201. }
  202. };
  203. socket.ConnectAsync (saea);
  204. // behave like there's no policy (no socket access) if we timeout
  205. if (!mre.WaitOne (Timeout))
  206. return null;
  207. return ms;
  208. }
  209. static Stream GetPolicyStream (Uri uri)
  210. {
  211. // FIXME
  212. throw new NotSupportedException ("Fetching socket policy from " + uri.ToString () + " is not yet available in moonlight");
  213. }
  214. public static ClientAccessPolicy CreateForEndPoint (IPEndPoint endpoint, SocketClientAccessPolicyProtocol protocol)
  215. {
  216. Stream s = null;
  217. switch (protocol) {
  218. case SocketClientAccessPolicyProtocol.Tcp:
  219. s = GetPolicyStream (endpoint);
  220. break;
  221. case SocketClientAccessPolicyProtocol.Http:
  222. // <quote>It will NOT attempt to download the policy via the custom TCP protocol if the
  223. // policy check fails.</quote>
  224. // http://blogs.msdn.com/ncl/archive/2010/04/15/silverlight-4-socket-policy-changes.aspx
  225. string url = String.Format ("http://{0}:80{1}", endpoint.Address.ToString (),
  226. CrossDomainPolicyManager.ClientAccessPolicyFile);
  227. s = GetPolicyStream (new Uri (url));
  228. break;
  229. }
  230. if (s == null)
  231. return null;
  232. ClientAccessPolicy policy = null;
  233. try {
  234. policy = (ClientAccessPolicy) ClientAccessPolicy.FromStream (s);
  235. } catch (Exception ex) {
  236. Console.WriteLine (String.Format ("CrossDomainAccessManager caught an exception while reading {0}: {1}",
  237. endpoint, ex.Message));
  238. // and ignore.
  239. }
  240. return policy;
  241. }
  242. static public bool CheckEndPoint (EndPoint endpoint, SocketClientAccessPolicyProtocol protocol)
  243. {
  244. // if needed transform the DnsEndPoint into a usable IPEndPoint
  245. IPEndPoint ip = (endpoint as IPEndPoint);
  246. if (ip == null)
  247. throw new ArgumentException ("endpoint");
  248. // find the policy (cached or to be downloaded) associated with the endpoint
  249. string address = ip.Address.ToString ();
  250. ClientAccessPolicy policy = null;
  251. if (!socket_policies.TryGetValue (address, out policy)) {
  252. policy = CreateForEndPoint (ip, protocol);
  253. socket_policies.Add (address, policy);
  254. }
  255. // no access granted if no policy is available
  256. if (policy == null)
  257. return false;
  258. // does the policy allows access ?
  259. return policy.IsAllowed (ip);
  260. }
  261. #endif
  262. }
  263. }
  264. #endif