ClientAccessPolicyParser.cs 8.1 KB

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