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