/src/h2o/lib/http2/frame.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2014 DeNA Co., Ltd. |
3 | | * |
4 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | | * of this software and associated documentation files (the "Software"), to |
6 | | * deal in the Software without restriction, including without limitation the |
7 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
8 | | * sell copies of the Software, and to permit persons to whom the Software is |
9 | | * furnished to do so, subject to the following conditions: |
10 | | * |
11 | | * The above copyright notice and this permission notice shall be included in |
12 | | * all copies or substantial portions of the Software. |
13 | | * |
14 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
19 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
20 | | * IN THE SOFTWARE. |
21 | | */ |
22 | | #include "h2o/http2_common.h" |
23 | | |
24 | | const h2o_http2_priority_t h2o_http2_default_priority = { |
25 | | 0, /* exclusive */ |
26 | | 0, /* dependency */ |
27 | | 16 /* weight */ |
28 | | }; |
29 | | |
30 | | const h2o_http2_settings_t H2O_HTTP2_SETTINGS_DEFAULT = { |
31 | | /* header_table_size */ 4096, |
32 | | /* enable_push */ 1, |
33 | | /* max_concurrent_streams */ UINT32_MAX, |
34 | | /* initial_window_size */ 65535, |
35 | | /* max_frame_size */ 16384}; |
36 | | |
37 | | int h2o_http2_update_peer_settings(h2o_http2_settings_t *settings, const uint8_t *src, size_t len, const char **err_desc) |
38 | 0 | { |
39 | 0 | for (; len >= 6; len -= 6, src += 6) { |
40 | 0 | uint16_t identifier = h2o_http2_decode16u(src); |
41 | 0 | uint32_t value = h2o_http2_decode32u(src + 2); |
42 | 0 | switch (identifier) { |
43 | 0 | #define SET(label, member, min, max, err_code) \ |
44 | 0 | case H2O_HTTP2_SETTINGS_##label: \ |
45 | 0 | if (!(min <= value && value <= max)) { \ |
46 | 0 | *err_desc = "invalid SETTINGS frame"; \ |
47 | 0 | return err_code; \ |
48 | 0 | } \ |
49 | 0 | settings->member = value; \ |
50 | 0 | break |
51 | 0 | SET(HEADER_TABLE_SIZE, header_table_size, 0, UINT32_MAX, 0); |
52 | 0 | SET(ENABLE_PUSH, enable_push, 0, 1, H2O_HTTP2_ERROR_PROTOCOL); |
53 | 0 | SET(MAX_CONCURRENT_STREAMS, max_concurrent_streams, 0, UINT32_MAX, 0); |
54 | 0 | SET(INITIAL_WINDOW_SIZE, initial_window_size, 0, 0x7fffffff, H2O_HTTP2_ERROR_FLOW_CONTROL); |
55 | 0 | SET(MAX_FRAME_SIZE, max_frame_size, 16384, 16777215, H2O_HTTP2_ERROR_PROTOCOL); |
56 | 0 | #undef SET |
57 | 0 | default: |
58 | | /* ignore unknown (5.5) */ |
59 | 0 | break; |
60 | 0 | } |
61 | 0 | } |
62 | | |
63 | 0 | if (len != 0) |
64 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
65 | 0 | return 0; |
66 | 0 | } |
67 | | |
68 | | uint8_t *h2o_http2_encode_frame_header(uint8_t *dst, size_t length, uint8_t type, uint8_t flags, int32_t stream_id) |
69 | 0 | { |
70 | 0 | if (length > 0xffffff) |
71 | 0 | h2o_fatal("invalid length"); |
72 | | |
73 | 0 | dst = h2o_http2_encode24u(dst, (uint32_t)length); |
74 | 0 | *dst++ = type; |
75 | 0 | *dst++ = flags; |
76 | 0 | dst = h2o_http2_encode32u(dst, stream_id); |
77 | |
|
78 | 0 | return dst; |
79 | 0 | } |
80 | | |
81 | | static uint8_t *allocate_frame(h2o_buffer_t **buf, size_t length, uint8_t type, uint8_t flags, int32_t stream_id) |
82 | 0 | { |
83 | 0 | h2o_iovec_t alloced = h2o_buffer_reserve(buf, H2O_HTTP2_FRAME_HEADER_SIZE + length); |
84 | 0 | (*buf)->size += H2O_HTTP2_FRAME_HEADER_SIZE + length; |
85 | 0 | return h2o_http2_encode_frame_header((uint8_t *)alloced.base, length, type, flags, stream_id); |
86 | 0 | } |
87 | | |
88 | | void h2o_http2__encode_rst_stream_frame(h2o_buffer_t **buf, uint32_t stream_id, int errnum) |
89 | 0 | { |
90 | 0 | uint8_t *dst = allocate_frame(buf, 4, H2O_HTTP2_FRAME_TYPE_RST_STREAM, 0, stream_id); |
91 | 0 | dst = h2o_http2_encode32u(dst, errnum); |
92 | 0 | } |
93 | | |
94 | | void h2o_http2_encode_ping_frame(h2o_buffer_t **buf, int is_ack, const uint8_t *data) |
95 | 0 | { |
96 | 0 | uint8_t *dst = allocate_frame(buf, 8, H2O_HTTP2_FRAME_TYPE_PING, is_ack ? H2O_HTTP2_FRAME_FLAG_ACK : 0, 0); |
97 | 0 | memcpy(dst, data, 8); |
98 | 0 | dst += 8; |
99 | 0 | } |
100 | | |
101 | | void h2o_http2_encode_goaway_frame(h2o_buffer_t **buf, uint32_t last_stream_id, int errnum, h2o_iovec_t additional_data) |
102 | 0 | { |
103 | 0 | uint8_t *dst = allocate_frame(buf, 8 + additional_data.len, H2O_HTTP2_FRAME_TYPE_GOAWAY, 0, 0); |
104 | 0 | dst = h2o_http2_encode32u(dst, last_stream_id); |
105 | 0 | dst = h2o_http2_encode32u(dst, (uint32_t)-errnum); |
106 | 0 | h2o_memcpy(dst, additional_data.base, additional_data.len); |
107 | 0 | } |
108 | | |
109 | | void h2o_http2_encode_settings_frame(h2o_buffer_t **buf, h2o_http2_settings_kvpair_t *settings, size_t num_settings) |
110 | 0 | { |
111 | 0 | uint8_t *dst = allocate_frame(buf, 6 * num_settings, H2O_HTTP2_FRAME_TYPE_SETTINGS, 0, 0); |
112 | 0 | for (size_t i = 0; i < num_settings; ++i) { |
113 | 0 | dst = h2o_http2_encode16u(dst, settings[i].key); |
114 | 0 | dst = h2o_http2_encode32u(dst, settings[i].value); |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | | void h2o_http2_encode_window_update_frame(h2o_buffer_t **buf, uint32_t stream_id, int32_t window_size_increment) |
119 | 0 | { |
120 | 0 | uint8_t *dst = allocate_frame(buf, 4, H2O_HTTP2_FRAME_TYPE_WINDOW_UPDATE, 0, stream_id); |
121 | 0 | dst = h2o_http2_encode32u(dst, window_size_increment); |
122 | 0 | } |
123 | | |
124 | | void h2o_http2_encode_origin_frame(h2o_buffer_t **buf, h2o_iovec_t payload) |
125 | 0 | { |
126 | 0 | uint8_t *dst = allocate_frame(buf, payload.len, H2O_HTTP2_FRAME_TYPE_ORIGIN, 0, 0); |
127 | 0 | memcpy(dst, payload.base, payload.len); |
128 | 0 | } |
129 | | |
130 | | ssize_t h2o_http2_decode_frame(h2o_http2_frame_t *frame, const uint8_t *src, size_t len, size_t max_frame_size, |
131 | | const char **err_desc) |
132 | 0 | { |
133 | 0 | if (len < H2O_HTTP2_FRAME_HEADER_SIZE) |
134 | 0 | return H2O_HTTP2_ERROR_INCOMPLETE; |
135 | | |
136 | 0 | frame->length = h2o_http2_decode24u(src); |
137 | 0 | frame->type = src[3]; |
138 | 0 | frame->flags = src[4]; |
139 | 0 | frame->stream_id = h2o_http2_decode32u(src + 5) & 0x7fffffff; |
140 | |
|
141 | 0 | if (frame->length > max_frame_size) |
142 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
143 | | |
144 | 0 | if (len < H2O_HTTP2_FRAME_HEADER_SIZE + frame->length) |
145 | 0 | return H2O_HTTP2_ERROR_INCOMPLETE; |
146 | | |
147 | 0 | frame->payload = src + H2O_HTTP2_FRAME_HEADER_SIZE; |
148 | |
|
149 | 0 | return H2O_HTTP2_FRAME_HEADER_SIZE + frame->length; |
150 | 0 | } |
151 | | |
152 | | int h2o_http2_decode_data_payload(h2o_http2_data_payload_t *payload, const h2o_http2_frame_t *frame, const char **err_desc) |
153 | 0 | { |
154 | 0 | if (frame->stream_id == 0) { |
155 | 0 | *err_desc = "invalid stream id in DATA frame"; |
156 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
157 | 0 | } |
158 | | |
159 | 0 | if ((frame->flags & H2O_HTTP2_FRAME_FLAG_PADDED) != 0) { |
160 | 0 | uint8_t padding_length; |
161 | 0 | if (frame->length < 1) { |
162 | 0 | *err_desc = "invalid DATA frame"; |
163 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
164 | 0 | } |
165 | 0 | padding_length = frame->payload[0]; |
166 | 0 | if (frame->length < 1 + padding_length) { |
167 | 0 | *err_desc = "invalid DATA frame"; |
168 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
169 | 0 | } |
170 | 0 | payload->data = frame->payload + 1; |
171 | 0 | payload->length = frame->length - (1 + padding_length); |
172 | 0 | } else { |
173 | 0 | payload->data = frame->payload; |
174 | 0 | payload->length = frame->length; |
175 | 0 | } |
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | | static const uint8_t *decode_priority(h2o_http2_priority_t *priority, const uint8_t *src) |
180 | 0 | { |
181 | 0 | uint32_t u4 = h2o_http2_decode32u(src); |
182 | 0 | src += 4; |
183 | 0 | priority->exclusive = u4 >> 31; |
184 | 0 | priority->dependency = u4 & 0x7fffffff; |
185 | 0 | priority->weight = (uint16_t)*src++ + 1; |
186 | 0 | return src; |
187 | 0 | } |
188 | | |
189 | | int h2o_http2_decode_headers_payload(h2o_http2_headers_payload_t *payload, const h2o_http2_frame_t *frame, const char **err_desc) |
190 | 0 | { |
191 | 0 | const uint8_t *src = frame->payload, *src_end = frame->payload + frame->length; |
192 | |
|
193 | 0 | if (frame->stream_id == 0) { |
194 | 0 | *err_desc = "invalid stream id in HEADERS frame"; |
195 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
196 | 0 | } |
197 | | |
198 | 0 | if ((frame->flags & H2O_HTTP2_FRAME_FLAG_PADDED) != 0) { |
199 | 0 | uint32_t padlen; |
200 | 0 | if (src == src_end) { |
201 | 0 | *err_desc = "invalid HEADERS frame"; |
202 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
203 | 0 | } |
204 | 0 | padlen = *src++; |
205 | 0 | if (src_end - src < padlen) { |
206 | 0 | *err_desc = "invalid HEADERS frame"; |
207 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
208 | 0 | } |
209 | 0 | src_end -= padlen; |
210 | 0 | } |
211 | | |
212 | 0 | if ((frame->flags & H2O_HTTP2_FRAME_FLAG_PRIORITY) != 0) { |
213 | 0 | if (src_end - src < 5) |
214 | 0 | return -1; |
215 | 0 | src = decode_priority(&payload->priority, src); |
216 | 0 | } else { |
217 | 0 | payload->priority = h2o_http2_default_priority; |
218 | 0 | } |
219 | | |
220 | 0 | payload->headers = src; |
221 | 0 | payload->headers_len = src_end - src; |
222 | |
|
223 | 0 | return 0; |
224 | 0 | } |
225 | | |
226 | | int h2o_http2_decode_priority_payload(h2o_http2_priority_t *payload, const h2o_http2_frame_t *frame, const char **err_desc) |
227 | 0 | { |
228 | 0 | if (frame->stream_id == 0) { |
229 | 0 | *err_desc = "invalid stream id in PRIORITY frame"; |
230 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
231 | 0 | } |
232 | 0 | if (frame->length != 5) { |
233 | 0 | *err_desc = "invalid PRIORITY frame"; |
234 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
235 | 0 | } |
236 | | |
237 | 0 | decode_priority(payload, frame->payload); |
238 | 0 | return 0; |
239 | 0 | } |
240 | | |
241 | | int h2o_http2_decode_rst_stream_payload(h2o_http2_rst_stream_payload_t *payload, const h2o_http2_frame_t *frame, |
242 | | const char **err_desc) |
243 | 0 | { |
244 | 0 | if (frame->stream_id == 0) { |
245 | 0 | *err_desc = "invalid stream id in RST_STREAM frame"; |
246 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
247 | 0 | } |
248 | 0 | if (frame->length != sizeof(payload->error_code)) { |
249 | 0 | *err_desc = "invalid RST_STREAM frame"; |
250 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
251 | 0 | } |
252 | | |
253 | 0 | payload->error_code = h2o_http2_decode32u(frame->payload); |
254 | 0 | return 0; |
255 | 0 | } |
256 | | |
257 | | int h2o_http2_decode_ping_payload(h2o_http2_ping_payload_t *payload, const h2o_http2_frame_t *frame, const char **err_desc) |
258 | 0 | { |
259 | 0 | if (frame->stream_id != 0) { |
260 | 0 | *err_desc = "invalid PING frame"; |
261 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
262 | 0 | } |
263 | 0 | if (frame->length != sizeof(payload->data)) { |
264 | 0 | *err_desc = "invalid PING frame"; |
265 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
266 | 0 | } |
267 | | |
268 | 0 | memcpy(payload->data, frame->payload, sizeof(payload->data)); |
269 | 0 | return 0; |
270 | 0 | } |
271 | | |
272 | | int h2o_http2_decode_goaway_payload(h2o_http2_goaway_payload_t *payload, const h2o_http2_frame_t *frame, const char **err_desc) |
273 | 0 | { |
274 | 0 | if (frame->stream_id != 0) { |
275 | 0 | *err_desc = "invalid stream id in GOAWAY frame"; |
276 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
277 | 0 | } |
278 | 0 | if (frame->length < 8) { |
279 | 0 | *err_desc = "invalid GOAWAY frame"; |
280 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
281 | 0 | } |
282 | | |
283 | 0 | payload->last_stream_id = h2o_http2_decode32u(frame->payload) & 0x7fffffff; |
284 | 0 | payload->error_code = h2o_http2_decode32u(frame->payload + 4); |
285 | 0 | if ((payload->debug_data.len = frame->length - 8) != 0) |
286 | 0 | payload->debug_data.base = (char *)frame->payload + 8; |
287 | 0 | else |
288 | 0 | payload->debug_data.base = NULL; |
289 | |
|
290 | 0 | return 0; |
291 | 0 | } |
292 | | |
293 | | int h2o_http2_decode_window_update_payload(h2o_http2_window_update_payload_t *payload, const h2o_http2_frame_t *frame, |
294 | | const char **err_desc, int *err_is_stream_level) |
295 | 0 | { |
296 | 0 | if (frame->length != 4) { |
297 | 0 | *err_is_stream_level = 0; |
298 | 0 | *err_desc = "invalid WINDOW_UPDATE frame"; |
299 | 0 | return H2O_HTTP2_ERROR_FRAME_SIZE; |
300 | 0 | } |
301 | | |
302 | 0 | payload->window_size_increment = h2o_http2_decode32u(frame->payload) & 0x7fffffff; |
303 | 0 | if (payload->window_size_increment == 0) { |
304 | 0 | *err_is_stream_level = frame->stream_id != 0; |
305 | 0 | *err_desc = "invalid WINDOW_UPDATE frame"; |
306 | 0 | return H2O_HTTP2_ERROR_PROTOCOL; |
307 | 0 | } |
308 | | |
309 | 0 | return 0; |
310 | 0 | } |