/src/h2o/lib/http3/common.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018 Fastly, Kazuho Oku |
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 | | #ifdef __APPLE__ |
23 | | #define __APPLE_USE_RFC_3542 /* to use IPV6_PKTINFO */ |
24 | | #endif |
25 | | #include <errno.h> |
26 | | #include <sys/types.h> |
27 | | #include <netinet/in.h> |
28 | | #include <netinet/udp.h> |
29 | | #include <pthread.h> |
30 | | #include <stdio.h> |
31 | | #include <sys/socket.h> |
32 | | #include "picotls/openssl.h" |
33 | | #include "h2o.h" |
34 | | #include "h2o/string_.h" |
35 | | #include "h2o/http3_common.h" |
36 | | #include "h2o/http3_internal.h" |
37 | | #include "h2o/multithread.h" |
38 | | #include "../probes_.h" |
39 | | |
40 | | h2o_quic_conn_t h2o_quic_accept_conn_decryption_failed; |
41 | | h2o_http3_conn_t h2o_http3_accept_conn_closed; |
42 | | |
43 | | struct st_h2o_http3_ingress_unistream_t { |
44 | | /** |
45 | | * back pointer |
46 | | */ |
47 | | quicly_stream_t *quic; |
48 | | /** |
49 | | * |
50 | | */ |
51 | | h2o_buffer_t *recvbuf; |
52 | | /** |
53 | | * A callback that passes unparsed input to be handled. `src` is set to NULL when receiving a reset. |
54 | | */ |
55 | | void (*handle_input)(h2o_http3_conn_t *conn, struct st_h2o_http3_ingress_unistream_t *stream, const uint8_t **src, |
56 | | const uint8_t *src_end, int is_eos); |
57 | | }; |
58 | | |
59 | | const char h2o_http3_err_frame_too_large[] = "HTTP/3 frame is too large"; |
60 | | |
61 | | const ptls_iovec_t h2o_http3_alpn[3] = {{(void *)H2O_STRLIT("h3")}, {(void *)H2O_STRLIT("h3-29")}, {(void *)H2O_STRLIT("h3-27")}}; |
62 | | |
63 | | static void report_sendmsg_errors(h2o_error_reporter_t *reporter, uint64_t total_successes, uint64_t cur_successes) |
64 | 0 | { |
65 | 0 | char errstr[256]; |
66 | 0 | fprintf(stderr, "sendmsg failed %" PRIu64 " time%s, succeeded: %" PRIu64 " time%s, over the last minute: %s\n", |
67 | 0 | reporter->cur_errors, reporter->cur_errors > 1 ? "s" : "", cur_successes, cur_successes > 1 ? "s" : "", |
68 | 0 | h2o_strerror_r((int)reporter->data, errstr, sizeof(errstr))); |
69 | 0 | } |
70 | | |
71 | | static h2o_error_reporter_t track_sendmsg = H2O_ERROR_REPORTER_INITIALIZER(report_sendmsg_errors); |
72 | | |
73 | | int h2o_quic_send_datagrams(h2o_quic_ctx_t *ctx, quicly_address_t *dest, quicly_address_t *src, struct iovec *datagrams, |
74 | | size_t num_datagrams) |
75 | 0 | { |
76 | 0 | union { |
77 | 0 | struct cmsghdr hdr; |
78 | 0 | char buf[ |
79 | 0 | #ifdef IPV6_PKTINFO |
80 | 0 | CMSG_SPACE(sizeof(struct in6_pktinfo)) |
81 | | #elif defined(IP_PKTINFO) |
82 | | CMSG_SPACE(sizeof(struct in_pktinfo)) |
83 | | #elif defined(IP_SENDSRCADDR) |
84 | | CMSG_SPACE(sizeof(struct in_addr)) |
85 | | #else |
86 | | CMSG_SPACE(1) |
87 | | #endif |
88 | 0 | #ifdef UDP_SEGMENT |
89 | 0 | + CMSG_SPACE(sizeof(uint16_t)) |
90 | 0 | #endif |
91 | | + CMSG_SPACE(1) /* sentry */ |
92 | 0 | ]; |
93 | 0 | } cmsgbuf = {.buf = {} /* zero-cleared so that CMSG_NXTHDR can be used for locating the *next* cmsghdr */}; |
94 | 0 | struct msghdr mess = { |
95 | 0 | .msg_name = &dest->sa, |
96 | 0 | .msg_namelen = quicly_get_socklen(&dest->sa), |
97 | 0 | .msg_control = cmsgbuf.buf, |
98 | 0 | .msg_controllen = sizeof(cmsgbuf.buf), |
99 | 0 | }; |
100 | 0 | struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mess); |
101 | 0 | int ret; |
102 | |
|
103 | 0 | #define PUSH_CMSG(level, type, value) \ |
104 | 0 | do { \ |
105 | 0 | cmsg->cmsg_level = (level); \ |
106 | 0 | cmsg->cmsg_type = (type); \ |
107 | 0 | cmsg->cmsg_len = CMSG_LEN(sizeof(value)); \ |
108 | 0 | memcpy(CMSG_DATA(cmsg), &value, sizeof(value)); \ |
109 | 0 | cmsg = CMSG_NXTHDR(&mess, cmsg); \ |
110 | 0 | } while (0) |
111 | | |
112 | | /* first CMSG is the source address */ |
113 | 0 | if (src->sa.sa_family != AF_UNSPEC) { |
114 | 0 | switch (src->sa.sa_family) { |
115 | 0 | case AF_INET: { |
116 | 0 | #if defined(IP_PKTINFO) |
117 | 0 | if (*ctx->sock.port != src->sin.sin_port) |
118 | 0 | return 0; |
119 | 0 | struct in_pktinfo info = {.ipi_spec_dst = src->sin.sin_addr}; |
120 | 0 | PUSH_CMSG(IPPROTO_IP, IP_PKTINFO, info); |
121 | | #elif defined(IP_SENDSRCADDR) |
122 | | if (*ctx->sock.port != src->sin.sin_port) |
123 | | return 0; |
124 | | struct sockaddr_in *fdaddr = (struct sockaddr_in *)&ctx->sock.addr; |
125 | | assert(fdaddr->sin_family == AF_INET); |
126 | | if (fdaddr->sin_addr.s_addr == INADDR_ANY) |
127 | | PUSH_CMSG(IPPROTO_IP, IP_SENDSRCADDR, src->sin.sin_addr); |
128 | | #else |
129 | | h2o_fatal("IP_PKTINFO not available"); |
130 | | #endif |
131 | 0 | } break; |
132 | 0 | case AF_INET6: |
133 | 0 | #ifdef IPV6_PKTINFO |
134 | 0 | if (*ctx->sock.port != src->sin6.sin6_port) |
135 | 0 | return 0; |
136 | 0 | struct in6_pktinfo info = {.ipi6_addr = src->sin6.sin6_addr}; |
137 | 0 | PUSH_CMSG(IPPROTO_IPV6, IPV6_PKTINFO, info); |
138 | | #else |
139 | | h2o_fatal("IPV6_PKTINFO not available"); |
140 | | #endif |
141 | 0 | break; |
142 | 0 | default: |
143 | 0 | h2o_fatal("unexpected address family"); |
144 | 0 | break; |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | /* next CMSG is UDP_SEGMENT size (for GSO) */ |
149 | 0 | int using_gso = 0; |
150 | 0 | #ifdef UDP_SEGMENT |
151 | 0 | if (num_datagrams > 1 && ctx->use_gso) { |
152 | 0 | for (size_t i = 1; i < num_datagrams - 1; ++i) |
153 | 0 | assert(datagrams[i].iov_len == datagrams[0].iov_len); |
154 | 0 | uint16_t segsize = (uint16_t)datagrams[0].iov_len; |
155 | 0 | PUSH_CMSG(SOL_UDP, UDP_SEGMENT, segsize); |
156 | 0 | using_gso = 1; |
157 | 0 | } |
158 | 0 | #endif |
159 | | |
160 | | /* commit CMSG length */ |
161 | 0 | if ((mess.msg_controllen = (socklen_t)((char *)cmsg - (char *)cmsgbuf.buf)) == 0) |
162 | 0 | mess.msg_control = NULL; |
163 | | |
164 | | /* send datagrams */ |
165 | 0 | if (using_gso) { |
166 | 0 | mess.msg_iov = datagrams; |
167 | 0 | mess.msg_iovlen = (int)num_datagrams; |
168 | 0 | while ((ret = (int)sendmsg(h2o_socket_get_fd(ctx->sock.sock), &mess, 0)) == -1 && errno == EINTR) |
169 | 0 | ; |
170 | 0 | if (ret == -1) |
171 | 0 | goto SendmsgError; |
172 | 0 | } else { |
173 | 0 | for (size_t i = 0; i < num_datagrams; ++i) { |
174 | 0 | mess.msg_iov = datagrams + i; |
175 | 0 | mess.msg_iovlen = 1; |
176 | 0 | while ((ret = (int)sendmsg(h2o_socket_get_fd(ctx->sock.sock), &mess, 0)) == -1 && errno == EINTR) |
177 | 0 | ; |
178 | 0 | if (ret == -1) |
179 | 0 | goto SendmsgError; |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | 0 | h2o_error_reporter_record_success(&track_sendmsg); |
184 | |
|
185 | 0 | return 1; |
186 | | |
187 | 0 | SendmsgError: |
188 | | /* The UDP stack returns EINVAL (linux) or EADDRNOTAVAIL (darwin, and presumably other BSD) when it was unable to use the |
189 | | * designated source address. We communicate that back to the caller so that the connection can be closed immediately. */ |
190 | 0 | if (src->sa.sa_family != AF_UNSPEC && (errno == EINVAL || errno == EADDRNOTAVAIL)) |
191 | 0 | return 0; |
192 | | |
193 | | /* Temporary failure to send a packet is not a permanent error fo the connection. (TODO do we want do something more |
194 | | * specific?) */ |
195 | | |
196 | | /* Log the number of failed invocations once per minute, if there has been such a failure. */ |
197 | 0 | h2o_error_reporter_record_error(ctx->loop, &track_sendmsg, 60000, errno); |
198 | |
|
199 | 0 | return 1; |
200 | |
|
201 | 0 | #undef PUSH_CMSG |
202 | 0 | } |
203 | | |
204 | | static inline const h2o_http3_conn_callbacks_t *get_callbacks(h2o_http3_conn_t *conn) |
205 | 0 | { |
206 | 0 | return (const h2o_http3_conn_callbacks_t *)conn->super.callbacks; |
207 | 0 | } |
208 | | |
209 | | static void ingress_unistream_on_destroy(quicly_stream_t *qs, quicly_error_t err) |
210 | 0 | { |
211 | 0 | struct st_h2o_http3_ingress_unistream_t *stream = qs->data; |
212 | 0 | h2o_buffer_dispose(&stream->recvbuf); |
213 | 0 | free(stream); |
214 | 0 | } |
215 | | |
216 | | static void ingress_unistream_on_receive(quicly_stream_t *qs, size_t off, const void *input, size_t len) |
217 | 0 | { |
218 | 0 | h2o_http3_conn_t *conn = *quicly_get_data(qs->conn); |
219 | 0 | struct st_h2o_http3_ingress_unistream_t *stream = qs->data; |
220 | | |
221 | | /* save received data */ |
222 | 0 | h2o_http3_update_recvbuf(&stream->recvbuf, off, input, len); |
223 | | |
224 | | /* determine bytes that can be handled */ |
225 | 0 | const uint8_t *src = (const uint8_t *)stream->recvbuf->bytes, |
226 | 0 | *src_end = src + quicly_recvstate_bytes_available(&stream->quic->recvstate); |
227 | 0 | if (src == src_end && !quicly_recvstate_transfer_complete(&stream->quic->recvstate)) |
228 | 0 | return; |
229 | | |
230 | | /* handle the bytes */ |
231 | 0 | stream->handle_input(conn, stream, &src, src_end, quicly_recvstate_transfer_complete(&stream->quic->recvstate)); |
232 | 0 | if (quicly_get_state(conn->super.quic) >= QUICLY_STATE_CLOSING) |
233 | 0 | return; |
234 | | |
235 | | /* remove bytes that have been consumed */ |
236 | 0 | size_t bytes_consumed = src - (const uint8_t *)stream->recvbuf->bytes; |
237 | 0 | if (bytes_consumed != 0) { |
238 | 0 | h2o_buffer_consume(&stream->recvbuf, bytes_consumed); |
239 | 0 | quicly_stream_sync_recvbuf(stream->quic, bytes_consumed); |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | static void ingress_unistream_on_receive_reset(quicly_stream_t *qs, quicly_error_t err) |
244 | 0 | { |
245 | 0 | h2o_http3_conn_t *conn = *quicly_get_data(qs->conn); |
246 | 0 | struct st_h2o_http3_ingress_unistream_t *stream = qs->data; |
247 | |
|
248 | 0 | stream->handle_input(conn, stream, NULL, NULL, 1); |
249 | 0 | } |
250 | | |
251 | | static void qpack_encoder_stream_handle_input(h2o_http3_conn_t *conn, struct st_h2o_http3_ingress_unistream_t *stream, |
252 | | const uint8_t **src, const uint8_t *src_end, int is_eos) |
253 | 0 | { |
254 | 0 | if (src == NULL || is_eos) { |
255 | 0 | h2o_quic_close_connection(&conn->super, H2O_HTTP3_ERROR_CLOSED_CRITICAL_STREAM, NULL); |
256 | 0 | return; |
257 | 0 | } |
258 | | |
259 | 0 | int64_t *unblocked_stream_ids; |
260 | 0 | size_t num_unblocked; |
261 | 0 | int ret; |
262 | 0 | const char *err_desc = NULL; |
263 | 0 | if ((ret = h2o_qpack_decoder_handle_input(conn->qpack.dec, &unblocked_stream_ids, &num_unblocked, src, src_end, &err_desc)) != |
264 | 0 | 0) { |
265 | 0 | h2o_quic_close_connection(&conn->super, ret, err_desc); |
266 | 0 | return; |
267 | 0 | } |
268 | | |
269 | | /* TODO handle unblocked streams */ |
270 | 0 | } |
271 | | |
272 | | static void qpack_decoder_stream_handle_input(h2o_http3_conn_t *conn, struct st_h2o_http3_ingress_unistream_t *stream, |
273 | | const uint8_t **src, const uint8_t *src_end, int is_eos) |
274 | 0 | { |
275 | 0 | if (src == NULL || is_eos) { |
276 | 0 | h2o_quic_close_connection(&conn->super, H2O_HTTP3_ERROR_CLOSED_CRITICAL_STREAM, NULL); |
277 | 0 | return; |
278 | 0 | } |
279 | | |
280 | 0 | int ret; |
281 | 0 | const char *err_desc = NULL; |
282 | 0 | if ((ret = h2o_qpack_encoder_handle_input(conn->qpack.enc, src, src_end, &err_desc)) != 0) |
283 | 0 | h2o_quic_close_connection(&conn->super, ret, err_desc); |
284 | 0 | } |
285 | | |
286 | | static void control_stream_handle_input(h2o_http3_conn_t *conn, struct st_h2o_http3_ingress_unistream_t *stream, |
287 | | const uint8_t **src, const uint8_t *src_end, int is_eos) |
288 | 0 | { |
289 | 0 | if (src == NULL || is_eos) { |
290 | 0 | h2o_quic_close_connection(&conn->super, H2O_HTTP3_ERROR_CLOSED_CRITICAL_STREAM, NULL); |
291 | 0 | return; |
292 | 0 | } |
293 | | |
294 | 0 | do { |
295 | 0 | h2o_http3_read_frame_t frame; |
296 | 0 | quicly_error_t ret; |
297 | 0 | const char *err_desc = NULL; |
298 | |
|
299 | 0 | if ((ret = h2o_http3_read_frame(&frame, quicly_is_client(conn->super.quic), H2O_HTTP3_STREAM_TYPE_CONTROL, |
300 | 0 | conn->max_frame_payload_size, src, src_end, &err_desc)) != 0) { |
301 | 0 | if (ret != H2O_HTTP3_ERROR_INCOMPLETE) |
302 | 0 | h2o_quic_close_connection(&conn->super, ret, err_desc); |
303 | 0 | break; |
304 | 0 | } |
305 | 0 | if (h2o_http3_has_received_settings(conn) == (frame.type == H2O_HTTP3_FRAME_TYPE_SETTINGS) || |
306 | 0 | frame.type == H2O_HTTP3_FRAME_TYPE_DATA) { |
307 | 0 | h2o_quic_close_connection(&conn->super, H2O_HTTP3_ERROR_FRAME_UNEXPECTED, NULL); |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | get_callbacks(conn)->handle_control_stream_frame(conn, frame.type, frame.payload, frame.length); |
311 | 0 | if (quicly_get_state(conn->super.quic) >= QUICLY_STATE_CLOSING) |
312 | 0 | break; |
313 | 0 | } while (*src != src_end); |
314 | 0 | } |
315 | | |
316 | | static void discard_handle_input(h2o_http3_conn_t *conn, struct st_h2o_http3_ingress_unistream_t *stream, const uint8_t **src, |
317 | | const uint8_t *src_end, int is_eos) |
318 | 0 | { |
319 | 0 | if (src == NULL) |
320 | 0 | return; |
321 | 0 | *src = src_end; |
322 | 0 | } |
323 | | |
324 | | static void unknown_type_handle_input(h2o_http3_conn_t *conn, struct st_h2o_http3_ingress_unistream_t *stream, const uint8_t **src, |
325 | | const uint8_t *src_end, int is_eos) |
326 | 0 | { |
327 | 0 | uint64_t type; |
328 | | |
329 | | /* resets are allowed at least until the type is being determined */ |
330 | 0 | if (src == NULL) |
331 | 0 | return; |
332 | | |
333 | | /* read the type, or just return if incomplete */ |
334 | 0 | if ((type = quicly_decodev(src, src_end)) == UINT64_MAX) |
335 | 0 | return; |
336 | | |
337 | 0 | switch (type) { |
338 | 0 | case H2O_HTTP3_STREAM_TYPE_CONTROL: |
339 | 0 | conn->_control_streams.ingress.control = stream; |
340 | 0 | stream->handle_input = control_stream_handle_input; |
341 | 0 | break; |
342 | 0 | case H2O_HTTP3_STREAM_TYPE_QPACK_ENCODER: |
343 | 0 | conn->_control_streams.ingress.qpack_encoder = stream; |
344 | 0 | stream->handle_input = qpack_encoder_stream_handle_input; |
345 | 0 | break; |
346 | 0 | case H2O_HTTP3_STREAM_TYPE_QPACK_DECODER: |
347 | 0 | conn->_control_streams.ingress.qpack_decoder = stream; |
348 | 0 | stream->handle_input = qpack_decoder_stream_handle_input; |
349 | 0 | break; |
350 | 0 | default: |
351 | 0 | quicly_request_stop(stream->quic, H2O_HTTP3_ERROR_STREAM_CREATION); |
352 | 0 | stream->handle_input = discard_handle_input; |
353 | 0 | break; |
354 | 0 | } |
355 | | |
356 | 0 | return stream->handle_input(conn, stream, src, src_end, is_eos); |
357 | 0 | } |
358 | | |
359 | | static void egress_unistream_on_destroy(quicly_stream_t *qs, quicly_error_t err) |
360 | 18.4k | { |
361 | 18.4k | struct st_h2o_http3_egress_unistream_t *stream = qs->data; |
362 | 18.4k | h2o_buffer_dispose(&stream->sendbuf); |
363 | 18.4k | free(stream); |
364 | 18.4k | } |
365 | | |
366 | | static void egress_unistream_on_send_shift(quicly_stream_t *qs, size_t delta) |
367 | 0 | { |
368 | 0 | struct st_h2o_http3_egress_unistream_t *stream = qs->data; |
369 | 0 | h2o_buffer_consume(&stream->sendbuf, delta); |
370 | 0 | } |
371 | | |
372 | | static void egress_unistream_on_send_emit(quicly_stream_t *qs, size_t off, void *dst, size_t *len, int *wrote_all) |
373 | 18.4k | { |
374 | 18.4k | struct st_h2o_http3_egress_unistream_t *stream = qs->data; |
375 | | |
376 | 18.4k | if (*len >= stream->sendbuf->size - off) { |
377 | 18.4k | *len = stream->sendbuf->size - off; |
378 | 18.4k | *wrote_all = 1; |
379 | 18.4k | } else { |
380 | 0 | *wrote_all = 0; |
381 | 0 | } |
382 | 18.4k | memcpy(dst, stream->sendbuf->bytes + off, *len); |
383 | 18.4k | } |
384 | | |
385 | | static void egress_unistream_on_send_stop(quicly_stream_t *qs, quicly_error_t err) |
386 | 0 | { |
387 | 0 | struct st_h2o_http3_conn_t *conn = *quicly_get_data(qs->conn); |
388 | 0 | h2o_quic_close_connection(&conn->super, H2O_HTTP3_ERROR_CLOSED_CRITICAL_STREAM, NULL); |
389 | 0 | } |
390 | | |
391 | | void h2o_http3_on_create_unidirectional_stream(quicly_stream_t *qs) |
392 | 18.4k | { |
393 | 18.4k | if (quicly_stream_is_self_initiated(qs)) { |
394 | | /* create egress unistream */ |
395 | 18.4k | static const quicly_stream_callbacks_t callbacks = {egress_unistream_on_destroy, egress_unistream_on_send_shift, |
396 | 18.4k | egress_unistream_on_send_emit, egress_unistream_on_send_stop}; |
397 | 18.4k | struct st_h2o_http3_egress_unistream_t *stream = h2o_mem_alloc(sizeof(*stream)); |
398 | 18.4k | qs->data = stream; |
399 | 18.4k | qs->callbacks = &callbacks; |
400 | 18.4k | stream->quic = qs; |
401 | 18.4k | h2o_buffer_init(&stream->sendbuf, &h2o_socket_buffer_prototype); |
402 | 18.4k | } else { |
403 | | /* create ingress unistream */ |
404 | 0 | static const quicly_stream_callbacks_t callbacks = { |
405 | 0 | ingress_unistream_on_destroy, NULL, NULL, NULL, ingress_unistream_on_receive, ingress_unistream_on_receive_reset}; |
406 | 0 | struct st_h2o_http3_ingress_unistream_t *stream = h2o_mem_alloc(sizeof(*stream)); |
407 | 0 | qs->data = stream; |
408 | 0 | qs->callbacks = &callbacks; |
409 | 0 | stream->quic = qs; |
410 | 0 | h2o_buffer_init(&stream->recvbuf, &h2o_socket_buffer_prototype); |
411 | 0 | stream->handle_input = unknown_type_handle_input; |
412 | 0 | } |
413 | 18.4k | } |
414 | | |
415 | | static quicly_error_t open_egress_unistream(h2o_http3_conn_t *conn, struct st_h2o_http3_egress_unistream_t **stream, |
416 | | h2o_iovec_t initial_bytes) |
417 | 18.4k | { |
418 | 18.4k | quicly_stream_t *qs; |
419 | 18.4k | quicly_error_t ret; |
420 | | |
421 | 18.4k | if ((ret = quicly_open_stream(conn->super.quic, &qs, 1)) != 0) |
422 | 0 | return ret; |
423 | 18.4k | *stream = qs->data; |
424 | 18.4k | assert((*stream)->quic == qs); |
425 | | |
426 | 18.4k | h2o_buffer_append(&(*stream)->sendbuf, initial_bytes.base, initial_bytes.len); |
427 | 18.4k | return quicly_stream_sync_sendbuf((*stream)->quic, 1); |
428 | 18.4k | } |
429 | | |
430 | | static uint8_t *accept_hashkey_flatten_address(uint8_t *p, quicly_address_t *addr) |
431 | 0 | { |
432 | 0 | switch (addr->sa.sa_family) { |
433 | 0 | case AF_INET: |
434 | 0 | *p++ = 4; |
435 | 0 | memcpy(p, &addr->sin.sin_addr.s_addr, 4); |
436 | 0 | p += 4; |
437 | 0 | memcpy(p, &addr->sin.sin_port, 2); |
438 | 0 | p += 2; |
439 | 0 | break; |
440 | 0 | case AF_INET6: |
441 | 0 | *p++ = 6; |
442 | 0 | memcpy(p, addr->sin6.sin6_addr.s6_addr, 16); |
443 | 0 | p += 16; |
444 | 0 | memcpy(p, &addr->sin.sin_port, 2); |
445 | 0 | p += 2; |
446 | 0 | break; |
447 | 0 | case AF_UNSPEC: |
448 | 0 | *p++ = 0; |
449 | 0 | break; |
450 | 0 | default: |
451 | 0 | h2o_fatal("unknown protocol family"); |
452 | 0 | break; |
453 | 0 | } |
454 | 0 | return p; |
455 | 0 | } |
456 | | |
457 | | static uint64_t calc_accept_hashkey(quicly_address_t *destaddr, quicly_address_t *srcaddr, ptls_iovec_t src_cid) |
458 | 0 | { |
459 | | /* prepare key */ |
460 | 0 | static __thread EVP_CIPHER_CTX *cipher = NULL; |
461 | 0 | if (cipher == NULL) { |
462 | 0 | static uint8_t key[PTLS_AES128_KEY_SIZE]; |
463 | 0 | H2O_MULTITHREAD_ONCE({ ptls_openssl_random_bytes(key, sizeof(key)); }); |
464 | 0 | cipher = EVP_CIPHER_CTX_new(); |
465 | 0 | EVP_EncryptInit_ex(cipher, EVP_aes_128_cbc(), NULL, key, NULL); |
466 | 0 | } |
467 | |
|
468 | 0 | uint8_t buf[(1 + 16 + 2) * 2 + QUICLY_MAX_CID_LEN_V1 + PTLS_AES_BLOCK_SIZE] = {0}; |
469 | 0 | uint8_t *p = buf; |
470 | | |
471 | | /* build plaintext to encrypt */ |
472 | 0 | p = accept_hashkey_flatten_address(p, destaddr); |
473 | 0 | p = accept_hashkey_flatten_address(p, srcaddr); |
474 | 0 | memcpy(p, src_cid.base, src_cid.len); |
475 | 0 | p += src_cid.len; |
476 | 0 | assert(p <= buf + sizeof(buf)); |
477 | 0 | size_t bytes_to_encrypt = ((p - buf) + PTLS_AES_BLOCK_SIZE - 1) / PTLS_AES_BLOCK_SIZE * PTLS_AES_BLOCK_SIZE; |
478 | 0 | assert(bytes_to_encrypt <= sizeof(buf)); |
479 | | |
480 | 0 | { /* encrypt */ |
481 | 0 | EVP_EncryptInit_ex(cipher, NULL, NULL, NULL, NULL); |
482 | 0 | int bytes_encrypted = 0, ret = EVP_EncryptUpdate(cipher, buf, &bytes_encrypted, buf, (int)bytes_to_encrypt); |
483 | 0 | assert(ret); |
484 | 0 | assert(bytes_encrypted == bytes_to_encrypt); |
485 | 0 | } |
486 | | |
487 | | /* use the last `size_t` bytes of the CBC output as the result */ |
488 | 0 | uint64_t result; |
489 | 0 | memcpy(&result, buf + bytes_to_encrypt - sizeof(result), sizeof(result)); |
490 | | /* avoid 0 (used as nonexist) */ |
491 | 0 | if (result == 0) |
492 | 0 | result = 1; |
493 | 0 | return result; |
494 | 0 | } |
495 | | |
496 | | static void drop_from_acceptmap(h2o_quic_ctx_t *ctx, h2o_quic_conn_t *conn) |
497 | 6.16k | { |
498 | 6.16k | if (conn->_accept_hashkey != 0) { |
499 | 0 | khint_t iter; |
500 | 0 | if ((iter = kh_get_h2o_quic_acceptmap(ctx->conns_accepting, conn->_accept_hashkey)) != kh_end(ctx->conns_accepting)) |
501 | 0 | kh_del_h2o_quic_acceptmap(ctx->conns_accepting, iter); |
502 | 0 | conn->_accept_hashkey = 0; |
503 | 0 | } |
504 | 6.16k | } |
505 | | |
506 | | static void send_version_negotiation(h2o_quic_ctx_t *ctx, quicly_address_t *destaddr, ptls_iovec_t dest_cid, |
507 | | quicly_address_t *srcaddr, ptls_iovec_t src_cid, const uint32_t *versions) |
508 | 0 | { |
509 | 0 | uint8_t payload[QUICLY_MIN_CLIENT_INITIAL_SIZE]; |
510 | 0 | size_t payload_size = quicly_send_version_negotiation(ctx->quic, dest_cid, src_cid, versions, payload); |
511 | 0 | assert(payload_size != SIZE_MAX); |
512 | 0 | struct iovec vec = {.iov_base = payload, .iov_len = payload_size}; |
513 | 0 | h2o_quic_send_datagrams(ctx, destaddr, srcaddr, &vec, 1); |
514 | 0 | return; |
515 | 0 | } |
516 | | |
517 | | static void process_packets(h2o_quic_ctx_t *ctx, quicly_address_t *destaddr, quicly_address_t *srcaddr, uint8_t ttl, |
518 | | quicly_decoded_packet_t *packets, size_t num_packets) |
519 | 0 | { |
520 | 0 | h2o_quic_conn_t *conn = NULL; |
521 | 0 | size_t accepted_packet_index = SIZE_MAX; |
522 | |
|
523 | 0 | assert(num_packets != 0); |
524 | | |
525 | 0 | if (ctx->quic_stats != NULL) { |
526 | 0 | ctx->quic_stats->packet_received += num_packets; |
527 | 0 | } |
528 | |
|
529 | | #if H2O_USE_DTRACE |
530 | | if (PTLS_UNLIKELY(H2O_H3_PACKET_RECEIVE_ENABLED())) { |
531 | | for (size_t i = 0; i != num_packets; ++i) |
532 | | H2O_H3_PACKET_RECEIVE(&destaddr->sa, &srcaddr->sa, packets[i].octets.base, packets[i].octets.len); |
533 | | } |
534 | | #endif |
535 | |
|
536 | 0 | if (packets[0].cid.src.len > QUICLY_MAX_CID_LEN_V1) |
537 | 0 | return; |
538 | | |
539 | | /* find the matching connection, by first looking at the CID (all packets as client, or Handshake, 1-RTT packets as server) */ |
540 | 0 | if (packets[0].cid.dest.plaintext.node_id == ctx->next_cid->node_id && |
541 | 0 | packets[0].cid.dest.plaintext.thread_id == ctx->next_cid->thread_id) { |
542 | 0 | khiter_t iter = kh_get_h2o_quic_idmap(ctx->conns_by_id, packets[0].cid.dest.plaintext.master_id); |
543 | 0 | if (iter != kh_end(ctx->conns_by_id)) { |
544 | 0 | conn = kh_val(ctx->conns_by_id, iter); |
545 | | /* CID-based matching on Initial and 0-RTT packets should only be applied for clients */ |
546 | 0 | if (!quicly_is_client(conn->quic) && packets[0].cid.dest.might_be_client_generated) |
547 | 0 | conn = NULL; |
548 | 0 | } else if (!packets[0].cid.dest.might_be_client_generated) { |
549 | | /* send stateless reset when we could not find a matching connection for a 1 RTT packet */ |
550 | 0 | if (packets[0].octets.len >= QUICLY_STATELESS_RESET_PACKET_MIN_LEN) { |
551 | 0 | uint8_t payload[QUICLY_MIN_CLIENT_INITIAL_SIZE]; |
552 | 0 | size_t payload_size = quicly_send_stateless_reset(ctx->quic, packets[0].cid.dest.encrypted.base, payload); |
553 | 0 | if (payload_size != SIZE_MAX) { |
554 | 0 | struct iovec vec = {.iov_base = payload, .iov_len = payload_size}; |
555 | 0 | h2o_quic_send_datagrams(ctx, srcaddr, destaddr, &vec, 1); |
556 | 0 | } |
557 | 0 | } |
558 | 0 | return; |
559 | 0 | } |
560 | 0 | } else if (!packets[0].cid.dest.might_be_client_generated) { |
561 | | /* forward 1-RTT packets belonging to different nodes, threads */ |
562 | 0 | if (ttl == 0) |
563 | 0 | return; |
564 | 0 | uint64_t offending_node_id = packets[0].cid.dest.plaintext.node_id; |
565 | 0 | if (ctx->forward_packets != NULL && ctx->forward_packets(ctx, &offending_node_id, packets[0].cid.dest.plaintext.thread_id, |
566 | 0 | destaddr, srcaddr, ttl, packets, num_packets)) |
567 | 0 | return; |
568 | | /* non-authenticating 1-RTT packets are potentially stateless resets (FIXME handle them, note that we need to use a hashdos- |
569 | | * resistant hash map that also meets constant-time comparison requirements) */ |
570 | 0 | return; |
571 | 0 | } |
572 | | |
573 | 0 | if (conn == NULL) { |
574 | | /* Initial or 0-RTT packet, use 4-tuple to match the thread and the connection */ |
575 | 0 | assert(packets[0].cid.dest.might_be_client_generated); |
576 | 0 | uint64_t accept_hashkey = calc_accept_hashkey(destaddr, srcaddr, packets[0].cid.src); |
577 | 0 | if (ctx->accept_thread_divisor != 0) { |
578 | 0 | uint32_t offending_thread = accept_hashkey % ctx->accept_thread_divisor; |
579 | 0 | if (offending_thread != ctx->next_cid->thread_id) { |
580 | 0 | if (ctx->forward_packets != NULL) |
581 | 0 | ctx->forward_packets(ctx, NULL, offending_thread, destaddr, srcaddr, ttl, packets, num_packets); |
582 | 0 | return; |
583 | 0 | } |
584 | 0 | } |
585 | 0 | khiter_t iter = kh_get_h2o_quic_acceptmap(ctx->conns_accepting, accept_hashkey); |
586 | 0 | if (iter == kh_end(ctx->conns_accepting)) { |
587 | | /* a new connection for this thread (at least on this process); accept or delegate to newer process */ |
588 | 0 | if (ctx->acceptor != NULL) { |
589 | 0 | if (packets[0].version != 0 && !quicly_is_supported_version(packets[0].version)) { |
590 | 0 | send_version_negotiation(ctx, srcaddr, packets[0].cid.src, destaddr, packets[0].cid.dest.encrypted, |
591 | 0 | quicly_supported_versions); |
592 | 0 | return; |
593 | 0 | } |
594 | 0 | } else { |
595 | | /* This is the offending thread but it is not accepting, which means that the process (or the thread) is not acting |
596 | | * as a server (likely gracefully shutting down). Let the application process forward the packet to the next |
597 | | * generation. */ |
598 | 0 | if (ctx->forward_packets != NULL && |
599 | 0 | ctx->forward_packets(ctx, NULL, ctx->next_cid->thread_id, destaddr, srcaddr, ttl, packets, num_packets)) |
600 | 0 | return; |
601 | | /* If not forwarded, send rejection to the peer. A Version Negotiation packet that carries only a greasing version |
602 | | * number is used for the purpose, hoping that that signal will trigger immediate downgrade to HTTP/2, across the |
603 | | * broad spectrum of the client implementations than if CONNECTION_REFUSED is being used. */ |
604 | 0 | if (packets[0].version != 0) { |
605 | 0 | static const uint32_t no_versions[] = {0}; |
606 | 0 | send_version_negotiation(ctx, srcaddr, packets[0].cid.src, destaddr, packets[0].cid.dest.encrypted, |
607 | 0 | no_versions); |
608 | 0 | } |
609 | 0 | return; |
610 | 0 | } |
611 | | /* try to accept any of the Initial packets being received */ |
612 | 0 | size_t i; |
613 | 0 | for (i = 0; i != num_packets; ++i) |
614 | 0 | if ((packets[i].octets.base[0] & QUICLY_PACKET_TYPE_BITMASK) == QUICLY_PACKET_TYPE_INITIAL) |
615 | 0 | if ((conn = ctx->acceptor(ctx, destaddr, srcaddr, packets + i)) != NULL) { |
616 | | /* non-null generally means success, except for H2O_QUIC_ACCEPT_CONN_DECRYPTION_FAILED */ |
617 | 0 | if (conn == &h2o_quic_accept_conn_decryption_failed) { |
618 | | /* failed to decrypt Initial packet <=> it could belong to a connection on a different node; forward it |
619 | | * to the destination being claimed by the DCID */ |
620 | 0 | uint64_t offending_node_id = packets[i].cid.dest.plaintext.node_id; |
621 | 0 | uint32_t offending_thread_id = packets[i].cid.dest.plaintext.thread_id; |
622 | 0 | if (ctx->forward_packets != NULL && ttl > 0 && |
623 | 0 | (offending_node_id != ctx->next_cid->node_id || offending_thread_id != ctx->next_cid->thread_id)) |
624 | 0 | ctx->forward_packets(ctx, &offending_node_id, offending_thread_id, destaddr, srcaddr, ttl, packets, |
625 | 0 | num_packets); |
626 | 0 | return; |
627 | 0 | } |
628 | 0 | break; |
629 | 0 | } |
630 | 0 | if (conn == NULL) |
631 | 0 | return; |
632 | 0 | accepted_packet_index = i; |
633 | 0 | conn->_accept_hashkey = accept_hashkey; |
634 | 0 | int r; |
635 | 0 | iter = kh_put_h2o_quic_acceptmap(conn->ctx->conns_accepting, accept_hashkey, &r); |
636 | 0 | assert(iter != kh_end(conn->ctx->conns_accepting)); |
637 | 0 | kh_val(conn->ctx->conns_accepting, iter) = conn; |
638 | 0 | } else { |
639 | | /* existing connection */ |
640 | 0 | conn = kh_val(ctx->conns_accepting, iter); |
641 | 0 | assert(conn != NULL); |
642 | 0 | assert(!quicly_is_client(conn->quic)); |
643 | 0 | if (quicly_is_destination(conn->quic, &destaddr->sa, &srcaddr->sa, packets)) |
644 | 0 | goto Receive; |
645 | 0 | uint64_t offending_node_id = packets[0].cid.dest.plaintext.node_id; |
646 | 0 | uint32_t offending_thread_id = packets[0].cid.dest.plaintext.thread_id; |
647 | 0 | if (offending_node_id != ctx->next_cid->node_id || offending_thread_id != ctx->next_cid->thread_id) { |
648 | | /* accept key matches to a connection being established, but DCID doesn't -- likely a second (or later) Initial that |
649 | | * is supposed to be handled by another node. forward it. */ |
650 | 0 | if (ttl == 0) |
651 | 0 | return; |
652 | 0 | if (ctx->forward_packets != NULL) |
653 | 0 | ctx->forward_packets(ctx, &offending_node_id, offending_thread_id, destaddr, srcaddr, ttl, packets, |
654 | 0 | num_packets); |
655 | 0 | } |
656 | | /* regardless of forwarding outcome, we need to drop this packet as it is not for us */ |
657 | 0 | return; |
658 | 0 | } |
659 | 0 | } |
660 | | |
661 | 0 | { /* receive packets to the found connection */ |
662 | 0 | if (!quicly_is_destination(conn->quic, &destaddr->sa, &srcaddr->sa, packets)) |
663 | 0 | return; |
664 | 0 | size_t i; |
665 | 0 | Receive: |
666 | 0 | for (i = 0; i != num_packets; ++i) { |
667 | 0 | if (i != accepted_packet_index) { |
668 | 0 | quicly_error_t ret = quicly_receive(conn->quic, &destaddr->sa, &srcaddr->sa, packets + i); |
669 | 0 | switch (ret) { |
670 | 0 | case QUICLY_ERROR_STATE_EXHAUSTION: |
671 | 0 | case PTLS_ERROR_NO_MEMORY: |
672 | 0 | fprintf(stderr, "%s: `quicly_receive()` returned ret:%" PRId64 "\n", __func__, ret); |
673 | 0 | conn->callbacks->destroy_connection(conn); |
674 | 0 | return; |
675 | 0 | } |
676 | 0 | if (ret != QUICLY_ERROR_PACKET_IGNORED && ret != QUICLY_ERROR_DECRYPTION_FAILED) { |
677 | 0 | if (ctx->quic_stats != NULL) { |
678 | 0 | ++ctx->quic_stats->packet_processed; |
679 | 0 | } |
680 | 0 | } |
681 | 0 | } |
682 | 0 | } |
683 | 0 | } |
684 | | |
685 | 0 | h2o_quic_schedule_timer(conn); |
686 | 0 | if (ctx->notify_conn_update != NULL) |
687 | 0 | ctx->notify_conn_update(ctx, conn); |
688 | 0 | } |
689 | | |
690 | | void h2o_quic_read_socket(h2o_quic_ctx_t *ctx, h2o_socket_t *sock) |
691 | 0 | { |
692 | 0 | struct { |
693 | 0 | quicly_address_t destaddr, srcaddr; |
694 | 0 | struct iovec vec; |
695 | 0 | uint8_t ttl; |
696 | 0 | char controlbuf[ |
697 | 0 | #ifdef IPV6_PKTINFO |
698 | 0 | CMSG_SPACE(sizeof(struct in6_pktinfo)) |
699 | | #elif defined(IP_PKTINFO) |
700 | | CMSG_SPACE(sizeof(struct in_pktinfo)) |
701 | | #elif defined(IP_RECVDSTADDR) |
702 | | CMSG_SPACE(sizeof(struct in_addr)) |
703 | | #else |
704 | | CMSG_SPACE(1) |
705 | | #endif |
706 | 0 | ]; |
707 | 0 | uint8_t buf[1600]; |
708 | 0 | } dgrams[10]; |
709 | 0 | #ifdef __linux__ |
710 | 0 | struct mmsghdr mess[PTLS_ELEMENTSOF(dgrams)]; |
711 | | #else |
712 | | struct { |
713 | | struct msghdr msg_hdr; |
714 | | } mess[PTLS_ELEMENTSOF(dgrams)]; |
715 | | #endif |
716 | |
|
717 | 0 | #define INIT_DGRAMS(i) \ |
718 | 0 | do { \ |
719 | 0 | mess[i].msg_hdr = (struct msghdr){ \ |
720 | 0 | .msg_name = &dgrams[i].srcaddr, \ |
721 | 0 | .msg_namelen = sizeof(dgrams[i].srcaddr), \ |
722 | 0 | .msg_iov = &dgrams[i].vec, \ |
723 | 0 | .msg_iovlen = 1, \ |
724 | 0 | .msg_control = &dgrams[i].controlbuf, \ |
725 | 0 | .msg_controllen = sizeof(dgrams[i].controlbuf), \ |
726 | 0 | }; \ |
727 | 0 | memset(&dgrams[i].destaddr, 0, sizeof(dgrams[i].destaddr)); \ |
728 | 0 | dgrams[i].vec.iov_base = dgrams[i].buf; \ |
729 | 0 | dgrams[i].vec.iov_len = sizeof(dgrams[i].buf); \ |
730 | 0 | } while (0) |
731 | |
|
732 | 0 | int fd = h2o_socket_get_fd(sock); |
733 | 0 | size_t dgram_index, num_dgrams; |
734 | | |
735 | | /* Read datagrams. Sender should be provided an ACK every fraction of RTT, otherwise its behavior becomes bursty (assuming that |
736 | | * pacing is not used), rather than packets being spread across entire round-trip. To minimize the chance of us entering such |
737 | | * situation, number of datagrams being read at once is limited to `PTLS_ELEMENTSOF(dgrams)`. In other words, one ack is |
738 | | * generated for no more than every 10 ack-eliciting packets being received, unless the ack-frequency extension is used. */ |
739 | 0 | #ifdef __linux__ |
740 | 0 | { |
741 | 0 | int rret; |
742 | 0 | do { |
743 | 0 | for (dgram_index = 0; dgram_index < PTLS_ELEMENTSOF(dgrams); ++dgram_index) |
744 | 0 | INIT_DGRAMS(dgram_index); |
745 | 0 | } while ((rret = recvmmsg(fd, mess, PTLS_ELEMENTSOF(mess), 0, NULL)) < 0 && errno == EINTR); |
746 | 0 | if (rret <= 0) |
747 | 0 | goto Exit; |
748 | 0 | num_dgrams = (size_t)rret; |
749 | 0 | for (dgram_index = 0; dgram_index < num_dgrams; ++dgram_index) |
750 | 0 | dgrams[dgram_index].vec.iov_len = (size_t)mess[dgram_index].msg_len; |
751 | 0 | } |
752 | | #else |
753 | | for (dgram_index = 0; dgram_index < PTLS_ELEMENTSOF(dgrams); ++dgram_index) { |
754 | | ssize_t rret; |
755 | | do { |
756 | | INIT_DGRAMS(dgram_index); |
757 | | } while ((rret = recvmsg(fd, &mess[dgram_index].msg_hdr, 0)) < 0 && errno == EINTR); |
758 | | if (rret < 0) |
759 | | break; |
760 | | dgrams[dgram_index].vec.iov_len = rret; |
761 | | } |
762 | | num_dgrams = dgram_index; |
763 | | if (num_dgrams == 0) |
764 | | goto Exit; |
765 | | #endif |
766 | | |
767 | | /* normalize and store the obtained data into `dgrams` */ |
768 | 0 | for (dgram_index = 0; dgram_index < num_dgrams; ++dgram_index) { |
769 | 0 | { /* fetch destination address */ |
770 | 0 | struct cmsghdr *cmsg; |
771 | 0 | for (cmsg = CMSG_FIRSTHDR(&mess[dgram_index].msg_hdr); cmsg != NULL; |
772 | 0 | cmsg = CMSG_NXTHDR(&mess[dgram_index].msg_hdr, cmsg)) { |
773 | 0 | #ifdef IP_PKTINFO |
774 | 0 | if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { |
775 | 0 | dgrams[dgram_index].destaddr.sin.sin_family = AF_INET; |
776 | 0 | memcpy(&dgrams[dgram_index].destaddr.sin.sin_addr, CMSG_DATA(cmsg) + offsetof(struct in_pktinfo, ipi_addr), |
777 | 0 | sizeof(struct in_addr)); |
778 | 0 | dgrams[dgram_index].destaddr.sin.sin_port = *ctx->sock.port; |
779 | 0 | goto DestAddrFound; |
780 | 0 | } |
781 | 0 | #endif |
782 | | #ifdef IP_RECVDSTADDR |
783 | | if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { |
784 | | dgrams[dgram_index].destaddr.sin.sin_family = AF_INET; |
785 | | memcpy(&dgrams[dgram_index].destaddr.sin.sin_addr, CMSG_DATA(cmsg), sizeof(struct in_addr)); |
786 | | dgrams[dgram_index].destaddr.sin.sin_port = *ctx->sock.port; |
787 | | goto DestAddrFound; |
788 | | } |
789 | | #endif |
790 | 0 | #ifdef IPV6_PKTINFO |
791 | 0 | if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { |
792 | 0 | dgrams[dgram_index].destaddr.sin6.sin6_family = AF_INET6; |
793 | 0 | memcpy(&dgrams[dgram_index].destaddr.sin6.sin6_addr, CMSG_DATA(cmsg) + offsetof(struct in6_pktinfo, ipi6_addr), |
794 | 0 | sizeof(struct in6_addr)); |
795 | 0 | dgrams[dgram_index].destaddr.sin6.sin6_port = *ctx->sock.port; |
796 | 0 | goto DestAddrFound; |
797 | 0 | } |
798 | 0 | #endif |
799 | 0 | } |
800 | 0 | dgrams[dgram_index].destaddr.sa.sa_family = AF_UNSPEC; |
801 | 0 | DestAddrFound:; |
802 | 0 | } |
803 | 0 | dgrams[dgram_index].ttl = ctx->default_ttl; |
804 | | /* preprocess (and drop the packet if it failed) */ |
805 | 0 | if (ctx->preprocess_packet != NULL && |
806 | 0 | !ctx->preprocess_packet(ctx, &mess[dgram_index].msg_hdr, &dgrams[dgram_index].destaddr, &dgrams[dgram_index].srcaddr, |
807 | 0 | &dgrams[dgram_index].ttl)) { |
808 | 0 | dgrams[dgram_index].vec.iov_len = 0; /* mark as unused */ |
809 | 0 | } else { |
810 | 0 | assert(dgrams[dgram_index].srcaddr.sa.sa_family == AF_INET || dgrams[dgram_index].srcaddr.sa.sa_family == AF_INET6); |
811 | 0 | } |
812 | 0 | } |
813 | | |
814 | | /* convert dgrams to decoded packets and process them in group of (4-tuple, dcid) */ |
815 | 0 | quicly_decoded_packet_t packets[64]; |
816 | 0 | size_t packet_index = 0; |
817 | 0 | dgram_index = 0; |
818 | 0 | while (dgram_index < num_dgrams) { |
819 | 0 | int has_decoded = 0; /* indicates if a decoded packet belonging to a different connection is stored at |
820 | | * `packets[packet_index]` */ |
821 | | /* skip zero-sized datagrams (or the ones for which preprocessing failed) */ |
822 | 0 | if (dgrams[dgram_index].vec.iov_len == 0) { |
823 | 0 | ++dgram_index; |
824 | 0 | continue; |
825 | 0 | } |
826 | | /* dispatch packets in `packets`, if the datagram at dgram_index is from a different path */ |
827 | 0 | if (packet_index != 0) { |
828 | 0 | assert(dgram_index != 0); |
829 | | /* check source address */ |
830 | 0 | if (h2o_socket_compare_address(&dgrams[dgram_index - 1].srcaddr.sa, &dgrams[dgram_index].srcaddr.sa, 1) != 0) |
831 | 0 | goto ProcessPackets; |
832 | | /* check destination address, if available */ |
833 | 0 | if (dgrams[dgram_index - 1].destaddr.sa.sa_family == AF_UNSPEC && |
834 | 0 | dgrams[dgram_index].destaddr.sa.sa_family == AF_UNSPEC) { |
835 | | /* ok */ |
836 | 0 | } else if (h2o_socket_compare_address(&dgrams[dgram_index - 1].destaddr.sa, &dgrams[dgram_index].destaddr.sa, 1) == 0) { |
837 | | /* ok */ |
838 | 0 | } else { |
839 | 0 | goto ProcessPackets; |
840 | 0 | } |
841 | | /* TTL should be same for dispatched packets */ |
842 | 0 | if (dgrams[dgram_index - 1].ttl != dgrams[dgram_index].ttl) |
843 | 0 | goto ProcessPackets; |
844 | 0 | } |
845 | | /* decode the first packet */ |
846 | 0 | size_t payload_off = 0; |
847 | 0 | if (quicly_decode_packet(ctx->quic, packets + packet_index, dgrams[dgram_index].vec.iov_base, |
848 | 0 | dgrams[dgram_index].vec.iov_len, &payload_off) == SIZE_MAX) { |
849 | 0 | ++dgram_index; |
850 | 0 | goto ProcessPackets; |
851 | 0 | } |
852 | | /* dispatch packets in `packets` if the DCID is different, setting the `has_decoded` flag */ |
853 | 0 | if (packet_index != 0) { |
854 | 0 | const ptls_iovec_t *prev_dcid = &packets[packet_index - 1].cid.dest.encrypted, |
855 | 0 | *cur_dcid = &packets[packet_index].cid.dest.encrypted; |
856 | 0 | if (!(prev_dcid->len == cur_dcid->len && memcmp(prev_dcid->base, cur_dcid->base, prev_dcid->len) == 0)) { |
857 | 0 | has_decoded = 1; |
858 | 0 | ++dgram_index; |
859 | 0 | goto ProcessPackets; |
860 | 0 | } |
861 | 0 | } |
862 | 0 | ++packet_index; |
863 | | /* add rest of the packets */ |
864 | 0 | while (payload_off < dgrams[dgram_index].vec.iov_len && packet_index < PTLS_ELEMENTSOF(packets)) { |
865 | 0 | if (quicly_decode_packet(ctx->quic, packets + packet_index, dgrams[dgram_index].vec.iov_base, |
866 | 0 | dgrams[dgram_index].vec.iov_len, &payload_off) == SIZE_MAX) |
867 | 0 | break; |
868 | 0 | ++packet_index; |
869 | 0 | } |
870 | 0 | ++dgram_index; |
871 | | /* if we have enough room for the next datagram, that is, the expected worst case of 4 packets in a coalesced datagram, |
872 | | * continue */ |
873 | 0 | if (packet_index + 4 < PTLS_ELEMENTSOF(packets)) |
874 | 0 | continue; |
875 | | |
876 | 0 | ProcessPackets: |
877 | 0 | if (packet_index != 0) { |
878 | 0 | process_packets(ctx, &dgrams[dgram_index - 1].destaddr, &dgrams[dgram_index - 1].srcaddr, dgrams[dgram_index - 1].ttl, |
879 | 0 | packets, packet_index); |
880 | 0 | if (has_decoded) { |
881 | 0 | packets[0] = packets[packet_index]; |
882 | 0 | packet_index = 1; |
883 | 0 | } else { |
884 | 0 | packet_index = 0; |
885 | 0 | } |
886 | 0 | } |
887 | 0 | } |
888 | 0 | if (packet_index != 0) |
889 | 0 | process_packets(ctx, &dgrams[dgram_index - 1].destaddr, &dgrams[dgram_index - 1].srcaddr, dgrams[dgram_index - 1].ttl, |
890 | 0 | packets, packet_index); |
891 | |
|
892 | 0 | Exit:; |
893 | |
|
894 | 0 | #undef INIT_DGRAMS |
895 | 0 | } |
896 | | |
897 | | static void on_read(h2o_socket_t *sock, const char *err) |
898 | 0 | { |
899 | 0 | h2o_quic_ctx_t *ctx = sock->data; |
900 | 0 | h2o_quic_read_socket(ctx, sock); |
901 | 0 | } |
902 | | |
903 | | static void on_timeout(h2o_timer_t *timeout) |
904 | 17.0k | { |
905 | 17.0k | h2o_quic_conn_t *conn = H2O_STRUCT_FROM_MEMBER(h2o_quic_conn_t, _timeout, timeout); |
906 | 17.0k | h2o_quic_send(conn); |
907 | 17.0k | } |
908 | | |
909 | | int h2o_http3_read_frame(h2o_http3_read_frame_t *frame, int is_client, uint64_t stream_type, size_t max_frame_payload_size, |
910 | | const uint8_t **_src, const uint8_t *src_end, const char **err_desc) |
911 | 22.1k | { |
912 | 22.1k | const uint8_t *src = *_src; |
913 | | |
914 | 22.1k | if ((frame->type = quicly_decodev(&src, src_end)) == UINT64_MAX) |
915 | 27 | return H2O_HTTP3_ERROR_INCOMPLETE; |
916 | 22.0k | if ((frame->length = quicly_decodev(&src, src_end)) == UINT64_MAX) |
917 | 90 | return H2O_HTTP3_ERROR_INCOMPLETE; |
918 | 22.0k | frame->_header_size = (uint8_t)(src - *_src); |
919 | | |
920 | | /* read the content of the frame (unless it's a DATA frame) */ |
921 | 22.0k | frame->payload = NULL; |
922 | 22.0k | if (frame->type != H2O_HTTP3_FRAME_TYPE_DATA) { |
923 | 7.97k | if (frame->length > max_frame_payload_size) { |
924 | 148 | H2O_PROBE(H3_FRAME_RECEIVE, frame->type, NULL, frame->length); |
925 | 148 | PTLS_LOG(h2o, h3_frame_receive, { |
926 | 148 | PTLS_LOG_ELEMENT_UNSIGNED(frame_type, frame->type); |
927 | 148 | PTLS_LOG_ELEMENT_UNSIGNED(payload_len, frame->length); |
928 | 148 | }); |
929 | 0 | *err_desc = h2o_http3_err_frame_too_large; |
930 | 148 | return H2O_HTTP3_ERROR_GENERAL_PROTOCOL; /* FIXME is this the correct code? */ |
931 | 148 | } |
932 | 7.83k | if (src_end - src < frame->length) |
933 | 46 | return H2O_HTTP3_ERROR_INCOMPLETE; |
934 | 7.78k | frame->payload = src; |
935 | 7.78k | src += frame->length; |
936 | 7.78k | } |
937 | | |
938 | 21.8k | H2O_PROBE(H3_FRAME_RECEIVE, frame->type, frame->payload, frame->length); |
939 | 21.8k | PTLS_LOG(h2o, h3_frame_receive, { |
940 | 21.8k | PTLS_LOG_ELEMENT_UNSIGNED(frame_type, frame->type); |
941 | 21.8k | if (frame->payload != NULL) { |
942 | 21.8k | PTLS_LOG_APPDATA_ELEMENT_HEXDUMP(payload, frame->payload, frame->length); |
943 | 21.8k | } else { |
944 | 21.8k | PTLS_LOG_ELEMENT_UNSIGNED(payload_len, frame->length); |
945 | 21.8k | } |
946 | 21.8k | }); |
947 | | |
948 | | /* validate frame type */ |
949 | 0 | switch (frame->type) { |
950 | 0 | #define FRAME(id, req_clnt, req_srvr, ctl_clnt, ctl_srvr) \ |
951 | 19.8k | case H2O_HTTP3_FRAME_TYPE_##id: \ |
952 | 19.8k | switch (stream_type) { \ |
953 | 19.8k | case H2O_HTTP3_STREAM_TYPE_REQUEST: \ |
954 | 19.8k | if (req_clnt && !is_client) \ |
955 | 19.8k | goto Validation_Success; \ |
956 | 19.8k | if (req_srvr && is_client) \ |
957 | 12 | goto Validation_Success; \ |
958 | 12 | break; \ |
959 | 12 | case H2O_HTTP3_STREAM_TYPE_CONTROL: \ |
960 | 0 | if (ctl_clnt && !is_client) \ |
961 | 0 | goto Validation_Success; \ |
962 | 0 | if (ctl_srvr && is_client) \ |
963 | 0 | goto Validation_Success; \ |
964 | 0 | break; \ |
965 | 0 | default: \ |
966 | 0 | h2o_fatal("unexpected stream type"); \ |
967 | 0 | break; \ |
968 | 19.8k | } \ |
969 | 19.8k | break |
970 | | /* clang-format off */ |
971 | | /* +-------------------------+-------------+-------------+ |
972 | | * | | req-stream | ctrl-stream | |
973 | | * | frame +------+------+------+------+ |
974 | | * | |client|server|client|server| |
975 | | * +-------------------------+------+------+------+------+ */ |
976 | 14.0k | FRAME( DATA , 1 , 1 , 0 , 0 ); |
977 | 5.81k | FRAME( HEADERS , 1 , 1 , 0 , 0 ); |
978 | 1 | FRAME( CANCEL_PUSH , 0 , 0 , 1 , 1 ); |
979 | 4 | FRAME( SETTINGS , 0 , 0 , 1 , 1 ); |
980 | 4 | FRAME( PUSH_PROMISE , 0 , 1 , 0 , 0 ); |
981 | 3 | FRAME( GOAWAY , 0 , 0 , 1 , 1 ); |
982 | 3 | FRAME( MAX_PUSH_ID , 0 , 0 , 1 , 0 ); |
983 | 1 | FRAME( PRIORITY_UPDATE_REQUEST , 0 , 0 , 1 , 0 ); |
984 | 1 | FRAME( PRIORITY_UPDATE_PUSH , 0 , 0 , 1 , 0 ); |
985 | | /* +-------------------------+------+------+------+------+ */ |
986 | | /* clang-format on */ |
987 | 1 | #undef FRAME |
988 | 1.96k | default: |
989 | | /* ignore extension frames that we do not handle */ |
990 | 1.96k | goto Validation_Success; |
991 | 21.8k | } |
992 | 12 | return H2O_HTTP3_ERROR_FRAME_UNEXPECTED; |
993 | 21.7k | Validation_Success:; |
994 | | |
995 | 21.7k | *_src = src; |
996 | 21.7k | return 0; |
997 | 21.8k | } |
998 | | |
999 | | void h2o_quic_init_context(h2o_quic_ctx_t *ctx, h2o_loop_t *loop, h2o_socket_t *sock, quicly_context_t *quic, |
1000 | | quicly_cid_plaintext_t *next_cid, h2o_quic_accept_cb acceptor, |
1001 | | h2o_quic_notify_connection_update_cb notify_conn_update, uint8_t use_gso, h2o_quic_stats_t *quic_stats) |
1002 | 0 | { |
1003 | 0 | assert(quic->stream_open != NULL); |
1004 | | |
1005 | 0 | *ctx = (h2o_quic_ctx_t){ |
1006 | 0 | .loop = loop, |
1007 | 0 | .sock = {.sock = sock}, |
1008 | 0 | .quic = quic, |
1009 | 0 | .next_cid = next_cid, |
1010 | 0 | .conns_by_id = kh_init_h2o_quic_idmap(), |
1011 | 0 | .conns_accepting = kh_init_h2o_quic_acceptmap(), |
1012 | 0 | .notify_conn_update = notify_conn_update, |
1013 | 0 | .acceptor = acceptor, |
1014 | 0 | .use_gso = use_gso, |
1015 | 0 | .quic_stats = quic_stats, |
1016 | 0 | }; |
1017 | 0 | ctx->sock.sock->data = ctx; |
1018 | 0 | ctx->sock.addrlen = h2o_socket_getsockname(ctx->sock.sock, (void *)&ctx->sock.addr); |
1019 | 0 | assert(ctx->sock.addrlen != 0); |
1020 | 0 | switch (ctx->sock.addr.ss_family) { |
1021 | 0 | case AF_INET: |
1022 | 0 | ctx->sock.port = &((struct sockaddr_in *)&ctx->sock.addr)->sin_port; |
1023 | 0 | break; |
1024 | 0 | case AF_INET6: |
1025 | 0 | ctx->sock.port = &((struct sockaddr_in6 *)&ctx->sock.addr)->sin6_port; |
1026 | 0 | break; |
1027 | 0 | default: |
1028 | 0 | assert(!"unexpected address family"); |
1029 | 0 | break; |
1030 | 0 | } |
1031 | | |
1032 | 0 | h2o_socket_read_start(ctx->sock.sock, on_read); |
1033 | 0 | } |
1034 | | |
1035 | | void h2o_quic_dispose_context(h2o_quic_ctx_t *ctx) |
1036 | 0 | { |
1037 | 0 | assert(kh_size(ctx->conns_by_id) == 0); |
1038 | 0 | assert(kh_size(ctx->conns_accepting) == 0); |
1039 | | |
1040 | 0 | h2o_socket_close(ctx->sock.sock); |
1041 | 0 | kh_destroy_h2o_quic_idmap(ctx->conns_by_id); |
1042 | 0 | kh_destroy_h2o_quic_acceptmap(ctx->conns_accepting); |
1043 | 0 | } |
1044 | | |
1045 | | void h2o_quic_set_forwarding_context(h2o_quic_ctx_t *ctx, uint32_t accept_thread_divisor, uint8_t ttl, |
1046 | | h2o_quic_forward_packets_cb forward_cb, h2o_quic_preprocess_packet_cb preprocess_cb) |
1047 | 0 | { |
1048 | 0 | ctx->accept_thread_divisor = accept_thread_divisor; |
1049 | 0 | ctx->forward_packets = forward_cb; |
1050 | 0 | ctx->default_ttl = ttl; |
1051 | 0 | ctx->preprocess_packet = preprocess_cb; |
1052 | 0 | } |
1053 | | |
1054 | | void h2o_quic_close_connection(h2o_quic_conn_t *conn, quicly_error_t err, const char *reason_phrase) |
1055 | 4.01k | { |
1056 | 4.01k | switch (quicly_get_state(conn->quic)) { |
1057 | 0 | case QUICLY_STATE_FIRSTFLIGHT: /* FIXME why is this separate? */ |
1058 | 0 | conn->callbacks->destroy_connection(conn); |
1059 | 0 | break; |
1060 | 4.01k | case QUICLY_STATE_CONNECTED: |
1061 | 4.01k | quicly_close(conn->quic, err, reason_phrase); |
1062 | 4.01k | h2o_quic_schedule_timer(conn); |
1063 | 4.01k | break; |
1064 | 0 | default: |
1065 | | /* only need to wait for the socket close */ |
1066 | 0 | break; |
1067 | 4.01k | } |
1068 | 4.01k | } |
1069 | | |
1070 | | void h2o_quic_close_all_connections(h2o_quic_ctx_t *ctx) |
1071 | 0 | { |
1072 | 0 | h2o_quic_conn_t *conn; |
1073 | |
|
1074 | 0 | kh_foreach_value(ctx->conns_by_id, conn, { h2o_quic_close_connection(conn, 0, NULL); }); |
1075 | | /* closing a connection should also remove an entry from conns_accepting */ |
1076 | 0 | assert(kh_size(ctx->conns_accepting) == 0); |
1077 | 0 | } |
1078 | | |
1079 | | size_t h2o_quic_num_connections(h2o_quic_ctx_t *ctx) |
1080 | 0 | { |
1081 | | /* throughout its lifetime, a connection is always registered to both conns_by_id and conns_accepting, |
1082 | | thus counting conns_by_id is enough */ |
1083 | 0 | return kh_size(ctx->conns_by_id); |
1084 | 0 | } |
1085 | | |
1086 | | void h2o_quic_init_conn(h2o_quic_conn_t *conn, h2o_quic_ctx_t *ctx, const h2o_quic_conn_callbacks_t *callbacks) |
1087 | 6.16k | { |
1088 | 6.16k | *conn = (h2o_quic_conn_t){ctx, NULL, callbacks}; |
1089 | 6.16k | h2o_timer_init(&conn->_timeout, on_timeout); |
1090 | 6.16k | } |
1091 | | |
1092 | | void h2o_quic_dispose_conn(h2o_quic_conn_t *conn) |
1093 | 6.16k | { |
1094 | 6.16k | if (conn->quic != NULL) { |
1095 | 6.16k | khiter_t iter; |
1096 | | /* unregister from maps */ |
1097 | 6.16k | if ((iter = kh_get_h2o_quic_idmap(conn->ctx->conns_by_id, quicly_get_master_id(conn->quic)->master_id)) != |
1098 | 6.16k | kh_end(conn->ctx->conns_by_id)) |
1099 | 6.16k | kh_del_h2o_quic_idmap(conn->ctx->conns_by_id, iter); |
1100 | 6.16k | drop_from_acceptmap(conn->ctx, conn); |
1101 | 6.16k | quicly_free(conn->quic); |
1102 | 6.16k | } |
1103 | 6.16k | h2o_timer_unlink(&conn->_timeout); |
1104 | 6.16k | } |
1105 | | |
1106 | | void h2o_quic_setup(h2o_quic_conn_t *conn, quicly_conn_t *quic) |
1107 | 6.16k | { |
1108 | | /* Setup relation between `h2o_quic_conn_t` and `quicly_conn_t`. At this point, `conn` will not have `quic` associated, though |
1109 | | * the back pointer might have alreday been set up (see how we call `quicly_accept`). */ |
1110 | 6.16k | assert(conn->quic == NULL); |
1111 | 6.16k | void **backptr = quicly_get_data(quic); |
1112 | 6.16k | if (*backptr == NULL) { |
1113 | 6.16k | *backptr = conn; |
1114 | 6.16k | } else { |
1115 | 0 | assert(*backptr == conn); |
1116 | 0 | } |
1117 | 6.16k | conn->quic = quic; |
1118 | | |
1119 | | /* register to the idmap */ |
1120 | 6.16k | int r; |
1121 | 6.16k | khiter_t iter = kh_put_h2o_quic_idmap(conn->ctx->conns_by_id, quicly_get_master_id(conn->quic)->master_id, &r); |
1122 | 6.16k | assert(iter != kh_end(conn->ctx->conns_by_id)); |
1123 | 6.16k | kh_val(conn->ctx->conns_by_id, iter) = conn; |
1124 | 6.16k | } |
1125 | | |
1126 | | void h2o_http3_init_conn(h2o_http3_conn_t *conn, h2o_quic_ctx_t *ctx, const h2o_http3_conn_callbacks_t *callbacks, |
1127 | | const h2o_http3_qpack_context_t *qpack_ctx, size_t max_frame_payload_size) |
1128 | 6.16k | { |
1129 | 6.16k | h2o_quic_init_conn(&conn->super, ctx, &callbacks->super); |
1130 | 6.16k | memset((char *)conn + sizeof(conn->super), 0, sizeof(*conn) - sizeof(conn->super)); |
1131 | 6.16k | conn->qpack.ctx = qpack_ctx; |
1132 | 6.16k | conn->max_frame_payload_size = max_frame_payload_size; |
1133 | 6.16k | } |
1134 | | |
1135 | | void h2o_http3_dispose_conn(h2o_http3_conn_t *conn) |
1136 | 6.16k | { |
1137 | 6.16k | if (conn->qpack.dec != NULL) |
1138 | 6.16k | h2o_qpack_destroy_decoder(conn->qpack.dec); |
1139 | 6.16k | if (conn->qpack.enc != NULL) |
1140 | 0 | h2o_qpack_destroy_encoder(conn->qpack.enc); |
1141 | 6.16k | h2o_quic_dispose_conn(&conn->super); |
1142 | 6.16k | } |
1143 | | |
1144 | | static size_t build_firstflight(h2o_http3_conn_t *conn, uint8_t *bytebuf, size_t capacity) |
1145 | 6.16k | { |
1146 | 6.16k | ptls_buffer_t buf; |
1147 | 6.16k | int ret = 0; |
1148 | | |
1149 | 6.16k | ptls_buffer_init(&buf, bytebuf, capacity); |
1150 | | |
1151 | | /* push stream type */ |
1152 | 6.16k | ptls_buffer_push_quicint(&buf, H2O_HTTP3_STREAM_TYPE_CONTROL); |
1153 | | |
1154 | | /* push SETTINGS frame */ |
1155 | 6.16k | ptls_buffer_push_quicint(&buf, H2O_HTTP3_FRAME_TYPE_SETTINGS); |
1156 | 6.16k | ptls_buffer_push_block(&buf, -1, { |
1157 | 6.16k | quicly_context_t *qctx = quicly_get_context(conn->super.quic); |
1158 | 6.16k | if (qctx->transport_params.max_datagram_frame_size != 0) { |
1159 | | // advertise that we are prepared to receive both RFC and draft-03 datagram formats |
1160 | 6.16k | ptls_buffer_push_quicint(&buf, H2O_HTTP3_SETTINGS_H3_DATAGRAM); |
1161 | 6.16k | ptls_buffer_push_quicint(&buf, 1); |
1162 | 6.16k | ptls_buffer_push_quicint(&buf, H2O_HTTP3_SETTINGS_H3_DATAGRAM_DRAFT03); |
1163 | 6.16k | ptls_buffer_push_quicint(&buf, 1); |
1164 | 6.16k | }; |
1165 | 6.16k | ptls_buffer_push_quicint(&buf, H2O_HTTP3_SETTINGS_ENABLE_CONNECT_PROTOCOL); |
1166 | 6.16k | ptls_buffer_push_quicint(&buf, 1); |
1167 | 6.16k | }); |
1168 | | |
1169 | 6.16k | assert(!buf.is_allocated); |
1170 | 6.16k | return buf.off; |
1171 | | |
1172 | 0 | Exit: |
1173 | 0 | h2o_fatal("unreachable"); |
1174 | 6.16k | } |
1175 | | |
1176 | | quicly_error_t h2o_http3_setup(h2o_http3_conn_t *conn, quicly_conn_t *quic) |
1177 | 6.16k | { |
1178 | 6.16k | quicly_error_t ret; |
1179 | | |
1180 | 6.16k | h2o_quic_setup(&conn->super, quic); |
1181 | 6.16k | conn->state = H2O_HTTP3_CONN_STATE_OPEN; |
1182 | | |
1183 | | /* setup h3 objects, only when the connection state has been created */ |
1184 | 6.16k | if (quicly_get_state(quic) > QUICLY_STATE_CONNECTED) |
1185 | 0 | goto Exit; |
1186 | | |
1187 | | /* create decoder with the table size set to zero; see SETTINGS sent below. */ |
1188 | 6.16k | conn->qpack.dec = h2o_qpack_create_decoder(0, 100 /* FIXME */); |
1189 | | |
1190 | 6.16k | { /* open control streams, send SETTINGS */ |
1191 | 6.16k | uint8_t firstflight[32]; |
1192 | 6.16k | size_t firstflight_len = build_firstflight(conn, firstflight, sizeof(firstflight)); |
1193 | 6.16k | if ((ret = open_egress_unistream(conn, &conn->_control_streams.egress.control, |
1194 | 6.16k | h2o_iovec_init(firstflight, firstflight_len))) != 0) |
1195 | 0 | return ret; |
1196 | 6.16k | } |
1197 | | |
1198 | 6.16k | { /* open QPACK encoder & decoder streams */ |
1199 | 6.16k | static const uint8_t encoder_first_flight[] = {H2O_HTTP3_STREAM_TYPE_QPACK_ENCODER}; |
1200 | 6.16k | static const uint8_t decoder_first_flight[] = {H2O_HTTP3_STREAM_TYPE_QPACK_DECODER}; |
1201 | 6.16k | if ((ret = open_egress_unistream(conn, &conn->_control_streams.egress.qpack_encoder, |
1202 | 6.16k | h2o_iovec_init(encoder_first_flight, sizeof(encoder_first_flight)))) != 0 || |
1203 | 6.16k | (ret = open_egress_unistream(conn, &conn->_control_streams.egress.qpack_decoder, |
1204 | 6.16k | h2o_iovec_init(decoder_first_flight, sizeof(decoder_first_flight)))) != 0) |
1205 | 0 | return ret; |
1206 | 6.16k | } |
1207 | | |
1208 | 6.16k | Exit: |
1209 | 6.16k | h2o_quic_schedule_timer(&conn->super); |
1210 | 6.16k | return 0; |
1211 | 6.16k | } |
1212 | | |
1213 | | quicly_error_t h2o_quic_send(h2o_quic_conn_t *conn) |
1214 | 23.2k | { |
1215 | 23.2k | quicly_address_t dest, src; |
1216 | 23.2k | struct iovec datagrams[10]; |
1217 | 23.2k | size_t num_datagrams = PTLS_ELEMENTSOF(datagrams); |
1218 | 23.2k | uint8_t datagram_buf[1500 * PTLS_ELEMENTSOF(datagrams)]; |
1219 | | |
1220 | 23.2k | quicly_error_t ret = quicly_send(conn->quic, &dest, &src, datagrams, &num_datagrams, datagram_buf, sizeof(datagram_buf)); |
1221 | 23.2k | switch (ret) { |
1222 | 17.0k | case 0: |
1223 | 17.0k | if (num_datagrams != 0 && !h2o_quic_send_datagrams(conn->ctx, &dest, &src, datagrams, num_datagrams)) { |
1224 | | /* FIXME close the connection immediately */ |
1225 | 0 | break; |
1226 | 0 | } |
1227 | 17.0k | break; |
1228 | 17.0k | case QUICLY_ERROR_STATE_EXHAUSTION: |
1229 | 6.16k | case QUICLY_ERROR_FREE_CONNECTION: |
1230 | 6.16k | conn->callbacks->destroy_connection(conn); |
1231 | 6.16k | return 0; |
1232 | 0 | default: |
1233 | 0 | h2o_fatal("quicly_send returned %" PRId64, ret); |
1234 | 23.2k | } |
1235 | | |
1236 | 17.0k | h2o_quic_schedule_timer(conn); |
1237 | | |
1238 | 17.0k | return 1; |
1239 | 23.2k | } |
1240 | | |
1241 | | void h2o_http3_update_recvbuf(h2o_buffer_t **buf, size_t off, const void *src, size_t len) |
1242 | 6.16k | { |
1243 | 6.16k | size_t new_size = off + len; |
1244 | | |
1245 | 6.16k | if ((*buf)->size < new_size) { |
1246 | 6.16k | h2o_buffer_reserve(buf, new_size - (*buf)->size); |
1247 | 6.16k | (*buf)->size = new_size; |
1248 | 6.16k | } |
1249 | 6.16k | memcpy((*buf)->bytes + off, src, len); |
1250 | 6.16k | } |
1251 | | |
1252 | | void h2o_quic_schedule_timer(h2o_quic_conn_t *conn) |
1253 | 30.9k | { |
1254 | 30.9k | int64_t timeout = quicly_get_first_timeout(conn->quic); |
1255 | 30.9k | if (h2o_timer_is_linked(&conn->_timeout)) { |
1256 | | #if !H2O_USE_LIBUV /* optimization to skip registering a timer specifying the same time */ |
1257 | 13.8k | if (timeout == conn->_timeout.expire_at) |
1258 | 13.7k | return; |
1259 | 89 | #endif |
1260 | 89 | h2o_timer_unlink(&conn->_timeout); |
1261 | 89 | } |
1262 | 17.1k | uint64_t now = h2o_now(conn->ctx->loop), delay = now < timeout ? timeout - now : 0; |
1263 | 17.1k | h2o_timer_link(conn->ctx->loop, delay, &conn->_timeout); |
1264 | 17.1k | } |
1265 | | |
1266 | | int h2o_http3_handle_settings_frame(h2o_http3_conn_t *conn, const uint8_t *payload, size_t length, const char **err_desc) |
1267 | 0 | { |
1268 | 0 | const uint8_t *src = payload, *src_end = src + length; |
1269 | 0 | uint32_t header_table_size = 0; |
1270 | 0 | uint64_t blocked_streams = 0; |
1271 | |
|
1272 | 0 | assert(!h2o_http3_has_received_settings(conn)); |
1273 | | |
1274 | 0 | while (src != src_end) { |
1275 | 0 | uint64_t id; |
1276 | 0 | uint64_t value; |
1277 | 0 | if ((id = quicly_decodev(&src, src_end)) == UINT64_MAX) |
1278 | 0 | goto Malformed; |
1279 | 0 | if ((value = quicly_decodev(&src, src_end)) == UINT64_MAX) |
1280 | 0 | goto Malformed; |
1281 | 0 | switch (id) { |
1282 | 0 | case H2O_HTTP3_SETTINGS_MAX_FIELD_SECTION_SIZE: |
1283 | 0 | conn->peer_settings.max_field_section_size = value; |
1284 | 0 | break; |
1285 | 0 | case H2O_HTTP3_SETTINGS_QPACK_MAX_TABLE_CAPACITY: |
1286 | 0 | header_table_size = |
1287 | 0 | value < conn->qpack.ctx->encoder_table_capacity ? (uint32_t)value : conn->qpack.ctx->encoder_table_capacity; |
1288 | 0 | break; |
1289 | 0 | case H2O_HTTP3_SETTINGS_QPACK_BLOCKED_STREAMS: |
1290 | 0 | blocked_streams = value; |
1291 | 0 | break; |
1292 | 0 | case H2O_HTTP3_SETTINGS_H3_DATAGRAM: |
1293 | 0 | case H2O_HTTP3_SETTINGS_H3_DATAGRAM_DRAFT03: |
1294 | 0 | switch (value) { |
1295 | 0 | case 0: |
1296 | 0 | break; |
1297 | 0 | case 1: { |
1298 | 0 | const quicly_transport_parameters_t *remote_tp = quicly_get_remote_transport_parameters(conn->super.quic); |
1299 | 0 | if (remote_tp->max_datagram_frame_size == 0) |
1300 | 0 | goto Malformed; |
1301 | 0 | conn->peer_settings.h3_datagram = 1; |
1302 | 0 | } break; |
1303 | 0 | default: |
1304 | 0 | goto Malformed; |
1305 | 0 | } |
1306 | 0 | break; |
1307 | 0 | default: |
1308 | 0 | break; |
1309 | 0 | } |
1310 | 0 | } |
1311 | | |
1312 | 0 | conn->qpack.enc = h2o_qpack_create_encoder(header_table_size, blocked_streams); |
1313 | 0 | return 0; |
1314 | 0 | Malformed: |
1315 | 0 | *err_desc = "malformed SETTINGS frame"; |
1316 | 0 | return H2O_HTTP3_ERROR_FRAME; |
1317 | 0 | } |
1318 | | |
1319 | | void h2o_http3_send_qpack_stream_cancel(h2o_http3_conn_t *conn, quicly_stream_id_t stream_id) |
1320 | 0 | { |
1321 | 0 | struct st_h2o_http3_egress_unistream_t *stream = conn->_control_streams.egress.qpack_decoder; |
1322 | | |
1323 | | /* allocate and write */ |
1324 | 0 | h2o_iovec_t buf = h2o_buffer_reserve(&stream->sendbuf, stream->sendbuf->size + H2O_HPACK_ENCODE_INT_MAX_LENGTH); |
1325 | 0 | assert(buf.base != NULL); |
1326 | 0 | stream->sendbuf->size += h2o_qpack_decoder_send_stream_cancel(conn->qpack.dec, (uint8_t *)buf.base, stream_id); |
1327 | | |
1328 | | /* notify the transport */ |
1329 | 0 | H2O_HTTP3_CHECK_SUCCESS(quicly_stream_sync_sendbuf(stream->quic, 1) == 0); |
1330 | 0 | } |
1331 | | |
1332 | | void h2o_http3_send_qpack_header_ack(h2o_http3_conn_t *conn, const void *bytes, size_t len) |
1333 | 0 | { |
1334 | 0 | struct st_h2o_http3_egress_unistream_t *stream = conn->_control_streams.egress.qpack_encoder; |
1335 | |
|
1336 | 0 | assert(stream != NULL); |
1337 | 0 | h2o_buffer_append(&stream->sendbuf, bytes, len); |
1338 | 0 | H2O_HTTP3_CHECK_SUCCESS(quicly_stream_sync_sendbuf(stream->quic, 1)); |
1339 | 0 | } |
1340 | | |
1341 | | void h2o_http3_send_shutdown_goaway_frame(h2o_http3_conn_t *conn) |
1342 | 0 | { |
1343 | | /* There is a moment where the transport-level close has been initiated while st_h2o_http3_server_conn_t remains. |
1344 | | * Check QUIC connection state to skip sending GOAWAY in such a case. */ |
1345 | 0 | if (conn->state < H2O_HTTP3_CONN_STATE_HALF_CLOSED && quicly_get_state(conn->super.quic) == QUICLY_STATE_CONNECTED) { |
1346 | | /* advertise the maximum stream ID to indicate that we will no longer accept new requests. |
1347 | | * HTTP/3 draft section 5.2.8 -- |
1348 | | * "An endpoint that is attempting to gracefully shut down a connection can send a GOAWAY frame with a value set to the |
1349 | | * maximum possible value (2^62-4 for servers, 2^62-1 for clients). This ensures that the peer stops creating new |
1350 | | * requests or pushes." */ |
1351 | 0 | h2o_http3_send_goaway_frame(conn, (UINT64_C(1) << 62) - 4); |
1352 | 0 | } |
1353 | 0 | } |
1354 | | |
1355 | | void h2o_http3_send_goaway_frame(h2o_http3_conn_t *conn, uint64_t stream_or_push_id) |
1356 | 0 | { |
1357 | 0 | size_t cap = h2o_http3_goaway_frame_capacity(stream_or_push_id); |
1358 | 0 | h2o_iovec_t alloced = h2o_buffer_reserve(&conn->_control_streams.egress.control->sendbuf, cap); |
1359 | 0 | h2o_http3_encode_goaway_frame((uint8_t *)alloced.base, stream_or_push_id); |
1360 | 0 | conn->_control_streams.egress.control->sendbuf->size += cap; |
1361 | 0 | quicly_stream_sync_sendbuf(conn->_control_streams.egress.control->quic, 1); |
1362 | 0 | } |
1363 | | |
1364 | | void h2o_http3_send_h3_datagrams(h2o_http3_conn_t *conn, uint64_t flow_id, h2o_iovec_t *datagrams, size_t num_datagrams) |
1365 | 0 | { |
1366 | 0 | for (size_t i = 0; i < num_datagrams; ++i) { |
1367 | 0 | h2o_iovec_t *src = datagrams + i; |
1368 | 0 | uint8_t buf[quicly_encodev_capacity(flow_id) + src->len], *p = buf; |
1369 | 0 | p = quicly_encodev(p, flow_id); |
1370 | 0 | memcpy(p, src->base, src->len); |
1371 | 0 | p += src->len; |
1372 | 0 | ptls_iovec_t payload = ptls_iovec_init(buf, p - buf); |
1373 | 0 | quicly_send_datagram_frames(conn->super.quic, &payload, 1); |
1374 | 0 | } |
1375 | |
|
1376 | 0 | h2o_quic_schedule_timer(&conn->super); |
1377 | 0 | } |
1378 | | |
1379 | | uint64_t h2o_http3_decode_h3_datagram(h2o_iovec_t *payload, const void *_src, size_t len) |
1380 | 0 | { |
1381 | 0 | const uint8_t *src = _src, *end = src + len; |
1382 | 0 | uint64_t flow_id; |
1383 | |
|
1384 | 0 | if ((flow_id = ptls_decode_quicint(&src, end)) != UINT64_MAX) |
1385 | 0 | *payload = h2o_iovec_init(src, end - src); |
1386 | 0 | return flow_id; |
1387 | 0 | } |