ClientAccessPolicyParser.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. //
  2. // ClientAccessPolicyParser.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.Linq;
  34. using System.Xml;
  35. /*
  36. default namespace = ""
  37. grammar {
  38. start = access-policy
  39. access-policy = element access-policy {
  40. element cross-domain-access {
  41. element policy { allow-from, grant-to }
  42. }
  43. }
  44. allow-from = element allow-from {
  45. attribute http-request-headers { text },
  46. element domain {
  47. attribute uri { text }
  48. }
  49. }
  50. grant-to = element grant-to {
  51. (resource | socket-resource)+
  52. }
  53. resource = element resource {
  54. attribute path { text },
  55. attribute include-subpaths { "true" | "false" }
  56. }
  57. socket-resource = element socket-resource {
  58. attribute port { text },
  59. attribute protocol { text }
  60. }
  61. }
  62. */
  63. namespace System.Net.Policy {
  64. partial class ClientAccessPolicy {
  65. static public ICrossDomainPolicy FromStream (Stream stream)
  66. {
  67. ClientAccessPolicy cap = new ClientAccessPolicy ();
  68. // Silverlight accepts whitespaces before the XML - which is invalid XML
  69. StreamReader sr = new StreamReader (stream);
  70. while (Char.IsWhiteSpace ((char) sr.Peek ()))
  71. sr.Read ();
  72. XmlReaderSettings policy_settings = new XmlReaderSettings ();
  73. policy_settings.DtdProcessing = DtdProcessing.Ignore;
  74. using (XmlReader reader = XmlReader.Create (sr, policy_settings)) {
  75. reader.MoveToContent ();
  76. if (reader.IsEmptyElement) {
  77. reader.Skip ();
  78. return null;
  79. }
  80. reader.ReadStartElement ("access-policy", String.Empty);
  81. for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
  82. if (reader.NodeType != XmlNodeType.Element)
  83. throw new XmlException (String.Format ("Unexpected access-policy content: {0}", reader.NodeType));
  84. if ((reader.LocalName != "cross-domain-access") || reader.IsEmptyElement) {
  85. reader.Skip ();
  86. continue;
  87. }
  88. reader.ReadStartElement ("cross-domain-access", String.Empty);
  89. for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
  90. if (reader.NodeType != XmlNodeType.Element)
  91. throw new XmlException (String.Format ("Unexpected access-policy content: {0}", reader.NodeType));
  92. if ((reader.Name != "policy") || reader.IsEmptyElement) {
  93. reader.Skip ();
  94. continue;
  95. }
  96. ReadPolicyElement (reader, cap);
  97. }
  98. reader.ReadEndElement ();
  99. }
  100. reader.ReadEndElement ();
  101. }
  102. return cap;
  103. }
  104. static void ReadPolicyElement (XmlReader reader, ClientAccessPolicy cap)
  105. {
  106. if (reader.HasAttributes || reader.IsEmptyElement) {
  107. reader.Skip ();
  108. return;
  109. }
  110. var policy = new AccessPolicy ();
  111. bool valid = true;
  112. reader.ReadStartElement ("policy", String.Empty);
  113. for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
  114. if (reader.NodeType != XmlNodeType.Element)
  115. throw new XmlException (String.Format ("Unexpected policy content: {0}", reader.NodeType));
  116. switch (reader.LocalName) {
  117. case "allow-from":
  118. ReadAllowFromElement (reader, policy);
  119. break;
  120. case "grant-to":
  121. ReadGrantToElement (reader, policy);
  122. break;
  123. default:
  124. valid = false;
  125. reader.Skip ();
  126. break;
  127. }
  128. }
  129. if (valid)
  130. cap.AccessPolicyList.Add (policy);
  131. reader.ReadEndElement ();
  132. }
  133. static void ReadAllowFromElement (XmlReader reader, AccessPolicy policy)
  134. {
  135. if (reader.IsEmptyElement) {
  136. reader.Skip ();
  137. return;
  138. }
  139. bool valid = true;
  140. string headers = null;
  141. string methods = null; // new in SL3
  142. if (reader.HasAttributes) {
  143. int n = reader.AttributeCount;
  144. headers = reader.GetAttribute ("http-request-headers");
  145. if (headers != null)
  146. n--;
  147. methods = reader.GetAttribute ("http-methods");
  148. if (methods != null)
  149. n--;
  150. valid = (n == 0);
  151. }
  152. var v = new AllowFrom ();
  153. v.HttpRequestHeaders.SetHeaders (headers);
  154. v.AllowAnyMethod = (methods == "*"); // only legal value defined, otherwise restricted to GET and POST
  155. reader.ReadStartElement ("allow-from", String.Empty);
  156. for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
  157. if (reader.NodeType != XmlNodeType.Element)
  158. throw new XmlException (String.Format ("Unexpected allow-from content: {0}", reader.NodeType));
  159. if (!String.IsNullOrEmpty (reader.NamespaceURI)) {
  160. reader.Skip ();
  161. continue;
  162. }
  163. switch (reader.LocalName) {
  164. case "domain":
  165. var d = reader.GetAttribute ("uri");
  166. switch (d) {
  167. case "*":
  168. v.AllowAnyDomain = true;
  169. break;
  170. case "http://*":
  171. v.Scheme = "http";
  172. break;
  173. case "https://*":
  174. v.Scheme = "https";
  175. break;
  176. case "file:///":
  177. v.Scheme = "file";
  178. break;
  179. default:
  180. v.Domains.Add (d);
  181. break;
  182. }
  183. reader.Skip ();
  184. break;
  185. default:
  186. valid = false;
  187. reader.Skip ();
  188. continue;
  189. }
  190. }
  191. if (valid)
  192. policy.AllowedServices.Add (v);
  193. reader.ReadEndElement ();
  194. }
  195. // only "path" and "include-subpaths" attributes are allowed - anything else is not considered
  196. static Resource CreateResource (XmlReader reader)
  197. {
  198. int n = reader.AttributeCount;
  199. string path = reader.GetAttribute ("path");
  200. if (path != null)
  201. n--;
  202. string subpaths = reader.GetAttribute ("include-subpaths");
  203. if (subpaths != null)
  204. n--;
  205. if ((n != 0) || !reader.IsEmptyElement)
  206. return null;
  207. return new Resource () {
  208. Path = path,
  209. IncludeSubpaths = subpaths == null ? false : XmlConvert.ToBoolean (subpaths)
  210. };
  211. }
  212. static void ReadGrantToElement (XmlReader reader, AccessPolicy policy)
  213. {
  214. var v = new GrantTo ();
  215. bool valid = true;
  216. if (reader.HasAttributes || reader.IsEmptyElement) {
  217. reader.Skip ();
  218. return;
  219. }
  220. reader.ReadStartElement ("grant-to", String.Empty);
  221. for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
  222. if (reader.NodeType != XmlNodeType.Element)
  223. throw new XmlException (String.Format ("Unexpected grant-to content: {0}", reader.NodeType));
  224. if (!String.IsNullOrEmpty (reader.NamespaceURI)) {
  225. reader.Skip ();
  226. continue;
  227. }
  228. switch (reader.LocalName) {
  229. case "resource":
  230. var r = CreateResource (reader);
  231. if (r == null)
  232. valid = false;
  233. else
  234. v.Resources.Add (r);
  235. break;
  236. case "socket-resource":
  237. // ignore everything that is not TCP
  238. if (reader.GetAttribute ("protocol") != "tcp")
  239. break;
  240. // we can merge them all together inside a policy
  241. policy.PortMask |= ParsePorts (reader.GetAttribute ("port"));
  242. break;
  243. default:
  244. valid = false;
  245. break;
  246. }
  247. reader.Skip ();
  248. }
  249. if (valid)
  250. policy.GrantedResources.Add (v);
  251. reader.ReadEndElement ();
  252. }
  253. // e.g. reserved ? 4534-4502
  254. static long ParsePorts (string ports)
  255. {
  256. long mask = 0;
  257. int sep = ports.IndexOf ('-');
  258. if (sep >= 0) {
  259. // range
  260. ushort from = ParsePort (ports.Substring (0, sep));
  261. ushort to = ParsePort (ports.Substring (sep + 1));
  262. for (int port = from; port <= to; port++)
  263. mask |= (long) (1ul << (port - AccessPolicy.MinPort));
  264. } else {
  265. // single
  266. ushort port = ParsePort (ports);
  267. mask |= (long) (1ul << (port - AccessPolicy.MinPort));
  268. }
  269. return mask;
  270. }
  271. static ushort ParsePort (string s)
  272. {
  273. ushort port;
  274. if (!UInt16.TryParse (s, out port) || (port < AccessPolicy.MinPort) || (port > AccessPolicy.MaxPort))
  275. throw new XmlException ("Invalid port");
  276. return port;
  277. }
  278. }
  279. }
  280. #endif