Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
quic_socket.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
5#include "quic_socket.h"
6
7#include <random>
8#include <chrono>
9#include <type_traits>
10
12{
13
14using namespace protocols::quic;
15
16// =============================================================================
17// Construction / Destruction
18// =============================================================================
19
20quic_socket::quic_socket(asio::ip::udp::socket socket, quic_role role)
21 : udp_socket_(std::move(socket))
22 , role_(role)
23 , retransmit_timer_(udp_socket_.get_executor())
24 , idle_timer_(udp_socket_.get_executor())
25{
26 // Generate local connection ID
28
29 // Initialize next stream ID based on role
30 // Client-initiated bidi streams: 0, 4, 8, ...
31 // Server-initiated bidi streams: 1, 5, 9, ...
33}
34
36{
38
39 // Cancel timers
40 retransmit_timer_.cancel();
41 idle_timer_.cancel();
42}
43
45 : udp_socket_(std::move(other.udp_socket_))
46 , remote_endpoint_(std::move(other.remote_endpoint_))
47 , recv_buffer_(std::move(other.recv_buffer_))
48 , role_(other.role_)
49 , state_(other.state_.load())
50 , crypto_(std::move(other.crypto_))
51 , local_conn_id_(std::move(other.local_conn_id_))
52 , remote_conn_id_(std::move(other.remote_conn_id_))
53 , next_packet_number_(other.next_packet_number_)
54 , largest_received_pn_(other.largest_received_pn_)
55 , next_stream_id_(other.next_stream_id_)
56 , pending_crypto_data_(std::move(other.pending_crypto_data_))
57 , pending_stream_data_(std::move(other.pending_stream_data_))
58 , stream_data_cb_(std::move(other.stream_data_cb_))
59 , connected_cb_(std::move(other.connected_cb_))
60 , error_cb_(std::move(other.error_cb_))
61 , close_cb_(std::move(other.close_cb_))
62 , is_receiving_(other.is_receiving_.load())
63 , handshake_complete_(other.handshake_complete_.load())
64 , retransmit_timer_(std::move(other.retransmit_timer_))
65 , idle_timer_(std::move(other.idle_timer_))
66{
67}
68
70{
71 if (this != &other)
72 {
73 stop_receive();
74
75 udp_socket_ = std::move(other.udp_socket_);
76 remote_endpoint_ = std::move(other.remote_endpoint_);
77 recv_buffer_ = std::move(other.recv_buffer_);
78 role_ = other.role_;
79 state_.store(other.state_.load());
80 crypto_ = std::move(other.crypto_);
81 local_conn_id_ = std::move(other.local_conn_id_);
82 remote_conn_id_ = std::move(other.remote_conn_id_);
83 next_packet_number_ = other.next_packet_number_;
84 largest_received_pn_ = other.largest_received_pn_;
85 next_stream_id_ = other.next_stream_id_;
86 pending_crypto_data_ = std::move(other.pending_crypto_data_);
87 pending_stream_data_ = std::move(other.pending_stream_data_);
88
89 std::lock_guard<std::mutex> lock(callback_mutex_);
90 stream_data_cb_ = std::move(other.stream_data_cb_);
91 connected_cb_ = std::move(other.connected_cb_);
92 error_cb_ = std::move(other.error_cb_);
93 close_cb_ = std::move(other.close_cb_);
94
95 is_receiving_.store(other.is_receiving_.load());
96 handshake_complete_.store(other.handshake_complete_.load());
97 retransmit_timer_ = std::move(other.retransmit_timer_);
98 idle_timer_ = std::move(other.idle_timer_);
99 }
100 return *this;
101}
102
103// =============================================================================
104// Callback Registration
105// =============================================================================
106
108{
109 std::lock_guard<std::mutex> lock(callback_mutex_);
110 stream_data_cb_ = std::move(cb);
111}
112
114{
115 std::lock_guard<std::mutex> lock(callback_mutex_);
116 connected_cb_ = std::move(cb);
117}
118
120{
121 std::lock_guard<std::mutex> lock(callback_mutex_);
122 error_cb_ = std::move(cb);
123}
124
126{
127 std::lock_guard<std::mutex> lock(callback_mutex_);
128 close_cb_ = std::move(cb);
129}
130
131// =============================================================================
132// Connection Management
133// =============================================================================
134
135auto quic_socket::connect(const asio::ip::udp::endpoint& endpoint,
136 const std::string& server_name) -> VoidResult
137{
138 if (role_ != quic_role::client)
139 {
140 return error_void(
142 "connect() can only be called on client sockets",
143 "quic_socket");
144 }
145
146 if (state_.load() != quic_connection_state::idle)
147 {
148 return error_void(
150 "Connection already in progress or established",
151 "quic_socket");
152 }
153
154 remote_endpoint_ = endpoint;
155
156 // Initialize client-side crypto
157 auto init_result = crypto_.init_client(
158 server_name.empty() ? endpoint.address().to_string() : server_name);
159 if (init_result.is_err())
160 {
161 return error_void(
163 "Failed to initialize TLS client",
164 "quic_socket",
165 init_result.error().message);
166 }
167
168 // Generate initial secrets from destination connection ID
169 // For client, we use a random connection ID as the destination
170 remote_conn_id_ = generate_connection_id();
171
172 auto derive_result = crypto_.derive_initial_secrets(remote_conn_id_);
173 if (derive_result.is_err())
174 {
175 return error_void(
177 "Failed to derive initial secrets",
178 "quic_socket",
179 derive_result.error().message);
180 }
181
183
184 // Start handshake - generate ClientHello
185 auto handshake_result = crypto_.start_handshake();
186 if (handshake_result.is_err())
187 {
188 transition_state(quic_connection_state::closed);
189 return error_void(
191 "Failed to start TLS handshake",
192 "quic_socket",
193 handshake_result.error().message);
194 }
195
196 // Queue the CRYPTO data for sending
197 if (!handshake_result.value().empty())
198 {
199 queue_crypto_data(std::move(handshake_result.value()));
200 }
201
202 transition_state(quic_connection_state::handshake);
203
204 // Start receiving
205 start_receive();
206
207 // Send initial packet
208 send_pending_packets();
209
210 return ok();
211}
212
213auto quic_socket::accept(const std::string& cert_file,
214 const std::string& key_file) -> VoidResult
215{
216 if (role_ != quic_role::server)
217 {
218 return error_void(
220 "accept() can only be called on server sockets",
221 "quic_socket");
222 }
223
224 if (state_.load() != quic_connection_state::idle)
225 {
226 return error_void(
228 "Connection already in progress",
229 "quic_socket");
230 }
231
232 // Initialize server-side crypto
233 auto init_result = crypto_.init_server(cert_file, key_file);
234 if (init_result.is_err())
235 {
236 return error_void(
238 "Failed to initialize TLS server",
239 "quic_socket",
240 init_result.error().message);
241 }
242
244
245 // Start receiving - server waits for Initial packet from client
246 start_receive();
247
248 return ok();
249}
250
251auto quic_socket::close(uint64_t error_code, const std::string& reason) -> VoidResult
252{
253 auto current_state = state_.load();
254 if (current_state == quic_connection_state::closed ||
255 current_state == quic_connection_state::closing ||
256 current_state == quic_connection_state::draining)
257 {
258 return ok(); // Already closing/closed
259 }
260
261 transition_state(quic_connection_state::closing);
262
263 // Build CONNECTION_CLOSE frame
264 connection_close_frame close_frame;
265 close_frame.error_code = error_code;
266 close_frame.reason_phrase = reason;
267 close_frame.is_application_error = (error_code != 0);
268
269 // Send CONNECTION_CLOSE
270 std::vector<frame> frames;
271 frames.push_back(close_frame);
272
273 auto level = handshake_complete_.load()
274 ? encryption_level::application
275 : encryption_level::initial;
276
277 auto send_result = send_packet(level, std::move(frames));
278 if (send_result.is_err())
279 {
280 // Log error but continue with close
281 }
282
283 transition_state(quic_connection_state::draining);
284
285 // Set a timer for draining period (3 * PTO), then fully close
286 idle_timer_.expires_after(std::chrono::milliseconds(300));
287 idle_timer_.async_wait(
288 [self = shared_from_this()](const std::error_code& ec)
289 {
290 if (!ec)
291 {
292 self->transition_state(quic_connection_state::closed);
293 self->stop_receive();
294 }
295 });
296
297 return ok();
298}
299
300// =============================================================================
301// I/O Operations
302// =============================================================================
303
305{
306 is_receiving_.store(true);
307 do_receive();
308}
309
311{
312 is_receiving_.store(false);
313}
314
315auto quic_socket::send_stream_data(uint64_t stream_id,
316 std::vector<uint8_t>&& data,
317 bool fin) -> VoidResult
318{
319 if (!is_connected())
320 {
321 return error_void(
323 "Connection not established",
324 "quic_socket");
325 }
326
327 {
328 std::lock_guard<std::mutex> lock(state_mutex_);
329 pending_stream_data_[stream_id].push_back({std::move(data), fin});
330 }
331
332 send_pending_packets();
333
334 return ok();
335}
336
337// =============================================================================
338// Stream Management
339// =============================================================================
340
342{
343 if (!is_connected())
344 {
345 return error<uint64_t>(
347 "Connection not established",
348 "quic_socket");
349 }
350
351 std::lock_guard<std::mutex> lock(state_mutex_);
352
353 // Stream ID encoding (RFC 9000 Section 2.1):
354 // - Bits 0-1: Type (0=client-initiated bidi, 1=server-initiated bidi,
355 // 2=client-initiated uni, 3=server-initiated uni)
356 // - Bits 2+: Sequence number
357
358 uint64_t type_bits = 0;
359 if (role_ == quic_role::server)
360 {
361 type_bits |= 0x01; // Server-initiated
362 }
363 if (unidirectional)
364 {
365 type_bits |= 0x02; // Unidirectional
366 }
367
368 uint64_t stream_id = (next_stream_id_ << 2) | type_bits;
369 next_stream_id_++;
370
371 // Initialize the stream's pending data queue
372 pending_stream_data_[stream_id] = {};
373
374 return ok(std::move(stream_id));
375}
376
377auto quic_socket::close_stream(uint64_t stream_id) -> VoidResult
378{
379 std::lock_guard<std::mutex> lock(state_mutex_);
380
381 auto it = pending_stream_data_.find(stream_id);
382 if (it == pending_stream_data_.end())
383 {
384 return error_void(
386 "Stream not found",
387 "quic_socket");
388 }
389
390 // Send FIN on the stream
391 it->second.push_back({{}, true});
392
393 send_pending_packets();
394
395 return ok();
396}
397
398// =============================================================================
399// State Queries
400// =============================================================================
401
402auto quic_socket::is_connected() const noexcept -> bool
403{
405}
406
407auto quic_socket::is_handshake_complete() const noexcept -> bool
408{
409 return handshake_complete_.load();
410}
411
413{
414 return state_.load();
415}
416
417auto quic_socket::role() const noexcept -> quic_role
418{
419 return role_;
420}
421
422auto quic_socket::remote_endpoint() const -> asio::ip::udp::endpoint
423{
424 return remote_endpoint_;
425}
426
428{
429 return local_conn_id_;
430}
431
433{
434 return remote_conn_id_;
435}
436
437// =============================================================================
438// Internal Methods
439// =============================================================================
440
441auto quic_socket::negotiated_alpn() const -> std::string
442{
443 return crypto_.get_alpn();
444}
445
447{
448 if (!is_receiving_.load())
449 {
450 return;
451 }
452
453 auto self = shared_from_this();
454 udp_socket_.async_receive_from(
455 asio::buffer(recv_buffer_),
456 remote_endpoint_,
457 [this, self](std::error_code ec, std::size_t bytes_transferred)
458 {
459 if (!is_receiving_.load())
460 {
461 return;
462 }
463
464 if (ec)
465 {
466 if (ec != asio::error::operation_aborted)
467 {
468 std::lock_guard<std::mutex> lock(callback_mutex_);
469 if (error_cb_)
470 {
471 error_cb_(ec);
472 }
473 }
474 return;
475 }
476
477 if (bytes_transferred > 0)
478 {
479 packets_received_.fetch_add(1, std::memory_order_relaxed);
480 bytes_received_.fetch_add(bytes_transferred,
481 std::memory_order_relaxed);
482 handle_packet(std::span(recv_buffer_.data(), bytes_transferred));
483 }
484
485 // Continue receiving
486 if (is_receiving_.load())
487 {
488 do_receive();
489 }
490 });
491}
492
493auto quic_socket::handle_packet(std::span<const uint8_t> data) -> void
494{
495 // Parse packet header
496 auto header_result = packet_parser::parse_header(data);
497 if (header_result.is_err())
498 {
499 // Invalid packet - silently ignore
500 return;
501 }
502
503 auto& [header, header_length] = header_result.value();
504
505 // Determine encryption level
506 auto level = determine_encryption_level(header);
507
508 // For server, derive initial secrets on first Initial packet
509 if (role_ == quic_role::server &&
511 {
512 if (std::holds_alternative<long_header>(header))
513 {
514 const auto& lh = std::get<long_header>(header);
515 if (lh.type() == packet_type::initial)
516 {
517 // Use client's DCID as our remote connection ID
518 remote_conn_id_ = lh.src_conn_id;
519
520 // Derive initial secrets from client's DCID
521 auto derive_result = crypto_.derive_initial_secrets(lh.dest_conn_id);
522 if (derive_result.is_err())
523 {
524 return;
525 }
526
527 transition_state(quic_connection_state::handshake);
528 }
529 }
530 }
531
532 // Get read keys for this level
533 auto keys_result = crypto_.get_read_keys(level);
534 if (keys_result.is_err())
535 {
536 // Keys not available yet - queue packet for later processing
537 return;
538 }
539
540 // Unprotect (decrypt) the packet
541 // First we need to get the packet number offset
542 size_t pn_offset = header_length;
543 if (std::holds_alternative<long_header>(header))
544 {
545 // For long headers, packet number is after the header
546 // (already accounted for in header_length for Initial/Handshake)
547 }
548
549 // Determine packet number length from header (after removing header protection)
550 size_t sample_offset = pn_offset + 4; // Sample is 4 bytes after PN start
551 if (sample_offset + hp_sample_size > data.size())
552 {
553 return; // Not enough data for sample
554 }
555
556 std::span<const uint8_t> sample(data.data() + sample_offset, hp_sample_size);
557
558 // Create mutable copy for header unprotection
559 std::vector<uint8_t> packet_copy(data.begin(), data.end());
560
561 auto unprotect_header_result = packet_protection::unprotect_header(
562 keys_result.value(),
563 std::span(packet_copy.data(), header_length + 4), // Include space for max PN
564 pn_offset,
565 sample);
566
567 if (unprotect_header_result.is_err())
568 {
569 return;
570 }
571
572 auto& [first_byte, pn_length] = unprotect_header_result.value();
573
574 // Extract packet number
575 uint64_t truncated_pn = 0;
576 for (size_t i = 0; i < pn_length; ++i)
577 {
578 truncated_pn = (truncated_pn << 8) | packet_copy[pn_offset + i];
579 }
580
581 // Decode full packet number
582 auto level_idx = static_cast<size_t>(level);
583 uint64_t full_pn = packet_number::decode(
584 truncated_pn, pn_length, largest_received_pn_[level_idx]);
585
586 // Update largest received
587 if (full_pn > largest_received_pn_[level_idx])
588 {
589 largest_received_pn_[level_idx] = full_pn;
590 }
591
592 // Decrypt packet payload
593 size_t payload_offset = pn_offset + pn_length;
594 auto unprotect_result = packet_protection::unprotect(
595 keys_result.value(),
596 std::span(packet_copy),
597 payload_offset,
598 full_pn);
599
600 if (unprotect_result.is_err())
601 {
602 return;
603 }
604
605 auto& [unprotected_header, payload] = unprotect_result.value();
606
607 // Parse frames from payload
608 auto frames_result = frame_parser::parse_all(payload);
609 if (frames_result.is_err())
610 {
611 return;
612 }
613
614 // Process each frame
615 for (const auto& f : frames_result.value())
616 {
617 process_frame(f);
618 }
619
620 // Send any pending responses
621 send_pending_packets();
622}
623
624auto quic_socket::process_frame(const frame& f) -> void
625{
626 std::visit([this](auto&& arg) {
627 using T = std::decay_t<decltype(arg)>;
628
629 if constexpr (std::is_same_v<T, crypto_frame>)
630 {
631 process_crypto_frame(arg);
632 }
633 else if constexpr (std::is_same_v<T, stream_frame>)
634 {
635 process_stream_frame(arg);
636 }
637 else if constexpr (std::is_same_v<T, ack_frame>)
638 {
639 process_ack_frame(arg);
640 }
641 else if constexpr (std::is_same_v<T, connection_close_frame>)
642 {
643 process_connection_close_frame(arg);
644 }
645 else if constexpr (std::is_same_v<T, handshake_done_frame>)
646 {
647 process_handshake_done_frame();
648 }
649 else if constexpr (std::is_same_v<T, ping_frame>)
650 {
651 // PING requires ACK - will be sent with next packet
652 }
653 else if constexpr (std::is_same_v<T, padding_frame>)
654 {
655 // PADDING is ignored
656 }
657 // Other frame types can be added as needed
658 }, f);
659}
660
662{
663 auto level = crypto_.current_level();
664
665 // Process the crypto data through TLS
666 auto response_result = crypto_.process_crypto_data(level, f.data);
667 if (response_result.is_err())
668 {
669 return;
670 }
671
672 // Queue any response crypto data
673 if (!response_result.value().empty())
674 {
675 queue_crypto_data(std::move(response_result.value()));
676 }
677
678 // Check if handshake is now complete
679 if (crypto_.is_handshake_complete() && !handshake_complete_.load())
680 {
681 handshake_complete_.store(true);
682
683 // For client, transition to connected
684 // For server, we send HANDSHAKE_DONE
685 if (role_ == quic_role::client)
686 {
687 transition_state(quic_connection_state::connected);
688
689 std::lock_guard<std::mutex> lock(callback_mutex_);
690 if (connected_cb_)
691 {
692 connected_cb_();
693 }
694 }
695 else
696 {
697 // Server sends HANDSHAKE_DONE
698 std::vector<frame> frames;
699 frames.push_back(handshake_done_frame{});
700
701 (void)send_packet(encryption_level::application, std::move(frames));
702
703 transition_state(quic_connection_state::connected);
704
705 std::lock_guard<std::mutex> lock(callback_mutex_);
706 if (connected_cb_)
707 {
708 connected_cb_();
709 }
710 }
711 }
712}
713
715{
716 std::lock_guard<std::mutex> lock(callback_mutex_);
717 if (stream_data_cb_)
718 {
719 stream_data_cb_(f.stream_id, f.data, f.fin);
720 }
721}
722
724{
725 // Process ACK - remove acknowledged packets from retransmission queue
726 // For now, just track the largest acknowledged
727 (void)f; // Placeholder for full implementation
728}
729
731{
732 transition_state(quic_connection_state::draining);
733
734 std::lock_guard<std::mutex> lock(callback_mutex_);
735 if (close_cb_)
736 {
737 close_cb_(f.error_code, f.reason_phrase);
738 }
739
740 // Enter draining period
741 idle_timer_.expires_after(std::chrono::milliseconds(300));
742 idle_timer_.async_wait(
743 [self = shared_from_this()](const std::error_code& ec)
744 {
745 if (!ec)
746 {
747 self->transition_state(quic_connection_state::closed);
748 self->stop_receive();
749 }
750 });
751}
752
754{
755 // Client receives HANDSHAKE_DONE
756 if (role_ == quic_role::client && !handshake_complete_.load())
757 {
758 handshake_complete_.store(true);
759 transition_state(quic_connection_state::connected);
760
761 std::lock_guard<std::mutex> lock(callback_mutex_);
762 if (connected_cb_)
763 {
764 connected_cb_();
765 }
766 }
767}
768
770{
771 auto current_state = state_.load();
772 if (current_state == quic_connection_state::closed ||
773 current_state == quic_connection_state::idle)
774 {
775 return;
776 }
777
778 // Determine which encryption level to use
779 encryption_level level = crypto_.current_level();
780
781 std::vector<frame> frames;
782
783 // Add pending CRYPTO data
784 {
785 std::lock_guard<std::mutex> lock(state_mutex_);
786 auto level_idx = static_cast<size_t>(level);
787 while (!pending_crypto_data_[level_idx].empty())
788 {
789 auto& data = pending_crypto_data_[level_idx].front();
790 crypto_frame cf;
791 cf.offset = 0; // Simplified - full impl needs offset tracking
792 cf.data = std::move(data);
793 frames.push_back(std::move(cf));
794 pending_crypto_data_[level_idx].pop_front();
795 }
796 }
797
798 // Add pending STREAM data (only if connected)
799 if (is_connected())
800 {
801 std::lock_guard<std::mutex> lock(state_mutex_);
802 for (auto& [stream_id, queue] : pending_stream_data_)
803 {
804 while (!queue.empty())
805 {
806 auto& [data, fin] = queue.front();
807 stream_frame sf;
808 sf.stream_id = stream_id;
809 sf.offset = 0; // Simplified - full impl needs offset tracking
810 sf.data = std::move(data);
811 sf.fin = fin;
812 frames.push_back(std::move(sf));
813 queue.pop_front();
814 }
815 }
816 }
817
818 if (!frames.empty())
819 {
820 (void)send_packet(level, std::move(frames));
821 }
822}
823
825 std::vector<frame>&& frames) -> VoidResult
826{
827 // Get write keys
828 auto keys_result = crypto_.get_write_keys(level);
829 if (keys_result.is_err())
830 {
831 return error_void(
833 "Write keys not available",
834 "quic_socket");
835 }
836
837 // Build frames payload
838 std::vector<uint8_t> payload;
839 for (const auto& f : frames)
840 {
841 auto frame_bytes = frame_builder::build(f);
842 payload.insert(payload.end(), frame_bytes.begin(), frame_bytes.end());
843 }
844
845 // Get next packet number
846 auto level_idx = static_cast<size_t>(level);
847 uint64_t pn = next_packet_number_[level_idx]++;
848
849 // Build header
850 std::vector<uint8_t> header;
851 if (level == encryption_level::initial)
852 {
854 remote_conn_id_, local_conn_id_, {}, pn);
855 }
856 else if (level == encryption_level::handshake)
857 {
859 remote_conn_id_, local_conn_id_, pn);
860 }
861 else
862 {
864 remote_conn_id_, pn, crypto_.key_phase());
865 }
866
867 // Protect (encrypt) the packet
868 auto protect_result = packet_protection::protect(
869 keys_result.value(), header, payload, pn);
870
871 if (protect_result.is_err())
872 {
873 return error_void(
875 "Failed to protect packet",
876 "quic_socket",
877 protect_result.error().message);
878 }
879
880 // Send the packet
881 auto& protected_packet = protect_result.value();
882
883 auto self = shared_from_this();
884 auto buffer = std::make_shared<std::vector<uint8_t>>(std::move(protected_packet));
885
886 packets_sent_.fetch_add(1, std::memory_order_relaxed);
887 bytes_sent_.fetch_add(buffer->size(), std::memory_order_relaxed);
888
889 auto on_sent = [self, buffer](std::error_code ec, std::size_t /*bytes_sent*/)
890 {
891 if (ec && ec != asio::error::operation_aborted)
892 {
893 std::lock_guard<std::mutex> lock(self->callback_mutex_);
894 if (self->error_cb_)
895 {
896 self->error_cb_(ec);
897 }
898 }
899 };
900
901 // Connected UDP sockets reject send_to on some platforms (including
902 // macOS). Use the established peer when the supplied socket is connected.
903 std::error_code endpoint_error;
904 const auto connected_endpoint = udp_socket_.remote_endpoint(endpoint_error);
905 if (!endpoint_error && connected_endpoint == remote_endpoint_)
906 {
907 udp_socket_.async_send(asio::buffer(*buffer), std::move(on_sent));
908 }
909 else
910 {
911 udp_socket_.async_send_to(asio::buffer(*buffer), remote_endpoint_, std::move(on_sent));
912 }
913
914 return ok();
915}
916
917auto quic_socket::queue_crypto_data(std::vector<uint8_t>&& data) -> void
918{
919 auto level = crypto_.current_level();
920 auto level_idx = static_cast<size_t>(level);
921
922 std::lock_guard<std::mutex> lock(state_mutex_);
923 pending_crypto_data_[level_idx].push_back(std::move(data));
924}
925
928{
929 if (std::holds_alternative<long_header>(header))
930 {
931 const auto& lh = std::get<long_header>(header);
932 switch (lh.type())
933 {
934 case packet_type::initial:
935 return encryption_level::initial;
936 case packet_type::zero_rtt:
937 return encryption_level::zero_rtt;
938 case packet_type::handshake:
939 return encryption_level::handshake;
940 default:
941 return encryption_level::initial;
942 }
943 }
944 else
945 {
946 // Short header = 1-RTT = application level
947 return encryption_level::application;
948 }
949}
950
952{
953 std::random_device rd;
954 std::mt19937 gen(rd());
955 std::uniform_int_distribution<unsigned int> dis(0, 255);
956
957 std::array<uint8_t, 8> id_bytes;
958 for (auto& byte : id_bytes)
959 {
960 byte = static_cast<uint8_t>(dis(gen));
961 }
962
963 return connection_id(std::span<const uint8_t>(id_bytes));
964}
965
967{
968 // Retransmission logic would go here
969 // For now, just resend pending packets
970 send_pending_packets();
971}
972
974{
975 state_.store(new_state);
976}
977
978} // namespace kcenon::network::internal
A QUIC socket that wraps UDP and integrates QUIC packet protection.
Definition quic_socket.h:80
auto local_connection_id() const -> const protocols::quic::connection_id &
Get the local connection ID.
auto determine_encryption_level(const protocols::quic::packet_header &header) const noexcept -> protocols::quic::encryption_level
Determine encryption level from packet header.
std::function< void( uint64_t stream_id, std::span< const uint8_t > data, bool fin)> stream_data_callback
Callback for receiving stream data.
Definition quic_socket.h:88
auto on_retransmit_timeout() -> void
Retransmission timeout handler.
auto remote_connection_id() const -> const protocols::quic::connection_id &
Get the remote connection ID.
std::function< void(uint64_t error_code, const std::string &reason)> close_callback
Callback when connection is closed.
std::atomic< quic_connection_state > state_
Connection state.
auto process_frame(const protocols::quic::frame &f) -> void
Process a parsed frame.
auto connect(const asio::ip::udp::endpoint &endpoint, const std::string &server_name="") -> VoidResult
Connect to a remote server (client only)
auto process_connection_close_frame(const protocols::quic::connection_close_frame &f) -> void
Process CONNECTION_CLOSE frame.
auto transition_state(quic_connection_state new_state) -> void
Transition to a new connection state.
auto state() const noexcept -> quic_connection_state
Get the current connection state.
protocols::quic::connection_id remote_conn_id_
Remote connection ID.
auto send_stream_data(uint64_t stream_id, std::vector< uint8_t > &&data, bool fin=false) -> VoidResult
Send data on a stream.
auto process_handshake_done_frame() -> void
Process HANDSHAKE_DONE frame.
auto is_handshake_complete() const noexcept -> bool
Check if the TLS handshake is complete.
auto create_stream(bool unidirectional=false) -> Result< uint64_t >
Create a new stream.
auto queue_crypto_data(std::vector< uint8_t > &&data) -> void
Queue crypto data for sending.
std::atomic< bool > handshake_complete_
Is handshake complete.
auto process_ack_frame(const protocols::quic::ack_frame &f) -> void
Process ACK frame.
auto send_pending_packets() -> void
Send pending outgoing packets.
auto process_stream_frame(const protocols::quic::stream_frame &f) -> void
Process STREAM frame data.
auto close(uint64_t error_code=0, const std::string &reason="") -> VoidResult
Close the connection gracefully.
auto send_packet(protocols::quic::encryption_level level, std::vector< protocols::quic::frame > &&frames) -> VoidResult
Build and send a packet with frames.
auto role() const noexcept -> quic_role
Get the role (client or server)
quic_socket(asio::ip::udp::socket socket, quic_role role)
Constructs a QUIC socket.
auto remote_endpoint() const -> asio::ip::udp::endpoint
Get the remote endpoint.
auto handle_packet(std::span< const uint8_t > data) -> void
Handle received packet data.
auto do_receive() -> void
Internal receive loop implementation.
auto is_connected() const noexcept -> bool
Check if the connection is established.
std::function< void(std::error_code)> error_callback
Callback for error handling.
quic_role role_
Socket role (client/server)
uint64_t next_stream_id_
Next stream ID to allocate.
auto generate_connection_id() -> protocols::quic::connection_id
Generate a new connection ID.
protocols::quic::quic_crypto crypto_
QUIC crypto handler.
std::function< void()> connected_callback
Callback when connection is established.
Definition quic_socket.h:96
auto close_stream(uint64_t stream_id) -> VoidResult
Close a stream.
protocols::quic::connection_id local_conn_id_
Local connection ID.
auto stop_receive() -> void
Stop the receive loop.
asio::ip::udp::endpoint remote_endpoint_
Remote endpoint.
auto set_stream_data_callback(stream_data_callback cb) -> void
Set callback for stream data reception.
asio::steady_timer retransmit_timer_
Retransmission timer.
auto start_receive() -> void
Start the receive loop.
asio::steady_timer idle_timer_
Idle timeout timer.
auto accept(const std::string &cert_file, const std::string &key_file) -> VoidResult
Accept an incoming connection (server only)
auto set_close_callback(close_callback cb) -> void
Set callback for connection close.
auto negotiated_alpn() const -> std::string
Get the ALPN protocol negotiated during the handshake.
quic_socket & operator=(const quic_socket &)=delete
auto set_connected_callback(connected_callback cb) -> void
Set callback for connection establishment.
auto process_crypto_frame(const protocols::quic::crypto_frame &f) -> void
Process CRYPTO frame data.
auto set_error_callback(error_callback cb) -> void
Set callback for errors.
QUIC Connection ID (RFC 9000 Section 5.1)
static auto build(const frame &f) -> std::vector< uint8_t >
Build any frame from variant.
Definition frame.cpp:893
static auto parse_all(std::span< const uint8_t > data) -> Result< std::vector< frame > >
Parse all frames from buffer.
Definition frame.cpp:343
static auto build_handshake(const connection_id &dest_cid, const connection_id &src_cid, uint64_t packet_number, uint32_t version=quic_version::version_1) -> std::vector< uint8_t >
Build a Handshake packet header.
Definition packet.cpp:469
static auto build_short(const connection_id &dest_cid, uint64_t packet_number, bool key_phase=false, bool spin_bit=false) -> std::vector< uint8_t >
Build a Short Header (1-RTT) packet.
Definition packet.cpp:580
static auto build_initial(const connection_id &dest_cid, const connection_id &src_cid, const std::vector< uint8_t > &token, uint64_t packet_number, uint32_t version=quic_version::version_1) -> std::vector< uint8_t >
Build an Initial packet header.
Definition packet.cpp:425
static auto decode(uint64_t truncated_pn, size_t pn_length, uint64_t largest_pn) noexcept -> uint64_t
Decode a packet number from received data.
Definition packet.cpp:103
static auto parse_header(std::span< const uint8_t > data) -> Result< std::pair< packet_header, size_t > >
Parse a packet header (without header protection removal)
Definition packet.cpp:170
static auto unprotect_header(const quic_keys &keys, std::span< uint8_t > header, size_t pn_offset, std::span< const uint8_t > sample) -> Result< std::pair< uint8_t, size_t > >
Remove header protection.
Definition crypto.cpp:691
static auto unprotect(const quic_keys &keys, std::span< const uint8_t > packet, size_t header_length, uint64_t packet_number) -> Result< std::pair< std::vector< uint8_t >, std::vector< uint8_t > > >
Unprotect (decrypt) a QUIC packet.
Definition crypto.cpp:512
static auto protect(const quic_keys &keys, std::span< const uint8_t > header, std::span< const uint8_t > payload, uint64_t packet_number) -> Result< std::vector< uint8_t > >
Protect (encrypt) a QUIC packet.
Definition crypto.cpp:429
auto get_alpn() const -> std::string
Get the negotiated ALPN protocol.
Definition crypto.cpp:1119
quic_role
Role of the QUIC endpoint (client or server)
Definition quic_socket.h:38
quic_connection_state
QUIC connection state machine states.
Definition quic_socket.h:48
@ draining
Draining period before close.
@ error
Black hole detected, reset to base.
constexpr size_t hp_sample_size
Header protection sample size.
Definition keys.h:37
encryption_level
QUIC encryption levels (RFC 9001 Section 4)
Definition keys.h:54
std::variant< padding_frame, ping_frame, ack_frame, reset_stream_frame, stop_sending_frame, crypto_frame, new_token_frame, stream_frame, max_data_frame, max_stream_data_frame, max_streams_frame, data_blocked_frame, stream_data_blocked_frame, streams_blocked_frame, new_connection_id_frame, retire_connection_id_frame, path_challenge_frame, path_response_frame, connection_close_frame, handshake_done_frame > frame
Variant type holding any QUIC frame.
std::variant< long_header, short_header > packet_header
Variant type for packet headers.
Definition packet.h:160
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
VoidResult ok()
ACK frame (RFC 9000 Section 19.3)
CONNECTION_CLOSE frame (RFC 9000 Section 19.19)
uint64_t error_code
Error code indicating reason.
bool is_application_error
True if application-level error.
CRYPTO frame (RFC 9000 Section 19.6)
uint64_t offset
Byte offset in crypto stream.
std::vector< uint8_t > data
Cryptographic handshake data.
HANDSHAKE_DONE frame (RFC 9000 Section 19.20)
STREAM frame (RFC 9000 Section 19.8)
std::vector< uint8_t > data
Stream data.
uint64_t offset
Byte offset in stream (0 if not present)
bool fin
True if this is the final data.