nsSecurityHeaderParser.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include "nsSecurityHeaderParser.h"
  5. #include "mozilla/Logging.h"
  6. // The character classes in this file are informed by [RFC2616], Section 2.2.
  7. // signed char is a signed data type one byte (8 bits) wide, so its value can
  8. // never be greater than 127. The following implicitly makes use of this.
  9. // A token is one or more CHAR except CTLs or separators.
  10. // A CHAR is any US-ASCII character (octets 0 - 127).
  11. // A CTL is any US-ASCII control character (octets 0 - 31) and DEL (127).
  12. // A separator is one of ()<>@,;:\"/[]?={} as well as space and
  13. // horizontal-tab (32 and 9, respectively).
  14. // So, this returns true if chr is any octet 33-126 except ()<>@,;:\"/[]?={}
  15. bool
  16. IsTokenSymbol(signed char chr) {
  17. if (chr < 33 || chr == 127 ||
  18. chr == '(' || chr == ')' || chr == '<' || chr == '>' ||
  19. chr == '@' || chr == ',' || chr == ';' || chr == ':' ||
  20. chr == '"' || chr == '/' || chr == '[' || chr == ']' ||
  21. chr == '?' || chr == '=' || chr == '{' || chr == '}' || chr == '\\') {
  22. return false;
  23. }
  24. return true;
  25. }
  26. // A quoted-string consists of a quote (") followed by any amount of
  27. // qdtext or quoted-pair, followed by a quote.
  28. // qdtext is any TEXT except a quote.
  29. // TEXT is any 8-bit octet except CTLs, but including LWS.
  30. // quoted-pair is a backslash (\) followed by a CHAR.
  31. // So, it turns out, \ can't really be a qdtext symbol for our purposes.
  32. // This returns true if chr is any octet 9,10,13,32-126 except <"> or "\"
  33. bool
  34. IsQuotedTextSymbol(signed char chr) {
  35. return ((chr >= 32 && chr != '"' && chr != '\\' && chr != 127) ||
  36. chr == 0x9 || chr == 0xa || chr == 0xd);
  37. }
  38. // The octet following the "\" in a quoted pair can be anything 0-127.
  39. bool
  40. IsQuotedPairSymbol(signed char chr) {
  41. return (chr >= 0);
  42. }
  43. static mozilla::LazyLogModule sSHParserLog("nsSecurityHeaderParser");
  44. #define SHPARSERLOG(args) MOZ_LOG(sSHParserLog, mozilla::LogLevel::Debug, args)
  45. nsSecurityHeaderParser::nsSecurityHeaderParser(const char *aHeader)
  46. : mCursor(aHeader)
  47. , mError(false)
  48. {
  49. }
  50. nsSecurityHeaderParser::~nsSecurityHeaderParser() {
  51. nsSecurityHeaderDirective *directive;
  52. while ((directive = mDirectives.popFirst())) {
  53. delete directive;
  54. }
  55. }
  56. mozilla::LinkedList<nsSecurityHeaderDirective> *
  57. nsSecurityHeaderParser::GetDirectives() {
  58. return &mDirectives;
  59. }
  60. nsresult
  61. nsSecurityHeaderParser::Parse() {
  62. MOZ_ASSERT(mDirectives.isEmpty());
  63. SHPARSERLOG(("trying to parse '%s'", mCursor));
  64. Header();
  65. // if we didn't consume the entire input, we were unable to parse it => error
  66. if (mError || *mCursor) {
  67. return NS_ERROR_FAILURE;
  68. } else {
  69. return NS_OK;
  70. }
  71. }
  72. bool
  73. nsSecurityHeaderParser::Accept(char aChr)
  74. {
  75. if (*mCursor == aChr) {
  76. Advance();
  77. return true;
  78. }
  79. return false;
  80. }
  81. bool
  82. nsSecurityHeaderParser::Accept(bool (*aClassifier) (signed char))
  83. {
  84. if (aClassifier(*mCursor)) {
  85. Advance();
  86. return true;
  87. }
  88. return false;
  89. }
  90. void
  91. nsSecurityHeaderParser::Expect(char aChr)
  92. {
  93. if (*mCursor != aChr) {
  94. mError = true;
  95. } else {
  96. Advance();
  97. }
  98. }
  99. void
  100. nsSecurityHeaderParser::Advance()
  101. {
  102. // Technically, 0 is valid in quoted-pair, but we were handed a
  103. // null-terminated const char *, so this doesn't handle that.
  104. if (*mCursor) {
  105. mOutput.Append(*mCursor);
  106. mCursor++;
  107. } else {
  108. mError = true;
  109. }
  110. }
  111. void
  112. nsSecurityHeaderParser::Header()
  113. {
  114. Directive();
  115. while (Accept(';')) {
  116. Directive();
  117. }
  118. }
  119. void
  120. nsSecurityHeaderParser::Directive()
  121. {
  122. mDirective = new nsSecurityHeaderDirective();
  123. LWSMultiple();
  124. DirectiveName();
  125. LWSMultiple();
  126. if (Accept('=')) {
  127. LWSMultiple();
  128. DirectiveValue();
  129. LWSMultiple();
  130. }
  131. mDirectives.insertBack(mDirective);
  132. SHPARSERLOG(("read directive name '%s', value '%s'",
  133. mDirective->mName.Data(), mDirective->mValue.Data()));
  134. }
  135. void
  136. nsSecurityHeaderParser::DirectiveName()
  137. {
  138. mOutput.Truncate(0);
  139. Token();
  140. mDirective->mName.Assign(mOutput);
  141. }
  142. void
  143. nsSecurityHeaderParser::DirectiveValue()
  144. {
  145. mOutput.Truncate(0);
  146. if (Accept(IsTokenSymbol)) {
  147. Token();
  148. mDirective->mValue.Assign(mOutput);
  149. } else if (Accept('"')) {
  150. // Accept advances the cursor if successful, which appends a character to
  151. // mOutput. The " is not part of what we want to capture, so truncate
  152. // mOutput again.
  153. mOutput.Truncate(0);
  154. QuotedString();
  155. mDirective->mValue.Assign(mOutput);
  156. Expect('"');
  157. }
  158. }
  159. void
  160. nsSecurityHeaderParser::Token()
  161. {
  162. while (Accept(IsTokenSymbol));
  163. }
  164. void
  165. nsSecurityHeaderParser::QuotedString()
  166. {
  167. while (true) {
  168. if (Accept(IsQuotedTextSymbol)) {
  169. QuotedText();
  170. } else if (Accept('\\')) {
  171. QuotedPair();
  172. } else {
  173. break;
  174. }
  175. }
  176. }
  177. void
  178. nsSecurityHeaderParser::QuotedText()
  179. {
  180. while (Accept(IsQuotedTextSymbol));
  181. }
  182. void
  183. nsSecurityHeaderParser::QuotedPair()
  184. {
  185. Accept(IsQuotedPairSymbol);
  186. }
  187. void
  188. nsSecurityHeaderParser::LWSMultiple()
  189. {
  190. while (true) {
  191. if (Accept('\r')) {
  192. LWSCRLF();
  193. } else if (Accept(' ') || Accept('\t')) {
  194. LWS();
  195. } else {
  196. break;
  197. }
  198. }
  199. }
  200. void
  201. nsSecurityHeaderParser::LWSCRLF() {
  202. Expect('\n');
  203. if (!(Accept(' ') || Accept('\t'))) {
  204. mError = true;
  205. }
  206. LWS();
  207. }
  208. void
  209. nsSecurityHeaderParser::LWS()
  210. {
  211. // Note that becaue of how we're called, we don't have to check for
  212. // the mandatory presense of at least one of SP or HT.
  213. while (Accept(' ') || Accept('\t'));
  214. }