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