ClientAccessPolicyParser.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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. if (d == "*")
  167. v.AllowAnyDomain = true;
  168. else
  169. v.Domains.Add (d);
  170. reader.Skip ();
  171. break;
  172. default:
  173. valid = false;
  174. reader.Skip ();
  175. continue;
  176. }
  177. }
  178. if (valid)
  179. policy.AllowedServices.Add (v);
  180. reader.ReadEndElement ();
  181. }
  182. // only "path" and "include-subpaths" attributes are allowed - anything else is not considered
  183. static Resource CreateResource (XmlReader reader)
  184. {
  185. int n = reader.AttributeCount;
  186. string path = reader.GetAttribute ("path");
  187. if (path != null)
  188. n--;
  189. string subpaths = reader.GetAttribute ("include-subpaths");
  190. if (subpaths != null)
  191. n--;
  192. if ((n != 0) || !reader.IsEmptyElement)
  193. return null;
  194. return new Resource () {
  195. Path = path,
  196. IncludeSubpaths = subpaths == null ? false : XmlConvert.ToBoolean (subpaths)
  197. };
  198. }
  199. static void ReadGrantToElement (XmlReader reader, AccessPolicy policy)
  200. {
  201. var v = new GrantTo ();
  202. bool valid = true;
  203. if (reader.HasAttributes || reader.IsEmptyElement) {
  204. reader.Skip ();
  205. return;
  206. }
  207. reader.ReadStartElement ("grant-to", String.Empty);
  208. for (reader.MoveToContent (); reader.NodeType != XmlNodeType.EndElement; reader.MoveToContent ()) {
  209. if (reader.NodeType != XmlNodeType.Element)
  210. throw new XmlException (String.Format ("Unexpected grant-to content: {0}", reader.NodeType));
  211. if (!String.IsNullOrEmpty (reader.NamespaceURI)) {
  212. reader.Skip ();
  213. continue;
  214. }
  215. switch (reader.LocalName) {
  216. case "resource":
  217. var r = CreateResource (reader);
  218. if (r == null)
  219. valid = false;
  220. else
  221. v.Resources.Add (r);
  222. break;
  223. case "socket-resource":
  224. // ignore everything that is not TCP
  225. if (reader.GetAttribute ("protocol") != "tcp")
  226. break;
  227. // we can merge them all together inside a policy
  228. policy.PortMask |= ParsePorts (reader.GetAttribute ("port"));
  229. break;
  230. default:
  231. valid = false;
  232. break;
  233. }
  234. reader.Skip ();
  235. }
  236. if (valid)
  237. policy.GrantedResources.Add (v);
  238. reader.ReadEndElement ();
  239. }
  240. // e.g. reserved ? 4534-4502
  241. static long ParsePorts (string ports)
  242. {
  243. long mask = 0;
  244. int sep = ports.IndexOf ('-');
  245. if (sep >= 0) {
  246. // range
  247. ushort from = ParsePort (ports.Substring (0, sep));
  248. ushort to = ParsePort (ports.Substring (sep + 1));
  249. for (int port = from; port <= to; port++)
  250. mask |= (long) (1ul << (port - AccessPolicy.MinPort));
  251. } else {
  252. // single
  253. ushort port = ParsePort (ports);
  254. mask |= (long) (1ul << (port - AccessPolicy.MinPort));
  255. }
  256. return mask;
  257. }
  258. static ushort ParsePort (string s)
  259. {
  260. ushort port;
  261. if (!UInt16.TryParse (s, out port) || (port < AccessPolicy.MinPort) || (port > AccessPolicy.MaxPort))
  262. throw new XmlException ("Invalid port");
  263. return port;
  264. }
  265. }
  266. }
  267. #endif