ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
lib/network/quic/connection.c
Go to the documentation of this file.
1#include "connection.h"
2#include "datagram.h"
6
7#include <ascii-chat/buffer.h>
8#include <ascii-chat/common.h>
11#include <ascii-chat/util/ip.h>
12#include <ngtcp2/ngtcp2_crypto_ossl.h>
13#include <sodium.h>
14#include <string.h>
15
16#define QUIC_CONNECTION_ID_LENGTH 16
17#define QUIC_INITIAL_STREAM_WINDOW (5U * 1024U * 1024U)
18#define QUIC_INITIAL_CONNECTION_WINDOW (16U * 1024U * 1024U)
19#define QUIC_IDLE_TIMEOUT_NS (30ULL * 1000000000ULL)
20#define QUIC_FLOW_COUNT 3
21#define QUIC_DATAGRAM_FRAME_LIMIT 1400U
22#define QUIC_DATAGRAM_OVERHEAD 128U
23#define QUIC_HTTP3_PEER_UNI_STREAMS 6U
24
33
51
63
64typedef struct {
65 int64_t id;
69 unsigned kind; /* 0 undecided, 1 HTTP/3, 2 WebTransport */
70 bool active;
72
80
128
129static asciichat_error_t update_readiness(quic_connection_t *connection);
130static asciichat_error_t queue_wire_packet(quic_connection_t *connection, acip_traffic_class_t traffic_class,
131 const void *packet, size_t length);
132static asciichat_error_t queue_wire_packet_internal(quic_connection_t *connection, acip_traffic_class_t traffic_class,
133 const void *packet, size_t length, bool internal);
134static const uint8_t quic_ready_payload[] = {'A', 'C', 'Q', 'R', 1};
135static const uint8_t quic_ack_payload[] = {'A', 'C', 'Q', 'A', 1};
136
137static asciichat_error_t queue_keyframe_request(quic_connection_t *connection, uint16_t type, uint32_t id) {
138 uint8_t payload[6];
139 uint16_t network_type = HOST_TO_NET_U16(type);
140 uint32_t network_id = HOST_TO_NET_U32(id);
141 memcpy(payload, &network_type, sizeof(network_type));
142 memcpy(payload + sizeof(network_type), &network_id, sizeof(network_id));
143 uint8_t packet[sizeof(packet_header_t) + sizeof(payload)];
144 size_t packet_length = 0;
145 asciichat_error_t result = packet_encode(PACKET_TYPE_QUIC_KEYFRAME_REQUEST, payload, sizeof(payload), 0, packet,
146 sizeof(packet), &packet_length);
147 if (result != ASCIICHAT_OK)
148 return result;
149 uint8_t *wire = NULL;
150 size_t wire_length = 0;
151 result = acip_security_seal(connection->security, ACIP_TRAFFIC_CONTROL, ACIP_DELIVERY_RELIABLE, packet, packet_length,
152 &wire, &wire_length);
153 if (result == ASCIICHAT_OK)
154 result = queue_wire_packet(connection, ACIP_TRAFFIC_CONTROL, wire, wire_length);
155 buffer_release(wire);
156 return result;
157}
158
159static asciichat_error_t queue_security_barrier(quic_connection_t *connection, const uint8_t *payload,
160 size_t payload_length) {
161 uint8_t marker[sizeof(packet_header_t) + sizeof(quic_ready_payload)];
162 if (payload_length != sizeof(quic_ready_payload))
163 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC readiness marker");
164 size_t marker_length = 0;
165 asciichat_error_t result =
166 packet_encode(PACKET_TYPE_QUIC_READY, payload, payload_length, 0, marker, sizeof(marker), &marker_length);
167 if (result != ASCIICHAT_OK)
168 return result;
169 uint8_t *wire = NULL;
170 size_t wire_length = 0;
171 result = acip_security_seal(connection->security, ACIP_TRAFFIC_CONTROL, ACIP_DELIVERY_RELIABLE, marker, marker_length,
172 &wire, &wire_length);
173 if (result == ASCIICHAT_OK)
174 result = queue_wire_packet(connection, ACIP_TRAFFIC_CONTROL, wire, wire_length);
175 buffer_release(wire);
176 return result;
177}
178
179static asciichat_error_t open_security_packet(quic_connection_t *connection, acip_traffic_class_t traffic_class,
180 const void *packet, size_t size) {
181 uint8_t *plaintext = NULL;
182 size_t plaintext_length = 0;
183 bool consumed = false;
184 asciichat_error_t result = acip_security_open(connection->security, traffic_class, ACIP_DELIVERY_RELIABLE, packet,
185 size, &plaintext, &plaintext_length, NULL, &consumed);
186 if (result != ASCIICHAT_OK || consumed) {
187 buffer_release(plaintext);
188 return result;
189 }
190 packet_header_t header;
191 memcpy(&header, plaintext, sizeof(header));
192 packet_type_t type = NET_TO_HOST_U16(header.type);
193 if (type == PACKET_TYPE_QUIC_READY) {
194 const uint8_t *payload = plaintext + sizeof(header);
195 if (traffic_class != ACIP_TRAFFIC_CONTROL || plaintext_length != sizeof(header) + sizeof(quic_ready_payload)) {
196 result = SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid QUIC readiness barrier");
197 } else if (!connection->peer_ready_seen && memcmp(payload, quic_ready_payload, sizeof(quic_ready_payload)) == 0) {
198 connection->peer_ready_seen = true;
199 result = queue_security_barrier(connection, quic_ack_payload, sizeof(quic_ack_payload));
200 } else if (connection->peer_ready_seen && !connection->peer_security_ready &&
201 memcmp(payload, quic_ack_payload, sizeof(quic_ack_payload)) == 0) {
202 connection->peer_security_ready = true;
203 if (connection->security_notify)
204 connection->security_notify(connection->security_notify_context);
205 } else {
206 result = SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Unexpected QUIC readiness barrier");
207 }
208 } else if (!connection->peer_ready_seen) {
209 result = SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Missing authenticated QUIC readiness barrier");
210 } else if (acip_security_rekey_packet(type)) {
211 result = SET_ERRNO(ERROR_NETWORK_PROTOCOL, "QUIC rekey before security readiness");
212 } else if (type == PACKET_TYPE_QUIC_KEYFRAME_REQUEST) {
213 uint16_t requested_type;
214 uint32_t stream_id;
215 if (traffic_class != ACIP_TRAFFIC_CONTROL || plaintext_length != sizeof(header) + 6) {
216 result = SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid QUIC keyframe request");
217 } else {
218 memcpy(&requested_type, plaintext + sizeof(header), sizeof(requested_type));
219 memcpy(&stream_id, plaintext + sizeof(header) + sizeof(requested_type), sizeof(stream_id));
220 requested_type = NET_TO_HOST_U16(requested_type);
221 stream_id = NET_TO_HOST_U32(stream_id);
222 if ((requested_type != PACKET_TYPE_IMAGE_FRAME_H264 && requested_type != PACKET_TYPE_IMAGE_FRAME_H265) ||
223 stream_id != 0)
224 result = SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Unsupported QUIC keyframe request");
225 else if (connection->keyframe_notify)
226 connection->keyframe_notify(requested_type, stream_id, connection->keyframe_notify_context);
227 }
228 } else {
229 result = connection->emit(traffic_class, plaintext, plaintext_length, connection->emit_context);
230 }
231 buffer_release(plaintext);
232 return result;
233}
234
235static bool initial_handshake_packet(packet_type_t type) {
239}
240
241static void free_chunk(quic_connection_t *connection, send_chunk_t *chunk) {
242 connection->pending_bytes -= chunk->cost;
243 buffer_release(chunk->data);
244 SAFE_FREE(chunk);
245}
246
247static void free_unqueued_chunk(send_chunk_t *chunk) {
248 if (chunk) {
249 buffer_release(chunk->data);
250 SAFE_FREE(chunk);
251 }
252}
253
254static send_chunk_t *copy_chunk(const void *data, size_t length) {
255 send_chunk_t *chunk = SAFE_CALLOC(1, sizeof(*chunk), send_chunk_t *);
256 if (!chunk)
257 return NULL;
258 chunk->data = buffer_alloc(NULL, length);
259 if (!chunk->data) {
260 SAFE_FREE(chunk);
261 return NULL;
262 }
263 memcpy(chunk->data, data, length);
264 chunk->length = length;
265 chunk->cost = buffer_capacity(chunk->data) + sizeof(*chunk);
266 return chunk;
267}
268
269static void append_chunk(quic_connection_t *connection, send_stream_t *stream, send_chunk_t *chunk) {
270 stream->queued_offset += chunk->length;
271 chunk->end_offset = stream->queued_offset;
272 if (stream->tail)
273 stream->tail->next = chunk;
274 else
275 stream->head = chunk;
276 stream->tail = chunk;
277 connection->pending_bytes += chunk->cost;
278}
279
280static int acknowledged_stream_data(ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t length,
281 void *user_data, void *stream_user_data) {
282 (void)conn;
283 (void)stream_user_data;
284 quic_connection_t *connection = user_data;
285 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i) {
286 send_stream_t *stream = &connection->sends[i];
287 if (stream->id != stream_id)
288 continue;
289 if (offset != stream->acked_offset || offset > stream->queued_offset || length > stream->queued_offset - offset)
290 return NGTCP2_ERR_CALLBACK_FAILURE;
291 stream->acked_offset = offset + length;
292 while (stream->head && stream->head->end_offset <= stream->acked_offset) {
293 send_chunk_t *chunk = stream->head;
294 if (chunk->submitted != chunk->length)
295 return NGTCP2_ERR_CALLBACK_FAILURE;
296 stream->head = chunk->next;
297 if (!stream->head)
298 stream->tail = NULL;
299 free_chunk(connection, chunk);
300 }
301 return 0;
302 }
303 if (connection->http3 && http_server_http3_ack(connection->http3, stream_id, length) == ASCIICHAT_OK)
304 return 0;
305 return NGTCP2_ERR_CALLBACK_FAILURE;
306}
307
320
321static asciichat_error_t register_receive_class(receive_context_t *context) {
322 receive_stream_t *stream = context->stream;
323 if (stream->registered)
324 return ASCIICHAT_OK;
325 acip_traffic_class_t traffic_class = stream->reader.traffic_class;
326 if ((unsigned)traffic_class >= QUIC_FLOW_COUNT || context->connection->received_classes[traffic_class])
327 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Duplicate QUIC traffic stream");
328 context->connection->received_classes[traffic_class] = true;
329 stream->registered = true;
330 return ASCIICHAT_OK;
331}
332
333static asciichat_error_t emit_received_packet(acip_traffic_class_t traffic_class, const void *packet, size_t size,
334 void *arg) {
335 receive_context_t *context = arg;
336 asciichat_error_t result = register_receive_class(context);
337 if (result != ASCIICHAT_OK)
338 return result;
339 if (!context->connection->emit || (traffic_class != ACIP_TRAFFIC_CONTROL &&
340 (!context->connection->security || !context->connection->peer_ready_seen)))
341 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC packet received before application readiness");
342 if (!context->connection->security) {
343 packet_header_t header;
344 memcpy(&header, packet, sizeof(header));
345 packet_type_t type = NET_TO_HOST_U16(header.type);
346 if (traffic_class == ACIP_TRAFFIC_CONTROL && type == PACKET_TYPE_ENCRYPTED) {
347 if (context->connection->early_security_packet || size > 256)
348 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid early QUIC security barrier");
349 context->connection->early_security_packet = buffer_alloc(NULL, size);
350 if (!context->connection->early_security_packet)
351 return SET_ERRNO(ERROR_MEMORY, "Cannot buffer early QUIC security barrier");
352 memcpy(context->connection->early_security_packet, packet, size);
353 context->connection->early_security_length = size;
354 return ASCIICHAT_OK;
355 }
356 if (!initial_handshake_packet(type))
357 return SET_ERRNO(ERROR_CRYPTO, "Non-handshake ACIP packet received before authentication");
358 return context->connection->emit(traffic_class, packet, size, context->connection->emit_context);
359 }
360 return open_security_packet(context->connection, traffic_class, packet, size);
361}
362
363static int receive_acip_stream(ngtcp2_conn *conn, quic_connection_t *connection, uint32_t flags, int64_t stream_id,
364 uint64_t offset, const uint8_t *data, size_t length) {
365 receive_stream_t *stream = NULL;
366 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i) {
367 if (connection->receives[i].active && connection->receives[i].id == stream_id) {
368 stream = &connection->receives[i];
369 break;
370 }
371 }
372 if (!stream) {
373 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i) {
374 if (!connection->receives[i].active) {
375 stream = &connection->receives[i];
376 stream->active = true;
377 stream->id = stream_id;
378 break;
379 }
380 }
381 }
382 if (!stream || offset != stream->received_offset || length > UINT64_MAX - offset) {
384 return NGTCP2_ERR_CALLBACK_FAILURE;
385 }
386 receive_context_t context = {.connection = connection, .stream = stream};
387 asciichat_error_t result = quic_stream_read(&stream->reader, data, length, emit_received_packet, &context);
389 result = register_receive_class(&context);
390 if (result == ASCIICHAT_OK && (flags & NGTCP2_STREAM_DATA_FLAG_FIN))
391 result = quic_stream_finish(&stream->reader);
392 if (result != ASCIICHAT_OK) {
393 connection->callback_error = result;
394 return NGTCP2_ERR_CALLBACK_FAILURE;
395 }
396 stream->received_offset += length;
397 if (ngtcp2_conn_extend_max_stream_offset(conn, stream_id, length) != 0) {
399 return NGTCP2_ERR_CALLBACK_FAILURE;
400 }
401 ngtcp2_conn_extend_max_offset(conn, length);
402 return 0;
403}
404
405static bool read_quic_varint(const uint8_t *data, size_t length, uint64_t *value, size_t *used) {
406 if (!length)
407 return false;
408 size_t width = (size_t)1 << (data[0] >> 6);
409 if (length < width)
410 return false;
411 uint64_t result = data[0] & 0x3f;
412 for (size_t i = 1; i < width; ++i)
413 result = (result << 8) | data[i];
414 *value = result;
415 *used = width;
416 return true;
417}
418
419static size_t write_quic_varint(uint8_t *out, uint64_t value) {
420 size_t width = value < (1ULL << 6) ? 1 : value < (1ULL << 14) ? 2 : value < (1ULL << 30) ? 4 : 8;
421 for (size_t i = 0; i < width; ++i)
422 out[width - i - 1] = (uint8_t)(value >> (8 * i));
423 out[0] |= width == 1 ? 0 : width == 2 ? 0x40 : width == 4 ? 0x80 : 0xc0;
424 return width;
425}
426
427static int receive_http3(ngtcp2_conn *conn, quic_connection_t *connection, uint32_t flags, int64_t stream_id,
428 const uint8_t *data, size_t length) {
429 size_t consumed = 0;
430 asciichat_error_t result =
431 http_server_http3_read(connection->http3, stream_id, data, length, (flags & NGTCP2_STREAM_DATA_FLAG_FIN) != 0,
432 connection->last_time_ns, &consumed);
433 if (result != ASCIICHAT_OK || ngtcp2_conn_extend_max_stream_offset(conn, stream_id, consumed) != 0) {
434 connection->callback_error = result != ASCIICHAT_OK ? result : ERROR_NETWORK_PROTOCOL;
435 return NGTCP2_ERR_CALLBACK_FAILURE;
436 }
437 ngtcp2_conn_extend_max_offset(conn, consumed);
438 int64_t session_id = -1;
439 if ((flags & NGTCP2_STREAM_DATA_FLAG_FIN) && http_server_http3_accepted(connection->http3, &session_id) &&
440 stream_id == session_id) {
441 connection->callback_error = ERROR_NETWORK;
442 return NGTCP2_ERR_CALLBACK_FAILURE;
443 }
444 return 0;
445}
446
447static int receive_webtransport_uni(ngtcp2_conn *conn, quic_connection_t *connection, uint32_t flags, int64_t stream_id,
448 uint64_t offset, const uint8_t *data, size_t length) {
449 http3_receive_stream_t *stream = NULL;
450 for (size_t i = 0; i < QUIC_HTTP3_PEER_UNI_STREAMS; ++i) {
451 if (connection->http3_receives[i].active && connection->http3_receives[i].id == stream_id) {
452 stream = &connection->http3_receives[i];
453 break;
454 }
455 }
456 if (!stream) {
457 for (size_t i = 0; i < QUIC_HTTP3_PEER_UNI_STREAMS; ++i) {
458 if (!connection->http3_receives[i].active) {
459 stream = &connection->http3_receives[i];
460 stream->active = true;
461 stream->id = stream_id;
462 break;
463 }
464 }
465 }
466 if (!stream || offset != stream->offset || length > UINT64_MAX - offset)
467 goto invalid;
468 stream->offset += length;
469 size_t input = 0;
470 while (!stream->kind && input < length) {
471 if (stream->prefix_used == sizeof(stream->prefix))
472 goto invalid;
473 stream->prefix[stream->prefix_used++] = data[input++];
474 uint64_t type = 0, session = 0;
475 size_t type_length = 0, session_length = 0;
476 if (!read_quic_varint(stream->prefix, stream->prefix_used, &type, &type_length))
477 continue;
478 if (type != 0x54) {
479 stream->kind = 1;
480 break;
481 }
482 if (!read_quic_varint(stream->prefix + type_length, stream->prefix_used - type_length, &session, &session_length))
483 continue;
484 int64_t accepted = -1;
485 if (!http_server_http3_accepted(connection->http3, &accepted) || session != (uint64_t)accepted)
486 goto invalid;
487 stream->kind = 2;
488 stream->prefix_length = type_length + session_length;
489 if (ngtcp2_conn_extend_max_stream_offset(conn, stream_id, stream->prefix_length) != 0)
490 goto invalid;
491 ngtcp2_conn_extend_max_offset(conn, stream->prefix_length);
492 }
493 if (!stream->kind) {
494 if (flags & NGTCP2_STREAM_DATA_FLAG_FIN)
495 goto invalid;
496 return 0;
497 }
498 if (stream->kind == 1) {
499 if (stream->prefix_used) {
500 uint32_t prefix_flags = input == length ? flags : flags & ~NGTCP2_STREAM_DATA_FLAG_FIN;
501 if (receive_http3(conn, connection, prefix_flags, stream_id, stream->prefix, stream->prefix_used) != 0)
502 return NGTCP2_ERR_CALLBACK_FAILURE;
503 stream->prefix_used = 0;
504 }
505 if (input < length)
506 return receive_http3(conn, connection, flags, stream_id, data + input, length - input);
507 if (!length && (flags & NGTCP2_STREAM_DATA_FLAG_FIN))
508 return receive_http3(conn, connection, flags, stream_id, NULL, 0);
509 return 0;
510 }
511 return receive_acip_stream(conn, connection, flags, stream_id, offset + input - stream->prefix_length,
512 data ? data + input : NULL, length - input);
513invalid:
515 return NGTCP2_ERR_CALLBACK_FAILURE;
516}
517
518static int receive_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id, uint64_t offset,
519 const uint8_t *data, size_t length, void *user_data, void *stream_user_data) {
520 (void)stream_user_data;
521 quic_connection_t *connection = user_data;
522 asciichat_error_t readiness = update_readiness(connection);
523 if (connection->webtransport) {
524 if ((flags & NGTCP2_STREAM_DATA_FLAG_0RTT) || ngtcp2_conn_is_local_stream2(conn, stream_id) ||
525 readiness != ASCIICHAT_OK || !connection->tls_ready || !connection->http3) {
526 connection->callback_error = readiness != ASCIICHAT_OK ? readiness : ERROR_NETWORK_PROTOCOL;
527 return NGTCP2_ERR_CALLBACK_FAILURE;
528 }
529 if (ngtcp2_is_bidi_stream(stream_id))
530 return receive_http3(conn, connection, flags, stream_id, data, length);
531 return receive_webtransport_uni(conn, connection, flags, stream_id, offset, data, length);
532 }
533 if ((flags & NGTCP2_STREAM_DATA_FLAG_0RTT) || ngtcp2_is_bidi_stream(stream_id) ||
534 ngtcp2_conn_is_local_stream2(conn, stream_id) || readiness != ASCIICHAT_OK || !connection->tls_ready) {
535 connection->callback_error = readiness != ASCIICHAT_OK ? readiness : ERROR_NETWORK_PROTOCOL;
536 return NGTCP2_ERR_CALLBACK_FAILURE;
537 }
538 return receive_acip_stream(conn, connection, flags, stream_id, offset, data, length);
539}
540
541static int reset_stream(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size, uint64_t app_error_code,
542 void *user_data, void *stream_user_data) {
543 (void)conn;
544 (void)stream_id;
545 (void)final_size;
546 (void)app_error_code;
547 (void)stream_user_data;
548 ((quic_connection_t *)user_data)->callback_error = ERROR_NETWORK_PROTOCOL;
549 return NGTCP2_ERR_CALLBACK_FAILURE;
550}
551
552static int receive_stop_sending(ngtcp2_conn *conn, int64_t stream_id, uint64_t app_error_code, void *user_data,
553 void *stream_user_data) {
554 (void)conn;
555 (void)stream_id;
556 (void)app_error_code;
557 (void)stream_user_data;
558 ((quic_connection_t *)user_data)->callback_error = ERROR_NETWORK_PROTOCOL;
559 return NGTCP2_ERR_CALLBACK_FAILURE;
560}
561
562static int receive_datagram(ngtcp2_conn *conn, uint32_t flags, const uint8_t *data, size_t length, void *user_data) {
563 (void)conn;
564 quic_connection_t *connection = user_data;
565 if ((flags & NGTCP2_DATAGRAM_FLAG_0RTT) || !connection->datagrams_enabled || !connection->security ||
566 !connection->peer_ready_seen || !connection->emit)
567 return 0;
568 if (connection->webtransport) {
569 int64_t session_id = -1;
570 uint64_t quarter = 0;
571 size_t prefix_length = 0;
572 if (!http_server_http3_accepted(connection->http3, &session_id) ||
573 !read_quic_varint(data, length, &quarter, &prefix_length) || quarter != (uint64_t)session_id / 4)
574 return 0;
575 data += prefix_length;
576 length -= prefix_length;
577 }
578 uint8_t *wire = NULL, *plain = NULL;
579 size_t wire_length = 0, plain_length = 0;
580 uint64_t sequence = 0;
582 asciichat_error_t result = quic_datagram_read(&connection->datagram_reader, data, length, connection->last_time_ns,
583 &flow, &wire, &wire_length);
584 if (result == ASCIICHAT_OK && wire) {
585 bool consumed = false;
586 result = acip_security_open(connection->security, flow, ACIP_DELIVERY_DATAGRAM, wire, wire_length, &plain,
587 &plain_length, &sequence, &consumed);
588 if (result == ASCIICHAT_OK && consumed)
589 goto done;
590 }
591 if (result == ASCIICHAT_OK && plain && flow == ACIP_TRAFFIC_VISUAL) {
592 if (sequence <= connection->last_visual_sequence)
593 goto done;
594 bool gap = connection->last_visual_sequence && sequence != connection->last_visual_sequence + 1;
595 connection->last_visual_sequence = sequence;
596 packet_header_t header;
597 memcpy(&header, plain, sizeof(header));
598 uint16_t type = NET_TO_HOST_U16(header.type);
599 uint32_t id = NET_TO_HOST_U32(header.client_id);
600 bool encoded = type == PACKET_TYPE_IMAGE_FRAME_H264 || type == PACKET_TYPE_IMAGE_FRAME_H265;
601 if (encoded) {
602 bool keyframe = plain_length > sizeof(header) && (plain[sizeof(header)] & 1) != 0;
603 if (gap || connection->visual_codec != type)
604 connection->visual_waiting_keyframe = true;
605 connection->visual_codec = type;
606 if (keyframe) {
607 connection->visual_waiting_keyframe = false;
608 connection->visual_keyframe_requested = false;
609 }
610 if (connection->visual_waiting_keyframe) {
611 if (!connection->visual_keyframe_requested && connection->peer_security_ready && id == 0) {
613 if (queue_keyframe_request(connection, type, id) == ASCIICHAT_OK)
614 connection->visual_keyframe_requested = true;
616 }
617 goto done;
618 }
619 if (connection->emit(flow, plain, plain_length, connection->emit_context) != ASCIICHAT_OK) {
620 connection->visual_waiting_keyframe = true;
621 connection->visual_keyframe_requested = false;
622 }
623 } else {
624 connection->visual_codec = 0;
625 connection->visual_waiting_keyframe = false;
626 connection->visual_keyframe_requested = false;
627 (void)connection->emit(flow, plain, plain_length, connection->emit_context);
628 }
629 } else if (result == ASCIICHAT_OK && plain) {
630 (void)connection->emit(flow, plain, plain_length, connection->emit_context);
631 }
632done:
633 buffer_release(plain);
634 buffer_release(wire);
635 return 0;
636}
637
638static ngtcp2_conn *get_connection(ngtcp2_crypto_conn_ref *reference) {
639 return ((quic_connection_t *)reference->user_data)->conn;
640}
641
642static void random_bytes(uint8_t *output, size_t length, const ngtcp2_rand_ctx *context) {
643 (void)context;
644 randombytes_buf(output, length);
645}
646
647static int new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, ngtcp2_stateless_reset_token *token, size_t length,
648 void *user_data) {
649 (void)conn;
650 (void)user_data;
651 if (length > sizeof(cid->data))
652 return NGTCP2_ERR_CALLBACK_FAILURE;
653 randombytes_buf(cid->data, length);
654 cid->datalen = length;
655 randombytes_buf(token->data, sizeof(token->data));
656 return 0;
657}
658
659static int path_validation(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path, const ngtcp2_path *fallback_path,
660 ngtcp2_path_validation_result result, void *user_data) {
661 (void)conn;
662 (void)flags;
663 (void)fallback_path;
664 (void)user_data;
665 if (result == NGTCP2_PATH_VALIDATION_RESULT_SUCCESS && path && path->remote.addr) {
666 unsigned port = 0;
667 if (path->remote.addr->sa_family == AF_INET && path->remote.addrlen >= (ngtcp2_socklen)sizeof(struct sockaddr_in))
668 port = NET_TO_HOST_U16(((const struct sockaddr_in *)path->remote.addr)->sin_port);
669 else if (path->remote.addr->sa_family == AF_INET6 &&
670 path->remote.addrlen >= (ngtcp2_socklen)sizeof(struct sockaddr_in6))
671 port = NET_TO_HOST_U16(((const struct sockaddr_in6 *)path->remote.addr)->sin6_port);
672 log_info("QUIC peer path validated: port=%u", port);
673 }
674 return 0;
675}
676
677static ngtcp2_callbacks connection_callbacks(bool server) {
678 ngtcp2_callbacks callbacks = {0};
679 callbacks.client_initial = server ? NULL : ngtcp2_crypto_client_initial_cb;
680 callbacks.recv_client_initial = server ? ngtcp2_crypto_recv_client_initial_cb : NULL;
681 callbacks.recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb;
682 callbacks.encrypt = ngtcp2_crypto_encrypt_cb;
683 callbacks.decrypt = ngtcp2_crypto_decrypt_cb;
684 callbacks.hp_mask = ngtcp2_crypto_hp_mask_cb;
685 callbacks.recv_stream_data = receive_stream_data;
686 callbacks.acked_stream_data_offset = acknowledged_stream_data;
687 callbacks.stream_reset = reset_stream;
688 callbacks.recv_stop_sending = receive_stop_sending;
689 callbacks.recv_datagram = receive_datagram;
690 callbacks.recv_retry = server ? NULL : ngtcp2_crypto_recv_retry_cb;
691 callbacks.rand = random_bytes;
692 callbacks.update_key = ngtcp2_crypto_update_key_cb;
693 callbacks.delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb;
694 callbacks.delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb;
695 callbacks.version_negotiation = ngtcp2_crypto_version_negotiation_cb;
696 callbacks.get_new_connection_id2 = new_connection_id;
697 callbacks.get_path_challenge_data2 = ngtcp2_crypto_get_path_challenge_data2_cb;
698 callbacks.path_validation = path_validation;
699 return callbacks;
700}
701
702static bool valid_address(const ngtcp2_addr *address) {
703 if (!address || !address->addr)
704 return false;
705 size_t length = (size_t)address->addrlen;
706 if (length < sizeof(address->addr->sa_family) || length > sizeof(struct sockaddr_storage))
707 return false;
708 if (address->addr->sa_family == AF_INET)
709 return length >= sizeof(struct sockaddr_in);
710 if (address->addr->sa_family == AF_INET6)
711 return length >= sizeof(struct sockaddr_in6);
712 return false;
713}
714
715static bool valid_path(const ngtcp2_path *path) {
716 return path && valid_address(&path->local) && valid_address(&path->remote);
717}
718
719static asciichat_error_t quic_connection_create(quic_tls_t *tls, const char *server_name, const ngtcp2_path *path,
720 const ngtcp2_pkt_hd *initial, const ngtcp2_cid *original_dcid,
721 uint64_t now_ns, quic_connection_t **out) {
722 if (!out)
723 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC connection output");
724 *out = NULL;
725 if (!tls || !valid_path(path) || now_ns == UINT64_MAX || (tls->server != (initial != NULL)) ||
726 (!tls->server && (!server_name || !*server_name)))
727 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC connection configuration");
728 if (sodium_init() < 0)
729 return SET_ERRNO(ERROR_CRYPTO, "Cannot initialize QUIC random source");
730
731 quic_connection_t *connection = SAFE_CALLOC(1, sizeof(*connection), quic_connection_t *);
732 if (!connection)
733 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate QUIC connection");
734 connection->tls = tls;
735 connection->datagrams_enabled = tls->enable_datagrams;
736 if (initial) {
737 connection->initial_dcid = initial->dcid;
738 connection->has_initial_dcid = true;
739 }
740 connection->last_time_ns = now_ns;
741 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i)
742 connection->sends[i].id = -1;
743 connection->reference.get_conn = get_connection;
744 connection->reference.user_data = connection;
745
746 ngtcp2_cid dcid = {0}, scid = {0};
747 scid.datalen = QUIC_CONNECTION_ID_LENGTH;
748 randombytes_buf(scid.data, scid.datalen);
749 if (initial)
750 dcid = initial->scid;
751 else {
752 dcid.datalen = QUIC_CONNECTION_ID_LENGTH;
753 randombytes_buf(dcid.data, dcid.datalen);
754 }
755 ngtcp2_settings settings;
756 ngtcp2_settings_default(&settings);
757 settings.initial_ts = now_ns;
758 if (original_dcid) {
759 settings.token = initial->token;
760 settings.tokenlen = initial->tokenlen;
761 settings.token_type = NGTCP2_TOKEN_TYPE_RETRY;
762 }
763 ngtcp2_transport_params params;
764 ngtcp2_transport_params_default(&params);
765 params.initial_max_streams_uni = tls->http3 ? 6 : 3;
766 params.initial_max_streams_bidi = tls->http3 ? 1 : 0;
767 params.initial_max_stream_data_bidi_local = tls->http3 ? QUIC_INITIAL_STREAM_WINDOW : 0;
768 params.initial_max_stream_data_bidi_remote = tls->http3 ? QUIC_INITIAL_STREAM_WINDOW : 0;
769 params.initial_max_stream_data_uni = QUIC_INITIAL_STREAM_WINDOW;
770 params.initial_max_data = QUIC_INITIAL_CONNECTION_WINDOW;
771 params.max_idle_timeout = QUIC_IDLE_TIMEOUT_NS;
772 if (connection->datagrams_enabled)
773 params.max_datagram_frame_size = 65535;
774 if (initial) {
775 params.original_dcid = original_dcid ? *original_dcid : initial->dcid;
776 params.original_dcid_present = 1;
777 if (original_dcid) {
778 params.retry_scid = initial->dcid;
779 params.retry_scid_present = 1;
780 }
781 }
782 ngtcp2_callbacks callbacks = connection_callbacks(tls->server);
783 int rc = initial ? ngtcp2_conn_server_new(&connection->conn, &dcid, &scid, path, initial->version, &callbacks,
784 &settings, &params, NULL, connection)
785 : ngtcp2_conn_client_new(&connection->conn, &dcid, &scid, path, NGTCP2_PROTO_VER_V1, &callbacks,
786 &settings, &params, NULL, connection);
787 if (rc != 0) {
788 asciichat_error_t result = SET_ERRNO(ERROR_NETWORK, "Cannot create QUIC connection: %s", ngtcp2_strerror(rc));
789 quic_connection_destroy(&connection);
790 return result;
791 }
792 asciichat_error_t result = quic_tls_session_create(tls, server_name, &connection->reference, &connection->ssl);
793 if (result != ASCIICHAT_OK) {
794 quic_connection_destroy(&connection);
795 return result;
796 }
797 rc = ngtcp2_crypto_ossl_ctx_new(&connection->crypto, connection->ssl);
798 if (rc != 0) {
799 result = SET_ERRNO(ERROR_CRYPTO, "Cannot create QUIC OpenSSL connection context: %d", rc);
800 quic_connection_destroy(&connection);
801 return result;
802 }
803 ngtcp2_conn_set_tls_native_handle(connection->conn, connection->crypto);
804 *out = connection;
805 return ASCIICHAT_OK;
806}
807
808asciichat_error_t quic_connection_client_create(quic_tls_t *tls, const char *server_name, const ngtcp2_path *path,
809 uint64_t now_ns, quic_connection_t **out) {
810 return quic_connection_create(tls, server_name, path, NULL, NULL, now_ns, out);
811}
812
813asciichat_error_t quic_connection_server_create(quic_tls_t *tls, const ngtcp2_path *path, const void *initial,
814 size_t initial_length, uint64_t now_ns, quic_connection_t **out) {
815 if (!out)
816 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC connection output");
817 *out = NULL;
818 if (!initial || !initial_length)
819 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC Initial packet");
820 ngtcp2_pkt_hd header;
821 int rc = ngtcp2_accept(&header, initial, initial_length);
822 if (rc != 0 || header.version != NGTCP2_PROTO_VER_V1)
823 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid or unsupported QUIC Initial packet");
824 return quic_connection_create(tls, NULL, path, &header, NULL, now_ns, out);
825}
826
827asciichat_error_t quic_connection_server_create_validated(quic_tls_t *tls, const ngtcp2_path *path, const void *initial,
828 size_t initial_length, const ngtcp2_cid *original_dcid,
829 uint64_t now_ns, quic_connection_t **out) {
830 if (!out)
831 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC connection output");
832 *out = NULL;
833 if (!initial || !initial_length || !original_dcid || !original_dcid->datalen ||
834 original_dcid->datalen > NGTCP2_MAX_CIDLEN)
835 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing validated QUIC Initial parameters");
836 ngtcp2_pkt_hd header;
837 int rc = ngtcp2_accept(&header, initial, initial_length);
838 if (rc != 0 || header.version != NGTCP2_PROTO_VER_V1 || !header.tokenlen)
839 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid validated QUIC Initial packet");
840 return quic_connection_create(tls, NULL, path, &header, original_dcid, now_ns, out);
841}
842
843static asciichat_error_t update_readiness(quic_connection_t *connection) {
844 if (!connection->tls_ready && ngtcp2_conn_get_handshake_completed2(connection->conn)) {
845 asciichat_error_t result = quic_tls_verify_ready(connection->tls, connection->ssl);
846 if (result != ASCIICHAT_OK) {
847 connection->failed = true;
848 return result;
849 }
850 const unsigned char *protocol = NULL;
851 unsigned int protocol_length = 0;
852 SSL_get0_alpn_selected(connection->ssl, &protocol, &protocol_length);
853 connection->webtransport =
854 connection->tls->http3 && protocol_length == 2 && protocol && !memcmp(protocol, "h3", sizeof("h3") - 1);
855 connection->tls_ready = true;
856 }
857 if (connection->tls_ready && connection->webtransport && !connection->http3) {
858 int64_t control = 0, encoder = 0, decoder = 0;
859 int rc = ngtcp2_conn_open_uni_stream(connection->conn, &control, NULL);
860 if (!rc)
861 rc = ngtcp2_conn_open_uni_stream(connection->conn, &encoder, NULL);
862 if (!rc)
863 rc = ngtcp2_conn_open_uni_stream(connection->conn, &decoder, NULL);
864 if (rc) {
865 connection->failed = true;
866 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Cannot open HTTP/3 control streams: %s", ngtcp2_strerror(rc));
867 }
868 char peer_address[64] = "unknown";
869 struct sockaddr_storage peer;
870 socklen_t peer_length = sizeof(peer);
871 if (quic_connection_peer_address(connection, &peer, &peer_length) == ASCIICHAT_OK)
872 (void)format_ip_address(peer.ss_family, (const struct sockaddr *)&peer, peer_address, sizeof(peer_address));
873 asciichat_error_t result =
874 http_server_http3_create(connection->tls->webtransport_origin, connection->tls->webtransport_path, control,
875 encoder, decoder, connection->tls->http_server, peer_address, &connection->http3);
876 if (result != ASCIICHAT_OK) {
877 connection->failed = true;
878 return result;
879 }
880 }
881 return ASCIICHAT_OK;
882}
883
884static asciichat_error_t update_time(quic_connection_t *connection, uint64_t now_ns) {
885 if (now_ns < connection->last_time_ns || now_ns == UINT64_MAX)
886 return SET_ERRNO(ERROR_INVALID_PARAM, "QUIC monotonic time moved backwards or is invalid");
887 connection->last_time_ns = now_ns;
888 quic_datagram_expire(&connection->datagram_reader, now_ns);
889 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i) {
890 datagram_send_t **link = &connection->datagram_head[i], *tail = NULL;
891 while (*link) {
892 datagram_send_t *item = *link;
893 if (item->deadline_ns && item->deadline_ns <= now_ns) {
894 *link = item->next;
895 connection->pending_bytes -= item->cost;
896 buffer_release(item->data);
897 SAFE_FREE(item);
898 } else {
899 tail = item;
900 link = &item->next;
901 }
902 }
903 connection->datagram_tail[i] = tail;
904 }
905 return ASCIICHAT_OK;
906}
907
908asciichat_error_t quic_connection_matches_cid(const quic_connection_t *connection, const uint8_t *cid, size_t length,
909 bool *matches) {
910 if (!connection || !connection->conn || !cid || !matches)
911 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC connection ID lookup");
912 *matches = false;
913 if (!length || length > NGTCP2_MAX_CIDLEN)
914 return ASCIICHAT_OK;
915 if (connection->has_initial_dcid && length == connection->initial_dcid.datalen &&
916 memcmp(cid, connection->initial_dcid.data, length) == 0) {
917 *matches = true;
918 return ASCIICHAT_OK;
919 }
920 size_t count = ngtcp2_conn_get_scid2(connection->conn, NULL);
921 if (!count)
922 return ASCIICHAT_OK;
923 if (count > SIZE_MAX / sizeof(ngtcp2_cid))
924 return SET_ERRNO(ERROR_MEMORY, "QUIC connection ID count overflow");
925 ngtcp2_cid *ids = SAFE_CALLOC(count, sizeof(*ids), ngtcp2_cid *);
926 if (!ids)
927 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate QUIC connection ID list");
928 size_t actual = ngtcp2_conn_get_scid2(connection->conn, ids);
929 for (size_t i = 0; i < actual; ++i) {
930 if (ids[i].datalen == length && memcmp(ids[i].data, cid, length) == 0) {
931 *matches = true;
932 break;
933 }
934 }
935 SAFE_FREE(ids);
936 return ASCIICHAT_OK;
937}
938
940 if (!connection || !connection->conn || connection->failed || !emit || connection->emit)
941 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC receive callback");
942 connection->emit = emit;
943 connection->emit_context = context;
944 return ASCIICHAT_OK;
945}
946
948 if (!connection || !connection->emit || connection->emit_context != context)
949 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC receive callback context");
950 connection->emit = NULL;
951 connection->emit_context = NULL;
952 connection->security_notify = NULL;
953 connection->security_notify_context = NULL;
954 connection->keyframe_notify = NULL;
955 connection->keyframe_notify_context = NULL;
956 return ASCIICHAT_OK;
957}
958
960 void *context) {
961 if (connection) {
962 connection->keyframe_notify = notify;
963 connection->keyframe_notify_context = notify ? context : NULL;
964 }
965}
966
967void quic_connection_set_security_notifier(quic_connection_t *connection, void (*notify)(void *), void *context) {
968 if (connection) {
969 connection->security_notify = notify;
970 connection->security_notify_context = notify ? context : NULL;
971 }
972}
973
975 if (!connection || !connection->conn || connection->failed || !connection->tls_ready || !connection->security ||
976 !connection->peer_security_ready)
977 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC and authenticated ACIP encryption are not ready for media");
978 connection->media_allowed = true;
979 return ASCIICHAT_OK;
980}
981
983 if (!connection || !connection->conn || connection->failed || !connection->tls_ready || !connection->emit)
984 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC is not ready for the ACIP security transition");
985 crypto->server_role = connection->tls->server;
986 crypto->server_role_known = true;
987 crypto_session_t *session = NULL;
988 asciichat_error_t result = crypto_session_lock(crypto, &session);
989 if (result == ASCIICHAT_OK) {
990 crypto_session_unlock(session);
991 connection->security = crypto;
992 }
993 if (result != ASCIICHAT_OK)
994 return result;
995 result = queue_security_barrier(connection, quic_ready_payload, sizeof(quic_ready_payload));
996 if (result == ASCIICHAT_OK && connection->early_security_packet)
997 result = open_security_packet(connection, ACIP_TRAFFIC_CONTROL, connection->early_security_packet,
998 connection->early_security_length);
1000 connection->early_security_packet = NULL;
1001 connection->early_security_length = 0;
1002 if (result != ASCIICHAT_OK)
1003 connection->failed = true;
1004 return result;
1005}
1006
1008 return connection && connection->security && connection->peer_security_ready && !connection->failed;
1009}
1010
1012 if (!connection || !connection->conn || connection->failed || !connection->tls_ready ||
1013 !connection->datagrams_enabled)
1014 return 0;
1015 const ngtcp2_transport_params *remote = ngtcp2_conn_get_remote_transport_params2(connection->conn);
1016 if (!remote || remote->max_datagram_frame_size <= QUIC_DATAGRAM_HEADER_SIZE + 9)
1017 return 0;
1018 size_t path = ngtcp2_conn_get_path_max_tx_udp_payload_size2(connection->conn);
1020 return 0;
1021 size_t frame = path - QUIC_DATAGRAM_OVERHEAD;
1022 if (frame > QUIC_DATAGRAM_FRAME_LIMIT)
1024 if (frame > remote->max_datagram_frame_size - 9)
1025 frame = (size_t)(remote->max_datagram_frame_size - 9);
1026 if (connection->webtransport) {
1027 int64_t session_id = -1;
1028 uint8_t prefix[8];
1029 if (!connection->http3 || !http_server_http3_accepted(connection->http3, &session_id))
1030 return 0;
1031 size_t size = write_quic_varint(prefix, (uint64_t)session_id / 4);
1032 if (frame <= size)
1033 return 0;
1034 frame -= size;
1035 }
1036 return frame > QUIC_DATAGRAM_HEADER_SIZE ? frame - QUIC_DATAGRAM_HEADER_SIZE : 0;
1037}
1038
1040 uint64_t deadline_ns) {
1041 if (!connection || (flow != ACIP_TRAFFIC_AUDIO && flow != ACIP_TRAFFIC_VISUAL))
1042 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC datagram flow");
1043 if (!quic_connection_datagram_payload(connection))
1044 return SET_ERRNO(ERROR_NOT_SUPPORTED, "QUIC datagrams were not negotiated");
1045 if (!connection->security || !connection->peer_security_ready || !connection->media_allowed)
1046 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC media security is not ready");
1047 if (deadline_ns && deadline_ns <= connection->last_time_ns)
1048 return SET_ERRNO(ERROR_NETWORK_TIMEOUT, "QUIC datagram deadline expired");
1049 return ASCIICHAT_OK;
1050}
1051
1053 const void *packet, size_t length, uint64_t deadline_ns) {
1054 asciichat_error_t validation = quic_connection_validate_datagram(connection, flow, deadline_ns);
1055 if (validation != ASCIICHAT_OK)
1056 return validation;
1057 if (flow == ACIP_TRAFFIC_VISUAL && packet && length >= sizeof(packet_header_t)) {
1058 packet_header_t header;
1059 memcpy(&header, packet, sizeof(header));
1060 uint16_t type = NET_TO_HOST_U16(header.type);
1061 if ((type == PACKET_TYPE_IMAGE_FRAME_H264 || type == PACKET_TYPE_IMAGE_FRAME_H265) && header.client_id != 0)
1062 return SET_ERRNO(ERROR_NOT_SUPPORTED, "QUIC encoded datagrams require stream ID zero");
1063 }
1064 size_t unit = quic_connection_datagram_payload(connection);
1065 if (connection->datagram_id[flow] == UINT64_MAX)
1066 return SET_ERRNO(ERROR_NETWORK_SIZE, "QUIC datagram ID exhausted");
1069 size_t limit = ACIP_OUTBOUND_BUDGET - reserve;
1070 if (connection->pending_bytes > limit || length > limit - connection->pending_bytes)
1071 return SET_ERRNO(ERROR_BUFFER_FULL, "QUIC datagram queue is full");
1072 uint8_t *wire = NULL;
1073 size_t wire_length = 0;
1074 asciichat_error_t result =
1075 acip_security_seal(connection->security, flow, ACIP_DELIVERY_DATAGRAM, packet, length, &wire, &wire_length);
1076 if (result != ASCIICHAT_OK)
1077 return result;
1078 size_t count = (wire_length + unit - 1) / unit;
1079 size_t cost = buffer_capacity(wire) + sizeof(datagram_send_t);
1080 if (count > QUIC_DATAGRAM_MAX_FRAGMENTS || cost > limit - connection->pending_bytes) {
1081 buffer_release(wire);
1083 "QUIC datagram exceeds fragmentation or queue limit");
1084 }
1085 datagram_send_t *item = SAFE_CALLOC(1, sizeof(*item), datagram_send_t *);
1086 if (!item) {
1087 buffer_release(wire);
1088 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate QUIC datagram queue entry");
1089 }
1090 item->data = wire;
1091 item->length = wire_length;
1092 item->cost = cost;
1093 item->id = ++connection->datagram_id[flow];
1094 item->deadline_ns = deadline_ns;
1095 item->unit = (uint16_t)unit;
1096 item->count = (uint16_t)count;
1097 if (connection->datagram_tail[flow])
1098 connection->datagram_tail[flow]->next = item;
1099 else
1100 connection->datagram_head[flow] = item;
1101 connection->datagram_tail[flow] = item;
1102 connection->pending_bytes += cost;
1103 return ASCIICHAT_OK;
1104}
1105
1106static void pop_datagram(quic_connection_t *connection, acip_traffic_class_t flow) {
1107 datagram_send_t *item = connection->datagram_head[flow];
1108 connection->datagram_head[flow] = item->next;
1109 if (!item->next)
1110 connection->datagram_tail[flow] = NULL;
1111 connection->pending_bytes -= item->cost;
1112 buffer_release(item->data);
1113 SAFE_FREE(item);
1114}
1115
1116static asciichat_error_t queue_wire_packet(quic_connection_t *connection, acip_traffic_class_t traffic_class,
1117 const void *packet, size_t length) {
1118 return queue_wire_packet_internal(connection, traffic_class, packet, length, false);
1119}
1120
1121static asciichat_error_t queue_wire_packet_internal(quic_connection_t *connection, acip_traffic_class_t traffic_class,
1122 const void *packet, size_t length, bool internal) {
1123 if (!connection || !connection->conn || connection->failed || (unsigned)traffic_class >= QUIC_FLOW_COUNT || !packet ||
1124 length < sizeof(packet_header_t) || length - sizeof(packet_header_t) > MAX_PACKET_SIZE)
1125 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC outbound packet");
1126 if (!connection->tls_ready || (!internal && traffic_class != ACIP_TRAFFIC_CONTROL && !connection->media_allowed))
1127 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC connection is not ready for this traffic");
1128 packet_header_t header;
1129 memcpy(&header, packet, sizeof(header));
1130 uint16_t type = NET_TO_HOST_U16(header.type);
1131 if (NET_TO_HOST_U64(header.magic) != PACKET_MAGIC || NET_TO_HOST_U32(header.length) != length - sizeof(header) ||
1132 (type != PACKET_TYPE_ENCRYPTED && acip_packet_traffic_class(type) != traffic_class))
1133 return SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid QUIC outbound ACIP packet or traffic class");
1134
1135 send_stream_t *stream = &connection->sends[traffic_class];
1136 if (stream->fin_requested)
1137 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC traffic stream is closing");
1139 size_t prefix_length = 0;
1140 if (!stream->initialized) {
1141 if (connection->webtransport) {
1142 int64_t session_id = -1;
1143 if (!http_server_http3_accepted(connection->http3, &session_id))
1144 return SET_ERRNO(ERROR_INVALID_STATE, "WebTransport session is not established");
1145 prefix_length += write_quic_varint(preamble + prefix_length, 0x54);
1146 prefix_length += write_quic_varint(preamble + prefix_length, (uint64_t)session_id);
1147 }
1148 asciichat_error_t preamble_result = quic_stream_preamble(traffic_class, preamble + prefix_length);
1149 if (preamble_result != ASCIICHAT_OK)
1150 return preamble_result;
1151 prefix_length += ACIP_QUIC_STREAM_PREAMBLE_SIZE;
1152 }
1153 if (stream->queued_offset > NGTCP2_MAX_VARINT - length - prefix_length)
1154 return SET_ERRNO(ERROR_NETWORK_SIZE, "QUIC stream offset exhausted");
1157 : 0;
1158 size_t limit = ACIP_OUTBOUND_BUDGET - reserve;
1159 if (connection->pending_bytes > limit || length > limit - connection->pending_bytes)
1160 return SET_ERRNO(ERROR_BUFFER_FULL, "QUIC outbound queue is full");
1161
1162 send_chunk_t *prefix = NULL;
1163 if (prefix_length) {
1164 prefix = copy_chunk(preamble, prefix_length);
1165 if (!prefix)
1166 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate QUIC stream preamble");
1167 }
1168 send_chunk_t *chunk = copy_chunk(packet, length);
1169 if (!chunk) {
1170 free_unqueued_chunk(prefix);
1171 return SET_ERRNO(ERROR_MEMORY, "Cannot copy QUIC outbound packet");
1172 }
1173 size_t prefix_cost = prefix ? prefix->cost : 0;
1174 if (prefix_cost > limit - connection->pending_bytes ||
1175 chunk->cost > limit - connection->pending_bytes - prefix_cost) {
1176 free_unqueued_chunk(prefix);
1177 free_unqueued_chunk(chunk);
1178 return SET_ERRNO(ERROR_BUFFER_FULL, "QUIC outbound queue is full");
1179 }
1180 if (prefix)
1181 append_chunk(connection, stream, prefix);
1182 append_chunk(connection, stream, chunk);
1183 stream->initialized = true;
1184 return ASCIICHAT_OK;
1185}
1186
1188 const void *packet, size_t length) {
1189 if (connection && !connection->security && packet && length >= sizeof(packet_header_t)) {
1190 packet_header_t header;
1191 memcpy(&header, packet, sizeof(header));
1192 packet_type_t type = NET_TO_HOST_U16(header.type);
1193 if (traffic_class == ACIP_TRAFFIC_CONTROL && !initial_handshake_packet(type))
1194 return SET_ERRNO(ERROR_CRYPTO, "Non-handshake ACIP packet sent before authentication");
1195 }
1196 if (!connection || !connection->security)
1197 return queue_wire_packet(connection, traffic_class, packet, length);
1198 if (packet && length >= sizeof(packet_header_t)) {
1199 packet_header_t header;
1200 memcpy(&header, packet, sizeof(header));
1201 if (NET_TO_HOST_U16(header.type) == PACKET_TYPE_ENCRYPTED)
1202 return queue_wire_packet_internal(connection, traffic_class, packet, length, true);
1203 }
1204 if (!connection->peer_security_ready)
1205 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC peer has not acknowledged ACIP security readiness");
1206 if (packet && length >= sizeof(packet_header_t)) {
1207 packet_header_t header;
1208 memcpy(&header, packet, sizeof(header));
1209 if (NET_TO_HOST_U16(header.type) == PACKET_TYPE_QUIC_READY ||
1212 return SET_ERRNO(ERROR_INVALID_PARAM, "QUIC readiness marker is reserved");
1213 }
1214 if (!connection->conn || connection->failed || !connection->tls_ready || (unsigned)traffic_class >= QUIC_FLOW_COUNT ||
1215 (traffic_class != ACIP_TRAFFIC_CONTROL && !connection->media_allowed))
1216 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC connection is not ready for this traffic");
1217 if (connection->sends[traffic_class].fin_requested)
1218 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC traffic stream is closing");
1219 uint8_t *wire = NULL;
1220 size_t wire_length = 0;
1221 asciichat_error_t result = acip_security_seal(connection->security, traffic_class, ACIP_DELIVERY_RELIABLE, packet,
1222 length, &wire, &wire_length);
1223 if (result == ASCIICHAT_OK)
1224 result = queue_wire_packet(connection, traffic_class, wire, wire_length);
1225 buffer_release(wire);
1226 return result;
1227}
1228
1230 return connection ? connection->pending_bytes : 0;
1231}
1232
1234 if (!connection || !connection->conn || connection->failed || (unsigned)traffic_class >= QUIC_FLOW_COUNT)
1235 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC traffic stream");
1236 send_stream_t *stream = &connection->sends[traffic_class];
1237 if (!stream->initialized || stream->fin_requested)
1238 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC traffic stream is not open or is already closing");
1239 stream->fin_requested = true;
1240 return ASCIICHAT_OK;
1241}
1242
1244 if (!connection || (unsigned)traffic_class >= QUIC_FLOW_COUNT)
1245 return false;
1246 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i)
1247 if (connection->receives[i].registered && connection->receives[i].reader.traffic_class == traffic_class)
1248 return connection->receives[i].reader.finished;
1249 return false;
1250}
1251
1252asciichat_error_t quic_connection_receive(quic_connection_t *connection, const ngtcp2_path *path, const void *packet,
1253 size_t length, uint64_t now_ns) {
1254 if (!connection || !connection->conn || connection->failed || !valid_path(path) || !packet || !length)
1255 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC packet input");
1256 asciichat_error_t result = update_time(connection, now_ns);
1257 if (result != ASCIICHAT_OK)
1258 return result;
1259 ngtcp2_pkt_info info = {0};
1260 connection->callback_error = ASCIICHAT_OK;
1261 int rc = ngtcp2_conn_read_pkt(connection->conn, path, &info, packet, length, now_ns);
1262 if (rc != 0) {
1263 connection->failed = true;
1264 asciichat_error_t code = connection->callback_error != ASCIICHAT_OK ? connection->callback_error
1265 : rc == NGTCP2_ERR_CRYPTO ? ERROR_CRYPTO
1266 : rc == NGTCP2_ERR_DRAINING ? ERROR_NETWORK
1268 return SET_ERRNO(code, "QUIC receive failed: %s", ngtcp2_strerror(rc));
1269 }
1270 return update_readiness(connection);
1271}
1272
1273static ngtcp2_ssize write_pending(quic_connection_t *connection, ngtcp2_path *path, ngtcp2_pkt_info *info,
1274 uint8_t *packet, size_t capacity, uint64_t now_ns) {
1275 // HTTP/3 session control precedes application traffic; blocked streams yield
1276 // so other flows and transport acknowledgements can still advance.
1277 connection->packet_has_stream_data = false;
1278 if (connection->http3) {
1279 int64_t stream_id = -1;
1280 bool fin = false;
1281 nghttp3_vec h3vec[8];
1282 size_t count = 0;
1283 if (http_server_http3_write(connection->http3, &stream_id, &fin, h3vec, 8, &count) != ASCIICHAT_OK)
1284 return NGTCP2_ERR_CALLBACK_FAILURE;
1285 if (stream_id >= 0) {
1286 ngtcp2_vec vectors[8];
1287 for (size_t i = 0; i < count; ++i)
1288 vectors[i] = (ngtcp2_vec){.base = h3vec[i].base, .len = h3vec[i].len};
1289 ngtcp2_ssize consumed = -1;
1290 ngtcp2_ssize written = ngtcp2_conn_writev_stream(
1291 connection->conn, path, info, packet, capacity, &consumed,
1292 fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, vectors, count, now_ns);
1293 if (consumed < 0) {
1294 if (http_server_http3_wrote(connection->http3, stream_id, 0) != ASCIICHAT_OK)
1295 return NGTCP2_ERR_CALLBACK_FAILURE;
1296 if (written != NGTCP2_ERR_STREAM_DATA_BLOCKED)
1297 return written;
1298 /* Other streams and QUIC acknowledgements can still make progress
1299 * while this HTTP/3 stream waits for flow-control credit. */
1300 } else {
1301 if (http_server_http3_wrote(connection->http3, stream_id, (size_t)consumed) != ASCIICHAT_OK)
1302 return NGTCP2_ERR_CALLBACK_FAILURE;
1303 return written;
1304 }
1305 }
1306 }
1307 // Service control, audio, then visual data. Expired datagrams release their
1308 // entire pending packet before another fragment can be submitted.
1309 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i) {
1311 while (connection->datagram_head[i] &&
1312 ((connection->datagram_head[i]->deadline_ns && connection->datagram_head[i]->deadline_ns <= now_ns) ||
1313 connection->datagram_head[i]->unit > quic_connection_datagram_payload(connection)))
1314 pop_datagram(connection, flow);
1315 datagram_send_t *item = connection->datagram_head[i];
1316 if (item) {
1318 size_t fragment_length = 0, prefix_length = 0;
1319 if (connection->webtransport) {
1320 int64_t session_id = -1;
1321 if (!http_server_http3_accepted(connection->http3, &session_id))
1322 return NGTCP2_ERR_CALLBACK_FAILURE;
1323 prefix_length = write_quic_varint(fragment, (uint64_t)session_id / 4);
1324 }
1325 if (quic_datagram_fragment(flow, item->id, item->data, item->length, item->unit, item->index,
1326 fragment + prefix_length, sizeof(fragment) - prefix_length,
1327 &fragment_length) != ASCIICHAT_OK)
1328 return NGTCP2_ERR_CALLBACK_FAILURE;
1329 fragment_length += prefix_length;
1330 int accepted = 0;
1331 ngtcp2_ssize written =
1332 ngtcp2_conn_write_datagram(connection->conn, path, info, packet, capacity, &accepted,
1333 NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 0, fragment, fragment_length, now_ns);
1334 if (accepted) {
1335 connection->packet_has_stream_data = true;
1336 connection->packet_traffic_class = flow;
1337 if (++item->index == item->count)
1338 pop_datagram(connection, flow);
1339 }
1340 return written;
1341 }
1342 send_stream_t *stream = &connection->sends[i];
1343 send_chunk_t *chunk = stream->head;
1344 while (chunk && chunk->submitted == chunk->length)
1345 chunk = chunk->next;
1346 bool send_fin = !chunk && stream->fin_requested && !stream->fin_submitted;
1347 if (!chunk && !send_fin)
1348 continue;
1349 if (stream->id < 0) {
1350 int rc = ngtcp2_conn_open_uni_stream(connection->conn, &stream->id, NULL);
1351 if (rc == NGTCP2_ERR_STREAM_ID_BLOCKED)
1352 continue;
1353 if (rc != 0)
1354 return rc;
1355 }
1356 ngtcp2_vec vec = {0};
1357 if (chunk)
1358 vec = (ngtcp2_vec){.base = chunk->data + chunk->submitted, .len = chunk->length - chunk->submitted};
1359 ngtcp2_ssize consumed = -1;
1360 ngtcp2_ssize written =
1361 ngtcp2_conn_writev_stream(connection->conn, path, info, packet, capacity, &consumed,
1362 send_fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : NGTCP2_WRITE_STREAM_FLAG_NONE, stream->id,
1363 send_fin ? NULL : &vec, send_fin ? 0 : 1, now_ns);
1364 if (written == NGTCP2_ERR_STREAM_DATA_BLOCKED)
1365 continue;
1366 if (written < 0)
1367 return written;
1368 if (send_fin && consumed == 0) {
1369 if (!written)
1370 return NGTCP2_ERR_CALLBACK_FAILURE;
1371 stream->fin_submitted = true;
1372 connection->packet_has_stream_data = true;
1374 }
1375 if (consumed > 0) {
1376 if (send_fin || (size_t)consumed > vec.len || !written)
1377 return NGTCP2_ERR_CALLBACK_FAILURE;
1378 chunk->submitted += (size_t)consumed;
1379 connection->packet_has_stream_data = true;
1381 }
1382 return written;
1383 }
1384 return ngtcp2_conn_write_pkt(connection->conn, path, info, packet, capacity, now_ns);
1385}
1386
1388 if (!connection || !connection->packet_pending || !connection->packet_has_stream_data || !traffic_class)
1389 return false;
1390 *traffic_class = connection->packet_traffic_class;
1391 return true;
1392}
1393
1394asciichat_error_t quic_connection_write_close(quic_connection_t *connection, ngtcp2_path_storage *path, void *packet,
1395 size_t capacity, size_t *length, uint64_t now_ns) {
1396 if (!connection || !connection->conn || !path || !packet || capacity < 1200 || !length)
1397 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC close packet output");
1398 *length = 0;
1399 if (connection->failed || ngtcp2_conn_in_closing_period2(connection->conn) ||
1400 ngtcp2_conn_in_draining_period2(connection->conn))
1401 return ASCIICHAT_OK;
1402 asciichat_error_t result = update_time(connection, now_ns);
1403 if (result != ASCIICHAT_OK)
1404 return result;
1405 ngtcp2_path_storage_zero(path);
1406 ngtcp2_pkt_info info = {0};
1407 ngtcp2_ccerr close_error;
1408 ngtcp2_ccerr_default(&close_error);
1409 ngtcp2_ssize written =
1410 ngtcp2_conn_write_connection_close(connection->conn, &path->path, &info, packet, capacity, &close_error, now_ns);
1411 if (written < 0)
1412 return SET_ERRNO(ERROR_NETWORK, "Cannot write QUIC close packet: %s", ngtcp2_strerror((int)written));
1413 *length = (size_t)written;
1414 return ASCIICHAT_OK;
1415}
1416
1417asciichat_error_t quic_connection_write(quic_connection_t *connection, ngtcp2_path_storage *path, void *packet,
1418 size_t capacity, size_t *length, uint64_t now_ns) {
1419 if (!length)
1420 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC packet length output");
1421 *length = 0;
1422 if (!connection || !connection->conn || connection->failed || !path || !packet || capacity < 1200)
1423 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC packet output");
1424 if (connection->packet_pending)
1425 return SET_ERRNO(ERROR_INVALID_STATE, "Previous QUIC packet has not been transmitted");
1426 asciichat_error_t result = update_time(connection, now_ns);
1427 if (result != ASCIICHAT_OK)
1428 return result;
1429 ngtcp2_path_storage_zero(path);
1430 ngtcp2_pkt_info info = {0};
1431 ngtcp2_ssize written = write_pending(connection, &path->path, &info, packet, capacity, now_ns);
1432 if (written < 0) {
1433 connection->failed = true;
1434 return SET_ERRNO(written == NGTCP2_ERR_CRYPTO ? ERROR_CRYPTO : ERROR_NETWORK, "QUIC write failed: %s",
1435 ngtcp2_strerror((int)written));
1436 }
1437 *length = (size_t)written;
1438 connection->packet_pending = written > 0;
1439 return update_readiness(connection);
1440}
1441
1443 if (!connection || !connection->conn || connection->failed || !connection->packet_pending)
1444 return SET_ERRNO(ERROR_INVALID_STATE, "No QUIC packet awaiting transmission");
1445 asciichat_error_t result = update_time(connection, now_ns);
1446 if (result != ASCIICHAT_OK)
1447 return result;
1448 ngtcp2_conn_update_pkt_tx_time(connection->conn, now_ns);
1449 connection->packet_pending = false;
1450 return ASCIICHAT_OK;
1451}
1452
1454 if (!connection || !connection->conn || connection->failed)
1455 return UINT64_MAX;
1456 uint64_t expiry = ngtcp2_conn_get_expiry(connection->conn);
1457 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i)
1458 for (const datagram_send_t *item = connection->datagram_head[i]; item; item = item->next)
1459 if (item->deadline_ns && item->deadline_ns < expiry)
1460 expiry = item->deadline_ns;
1461 for (size_t i = 0; i < QUIC_DATAGRAM_SLOTS; ++i) {
1462 const quic_datagram_slot_t *slot = &connection->datagram_reader.slots[i];
1463 if (slot->data && slot->started_ns <= UINT64_MAX - QUIC_DATAGRAM_EXPIRY_NS &&
1464 slot->started_ns + QUIC_DATAGRAM_EXPIRY_NS < expiry)
1465 expiry = slot->started_ns + QUIC_DATAGRAM_EXPIRY_NS;
1466 }
1467 return expiry;
1468}
1469
1471 if (!connection || !connection->conn || connection->failed)
1472 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC timeout state");
1473 asciichat_error_t result = update_time(connection, now_ns);
1474 if (result != ASCIICHAT_OK)
1475 return result;
1476 int rc = ngtcp2_conn_get_expiry(connection->conn) <= now_ns ? ngtcp2_conn_handle_expiry(connection->conn, now_ns) : 0;
1477 if (rc != 0) {
1478 connection->failed = true;
1479 return SET_ERRNO(ERROR_NETWORK_TIMEOUT, "QUIC timer failed: %s", ngtcp2_strerror(rc));
1480 }
1481 return update_readiness(connection);
1482}
1483
1485 return connection && connection->tls_ready && !connection->failed &&
1486 (!connection->webtransport || !connection->tls->webtransport ||
1488}
1489
1491 return connection && connection->webtransport && connection->http3 &&
1492 http_server_http3_accepted(connection->http3, NULL) && !connection->failed;
1493}
1494
1496 return connection && !connection->failed && connection->tls_ready;
1497}
1498
1499asciichat_error_t quic_connection_peer_address(const quic_connection_t *connection, struct sockaddr_storage *address,
1500 socklen_t *length) {
1501 if (!connection || !connection->conn || connection->failed || !address || !length)
1502 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC peer address query");
1503 const ngtcp2_path *path = ngtcp2_conn_get_path2(connection->conn);
1504 if (!path || !valid_address(&path->remote))
1505 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC peer address is unavailable");
1506 memset(address, 0, sizeof(*address));
1507 memcpy(address, path->remote.addr, (size_t)path->remote.addrlen);
1508 *length = path->remote.addrlen;
1509 return ASCIICHAT_OK;
1510}
1511
1513 if (!connection || !*connection)
1514 return;
1515 ngtcp2_conn_del((*connection)->conn);
1516 (*connection)->conn = NULL;
1517 http_server_http3_destroy(&(*connection)->http3);
1518 buffer_release((*connection)->early_security_packet);
1519 quic_datagram_reset(&(*connection)->datagram_reader);
1520 for (size_t i = 0; i < QUIC_FLOW_COUNT; ++i) {
1521 while ((*connection)->datagram_head[i])
1522 pop_datagram(*connection, (acip_traffic_class_t)i);
1523 send_chunk_t *chunk = (*connection)->sends[i].head;
1524 while (chunk) {
1525 send_chunk_t *next = chunk->next;
1526 free_chunk(*connection, chunk);
1527 chunk = next;
1528 }
1529 quic_stream_reset(&(*connection)->receives[i].reader);
1530 }
1531 quic_tls_session_destroy(&(*connection)->ssl);
1532 ngtcp2_crypto_ossl_ctx_del((*connection)->crypto);
1533 (*connection)->security = NULL;
1534 SAFE_FREE(*connection);
1535}
1536
1537asciichat_error_t quic_connection_migrate(quic_connection_t *connection, const struct sockaddr *local, socklen_t length,
1538 uint64_t now_ns) {
1539 if (!connection || !connection->conn || connection->failed || connection->tls->server || !local ||
1540 (size_t)length > sizeof(ngtcp2_sockaddr_union))
1541 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC client migration");
1542 const ngtcp2_path *current = ngtcp2_conn_get_path2(connection->conn);
1543 ngtcp2_path path = *current;
1544 path.local.addr = (struct sockaddr *)local;
1545 path.local.addrlen = length;
1546 if (!valid_address(&path.local) || local->sa_family != current->remote.addr->sa_family)
1547 return SET_ERRNO(ERROR_INVALID_PARAM, "QUIC migration requires a concrete address in the peer address family");
1548 asciichat_error_t result = update_time(connection, now_ns);
1549 if (result != ASCIICHAT_OK)
1550 return result;
1551 int rc = ngtcp2_conn_initiate_immediate_migration(connection->conn, &path, now_ns);
1552 if (rc == NGTCP2_ERR_INVALID_STATE)
1553 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC handshake is unconfirmed or peer disabled active migration");
1554 if (rc == NGTCP2_ERR_CONN_ID_BLOCKED)
1555 return SET_ERRNO(ERROR_BUFFER_FULL, "QUIC migration requires an unused peer connection ID");
1556 if (rc != 0)
1557 return SET_ERRNO(ERROR_NETWORK, "Cannot initiate QUIC migration: %s", ngtcp2_strerror(rc));
1558 return ASCIICHAT_OK;
1559}
void errno_scope_end(asciichat_errno_scope_t scope, asciichat_errno_outcome_t outcome)
asciichat_errno_scope_t errno_scope_begin(void)
#define SET_ERRNO(code,...)
@ ASCIICHAT_ERRNO_DISMISSED
#define ACIP_OUTBOUND_BUDGET
#define ACIP_OUTBOUND_AUDIO_RESERVE
#define ACIP_OUTBOUND_CONTROL_RESERVE
size_t buffer_capacity(const void *data)
Definition buffer.c:280
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
#define buffer_release(data)
Definition buffer.h:91
⚙️ Common definitions, error codes, macros, and types shared throughout the application
void quic_datagram_reset(quic_datagram_reader_t *reader)
Definition datagram.c:96
void quic_datagram_expire(quic_datagram_reader_t *reader, uint64_t now_ns)
Definition datagram.c:85
asciichat_error_t quic_datagram_fragment(acip_traffic_class_t flow, uint64_t id, const void *packet, size_t length, uint16_t unit, uint16_t index, void *out, size_t capacity, size_t *out_length)
Definition datagram.c:45
asciichat_error_t quic_datagram_read(quic_datagram_reader_t *reader, const void *data, size_t length, uint64_t now_ns, acip_traffic_class_t *flow_out, uint8_t **out, size_t *out_length)
Definition datagram.c:104
#define QUIC_DATAGRAM_EXPIRY_NS
Definition datagram.h:15
#define QUIC_DATAGRAM_HEADER_SIZE
Definition datagram.h:11
#define QUIC_DATAGRAM_MAX_FRAGMENTS
Definition datagram.h:12
#define QUIC_DATAGRAM_SLOTS
Definition datagram.h:13
🔄 Network byte order conversion helpers
#define HOST_TO_NET_U16(val)
Definition endian.h:96
#define NET_TO_HOST_U64(val)
Definition endian.h:228
#define HOST_TO_NET_U32(val)
Definition endian.h:66
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
unsigned int width
Definition fft.c:23
unsigned short uint16_t
Definition common.h:61
unsigned int uint32_t
Definition common.h:62
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:72
@ ERROR_BUFFER_FULL
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_CRYPTO
Definition error_codes.h:93
@ ERROR_INVALID_PARAM
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:75
@ ERROR_NOT_SUPPORTED
Definition error_codes.h:69
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
asciichat_error_t packet_encode(packet_type_t type, const void *payload, size_t length, uint32_t client_id, void *output, size_t capacity, size_t *written)
Definition packet.c:67
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:301
#define MAX_PACKET_SIZE
Maximum packet size (5MB).
Definition packet.h:130
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID).
Definition packet.h:270
@ PACKET_TYPE_CRYPTO_CLIENT_HELLO
Client -> Server: Expected server key fingerprint for multi-key selection (UNENCRYPTED).
Definition packet.h:314
@ PACKET_TYPE_QUIC_READY
Internal authenticated readiness barrier for ACIP over QUIC.
Definition packet.h:342
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion).
Definition packet.h:355
@ PACKET_TYPE_PROTOCOL_VERSION
Protocol version and capabilities negotiation.
Definition packet.h:303
@ PACKET_TYPE_CRYPTO_NO_ENCRYPTION
Client -> Server: "I want to proceed without encryption" (UNENCRYPTED).
Definition packet.h:340
@ PACKET_TYPE_IMAGE_FRAME_H265
H.265-encoded frame from client (HEVC/H.265 codec).
Definition packet.h:387
@ PACKET_TYPE_CRYPTO_CAPABILITIES
Client -> Server: Supported crypto algorithms (UNENCRYPTED).
Definition packet.h:322
@ PACKET_TYPE_IMAGE_FRAME_H264
Definition packet.h:389
@ PACKET_TYPE_ERROR_MESSAGE
Error packet with asciichat_error_t code and human-readable message.
Definition packet.h:374
@ PACKET_TYPE_QUIC_KEYFRAME_REQUEST
Internal authenticated request for a QUIC encoded-video keyframe.
Definition packet.h:344
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Format IP address from socket address structure.
bool http_server_http3_accepted(const http_server_http3_t *server, int64_t *session_id)
Definition http3.c:549
asciichat_error_t http_server_http3_create(const char *origin, const char *path, int64_t control, int64_t encoder, int64_t decoder, http_server_t *http_server, const char *peer_address, http_server_http3_t **out)
Definition http3.c:406
asciichat_error_t http_server_http3_write(http_server_http3_t *server, int64_t *stream_id, bool *fin, nghttp3_vec *vectors, size_t capacity, size_t *count)
Definition http3.c:472
void http_server_http3_destroy(http_server_http3_t **server)
Definition http3.c:557
asciichat_error_t http_server_http3_read(http_server_http3_t *server, int64_t stream_id, const uint8_t *data, size_t length, bool fin, uint64_t now_ns, size_t *consumed)
Definition http3.c:460
asciichat_error_t http_server_http3_wrote(http_server_http3_t *server, int64_t stream_id, size_t length)
Definition http3.c:520
asciichat_error_t http_server_http3_ack(http_server_http3_t *server, int64_t stream_id, uint64_t length)
Definition http3.c:535
struct http_server_http3 http_server_http3_t
Definition http3.h:11
struct crypto_session crypto_session_t
For crypto session, this is the live record that ties owner, mutex, records, rotation together....
🌍 IP Address Parsing and Formatting Utilities
void crypto_session_unlock(crypto_session_t *session)
asciichat_error_t crypto_session_lock(crypto_context_t *crypto, crypto_session_t **output)
asciichat_error_t acip_security_seal(crypto_context_t *crypto, acip_traffic_class_t flow, acip_delivery_t delivery, const void *packet, size_t length, uint8_t **output, size_t *written)
asciichat_error_t acip_security_open(crypto_context_t *crypto, acip_traffic_class_t flow, acip_delivery_t delivery, const void *packet, size_t length, uint8_t **output, size_t *written, uint64_t *sequence, bool *consumed)
bool acip_security_rekey_packet(uint16_t type)
asciichat_error_t quic_connection_set_receiver(quic_connection_t *connection, quic_stream_emit_fn emit, void *context)
asciichat_error_t quic_connection_client_create(quic_tls_t *tls, const char *server_name, const ngtcp2_path *path, uint64_t now_ns, quic_connection_t **out)
void quic_connection_destroy(quic_connection_t **connection)
size_t quic_connection_pending_bytes(const quic_connection_t *connection)
asciichat_error_t quic_connection_peer_address(const quic_connection_t *connection, struct sockaddr_storage *address, socklen_t *length)
#define QUIC_CONNECTION_ID_LENGTH
bool quic_connection_received_fin(const quic_connection_t *connection, acip_traffic_class_t traffic_class)
asciichat_error_t quic_connection_validate_datagram(const quic_connection_t *connection, acip_traffic_class_t flow, uint64_t deadline_ns)
asciichat_error_t quic_connection_handle_expiry(quic_connection_t *connection, uint64_t now_ns)
bool quic_connection_tls_ready(const quic_connection_t *connection)
#define QUIC_INITIAL_CONNECTION_WINDOW
asciichat_error_t quic_connection_enable_security(quic_connection_t *connection, crypto_context_t *crypto)
asciichat_error_t quic_connection_write_close(quic_connection_t *connection, ngtcp2_path_storage *path, void *packet, size_t capacity, size_t *length, uint64_t now_ns)
asciichat_error_t quic_connection_matches_cid(const quic_connection_t *connection, const uint8_t *cid, size_t length, bool *matches)
asciichat_error_t quic_connection_write(quic_connection_t *connection, ngtcp2_path_storage *path, void *packet, size_t capacity, size_t *length, uint64_t now_ns)
asciichat_error_t quic_connection_finish_stream(quic_connection_t *connection, acip_traffic_class_t traffic_class)
#define QUIC_DATAGRAM_OVERHEAD
asciichat_error_t quic_connection_receive(quic_connection_t *connection, const ngtcp2_path *path, const void *packet, size_t length, uint64_t now_ns)
#define QUIC_FLOW_COUNT
#define QUIC_INITIAL_STREAM_WINDOW
asciichat_error_t quic_connection_submit(quic_connection_t *connection, acip_traffic_class_t traffic_class, const void *packet, size_t length)
asciichat_error_t quic_connection_server_create(quic_tls_t *tls, const ngtcp2_path *path, const void *initial, size_t initial_length, uint64_t now_ns, quic_connection_t **out)
void quic_connection_set_keyframe_notifier(quic_connection_t *connection, void(*notify)(uint16_t, uint32_t, void *), void *context)
#define QUIC_HTTP3_PEER_UNI_STREAMS
bool quic_connection_security_ready(const quic_connection_t *connection)
#define QUIC_IDLE_TIMEOUT_NS
bool quic_connection_webtransport_ready(const quic_connection_t *connection)
asciichat_error_t quic_connection_packet_sent(quic_connection_t *connection, uint64_t now_ns)
asciichat_error_t quic_connection_submit_datagram(quic_connection_t *connection, acip_traffic_class_t flow, const void *packet, size_t length, uint64_t deadline_ns)
uint64_t quic_connection_expiry(const quic_connection_t *connection)
#define QUIC_DATAGRAM_FRAME_LIMIT
struct send_chunk send_chunk_t
bool quic_connection_pending_packet_class(const quic_connection_t *connection, acip_traffic_class_t *traffic_class)
struct datagram_send datagram_send_t
bool quic_connection_accept_ready(const quic_connection_t *connection)
void quic_connection_set_security_notifier(quic_connection_t *connection, void(*notify)(void *), void *context)
asciichat_error_t quic_connection_migrate(quic_connection_t *connection, const struct sockaddr *local, socklen_t length, uint64_t now_ns)
asciichat_error_t quic_connection_server_create_validated(quic_tls_t *tls, const ngtcp2_path *path, const void *initial, size_t initial_length, const ngtcp2_cid *original_dcid, uint64_t now_ns, quic_connection_t **out)
size_t quic_connection_datagram_payload(const quic_connection_t *connection)
asciichat_error_t quic_connection_clear_receiver(quic_connection_t *connection, void *context)
asciichat_error_t quic_connection_allow_media(quic_connection_t *connection)
struct quic_connection quic_connection_t
asciichat_error_t quic_stream_read(quic_stream_reader_t *reader, const void *bytes, size_t length, quic_stream_emit_fn emit, void *context)
asciichat_error_t quic_stream_preamble(acip_traffic_class_t traffic_class, uint8_t output[8])
asciichat_error_t quic_stream_finish(quic_stream_reader_t *reader)
void quic_stream_reset(quic_stream_reader_t *reader)
asciichat_error_t(* quic_stream_emit_fn)(acip_traffic_class_t traffic_class, const void *packet, size_t size, void *context)
#define ACIP_QUIC_STREAM_PREAMBLE_SIZE
static bool size_t gotel_query_t * out
Definition queries.h:8
uint8_t session_id[SESSION_ID_SIZE]
🔤 String Manipulation and Shell Escaping Utilities
When code needs to work with asciichat errno snapshot, it keeps total, copied here instead of scatter...
Endpoint cryptographic state for handshake, packet protection, authentication, and rekeying.
struct datagram_send * next
Fixed prefix shared by every ACIP packet on the wire.
Definition packet.h:628
This is the handoff point for receive stream. Callers fill id, received_offset, reader,...
asciichat_error_t callback_error
http3_receive_stream_t http3_receives[6U]
ngtcp2_crypto_conn_ref reference
receive_stream_t receives[3]
acip_traffic_class_t packet_traffic_class
datagram_send_t * datagram_tail[3]
void(* keyframe_notify)(uint16_t, uint32_t, void *)
ngtcp2_crypto_ossl_ctx * crypto
quic_datagram_reader_t datagram_reader
crypto_context_t * security
datagram_send_t * datagram_head[3]
http_server_http3_t * http3
acip quic datagram slot keeps the values that move through the subsystem: data, received,...
Definition datagram.h:34
quic_datagram_slot_t slots[8U]
Definition datagram.h:35
uint64_t started_ns
Definition datagram.h:21
acip_traffic_class_t traffic_class
bool server
Definition tls.h:15
char webtransport_origin[512]
Definition tls.h:22
char webtransport_path[512]
Definition tls.h:23
bool http3
Definition tls.h:19
http_server_t * http_server
Definition tls.h:21
bool enable_datagrams
Definition tls.h:17
bool webtransport
Definition tls.h:18
quic_stream_reader_t reader
struct send_chunk * next
asciichat_error_t quic_tls_verify_ready(quic_tls_t *tls, SSL *ssl)
Definition tls.c:161
asciichat_error_t quic_tls_session_create(quic_tls_t *tls, const char *server_name, ngtcp2_crypto_conn_ref *connection, SSL **out)
Definition tls.c:126
void quic_tls_session_destroy(SSL **ssl)
Definition tls.c:194
acip_traffic_class_t acip_packet_traffic_class(uint16_t type)
Definition traffic.c:4
acip_traffic_class_t
Definition traffic.h:5
@ ACIP_TRAFFIC_CONTROL
Definition traffic.h:6
@ ACIP_TRAFFIC_VISUAL
Definition traffic.h:8
@ ACIP_TRAFFIC_AUDIO
Definition traffic.h:7
@ ACIP_DELIVERY_RELIABLE
Definition traffic.h:12
@ ACIP_DELIVERY_DATAGRAM
Definition traffic.h:13