cow_http3.erl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. %% Copyright (c) 2023, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(cow_http3).
  15. -export([parse/1]).
  16. -export([parse_unidi_stream_header/1]).
  17. -spec parse(_) -> _. %% @todo
  18. %%
  19. %% DATA frames.
  20. %%
  21. parse(<<0, 0:2, Len:6, Data:Len/binary, Rest/bits>>) ->
  22. {ok, {data, Data}, Rest};
  23. parse(<<0, 1:2, Len:14, Data:Len/binary, Rest/bits>>) ->
  24. {ok, {data, Data}, Rest};
  25. parse(<<0, 2:2, Len:30, Data:Len/binary, Rest/bits>>) ->
  26. {ok, {data, Data}, Rest};
  27. parse(<<0, 3:2, Len:62, Data:Len/binary, Rest/bits>>) ->
  28. {ok, {data, Data}, Rest};
  29. %% DATA frames may be split over multiple QUIC packets
  30. %% but we want to process them immediately rather than
  31. %% risk buffering a very large payload.
  32. parse(<<0, 0:2, Len:6, Data/bits>>) when byte_size(Data) < Len ->
  33. {more, {data, Data}, Len - byte_size(Data)};
  34. parse(<<0, 1:2, Len:14, Data/bits>>) when byte_size(Data) < Len ->
  35. {more, {data, Data}, Len - byte_size(Data)};
  36. parse(<<0, 2:2, Len:30, Data/bits>>) when byte_size(Data) < Len ->
  37. {more, {data, Data}, Len - byte_size(Data)};
  38. parse(<<0, 3:2, Len:62, Data/bits>>) when byte_size(Data) < Len ->
  39. {more, {data, Data}, Len - byte_size(Data)};
  40. %%
  41. %% HEADERS frames.
  42. %%
  43. parse(<<1, 0:2, Len:6, EncodedFieldSection:Len/binary, Rest/bits>>) ->
  44. {ok, {headers, EncodedFieldSection}, Rest};
  45. parse(<<1, 1:2, Len:14, EncodedFieldSection:Len/binary, Rest/bits>>) ->
  46. {ok, {headers, EncodedFieldSection}, Rest};
  47. parse(<<1, 2:2, Len:30, EncodedFieldSection:Len/binary, Rest/bits>>) ->
  48. {ok, {headers, EncodedFieldSection}, Rest};
  49. parse(<<1, 3:2, Len:62, EncodedFieldSection:Len/binary, Rest/bits>>) ->
  50. {ok, {headers, EncodedFieldSection}, Rest};
  51. %%
  52. %% CANCEL_PUSH frames.
  53. %%
  54. parse(<<3, 0:2, 1:6, 0:2, PushID:6, Rest/bits>>) ->
  55. {ok, {cancel_push, PushID}, Rest};
  56. parse(<<3, 0:2, 2:6, 1:2, PushID:14, Rest/bits>>) ->
  57. {ok, {cancel_push, PushID}, Rest};
  58. parse(<<3, 0:2, 4:6, 2:2, PushID:30, Rest/bits>>) ->
  59. {ok, {cancel_push, PushID}, Rest};
  60. parse(<<3, 0:2, 8:6, 3:2, PushID:62, Rest/bits>>) ->
  61. {ok, {cancel_push, PushID}, Rest};
  62. parse(<<3, _/bits>>) ->
  63. {connection_error, h3_frame_error,
  64. 'CANCEL_PUSH frames payload MUST be 1, 2, 4 or 8 bytes wide. (RFC9114 7.1, RFC9114 7.2.3)'};
  65. %%
  66. %% SETTINGS frames.
  67. %%
  68. parse(<<4, 0:2, Len:6, Rest/bits>>) when byte_size(Rest) >= Len ->
  69. parse_settings_id(Rest, Len, #{});
  70. parse(<<4, 1:2, Len:14, Rest/bits>>) when byte_size(Rest) >= Len ->
  71. parse_settings_id(Rest, Len, #{});
  72. parse(<<4, 2:2, Len:30, Rest/bits>>) when byte_size(Rest) >= Len ->
  73. parse_settings_id(Rest, Len, #{});
  74. parse(<<4, 3:2, Len:62, Rest/bits>>) when byte_size(Rest) >= Len ->
  75. parse_settings_id(Rest, Len, #{});
  76. %%
  77. %% PUSH_PROMISE frames.
  78. %%
  79. parse(<<5, 0:2, Len:6, Rest/bits>>) when byte_size(Rest) >= Len ->
  80. parse_push_promise(Rest, Len);
  81. parse(<<5, 1:2, Len:14, Rest/bits>>) when byte_size(Rest) >= Len ->
  82. parse_push_promise(Rest, Len);
  83. parse(<<5, 2:2, Len:30, Rest/bits>>) when byte_size(Rest) >= Len ->
  84. parse_push_promise(Rest, Len);
  85. parse(<<5, 3:2, Len:62, Rest/bits>>) when byte_size(Rest) >= Len ->
  86. parse_push_promise(Rest, Len);
  87. %%
  88. %% GOAWAY frames.
  89. %%
  90. parse(<<7, 0:2, 1:6, 0:2, StreamOrPushID:6, Rest/bits>>) ->
  91. {ok, {goaway, StreamOrPushID}, Rest};
  92. parse(<<7, 0:2, 2:6, 1:2, StreamOrPushID:14, Rest/bits>>) ->
  93. {ok, {goaway, StreamOrPushID}, Rest};
  94. parse(<<7, 0:2, 4:6, 2:2, StreamOrPushID:30, Rest/bits>>) ->
  95. {ok, {goaway, StreamOrPushID}, Rest};
  96. parse(<<7, 0:2, 8:6, 3:2, StreamOrPushID:62, Rest/bits>>) ->
  97. {ok, {goaway, StreamOrPushID}, Rest};
  98. parse(<<7, _/bits>>) ->
  99. {connection_error, h3_frame_error,
  100. 'GOAWAY frames payload MUST be 1, 2, 4 or 8 bytes wide. (RFC9114 7.1, RFC9114 7.2.6)'};
  101. %%
  102. %% MAX_PUSH_ID frames.
  103. %%
  104. parse(<<13, 0:2, 1:6, 0:2, PushID:6, Rest/bits>>) ->
  105. {ok, {max_push_id, PushID}, Rest};
  106. parse(<<13, 0:2, 2:6, 1:2, PushID:14, Rest/bits>>) ->
  107. {ok, {max_push_id, PushID}, Rest};
  108. parse(<<13, 0:2, 4:6, 2:2, PushID:30, Rest/bits>>) ->
  109. {ok, {max_push_id, PushID}, Rest};
  110. parse(<<13, 0:2, 8:6, 3:2, PushID:62, Rest/bits>>) ->
  111. {ok, {max_push_id, PushID}, Rest};
  112. parse(<<13, _/bits>>) ->
  113. {connection_error, h3_frame_error,
  114. 'MAX_PUSH_ID frames payload MUST be 1, 2, 4 or 8 bytes wide. (RFC9114 7.1, RFC9114 7.2.6)'};
  115. %%
  116. %% HTTP/2 frame types must be rejected.
  117. %%
  118. parse(<<2, _/bits>>) ->
  119. {connection_error, h3_frame_unexpected,
  120. 'HTTP/2 PRIORITY frame not defined for HTTP/3 must be rejected. (RFC9114 7.2.8)'};
  121. parse(<<6, _/bits>>) ->
  122. {connection_error, h3_frame_unexpected,
  123. 'HTTP/2 PING frame not defined for HTTP/3 must be rejected. (RFC9114 7.2.8)'};
  124. parse(<<8, _/bits>>) ->
  125. {connection_error, h3_frame_unexpected,
  126. 'HTTP/2 WINDOW_UPDATE frame not defined for HTTP/3 must be rejected. (RFC9114 7.2.8)'};
  127. parse(<<9, _/bits>>) ->
  128. {connection_error, h3_frame_unexpected,
  129. 'HTTP/2 CONTINUATION frame not defined for HTTP/3 must be rejected. (RFC9114 7.2.8)'};
  130. %%
  131. %% Unknown frames must be ignored.
  132. %%
  133. %% @todo This can lead to DoS especially for larger frames
  134. %% and HTTP/3 doesn't have a limit in SETTINGS. Perhaps
  135. %% we should have an option to limit the stream buffer
  136. %% size and error out (h3_excessive_load) when exceeded.
  137. parse(<<0:2, Type:6, 0:2, Len:6, _:Len/binary, Rest/bits>>)
  138. when Type =:= 10; Type =:= 11; Type =:= 12; Type > 13 ->
  139. {ignore, Rest};
  140. parse(<<0:2, Type:6, 1:2, Len:14, _:Len/binary, Rest/bits>>)
  141. when Type =:= 10; Type =:= 11; Type =:= 12; Type > 13 ->
  142. {ignore, Rest};
  143. parse(<<0:2, Type:6, 2:2, Len:30, _:Len/binary, Rest/bits>>)
  144. when Type =:= 10; Type =:= 11; Type =:= 12; Type > 13 ->
  145. {ignore, Rest};
  146. parse(<<0:2, Type:6, 3:2, Len:62, _:Len/binary, Rest/bits>>)
  147. when Type =:= 10; Type =:= 11; Type =:= 12; Type > 13 ->
  148. {ignore, Rest};
  149. parse(<<1:2, _:14, 0:2, Len:6, _:Len/binary, Rest/bits>>) ->
  150. {ignore, Rest};
  151. parse(<<1:2, _:14, 1:2, Len:14, _:Len/binary, Rest/bits>>) ->
  152. {ignore, Rest};
  153. parse(<<1:2, _:14, 2:2, Len:30, _:Len/binary, Rest/bits>>) ->
  154. {ignore, Rest};
  155. parse(<<1:2, _:14, 3:2, Len:62, _:Len/binary, Rest/bits>>) ->
  156. {ignore, Rest};
  157. parse(<<2:2, _:30, 0:2, Len:6, _:Len/binary, Rest/bits>>) ->
  158. {ignore, Rest};
  159. parse(<<2:2, _:30, 1:2, Len:14, _:Len/binary, Rest/bits>>) ->
  160. {ignore, Rest};
  161. parse(<<2:2, _:30, 2:2, Len:30, _:Len/binary, Rest/bits>>) ->
  162. {ignore, Rest};
  163. parse(<<2:2, _:30, 3:2, Len:62, _:Len/binary, Rest/bits>>) ->
  164. {ignore, Rest};
  165. parse(<<3:2, _:62, 0:2, Len:6, _:Len/binary, Rest/bits>>) ->
  166. {ignore, Rest};
  167. parse(<<3:2, _:62, 1:2, Len:14, _:Len/binary, Rest/bits>>) ->
  168. {ignore, Rest};
  169. parse(<<3:2, _:62, 2:2, Len:30, _:Len/binary, Rest/bits>>) ->
  170. {ignore, Rest};
  171. parse(<<3:2, _:62, 3:2, Len:62, _:Len/binary, Rest/bits>>) ->
  172. {ignore, Rest};
  173. %%
  174. %% Incomplete frames for those we fully process only.
  175. %%
  176. parse(_) ->
  177. more.
  178. parse_settings_id(Rest, 0, Settings) ->
  179. {ok, {settings, Settings}, Rest};
  180. parse_settings_id(<<0:2, Identifier:6, Rest/bits>>, Len, Settings) when Len >= 1 ->
  181. parse_settings_val(Rest, Len - 1, Settings, Identifier);
  182. parse_settings_id(<<1:2, Identifier:14, Rest/bits>>, Len, Settings) when Len >= 2 ->
  183. parse_settings_val(Rest, Len - 2, Settings, Identifier);
  184. parse_settings_id(<<2:2, Identifier:30, Rest/bits>>, Len, Settings) when Len >= 4 ->
  185. parse_settings_val(Rest, Len - 4, Settings, Identifier);
  186. parse_settings_id(<<3:2, Identifier:62, Rest/bits>>, Len, Settings) when Len >= 8 ->
  187. parse_settings_val(Rest, Len - 8, Settings, Identifier);
  188. parse_settings_id(_, _, _) ->
  189. {connection_error, h3_frame_error,
  190. 'SETTINGS payload size exceeds the length given. (RFC9114 7.1, RFC9114 7.2.4)'}.
  191. parse_settings_val(<<0:2, Value:6, Rest/bits>>, Len, Settings, Identifier) when Len >= 1 ->
  192. parse_settings_id_val(Rest, Len - 1, Settings, Identifier, Value);
  193. parse_settings_val(<<1:2, Value:14, Rest/bits>>, Len, Settings, Identifier) when Len >= 2 ->
  194. parse_settings_id_val(Rest, Len - 2, Settings, Identifier, Value);
  195. parse_settings_val(<<2:2, Value:30, Rest/bits>>, Len, Settings, Identifier) when Len >= 4 ->
  196. parse_settings_id_val(Rest, Len - 4, Settings, Identifier, Value);
  197. parse_settings_val(<<3:2, Value:62, Rest/bits>>, Len, Settings, Identifier) when Len >= 8 ->
  198. parse_settings_id_val(Rest, Len - 8, Settings, Identifier, Value);
  199. parse_settings_val(_, _, _, _) ->
  200. {connection_error, h3_frame_error,
  201. 'SETTINGS payload size exceeds the length given. (RFC9114 7.1, RFC9114 7.2.4)'}.
  202. parse_settings_id_val(Rest, Len, Settings, Identifier, Value) ->
  203. case Identifier of
  204. 6 ->
  205. parse_settings_key_val(Rest, Len, Settings, max_field_section_size, Value);
  206. _ when Identifier < 6, Identifier =/= 1 ->
  207. {connection_error, h3_settings_error,
  208. 'HTTP/2 setting not defined for HTTP/3 must be rejected. (RFC9114 7.2.4.1)'};
  209. %% Unknown settings must be ignored.
  210. _ ->
  211. parse_settings_id(Rest, Len, Settings)
  212. end.
  213. parse_settings_key_val(Rest, Len, Settings, Key, Value) ->
  214. case Settings of
  215. #{Key := _} ->
  216. {connection_error, h3_settings_error,
  217. 'A duplicate setting identifier was found. (RFC9114 7.2.4)'};
  218. _ ->
  219. parse_settings_id(Rest, Len, Settings#{Key => Value})
  220. end.
  221. parse_push_promise(<<0:2, PushID:6, Data/bits>>, Len) ->
  222. <<EncodedFieldSection:(Len - 1)/bytes, Rest/bits>> = Data,
  223. {ok, {push_promise, PushID, EncodedFieldSection}, Rest};
  224. parse_push_promise(<<1:2, PushID:14, Data/bits>>, Len) ->
  225. <<EncodedFieldSection:(Len - 2)/bytes, Rest/bits>> = Data,
  226. {ok, {push_promise, PushID, EncodedFieldSection}, Rest};
  227. parse_push_promise(<<2:2, PushID:30, Data/bits>>, Len) ->
  228. <<EncodedFieldSection:(Len - 4)/bytes, Rest/bits>> = Data,
  229. {ok, {push_promise, PushID, EncodedFieldSection}, Rest};
  230. parse_push_promise(<<3:2, PushID:62, Data/bits>>, Len) ->
  231. <<EncodedFieldSection:(Len - 8)/bytes, Rest/bits>> = Data,
  232. {ok, {push_promise, PushID, EncodedFieldSection}, Rest}.
  233. -spec parse_unidi_stream_header(_) -> _. %% @todo
  234. parse_unidi_stream_header(<<0, Rest/bits>>) ->
  235. {ok, control, Rest};
  236. parse_unidi_stream_header(<<1, Rest/bits>>) ->
  237. {ok, push, Rest};
  238. parse_unidi_stream_header(<<2, Rest/bits>>) ->
  239. {ok, encoder, Rest};
  240. parse_unidi_stream_header(<<3, Rest/bits>>) ->
  241. {ok, decoder, Rest};
  242. parse_unidi_stream_header(<<0:2, _:6, Rest/bits>>) ->
  243. {undefined, Rest};
  244. parse_unidi_stream_header(<<1:2, _:14, Rest/bits>>) ->
  245. {undefined, Rest};
  246. parse_unidi_stream_header(<<2:2, _:30, Rest/bits>>) ->
  247. {undefined, Rest};
  248. parse_unidi_stream_header(<<3:2, _:62, Rest/bits>>) ->
  249. {undefined, Rest}.